|
|
@@ -48,17 +48,12 @@ pub struct AvellanedaStoikov {
|
|
|
|
|
|
pub is_ready: bool,
|
|
|
pub prev_trade_time: i64, // 上次交易时间,也就是t
|
|
|
- pub cross_time: i64, // 上次穿過0軸的時間
|
|
|
- pub cross_time_diff: i64, // 穿越0軸的diff
|
|
|
- pub cross_time_diff_avg: i64, // 穿越0軸的diff的平均值
|
|
|
pub t_diff: Decimal, // (T-t)
|
|
|
- pub prev_close_time: i64, // 上次平倉時間
|
|
|
- pub prev_prev_close_time: i64, // 上上次平倉時間
|
|
|
}
|
|
|
|
|
|
impl AvellanedaStoikov {
|
|
|
// 时间窗口大小(微秒)
|
|
|
- const MAX_TIME_RANGE_MICROS: i64 = 5 * 60_000_000;
|
|
|
+ const MAX_TIME_RANGE_MICROS: i64 = 3 * 60_000_000;
|
|
|
const TRADE_LONG_RANGE_MICROS: i64 = 3 * 60_000_000;
|
|
|
const TRADE_SHORT_RANGE_MICROS: i64 = 20_000_000;
|
|
|
// const ONE_MILLION: Decimal = dec!(1_000_000);
|
|
|
@@ -99,14 +94,9 @@ impl AvellanedaStoikov {
|
|
|
|
|
|
is_ready: false,
|
|
|
prev_trade_time: Utc::now().timestamp_micros(),
|
|
|
- cross_time: 0,
|
|
|
- cross_time_diff: 0,
|
|
|
- cross_time_diff_avg: 0,
|
|
|
t_diff: Default::default(),
|
|
|
flow_ratio_long: Decimal::ONE,
|
|
|
level: Default::default(),
|
|
|
- prev_close_time: Utc::now().timestamp_micros(),
|
|
|
- prev_prev_close_time: Utc::now().timestamp_micros(),
|
|
|
flow_ratio_short: Default::default(),
|
|
|
};
|
|
|
|
|
|
@@ -185,16 +175,12 @@ impl AvellanedaStoikov {
|
|
|
self.prev_trade_time = Utc::now().timestamp_micros();
|
|
|
self.inventory = (inventory / (min_amount_value / self.mid_price)).round();
|
|
|
|
|
|
- // if self.inventory.is_zero() {
|
|
|
- // self.prev_prev_close_time = self.prev_close_time;
|
|
|
- // self.prev_close_time = Utc::now().timestamp_millis();
|
|
|
- // }
|
|
|
self.update_level().await;
|
|
|
self.processor().await;
|
|
|
}
|
|
|
|
|
|
pub fn update_sigma_square(&mut self) {
|
|
|
- self.sigma_square = self.spread_max * dec!(1.618);
|
|
|
+ self.sigma_square = self.spread_max * dec!(0.5);
|
|
|
self.sigma_square.rescale(10);
|
|
|
}
|
|
|
|
|
|
@@ -206,7 +192,7 @@ impl AvellanedaStoikov {
|
|
|
// };
|
|
|
// self.gamma.rescale(8);
|
|
|
|
|
|
- self.gamma = dec!(1) * Self::IRA;
|
|
|
+ self.gamma = dec!(0.236) * Self::IRA;
|
|
|
}
|
|
|
|
|
|
pub fn update_kappa(&mut self) {
|
|
|
@@ -233,56 +219,43 @@ impl AvellanedaStoikov {
|
|
|
}
|
|
|
|
|
|
pub fn update_delta(&mut self) {
|
|
|
- let pos_edge = self.gamma * self.sigma_square * self.inventory.abs().powd(Decimal::TWO) * self.t_diff;
|
|
|
-
|
|
|
- self.base_delta = self.sigma_square;
|
|
|
+ if self.gamma != Decimal::ZERO {
|
|
|
+ // let pos_edge = if self.inventory.abs() < dec!(5) {
|
|
|
+ // self.gamma * self.sigma_square * self.inventory.abs().powd(Decimal::TWO) * self.t_diff
|
|
|
+ // } else {
|
|
|
+ // Decimal::PI * self.gamma * self.sigma_square * self.inventory.abs().powd(Decimal::TWO) * self.t_diff
|
|
|
+ // };
|
|
|
+ let pos_edge = self.gamma * self.sigma_square * self.inventory.abs().powd(dec!(2)) * self.t_diff;
|
|
|
|
|
|
- self.bid_delta = self.base_delta;
|
|
|
- self.ask_delta = self.base_delta;
|
|
|
+ self.base_delta = self.gamma * self.sigma_square * self.t_diff / Decimal::TWO + (Decimal::ONE / self.gamma) * (Decimal::ONE + self.gamma / self.kappa).ln();
|
|
|
+ self.ratio_edge = self.flow_ratio_long * self.sigma_square;
|
|
|
|
|
|
- if self.inventory > Decimal::ZERO {
|
|
|
- self.bid_delta += pos_edge;
|
|
|
- } else if self.inventory < Decimal::ZERO {
|
|
|
- self.ask_delta += pos_edge;
|
|
|
- }
|
|
|
+ self.bid_delta = self.base_delta;
|
|
|
+ self.ask_delta = self.base_delta;
|
|
|
|
|
|
- if self.flow_ratio_long.is_zero() || self.cross_time_diff < 20 {
|
|
|
- self.ask_delta += self.base_delta;
|
|
|
- self.bid_delta += self.base_delta;
|
|
|
-
|
|
|
- return;
|
|
|
- }
|
|
|
+ if self.inventory > Decimal::ZERO {
|
|
|
+ self.bid_delta += pos_edge;
|
|
|
+ } else if self.inventory < Decimal::ZERO {
|
|
|
+ self.ask_delta += pos_edge;
|
|
|
+ }
|
|
|
|
|
|
- if self.flow_ratio_long < Decimal::ZERO {
|
|
|
- if self.flow_ratio_short > Decimal::ZERO {
|
|
|
- self.ask_delta -= self.base_delta * (self.flow_ratio_short.abs() * Decimal::PI);
|
|
|
- self.bid_delta += self.base_delta;
|
|
|
- } else if self.flow_ratio_short < Decimal::ZERO && self.inventory < Decimal::ZERO {
|
|
|
- self.ask_delta += self.base_delta;
|
|
|
- // self.bid_delta -= self.base_delta * (self.flow_ratio_short.abs() + self.flow_ratio_long.abs());
|
|
|
- self.bid_delta -= self.base_delta * (self.flow_ratio_short.abs() * dec!(1.5));
|
|
|
- } else {
|
|
|
- self.ask_delta += self.base_delta;
|
|
|
- self.bid_delta += self.base_delta;
|
|
|
+ if self.ratio_edge > Decimal::ZERO {
|
|
|
+ self.ask_delta = self.ask_delta - self.ratio_edge.abs() * (Decimal::TWO - self.t_diff);
|
|
|
+ self.bid_delta = self.bid_delta + self.ratio_edge.abs() * dec!(16);
|
|
|
+ } else if self.ratio_edge < Decimal::ZERO {
|
|
|
+ self.ask_delta = self.ask_delta + self.ratio_edge.abs() * dec!(16);
|
|
|
+ self.bid_delta = self.bid_delta - self.ratio_edge.abs() * (Decimal::TWO - self.t_diff);
|
|
|
}
|
|
|
- } else if self.flow_ratio_long > Decimal::ZERO {
|
|
|
- if self.flow_ratio_short > Decimal::ZERO && self.inventory > Decimal::ZERO {
|
|
|
- // self.ask_delta -= self.base_delta * (self.flow_ratio_short.abs() + self.flow_ratio_long.abs());
|
|
|
- self.ask_delta -= self.base_delta * (self.flow_ratio_short.abs() * dec!(1.5));
|
|
|
- self.bid_delta += self.base_delta;
|
|
|
- } else if self.flow_ratio_short < Decimal::ZERO {
|
|
|
- self.ask_delta += self.base_delta;
|
|
|
- self.bid_delta -= self.base_delta * (self.flow_ratio_short.abs() * Decimal::PI);
|
|
|
- } else {
|
|
|
- self.ask_delta += self.base_delta;
|
|
|
- self.bid_delta += self.base_delta;
|
|
|
+
|
|
|
+ if self.init_delta_plus.is_zero() {
|
|
|
+ self.init_delta_plus = (self.bid_delta + self.ask_delta) / Decimal::TWO
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
pub fn update_optimal_ask_and_bid(&mut self) {
|
|
|
- self.optimal_ask_price = max(self.ref_price + self.ask_delta / dec!(2), self.ask_price);
|
|
|
- self.optimal_bid_price = min(self.ref_price - self.bid_delta / dec!(2), self.bid_price);
|
|
|
+ self.optimal_ask_price = max(self.ref_price + self.ask_delta / Decimal::TWO, self.ask_price);
|
|
|
+ self.optimal_bid_price = min(self.ref_price - self.bid_delta / Decimal::TWO, self.bid_price);
|
|
|
}
|
|
|
|
|
|
pub fn update_t_diff(&mut self) {
|
|
|
@@ -295,46 +268,35 @@ impl AvellanedaStoikov {
|
|
|
}
|
|
|
|
|
|
fn calc_flow_ratio(_prev_flow_ratio: &Decimal, min_volume: &Decimal, trades: &mut FixedTimeRangeDeque<Trade>) -> Decimal {
|
|
|
- // 使用EMA來更新資金流,確保平滑性
|
|
|
- // let a = Decimal::TWO / dec!(50);
|
|
|
-
|
|
|
- let mut flow_in_value = Decimal::ZERO;
|
|
|
- let mut flow_out_value = Decimal::ZERO;
|
|
|
- for (index, trade_iter) in trades.deque.iter().enumerate() {
|
|
|
- if index == 0 {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- let prev_trade_iter = trades.deque.get(index - 1).unwrap();
|
|
|
- let trade = trade_iter;
|
|
|
- if trade.price > prev_trade_iter.price {
|
|
|
- flow_in_value += trade.value * (prev_trade_iter.price - trade.price).abs();
|
|
|
- // flow_in_value += Decimal::ONE;
|
|
|
- } else if trade.price < prev_trade_iter.price {
|
|
|
- flow_out_value += trade.value * (prev_trade_iter.price - trade.price).abs();
|
|
|
- // flow_out_value += Decimal::ONE;
|
|
|
- } else {
|
|
|
- // if trade.size > Decimal::ZERO {
|
|
|
- // flow_in_value += trade.value;
|
|
|
- // } else {
|
|
|
- // flow_out_value += trade.value;
|
|
|
- // }
|
|
|
- }
|
|
|
-
|
|
|
- // if trade_iter.size > Decimal::ZERO {
|
|
|
- // flow_in_value += trade_iter.value;
|
|
|
- // } else {
|
|
|
- // flow_out_value += trade_iter.value;
|
|
|
- // }
|
|
|
- }
|
|
|
-
|
|
|
- if flow_out_value + flow_in_value > *min_volume {
|
|
|
- // let now = (flow_in_value - flow_out_value) / (flow_out_value + flow_in_value);
|
|
|
- // a * now + (Decimal::ONE - a) * prev_flow_ratio
|
|
|
- (flow_in_value - flow_out_value) / (flow_out_value + flow_in_value)
|
|
|
- } else {
|
|
|
- Decimal::ZERO
|
|
|
- }
|
|
|
+ // let mut flow_in_value = Decimal::ZERO;
|
|
|
+ // let mut flow_out_value = Decimal::ZERO;
|
|
|
+ // for (index, trade_iter) in trades.deque.iter().enumerate() {
|
|
|
+ // if index == 0 {
|
|
|
+ // continue
|
|
|
+ // }
|
|
|
+ //
|
|
|
+ // let prev_trade_iter = trades.deque.get(index - 1).unwrap();
|
|
|
+ // let trade = trade_iter;
|
|
|
+ // if trade.price > prev_trade_iter.price {
|
|
|
+ // flow_in_value += trade.value * (prev_trade_iter.price - trade.price).abs();
|
|
|
+ // // flow_in_value += Decimal::ONE;
|
|
|
+ // } else if trade.price < prev_trade_iter.price {
|
|
|
+ // flow_out_value += trade.value * (prev_trade_iter.price - trade.price).abs();
|
|
|
+ // // flow_out_value += Decimal::ONE;
|
|
|
+ // } else {
|
|
|
+ // // if trade.size > Decimal::ZERO {
|
|
|
+ // // flow_in_value += trade.value;
|
|
|
+ // // } else {
|
|
|
+ // // flow_out_value += trade.value;
|
|
|
+ // // }
|
|
|
+ // }
|
|
|
+ //
|
|
|
+ // // if trade_iter.size > Decimal::ZERO {
|
|
|
+ // // flow_in_value += trade_iter.value;
|
|
|
+ // // } else {
|
|
|
+ // // flow_out_value += trade_iter.value;
|
|
|
+ // // }
|
|
|
+ // }
|
|
|
|
|
|
// if self.trade_vec.deque.len() > 1 {
|
|
|
// let prev_trade_iter = self.trade_vec.deque.get(self.trade_vec.deque.len() - 2).unwrap();
|
|
|
@@ -369,42 +331,66 @@ impl AvellanedaStoikov {
|
|
|
// }
|
|
|
// }
|
|
|
|
|
|
- // self.flow_in_value = Decimal::ZERO;
|
|
|
- // self.flow_out_value = Decimal::ZERO;
|
|
|
- // for trade_iter in self.trade_vec.deque.iter() {
|
|
|
- // if trade_iter.size > Decimal::ZERO {
|
|
|
- // self.flow_in_value += trade_iter.value;
|
|
|
- // } else {
|
|
|
- // self.flow_out_value += trade_iter.value;
|
|
|
- // }
|
|
|
- // }
|
|
|
+ let mut flow_in_value = Decimal::ZERO;
|
|
|
+ let mut flow_out_value = Decimal::ZERO;
|
|
|
+ for trade_iter in trades.deque.iter() {
|
|
|
+ if trade_iter.size > Decimal::ZERO {
|
|
|
+ flow_in_value += trade_iter.value;
|
|
|
+ } else {
|
|
|
+ flow_out_value += trade_iter.value;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用EMA來更新資金流,確保平滑性
|
|
|
+ // let a = Decimal::TWO / dec!(50);
|
|
|
+ if flow_out_value + flow_in_value > *min_volume {
|
|
|
+ // let now = (flow_in_value - flow_out_value) / (flow_out_value + flow_in_value);
|
|
|
+ // a * now + (Decimal::ONE - a) * prev_flow_ratio
|
|
|
+ (flow_in_value - flow_out_value) / (flow_out_value + flow_in_value)
|
|
|
+ } else {
|
|
|
+ Decimal::ZERO
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- pub fn update_flow_ratio(&mut self) {
|
|
|
- let prev_flow_ratio_long = self.flow_ratio_long;
|
|
|
- self.flow_ratio_long = Self::calc_flow_ratio(&self.flow_ratio_long, &dec!(0), &mut self.trade_long_vec);
|
|
|
- let time = Utc::now().timestamp_millis();
|
|
|
- // let mut is_cross = false;
|
|
|
- if (self.flow_ratio_long > Decimal::ZERO && prev_flow_ratio_long <= Decimal::ZERO)
|
|
|
- || (self.flow_ratio_long < Decimal::ZERO && prev_flow_ratio_long >= Decimal::ZERO) {
|
|
|
- self.cross_time = time;
|
|
|
- // is_cross = true;
|
|
|
+ fn calc_flow_ratio_2(_prev_flow_ratio: &Decimal, min_volume: &Decimal, trades: &mut FixedTimeRangeDeque<Trade>) -> Decimal {
|
|
|
+ let mut flow_in_value = Decimal::ZERO;
|
|
|
+ let mut flow_out_value = Decimal::ZERO;
|
|
|
+ for (index, trade_iter) in trades.deque.iter().enumerate() {
|
|
|
+ if index == 0 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ let prev_trade_iter = trades.deque.get(index - 1).unwrap();
|
|
|
+ let trade = trade_iter;
|
|
|
+ if trade.price > prev_trade_iter.price {
|
|
|
+ flow_in_value += trade.value;
|
|
|
+ // flow_in_value += Decimal::ONE;
|
|
|
+ } else if trade.price < prev_trade_iter.price {
|
|
|
+ flow_out_value += trade.value;
|
|
|
+ // flow_out_value += Decimal::ONE;
|
|
|
+ } else {
|
|
|
+ if trade.size > Decimal::ZERO {
|
|
|
+ flow_in_value += trade.value;
|
|
|
+ } else {
|
|
|
+ flow_out_value += trade.value;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- if self.cross_time != 0 {
|
|
|
- // let prev_cross_time_diff = self.cross_time_diff;
|
|
|
- self.cross_time_diff = (time - self.cross_time) / 1000;
|
|
|
-
|
|
|
- // if is_cross {
|
|
|
- // self.cross_time_diff_avg = if self.cross_time_diff_avg == 0 {
|
|
|
- // prev_cross_time_diff
|
|
|
- // } else {
|
|
|
- // (self.cross_time_diff_avg * 5 + prev_cross_time_diff * 5) / 10
|
|
|
- // };
|
|
|
- // }
|
|
|
+ // 使用EMA來更新資金流,確保平滑性
|
|
|
+ // let a = Decimal::TWO / dec!(50);
|
|
|
+ if flow_out_value + flow_in_value > *min_volume {
|
|
|
+ // let now = (flow_in_value - flow_out_value) / (flow_out_value + flow_in_value);
|
|
|
+ // a * now + (Decimal::ONE - a) * prev_flow_ratio
|
|
|
+ (flow_in_value - flow_out_value) / (flow_out_value + flow_in_value)
|
|
|
+ } else {
|
|
|
+ Decimal::ZERO
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- self.flow_ratio_short = Self::calc_flow_ratio(&self.flow_ratio_short, &dec!(0), &mut self.trade_short_vec);
|
|
|
+ pub fn update_flow_ratio(&mut self) {
|
|
|
+ self.flow_ratio_long = Self::calc_flow_ratio_2(&self.flow_ratio_long, &dec!(0), &mut self.trade_long_vec);
|
|
|
+ self.flow_ratio_short = Self::calc_flow_ratio(&self.flow_ratio_short, &dec!(0), &mut self.trade_long_vec);
|
|
|
}
|
|
|
|
|
|
pub fn check_ready(&mut self) {
|
|
|
@@ -449,7 +435,7 @@ impl AvellanedaStoikov {
|
|
|
self.update_t_diff();
|
|
|
// info!(?self.t_diff);
|
|
|
self.update_flow_ratio();
|
|
|
- // info!(?self.flow_ratio);
|
|
|
+ // info!(?self.flow_ratio_long);
|
|
|
self.update_sigma_square();
|
|
|
// info!(?self.sigma_square);
|
|
|
self.update_gamma();
|
|
|
@@ -459,7 +445,7 @@ impl AvellanedaStoikov {
|
|
|
self.update_ref_price();
|
|
|
// info!(?self.ref_price);
|
|
|
self.update_delta();
|
|
|
- // info!(?self.delta_ask, ?self.delta_bid);
|
|
|
+ // info!(?self.ask_delta, ?self.bid_delta);
|
|
|
self.update_optimal_ask_and_bid();
|
|
|
// info!("=============================================");
|
|
|
|
|
|
@@ -485,7 +471,7 @@ impl AvellanedaStoikov {
|
|
|
inventory: self.inventory,
|
|
|
sigma_square: self.flow_ratio_long,
|
|
|
gamma: self.flow_ratio_short,
|
|
|
- kappa: Decimal::from(self.cross_time_diff),
|
|
|
+ kappa: self.t_diff,
|
|
|
|
|
|
flow_ratio: self.flow_ratio_long,
|
|
|
ref_price: self.ref_price,
|