|
|
@@ -1,3 +1,4 @@
|
|
|
+use std::cmp::max;
|
|
|
use std::collections::{BTreeMap, HashMap};
|
|
|
use std::io::Error;
|
|
|
use std::str::FromStr;
|
|
|
@@ -11,7 +12,7 @@ use tokio::sync::mpsc::{channel, Receiver, Sender};
|
|
|
use tokio::sync::Mutex;
|
|
|
use tokio::task::JoinHandle;
|
|
|
use tokio::time::sleep;
|
|
|
-use tracing::{error, info};
|
|
|
+use tracing::{error, info, warn};
|
|
|
use exchanges::binance_swap_ws::{BinanceSubscribeType, BinanceSwapWs, BinanceWsType};
|
|
|
use exchanges::gate_swap_rest::GateSwapRest;
|
|
|
use exchanges::gate_swap_ws::{GateSubscribeType, GateSwapWs, GateWsType};
|
|
|
@@ -797,6 +798,169 @@ impl Quant {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ pub fn check_risk(&mut self) {
|
|
|
+ // 参数检查的风控
|
|
|
+ if self.strategy.start_cash == Decimal::ZERO {
|
|
|
+ warn!("请检查交易账户余额");
|
|
|
+ warn!(?_self.strategy.start_cash);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if self.strategy.mp == Decimal::ZERO {
|
|
|
+ warn!("请检查最新价格");
|
|
|
+ warn!(?_self.strategy.mp);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 不是现货执行的回撤风控1
|
|
|
+ if !self.exchange.contains("spot") {
|
|
|
+ let draw_back = Decimal::ONE - self.strategy.equity / self.strategy.max_equity;
|
|
|
+
|
|
|
+ if draw_back > self.stop_loss {
|
|
|
+ let exit_msg = format!("{} 总资金吊灯回撤 {}。当前净值:{}, 最高净值{},触发止损,准备停机。",
|
|
|
+ self.params.account_name, draw_back, self.strategy.equity, self.strategy.max_equity);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 回撤风控2
|
|
|
+ let draw_back = self.local_profit / self.strategy.start_equity;
|
|
|
+ if draw_back < -self.stop_loss {
|
|
|
+ let exit_msg = format!("{} 交易亏损,触发止损,准备停机。", self.params.account_name);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ // 报单延迟风控,平均延迟允许上限5000ms
|
|
|
+ // TODO quant.platform_rest.avg_delay不存在
|
|
|
+ // if _self.platform_rest.avg_delay > 5000 {
|
|
|
+ // let exit_msg = format!("{} 延迟爆表 触发风控 准备停机。", _self.params.account_name);
|
|
|
+ // warn!(exit_msg);
|
|
|
+ // _self.exit_msg = exit_msg;
|
|
|
+ // // TODO quant.stop()不存在
|
|
|
+ // // _self.stop()
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 仓位异常风控,只在合约模式下执行
|
|
|
+ if !self.exchange.contains("spot") {
|
|
|
+ let long_diff = (self.local_position.long_pos - self.local_position_by_orders.long_pos).abs();
|
|
|
+ let short_diff = (self.local_position.short_pos - self.local_position_by_orders.short_pos).abs();
|
|
|
+ let diff_pos = max(long_diff, short_diff);
|
|
|
+ let diff_pos_value = diff_pos * self.strategy.mp;
|
|
|
+ if diff_pos_value > self.strategy._min_amount_value {
|
|
|
+ warn!("{}发现仓位异常", _self.params.account_name);
|
|
|
+ warn!(?_self.local_position_by_orders, ?_self.local_position);
|
|
|
+ self.position_check_series.push(1);
|
|
|
+ } else {
|
|
|
+ self.position_check_series.push(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // _self.position_check_series长度限制
|
|
|
+ if self.position_check_series.len() > 30 {
|
|
|
+ self.position_check_series.remove(0);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 连续不符合判定
|
|
|
+ if self.position_check_series.iter().sum::<i8>() >= 30 {
|
|
|
+ let exit_msg = format!("{} 合约连续检查本地仓位和推算仓位不符合,退出。", self.params.account_name);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下单异常风控
|
|
|
+ if self.strategy.total_amount == Decimal::ZERO {
|
|
|
+ let exit_msg = format!("{} 开仓量为0,退出。", self.params.account_name);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+
|
|
|
+ // 行情更新异常风控
|
|
|
+ let mut exchange_names = self.ref_name.clone();
|
|
|
+ exchange_names.push(self.trade_name.clone());
|
|
|
+ for exchange_name in exchange_names {
|
|
|
+ let now_time_millis = Utc::now().timestamp_millis();
|
|
|
+ let last_update_millis = self.market_update_time.get(&exchange_name).unwrap();
|
|
|
+ let delay = now_time_millis - last_update_millis;
|
|
|
+ let limit = global::public_params::MARKET_DELAY_LIMIT;
|
|
|
+
|
|
|
+ if delay > limit {
|
|
|
+ let exit_msg = format!("{} ticker_name:{}, delay:{}ms,行情更新延迟过高,退出。",
|
|
|
+ self.params.account_name, exchange_name, delay);
|
|
|
+ warn!(?now_time_millis, ?last_update_millis, ?limit);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单异常风控
|
|
|
+ for (client_id, order) in self.local_orders {
|
|
|
+ // 订单长时间停留 怀疑漏单 但未必一定漏 5min
|
|
|
+ if Utc::now().timestamp_millis() - order.local_time > 5 * 60 * 1000 {
|
|
|
+ let exit_msg = format!("{}订单停留过长,怀疑异常,退出,cid:{}。", self.params.account_name, client_id);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 持仓均价异常风控
|
|
|
+ if self.strategy.long_pos_bias != Decimal::ZERO {
|
|
|
+ if self.strategy.long_hold_value > Decimal::TWO * self.strategy._min_amount_value {
|
|
|
+ if self.strategy.long_pos_bias > dec!(4) || self.strategy.long_pos_bias < -Decimal::TWO {
|
|
|
+ let exit_msg = format!("{} long_pos_bias: {},持仓均价异常,退出。", self.params.account_name, self.strategy.long_pos_bias);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if self.strategy.short_pos_bias != Decimal::ZERO {
|
|
|
+ if self.strategy.short_hold_value > Decimal::TWO * self.strategy._min_amount_value {
|
|
|
+ if self.strategy.short_pos_bias > dec!(4) || self.strategy.short_pos_bias < -Decimal::TWO {
|
|
|
+ let exit_msg = format!("{} short_pos_bias: {},持仓均价异常,退出。", self.params.account_name, self.strategy.long_pos_bias);
|
|
|
+ warn!(exit_msg);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单撤单异常风控
|
|
|
+ for (client_id, cancel_delay) in self.local_cancel_log {
|
|
|
+ if cancel_delay > 300 {
|
|
|
+ let exit_msg = format!("{} 长时间无法撤销,client_id: {},退出。", self.params.account_name, client_id);
|
|
|
+ warn!(exit_msg);
|
|
|
+ warn!(?_self.strategy.ref_price, ?_self.strategy.mp);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 定价异常风控
|
|
|
+ if (self.strategy.ref_price - self.strategy.mp).abs() / self.strategy.mp > dec!(0.03) {
|
|
|
+ let exit_msg = format!("{} 定价偏离过大,怀疑定价异常,退出。", self.params.account_name);
|
|
|
+ warn!(exit_msg);
|
|
|
+ warn!(?_self.strategy.ref_price, ?_self.strategy.mp);
|
|
|
+ self.exit_msg = exit_msg;
|
|
|
+ // TODO quant.stop()不存在
|
|
|
+ // _self.stop()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
pub async fn before_trade(&mut self) -> bool {
|
|
|
sleep(Duration::from_secs(1)).await;
|
|
|
|