rs_backtester/
strategies.rs

1use csv::Writer;
2use crate::datas::Data;
3use crate::orders::Order;
4use crate::orders::Order::{BUY,SHORTSELL,NULL};
5use std::error::Error;
6use crate::ta::{Indicator,sma,rsi};
7use serde::{Serialize};
8
9/// Struct to hold vector of choices and indicators<BR>
10/// There is no specific constructor<BR>
11/// Need to be created via a user-defined function which return a Strategy
12#[derive(Clone, Serialize)]
13pub struct Strategy{
14    pub name:String,
15    pub choices:Vec<Order>,
16    pub indicator:Option<Vec<Vec<f64>>>,
17}
18
19impl Strategy{
20    pub fn choices(&self)->Vec<Order>{
21        return self.choices.clone();
22    }
23    pub fn name(&self)->&String{ return &self.name;}
24    pub fn indicator(&self)->Option<Vec<Vec<f64>>>{ return self.indicator.clone();}
25    pub fn invert(&self) ->Self{
26        let length = self.choices.len();
27        let mut inv_choices = self.choices.clone();
28        for i in 0..length{
29            if self.choices[i]==BUY { inv_choices[i]=SHORTSELL} else if self.choices[i]==SHORTSELL { inv_choices[i]=BUY}
30        }
31        let indicator = self.indicator.clone();
32        Strategy{
33            name:self.name.clone()+"_inv",
34            choices: inv_choices,
35            indicator,
36        }
37    }
38    pub fn long_only(&self) ->Self{
39        let length = self.choices.len();
40        let mut long_choices = self.choices.clone();
41        for i in 0..length{
42            if self.choices[i]==SHORTSELL { long_choices[i]=NULL}
43        }
44        let indicator = self.indicator.clone();
45        Strategy{
46            name:self.name.clone()+"_long",
47            choices: long_choices,
48            indicator,
49        }
50    }
51    pub fn short_only(&self) ->Self{
52        let length = self.choices.len();
53        let mut short_choices = self.choices.clone();
54        for i in 0..length{
55            if self.choices[i]==BUY { short_choices[i]=NULL}
56        }
57        let indicator = self.indicator.clone();
58        Strategy{
59            name:self.name.clone()+"_short",
60            choices: short_choices,
61            indicator,
62        }
63    }
64    pub fn to_csv(&self, filename:&str)->Result<(),Box<dyn Error>>{
65        let mut wrt = Writer::from_path(filename)?;
66        let choices_transpose:Vec<Vec<String>>= self.choices.iter().map(|e|vec![e.clone().to_string().to_string()]).collect();
67        wrt.serialize("choices")?;
68        for col in choices_transpose.iter(){
69        wrt.serialize(col)?;}
70        wrt.flush()?;
71        Ok(())
72    }
73}
74
75///Returns typical Buy and Hold Strategy
76pub fn buy_n_hold(quotes:Data)->Strategy{
77    let length = quotes.timestamps().len();
78    let choices = vec![BUY;length];
79    let name = "buy_and_hold".to_string();
80    let indicator = Some(vec![vec![-1.;length]]);
81    Strategy{
82        name:name,
83        choices:choices,
84        indicator,
85    }
86}
87///Returns the opposite of a Buy and Hold Strategy:
88/// start by shortselling and keep the short position open to the end
89pub fn short_n_hold(quotes:Data)->Strategy{
90    let length = quotes.timestamps().len();
91    let choices = vec![SHORTSELL;length];
92    let name = "short and hold".to_string();
93    let indicator = Some(vec![vec![-1.;length]]);
94    Strategy{
95        name:name,
96        choices:choices,
97        indicator,
98    }
99}
100///Returns a Strategy which does exactly nothing (i.e. always stays out of the market)
101pub fn do_nothing(quotes:Data)->Strategy{
102    let length = quotes.timestamps().len();
103    let choices = vec![NULL;length];
104    let name = "do nothing".to_string();
105    let indicator = Some(vec![vec![-1.;length]]);
106    Strategy{
107        name:name,
108        choices:choices,
109        indicator,
110    }
111}
112///Returns a Simple Moving Average Strategy with a user specified time-period
113pub fn simple_sma(quotes:Data, period:usize) ->Strategy{
114    let sma = sma(&quotes,period);
115    let indicator = Indicator{indicator:sma,quotes:quotes};
116    let length = indicator.quotes.timestamps().len();
117    let mut choices = vec![NULL;length];
118    for i in 0..length{
119        if indicator.indicator[i]!=-1.{
120            if indicator.indicator[i]<=indicator.quotes.close()[i]{
121                choices[i] = BUY;
122            }else if indicator.indicator[i]>indicator.quotes.close()[i]{
123               choices[i] = SHORTSELL}
124        }
125    }
126    let name = format!("simple_sma_{}",period);
127    let indicator = Some(vec![indicator.indicator()]);
128    Strategy{
129        name:name,
130        choices:choices,
131        indicator,
132    }
133}
134///Returns a Simple Moving Average Crossing Strategy (i.e. goes long when SMA short crosses SMA long and shortsells otherwise)<BR>
135///User can specify both time-periods (short and long, with short first)
136pub fn sma_cross(quotes:Data, short_period:usize, long_period:usize)->Strategy{
137    if short_period >= long_period {panic!("Error: short SMA parameter should be shorter than long SMA parameter");}
138    let sma_short = sma(&quotes, short_period);
139    let sma_long = sma(&quotes, long_period);
140    let ind_short = Indicator{indicator:sma_short,quotes:quotes.clone()};
141    let ind_long = Indicator{indicator:sma_long, quotes:quotes.clone()};
142    let length = ind_short.quotes().timestamps().len();
143    let mut choices = vec![NULL;length];
144    for i in 0..length{
145        if ind_long.indicator()[i]!=-1.{
146            if ind_short.indicator()[i]>ind_long.indicator()[i]{choices[i]=BUY}
147            else {choices[i]=SHORTSELL};
148        }
149    }
150    let name=format!("sma_cross_{}_{}",short_period,long_period);
151    let indicator = Some(vec![ind_short.indicator(),ind_long.indicator()]);
152    Strategy{
153        name:name,
154        choices:choices,
155        indicator:indicator,
156    }
157}
158///Returns a Relative Strength Index Strategy (i.e. goes short if RSI > 70, long when RSI < 30, and stay out of market elsewhere)
159pub fn rsi_strategy(quotes:Data, period:usize)->Strategy{
160    let rsi = rsi(&quotes,period);
161    let indicator = Indicator{indicator:rsi,quotes};
162    let length = indicator.quotes().timestamps().len();
163    let mut choices = vec![NULL;length];
164    for i in 0..length{
165        if indicator.indicator()[i]!=-1.{
166            if indicator.indicator()[i]>70.{choices[i]=SHORTSELL}
167            else if indicator.indicator()[i]<30. {choices[i]=BUY}
168        }
169    }
170    let name = format!("rsi_{}",period);
171    let indicator=Some(vec![indicator.indicator()]);
172    Strategy{
173        name,
174        choices,
175        indicator,
176    }
177}