use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use tracing::{warn, error};
use crate::errors::HyperliquidBacktestError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TradingMode {
Backtest,
PaperTrade,
LiveTrade,
}
impl std::fmt::Display for TradingMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TradingMode::Backtest => write!(f, "Backtest"),
TradingMode::PaperTrade => write!(f, "Paper Trading"),
TradingMode::LiveTrade => write!(f, "Live Trading"),
}
}
}
#[derive(Debug, Error)]
pub enum TradingModeError {
#[error("Unsupported trading mode transition from {from} to {to}")]
UnsupportedModeTransition {
from: TradingMode,
to: TradingMode,
},
#[error("Missing configuration for {0} mode")]
MissingConfiguration(TradingMode),
#[error("Strategy execution error in {mode} mode: {message}")]
StrategyExecutionError {
mode: TradingMode,
message: String,
},
#[error("Invalid configuration for {mode} mode: {message}")]
InvalidConfiguration {
mode: TradingMode,
message: String,
},
#[error("Backtesting error: {0}")]
BacktestError(#[from] HyperliquidBacktestError),
#[error("Feature not implemented: {0}")]
NotImplemented(String),
}
#[derive(Debug, Clone)]
pub struct TradingConfig {
pub initial_balance: f64,
pub risk_config: Option<RiskConfig>,
pub slippage_config: Option<SlippageConfig>,
pub api_config: Option<ApiConfig>,
pub parameters: HashMap<String, String>,
}
impl TradingConfig {
pub fn new(initial_balance: f64) -> Self {
Self {
initial_balance,
risk_config: None,
slippage_config: None,
api_config: None,
parameters: HashMap::new(),
}
}
pub fn with_risk_config(mut self, risk_config: RiskConfig) -> Self {
self.risk_config = Some(risk_config);
self
}
pub fn with_slippage_config(mut self, slippage_config: SlippageConfig) -> Self {
self.slippage_config = Some(slippage_config);
self
}
pub fn with_api_config(mut self, api_config: ApiConfig) -> Self {
self.api_config = Some(api_config);
self
}
pub fn with_parameter(mut self, key: &str, value: &str) -> Self {
self.parameters.insert(key.to_string(), value.to_string());
self
}
pub fn validate_for_mode(&self, mode: TradingMode) -> std::result::Result<(), TradingModeError> {
match mode {
TradingMode::Backtest => {
if self.initial_balance <= 0.0 {
return Err(TradingModeError::InvalidConfiguration {
mode,
message: "Initial balance must be positive".to_string(),
});
}
},
TradingMode::PaperTrade => {
if self.initial_balance <= 0.0 {
return Err(TradingModeError::InvalidConfiguration {
mode,
message: "Initial balance must be positive".to_string(),
});
}
if self.slippage_config.is_none() {
warn!("No slippage configuration provided for paper trading mode. Using default values.");
}
},
TradingMode::LiveTrade => {
if self.initial_balance <= 0.0 {
return Err(TradingModeError::InvalidConfiguration {
mode,
message: "Initial balance must be positive".to_string(),
});
}
if self.risk_config.is_none() {
return Err(TradingModeError::InvalidConfiguration {
mode,
message: "Risk configuration is required for live trading".to_string(),
});
}
if self.api_config.is_none() {
return Err(TradingModeError::InvalidConfiguration {
mode,
message: "API configuration is required for live trading".to_string(),
});
}
},
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct RiskConfig {
pub max_position_size_pct: f64,
pub max_daily_loss_pct: f64,
pub stop_loss_pct: f64,
pub take_profit_pct: f64,
pub max_leverage: f64,
pub max_concentration_pct: f64,
pub max_correlation_pct: f64,
pub max_portfolio_volatility_pct: f64,
pub volatility_sizing_factor: f64,
pub max_drawdown_pct: f64,
}
impl Default for RiskConfig {
fn default() -> Self {
Self {
max_position_size_pct: 0.1, max_daily_loss_pct: 0.02, stop_loss_pct: 0.05, take_profit_pct: 0.1, max_leverage: 3.0, max_concentration_pct: 0.25, max_correlation_pct: 0.7, max_portfolio_volatility_pct: 0.2, volatility_sizing_factor: 0.5, max_drawdown_pct: 0.15, }
}
}
#[derive(Debug, Clone)]
pub struct SlippageConfig {
pub base_slippage_pct: f64,
pub volume_impact_factor: f64,
pub volatility_impact_factor: f64,
pub random_slippage_max_pct: f64,
pub simulated_latency_ms: u64,
}
impl Default for SlippageConfig {
fn default() -> Self {
Self {
base_slippage_pct: 0.0005, volume_impact_factor: 0.1, volatility_impact_factor: 0.2, random_slippage_max_pct: 0.001, simulated_latency_ms: 500, }
}
}
#[derive(Debug, Clone)]
pub struct ApiConfig {
pub api_key: String,
pub api_secret: String,
pub endpoint: String,
pub use_testnet: bool,
pub timeout_ms: u64,
}