hyperliquid_backtest/
trading_mode_impl.rs

1use std::collections::HashMap;
2use std::sync::{Arc, Mutex};
3use chrono::{DateTime, FixedOffset};
4use tracing::{info, warn};
5
6use crate::backtest::HyperliquidBacktest;
7use crate::data::HyperliquidData;
8use crate::strategies::HyperliquidStrategy;
9use crate::trading_mode::{
10    TradingMode, TradingModeError, TradingConfig
11};
12use crate::unified_data::{
13    Position, OrderResult
14};
15
16/// Result of strategy execution
17#[derive(Debug)]
18pub struct TradingResult {
19    /// Trading mode used for execution
20    pub mode: TradingMode,
21    
22    /// Final portfolio value
23    pub portfolio_value: f64,
24    
25    /// Profit and loss
26    pub pnl: f64,
27    
28    /// Trading PnL (excluding funding)
29    pub trading_pnl: Option<f64>,
30    
31    /// Funding PnL
32    pub funding_pnl: Option<f64>,
33    
34    /// Number of trades executed
35    pub trade_count: usize,
36    
37    /// Win rate (percentage)
38    pub win_rate: f64,
39    
40    /// Execution timestamp
41    pub timestamp: DateTime<FixedOffset>,
42    
43    /// Mode-specific result data
44    pub mode_specific_data: HashMap<String, String>,
45}
46
47
48
49/// Trading mode manager for seamless transitions between trading modes
50pub struct TradingModeManager {
51    /// Current trading mode
52    current_mode: TradingMode,
53    
54    /// Trading configuration
55    config: TradingConfig,
56    
57    /// Backtest data (for backtest mode)
58    backtest_data: Option<Arc<HyperliquidData>>,
59    
60    /// Backtest instance (for backtest mode)
61    backtest_instance: Option<Arc<Mutex<HyperliquidBacktest>>>,
62    
63    /// Paper trading engine (for paper trading mode)
64    paper_trading_engine: Option<Arc<Mutex<()>>>, // Placeholder for future implementation
65    
66    /// Live trading engine (for live trading mode)
67    live_trading_engine: Option<Arc<Mutex<()>>>, // Placeholder for future implementation
68    
69    /// Current positions across all modes
70    positions: HashMap<String, Position>,
71    
72    /// Trading history
73    trading_history: Vec<OrderResult>,
74}
75
76impl TradingModeManager {
77    /// Create a new trading mode manager with the specified mode and configuration
78    pub fn new(mode: TradingMode, config: TradingConfig) -> Self {
79        // Validate configuration for the initial mode
80        if let Err(e) = config.validate_for_mode(mode) {
81            warn!("Invalid configuration for {}: {}", mode, e);
82        }
83        
84        info!("Initializing trading mode manager in {} mode", mode);
85        
86        Self {
87            current_mode: mode,
88            config,
89            backtest_data: None,
90            backtest_instance: None,
91            paper_trading_engine: None,
92            live_trading_engine: None,
93            positions: HashMap::new(),
94            trading_history: Vec::new(),
95        }
96    }
97    
98    /// Get the current trading mode
99    pub fn current_mode(&self) -> TradingMode {
100        self.current_mode
101    }
102    
103    /// Get the current trading configuration
104    pub fn config(&self) -> &TradingConfig {
105        &self.config
106    }
107    
108    /// Get a mutable reference to the trading configuration
109    pub fn config_mut(&mut self) -> &mut TradingConfig {
110        &mut self.config
111    }
112    
113    /// Update the trading configuration
114    pub fn update_config(&mut self, config: TradingConfig) -> std::result::Result<(), TradingModeError> {
115        // Validate the new configuration for the current mode
116        config.validate_for_mode(self.current_mode)?;
117        
118        info!("Updating trading configuration");
119        self.config = config;
120        
121        Ok(())
122    }
123    
124    /// Switch to a different trading mode
125    pub fn switch_mode(&mut self, mode: TradingMode) -> std::result::Result<(), TradingModeError> {
126        if self.current_mode == mode {
127            info!("Already in {} mode, no switch needed", mode);
128            return Ok(());
129        }
130        
131        // Validate configuration for the new mode
132        self.config.validate_for_mode(mode)?;
133        
134        // Check if the mode transition is supported
135        match (self.current_mode, mode) {
136            // Allow transitions from backtest to paper trade
137            (TradingMode::Backtest, TradingMode::PaperTrade) => {
138                info!("Switching from {} mode to {} mode", self.current_mode, mode);
139                // Additional logic for transitioning from backtest to paper trade
140            },
141            
142            // Allow transitions from paper trade to live trade
143            (TradingMode::PaperTrade, TradingMode::LiveTrade) => {
144                info!("Switching from {} mode to {} mode", self.current_mode, mode);
145                // Additional safety checks for live trading
146                if self.config.risk_config.is_none() {
147                    return Err(TradingModeError::InvalidConfiguration {
148                        mode,
149                        message: "Risk configuration is required for live trading".to_string(),
150                    });
151                }
152                
153                if self.config.api_config.is_none() {
154                    return Err(TradingModeError::InvalidConfiguration {
155                        mode,
156                        message: "API configuration is required for live trading".to_string(),
157                    });
158                }
159                
160                // Additional logic for transitioning from paper trade to live trade
161            },
162            
163            // Allow transitions from live trade to paper trade (safety feature)
164            (TradingMode::LiveTrade, TradingMode::PaperTrade) => {
165                info!("Switching from {} mode to {} mode", self.current_mode, mode);
166                // Additional logic for transitioning from live trade to paper trade
167            },
168            
169            // Allow transitions to backtest mode from any mode
170            (_, TradingMode::Backtest) => {
171                info!("Switching from {} mode to {} mode", self.current_mode, mode);
172                // Additional logic for transitioning to backtest mode
173            },
174            
175            // Disallow direct transition from backtest to live trade
176            (TradingMode::Backtest, TradingMode::LiveTrade) => {
177                return Err(TradingModeError::UnsupportedModeTransition {
178                    from: self.current_mode,
179                    to: mode,
180                });
181            },
182            
183            // Handle any other transitions
184            _ => {
185                info!("Switching from {} mode to {} mode", self.current_mode, mode);
186            },
187        }
188        
189        // Update the current mode
190        self.current_mode = mode;
191        
192        // Initialize mode-specific components if needed
193        match mode {
194            TradingMode::Backtest => {
195                // Initialize backtest components if needed
196            },
197            TradingMode::PaperTrade => {
198                // Initialize paper trading components if needed
199                if self.paper_trading_engine.is_none() {
200                    // Placeholder for future implementation
201                    self.paper_trading_engine = Some(Arc::new(Mutex::new(())));
202                }
203            },
204            TradingMode::LiveTrade => {
205                // Initialize live trading components if needed
206                if self.live_trading_engine.is_none() {
207                    // Placeholder for future implementation
208                    self.live_trading_engine = Some(Arc::new(Mutex::new(())));
209                }
210            },
211        }
212        
213        Ok(())
214    }
215    
216    /// Set historical data for backtesting
217    pub fn set_backtest_data(&mut self, data: HyperliquidData) -> std::result::Result<(), TradingModeError> {
218        info!("Setting backtest data for {}", data.symbol);
219        self.backtest_data = Some(Arc::new(data));
220        Ok(())
221    }
222    
223    /// Execute a strategy in the current trading mode
224    pub fn execute_strategy<S>(&mut self, strategy: S) -> std::result::Result<TradingResult, TradingModeError>
225    where
226        S: HyperliquidStrategy + 'static,
227    {
228        match self.current_mode {
229            TradingMode::Backtest => {
230                self.execute_backtest_strategy(strategy)
231            },
232            TradingMode::PaperTrade => {
233                Err(TradingModeError::NotImplemented("Paper trading execution".to_string()))
234            },
235            TradingMode::LiveTrade => {
236                Err(TradingModeError::NotImplemented("Live trading execution".to_string()))
237            },
238        }
239    }
240    
241    /// Execute a strategy in backtest mode
242    fn execute_backtest_strategy<S>(&mut self, strategy: S) -> std::result::Result<TradingResult, TradingModeError>
243    where
244        S: HyperliquidStrategy + 'static,
245    {
246        // Check if we have backtest data
247        let data = match &self.backtest_data {
248            Some(data) => data.clone(),
249            None => {
250                return Err(TradingModeError::StrategyExecutionError {
251                    mode: TradingMode::Backtest,
252                    message: "No backtest data available".to_string(),
253                });
254            }
255        };
256        
257        // Create a new backtest instance
258        let mut backtest = HyperliquidBacktest::new(
259            (*data).clone(),
260            "Custom Strategy".to_string(),
261            self.config.initial_balance,
262            crate::backtest::HyperliquidCommission::default(),
263        );
264        
265        // Run the backtest with funding calculations
266        backtest.calculate_with_funding()?;
267        
268        // Get the backtest report
269        let report = backtest.enhanced_report()?;
270        
271        // Store the backtest instance for later reference
272        self.backtest_instance = Some(Arc::new(Mutex::new(backtest)));
273        
274        // Create a trading result from the backtest report
275        let result = TradingResult {
276            mode: TradingMode::Backtest,
277            portfolio_value: report.final_equity,
278            pnl: report.total_return * self.config.initial_balance, // Calculate PnL from return
279            trading_pnl: None, // Will be calculated from report if available
280            funding_pnl: None, // Will be calculated from report if available
281            trade_count: report.trade_count,
282            win_rate: report.win_rate,
283            timestamp: chrono::Utc::now().with_timezone(&chrono::FixedOffset::east_opt(0).unwrap()),
284            mode_specific_data: {
285                let mut data = HashMap::new();
286                data.insert("sharpe_ratio".to_string(), report.sharpe_ratio.to_string());
287                data.insert("max_drawdown".to_string(), report.max_drawdown.to_string());
288                data.insert("total_return".to_string(), report.total_return.to_string());
289                data
290            },
291        };
292        
293        Ok(result)
294    }
295    
296    /// Get current positions
297    pub fn get_positions(&self) -> &HashMap<String, Position> {
298        &self.positions
299    }
300    
301    /// Get trading history
302    pub fn get_trading_history(&self) -> &[OrderResult] {
303        &self.trading_history
304    }
305}