hyperliquid_backtest/
trading_mode.rs

1//! # Trading Mode Management
2//!
3//! This module provides functionality for managing different trading modes (backtest, paper trading, live trading)
4//! and seamlessly transitioning between them while maintaining consistent strategy execution.
5//!
6//! ## Features
7//!
8//! - Unified trading mode interface across backtest, paper trading, and live trading
9//! - Configuration management for different trading modes
10//! - Seamless strategy execution across all modes
11//! - Mode-specific configuration and safety checks
12
13use std::collections::HashMap;
14use serde::{Deserialize, Serialize};
15use thiserror::Error;
16use tracing::{warn, error};
17
18use crate::errors::HyperliquidBacktestError;
19
20/// Represents the different trading modes available in the system.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22pub enum TradingMode {
23    /// Backtesting mode using historical data
24    Backtest,
25    
26    /// Paper trading mode using real-time data but simulated execution
27    PaperTrade,
28    
29    /// Live trading mode using real-time data and real order execution
30    LiveTrade,
31}
32
33impl std::fmt::Display for TradingMode {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        match self {
36            TradingMode::Backtest => write!(f, "Backtest"),
37            TradingMode::PaperTrade => write!(f, "Paper Trading"),
38            TradingMode::LiveTrade => write!(f, "Live Trading"),
39        }
40    }
41}
42
43/// Error types specific to trading mode operations
44#[derive(Debug, Error)]
45pub enum TradingModeError {
46    /// Error when switching to an unsupported mode
47    #[error("Unsupported trading mode transition from {from} to {to}")]
48    UnsupportedModeTransition {
49        from: TradingMode,
50        to: TradingMode,
51    },
52    
53    /// Error when configuration is missing for a specific mode
54    #[error("Missing configuration for {0} mode")]
55    MissingConfiguration(TradingMode),
56    
57    /// Error when executing a strategy
58    #[error("Strategy execution error in {mode} mode: {message}")]
59    StrategyExecutionError {
60        mode: TradingMode,
61        message: String,
62    },
63    
64    /// Error when validating configuration
65    #[error("Invalid configuration for {mode} mode: {message}")]
66    InvalidConfiguration {
67        mode: TradingMode,
68        message: String,
69    },
70    
71    /// Wrapper for backtesting errors
72    #[error("Backtesting error: {0}")]
73    BacktestError(#[from] HyperliquidBacktestError),
74    
75    /// Error when a feature is not yet implemented
76    #[error("Feature not implemented: {0}")]
77    NotImplemented(String),
78}
79
80/// Configuration for trading modes
81#[derive(Debug, Clone)]
82pub struct TradingConfig {
83    /// Initial balance for trading
84    pub initial_balance: f64,
85    
86    /// Risk management configuration
87    pub risk_config: Option<RiskConfig>,
88    
89    /// Slippage configuration for paper trading
90    pub slippage_config: Option<SlippageConfig>,
91    
92    /// API configuration for live trading
93    pub api_config: Option<ApiConfig>,
94    
95    /// Additional mode-specific configuration parameters
96    pub parameters: HashMap<String, String>,
97}
98
99impl TradingConfig {
100    /// Create a new trading configuration with the specified initial balance
101    pub fn new(initial_balance: f64) -> Self {
102        Self {
103            initial_balance,
104            risk_config: None,
105            slippage_config: None,
106            api_config: None,
107            parameters: HashMap::new(),
108        }
109    }
110    
111    /// Add a risk configuration
112    pub fn with_risk_config(mut self, risk_config: RiskConfig) -> Self {
113        self.risk_config = Some(risk_config);
114        self
115    }
116    
117    /// Add a slippage configuration for paper trading
118    pub fn with_slippage_config(mut self, slippage_config: SlippageConfig) -> Self {
119        self.slippage_config = Some(slippage_config);
120        self
121    }
122    
123    /// Add an API configuration for live trading
124    pub fn with_api_config(mut self, api_config: ApiConfig) -> Self {
125        self.api_config = Some(api_config);
126        self
127    }
128    
129    /// Add a custom parameter
130    pub fn with_parameter(mut self, key: &str, value: &str) -> Self {
131        self.parameters.insert(key.to_string(), value.to_string());
132        self
133    }
134    
135    /// Validate the configuration for a specific trading mode
136    pub fn validate_for_mode(&self, mode: TradingMode) -> std::result::Result<(), TradingModeError> {
137        match mode {
138            TradingMode::Backtest => {
139                // Backtesting mode has minimal requirements
140                if self.initial_balance <= 0.0 {
141                    return Err(TradingModeError::InvalidConfiguration {
142                        mode,
143                        message: "Initial balance must be positive".to_string(),
144                    });
145                }
146            },
147            TradingMode::PaperTrade => {
148                // Paper trading requires initial balance and should have slippage config
149                if self.initial_balance <= 0.0 {
150                    return Err(TradingModeError::InvalidConfiguration {
151                        mode,
152                        message: "Initial balance must be positive".to_string(),
153                    });
154                }
155                
156                if self.slippage_config.is_none() {
157                    warn!("No slippage configuration provided for paper trading mode. Using default values.");
158                }
159            },
160            TradingMode::LiveTrade => {
161                // Live trading requires initial balance, risk config, and API config
162                if self.initial_balance <= 0.0 {
163                    return Err(TradingModeError::InvalidConfiguration {
164                        mode,
165                        message: "Initial balance must be positive".to_string(),
166                    });
167                }
168                
169                if self.risk_config.is_none() {
170                    return Err(TradingModeError::InvalidConfiguration {
171                        mode,
172                        message: "Risk configuration is required for live trading".to_string(),
173                    });
174                }
175                
176                if self.api_config.is_none() {
177                    return Err(TradingModeError::InvalidConfiguration {
178                        mode,
179                        message: "API configuration is required for live trading".to_string(),
180                    });
181                }
182            },
183        }
184        
185        Ok(())
186    }
187}
188
189/// Risk management configuration
190#[derive(Debug, Clone)]
191pub struct RiskConfig {
192    /// Maximum position size as a percentage of portfolio value
193    pub max_position_size_pct: f64,
194    
195    /// Maximum daily loss as a percentage of portfolio value
196    pub max_daily_loss_pct: f64,
197    
198    /// Stop loss percentage for positions
199    pub stop_loss_pct: f64,
200    
201    /// Take profit percentage for positions
202    pub take_profit_pct: f64,
203    
204    /// Maximum leverage allowed
205    pub max_leverage: f64,
206    
207    /// Maximum portfolio concentration in a single asset class (percentage)
208    pub max_concentration_pct: f64,
209    
210    /// Maximum correlation between positions (0.0 to 1.0)
211    pub max_correlation_pct: f64,
212    
213    /// Maximum portfolio volatility (percentage)
214    pub max_portfolio_volatility_pct: f64,
215    
216    /// Volatility-based position sizing factor (0.0 to 1.0)
217    pub volatility_sizing_factor: f64,
218    
219    /// Maximum drawdown before emergency stop (percentage)
220    pub max_drawdown_pct: f64,
221}
222
223impl Default for RiskConfig {
224    fn default() -> Self {
225        Self {
226            max_position_size_pct: 0.1,  // 10% of portfolio
227            max_daily_loss_pct: 0.02,    // 2% max daily loss
228            stop_loss_pct: 0.05,         // 5% stop loss
229            take_profit_pct: 0.1,        // 10% take profit
230            max_leverage: 3.0,           // 3x max leverage
231            max_concentration_pct: 0.25, // 25% max concentration in one asset class
232            max_correlation_pct: 0.7, // 0.7 maximum correlation between positions
233            max_portfolio_volatility_pct: 0.2, // 20% maximum portfolio volatility
234            volatility_sizing_factor: 0.5, // 50% volatility-based position sizing
235            max_drawdown_pct: 0.15,     // 15% maximum drawdown before emergency stop
236        }
237    }
238}
239
240/// Slippage simulation configuration for paper trading
241#[derive(Debug, Clone)]
242pub struct SlippageConfig {
243    /// Base slippage as a percentage
244    pub base_slippage_pct: f64,
245    
246    /// Volume-based slippage factor
247    pub volume_impact_factor: f64,
248    
249    /// Volatility-based slippage factor
250    pub volatility_impact_factor: f64,
251    
252    /// Random slippage component maximum (percentage)
253    pub random_slippage_max_pct: f64,
254    
255    /// Simulated latency in milliseconds
256    pub simulated_latency_ms: u64,
257}
258
259impl Default for SlippageConfig {
260    fn default() -> Self {
261        Self {
262            base_slippage_pct: 0.0005,   // 0.05% base slippage
263            volume_impact_factor: 0.1,   // Volume impact factor
264            volatility_impact_factor: 0.2, // Volatility impact factor
265            random_slippage_max_pct: 0.001, // 0.1% max random component
266            simulated_latency_ms: 500,   // 500ms simulated latency
267        }
268    }
269}
270
271/// API configuration for live trading
272#[derive(Debug, Clone)]
273pub struct ApiConfig {
274    /// API key for authentication
275    pub api_key: String,
276    
277    /// API secret for authentication
278    pub api_secret: String,
279    
280    /// API endpoint URL
281    pub endpoint: String,
282    
283    /// Whether to use testnet
284    pub use_testnet: bool,
285    
286    /// Timeout for API requests in milliseconds
287    pub timeout_ms: u64,
288}
289
290// Additional types and implementations will be in trading_mode_impl.rs