Documentation

use crate::{arbitrage::Arbitrage, blockchain_performance::BlockchainPerformance, bollinger_bands::BollingerBands, breakout::Breakout, day_trading::DayTrading, dollar_cost_averaging::DollarCostAveraging, mean_reversion::MeanReversion, momentum::Momentum, moving_average_cross::MovingAverageCross, news_sentiment::NewsSentiment, scalping::Scalping, swing_trading::SwingTrading, target_price::TargetPrice, trend_following::TrendFollowing, volatility::Volatility, volume_strategy::VolumeStrategy};

pub struct StrategyEngine {
    pub strategies: Vec<Box<dyn Strategy>>,
}

impl StrategyEngine {
    /// 初始化策略引擎
    pub fn new() -> Self {
        Self {
            strategies: vec![],
        }
    }

    /// 添加策略
    pub fn add_strategy(&mut self, strategy: Box<dyn Strategy>) {
        self.strategies.push(strategy);
    }

    /// 回测所有策略
    pub fn backtest(&self, prices: Vec<f64>) -> Vec<f64> {
        let mut results = vec![];
        for strategy in &self.strategies {
            let mut result = 0.0;
            for price in &prices {
                result += strategy.evaluate(price);
            }
            results.push(result);
        }
        results
    }

    /// 计算指标
    pub fn calculate_indicators(&self, prices: Vec<f64>) -> Vec<f64> {
        let mut indicators = vec![];
        for strategy in &self.strategies {
            let indicator = strategy.calculate_indicator(&prices);
            indicators.push(indicator);
        }
        indicators
    }
}

impl Default for StrategyEngine {
    fn default() -> Self {
        let mut engine = Self::new();

        let trend_following = TrendFollowing::new(3, 6);
        let moving_average_cross = MovingAverageCross::new(3, 6);
        let bollinger_bands = BollingerBands::new(10);
        let mean_reversion = MeanReversion::new(5);
        let momentum = Momentum::new(3);
        let arbitrage = Arbitrage::new();
        let swing_trading = SwingTrading::new(3, 6);
        let day_trading = DayTrading::new();
        let dollar_cost_averaging = DollarCostAveraging::new();
        let scalping = Scalping::new();
        let breakout = Breakout::new(150.0, 100.0);
        let news_sentiment = NewsSentiment::new();
        let volatility = Volatility::new();
        let blockchain_performance = BlockchainPerformance::new();
        let target_price = TargetPrice::new(120.0);
        let volume_strategy = VolumeStrategy::new();
        
        engine.add_strategy(Box::new(trend_following.clone()));
        engine.add_strategy(Box::new(moving_average_cross.clone()));
        engine.add_strategy(Box::new(bollinger_bands.clone()));
        engine.add_strategy(Box::new(mean_reversion.clone()));
        engine.add_strategy(Box::new(momentum.clone()));
        engine.add_strategy(Box::new(arbitrage.clone()));
        engine.add_strategy(Box::new(swing_trading.clone()));
        engine.add_strategy(Box::new(day_trading.clone()));
        engine.add_strategy(Box::new(dollar_cost_averaging.clone()));
        engine.add_strategy(Box::new(scalping.clone()));
        engine.add_strategy(Box::new(breakout.clone()));
        engine.add_strategy(Box::new(news_sentiment.clone()));
        engine.add_strategy(Box::new(volatility.clone()));
        engine.add_strategy(Box::new(blockchain_performance.clone()));
        engine.add_strategy(Box::new(target_price.clone()));
        engine.add_strategy(Box::new(volume_strategy.clone()));

        engine
    }
}

/// 交易策略trait
pub trait Strategy {
    fn evaluate(&self, price: &f64) -> f64;
    fn calculate_indicator(&self, prices: &Vec<f64>) -> f64;
}

#[cfg(test)]
mod tests {
    use crate::{arbitrage::Arbitrage, blockchain_performance::BlockchainPerformance, bollinger_bands::BollingerBands, breakout::Breakout, day_trading::DayTrading, dollar_cost_averaging::DollarCostAveraging, mean_reversion::MeanReversion, momentum::Momentum, moving_average_cross::MovingAverageCross, news_sentiment::NewsSentiment, scalping::Scalping, swing_trading::SwingTrading, target_price::TargetPrice, trend_following::TrendFollowing, volatility::Volatility, volume_strategy::VolumeStrategy};

