|
|
@@ -1,62 +1,14 @@
|
|
|
-use std::collections::{BTreeMap, VecDeque};
|
|
|
-use chrono::Utc;
|
|
|
+use std::collections::{BTreeMap};
|
|
|
use rust_decimal::prelude::*;
|
|
|
use tracing::info;
|
|
|
+use global::fixed_time_range_deque::FixedTimeRangeDeque;
|
|
|
use standard::{Depth, Ticker, Trade};
|
|
|
|
|
|
-// 定制的队列,可以统一指定长度
|
|
|
-#[derive(Debug)]
|
|
|
-pub struct FixedLengthDeque<T> {
|
|
|
- pub deque: VecDeque<T>,
|
|
|
- pub deque_t: VecDeque<i64>
|
|
|
-}
|
|
|
-
|
|
|
-impl<T> FixedLengthDeque<T> {
|
|
|
- // 时间窗口大小(微秒)
|
|
|
- const MAX_TIME_RANGE_MICROS: i64 = 10_000_000_000;
|
|
|
-
|
|
|
- fn new() -> Self {
|
|
|
- FixedLengthDeque {
|
|
|
- deque: VecDeque::new(),
|
|
|
- deque_t: VecDeque::new()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- pub fn push_back(&mut self, value: T) {
|
|
|
- let now = Utc::now().timestamp_micros();
|
|
|
-
|
|
|
- self.deque.push_back(value);
|
|
|
- self.deque_t.push_back(now);
|
|
|
-
|
|
|
- // =================== 检查长度,如果超过时间窗口,则移除前端元素 ==================
|
|
|
- while self.deque_t.len() > 0 && now - self.deque_t.get(0).unwrap() > Self::MAX_TIME_RANGE_MICROS {
|
|
|
- self.deque.pop_front();
|
|
|
- self.deque_t.pop_front();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- pub fn len(&self) -> usize {
|
|
|
- self.deque.len()
|
|
|
- }
|
|
|
-
|
|
|
- pub fn get(&self, index: usize) -> Option<&T> {
|
|
|
- self.deque.get(index)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
#[derive(Debug)]
|
|
|
pub struct AvellanedaStoikov {
|
|
|
- pub depth_vec: FixedLengthDeque<Depth>, // 深度队列
|
|
|
- pub trade_vec: FixedLengthDeque<Trade>, // 交易队列
|
|
|
-
|
|
|
- pub mid_price_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub ask_price_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub bid_price_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub spread_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub spread_max_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub spread_min_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub optimal_ask_price_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub optimal_bid_price_vec: FixedLengthDeque<Decimal>,
|
|
|
+ pub depth_vec: FixedTimeRangeDeque<Depth>, // 深度队列
|
|
|
+ pub trade_vec: FixedTimeRangeDeque<Trade>, // 交易队列
|
|
|
+ pub spread_vec: FixedTimeRangeDeque<Decimal>,
|
|
|
|
|
|
pub mid_price: Decimal, // 中间价
|
|
|
pub ask_price: Decimal, // 卖一价
|
|
|
@@ -64,15 +16,8 @@ pub struct AvellanedaStoikov {
|
|
|
pub spread: Decimal, // 市场冲击
|
|
|
pub spread_max: Decimal, // 最大市场冲击
|
|
|
pub spread_min: Decimal, // 最小市场冲击
|
|
|
- pub optimal_ask_price: Decimal, // 卖出价挂单
|
|
|
- pub optimal_bid_price: Decimal, // 买入价挂单
|
|
|
-
|
|
|
- pub inventory_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub sigma_square_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub gamma_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub kappa_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub delta_plus_vec: FixedLengthDeque<Decimal>,
|
|
|
- pub ref_price_vec: FixedLengthDeque<Decimal>,
|
|
|
+ pub optimal_ask_price: Decimal, // 卖出挂单价
|
|
|
+ pub optimal_bid_price: Decimal, // 买入挂单价
|
|
|
|
|
|
pub inventory: Decimal, // 库存,也就是q
|
|
|
pub sigma_square: Decimal, // σ^2,波动性的平方
|
|
|
@@ -82,26 +27,17 @@ pub struct AvellanedaStoikov {
|
|
|
pub ref_price: Decimal, // 预定价格
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
- 使用Builder设计模式创建价格预测器,可以有效提高代码整洁度
|
|
|
- 下面的单元测试有使用示例
|
|
|
-*/
|
|
|
impl AvellanedaStoikov {
|
|
|
+ // 时间窗口大小(微秒)
|
|
|
+ const MAX_TIME_RANGE_MICROS: i64 = 10_000_000_000;
|
|
|
+
|
|
|
pub fn new() -> Self {
|
|
|
let avellaneda_stoikov = Self {
|
|
|
// 分别给与的长度
|
|
|
- depth_vec: FixedLengthDeque::new(),
|
|
|
- trade_vec: FixedLengthDeque::new(),
|
|
|
-
|
|
|
- mid_price_vec: FixedLengthDeque::new(),
|
|
|
- ask_price_vec: FixedLengthDeque::new(),
|
|
|
- bid_price_vec: FixedLengthDeque::new(),
|
|
|
- spread_vec: FixedLengthDeque::new(),
|
|
|
- spread_max_vec: FixedLengthDeque::new(),
|
|
|
- spread_min_vec: FixedLengthDeque::new(),
|
|
|
- optimal_ask_price_vec: FixedLengthDeque::new(),
|
|
|
- optimal_bid_price_vec: FixedLengthDeque::new(),
|
|
|
-
|
|
|
+ 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),
|
|
|
+
|
|
|
mid_price: Default::default(),
|
|
|
ask_price: Default::default(),
|
|
|
bid_price: Default::default(),
|
|
|
@@ -111,13 +47,6 @@ impl AvellanedaStoikov {
|
|
|
optimal_ask_price: Default::default(),
|
|
|
optimal_bid_price: Default::default(),
|
|
|
|
|
|
- inventory_vec: FixedLengthDeque::new(),
|
|
|
- gamma_vec: FixedLengthDeque::new(),
|
|
|
- sigma_square_vec: FixedLengthDeque::new(),
|
|
|
- delta_plus_vec: FixedLengthDeque::new(),
|
|
|
- kappa_vec: FixedLengthDeque::new(),
|
|
|
- ref_price_vec: FixedLengthDeque::new(),
|
|
|
-
|
|
|
inventory: Default::default(),
|
|
|
gamma: Default::default(),
|
|
|
sigma_square: Default::default(),
|
|
|
@@ -136,7 +65,6 @@ impl AvellanedaStoikov {
|
|
|
} else {
|
|
|
Decimal::NEGATIVE_ONE
|
|
|
};
|
|
|
- self.spread_max_vec.push_back(self.spread_max);
|
|
|
}
|
|
|
|
|
|
// 更新最小市场冲击
|
|
|
@@ -146,7 +74,6 @@ impl AvellanedaStoikov {
|
|
|
} else {
|
|
|
Decimal::NEGATIVE_ONE
|
|
|
};
|
|
|
- self.spread_min_vec.push_back(self.spread_min);
|
|
|
}
|
|
|
|
|
|
pub fn update_spread(&mut self, mid_price_now: &Decimal) {
|
|
|
@@ -164,15 +91,12 @@ impl AvellanedaStoikov {
|
|
|
self.depth_vec.push_back(depth.clone());
|
|
|
|
|
|
self.ask_price = depth.asks[0].price;
|
|
|
- self.ask_price_vec.push_back(self.ask_price);
|
|
|
|
|
|
self.bid_price = depth.bids[0].price;
|
|
|
- self.bid_price_vec.push_back(self.bid_price);
|
|
|
|
|
|
let mid_price_now = (self.ask_price + self.bid_price) / Decimal::TWO;
|
|
|
self.update_spread(&mid_price_now);
|
|
|
self.mid_price = mid_price_now;
|
|
|
- self.mid_price_vec.push_back(self.mid_price);
|
|
|
|
|
|
self.processor();
|
|
|
}
|
|
|
@@ -184,7 +108,6 @@ impl AvellanedaStoikov {
|
|
|
|
|
|
pub fn update_inventory(&mut self, inventory: Decimal) {
|
|
|
self.inventory = inventory;
|
|
|
- self.inventory_vec.push_back(inventory);
|
|
|
|
|
|
self.processor();
|
|
|
}
|
|
|
@@ -216,7 +139,6 @@ impl AvellanedaStoikov {
|
|
|
};
|
|
|
|
|
|
self.sigma_square.rescale(10);
|
|
|
- self.sigma_square_vec.push_back(self.sigma_square);
|
|
|
}
|
|
|
|
|
|
pub fn update_gamma(&mut self) {
|
|
|
@@ -228,7 +150,6 @@ impl AvellanedaStoikov {
|
|
|
IRA * (self.spread_max - self.spread_min) / (Decimal::TWO * self.inventory.abs() * self.sigma_square)
|
|
|
};
|
|
|
self.gamma.rescale(8);
|
|
|
- self.gamma_vec.push_back(self.gamma);
|
|
|
}
|
|
|
|
|
|
pub fn update_kappa(&mut self) {
|
|
|
@@ -239,12 +160,10 @@ impl AvellanedaStoikov {
|
|
|
self.gamma / (temp.exp() - Decimal::ONE)
|
|
|
};
|
|
|
self.kappa.rescale(8);
|
|
|
- self.kappa_vec.push_back(self.kappa);
|
|
|
}
|
|
|
|
|
|
pub fn update_ref_price(&mut self) {
|
|
|
self.ref_price = self.mid_price - self.inventory * self.gamma * self.sigma_square;
|
|
|
- self.ref_price_vec.push_back(self.ref_price);
|
|
|
}
|
|
|
|
|
|
pub fn update_delta_plus(&mut self) {
|
|
|
@@ -256,7 +175,11 @@ impl AvellanedaStoikov {
|
|
|
left_value + right_value
|
|
|
};
|
|
|
self.delta_plus.rescale(8);
|
|
|
- self.delta_plus_vec.push_back(self.delta_plus);
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn update_optimal_ask_and_bid(&mut self) {
|
|
|
+ self.optimal_ask_price = self.ref_price + self.delta_plus / Decimal::TWO;
|
|
|
+ self.optimal_bid_price = self.ref_price - self.delta_plus / Decimal::TWO;
|
|
|
}
|
|
|
|
|
|
// #[instrument(skip(self), level="TRACE")]
|
|
|
@@ -271,6 +194,7 @@ impl AvellanedaStoikov {
|
|
|
info!(?self.ref_price);
|
|
|
self.update_delta_plus();
|
|
|
info!(?self.delta_plus);
|
|
|
+ self.update_optimal_ask_and_bid();
|
|
|
info!(?self.spread_max, ?self.mid_price, ?self.ref_price, ?self.inventory);
|
|
|
}
|
|
|
|