|
|
@@ -14,7 +14,8 @@ use standard::{Depth, Ticker, Trade};
|
|
|
#[derive(Debug)]
|
|
|
pub struct AvellanedaStoikov {
|
|
|
pub depth_vec: FixedTimeRangeDeque<Depth>, // 深度队列
|
|
|
- pub trade_vec: FixedTimeRangeDeque<Trade>, // 交易队列
|
|
|
+ pub trade_long_vec: FixedTimeRangeDeque<Trade>, // 交易队列
|
|
|
+ pub trade_short_vec: FixedTimeRangeDeque<Trade>, // 交易队列
|
|
|
pub spread_vec: FixedTimeRangeDeque<Decimal>,
|
|
|
|
|
|
pub mid_price: Decimal, // 中间价
|
|
|
@@ -33,9 +34,8 @@ pub struct AvellanedaStoikov {
|
|
|
pub gamma: Decimal, // γ,库存风险厌恶参数
|
|
|
pub kappa: Decimal, // κ 订单簿 流动性 参数
|
|
|
|
|
|
- pub flow_in_value: Decimal,
|
|
|
- pub flow_out_value: Decimal,
|
|
|
- pub flow_ratio: Decimal, // 资金流比例
|
|
|
+ pub flow_ratio_long: Decimal, // 资金流比例
|
|
|
+ pub flow_ratio_short: Decimal, // 资金流比例
|
|
|
|
|
|
pub ask_delta: Decimal, // δa
|
|
|
pub bid_delta: Decimal, // δb
|
|
|
@@ -54,6 +54,8 @@ pub struct AvellanedaStoikov {
|
|
|
impl AvellanedaStoikov {
|
|
|
// 时间窗口大小(微秒)
|
|
|
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);
|
|
|
// const TWENTY_THOUSAND: Decimal = dec!(20_000);
|
|
|
const IRA: Decimal = dec!(1);
|
|
|
@@ -62,8 +64,9 @@ impl AvellanedaStoikov {
|
|
|
let avellaneda_stoikov = Self {
|
|
|
// 分别给与的长度
|
|
|
depth_vec: FixedTimeRangeDeque::new(Self::MAX_TIME_RANGE_MICROS),
|
|
|
- trade_vec: FixedTimeRangeDeque::new(Self::MAX_TIME_RANGE_MICROS),
|
|
|
spread_vec: FixedTimeRangeDeque::new(Self::MAX_TIME_RANGE_MICROS),
|
|
|
+ trade_long_vec: FixedTimeRangeDeque::new(Self::TRADE_LONG_RANGE_MICROS),
|
|
|
+ trade_short_vec: FixedTimeRangeDeque::new(Self::TRADE_SHORT_RANGE_MICROS),
|
|
|
|
|
|
mid_price: Default::default(),
|
|
|
ask_price: Default::default(),
|
|
|
@@ -85,7 +88,6 @@ impl AvellanedaStoikov {
|
|
|
|
|
|
ratio_edge: Default::default(),
|
|
|
kappa: Default::default(),
|
|
|
- flow_in_value: Default::default(),
|
|
|
ref_price: Default::default(),
|
|
|
|
|
|
cci_arc,
|
|
|
@@ -93,9 +95,9 @@ impl AvellanedaStoikov {
|
|
|
is_ready: false,
|
|
|
prev_trade_time: Utc::now().timestamp_micros(),
|
|
|
t_diff: Default::default(),
|
|
|
- flow_ratio: Decimal::ONE,
|
|
|
- flow_out_value: Default::default(),
|
|
|
+ flow_ratio_long: Decimal::ONE,
|
|
|
level: Default::default(),
|
|
|
+ flow_ratio_short: Default::default(),
|
|
|
};
|
|
|
|
|
|
avellaneda_stoikov
|
|
|
@@ -120,14 +122,14 @@ impl AvellanedaStoikov {
|
|
|
}
|
|
|
|
|
|
pub fn update_spread(&mut self) {
|
|
|
- if self.trade_vec.len() > 0 {
|
|
|
+ if self.trade_long_vec.len() > 0 {
|
|
|
//
|
|
|
- let last_trade = self.trade_vec.get(self.trade_vec.len() - 1).unwrap();
|
|
|
+ let last_trade = self.trade_long_vec.get(self.trade_long_vec.len() - 1).unwrap();
|
|
|
let last_trade_price = last_trade.price;
|
|
|
let last_trade_time = last_trade.time;
|
|
|
|
|
|
let mut first_trade_price = last_trade.price;
|
|
|
- for trade in self.trade_vec.deque.iter().rev() {
|
|
|
+ for trade in self.trade_long_vec.deque.iter().rev() {
|
|
|
if last_trade_time - trade.time > Decimal::TEN {
|
|
|
break;
|
|
|
}
|
|
|
@@ -156,22 +158,10 @@ impl AvellanedaStoikov {
|
|
|
}
|
|
|
|
|
|
pub async fn on_trade(&mut self, trade: &Trade) {
|
|
|
- self.trade_vec.push_back(trade.clone());
|
|
|
- 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;
|
|
|
- }
|
|
|
- }
|
|
|
+ self.trade_long_vec.push_back(trade.clone());
|
|
|
+ self.trade_short_vec.push_back(trade.clone());
|
|
|
|
|
|
- if self.last_price != trade.price {
|
|
|
- self.last_price = trade.price;
|
|
|
- } else {
|
|
|
- self.last_price = Decimal::ZERO;
|
|
|
- }
|
|
|
+ self.last_price = trade.price;
|
|
|
self.update_spread();
|
|
|
self.processor().await;
|
|
|
}
|
|
|
@@ -233,7 +223,7 @@ impl AvellanedaStoikov {
|
|
|
let pos_edge = self.gamma * self.sigma_square * self.inventory.abs().powd(Decimal::TWO) * self.t_diff / Decimal::TWO;
|
|
|
|
|
|
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 * self.sigma_square;
|
|
|
+ self.ratio_edge = self.flow_ratio_long * self.sigma_square;
|
|
|
|
|
|
self.bid_delta = self.base_delta;
|
|
|
self.ask_delta = self.base_delta;
|
|
|
@@ -272,14 +262,96 @@ impl AvellanedaStoikov {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn update_flow_ratio(&mut self) {
|
|
|
- if self.trade_vec.len() > 200 {
|
|
|
- self.flow_ratio = (self.flow_in_value - self.flow_out_value) / (self.flow_out_value + self.flow_in_value);
|
|
|
+ fn calc_flow_ratio(_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 * (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();
|
|
|
+ // if trade.price > prev_trade_iter.price {
|
|
|
+ // self.flow_in_value += trade.value;
|
|
|
+ // } else if trade.price < prev_trade_iter.price {
|
|
|
+ // self.flow_out_value += trade.value;
|
|
|
+ // } else {
|
|
|
+ // // if trade.size > Decimal::ZERO {
|
|
|
+ // // self.flow_in_value += trade.value;
|
|
|
+ // // } else {
|
|
|
+ // // self.flow_out_value += trade.value;
|
|
|
+ // // }
|
|
|
+ // }
|
|
|
+ //
|
|
|
+ // // if trade.size > Decimal::ZERO {
|
|
|
+ // // self.flow_in_value += trade.value;
|
|
|
+ // // } else {
|
|
|
+ // // self.flow_out_value += trade.value;
|
|
|
+ // // }
|
|
|
+ //
|
|
|
+ // if self.flow_out_value + self.flow_in_value > dec!(2_000_000) {
|
|
|
+ // self.flow_out_value = self.flow_out_value * dec!(0.618);
|
|
|
+ // self.flow_in_value = self.flow_in_value * dec!(0.618);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // else {
|
|
|
+ // if trade.size > Decimal::ZERO {
|
|
|
+ // self.flow_in_value += trade.value;
|
|
|
+ // } else {
|
|
|
+ // self.flow_out_value += trade.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 {
|
|
|
- self.flow_ratio = Decimal::ZERO;
|
|
|
+ Decimal::ZERO
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ pub fn update_flow_ratio(&mut self) {
|
|
|
+ self.flow_ratio_long = Self::calc_flow_ratio(&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_short_vec);
|
|
|
+ }
|
|
|
+
|
|
|
pub fn check_ready(&mut self) {
|
|
|
if self.is_ready {
|
|
|
return;
|
|
|
@@ -309,7 +381,7 @@ impl AvellanedaStoikov {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if self.trade_vec.len() < 100 {
|
|
|
+ if self.trade_long_vec.len() < 100 {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -345,7 +417,7 @@ impl AvellanedaStoikov {
|
|
|
cci.predictor_state_vec.push_back(PredictorState {
|
|
|
update_time: Decimal::from_i64(Utc::now().timestamp_millis()).unwrap(),
|
|
|
|
|
|
- mid_price: self.mid_price,
|
|
|
+ mid_price: self.last_price,
|
|
|
ask_price: self.ask_price,
|
|
|
bid_price: self.bid_price,
|
|
|
last_price: self.last_price,
|
|
|
@@ -356,11 +428,11 @@ impl AvellanedaStoikov {
|
|
|
optimal_bid_price: self.optimal_bid_price,
|
|
|
|
|
|
inventory: self.inventory,
|
|
|
- sigma_square: self.flow_ratio,
|
|
|
- gamma: self.ratio_edge,
|
|
|
+ sigma_square: self.flow_ratio_long,
|
|
|
+ gamma: self.flow_ratio_short,
|
|
|
kappa: self.t_diff,
|
|
|
|
|
|
- flow_ratio: self.flow_ratio,
|
|
|
+ flow_ratio: self.flow_ratio_long,
|
|
|
ref_price: self.ref_price,
|
|
|
});
|
|
|
}
|