1use crate::backtest::BacktestMetaData;
2use crate::backtest::Decode;
3use crate::backtest::Signals;
4use crate::backtest::TimeseriesStats;
5use crate::backtest::Trades;
6use crate::{Error, Result};
7use std::io::Read;
8
9fn decode_vector<T, R>(reader: &mut R) -> Result<Vec<T>>
11where
12 R: Read, T: Decode<R>, {
15 let mut length_buf = [0u8; 4];
17 reader
18 .read_exact(&mut length_buf)
19 .map_err(|_| Error::CustomError("Failed to read vector length".to_string()))?;
20 let length = u32::from_le_bytes(length_buf) as usize;
21
22 let mut result = Vec::with_capacity(length);
24 for _ in 0..length {
25 result.push(T::decode(reader)?);
26 }
27
28 Ok(result)
29}
30
31pub struct BacktestDecoder<R: Read> {
32 cursor: R,
33}
34
35impl<R: Read> BacktestDecoder<R> {
36 pub fn new(reader: R) -> Self {
37 BacktestDecoder { cursor: reader }
38 }
39
40 pub fn decode_metadata(&mut self) -> Result<BacktestMetaData> {
41 BacktestMetaData::decode(&mut self.cursor)
42 }
43
44 pub fn decode_timeseries(&mut self) -> Result<Vec<TimeseriesStats>> {
45 decode_vector(&mut self.cursor)
46 }
47
48 pub fn decode_trades(&mut self) -> Result<Vec<Trades>> {
49 decode_vector(&mut self.cursor)
50 }
51
52 pub fn decode_signals(&mut self) -> Result<Vec<Signals>> {
53 decode_vector(&mut self.cursor)
54 }
55}
56
57#[cfg(test)]
58mod tests {
59
60 use super::*;
61 use crate::{
62 backtest::{BacktestData, Parameters, SignalInstructions, StaticStats},
63 backtest_encode::BacktestEncoder,
64 };
65
66 #[test]
67 fn backtestencoder() -> anyhow::Result<()> {
68 let params = Parameters {
69 strategy_name: "Testing".to_string(),
70 capital: 10000,
71 schema: "Ohlcv-1s".to_string(),
72 data_type: "BAR".to_string(),
73 start: 1730160814000000000,
74 end: 1730160814000000000,
75 tickers: vec!["HE.n.0".to_string(), "AAPL".to_string()],
76 };
77
78 let static_stats = StaticStats {
79 total_trades: 100,
80 total_winning_trades: 50,
81 total_losing_trades: 50,
82 avg_profit: 1000000000000,
83 avg_profit_percent: 10383783337737,
84 avg_gain: 23323212233,
85 avg_gain_percent: 24323234,
86 avg_loss: 203982828,
87 avg_loss_percent: 23432134323,
88 profitability_ratio: 130213212323,
89 profit_factor: 12342123431,
90 profit_and_loss_ratio: 1234321343,
91 total_fees: 123453234,
92 net_profit: 1234323,
93 beginning_equity: 12343234323,
94 ending_equity: 12343234,
95 total_return: 234532345,
96 annualized_return: 234532345,
97 daily_standard_deviation_percentage: 23453234,
98 annual_standard_deviation_percentage: 34543443,
99 max_drawdown_percentage_period: 234543234,
100 max_drawdown_percentage_daily: 23432345,
101 sharpe_ratio: 23432343,
102 sortino_ratio: 123453234543,
103 };
104 let bt_metadata = BacktestMetaData::new(None, "testing", params, static_stats);
105
106 let timeseries = TimeseriesStats {
107 timestamp: 123700000000000,
108 equity_value: 9999999,
109 percent_drawdown: 2343234,
110 cumulative_return: 2343234,
111 period_return: 2345432345,
112 };
113
114 let stats: Vec<TimeseriesStats> = vec![timeseries.clone(), timeseries.clone()];
115
116 let trade = Trades {
117 trade_id: 1,
118 leg_id: 1,
119 timestamp: 1704903000,
120 ticker: "AAPL".to_string(),
121 quantity: 4,
122 avg_price: 13074,
123 trade_value: -52296,
124 trade_cost: -52296,
125 action: "BUY".to_string(),
126 fees: 100,
127 };
128
129 let trades: Vec<Trades> = vec![trade.clone(), trade.clone()];
130
131 let instructions = SignalInstructions {
132 ticker: "AAPL".to_string(),
133 order_type: "MKT".to_string(),
134 action: "BUY".to_string(),
135 trade_id: 1,
136 leg_id: 2,
137 weight: 13213432,
138 quantity: 2343,
139 limit_price: "12341".to_string(),
140 aux_price: "1233212".to_string(),
141 };
142
143 let vec: Vec<SignalInstructions> = vec![instructions.clone(), instructions.clone()];
144 let signal = Signals {
145 timestamp: 1234565432345,
146 trade_instructions: vec,
147 };
148
149 let signals: Vec<Signals> = vec![signal.clone(), signal.clone()];
150 let backtest = BacktestData {
151 metadata: bt_metadata,
152 daily_timeseries_stats: stats.clone(),
153 period_timeseries_stats: stats,
154 trades,
155 signals,
156 };
157
158 let mut bytes = Vec::new();
160 let mut encoder = BacktestEncoder::new(&mut bytes);
161 encoder.encode_metadata(&backtest.metadata);
162 encoder.encode_timeseries(&backtest.period_timeseries_stats);
163 encoder.encode_timeseries(&backtest.daily_timeseries_stats);
164 encoder.encode_trades(&backtest.trades);
165 encoder.encode_signals(&backtest.signals);
166
167 let decode = bytes.as_slice();
169 let mut decoder = BacktestDecoder::new(decode);
170
171 let metadata = decoder.decode_metadata()?;
172 let period_stats = decoder.decode_timeseries()?;
173 let daily_stats = decoder.decode_timeseries()?;
174 let trades = decoder.decode_trades()?;
175 let signals = decoder.decode_signals()?;
176
177 let decoded_backtest = BacktestData {
178 metadata,
179 period_timeseries_stats: period_stats,
180 daily_timeseries_stats: daily_stats,
181 trades,
182 signals,
183 };
184
185 assert_eq!(backtest, decoded_backtest);
187
188 Ok(())
189 }
190}