use std::collections::HashMap;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use chrono::{DateTime, FixedOffset, Utc};
use ethers::signers::LocalWallet;
use hyperliquid_backtest::prelude::*;
use hyperliquid_backtest::trading_mode::{
TradingMode, TradingModeManager, TradingConfig, RiskConfig, SlippageConfig, ApiConfig
};
use hyperliquid_backtest::unified_data::{
Position, OrderRequest, OrderResult, MarketData,
OrderSide, OrderType, TimeInForce, OrderStatus,
TradingStrategy, Signal, SignalDirection, FundingPayment
};
use hyperliquid_backtest::logging::init_logger;
/ Enhanced SMA Crossover Strategy with Funding Rate Awareness
struct EnhancedSmaStrategy {
name: String,
symbol: String,
short_period: usize,
long_period: usize,
prices: Vec<f64>,
funding_rates: Vec<f64>,
funding_threshold: f64,
funding_weight: f64,
position_size: f64,
current_position: f64,
signals: HashMap<String, Signal>,
parameters: HashMap<String, String>,
}
impl EnhancedSmaStrategy {
fn new(
symbol: &str,
short_period: usize,
long_period: usize,
funding_threshold: f64,
funding_weight: f64,
position_size: f64
) -> Self {
Self {
name: format!("Enhanced SMA {}/{}", short_period, long_period),
symbol: symbol.to_string(),
short_period,
long_period,
prices: Vec::new(),
funding_rates: Vec::new(),
funding_threshold,
funding_weight,
position_size,
current_position: 0.0,
signals: HashMap::new(),
parameters: {
let mut params = HashMap::new();
params.insert("short_period".to_string(), short_period.to_string());
params.insert("long_period".to_string(), long_period.to_string());
params.insert("funding_threshold".to_string(), funding_threshold.to_string());
params.insert("funding_weight".to_string(), funding_weight.to_string());
params.insert("position_size".to_string(), position_size.to_string());
params
},
}
}
fn calculate_sma(&self, period: usize) -> Option<f64> {
if self.prices.len() < period {
return None;
}
let sum: f64 = self.prices.iter().rev().take(period).sum();
Some(sum / period as f64)
}
fn get_current_funding_bias(&self) -> f64 {
if self.funding_rates.is_empty() {
return 0.0;
}
let latest_funding = *self.funding_rates.last().unwrap();
if latest_funding.abs() < self.funding_threshold {
return 0.0; }
-latest_funding.signum() * self.funding_weight
}
fn save_state(&self) -> HashMap<String, String> {
let mut state = HashMap::new();
for (key, value) in &self.parameters {
state.insert(format!("param_{}", key), value.clone());
}
state.insert("current_position".to_string(), self.current_position.to_string());
for (i, price) in self.prices.iter().rev().take(5).enumerate() {
state.insert(format!("price_{}", i), price.to_string());
}
for (i, rate) in self.funding_rates.iter().rev().take(3).enumerate() {
state.insert(format!("funding_{}", i), rate.to_string());
}
state
}
fn load_state(&mut self, state: &HashMap<String, String>) {
if let Some(pos) = state.get("current_position") {
if let Ok(pos_val) = pos.parse::<f64>() {
self.current_position = pos_val;
}
}
for (key, value) in state {
if key.starts_with("param_") {
let param_name = key.strip_prefix("param_").unwrap();
self.parameters.insert(param_name.to_string(), value.clone());
}
}
}
}
impl Tra
dingStrategy for EnhancedSmaStrategy {
fn name(&self) -> &str {
&self.name
}
fn on_market_data(&mut self, data: &MarketData) -> Result<Vec<OrderRequest>, String> {
if data.symbol != self.symbol {
return Ok(Vec::new());
}
self.prices.push(data.price);
if let Some(funding_rate) = data.funding_rate {
self.funding_rates.push(funding_rate);
}
let max_period = self.short_period.max(self.long_period);
if self.prices.len() > max_period * 2 {
self.prices.remove(0);
}
if self.funding_rates.len() > 10 {
self.funding_rates.remove(0);
}
let short_sma = self.calculate_sma(self.short_period);
let long_sma = self.calculate_sma(self.long_period);
let mut orders = Vec::new();
if let (Some(short), Some(long)) = (short_sma, long_sma) {
let now = Utc::now().with_timezone(&FixedOffset::east(0));
let funding_bias = self.get_current_funding_bias();
let signal_direction = if short > long && funding_bias >= 0.0 {
SignalDirection::Buy
} else if short < long && funding_bias <= 0.0 {
SignalDirection::Sell
} else if short > long && funding_bias < 0.0 {
if funding_bias.abs() > 0.7 {
SignalDirection::Sell
} else {
SignalDirection::Buy
}
} else if short < long && funding_bias > 0.0 {
if funding_bias.abs() > 0.7 {
SignalDirection::Buy
} else {
SignalDirection::Sell
}
} else {
SignalDirection::Neutral
};
let signal = Signal {
symbol: self.symbol.clone(),
direction: signal_direction,
strength: 1.0 - funding_bias.abs() * 0.3, timestamp: now,
metadata: {
let mut metadata = HashMap::new();
metadata.insert("short_sma".to_string(), short.to_string());
metadata.insert("long_sma".to_string(), long.to_string());
metadata.insert("funding_bias".to_string(), funding_bias.to_string());
metadata
},
};
let previous_signal = self.signals.get(&self.symbol);
let signal_changed = match previous_signal {
Some(prev) => prev.direction != signal.direction,
None => signal.direction != SignalDirection::Neutral,
};
self.signals.insert(self.symbol.clone(), signal.clone());
if signal_changed {
match signal.direction {
SignalDirection::Buy => {
if self.current_position < 0.0 {
orders.push(OrderRequest {
symbol: self.symbol.clone(),
side: OrderSide::Buy,
order_type: OrderType::Market,
quantity: self.current_position.abs(),
price: None,
reduce_only: true,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("close_short_{}", now.timestamp())),
metadata: HashMap::new(),
});
}
orders.push(OrderRequest {
symbol: self.symbol.clone(),
side: OrderSide::Buy,
order_type: OrderType::Market,
quantity: self.position_size * signal.strength,
price: None,
reduce_only: false,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("open_long_{}", now.timestamp())),
metadata: HashMap::new(),
});
},
SignalDirection::Sell => {
if self.current_position > 0.0 {
orders.push(OrderRequest {
symbol: self.symbol.clone(),
side: OrderSide::Sell,
order_type: OrderType::Market,
quantity: self.current_position,
price: None,
reduce_only: true,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("close_long_{}", now.timestamp())),
metadata: HashMap::new(),
});
}
orders.push(OrderRequest {
symbol: self.symbol.clone(),
side: OrderSide::Sell,
order_type: OrderType::Market,
quantity: self.position_size * signal.strength,
price: None,
reduce_only: false,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("open_short_{}", now.timestamp())),
metadata: HashMap::new(),
});
},
SignalDirection::Neutral => {
if self.current_position > 0.0 {
orders.push(OrderRequest {
symbol: self.symbol.clone(),
side: OrderSide::Sell,
order_type: OrderType::Market,
quantity: self.current_position,
price: None,
reduce_only: true,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("close_position_{}", now.timestamp())),
metadata: HashMap::new(),
});
} else if self.current_position < 0.0 {
orders.push(OrderRequest {
symbol: self.symbol.clone(),
side: OrderSide::Buy,
order_type: OrderType::Market,
quantity: self.current_position.abs(),
price: None,
reduce_only: true,
time_in_force: TimeInForce::ImmediateOrCancel,
client_order_id: Some(format!("close_position_{}", now.timestamp())),
metadata: HashMap::new(),
});
}
},
_ => {}
}
}
}
Ok(orders)
}
fn on_order_fill(&mut self, fill: &OrderResult) -> Result<(), String> {
if fill.symbol != self.symbol {
return Ok(());
}
match fill.side {
OrderSide::Buy => {
self.current_position += fill.filled_quantity;
},
OrderSide::Sell => {
self.current_position -= fill.filled_quantity;
},
}
Ok(())
}
fn on_funding_payment(&mut self, payment: &FundingPayment) -> Result<(), String> {
println!("Funding payment received: {} {} (rate: {})",
payment.symbol, payment.amount, payment.rate);
Ok(())
}
fn get_current_signals(&self) -> HashMap<String, Signal> {
self.signals.clone()
}
} Run backtest phase of the strategy
async fn run_backtest_phase(symbol: &str) -> Result<(EnhancedSmaStrategy, FundingReport), Box<dyn std::error::Error>> {
println!("\n📊 Phase 1: Backtesting");
println!("====================");
println!("Fetching historical data for {}...", symbol);
let start_time = chrono::Utc::now() - chrono::Duration::days(30);
let end_time = chrono::Utc::now();
let data = HyperliquidData::fetch(
symbol,
"1h",
start_time.timestamp() as u64,
end_time.timestamp() as u64
).await?;
println!("Fetched {} data points", data.datetime.len());
let strategy = EnhancedSmaStrategy::new(
symbol, 12, 26, 0.0001, 0.5, 0.1 );
println!("Running backtest...");
let mut backtest = HyperliquidBacktest::new(
data,
Box::new(BacktestStrategyAdapter::new(Box::new(strategy.clone()))),
10000.0,
HyperliquidCommission::default(),
)?;
backtest.calculate_with_funding()?;
let report = backtest.funding_report()?;
let enhanced_report = backtest.enhanced_report()?;
println!("\nBacktest Results:");
println!("----------------");
println!("Net profit: ${:.2}", report.net_profit);
println!("Return: {:.2}%", report.net_profit / 10000.0 * 100.0);
println!("Sharpe ratio: {:.2}", enhanced_report.sharpe_ratio);
println!("Max drawdown: {:.2}%", enhanced_report.max_drawdown * 100.0);
println!("Win rate: {:.2}%", enhanced_report.win_rate * 100.0);
println!("Trading PnL: ${:.2}", report.net_trading_pnl);
println!("Funding PnL: ${:.2}", report.net_funding_pnl);
println!("Total trades: {}", enhanced_report.trade_count);
Ok((strategy, report))
}
async fn run_paper_trading_phase(
symbol: &str,
mut strategy: EnhancedSmaStrategy,
initial_balance: f64
) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
println!("\n📝 Phase 2: Paper Trading");
println!("======================");
let slippage_config = SlippageConfig {
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, };
println!("Creating paper trading engine...");
let mut paper_engine = PaperTradingEngine::new(initial_balance, slippage_config);
let data_stream = RealTimeDataStream::new().await?;
let data_stream = Arc::new(Mutex::new(data_stream));
paper_engine.set_real_time_data(data_stream.clone());
{
let mut stream = data_stream.lock().unwrap();
stream.connect().await?;
stream.subscribe_ticker(symbol).await?;
stream.subscribe_order_book(symbol).await?;
stream.subscribe_funding_rate(symbol).await?;
}
println!("Connected to real-time data stream");
println!("Subscribed to {} market data", symbol);
let paper_engine_arc = Arc::new(Mutex::new(paper_engine));
let paper_engine_for_task = paper_engine_arc.clone();
let strategy_box: Box<dyn TradingStrategy> = Box::new(strategy);
let task_handle = tokio::spawn(async move {
let mut engine = paper_engine_for_task.lock().unwrap();
if let Err(e) = engine.start_simulation(strategy_box).await {
eprintln!("Error in paper trading simulation: {}", e);
}
});
println!("Paper trading simulation started. Running for 30 seconds...");
println!("(In a real application, this would run for days or weeks)");
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
{
let mut engine = paper_engine_arc.lock().unwrap();
engine.stop_simulation();
println!("Paper trading simulation stopped");
}
let _ = task_handle.await;
let report = {
let engine = paper_engine_arc.lock().unwrap();
engine.generate_report()
};
println!("\nPaper Trading Results:");
println!("---------------------");
println!("{}", report);
let positions = {
let engine = paper_engine_arc.lock().unwrap();
engine.get_positions().clone()
};
println!("\nFinal Positions:");
if positions.is_empty() {
println!("No open positions");
} else {
for (symbol, position) in &positions {
println!("{}: {} @ ${} (PnL: ${:.2})",
symbol,
position.size,
position.current_price,
position.total_pnl()
);
}
}
let mut state = strategy.save_state();
if let Some(position) = positions.get(symbol) {
state.insert("position_size".to_string(), position.size.to_string());
state.insert("position_entry_price".to_string(), position.entry_price.to_string());
}
Ok(state)
}live trading phase of the strategy (simulated)
async fn run_live_trading_phase(
symbol: &str,
mut strategy: EnhancedSmaStrategy,
state: HashMap<String, String>,
initial_balance: f64
) -> Result<(), Box<dyn std::error::Error>> {
println!("\n🚀 Phase 3: Live Trading (Simulated)");
println!("=================================");
println!("⚠️ This is a simulation and does not execute real trades");
strategy.load_state(&state);
println!("Loaded strategy state from paper trading phase");
let risk_config = RiskConfig {
max_position_size_pct: 0.05, max_daily_loss_pct: 0.01, stop_loss_pct: 0.03, take_profit_pct: 0.05, max_leverage: 2.0, max_concentration_pct: 0.15, max_position_correlation: 0.5, volatility_sizing_factor: 0.3, max_portfolio_volatility_pct: 0.03, max_drawdown_pct: 0.05, };
let api_config = ApiConfig {
api_key: "your_api_key".to_string(),
api_secret: "your_api_secret".to_string(),
endpoint: "https://api.hyperliquid-testnet.xyz".to_string(),
use_testnet: true, timeout_ms: 5000,
};
println!("Setting up wallet...");
let private_key = "0000000000000000000000000000000000000000000000000000000000000001";
let wallet = LocalWallet::from_str(private_key).unwrap();
println!("Creating live trading engine...");
let mut live_engine = SimulatedLiveTradingEngine::new(initial_balance, risk_config)?;
if let Some(position_size) = state.get("position_size") {
if let Ok(size) = position_size.parse::<f64>() {
if size != 0.0 {
let entry_price = state.get("position_entry_price")
.and_then(|p| p.parse::<f64>().ok())
.unwrap_or(50000.0);
println!("Initializing position from paper trading phase:");
println!("{}: {} @ ${}", symbol, size, entry_price);
live_engine.initialize_position(
symbol,
size,
entry_price,
Utc::now().with_timezone(&FixedOffset::east(0))
)?;
}
}
}
println!("Starting live trading simulation...");
let live_engine_arc = Arc::new(Mutex::new(live_engine));
let live_engine_for_task = live_engine_arc.clone();
let strategy_box: Box<dyn TradingStrategy> = Box::new(strategy);
let task_handle = tokio::spawn(async move {
let mut engine = live_engine_for_task.lock().unwrap();
if let Err(e) = engine.start_trading(strategy_box).await {
eprintln!("Error in live trading simulation: {}", e);
}
});
println!("Live trading simulation started. Running for 30 seconds...");
println!("(In a real application, this would run continuously)");
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
{
let mut engine = live_engine_arc.lock().unwrap();
engine.stop_trading().await?;
println!("Live trading simulation stopped");
let positions = engine.get_positions().await?;
println!("\nFinal Positions:");
if positions.is_empty() {
println!("No open positions");
} else {
for (symbol, position) in positions {
println!("{}: {} @ ${} (PnL: ${:.2})",
symbol,
position.size,
position.current_price,
position.unrealized_pnl
);
}
}
let metrics = engine.get_performance_metrics().await?;
println!("\nPerformance Metrics:");
println!("Trading PnL: ${:.2}", metrics.trading_pnl);
println!("Funding PnL: ${:.2}", metrics.funding_pnl);
println!("Total PnL: ${:.2}", metrics.total_pnl);
println!("Total fees: ${:.2}", metrics.total_fees);
}
let _ = task_handle.await;
Ok(())
}Adapter to use TradingStrategy with rs-backtester
struct BacktestStrategyAdapter {
strategy: Box<dyn TradingStrategy>,
current_index: usize,
}
impl BacktestStrategyAdapter {
fn new(strategy: Box<dyn TradingStrategy>) -> Self {
Self {
strategy,
current_index: 0,
}
}
}
impl rs_backtester::strategies::Strategy for BacktestStrategyAdapter {
fn next(&mut self, ctx: &mut rs_backtester::strategies::Context, _: &mut rs_backtester::strategies::Broker) {
let index = ctx.index();
self.current_index = index;
let data = ctx.data();
let timestamp = data.datetime[index];
let market_data = MarketData::new(
&data.ticker,
data.close[index],
data.low[index],
data.high[index],
data.volume[index],
timestamp,
);
if let Ok(orders) = self.strategy.on_market_data(&market_data) {
for order in orders {
match order.side {
OrderSide::Buy => {
ctx.entry_qty(order.quantity);
},
OrderSide::Sell => {
ctx.entry_qty(-order.quantity);
},
}
}
}
}
}
struct SimulatedLiveTradingEngine {
balance: f64,
positions: HashMap<String, Position>,
order_history: Vec<OrderResult>,
next_order_id: usize,
risk_config: RiskConfig,
is_trading: bool,
}
impl SimulatedLiveTradingEngine {
fn new(initial_balance: f64, risk_config: RiskConfig) -> Result<Self, Box<dyn std::error::Error>> {
Ok(Self {
balance: initial_balance,
positions: HashMap::new(),
order_history: Vec::new(),
next_order_id: 1,
risk_config,
is_trading: false,
})
}
fn initialize_position(
&mut self,
symbol: &str,
size: f64,
entry_price: f64,
timestamp: DateTime<FixedOffset>
) -> Result<(), Box<dyn std::error::Error>> {
let position = Position::new(
symbol,
size,
entry_price,
entry_price,
timestamp,
);
self.positions.insert(symbol.to_string(), position);
Ok(())
}
async fn execute_order(&mut self, order: OrderRequest) -> Result<OrderResult, Box<dyn std::error::Error>> {
let timestamp = Utc::now().with_timezone(&FixedOffset::east(0));
let order_id = format!("order_{}", self.next_order_id);
self.next_order_id += 1;
let execution_price = match order.price {
Some(price) => price,
None => {
let position = self.positions.get(&order.symbol).cloned();
match order.side {
OrderSide::Buy => {
if let Some(pos) = &position {
pos.current_price * 1.001 } else {
50000.0 }
},
OrderSide::Sell => {
if let Some(pos) = &position {
pos.current_price * 0.999 } else {
50000.0 }
},
}
}
};
let fees = execution_price * order.quantity * 0.0005;
let mut result = OrderResult {
order_id,
symbol: order.symbol.clone(),
side: order.side,
order_type: order.order_type,
requested_quantity: order.quantity,
filled_quantity: order.quantity,
average_price: Some(execution_price),
status: OrderStatus::Filled,
timestamp,
fees: Some(fees),
error: None,
client_order_id: order.client_order_id.clone(),
metadata: HashMap::new(),
};
let position_size = match order.side {
OrderSide::Buy => order.quantity,
OrderSide::Sell => -order.quantity,
};
let position = self.positions.entry(order.symbol.clone()).or_insert_with(|| {
Position::new(
&order.symbol,
0.0,
execution_price,
execution_price,
timestamp,
)
});
if position.size == 0.0 {
position.size = position_size;
position.entry_price = execution_price;
} else if position.size > 0.0 && position_size > 0.0 {
let new_size = position.size + position_size;
position.entry_price = (position.entry_price * position.size + execution_price * position_size) / new_size;
position.size = new_size;
} else if position.size < 0.0 && position_size < 0.0 {
let new_size = position.size + position_size;
position.entry_price = (position.entry_price * position.size + execution_price * position_size) / new_size;
position.size = new_size;
} else {
let new_size = position.size + position_size;
if new_size.abs() < 0.000001 {
position.realized_pnl += position.unrealized_pnl;
position.size = 0.0;
position.unrealized_pnl = 0.0;
} else if new_size * position.size < 0.0 {
let closed_size = position.size;
let closed_pnl = closed_size * (execution_price - position.entry_price);
position.realized_pnl += closed_pnl;
position.size = new_size;
position.entry_price = execution_price;
position.unrealized_pnl = 0.0;
} else {
let closed_size = -position_size;
let closed_pnl = closed_size * (execution_price - position.entry_price);
position.realized_pnl += closed_pnl;
position.size = new_size;
position.unrealized_pnl = position.size * (execution_price - position.entry_price);
}
}
position.current_price = execution_price;
position.timestamp = timestamp;
self.balance -= fees;
self.order_history.push(result.clone());
Ok(result)
}
async fn start_trading(&mut self, strategy: Box<dyn TradingStrategy>) -> Result<(), Box<dyn std::error::Error>> {
self.is_trading = true;
let symbol = "BTC"; let mut price = 50000.0;
let mut iteration = 0;
while self.is_trading && iteration < 10 {
let price_change = (rand::random::<f64>() - 0.5) * 100.0;
price += price_change;
let timestamp = Utc::now().with_timezone(&FixedOffset::east(0));
let market_data = MarketData::new(
symbol,
price,
price - 10.0,
price + 10.0,
1000.0,
timestamp,
).with_funding_rate(
0.0001 * (rand::random::<f64>() - 0.5),
timestamp + chrono::Duration::hours(8)
);
let orders = strategy.on_market_data(&market_data)
.map_err(|e| format!("Strategy error: {}", e))?;
for order in orders {
let result = self.execute_order(order).await?;
if result.status == OrderStatus::Filled || result.status == OrderStatus::PartiallyFilled {
strategy.on_order_fill(&result)
.map_err(|e| format!("Strategy error: {}", e))?;
println!("Order executed: {} {} {} at ${} with size {}",
result.symbol,
result.side,
result.order_type,
result.average_price.unwrap_or(0.0),
result.filled_quantity
);
}
}
for (_, position) in &mut self.positions {
position.current_price = price;
position.unrealized_pnl = position.size * (price - position.entry_price);
}
let account_value = self.get_account_value().await?;
println!("Iteration {}: Account value: ${:.2}", iteration + 1, account_value);
tokio::time::sleep(std::time::Duration::from_secs(3)).await;
iteration += 1;
}
Ok(())
}
async fn stop_trading(&mut self) -> Result<(), Box<dyn std::error::Error>> {
self.is_trading = false;
Ok(())
}
async fn get_positions(&self) -> Result<HashMap<String, Position>, Box<dyn std::error::Error>> {
Ok(self.positions.clone())
}
async fn get_account_value(&self) -> Result<f64, Box<dyn std::error::Error>> {
let mut total = self.balance;
for (_, position) in &self.positions {
total += position.unrealized_pnl;
}
Ok(total)
}
async fn get_performance_metrics(&self) -> Result<PerformanceMetrics, Box<dyn std::error::Error>> {
let mut trading_pnl = 0.0;
let mut funding_pnl = 0.0;
for (_, position) in &self.positions {
trading_pnl += position.realized_pnl + position.unrealized_pnl;
funding_pnl += position.funding_pnl;
}
Ok(PerformanceMetrics {
trading_pnl,
funding_pnl,
total_pnl: trading_pnl + funding_pnl,
total_fees: self.order_history.iter()
.filter_map(|order| order.fees)
.sum(),
})
}
}
struct PerformanceMetrics {
trading_pnl: f64,
funding_pnl: f64,
total_pnl: f64,
total_fees: f64,
}
impl Clone for EnhancedSmaStrategy {
fn clone(&self) -> Self {
Self {
name: self.name.clone(),
symbol: self.symbol.clone(),
short_period: self.short_period,
long_period: self.long_period,
prices: self.prices.clone(),
funding_rates: self.funding_rates.clone(),
funding_threshold: self.funding_threshold,
funding_weight: self.funding_weight,
position_size: self.position_size,
current_position: self.current_position,
signals: self.signals.clone(),
parameters: self.parameters.clone(),
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
init_logger();
println!("Strategy Migration Example: Backtest → Paper → Live");
println!("=================================================");
println!("This example demonstrates the workflow of developing and");
println!("migrating a trading strategy through three phases:");
println!("1. Backtesting with historical data");
println!("2. Paper trading with real-time data");
println!("3. Live trading with real execution (simulated)");
let symbol = "BTC";
let initial_balance = 10000.0;
let (strategy, backtest_report) = run_backtest_phase(symbol).await?;
let strategy_state = run_paper_trading_phase(symbol, strategy.clone(), initial_balance).await?;
run_live_trading_phase(symbol, strategy, strategy_state, initial_balance).await?;
println!("\nStrategy Migration Example Completed Successfully!");
println!("In a real-world scenario, each phase would run for much longer");
println!("periods, with careful analysis between phases.");
Ok(())
}