engine/strategies/
avellaneda_stoikov_exact.rs1use super::Strategy;
2use crate::types::{Observation, Order, OrderRequest, Side};
3use solver::lookup::LookupTable;
4use std::sync::Arc;
5
6#[derive(Clone, Debug)]
12pub struct AvellanedaStoikovExactParameters {
13 pub t_horizon: f64,
15}
16
17#[derive(Clone)]
31pub struct AvellanedaStoikovExactStrategy {
32 pub params: AvellanedaStoikovExactParameters,
33 pub bid_spread_table: Arc<LookupTable>,
35 pub ask_spread_table: Arc<LookupTable>,
37 q_min: f64,
38 q_max: f64,
39 start_time: Option<f64>,
40 order_counter: u64,
41}
42
43impl AvellanedaStoikovExactStrategy {
44 pub fn new(
45 params: AvellanedaStoikovExactParameters,
46 bid_spread_table: Arc<LookupTable>,
47 ask_spread_table: Arc<LookupTable>,
48 ) -> Self {
49 assert_eq!(
50 bid_spread_table.axes.len(),
51 2,
52 "bid table must be 2D [q, tau]"
53 );
54 assert_eq!(
55 ask_spread_table.axes.len(),
56 2,
57 "ask table must be 2D [q, tau]"
58 );
59 let q_axis = &bid_spread_table.axes[0];
60 assert!(!q_axis.is_empty(), "q axis must not be empty");
61 let q_min = q_axis[0];
62 let q_max = q_axis[q_axis.len() - 1];
63 Self {
64 params,
65 bid_spread_table,
66 ask_spread_table,
67 q_min,
68 q_max,
69 start_time: None,
70 order_counter: 0,
71 }
72 }
73}
74
75impl Strategy for AvellanedaStoikovExactStrategy {
76 fn on_tick(&mut self, obs: &Observation, requests: &mut Vec<OrderRequest>) {
77 if self.start_time.is_none() {
78 self.start_time = Some(obs.timestamp);
79 }
80
81 let elapsed = obs.timestamp - self.start_time.unwrap();
82 let tau = (self.params.t_horizon - elapsed).max(0.0);
83
84 if tau <= 0.0 {
85 requests.push(OrderRequest::CancelAll);
86 return;
87 }
88
89 let mid = obs.mid_price();
90 let q = obs.portfolio.position;
91
92 let delta_bid = self.bid_spread_table.interpolate(&[q, tau]).max(1e-9);
94 let delta_ask = self.ask_spread_table.interpolate(&[q, tau]).max(1e-9);
95
96 let bid_price = mid - delta_bid;
98 let ask_price = mid + delta_ask;
99
100 let place_bid = q < self.q_max;
104 let place_ask = q > self.q_min;
105
106 requests.push(OrderRequest::CancelAll);
107 if place_bid {
108 self.order_counter += 1;
109 let bid_id = self.order_counter;
110 requests.push(OrderRequest::New(Order::new(
111 bid_id,
112 Side::Buy,
113 bid_price,
114 1.0,
115 )));
116 }
117 if place_ask {
118 self.order_counter += 1;
119 let ask_id = self.order_counter;
120 requests.push(OrderRequest::New(Order::new(
121 ask_id,
122 Side::Sell,
123 ask_price,
124 1.0,
125 )));
126 }
127 }
128
129 fn as_any(&self) -> &dyn std::any::Any {
130 self
131 }
132 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
133 self
134 }
135}