use chrono::{Duration, TimeZone, Timelike, Utc};
use day_trade::{
BollingerBandsStrategy, IntradayTradingStrategy, MinuteOhlcv, OhlcvData, Signal, TradeError,
VwapStrategy,
};
use std::collections::HashMap;
use std::fs::File;
use std::io::{self, BufWriter, Write};
fn generate_test_data() -> Vec<MinuteOhlcv> {
let mut data = Vec::new();
for day in 0..5 {
let base_date = Utc.with_ymd_and_hms(2023, 6, 5 + day, 9, 30, 0).unwrap();
let mut price = 100.0 + (day as f64 * 2.0) + (day % 3) as f64;
for minute in 0..390 {
let timestamp = base_date + Duration::minutes(minute);
let hour = timestamp.hour();
let min = timestamp.minute();
let mut price_change = 0.0;
let mut volume_multiplier = 1.0;
if hour == 9 || (hour == 10 && min < 30) {
price_change = ((minute % 15) as f64 / 15.0).sin() * 0.3;
volume_multiplier = 1.5;
}
else if (hour == 11 && min >= 30) || hour == 12 || (hour == 13 && min <= 30) {
price_change = ((minute % 20) as f64 / 20.0).sin() * 0.1;
volume_multiplier = 0.7;
}
else if hour >= 15 {
price_change = ((minute % 10) as f64 / 10.0).sin() * 0.25;
volume_multiplier = 1.3;
}
else {
price_change = ((minute % 30) as f64 / 30.0).sin() * 0.2;
}
if day % 3 == 0 {
price_change += 0.01;
} else if day % 3 == 1 {
price_change -= 0.01;
}
price *= 1.0 + price_change;
price += (minute % 5) as f64 * 0.05 - 0.125;
price = price.max(50.0);
let high = price * (1.0 + 0.001 * ((minute % 3) as f64));
let low = price * (1.0 - 0.001 * ((minute % 4) as f64));
let base_volume = 1000u64;
let volume = (base_volume as f64 * volume_multiplier) as u64
+ ((minute % 5) * 50) as u64
+ if minute % 15 == 0 { 500 } else { 0 };
data.push(MinuteOhlcv {
timestamp,
data: OhlcvData {
open: price,
high,
low,
close: price,
volume,
},
});
}
}
data
}
fn run_backtest<T: IntradayTradingStrategy>(
strategy: &T,
data: &[MinuteOhlcv],
name: &str,
) -> Result<(), TradeError> {
let signals = strategy.generate_signals(data)?;
let performance = strategy.calculate_performance(data, &signals)?;
println!("Strategy: {}", name);
println!(" Performance: {:.2}%", performance);
let mut signal_counts = HashMap::new();
for signal in &signals {
*signal_counts.entry(signal).or_insert(0) += 1;
}
println!(" Signal counts:");
println!(
" Buy: {}",
signal_counts.get(&Signal::Buy).unwrap_or(&0)
);
println!(
" Sell: {}",
signal_counts.get(&Signal::Sell).unwrap_or(&0)
);
println!(
" Hold: {}",
signal_counts.get(&Signal::Hold).unwrap_or(&0)
);
if let Err(e) = export_signals_to_csv(data, &signals, name) {
println!("Warning: Failed to export signals to CSV: {}", e);
}
Ok(())
}
fn export_signals_to_csv(
data: &[MinuteOhlcv],
signals: &[Signal],
strategy_name: &str,
) -> Result<(), io::Error> {
let filename = format!(
"{}_signals.csv",
strategy_name.to_lowercase().replace(" ", "_")
);
let file = File::create(&filename)?;
let mut writer = BufWriter::new(file);
writeln!(writer, "timestamp,open,high,low,close,volume,signal")?;
for (i, point) in data.iter().enumerate() {
let signal_str = match signals[i] {
Signal::Buy => "buy",
Signal::Sell => "sell",
Signal::Hold => "hold",
};
writeln!(
writer,
"{},{},{},{},{},{},{}",
point.timestamp.format("%Y-%m-%d %H:%M:%S"),
point.data.open,
point.data.high,
point.data.low,
point.data.close,
point.data.volume,
signal_str
)?;
}
println!(" Exported signals to {}", filename);
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Intraday Trading Strategy Backtest");
println!("==================================");
let data = generate_test_data();
println!(
"Generated {} minutes of test data across {} days",
data.len(),
data.len() / 390
);
let vwap_mean_reversion = VwapStrategy::mean_reversion();
let vwap_trend_following = VwapStrategy::trend_following();
let bb_mean_reversion = BollingerBandsStrategy::mean_reversion();
let bb_breakout = BollingerBandsStrategy::volatility_breakout();
run_backtest(&vwap_mean_reversion, &data, "VWAP Mean Reversion")?;
run_backtest(&vwap_trend_following, &data, "VWAP Trend Following")?;
run_backtest(&bb_mean_reversion, &data, "Bollinger Bands Mean Reversion")?;
run_backtest(&bb_breakout, &data, "Bollinger Bands Volatility Breakout")?;
println!("\nBacktests complete. Results saved to CSV files for visualization.");
println!("You can import these files into a charting tool or spreadsheet for analysis.");
Ok(())
}