|
|
@@ -1,4 +1,4 @@
|
|
|
-use std::cmp::{min};
|
|
|
+use std::cmp::{max, min};
|
|
|
use std::collections::HashMap;
|
|
|
use std::ops::{Div, Mul};
|
|
|
use chrono::Utc;
|
|
|
@@ -11,6 +11,7 @@ use tracing::{info, error, warn};
|
|
|
use tokio::time::Instant;
|
|
|
use global::params::Params;
|
|
|
use standard::{OrderCommand};
|
|
|
+use crate::avellaneda_stoikov::AvellanedaStoikov;
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
pub struct Strategy {
|
|
|
@@ -90,7 +91,6 @@ pub struct Strategy {
|
|
|
pub max_short_value: Decimal, // 最大做空持仓
|
|
|
|
|
|
pub open_dist: Vec<Decimal>, // 开仓相关价格
|
|
|
- pub close_dist: Vec<Decimal>, // 平仓相关价格
|
|
|
|
|
|
pub trade_close_dist: Decimal, //
|
|
|
pub trade_open_dist: Decimal, //
|
|
|
@@ -101,10 +101,6 @@ pub struct Strategy {
|
|
|
pub post_side: i64, // 交易方向
|
|
|
pub trade_vol_24h_w: Decimal, // 24小时成交额(单位:万)
|
|
|
pub grid: Decimal, // 网格数量
|
|
|
-
|
|
|
- // 速度限制,至少0.5秒才取消订单
|
|
|
- pub prev_place_order_timestamp: i64, // 上次挂单的时间
|
|
|
- pub min_cancel_interval_mills: i64, // 至少要挂这么久才允许撤销
|
|
|
}
|
|
|
|
|
|
impl Strategy {
|
|
|
@@ -187,7 +183,6 @@ impl Strategy {
|
|
|
max_long_value: Default::default(),
|
|
|
max_short_value: Default::default(),
|
|
|
open_dist: vec![],
|
|
|
- close_dist: vec![],
|
|
|
trade_close_dist: params.close,
|
|
|
trade_open_dist: params.open,
|
|
|
ref_index: 0,
|
|
|
@@ -196,8 +191,6 @@ impl Strategy {
|
|
|
post_side: 0,
|
|
|
trade_vol_24h_w: Default::default(),
|
|
|
grid: Decimal::from(params.grid),
|
|
|
- prev_place_order_timestamp: 0,
|
|
|
- min_cancel_interval_mills: 500,
|
|
|
};
|
|
|
|
|
|
// 交易名字
|
|
|
@@ -239,110 +232,110 @@ impl Strategy {
|
|
|
// 更新当前strategy的各类信息
|
|
|
// #[instrument(skip(self, trader_msg), level="TRACE")]
|
|
|
pub fn _update_data(&mut self,
|
|
|
- _local_position: &LocalPosition,
|
|
|
- _agg_market: &Vec<Decimal>,
|
|
|
- _local_cash: &Decimal,
|
|
|
- _local_coin: &Decimal,
|
|
|
- _ref_price: &Vec<Vec<Decimal>>,
|
|
|
- _predict: &Decimal) -> bool {
|
|
|
- // // position信息更新
|
|
|
- // if self.pos.long_pos != local_position.long_pos {
|
|
|
- // self.pos.long_pos = local_position.long_pos;
|
|
|
- // self.pos.long_avg = local_position.long_avg;
|
|
|
- // }
|
|
|
- // if self.pos.short_pos != local_position.short_pos {
|
|
|
- // self.pos.short_pos = local_position.short_pos;
|
|
|
- // self.pos.short_avg = local_position.short_avg;
|
|
|
- // }
|
|
|
- // // debug!(?self.pos);
|
|
|
- //
|
|
|
- // // 价格值处理
|
|
|
- // self.bp = agg_market[global::public_params::BID_PRICE_INDEX];
|
|
|
- // self.ap = agg_market[global::public_params::ASK_PRICE_INDEX];
|
|
|
- // self.mp = (self.bp + self.ap) * dec!(0.5);
|
|
|
- // // 中间价的ema值处理
|
|
|
- // if self.mp_ema.eq(&Decimal::ZERO) {
|
|
|
- // self.mp_ema = self.mp;
|
|
|
- // } else {
|
|
|
- // self.mp_ema = self.mp_ema * dec!(0.999) + self.mp * dec!(0.001);
|
|
|
- // }
|
|
|
- // // debug!(?self.bp, ?self.ap, ?self.mp, ?self.mp_ema);
|
|
|
- //
|
|
|
- // // 动态杠杆调节
|
|
|
- // if self.mp > self.mp_ema {
|
|
|
- // self.adjust_lever_rate = Decimal::ONE;
|
|
|
- // } else {
|
|
|
- // self.adjust_lever_rate = dec!(0.8);
|
|
|
- // }
|
|
|
- // // debug!(?self.adjust_lever_rate);
|
|
|
- //
|
|
|
- // // 当前持仓价值处理
|
|
|
- // self.long_hold_value = self.pos.long_pos * self.mp;
|
|
|
- // self.short_hold_value = self.pos.short_pos * self.mp;
|
|
|
- // // debug!(?self.long_hold_value, ?self.short_hold_value);
|
|
|
- //
|
|
|
- // // 分现货或合约计算最大开仓价值
|
|
|
- // if self.exchange.contains("spot") {
|
|
|
- // self.max_long_value = *local_cash * self.lever_rate * self.adjust_lever_rate;
|
|
|
- // self.max_short_value = *local_coin * self.lever_rate * self.adjust_lever_rate * self.mp;
|
|
|
- // } else {
|
|
|
- // self.max_long_value = self.equity * self.lever_rate * self.adjust_lever_rate;
|
|
|
- // self.max_short_value = self.max_long_value;
|
|
|
- // }
|
|
|
- // // debug!(?self.max_long_value, ?self.max_short_value, ?self.equity, ?self.lever_rate, ?self.adjust_lever_rate);
|
|
|
- //
|
|
|
- // // 做市模式识别
|
|
|
- // if self.ref_name[self.ref_index].eq(&self.trade_name) {
|
|
|
- // self.maker_mode = "free".to_string();
|
|
|
- // } else {
|
|
|
- // self.maker_mode = "follow".to_string();
|
|
|
- // }
|
|
|
- // // debug!(?self.maker_mode);
|
|
|
- //
|
|
|
- // // 参考价格
|
|
|
- // if ref_price.len() == 0 {
|
|
|
- // self.ref_bp = self.bp;
|
|
|
- // self.ref_ap = self.ap;
|
|
|
- // self.ref_price = self.mp;
|
|
|
- // } else {
|
|
|
- // self.ref_bp = ref_price[self.ref_index][0];
|
|
|
- // self.ref_ap = ref_price[self.ref_index][1];
|
|
|
- // self.ref_price = (self.ref_bp + self.ref_ap) * dec!(0.5);
|
|
|
- // }
|
|
|
- // // debug!(?self.ref_bp, ?self.ref_ap, %self.ref_price);
|
|
|
- //
|
|
|
- // // spread
|
|
|
- // let temp_predict = predict * self.predict_alpha;
|
|
|
- // self.predict = utils::clip(temp_predict, -self.trade_open_dist, self.trade_open_dist);
|
|
|
- // // debug!(?self.predict);
|
|
|
- //
|
|
|
- // // 计算当前账户cash和coin
|
|
|
- // self.coin = local_coin.clone();
|
|
|
- // self.cash = local_cash.clone();
|
|
|
- // self.equity = local_cash + local_coin * self.mp;
|
|
|
- // if self.equity > self.max_equity {
|
|
|
- // self.max_equity = self.equity;
|
|
|
- // }
|
|
|
- // // debug!(?self.coin, ?self.cash, ?self.equity, ?self.max_equity);
|
|
|
- //
|
|
|
- // // 总可开数量
|
|
|
- // self.total_amount = self.equity * self.lever_rate * self.adjust_lever_rate / self.mp;
|
|
|
- // self.total_amount = utils::fix_amount(self.total_amount, self.step_size);
|
|
|
- // // debug!(?self.total_amount);
|
|
|
- // if self.total_amount.eq(&Decimal::ZERO) {
|
|
|
- // error!("总可开数量低于一张,请尝试加大杠杆倍数或资金!equity={}, lever_rate={}, adjust_lever_rate={}, mp={}, step_size={}",
|
|
|
- // self.equity, self.lever_rate, self.adjust_lever_rate, self.mp, self.step_size);
|
|
|
- // return false;
|
|
|
- // }
|
|
|
- //
|
|
|
- // // 求最大pos
|
|
|
- // if self.equity > Decimal::ZERO {
|
|
|
- // let max_pos_rate = max(self.pos.long_pos, self.pos.short_pos) * self.mp / self.equity;
|
|
|
- // if max_pos_rate > self.max_pos_rate {
|
|
|
- // self.max_pos_rate = max_pos_rate;
|
|
|
- // }
|
|
|
- // // debug!(?max_pos_rate, ?self.max_pos_rate);
|
|
|
- // }
|
|
|
+ local_position: &LocalPosition,
|
|
|
+ agg_market: &Vec<Decimal>,
|
|
|
+ local_cash: &Decimal,
|
|
|
+ local_coin: &Decimal,
|
|
|
+ ref_price: &Vec<Vec<Decimal>>,
|
|
|
+ predict: &Decimal) -> bool {
|
|
|
+ // position信息更新
|
|
|
+ if self.pos.long_pos != local_position.long_pos {
|
|
|
+ self.pos.long_pos = local_position.long_pos;
|
|
|
+ self.pos.long_avg = local_position.long_avg;
|
|
|
+ }
|
|
|
+ if self.pos.short_pos != local_position.short_pos {
|
|
|
+ self.pos.short_pos = local_position.short_pos;
|
|
|
+ self.pos.short_avg = local_position.short_avg;
|
|
|
+ }
|
|
|
+ // debug!(?self.pos);
|
|
|
+
|
|
|
+ // 价格值处理
|
|
|
+ self.bp = agg_market[global::public_params::BID_PRICE_INDEX];
|
|
|
+ self.ap = agg_market[global::public_params::ASK_PRICE_INDEX];
|
|
|
+ self.mp = (self.bp + self.ap) * dec!(0.5);
|
|
|
+ // 中间价的ema值处理
|
|
|
+ if self.mp_ema.eq(&Decimal::ZERO) {
|
|
|
+ self.mp_ema = self.mp;
|
|
|
+ } else {
|
|
|
+ self.mp_ema = self.mp_ema * dec!(0.999) + self.mp * dec!(0.001);
|
|
|
+ }
|
|
|
+ // debug!(?self.bp, ?self.ap, ?self.mp, ?self.mp_ema);
|
|
|
+
|
|
|
+ // 动态杠杆调节
|
|
|
+ if self.mp > self.mp_ema {
|
|
|
+ self.adjust_lever_rate = Decimal::ONE;
|
|
|
+ } else {
|
|
|
+ self.adjust_lever_rate = dec!(0.8);
|
|
|
+ }
|
|
|
+ // debug!(?self.adjust_lever_rate);
|
|
|
+
|
|
|
+ // 当前持仓价值处理
|
|
|
+ self.long_hold_value = self.pos.long_pos * self.mp;
|
|
|
+ self.short_hold_value = self.pos.short_pos * self.mp;
|
|
|
+ // debug!(?self.long_hold_value, ?self.short_hold_value);
|
|
|
+
|
|
|
+ // 分现货或合约计算最大开仓价值
|
|
|
+ if self.exchange.contains("spot") {
|
|
|
+ self.max_long_value = *local_cash * self.lever_rate * self.adjust_lever_rate;
|
|
|
+ self.max_short_value = *local_coin * self.lever_rate * self.adjust_lever_rate * self.mp;
|
|
|
+ } else {
|
|
|
+ self.max_long_value = self.equity * self.lever_rate * self.adjust_lever_rate;
|
|
|
+ self.max_short_value = self.max_long_value;
|
|
|
+ }
|
|
|
+ // debug!(?self.max_long_value, ?self.max_short_value, ?self.equity, ?self.lever_rate, ?self.adjust_lever_rate);
|
|
|
+
|
|
|
+ // 做市模式识别
|
|
|
+ if self.ref_name[self.ref_index].eq(&self.trade_name) {
|
|
|
+ self.maker_mode = "free".to_string();
|
|
|
+ } else {
|
|
|
+ self.maker_mode = "follow".to_string();
|
|
|
+ }
|
|
|
+ // debug!(?self.maker_mode);
|
|
|
+
|
|
|
+ // 参考价格
|
|
|
+ if ref_price.len() == 0 {
|
|
|
+ self.ref_bp = self.bp;
|
|
|
+ self.ref_ap = self.ap;
|
|
|
+ self.ref_price = self.mp;
|
|
|
+ } else {
|
|
|
+ self.ref_bp = ref_price[self.ref_index][0];
|
|
|
+ self.ref_ap = ref_price[self.ref_index][1];
|
|
|
+ self.ref_price = (self.ref_bp + self.ref_ap) * dec!(0.5);
|
|
|
+ }
|
|
|
+ // debug!(?self.ref_bp, ?self.ref_ap, %self.ref_price);
|
|
|
+
|
|
|
+ // spread
|
|
|
+ let temp_predict = predict * self.predict_alpha;
|
|
|
+ self.predict = utils::clip(temp_predict, -self.trade_open_dist, self.trade_open_dist);
|
|
|
+ // debug!(?self.predict);
|
|
|
+
|
|
|
+ // 计算当前账户cash和coin
|
|
|
+ self.coin = local_coin.clone();
|
|
|
+ self.cash = local_cash.clone();
|
|
|
+ self.equity = local_cash + local_coin * self.mp;
|
|
|
+ if self.equity > self.max_equity {
|
|
|
+ self.max_equity = self.equity;
|
|
|
+ }
|
|
|
+ // debug!(?self.coin, ?self.cash, ?self.equity, ?self.max_equity);
|
|
|
+
|
|
|
+ // 总可开数量
|
|
|
+ self.total_amount = self.equity * self.lever_rate * self.adjust_lever_rate / self.mp;
|
|
|
+ self.total_amount = utils::fix_amount(self.total_amount, self.step_size);
|
|
|
+ // debug!(?self.total_amount);
|
|
|
+ if self.total_amount.eq(&Decimal::ZERO) {
|
|
|
+ error!("总可开数量低于一张,请尝试加大杠杆倍数或资金!equity={}, lever_rate={}, adjust_lever_rate={}, mp={}, step_size={}",
|
|
|
+ self.equity, self.lever_rate, self.adjust_lever_rate, self.mp, self.step_size);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 求最大pos
|
|
|
+ if self.equity > Decimal::ZERO {
|
|
|
+ let max_pos_rate = max(self.pos.long_pos, self.pos.short_pos) * self.mp / self.equity;
|
|
|
+ if max_pos_rate > self.max_pos_rate {
|
|
|
+ self.max_pos_rate = max_pos_rate;
|
|
|
+ }
|
|
|
+ // debug!(?max_pos_rate, ?self.max_pos_rate);
|
|
|
+ }
|
|
|
|
|
|
return true;
|
|
|
}
|
|
|
@@ -468,68 +461,29 @@ impl Strategy {
|
|
|
// debug!(?command);
|
|
|
}
|
|
|
|
|
|
- // 生成各类挂单价格,原文是gen_dist
|
|
|
+ // 修复挂单价格,不然单子打不出去
|
|
|
// #[instrument(skip(self), level="TRACE")]
|
|
|
- pub fn generate_dist(&mut self) {
|
|
|
- let open = self.trade_open_dist;
|
|
|
- let close = self.trade_close_dist;
|
|
|
- let pos_rate = vec![self.long_hold_rate, self.short_hold_rate];
|
|
|
- let ref_bp = self.ref_bp;
|
|
|
- let ref_ap = self.ref_ap;
|
|
|
- let predict = self.predict;
|
|
|
- let grid = self.grid;
|
|
|
-
|
|
|
- // 平仓相关
|
|
|
- let mut mp = (ref_bp + ref_ap) * dec!(0.5);
|
|
|
- let mut buy_start = mp;
|
|
|
- let mut sell_start = mp;
|
|
|
- let mut avoid = min(dec!(0.0005), close * dec!(0.5));
|
|
|
- self.close_dist = vec![
|
|
|
- buy_start * (Decimal::ONE + predict - close + avoid), // buy upper
|
|
|
- buy_start * (Decimal::ONE + predict - close - avoid), // buy lower
|
|
|
- sell_start * (Decimal::ONE + predict + close - avoid), // sell lower
|
|
|
- sell_start * (Decimal::ONE + predict + close + avoid), // sell upper
|
|
|
- ];
|
|
|
- // debug!(?mp, ?buy_start, ?sell_start, ?avoid, ?close_dist);
|
|
|
+ pub fn fix_price(&mut self, predictor: &mut AvellanedaStoikov) {
|
|
|
+ predictor.optimal_ask_price = utils::fix_price(predictor.optimal_ask_price, self.tick_size);
|
|
|
+ predictor.optimal_bid_price = utils::fix_price(predictor.optimal_bid_price, self.tick_size);
|
|
|
|
|
|
- // 自由做市模式
|
|
|
- if self.maker_mode == "free".to_string() {
|
|
|
- buy_start = ref_bp;
|
|
|
- sell_start = ref_ap;
|
|
|
- }
|
|
|
- // 跟随做市模式
|
|
|
- else if self.maker_mode == "follow".to_string() {
|
|
|
- mp = (ref_bp + ref_ap) * dec!(0.5);
|
|
|
- buy_start = mp;
|
|
|
- sell_start = mp;
|
|
|
- } else {
|
|
|
- error!("未知做市类型:mode={}", self.maker_mode);
|
|
|
- panic!("未知做市类型:mode={}", self.maker_mode);
|
|
|
- }
|
|
|
- // debug!(?mode, ?buy_start, ?sell_start, ?mp);
|
|
|
+ let open = predictor.delta_plus.clone();
|
|
|
|
|
|
// 开仓相关
|
|
|
- avoid = min(dec!(0.002), open * dec!(0.1));
|
|
|
- // 持仓偏移
|
|
|
- let buy_shift = Decimal::ONE + pos_rate[0] * grid;
|
|
|
- let sell_shift = Decimal::ONE + pos_rate[1] * grid;
|
|
|
+ let avoid = min(dec!(0.002), open * dec!(0.1));
|
|
|
+
|
|
|
+ // 用于判断价格是否出界
|
|
|
self.open_dist = vec![
|
|
|
- buy_start * (Decimal::ONE + predict - open * buy_shift + avoid), // buy upper
|
|
|
- buy_start * (Decimal::ONE + predict - open * buy_shift - avoid), // buy lower
|
|
|
- sell_start * (Decimal::ONE + predict + open * sell_shift - avoid), // sell lower
|
|
|
- sell_start * (Decimal::ONE + predict + open * sell_shift + avoid), // sell upper
|
|
|
+ predictor.optimal_bid_price * (Decimal::ONE + avoid), // buy upper
|
|
|
+ predictor.optimal_bid_price * (Decimal::ONE - avoid), // buy lower
|
|
|
+ predictor.optimal_ask_price * (Decimal::ONE - avoid), // sell lower
|
|
|
+ predictor.optimal_ask_price * (Decimal::ONE + avoid), // sell upper
|
|
|
];
|
|
|
- // debug!(?avoid, ?buy_shift, ?sell_shift, ?avoid, ?open_dist);
|
|
|
|
|
|
- // 修复价格
|
|
|
+ // 修复价格范围
|
|
|
for open_price in &mut self.open_dist {
|
|
|
*open_price = utils::fix_price(*open_price, self.tick_size);
|
|
|
}
|
|
|
- for close_price in &mut self.close_dist {
|
|
|
- *close_price = utils::fix_price(*close_price, self.tick_size);
|
|
|
- }
|
|
|
- // debug!(?open_dist);
|
|
|
- // debug!(?close_dist);
|
|
|
}
|
|
|
|
|
|
// 统计请求次数
|
|
|
@@ -865,167 +819,9 @@ impl Strategy {
|
|
|
return diff_time > 30 && diff_time < 3570;
|
|
|
}
|
|
|
|
|
|
- // 平仓订单处理命令
|
|
|
- // #[instrument(skip(self, command), level="TRACE")]
|
|
|
- pub fn _post_close(&self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>) {
|
|
|
- // debug!(?command);
|
|
|
-
|
|
|
- let mut pd_amount = Decimal::ZERO;
|
|
|
- let mut pd_order_num = 0;
|
|
|
- let mut pk_amount = Decimal::ZERO;
|
|
|
- let mut pk_order_num = 0;
|
|
|
- // 各类价格
|
|
|
- let long_upper = self.close_dist[0];
|
|
|
- let long_lower = self.close_dist[1];
|
|
|
- let short_lower = self.close_dist[2];
|
|
|
- let short_upper = self.close_dist[3];
|
|
|
-
|
|
|
- // 获取当前挂单,如果超过挂单范围则取消当前平仓单
|
|
|
- for order_client_id in local_orders.keys() {
|
|
|
- let order = local_orders.get(order_client_id).unwrap();
|
|
|
- let key = format!("Cancel{}", *order_client_id);
|
|
|
- let value = vec![order.client_id.clone(), order.order_id.clone()];
|
|
|
-
|
|
|
- if order.side == "pk".to_string() {
|
|
|
- if order.price > long_upper || order.price < long_lower {
|
|
|
- command.cancel.insert(key.clone(), value.clone());
|
|
|
- }
|
|
|
-
|
|
|
- pk_amount += order.amount;
|
|
|
- pk_order_num += 1;
|
|
|
- }
|
|
|
-
|
|
|
- if order.side == "pd".to_string() {
|
|
|
- if order.price < short_lower || order.price > short_upper {
|
|
|
- command.cancel.insert(key.clone(), value.clone());
|
|
|
- }
|
|
|
-
|
|
|
- pd_amount += order.amount;
|
|
|
- pd_order_num += 1;
|
|
|
- }
|
|
|
- }
|
|
|
- // debug!(?command);
|
|
|
-
|
|
|
- // 判断是否需要全平
|
|
|
- let is_need_cancel_all_close =
|
|
|
- (pd_amount - self.pos.long_pos).abs() * self.mp > self._min_amount_value
|
|
|
- || (pk_amount - self.pos.short_pos).abs() * self.mp > self._min_amount_value;
|
|
|
- if is_need_cancel_all_close {
|
|
|
- for order_client_id in local_orders.keys() {
|
|
|
- let order = local_orders.get(order_client_id).unwrap();
|
|
|
-
|
|
|
- if order.side == "pk".to_string() || order.side == "pd".to_string() {
|
|
|
- let key = format!("Cancel{}", *order_client_id);
|
|
|
- let value = vec![order.client_id.clone(), order.order_id.clone()];
|
|
|
-
|
|
|
- command.cancel.insert(key, value);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- // debug!(?command);
|
|
|
-
|
|
|
- // 区分现货和期货
|
|
|
- if self.exchange.contains("spot") {
|
|
|
- // 平多价值大于最小交易价值,执行平多逻辑
|
|
|
- if self.pos.long_pos * self.mp > self._min_amount_value {
|
|
|
- if pd_order_num == 0 {
|
|
|
- let mut price = (short_lower + short_upper) * dec!(0.5);
|
|
|
- price = utils::clip(price, self.bp * dec!(0.9995), self.ap * dec!(1.03));
|
|
|
- price = utils::fix_price(price, self.tick_size);
|
|
|
- let mut amount = self.pos.long_pos;
|
|
|
- amount = utils::fix_amount(amount, self.step_size);
|
|
|
-
|
|
|
- if amount * price > self._min_amount_value {
|
|
|
- let order_client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
- let order = vec![
|
|
|
- amount.to_string(),
|
|
|
- "pd".to_string(),
|
|
|
- price.to_string(),
|
|
|
- order_client_id.clone()
|
|
|
- ];
|
|
|
- command.limits_close.insert(order_client_id, order);
|
|
|
-
|
|
|
- // debug!(?command);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 平空价值大于最小交易价值,执行平空逻辑
|
|
|
- if self.pos.short_pos > self._min_amount_value {
|
|
|
- if pk_order_num == 0 {
|
|
|
- let mut price = (long_upper + long_lower) * dec!(0.5);
|
|
|
- price = utils::clip(price, self.bp * dec!(0.97), self.ap * dec!(1.0005));
|
|
|
- price = utils::fix_price(price, self.tick_size);
|
|
|
- let mut amount = self.pos.short_pos;
|
|
|
- amount = utils::fix_amount(amount, self.step_size);
|
|
|
-
|
|
|
- if amount * price > self._min_amount_value {
|
|
|
- let order_client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
- let order = vec![
|
|
|
- amount.to_string(),
|
|
|
- "pk".to_string(),
|
|
|
- price.to_string(),
|
|
|
- order_client_id.clone()
|
|
|
- ];
|
|
|
- command.limits_close.insert(order_client_id, order);
|
|
|
-
|
|
|
- // debug!(?command);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- if self.pos.long_pos > Decimal::ZERO {
|
|
|
- if pd_order_num == 0 {
|
|
|
- let mut price = (short_lower + short_upper) * dec!(0.5);
|
|
|
- // 不限制大小
|
|
|
- // price = utils::clip(price, self.bp * dec!(0.9995), self.ap * dec!(1.03));
|
|
|
- price = utils::fix_price(price, self.tick_size);
|
|
|
-
|
|
|
- let order_client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
- let order = vec![
|
|
|
- self.pos.long_pos.to_string(),
|
|
|
- "pd".to_string(),
|
|
|
- price.to_string(),
|
|
|
- order_client_id.clone()
|
|
|
- ];
|
|
|
-
|
|
|
- command.limits_close.insert(order_client_id, order);
|
|
|
-
|
|
|
- // debug!(?command);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if self.pos.short_pos > Decimal::ZERO {
|
|
|
- if pk_order_num == 0 {
|
|
|
- let mut price = (long_upper + long_lower) * dec!(0.5);
|
|
|
- // 不限制大小
|
|
|
- // price = utils::clip(price, self.bp * dec!(0.97), self.ap * dec!(1.0005));
|
|
|
- price = utils::fix_price(price, self.tick_size);
|
|
|
-
|
|
|
- let order_client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
- let order = vec![
|
|
|
- self.pos.short_pos.to_string(),
|
|
|
- "pk".to_string(),
|
|
|
- price.to_string(),
|
|
|
- order_client_id.clone()
|
|
|
- ];
|
|
|
-
|
|
|
- command.limits_close.insert(order_client_id, order);
|
|
|
-
|
|
|
- // debug!(?command);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
// 生成取消订单的指令
|
|
|
// #[instrument(skip(self, command), level="TRACE")]
|
|
|
pub fn _cancel_open(&self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>) {
|
|
|
- // 强制性时间间隔
|
|
|
- if self.prev_place_order_timestamp + self.min_cancel_interval_mills > Utc::now().timestamp_millis() {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
// debug!(?command);
|
|
|
// 挂单范围
|
|
|
let long_upper = self.open_dist[0];
|
|
|
@@ -1103,8 +899,7 @@ impl Strategy {
|
|
|
|
|
|
// 开单指令生成逻辑
|
|
|
// #[instrument(skip(self, command), level="TRACE")]
|
|
|
- pub fn _post_open(&mut self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>) {
|
|
|
- // debug!(?command);
|
|
|
+ pub fn _post_open(&mut self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>, predictor: &mut AvellanedaStoikov) {
|
|
|
// 开仓逻辑检测,主要是检测整点开仓逻辑
|
|
|
if !self.check_allow_post_open() {
|
|
|
return;
|
|
|
@@ -1118,12 +913,6 @@ impl Strategy {
|
|
|
// 报单时间更新
|
|
|
self.post_open_time = self.local_time;
|
|
|
|
|
|
- // 挂单范围获取
|
|
|
- let long_upper = self.open_dist[0];
|
|
|
- let long_lower = self.open_dist[1];
|
|
|
- let short_lower = self.open_dist[2];
|
|
|
- let short_upper = self.open_dist[3];
|
|
|
-
|
|
|
// 获取当前挂单价值
|
|
|
let mut buy_price_list: Vec<Decimal> = vec![];
|
|
|
let mut sell_price_list: Vec<Decimal> = vec![];
|
|
|
@@ -1142,78 +931,64 @@ impl Strategy {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 计算可开价值
|
|
|
- let mut long_free_value = self.max_long_value - self.long_hold_value - buy_value;
|
|
|
- let mut short_free_value = self.max_short_value - self.short_hold_value - sell_value;
|
|
|
- // debug!(?long_free_value, ?short_free_value);
|
|
|
- // 现货要特殊处理
|
|
|
- if self.exchange.contains("spot") {
|
|
|
- let coin_value = self.coin * self.mp * self.lever_rate * self.adjust_lever_rate;
|
|
|
- let cash_value = self.cash * self.lever_rate * self.adjust_lever_rate;
|
|
|
-
|
|
|
- long_free_value = min(cash_value, self.max_long_value) - buy_value;
|
|
|
- short_free_value = min(coin_value, self.max_short_value) - sell_value;
|
|
|
- }
|
|
|
- // 一手开单价值计算
|
|
|
- let one_hand_long_value = dec!(0.99) * (self.max_long_value / self.grid);
|
|
|
- let one_hand_short_value = dec!(0.99) * (self.max_short_value / self.grid);
|
|
|
-
|
|
|
- // debug!(?self.post_side);
|
|
|
+ // // 计算可开价值
|
|
|
+ // let mut long_free_value = self.max_long_value - self.long_hold_value - buy_value;
|
|
|
+ // let mut short_free_value = self.max_short_value - self.short_hold_value - sell_value;
|
|
|
+ // // debug!(?long_free_value, ?short_free_value);
|
|
|
+ // // 现货要特殊处理
|
|
|
+ // if self.exchange.contains("spot") {
|
|
|
+ // let coin_value = self.coin * self.mp * self.lever_rate * self.adjust_lever_rate;
|
|
|
+ // let cash_value = self.cash * self.lever_rate * self.adjust_lever_rate;
|
|
|
+ //
|
|
|
+ // long_free_value = min(cash_value, self.max_long_value) - buy_value;
|
|
|
+ // short_free_value = min(coin_value, self.max_short_value) - sell_value;
|
|
|
+ // }
|
|
|
+ // // 一手开单价值计算
|
|
|
+ // let one_hand_long_value = dec!(0.99) * (self.max_long_value / self.grid);
|
|
|
+ // let one_hand_short_value = dec!(0.99) * (self.max_short_value / self.grid);
|
|
|
// 挂多单
|
|
|
- if self.post_side >= 0 {
|
|
|
- // debug!(?buy_price_list);
|
|
|
- if buy_price_list.len() == 0 {
|
|
|
- let mut target_buy_price = (long_upper + long_lower) * dec!(0.5);
|
|
|
- // 取消大小限制
|
|
|
- target_buy_price = target_buy_price;
|
|
|
- // target_buy_price = utils::clip(target_buy_price, self.bp * dec!(0.97), self.ap * dec!(1.0005));
|
|
|
- target_buy_price = utils::fix_price(target_buy_price, self.tick_size);
|
|
|
- let value = min(one_hand_long_value, long_free_value);
|
|
|
- let amount = utils::fix_amount(value / self.mp, self.step_size);
|
|
|
- let amount_value = amount * self.mp;
|
|
|
- // debug!(?one_hand_long_value, ?long_free_value, ?amount);
|
|
|
-
|
|
|
- // 下单价值不能太大,也不能太小
|
|
|
- if amount_value >= self._min_amount_value && amount_value <= long_free_value {
|
|
|
- let client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
- let order = vec![
|
|
|
- amount.to_string(),
|
|
|
- "kd".to_string(),
|
|
|
- target_buy_price.to_string(),
|
|
|
- client_id.clone(),
|
|
|
- ];
|
|
|
-
|
|
|
- // debug!(?order);
|
|
|
- command.limits_open.insert(client_id, order);
|
|
|
- }
|
|
|
+ if self.post_side >= 0 && buy_value == Decimal::ZERO {
|
|
|
+ let mut target_buy_price = predictor.optimal_bid_price;
|
|
|
+ // target_buy_price = utils::clip(target_buy_price, self.bp * dec!(0.97), self.ap * dec!(1.0005));
|
|
|
+ target_buy_price = utils::fix_price(target_buy_price, self.tick_size);
|
|
|
+ let amount = utils::fix_amount(dec!(0.01), self.step_size);
|
|
|
+ let amount_value = amount * predictor.mid_price;
|
|
|
+
|
|
|
+ // 下单价值不能太大,也不能太小
|
|
|
+ if amount_value >= self._min_amount_value {
|
|
|
+ let client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
+ let order = vec![
|
|
|
+ amount.to_string(),
|
|
|
+ "kd".to_string(),
|
|
|
+ target_buy_price.to_string(),
|
|
|
+ client_id.clone(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ // debug!(?order);
|
|
|
+ command.limits_open.insert(client_id, order);
|
|
|
}
|
|
|
}
|
|
|
// 挂空单
|
|
|
- if self.post_side <= 0 {
|
|
|
- // debug!(?sell_price_list);
|
|
|
- if sell_price_list.len() == 0 {
|
|
|
- let mut target_sell_price = (short_lower + short_upper) * dec!(0.5);
|
|
|
- // target_sell_price = utils::clip(target_sell_price, self.bp * dec!(0.9995), self.ap * dec!(1.03));
|
|
|
- // 取消大小限制
|
|
|
- target_sell_price = target_sell_price;
|
|
|
- target_sell_price = utils::fix_price(target_sell_price, self.tick_size);
|
|
|
- let value = min(one_hand_short_value, short_free_value);
|
|
|
- let amount = utils::fix_amount(value / self.mp, self.step_size);
|
|
|
- let amount_value = amount * self.mp;
|
|
|
-
|
|
|
- // 下单价值不能太大,也不能太小
|
|
|
- if amount_value >= self._min_amount_value && amount_value <= short_free_value {
|
|
|
- let client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
- let order = vec![
|
|
|
- amount.to_string(),
|
|
|
- "kk".to_string(),
|
|
|
- target_sell_price.to_string(),
|
|
|
- client_id.clone(),
|
|
|
- ];
|
|
|
-
|
|
|
- // debug!(?order);
|
|
|
- command.limits_open.insert(client_id, order);
|
|
|
- }
|
|
|
+ if self.post_side <= 0 && sell_value == Decimal::ZERO {
|
|
|
+ let mut target_sell_price = predictor.optimal_ask_price;
|
|
|
+ // target_sell_price = utils::clip(target_sell_price, self.bp * dec!(0.9995), self.ap * dec!(1.03));
|
|
|
+ // 取消大小限制
|
|
|
+ target_sell_price = utils::fix_price(target_sell_price, self.tick_size);
|
|
|
+ let amount = utils::fix_amount(dec!(0.01), self.step_size);
|
|
|
+ let amount_value = amount * self.mp;
|
|
|
+
|
|
|
+ // 下单价值不能太大,也不能太小
|
|
|
+ if amount_value >= self._min_amount_value {
|
|
|
+ let client_id = utils::generate_client_id(Some(self.broker_id.clone()));
|
|
|
+ let order = vec![
|
|
|
+ amount.to_string(),
|
|
|
+ "kk".to_string(),
|
|
|
+ target_sell_price.to_string(),
|
|
|
+ client_id.clone(),
|
|
|
+ ];
|
|
|
+
|
|
|
+ // debug!(?order);
|
|
|
+ command.limits_open.insert(client_id, order);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1239,9 +1014,9 @@ impl Strategy {
|
|
|
info!("预热中");
|
|
|
}
|
|
|
|
|
|
- // 在满足条件后,返回非空command,否则返回一个空的command。原文的onTime。
|
|
|
+ // 在满足条件后,返回非空command,否则返回一个空的command。
|
|
|
pub fn on_tick(&mut self,
|
|
|
- local_orders: &HashMap<String, OrderInfo>,
|
|
|
+ _local_orders: &HashMap<String, OrderInfo>,
|
|
|
local_position: &LocalPosition,
|
|
|
agg_market: &Vec<Decimal>,
|
|
|
local_cash: &Decimal,
|
|
|
@@ -1251,7 +1026,7 @@ impl Strategy {
|
|
|
_ins: &Instant) -> OrderCommand {
|
|
|
self.on_time_print();
|
|
|
|
|
|
- let mut command = OrderCommand::new();
|
|
|
+ let command = OrderCommand::new();
|
|
|
|
|
|
// 更新逻辑数据出错时,不进行后面的逻辑处理
|
|
|
if !self._update_data(local_position,
|
|
|
@@ -1268,110 +1043,32 @@ impl Strategy {
|
|
|
return command;
|
|
|
}
|
|
|
|
|
|
- // 刷新多空双方持仓比例
|
|
|
- self._pos_rate();
|
|
|
- // 生成开仓、平仓等相关价格
|
|
|
- self.generate_dist();
|
|
|
+ return command;
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn do_strategy(&mut self, predictor: &mut AvellanedaStoikov, local_orders: &HashMap<String, OrderInfo>) -> OrderCommand {
|
|
|
+ // 修复相关价格
|
|
|
+ self.fix_price(predictor);
|
|
|
+ self.ref_ap = predictor.optimal_ask_price;
|
|
|
+ self.ref_bp = predictor.optimal_bid_price;
|
|
|
+ self.ref_price = predictor.ref_price;
|
|
|
+ self.mp = predictor.mid_price;
|
|
|
+
|
|
|
+ let mut command = OrderCommand::new();
|
|
|
+
|
|
|
+ // 检查是否准备充分
|
|
|
+ if !predictor.is_ready {
|
|
|
+ return command;
|
|
|
+ }
|
|
|
|
|
|
- // 下单指令处理逻辑
|
|
|
self._cancel_open(&mut command, local_orders); // 撤单命令处理
|
|
|
- self._post_close(&mut command, local_orders); // 平仓单命令处理
|
|
|
- self._post_open(&mut command, local_orders); // 限价单命令处理
|
|
|
+ self._post_open(&mut command, local_orders, predictor); // 限价单命令处理
|
|
|
self._check_local_orders(&mut command, local_orders); // 固定时间检查超时订单
|
|
|
self._update_in_cancel(&mut command, local_orders); // 更新撤单队列,是一个filter
|
|
|
self._check_request_limit(&mut command); // 限制频率,移除不合规则之订单,是一个filter
|
|
|
self._refresh_request_limit(); // 刷新频率限制
|
|
|
self._update_request_num(&mut command); // 统计刷新频率
|
|
|
|
|
|
- // 如果提交了订单,则更新最后提交时间
|
|
|
- if command.limits_open.len() != 0 {
|
|
|
- self.prev_place_order_timestamp = Utc::now().timestamp_millis();
|
|
|
- }
|
|
|
-
|
|
|
- // if command.limits_open.len() != 0 || command.limits_close.len() != 0 {
|
|
|
- // let name = self.params.account_name.clone();
|
|
|
- // // 参考卖价
|
|
|
- // let ref_ap = self.ref_ap;
|
|
|
- // // 参考买价
|
|
|
- // let ref_bp = self.ref_bp;
|
|
|
- // let limits_open = command.limits_open.clone();
|
|
|
- // let limits_close = command.limits_close.clone();
|
|
|
- // spawn(async move {
|
|
|
- // let time = chrono::Utc::now().timestamp_millis();
|
|
|
- // let param_list = paras_limit_command(name.clone(), time.clone(), ref_ap.clone(), ref_bp.clone(), limits_open, limits_close);
|
|
|
- // let param_json_obj = serde_json::to_string(¶m_list).unwrap();
|
|
|
- // market_warehouse_request(param_json_obj).await;
|
|
|
- // });
|
|
|
- // }
|
|
|
-
|
|
|
return command;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-// async fn market_warehouse_request(body_params: String) {
|
|
|
-// /****请求接口与 地址*/
|
|
|
-// let url = "http://as.skyfffire.com:8848/basic/saveDealRecords";
|
|
|
-//
|
|
|
-// let client = Client::new();
|
|
|
-// let req = client.post(url).header("auth", "43626546liangjiang")
|
|
|
-// .header("Content-Type", "application/json").body(body_params.clone());
|
|
|
-//
|
|
|
-// match req.send().await {
|
|
|
-// Ok(_) => {}
|
|
|
-// Err(_) => {}
|
|
|
-// };
|
|
|
-// // if !response.status().is_success() {
|
|
|
-// // error!("行情数据------仓库挂单数据存储失败--------!{}", response.status());
|
|
|
-// // error!(body_params);
|
|
|
-// // }
|
|
|
-// }
|
|
|
-//
|
|
|
-// fn paras_limit_command (robot_name: String, time: i64, ref_ap: Decimal, ref_bp: Decimal, limits_open: HashMap<String, Vec<String>>, limits_close: HashMap<String, Vec<String>>) -> Vec<DealRecord>{
|
|
|
-// let mut limits = HashMap::new();
|
|
|
-// limits.extend(limits_open);
|
|
|
-// limits.extend(limits_close);
|
|
|
-// let mut list: Vec<DealRecord> = Vec::with_capacity(limits.len());
|
|
|
-// for item in limits.keys() {
|
|
|
-// let item_clone = item.clone();
|
|
|
-// let value = limits[&item_clone].clone();
|
|
|
-// let amount = Decimal::from_str(value.get(0).unwrap_or(&"0".to_string())).unwrap();
|
|
|
-// let side = value.get(1).unwrap();
|
|
|
-// let price = Decimal::from_str(value.get(2).unwrap_or(&"0".to_string())).unwrap();
|
|
|
-// let mut ref_price = ref_ap;
|
|
|
-// if "kd" == side {
|
|
|
-// ref_price = ref_bp;
|
|
|
-// }
|
|
|
-// let deal_recode = DealRecord {
|
|
|
-// refPrice: ref_price.to_string(),
|
|
|
-// regPrice: price.to_string(),
|
|
|
-// num: amount.to_string(),
|
|
|
-// triggerTime: time,
|
|
|
-// robotName: robot_name.clone(),
|
|
|
-// side: side.to_string(),
|
|
|
-// };
|
|
|
-// list.push(deal_recode);
|
|
|
-// }
|
|
|
-// return list;
|
|
|
-// }
|
|
|
-
|
|
|
-#[cfg(test)]
|
|
|
-mod tests {
|
|
|
- use rust_decimal::Decimal;
|
|
|
- use rust_decimal_macros::dec;
|
|
|
- use global::params::Params;
|
|
|
- use crate::strategy::Strategy;
|
|
|
-
|
|
|
- #[test]
|
|
|
- fn on_time_test() {
|
|
|
- global::log_utils::init_log_with_debug();
|
|
|
-
|
|
|
- let params = Params::new("config.toml.gate").unwrap();
|
|
|
- let mut strategy = Strategy::new(¶ms, true);
|
|
|
-
|
|
|
- strategy.is_ready = true;
|
|
|
- strategy.equity = dec!(1000);
|
|
|
- strategy.lever_rate = Decimal::ONE;
|
|
|
-
|
|
|
- // debug!("{:?}", strategy.on_time(&trader_msg));
|
|
|
- }
|
|
|
-}
|