1use serde::{Serialize};
2
3fn first<T>(v: &Vec<T>) -> Option<&T> {
4 v.first()
5}
6
7#[derive(Debug, Clone, Serialize)]
8pub struct TradeActionOut {
9 pub index_in: i32,
10 pub price_in: f64,
11 pub amt: i32,
12 pub index_out: i32,
13 pub price_out: f64,
14 pub diff: f64,
15}
16
17#[derive(Debug, Serialize)]
18pub struct TradeActionIn {
19 pub index_in: i32,
20 pub price_in: f64,
21 pub amt: i32,
22}
23
24#[derive(Debug, Serialize)]
25pub struct TradeInputResults {
26 pub returns: BacktestResults,
27}
28
29#[derive(Debug, Serialize)]
30pub struct BacktestResults {
31 pub calculated_returns: f64,
32 pub tradesin: Vec<TradeActionIn>,
33 pub tradesout: Vec<TradeActionOut>,
34}
35
36#[derive(Debug, Serialize)]
37pub struct EnterMarketInfo<'a> {
38 pub index: i32,
39 pub current_price: f64,
40 pub holding: f64,
41 pub inmarket: f64,
42 pub data: &'a Vec<Vec<f64>>,
43}
44
45#[derive(Debug, Serialize)]
46pub struct ExitMarketInfo<'a> {
47 pub index: i32,
48 pub current_price: f64,
49 pub index_in: i32,
50 pub price_in: f64,
51 pub holding: f64,
52 pub inmarket: f64,
53 pub data: &'a Vec<Vec<f64>>,
54 pub diff: f64,
55}
56
57#[derive(Debug, Serialize)]
58pub struct Portfolio {
59 pub holding: i32,
60 pub inmarket: i32,
61}
62
63pub fn backtest(
64 values: Vec<Vec<f64>>,
65 holding: i32,
66 default_amt: i32,
67 enter_market_function: &dyn Fn(EnterMarketInfo) -> bool,
68 exit_market_function: &dyn Fn(ExitMarketInfo) -> bool,
69) -> BacktestResults {
70 let mut calculated_returns: f64 = 0.0;
71
72 let close_prices: Vec<f64> = first(&values).unwrap().to_vec();
73
74 let mut portfolio = Portfolio {
75 holding: holding,
76 inmarket: 0,
77 };
78
79 let mut tradesin: Vec<TradeActionIn> = vec![];
80 let mut tradesout: Vec<TradeActionOut> = vec![];
81
82 println!("{:?}", close_prices);
83 {
84 for n in 0..close_prices.len() as i32 {
85 let amt: i32 = default_amt;
86 let have_money = portfolio.holding > amt;
87
88 let to_make_enter_decision_on = EnterMarketInfo {
89 index: n,
90 current_price: close_prices[n as usize],
91 holding: portfolio.holding.clone() as f64,
92 inmarket: portfolio.inmarket.clone() as f64,
93 data: &values,
94 };
95
96 if have_money && enter_market_function(to_make_enter_decision_on) {
97 let action = TradeActionIn {
100 index_in: n,
101 price_in: close_prices[n as usize],
102 amt: amt,
103 };
104 tradesin.push(action);
105 portfolio.holding = portfolio.holding - amt;
106 portfolio.inmarket = portfolio.inmarket + amt;
107 }
108
109 tradesin.retain(|item| {
110 let difference = close_prices[n as usize] - item.price_in;
111
112 let to_make_exit_decision_on = ExitMarketInfo {
113 index: n,
114 index_in: item.index_in,
115 price_in: item.price_in,
116 current_price: close_prices[n as usize],
117 holding: portfolio.holding.clone() as f64,
118 inmarket: portfolio.inmarket.clone() as f64,
119 diff: difference,
120 data: &values,
121 };
122
123 if exit_market_function(to_make_exit_decision_on) {
124 let action_out = TradeActionOut {
125 index_in: item.index_in,
126 price_in: item.price_in,
127 amt: item.amt,
128 index_out: n,
129 price_out: close_prices[n as usize],
130 diff: difference,
131 };
132 tradesout.push(action_out);
133 portfolio.holding = portfolio.holding + amt;
134 portfolio.inmarket = portfolio.inmarket - amt;
135 return false;
136 }
137 return true;
138 });
139 }
140
141 for trade in tradesout.clone() {
143 calculated_returns += trade.diff;
144 }
145 }
146
147 BacktestResults {
148 calculated_returns: calculated_returns,
149 tradesin: tradesin,
150 tradesout: tradesout,
151 }
152}