    use super::*;

    #[test]
    fn test_strategy_engine() {
        let mut engine = StrategyEngine::new();

        let mut trend_following = TrendFollowing::new(3, 6);
        let mut moving_average_cross = MovingAverageCross::new(3, 6);
        let mut bollinger_bands = BollingerBands::new(10);
        let mut mean_reversion = MeanReversion::new(5);
        let mut momentum = Momentum::new(3);
        let arbitrage = Arbitrage::new();
        let mut swing_trading = SwingTrading::new(3, 6);
        let mut day_trading = DayTrading::new();
        let mut dollar_cost_averaging = DollarCostAveraging::new();
        let mut scalping = Scalping::new();
        let mut breakout = Breakout::new(150.0, 100.0);
        let mut news_sentiment = NewsSentiment::new();
        let mut volatility = Volatility::new();
        let mut blockchain_performance = BlockchainPerformance::new();
        let target_price = TargetPrice::new(120.0);
        let mut volume_strategy = VolumeStrategy::new();
        
        engine.add_strategy(Box::new(trend_following.clone()));
        engine.add_strategy(Box::new(moving_average_cross.clone()));
        engine.add_strategy(Box::new(bollinger_bands.clone()));
        engine.add_strategy(Box::new(mean_reversion.clone()));
        engine.add_strategy(Box::new(momentum.clone()));
        engine.add_strategy(Box::new(arbitrage.clone()));
        engine.add_strategy(Box::new(swing_trading.clone()));
        engine.add_strategy(Box::new(day_trading.clone()));
        engine.add_strategy(Box::new(dollar_cost_averaging.clone()));
        engine.add_strategy(Box::new(scalping.clone()));
        engine.add_strategy(Box::new(breakout.clone()));
        engine.add_strategy(Box::new(news_sentiment.clone()));
        engine.add_strategy(Box::new(volatility.clone()));
        engine.add_strategy(Box::new(blockchain_performance.clone()));
        engine.add_strategy(Box::new(target_price.clone()));
        engine.add_strategy(Box::new(volume_strategy.clone()));

        let prices = vec![10.0, 12.0, 15.0, 14.0, 13.0, 12.0];

        for price in &prices {
            trend_following.update_price(*price);
            moving_average_cross.update_price(*price);
            bollinger_bands.update_price(*price);
            mean_reversion.update_price(*price);
            momentum.update_price(*price);
            swing_trading.update_price(*price);
            day_trading.update_price(*price);
            dollar_cost_averaging.update_price(*price);
            scalping.update_price(*price);

            breakout.update_price(*price);
            news_sentiment.update_sentiment(0.5);
            volatility.update_price(*price);
            blockchain_performance.update_performance(0.5);
            volume_strategy.update_volume(1000.0);
        }

        let results = engine.backtest(prices.clone());
        let indicators = engine.calculate_indicators(prices.clone());

        println!("Backtest Results: {:?}", results);
        println!("Indicators: {:?}", indicators);
    }

    #[test]
    fn test_strategy_performance() {
        let mut engine = StrategyEngine::new();

        let mut trend_following = TrendFollowing::new(3, 6);
        let mut moving_average_cross = MovingAverageCross::new(3, 6);

        engine.add_strategy(Box::new(trend_following.clone()));
        engine.add_strategy(Box::new(moving_average_cross.clone()));

        let prices = vec![10.0, 12.0, 15.0, 14.0, 13.0, 12.0];

        for price in &prices {
            trend_following.update_price(*price);
            moving_average_cross.update_price(*price);
        }

        let results = engine.backtest(prices.clone());
        let indicators = engine.calculate_indicators(prices.clone());

        assert!(results.len() == 2);
        assert!(indicators.len() == 2);
    }

    #[test]
    fn test_empty_strategy_engine() {
        let engine = StrategyEngine::new();

        let prices = vec![10.0, 12.0, 15.0, 14.0, 13.0, 12.0];

        let results = engine.backtest(prices.clone());
        let indicators = engine.calculate_indicators(prices.clone());

        assert!(results.is_empty());
        assert!(indicators.is_empty());
    }
}