|
|
@@ -6,7 +6,7 @@ use chrono::Utc;
|
|
|
use rust_decimal::Decimal;
|
|
|
use rust_decimal::prelude::{FromPrimitive, ToPrimitive};
|
|
|
use rust_decimal_macros::dec;
|
|
|
-use crate::model::{DealRecord, LocalPosition, OrderInfo, TraderMsg};
|
|
|
+use crate::model::{DealRecord, LocalPosition, OrderInfo};
|
|
|
use crate::utils;
|
|
|
use tracing::{info, error, warn};
|
|
|
use reqwest::{Client};
|
|
|
@@ -239,24 +239,27 @@ impl Strategy {
|
|
|
|
|
|
// 更新当前strategy的各类信息
|
|
|
// #[instrument(skip(self, trader_msg), level="TRACE")]
|
|
|
- pub fn _update_data(&mut self, trader_msg: &TraderMsg) -> bool {
|
|
|
- // debug!(?self);
|
|
|
- // debug!(?trader_msg);
|
|
|
-
|
|
|
+ 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 != trader_msg.position.long_pos {
|
|
|
- self.pos.long_pos = trader_msg.position.long_pos;
|
|
|
- self.pos.long_avg = trader_msg.position.long_avg;
|
|
|
+ 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 != trader_msg.position.short_pos {
|
|
|
- self.pos.short_pos = trader_msg.position.short_pos;
|
|
|
- self.pos.short_avg = trader_msg.position.short_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 = trader_msg.market[global::public_params::BID_PRICE_INDEX];
|
|
|
- self.ap = trader_msg.market[global::public_params::ASK_PRICE_INDEX];
|
|
|
+ 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) {
|
|
|
@@ -281,8 +284,8 @@ impl Strategy {
|
|
|
|
|
|
// 分现货或合约计算最大开仓价值
|
|
|
if self.exchange.contains("spot") {
|
|
|
- self.max_long_value = trader_msg.cash * self.lever_rate * self.adjust_lever_rate;
|
|
|
- self.max_short_value = trader_msg.coin * self.lever_rate * self.adjust_lever_rate * self.mp;
|
|
|
+ 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;
|
|
|
@@ -298,26 +301,26 @@ impl Strategy {
|
|
|
// debug!(?self.maker_mode);
|
|
|
|
|
|
// 参考价格
|
|
|
- if trader_msg.ref_price.len() == 0 {
|
|
|
+ if ref_price.len() == 0 {
|
|
|
self.ref_bp = self.bp;
|
|
|
self.ref_ap = self.ap;
|
|
|
self.ref_price = self.mp;
|
|
|
} else {
|
|
|
- self.ref_bp = trader_msg.ref_price[self.ref_index][0];
|
|
|
- self.ref_ap = trader_msg.ref_price[self.ref_index][1];
|
|
|
+ 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 = trader_msg.predict * self.predict_alpha;
|
|
|
+ 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 = trader_msg.coin;
|
|
|
- self.cash = trader_msg.cash;
|
|
|
- self.equity = trader_msg.cash + trader_msg.coin * self.mp;
|
|
|
+ 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;
|
|
|
}
|
|
|
@@ -342,8 +345,6 @@ impl Strategy {
|
|
|
// debug!(?max_pos_rate, ?self.max_pos_rate);
|
|
|
}
|
|
|
|
|
|
- self.side = trader_msg.side.clone();
|
|
|
-
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -577,18 +578,20 @@ impl Strategy {
|
|
|
|
|
|
// 新增正在撤单、检查撤单队列,释放过时限制
|
|
|
// #[instrument(skip(self), level="TRACE")]
|
|
|
- pub fn _update_in_cancel(&mut self, command: &mut OrderCommand, trader_msg: &TraderMsg) {
|
|
|
+ pub fn _update_in_cancel(&mut self,
|
|
|
+ command: &mut OrderCommand,
|
|
|
+ local_orders: &HashMap<String, OrderInfo>) {
|
|
|
let mut new_cancel: HashMap<String, Vec<String>> = HashMap::new();
|
|
|
|
|
|
for cancel_name in command.cancel.keys() {
|
|
|
let cancel = command.cancel.get(cancel_name).unwrap();
|
|
|
let client_id = cancel[0].clone();
|
|
|
let mut need_limit_cancel = true;
|
|
|
- let order_some = trader_msg.orders.get(&client_id);
|
|
|
+ let order_some = local_orders.get(&client_id);
|
|
|
|
|
|
// 判断是否在本地挂单表中
|
|
|
if let Some(order) = order_some {
|
|
|
- let is_side_error = (order.side == "kk" && self.side == "long") || (order.side == "kd" && self.side == "short");
|
|
|
+ let is_side_error = (order.side == "kk") || (order.side == "kd");
|
|
|
|
|
|
// 如果订单创建时间大于100ms,才能有撤单操作
|
|
|
if self.local_time - order.create_time < 100 {
|
|
|
@@ -699,13 +702,22 @@ impl Strategy {
|
|
|
}
|
|
|
|
|
|
// 当退出时调用,全撤全平 准备退出
|
|
|
- pub fn on_exit(&mut self, trader_msg: &TraderMsg) -> OrderCommand {
|
|
|
+ pub fn on_exit(&mut self,
|
|
|
+ local_orders: &HashMap<String, OrderInfo>,
|
|
|
+ local_position: &LocalPosition,
|
|
|
+ agg_market: &Vec<Decimal>,
|
|
|
+ local_cash: &Decimal,
|
|
|
+ local_coin: &Decimal,
|
|
|
+ ref_price: &Vec<Vec<Decimal>>,
|
|
|
+ predict: &Decimal) -> OrderCommand {
|
|
|
let mut command = OrderCommand::new();
|
|
|
|
|
|
- self.local_orders.clear();
|
|
|
- self.local_orders = trader_msg.orders.clone();
|
|
|
-
|
|
|
- if self._update_data(trader_msg) {
|
|
|
+ if self._update_data(local_position,
|
|
|
+ agg_market,
|
|
|
+ local_cash,
|
|
|
+ local_coin,
|
|
|
+ ref_price,
|
|
|
+ predict) {
|
|
|
if !self.check_ready() {
|
|
|
return command;
|
|
|
}
|
|
|
@@ -713,7 +725,7 @@ impl Strategy {
|
|
|
// 取消、平掉所有
|
|
|
self._close_all(&mut command);
|
|
|
// 更新撤单队列
|
|
|
- self._update_in_cancel(&mut command, trader_msg);
|
|
|
+ self._update_in_cancel(&mut command, local_orders);
|
|
|
// 检查限频
|
|
|
self._check_request_limit(&mut command);
|
|
|
// 统计请求频率
|
|
|
@@ -725,13 +737,22 @@ impl Strategy {
|
|
|
}
|
|
|
|
|
|
// 休眠时调用,全撤 不再下新订单了 防止影响check_position执行
|
|
|
- pub fn on_sleep(&mut self, trader_msg: &TraderMsg) -> OrderCommand {
|
|
|
+ pub fn on_sleep(&mut self,
|
|
|
+ local_orders: &HashMap<String, OrderInfo>,
|
|
|
+ local_position: &LocalPosition,
|
|
|
+ agg_market: &Vec<Decimal>,
|
|
|
+ local_cash: &Decimal,
|
|
|
+ local_coin: &Decimal,
|
|
|
+ ref_price: &Vec<Vec<Decimal>>,
|
|
|
+ predict: &Decimal) -> OrderCommand {
|
|
|
let mut command = OrderCommand::new();
|
|
|
|
|
|
- self.local_orders.clear();
|
|
|
- self.local_orders = trader_msg.orders.clone();
|
|
|
-
|
|
|
- if self._update_data(trader_msg) {
|
|
|
+ if self._update_data(local_position,
|
|
|
+ agg_market,
|
|
|
+ local_cash,
|
|
|
+ local_coin,
|
|
|
+ ref_price,
|
|
|
+ predict) {
|
|
|
if !self.check_ready() {
|
|
|
return command;
|
|
|
}
|
|
|
@@ -739,7 +760,7 @@ impl Strategy {
|
|
|
// 只是取消掉目标侧订单
|
|
|
self._cancel_target_side_orders(&mut command);
|
|
|
// 更新撤单队列
|
|
|
- self._update_in_cancel(&mut command, trader_msg);
|
|
|
+ self._update_in_cancel(&mut command, local_orders);
|
|
|
// 检查限频
|
|
|
self._check_request_limit(&mut command);
|
|
|
// 统计请求频率
|
|
|
@@ -847,7 +868,7 @@ impl Strategy {
|
|
|
|
|
|
// 平仓订单处理命令
|
|
|
// #[instrument(skip(self, command), level="TRACE")]
|
|
|
- pub fn _post_close(&self, command: &mut OrderCommand, trader_msg: &TraderMsg) {
|
|
|
+ pub fn _post_close(&self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>) {
|
|
|
// debug!(?command);
|
|
|
|
|
|
let mut pd_amount = Decimal::ZERO;
|
|
|
@@ -861,8 +882,8 @@ impl Strategy {
|
|
|
let short_upper = self.close_dist[3];
|
|
|
|
|
|
// 获取当前挂单,如果超过挂单范围则取消当前平仓单
|
|
|
- for order_client_id in trader_msg.orders.keys() {
|
|
|
- let order = trader_msg.orders.get(order_client_id).unwrap();
|
|
|
+ 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()];
|
|
|
|
|
|
@@ -891,8 +912,8 @@ impl Strategy {
|
|
|
(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 trader_msg.orders.keys() {
|
|
|
- let order = trader_msg.orders.get(order_client_id).unwrap();
|
|
|
+ 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);
|
|
|
@@ -1000,7 +1021,7 @@ impl Strategy {
|
|
|
|
|
|
// 生成取消订单的指令
|
|
|
// #[instrument(skip(self, command), level="TRACE")]
|
|
|
- pub fn _cancel_open(&self, command: &mut OrderCommand, trader_msg: &TraderMsg) {
|
|
|
+ pub fn _cancel_open(&self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>) {
|
|
|
// debug!(?command);
|
|
|
// 挂单范围
|
|
|
let long_upper = self.open_dist[0];
|
|
|
@@ -1008,22 +1029,22 @@ impl Strategy {
|
|
|
let short_lower = self.open_dist[2];
|
|
|
let short_upper = self.open_dist[3];
|
|
|
|
|
|
- for order_client_id in trader_msg.orders.keys() {
|
|
|
- let order = trader_msg.orders.get(order_client_id).unwrap();
|
|
|
+ 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 == "kd".to_string() {
|
|
|
// 在价格范围内时不处理
|
|
|
- if order.price <= long_upper && order.price >= long_lower && self.side != "short".to_string() {
|
|
|
+ if order.price <= long_upper && order.price >= long_lower {
|
|
|
continue
|
|
|
}
|
|
|
// debug!(?key, ?order.price, ?long_upper, ?long_lower);
|
|
|
command.cancel.insert(key, value);
|
|
|
} else if order.side == "kk".to_string() { // 开空订单处理
|
|
|
// 在价格范围内时不处理
|
|
|
- if order.price >= short_lower && order.price <= short_upper && self.side != "long".to_string() {
|
|
|
+ if order.price >= short_lower && order.price <= short_upper {
|
|
|
continue
|
|
|
}
|
|
|
// debug!(?key, ?order.price, ?short_lower, ?short_upper);
|
|
|
@@ -1034,7 +1055,9 @@ impl Strategy {
|
|
|
|
|
|
// 超时触发查单信号
|
|
|
// #[instrument(skip(self, command), level="TRACE")]
|
|
|
- pub fn _check_local_orders(&mut self, command: &mut OrderCommand, trader_msg: &TraderMsg) {
|
|
|
+ pub fn _check_local_orders(&mut self,
|
|
|
+ command: &mut OrderCommand,
|
|
|
+ local_orders: &HashMap<String, OrderInfo>) {
|
|
|
// debug!(?command);
|
|
|
// 超时检测
|
|
|
if self.local_time - self._check_local_orders_time < self._check_local_orders_interval {
|
|
|
@@ -1042,7 +1065,7 @@ impl Strategy {
|
|
|
}
|
|
|
|
|
|
// 查单指令生成主逻辑
|
|
|
- for client_id in trader_msg.orders.keys() {
|
|
|
+ for client_id in local_orders.keys() {
|
|
|
let check_some = self.in_check.get(client_id);
|
|
|
|
|
|
// 如果在查单队列中,不需要再添加
|
|
|
@@ -1050,7 +1073,7 @@ impl Strategy {
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- let order = trader_msg.orders.get(client_id).unwrap();
|
|
|
+ let order = local_orders.get(client_id).unwrap();
|
|
|
// 没有超过10s的订单,不需要检查
|
|
|
if self.local_time - order.local_time < self._check_local_orders_interval {
|
|
|
continue;
|
|
|
@@ -1076,7 +1099,7 @@ impl Strategy {
|
|
|
|
|
|
// 开单指令生成逻辑
|
|
|
// #[instrument(skip(self, command), level="TRACE")]
|
|
|
- pub fn _post_open(&mut self, command: &mut OrderCommand, trader_msg: &TraderMsg) {
|
|
|
+ pub fn _post_open(&mut self, command: &mut OrderCommand, local_orders: &HashMap<String, OrderInfo>) {
|
|
|
// debug!(?command);
|
|
|
// 开仓逻辑检测,主要是检测整点开仓逻辑
|
|
|
if !self.check_allow_post_open() {
|
|
|
@@ -1102,8 +1125,8 @@ impl Strategy {
|
|
|
let mut sell_price_list: Vec<Decimal> = vec![];
|
|
|
let mut buy_value = Decimal::ZERO;
|
|
|
let mut sell_value = Decimal::ZERO;
|
|
|
- for client_id in trader_msg.orders.keys() {
|
|
|
- let order = trader_msg.orders.get(client_id).unwrap();
|
|
|
+ for client_id in local_orders.keys() {
|
|
|
+ let order = local_orders.get(client_id).unwrap();
|
|
|
if order.side == "kd".to_string() {
|
|
|
buy_price_list.push(order.price);
|
|
|
buy_value += order.amount * order.price;
|
|
|
@@ -1133,7 +1156,7 @@ impl Strategy {
|
|
|
|
|
|
// debug!(?self.post_side);
|
|
|
// 挂多单
|
|
|
- if self.post_side >= 0 && self.side != "short".to_string() {
|
|
|
+ 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);
|
|
|
@@ -1162,7 +1185,7 @@ impl Strategy {
|
|
|
}
|
|
|
}
|
|
|
// 挂空单
|
|
|
- if self.post_side <= 0 && self.side != "long".to_string() {
|
|
|
+ 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);
|
|
|
@@ -1213,13 +1236,26 @@ impl Strategy {
|
|
|
}
|
|
|
|
|
|
// 在满足条件后,返回非空command,否则返回一个空的command。原文的onTime。
|
|
|
- pub fn on_tick(&mut self, trader_msg: &TraderMsg, _ins: &Instant) -> OrderCommand {
|
|
|
+ pub fn on_tick(&mut self,
|
|
|
+ local_orders: &HashMap<String, OrderInfo>,
|
|
|
+ local_position: &LocalPosition,
|
|
|
+ agg_market: &Vec<Decimal>,
|
|
|
+ local_cash: &Decimal,
|
|
|
+ local_coin: &Decimal,
|
|
|
+ ref_price: &Vec<Vec<Decimal>>,
|
|
|
+ predict: &Decimal,
|
|
|
+ _ins: &Instant) -> OrderCommand {
|
|
|
self.on_time_print();
|
|
|
|
|
|
let mut command = OrderCommand::new();
|
|
|
|
|
|
// 更新逻辑数据出错时,不进行后面的逻辑处理
|
|
|
- if !self._update_data(trader_msg) {
|
|
|
+ if !self._update_data(local_position,
|
|
|
+ agg_market,
|
|
|
+ local_cash,
|
|
|
+ local_coin,
|
|
|
+ ref_price,
|
|
|
+ predict) {
|
|
|
return command;
|
|
|
}
|
|
|
|
|
|
@@ -1234,15 +1270,15 @@ impl Strategy {
|
|
|
self.generate_dist();
|
|
|
|
|
|
// 下单指令处理逻辑
|
|
|
- self._cancel_open(&mut command, trader_msg); // 撤单命令处理
|
|
|
- self._post_open(&mut command, trader_msg); // 限价单命令处理
|
|
|
- self._post_close(&mut command, trader_msg); // 平仓单命令处理
|
|
|
+ self._cancel_open(&mut command, local_orders); // 撤单命令处理
|
|
|
+ self._post_open(&mut command, local_orders); // 限价单命令处理
|
|
|
+ self._post_close(&mut command, local_orders); // 平仓单命令处理
|
|
|
|
|
|
- self._check_local_orders(&mut command, trader_msg); // 固定时间检查超时订单
|
|
|
- self._update_in_cancel(&mut command, trader_msg); // 更新撤单队列,是一个filter
|
|
|
- self._check_request_limit(&mut command); // 限制频率,移除不合规则之订单,是一个filter
|
|
|
- self._refresh_request_limit(); // 刷新频率限制
|
|
|
- self._update_request_num(&mut command); // 统计刷新频率
|
|
|
+ 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 || command.limits_close.len() != 0 {
|
|
|
let name = self.params.account_name.clone();
|
|
|
@@ -1314,7 +1350,6 @@ fn paras_limit_command (robot_name: String, time: i64, ref_ap: Decimal, ref_bp:
|
|
|
mod tests {
|
|
|
use rust_decimal::Decimal;
|
|
|
use rust_decimal_macros::dec;
|
|
|
- use crate::model::{OrderInfo, TraderMsg};
|
|
|
use global::params::Params;
|
|
|
use crate::strategy::Strategy;
|
|
|
|
|
|
@@ -1324,27 +1359,6 @@ mod tests {
|
|
|
|
|
|
let params = Params::new("config.toml.gate").unwrap();
|
|
|
let mut strategy = Strategy::new(¶ms, true);
|
|
|
- let mut trader_msg = TraderMsg::new();
|
|
|
- trader_msg.market.append(&mut vec![dec!(0.92), dec!(1.0), dec!(0.89), dec!(0.79), dec!(0.99), dec!(1.0), dec!(0.89), dec!(0.79), dec!(0.89), dec!(0.79), dec!(0.99), dec!(1.0), dec!(0.89), dec!(0.79)]);
|
|
|
- trader_msg.position.long_pos = dec!(100);
|
|
|
- trader_msg.position.long_avg = dec!(0.9);
|
|
|
- trader_msg.orders.insert("0001".to_string(), OrderInfo{
|
|
|
- symbol: "".to_string(),
|
|
|
- amount: Default::default(),
|
|
|
- side: "pd".to_string(),
|
|
|
- price: dec!(10),
|
|
|
- client_id: "0001".to_string(),
|
|
|
- filled_price: Default::default(),
|
|
|
- filled: Decimal::ZERO,
|
|
|
- order_id: "".to_string(),
|
|
|
- local_time: 0,
|
|
|
- create_time: 0,
|
|
|
- status: "".to_string(),
|
|
|
- fee: Default::default(),
|
|
|
- trace_stack: Default::default(),
|
|
|
- });
|
|
|
- trader_msg.cash = dec!(1000);
|
|
|
- trader_msg.coin = Decimal::ZERO;
|
|
|
|
|
|
strategy.is_ready = true;
|
|
|
strategy.equity = dec!(1000);
|