#![allow(dead_code)]
use serde_json::{json, Value};
use wickra_benchmark_core::{canonicalize, hash, BenchmarkCase, Candle, StrategySpec};
pub const SYMBOL: &str = "TEST";
pub fn strategy_json() -> Value {
json!({
"symbol": SYMBOL,
"timeframe": "1h",
"indicators": {
"ema_fast": { "type": "Ema", "params": [3] },
"ema_slow": { "type": "Ema", "params": [8] }
},
"entry": { "cross_above": ["ema_fast", "ema_slow"] },
"exit": { "cross_below": ["ema_fast", "ema_slow"] },
"sizing": { "type": "fixed_fraction", "fraction": 0.95 },
"costs": { "taker_bps": 5, "slippage": { "type": "fixed_bps", "bps": 2 } }
})
}
pub fn candles_from(closes: &[f64]) -> Vec<Candle> {
closes
.iter()
.enumerate()
.map(|(i, &c)| {
let o = if i == 0 { c } else { closes[i - 1] };
Candle {
time: 1_700_000_000 + i64::try_from(i).unwrap() * 3600,
open: o,
high: o.max(c) + 1.0,
low: o.min(c) - 1.0,
close: c,
volume: 1000.0,
}
})
.collect()
}
pub fn sample_closes() -> Vec<f64> {
(0..40)
.map(|i| {
if i <= 10 {
120.0 - 2.0 * f64::from(i)
} else {
100.0 + 2.0 * f64::from(i - 10)
}
})
.collect()
}
pub fn sample_candles() -> Vec<Candle> {
candles_from(&sample_closes())
}
pub fn recompute(strategy: &Value, candles: &[Candle]) -> (Value, String) {
let spec: StrategySpec = serde_json::from_value(strategy.clone()).expect("valid strategy");
let report = wickra_backtest_core::run(&spec, candles).expect("engine runs");
let recomputed = serde_json::to_value(&report).expect("report serializes");
let canon = canonicalize(&recomputed).expect("canonicalizes");
let digest = hash(&canon);
(recomputed, digest)
}
pub fn bless(id: &str, description: &str, strategy: Value, candles: &[Candle]) -> BenchmarkCase {
let (expected, expected_hash) = recompute(&strategy, candles);
BenchmarkCase {
id: id.to_string(),
description: description.to_string(),
strategy,
dataset_ref: format!("{id}.csv"),
expected,
expected_hash,
}
}
pub fn sample_case() -> BenchmarkCase {
bless(
"ema-cross-01",
"EMA(3/8) cross over a V-shaped universe.",
strategy_json(),
&sample_candles(),
)
}