trader_bot/strategy/
settings.rs1use chrono::NaiveTime;
4use rust_decimal::Decimal;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct TradingSettings {
10 pub instrument: String,
12
13 pub minimum_profit_steps: u32,
15
16 pub minimum_market_order_size_to_buy: u64,
18
19 pub minimum_market_order_size_to_sell: u64,
21
22 pub minimum_market_order_size_to_change_buy_price: u64,
24
25 pub minimum_market_order_size_to_change_sell_price: u64,
27
28 pub market_order_book_depth: usize,
30
31 pub max_position_lots: u64,
33
34 pub lot_size: u64,
36
37 pub minimum_time_to_buy: Option<NaiveTime>,
39
40 pub maximum_time_to_buy: Option<NaiveTime>,
42
43 pub early_sell_owned_lots_delta: Option<u32>,
45
46 pub early_sell_owned_lots_multiplier: Option<Decimal>,
48
49 pub price_step: Decimal,
51
52 pub enabled: bool,
54}
55
56impl Default for TradingSettings {
57 fn default() -> Self {
58 Self {
59 instrument: String::new(),
60 minimum_profit_steps: 1,
61 minimum_market_order_size_to_buy: 10,
62 minimum_market_order_size_to_sell: 10,
63 minimum_market_order_size_to_change_buy_price: 50,
64 minimum_market_order_size_to_change_sell_price: 50,
65 market_order_book_depth: 5,
66 max_position_lots: 100,
67 lot_size: 1,
68 minimum_time_to_buy: None,
69 maximum_time_to_buy: None,
70 early_sell_owned_lots_delta: None,
71 early_sell_owned_lots_multiplier: None,
72 price_step: Decimal::new(1, 2), enabled: true,
74 }
75 }
76}
77
78impl TradingSettings {
79 #[must_use]
81 pub fn new(instrument: impl Into<String>) -> Self {
82 Self {
83 instrument: instrument.into(),
84 ..Default::default()
85 }
86 }
87
88 #[must_use]
90 pub fn with_minimum_profit_steps(mut self, steps: u32) -> Self {
91 self.minimum_profit_steps = steps;
92 self
93 }
94
95 #[must_use]
97 pub fn with_price_step(mut self, step: Decimal) -> Self {
98 self.price_step = step;
99 self
100 }
101
102 #[must_use]
104 pub fn with_lot_size(mut self, size: u64) -> Self {
105 self.lot_size = size;
106 self
107 }
108
109 #[must_use]
111 pub fn with_max_position(mut self, lots: u64) -> Self {
112 self.max_position_lots = lots;
113 self
114 }
115
116 #[must_use]
118 pub fn with_order_book_depth(mut self, depth: usize) -> Self {
119 self.market_order_book_depth = depth;
120 self
121 }
122
123 #[must_use]
125 pub fn with_min_order_size_to_buy(mut self, size: u64) -> Self {
126 self.minimum_market_order_size_to_buy = size;
127 self
128 }
129
130 #[must_use]
132 pub fn with_min_order_size_to_sell(mut self, size: u64) -> Self {
133 self.minimum_market_order_size_to_sell = size;
134 self
135 }
136
137 #[must_use]
139 pub fn with_trading_hours(mut self, start: NaiveTime, end: NaiveTime) -> Self {
140 self.minimum_time_to_buy = Some(start);
141 self.maximum_time_to_buy = Some(end);
142 self
143 }
144
145 #[must_use]
147 pub fn with_enabled(mut self, enabled: bool) -> Self {
148 self.enabled = enabled;
149 self
150 }
151
152 #[must_use]
154 pub fn minimum_profit(&self) -> Decimal {
155 self.price_step * Decimal::from(self.minimum_profit_steps)
156 }
157
158 #[must_use]
160 pub fn is_trading_time(&self, time: NaiveTime) -> bool {
161 match (self.minimum_time_to_buy, self.maximum_time_to_buy) {
162 (Some(start), Some(end)) => time >= start && time <= end,
163 (Some(start), None) => time >= start,
164 (None, Some(end)) => time <= end,
165 (None, None) => true,
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173
174 #[test]
175 fn test_default_settings() {
176 let settings = TradingSettings::default();
177 assert_eq!(settings.minimum_profit_steps, 1);
178 assert!(settings.enabled);
179 }
180
181 #[test]
182 fn test_builder_pattern() {
183 let settings = TradingSettings::new("TEST")
184 .with_minimum_profit_steps(2)
185 .with_price_step(Decimal::new(1, 1))
186 .with_max_position(50);
187
188 assert_eq!(settings.instrument, "TEST");
189 assert_eq!(settings.minimum_profit_steps, 2);
190 assert_eq!(settings.price_step, Decimal::new(1, 1));
191 assert_eq!(settings.max_position_lots, 50);
192 }
193
194 #[test]
195 fn test_minimum_profit() {
196 let settings = TradingSettings::new("TEST")
197 .with_minimum_profit_steps(3)
198 .with_price_step(Decimal::new(1, 2)); assert_eq!(settings.minimum_profit(), Decimal::new(3, 2)); }
202
203 #[test]
204 fn test_trading_time() {
205 let settings = TradingSettings::new("TEST").with_trading_hours(
206 NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
207 NaiveTime::from_hms_opt(17, 0, 0).unwrap(),
208 );
209
210 assert!(settings.is_trading_time(NaiveTime::from_hms_opt(12, 0, 0).unwrap()));
211 assert!(!settings.is_trading_time(NaiveTime::from_hms_opt(8, 0, 0).unwrap()));
212 assert!(!settings.is_trading_time(NaiveTime::from_hms_opt(18, 0, 0).unwrap()));
213 }
214
215 #[test]
216 fn test_no_trading_time_restrictions() {
217 let settings = TradingSettings::new("TEST");
218 assert!(settings.is_trading_time(NaiveTime::from_hms_opt(3, 0, 0).unwrap()));
219 assert!(settings.is_trading_time(NaiveTime::from_hms_opt(23, 0, 0).unwrap()));
220 }
221}