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#[derive(Debug)]
18pub struct TradingResult {
19 pub mode: TradingMode,
21
22 pub portfolio_value: f64,
24
25 pub pnl: f64,
27
28 pub trading_pnl: Option<f64>,
30
31 pub funding_pnl: Option<f64>,
33
34 pub trade_count: usize,
36
37 pub win_rate: f64,
39
40 pub timestamp: DateTime<FixedOffset>,
42
43 pub mode_specific_data: HashMap<String, String>,
45}
46
47
48
49pub struct TradingModeManager {
51 current_mode: TradingMode,
53
54 config: TradingConfig,
56
57 backtest_data: Option<Arc<HyperliquidData>>,
59
60 backtest_instance: Option<Arc<Mutex<HyperliquidBacktest>>>,
62
63 paper_trading_engine: Option<Arc<Mutex<()>>>, live_trading_engine: Option<Arc<Mutex<()>>>, positions: HashMap<String, Position>,
71
72 trading_history: Vec<OrderResult>,
74}
75
76impl TradingModeManager {
77 pub fn new(mode: TradingMode, config: TradingConfig) -> Self {
79 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 pub fn current_mode(&self) -> TradingMode {
100 self.current_mode
101 }
102
103 pub fn config(&self) -> &TradingConfig {
105 &self.config
106 }
107
108 pub fn config_mut(&mut self) -> &mut TradingConfig {
110 &mut self.config
111 }
112
113 pub fn update_config(&mut self, config: TradingConfig) -> std::result::Result<(), TradingModeError> {
115 config.validate_for_mode(self.current_mode)?;
117
118 info!("Updating trading configuration");
119 self.config = config;
120
121 Ok(())
122 }
123
124 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 self.config.validate_for_mode(mode)?;
133
134 match (self.current_mode, mode) {
136 (TradingMode::Backtest, TradingMode::PaperTrade) => {
138 info!("Switching from {} mode to {} mode", self.current_mode, mode);
139 },
141
142 (TradingMode::PaperTrade, TradingMode::LiveTrade) => {
144 info!("Switching from {} mode to {} mode", self.current_mode, mode);
145 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 },
162
163 (TradingMode::LiveTrade, TradingMode::PaperTrade) => {
165 info!("Switching from {} mode to {} mode", self.current_mode, mode);
166 },
168
169 (_, TradingMode::Backtest) => {
171 info!("Switching from {} mode to {} mode", self.current_mode, mode);
172 },
174
175 (TradingMode::Backtest, TradingMode::LiveTrade) => {
177 return Err(TradingModeError::UnsupportedModeTransition {
178 from: self.current_mode,
179 to: mode,
180 });
181 },
182
183 _ => {
185 info!("Switching from {} mode to {} mode", self.current_mode, mode);
186 },
187 }
188
189 self.current_mode = mode;
191
192 match mode {
194 TradingMode::Backtest => {
195 },
197 TradingMode::PaperTrade => {
198 if self.paper_trading_engine.is_none() {
200 self.paper_trading_engine = Some(Arc::new(Mutex::new(())));
202 }
203 },
204 TradingMode::LiveTrade => {
205 if self.live_trading_engine.is_none() {
207 self.live_trading_engine = Some(Arc::new(Mutex::new(())));
209 }
210 },
211 }
212
213 Ok(())
214 }
215
216 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 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 fn execute_backtest_strategy<S>(&mut self, strategy: S) -> std::result::Result<TradingResult, TradingModeError>
243 where
244 S: HyperliquidStrategy + 'static,
245 {
246 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 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 backtest.calculate_with_funding()?;
267
268 let report = backtest.enhanced_report()?;
270
271 self.backtest_instance = Some(Arc::new(Mutex::new(backtest)));
273
274 let result = TradingResult {
276 mode: TradingMode::Backtest,
277 portfolio_value: report.final_equity,
278 pnl: report.total_return * self.config.initial_balance, trading_pnl: None, funding_pnl: None, 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 pub fn get_positions(&self) -> &HashMap<String, Position> {
298 &self.positions
299 }
300
301 pub fn get_trading_history(&self) -> &[OrderResult] {
303 &self.trading_history
304 }
305}