use std::collections::HashMap;
use yuzu_core::backtest::BacktestConfig;
use yuzu_core::panel::Panel;
use yuzu_core::{run_backtest, EvalContext};
fn main() {
let dates = vec![20240102, 20240103, 20240104, 20240105, 20240108, 20240109];
let symbols = vec!["ACME".to_string(), "BETA".to_string()];
let close = Panel::from_rows(
dates,
symbols,
vec![
vec![20.0, 30.0],
vec![23.0, 31.0],
vec![27.0, 32.0],
vec![32.0, 33.0],
vec![38.0, 34.0],
vec![44.0, 35.0],
],
)
.expect("prices form a valid dates × symbols matrix");
let mut panels = HashMap::new();
panels.insert("close".to_string(), close);
let ctx = EvalContext::new(panels);
let strategy_src = "is_largest(sma(close, 2), 1)";
let spec = lemon::parse(strategy_src).expect("strategy is valid lemon source");
let spec_json = serde_json::to_string(&spec).expect("spec serializes to JSON");
let report = run_backtest(&spec_json, &ctx, "close", &BacktestConfig::default())
.expect("backtest runs to completion");
let m = &report.metrics;
println!("Strategy: {strategy_src}");
println!("Days simulated: {}", report.equity.len());
println!(
"Final equity (base 1.0): {:.4}",
report.equity.last().copied().unwrap_or(f64::NAN)
);
println!();
println!("Headline metrics");
println!(" total return : {:>8.2}%", m.total_return * 100.0);
println!(" Sharpe : {:>8.2}", m.sharpe);
println!(" max drawdown : {:>8.2}%", m.max_drawdown * 100.0);
println!(" # trades : {:>8}", m.num_trades as i64);
println!(" win rate : {:>8.2}%", m.win_rate * 100.0);
println!();
println!("Trades");
for t in &report.trades {
let exit = match t.exit_date {
Some(d) => d.to_string(),
None => "open".to_string(),
};
println!(
" {:<4} {} -> {:<6} return {:>7.2}% ({} days)",
t.symbol,
t.entry_date,
exit,
t.ret * 100.0,
t.period
);
}
}