use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};
use day_trade::ForecastingStrategy;
use day_trade::RealtimeTradingStrategy;
use std::fs::File;
use std::io::{self, BufRead, BufReader};
use std::path::Path;
use std::str::FromStr;
fn load_price_data(file_path: &Path) -> io::Result<Vec<(DateTime<Utc>, f64, f64, f64, f64, f64)>> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let mut data = Vec::new();
for line in reader.lines().skip(1) {
let line = line?;
let fields: Vec<&str> = line.split(',').collect();
if fields.len() >= 6 {
let timestamp_str = fields[0];
let naive_datetime = NaiveDateTime::parse_from_str(
timestamp_str.trim_end_matches(" UTC"),
"%Y-%m-%d %H:%M:%S",
)
.unwrap_or_else(|_| NaiveDateTime::from_timestamp_opt(0, 0).unwrap());
let timestamp = DateTime::<Utc>::from_naive_utc_and_offset(naive_datetime, Utc);
let open = f64::from_str(fields[1]).unwrap_or(0.0);
let high = f64::from_str(fields[2]).unwrap_or(0.0);
let low = f64::from_str(fields[3]).unwrap_or(0.0);
let close = f64::from_str(fields[4]).unwrap_or(0.0);
let volume = f64::from_str(fields[5]).unwrap_or(0.0);
data.push((timestamp, open, high, low, close, volume));
}
}
data.sort_by(|a, b| a.0.cmp(&b.0));
Ok(data)
}
fn main() -> io::Result<()> {
println!("Forecasting Strategy Example with AAPL Data");
println!("===========================================\n");
let mut strategy = ForecastingStrategy::new().unwrap();
let csv_path = Path::new("examples/csv/AAPL_daily_ohlcv.csv");
let test_data = match load_price_data(&csv_path) {
Ok(data) => data,
Err(e) => {
println!("Error loading CSV data: {}", e);
return Err(e);
}
};
println!("Loaded {} days of AAPL price data", test_data.len());
println!(
"Date range: {} to {}",
test_data.first().unwrap().0.format("%Y-%m-%d"),
test_data.last().unwrap().0.format("%Y-%m-%d")
);
println!("\nRunning strategy backtest...");
let mut position = 0; let mut cash = 10000.0;
let mut shares = 0.0;
let mut trades = 0;
let initial_price = test_data[0].4; let initial_shares = cash / initial_price;
println!("\nDATE | PRICE | SIGNAL | POSITION | CASH | EQUITY | B&H EQUITY");
println!("-----------|--------|--------|----------|---------|---------|----------");
for (i, (timestamp, open, high, low, close, volume)) in test_data.iter().enumerate() {
strategy
.update(*timestamp, *open, *high, *low, *close, *volume)
.unwrap();
if i < 15 {
continue;
}
let signal = strategy.generate_signal().unwrap();
if signal >= 1 && position <= 0 {
if position == -1 {
cash += shares * close;
trades += 1;
}
shares = cash / close;
cash = 0.0;
position = 1;
trades += 1;
} else if signal <= -1 && position >= 0 {
if position == 1 {
cash += shares * close;
trades += 1;
}
shares = -(cash / close);
cash = cash * 2.0; position = -1;
trades += 1;
}
let equity = cash + (shares * close);
let buy_and_hold_value = initial_shares * close;
if i % 30 == 0 || i == test_data.len() - 1 {
let position_str = match position {
-1 => "SHORT",
0 => "NONE",
1 => "LONG",
_ => "UNKNOWN",
};
let signal_str = match signal {
-2 => "STRONG SELL",
-1 => "SELL",
0 => "HOLD",
1 => "BUY",
2 => "STRONG BUY",
_ => "UNKNOWN",
};
println!(
"{} | {:6.2} | {:10} | {:8} | {:7.0} | {:7.0} | {:7.0}",
timestamp.format("%Y-%m-%d"),
close,
signal_str,
position_str,
cash,
equity,
buy_and_hold_value
);
}
}
let final_price = test_data.last().unwrap().4;
let strategy_final_equity = cash + (shares * final_price);
let buy_and_hold_final = 10000.0 * (final_price / initial_price);
println!("\nPerformance Summary:");
println!("-------------------");
println!("Initial Capital: $10,000.00");
println!("Final Equity: ${:.2}", strategy_final_equity);
println!(
"Total Return: {:.2}%",
(strategy_final_equity - 10000.0) / 100.0
);
println!(
"Buy & Hold Return: {:.2}%",
(buy_and_hold_final - 10000.0) / 100.0
);
println!("Number of Trades: {}", trades);
let outperformance = strategy_final_equity - buy_and_hold_final;
println!(
"Outperformance vs Buy & Hold: ${:.2} ({:.2}%)",
outperformance,
outperformance / 10000.0 * 100.0
);
Ok(())
}