use std::cmp::{max, min}; use std::collections::HashMap; use std::ops::{Add, Div, Mul}; use chrono::Utc; use rust_decimal::Decimal; use rust_decimal::prelude::{FromPrimitive, ToPrimitive}; use rust_decimal_macros::dec; use crate::model::{LocalPosition, OrderInfo, TraderMsg}; use crate::params::Params; use crate::utils; use tracing::{info, instrument, error, debug}; use tracing::field::debug; use tracing_subscriber; use standard::OrderCommand; #[derive(Debug)] pub struct Strategy { // pub interval: Decimal, // 原文没有使用过这个参数 // 各类时间戳和时延,我们都改成了毫秒级 pub _print_time: i64, // 上次打印时间 pub _print_interval: i64, // 打印时延 pub _start_time: i64, // 开始时间 pub local_time: i64, // 本地时间 pub local_start_time: i64, // 本地开始时间 pub post_open_time: i64, // 上次提交订单的时间戳 pub post_open_interval: i64, // 提交订单时延 pub _check_local_orders_time: i64, // 上次查单时间 pub _check_local_orders_interval: i64, // 查单间距,原文是秒级,这里改成毫秒级 pub in_cancel: HashMap, //撤单队列 pub cancel_wait_interval: i64, // 取消等待时延 pub in_check: HashMap, // 查单队列 pub check_wait_interval: i64, // 检测时延 pub request_limit_check_time: i64, // 上次检查订单的时间 pub request_limit_check_interval: i64, // 原文是秒级,这里改成毫秒级 pub request_count: i64, // 记录请求次数,原文的request_num pub request_order_count: i64, // 记录下单次数,原文的request_order_num pub limit_requests_num: i64, // 单位(时延)时间内请求次数上限 pub limit_order_requests_num: i64, // 单位(时延)时间内下单次数上限 pub _req_num_per_window: i64, // 单位(时延)时间内请求上限窗口 pub place_order_limit: i64, // 参数层面传进来的下单数量限制 pub params: Params, // pub exchange: String, // pub broker_id: String, // pub trade_name: String, // pub ref_exchange_length: usize, // pub ref_name: Vec, // pub maker_mode: String, // pub local_orders: HashMap, // 本地订单 pub pos: LocalPosition, // pub long_hold_value: Decimal, // pub short_hold_value: Decimal, // pub equity: Decimal, // pub coin: Decimal, // pub cash: Decimal, // pub start_equity: Decimal, // pub start_coin: Decimal, // pub start_cash: Decimal, // pub max_equity: Decimal, // pub local_profit: Decimal, // pub total_amount: Decimal, // pub is_ready: bool, // 程序是否已经准备好,ready pub _is_print: bool, // pub _min_amount_value: Decimal, // pub _max_amount_value: Decimal, // pub mp_ema: Decimal, // 原文的mp_ewma pub mp: Decimal, // pub bp: Decimal, // pub ap: Decimal, // pub ref_price: Decimal, // pub ref_bp: Decimal, // pub ref_ap: Decimal, // pub step_size: Decimal, // 原文的stepSize pub tick_size: Decimal, // 原文的tickSize pub max_pos_rate: Decimal, // 原文的maxPos,其实是最大持仓比例 pub profit: Decimal, // pub daily_return: Decimal, // pub adjust_lever_rate: Decimal, // 原文的adjust_leverrate pub lever_rate: Decimal, // 原文的leverrate pub long_pos_bias: Decimal, // pub short_pos_bias: Decimal, // pub long_hold_rate: Decimal, // pub short_hold_rate: Decimal, // pub max_long_value: Decimal, // 最大做多持仓 pub max_short_value: Decimal, // 最大做空持仓 pub open_dist: Vec, // 开仓相关价格 pub close_dist: Vec, // 平仓相关价格 pub trade_close_dist: Decimal, // pub trade_open_dist: Decimal, // pub ref_index: usize, // pub predict: Decimal, // pub predict_alpha: Decimal, // pub post_side: i64, // 交易方向 pub trade_vol_24h_w: Decimal, // 24小时成交额(单位:万) pub grid: Decimal, // 网格数量 } impl Strategy { pub fn new(params: &Params, is_print: bool) -> Self { if params.ref_exchange.len() != params.ref_pair.len() { panic!("参考盘口数不等于参考品种数,退出,请检查配置!") } // strategy的初始化,里面已经有一些参数初始化了 let mut strategy = Self { _print_time: 0, _start_time: 0, local_time: 0, local_start_time: 0, request_count: 0, request_order_count: 0, _print_interval: 5 * 1000, in_cancel: Default::default(), cancel_wait_interval: (0.2 * 1000f64).to_i64().unwrap(), in_check: Default::default(), check_wait_interval: 10 * 1000, _check_local_orders_time: 0, _check_local_orders_interval: 0, place_order_limit: 0, request_limit_check_time: 0, request_limit_check_interval: 0, limit_requests_num: 0, limit_order_requests_num: 0, _req_num_per_window: 0, post_open_time: 0, post_open_interval: 0, params: params.clone(), exchange: params.exchange.clone(), broker_id: params.broker_id.clone(), trade_name: "".to_string(), ref_exchange_length: params.ref_exchange.len(), ref_name: vec![], maker_mode: "free".to_string(), local_orders: Default::default(), pos: LocalPosition { long_pos: Default::default(), short_pos: Default::default(), long_avg: Default::default(), short_avg: Default::default(), }, long_hold_value: Default::default(), short_hold_value: Default::default(), equity: Default::default(), coin: Default::default(), cash: Default::default(), start_equity: Default::default(), start_coin: Default::default(), start_cash: Default::default(), max_equity: Default::default(), local_profit: Default::default(), total_amount: Default::default(), is_ready: false, _is_print: is_print, _min_amount_value: dec!(30.0), _max_amount_value: dec!(10000.0), mp_ema: Default::default(), mp: Default::default(), bp: Default::default(), ap: Default::default(), ref_price: Default::default(), ref_bp: Default::default(), ref_ap: Default::default(), step_size: dec!(1e-10), tick_size: dec!(1e-10), max_pos_rate: Default::default(), profit: Default::default(), daily_return: Default::default(), adjust_lever_rate: Decimal::ONE, lever_rate: Default::default(), long_pos_bias: Default::default(), short_pos_bias: Default::default(), long_hold_rate: Default::default(), short_hold_rate: Default::default(), 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, predict: Default::default(), predict_alpha: Default::default(), post_side: 0, trade_vol_24h_w: Default::default(), grid: Decimal::from(params.grid), }; // 交易名字 strategy.trade_name = format!("{}@{}", params.exchange.clone(), params.pair.clone()); // 参考交易所的trade_name for index in 0..strategy.ref_exchange_length { strategy.ref_name.push(format!("{}@{}", params.ref_exchange[index], params.ref_pair[index])); } // 杠杆比例处理 strategy.lever_rate = params.lever_rate; if strategy.exchange.contains("spot") { strategy.lever_rate = min(params.lever_rate, Decimal::ONE); } // 各类时间戳 let now = Utc::now(); strategy.local_time = now.timestamp_millis(); strategy.local_start_time = now.timestamp_millis(); strategy._print_time = now.timestamp_millis(); strategy._start_time = now.timestamp_millis(); // 检查订单的时间戳 strategy._check_local_orders_time = now.timestamp_millis(); strategy._check_local_orders_interval = 10 * 1000; // 下单的相关限制处理 strategy.place_order_limit = params.place_order_limit; strategy.request_limit_check_time = now.timestamp_millis(); strategy.request_limit_check_interval = 10 * 1000; // 求得正常请求数量和下单请求数量(interval时间内) let request_limit_check_interval_per_second = strategy.request_limit_check_interval / 1000; strategy.limit_requests_num = utils::get_limit_requests_num_per_second(params.exchange.clone(), params.place_order_limit) * (request_limit_check_interval_per_second); strategy.limit_order_requests_num = utils::get_limit_order_requests_num_per_second(params.exchange.clone(), params.place_order_limit) * (request_limit_check_interval_per_second); // 开仓下单间隔 均匀下单机会 strategy.post_open_time = now.timestamp_millis(); let post_open_interval_per_second = Decimal::ONE.div(Decimal::from_i64(utils::get_limit_order_requests_num_per_second(params.exchange.clone(), 0)).unwrap()); strategy.post_open_interval = dec!(1000).mul(post_open_interval_per_second).to_i64().unwrap(); info!("策略模块初始化完成!"); return strategy; } // 更新当前strategy的各类信息 #[instrument(skip(self, trader_msg), level="TRACE")] pub fn _update_data(&mut self, trader_msg: &TraderMsg) -> bool { debug!(?self); debug!(?trader_msg); self.local_orders.clear(); self.local_orders = trader_msg.orders.clone(); // 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.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; } 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.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 = self.start_cash * self.lever_rate * self.adjust_lever_rate; self.max_short_value = self.start_cash * 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 trader_msg.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_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; 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; 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={}", self.equity, self.lever_rate, self.adjust_lever_rate); 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; } // 打印状态信息 pub fn _print_summary(&self) { info!("_print_summary还没有施工……") } // 取消目标方向订单,原文是_cancel_targit_side_orders #[instrument(skip(self, command), level="TRACE")] pub fn _cancel_target_side_orders(&self, command: &mut OrderCommand) { // 要取消的目标方向 let target_side = vec![ "kd".to_string(), "kk".to_string(), "pd".to_string(), "pk".to_string() ]; debug!(?self.local_orders); for client_id in self.local_orders.keys() { let order = self.local_orders.get(client_id).unwrap(); // 如果不属于目标方向,则不需要取消 if !target_side.contains(&order.side.clone()) { continue; } // 属于目标方向,则取消 let key = format!("Cancel{}", client_id); let value = vec![order.client_id.clone(), order.order_id.clone()]; command.cancel.insert(key, value); } 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 mode = self.maker_mode.clone(); // 平仓相关 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)); let mut 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); // 自由做市模式 if mode == "free".to_string() { buy_start = ref_bp; sell_start = ref_ap; } // 跟随做市模式 else if mode == "follow".to_string() { mp = (ref_bp + ref_ap) * dec!(0.5); buy_start = mp; sell_start = mp; } else { panic!("未知做市类型:mode={}", mode) } debug!(?mode, ?buy_start, ?sell_start, ?mp); // 开仓相关 avoid = min(dec!(0.001), open * dec!(0.05)); // 持仓偏移 let buy_shift = Decimal::ONE + pos_rate[0] * grid; let sell_shift = Decimal::ONE + pos_rate[1] * grid; let mut 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 ]; debug!(?avoid, ?buy_shift, ?sell_shift, ?avoid, ?open_dist); // 修复价格 for open_price in &mut open_dist { *open_price = utils::fix_price(*open_price, self.tick_size); } for close_price in &mut close_dist { *close_price = utils::fix_price(*close_price, self.tick_size); } self.open_dist = open_dist.clone(); self.close_dist = close_dist.clone(); debug!(?open_dist); debug!(?close_dist); } // 统计请求次数 #[instrument(skip(self, command), level="TRACE")] pub fn _update_request_num(&mut self, command: &OrderCommand) { debug!(?command); debug!(?self.request_order_count, ?self.request_count); let order_count = (command.limits_open.len() + command.limits_close.len()).to_i64().unwrap(); let request_count = order_count + (command.cancel.len() + command.check.len()).to_i64().unwrap(); self.request_order_count += order_count; self.request_count += request_count; debug!(?self.request_order_count, ?self.request_count); } // 根据平均请求次数限制开仓下单 #[instrument(skip(self, command), level="TRACE")] pub fn _check_request_limit(&mut self, command: &mut OrderCommand) { debug!(?command); // 如果当前请求数超过限制 if self.request_count > self.limit_requests_num { command.cancel.clear(); command.check.clear(); command.limits_open.clear(); command.limits_close.clear(); error!("请求频率溢出,程序禁止任何操作!"); debug!(?command); return; } // 普通request超过普通请求频率上限的50%再进行判断 // 下单request要超过下单请求频率上限的80%再进行判断 if self.request_count < self.limit_requests_num * 5 / 10 && self.request_order_count < self.limit_order_requests_num * 8 / 10 { return; } // 超过80%,直接取消limits_open的下单指令 error!("80%+下单频率,程序禁止开仓!"); debug!(?self.request_order_count, ?self.limit_order_requests_num); command.limits_open.clear(); // 100%超过下单频率,则不再进行平仓挂单 if self.request_order_count >= self.limit_order_requests_num { command.limits_close.clear(); error!("100%下单频率!程序禁止平仓!"); debug!(?self.request_order_count, ?self.limit_order_requests_num) } debug!(?command); } // 新增正在撤单、检查撤单队列,释放过时限制 #[instrument(skip(self), level="TRACE")] pub fn _update_in_cancel(&mut self, command: &mut OrderCommand) { let mut new_cancel: HashMap> = 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 = self.local_orders.get(&client_id); // 判断是否在本地挂单表中 if let Some(order) = order_some { // 如果订单创建时间大于100ms,才能有撤单操作 if self.local_time - order.create_time < 100 { need_limit_cancel = false; } } if need_limit_cancel { // 如果已经不在撤销队列里,增加到撤销队列 if self.in_cancel.get(&client_id).is_none() { self.in_cancel.insert(client_id.clone(), self.local_time); new_cancel.insert(cancel_name.clone(), cancel.clone()); } } } debug!(?command); command.cancel = new_cancel; debug!(?command); // 释放撤单限制 self._release_in_cancel(); } // 维护查单队列,检查是否在撤单 #[instrument(skip(self), level="TRACE")] pub fn _release_in_check(&mut self) { debug!(?self.in_check); // 为什么要移出来:Rust不允许边循环边修改map let mut to_remove = Vec::new(); for client_id in self.in_check.keys() { let time = self.in_check.get(client_id).unwrap(); // 等待不超时,就不移除 if self.local_time - time <= self.check_wait_interval { continue; } // 等待超时,就移除正在撤单队列 info!("移除查单队列:{}", client_id.clone()); to_remove.push(client_id.clone()); } // 在后面的循环中去单独处理map的更新 for client_id in to_remove { self.in_check.remove(&client_id); } debug!(?self.in_check); } // 检查是否正在撤单 #[instrument(skip(self), level="TRACE")] pub fn _release_in_cancel(&mut self) { debug!(?self.in_cancel); // 为什么要移出来:Rust不允许边循环边修改map let mut to_remove = Vec::new(); for client_id in self.in_cancel.keys() { let time = self.in_cancel.get(client_id).unwrap(); // 等待不超时,就不移除 if self.local_time - time <= self.cancel_wait_interval { continue; } // 等待超时,就移除正在撤单队列 info!("移除取消队列:{}", client_id.clone()); to_remove.push(client_id.clone()); } // 在后面的循环中去单独处理map的更新 for client_id in to_remove { self.in_cancel.remove(&client_id); } debug!(?self.in_cancel); } // 刷新请求限制 pub fn _refresh_request_limit(&mut self) { if self.local_time - self.request_limit_check_time < self.request_limit_check_interval { return; } self._req_num_per_window = self.request_count; self.request_count = 0; self.request_order_count = 0; self.request_limit_check_time = self.local_time; } // 刷新持仓比例 #[instrument(skip(self), level="TRACE")] pub fn _pos_rate(&mut self) { debug!(?self); if self.max_long_value > Decimal::ZERO { self.long_hold_rate = self.long_hold_value / self.max_long_value; debug!(?self.long_hold_rate); } if self.max_short_value > Decimal::ZERO { self.short_hold_rate = self.short_hold_value / self.max_short_value; debug!(?self.short_hold_rate); } } // 当退出时调用,全撤全平 准备退出 pub fn on_exit(&mut self, trader_msg: &TraderMsg) -> OrderCommand { let mut command = OrderCommand::new(); if self._update_data(trader_msg) { if !self.check_ready() { return command; } // 取消、平掉所有 self._close_all(&mut command); // 更新撤单队列 self._update_in_cancel(&mut command); // 检查限频 self._check_request_limit(&mut command); // 统计请求频率 self._update_request_num(&mut command); } debug!(?command); return command; } // 休眠时调用,全撤 不再下新订单了 防止影响check_position执行 pub fn on_sleep(&mut self, trader_msg: &TraderMsg) -> OrderCommand { let mut command = OrderCommand::new(); if self._update_data(trader_msg) { if !self.check_ready() { return command; } // 只是取消掉目标侧订单 self._cancel_target_side_orders(&mut command); // 更新撤单队列 self._update_in_cancel(&mut command); // 检查限频 self._check_request_limit(&mut command); // 统计请求频率 self._update_request_num(&mut command); } debug!(?command); return command; } // 清空所有挂单和仓位保持休眠状态 #[instrument(skip(self, command), level="TRACE")] pub fn _close_all(&self, command: &mut OrderCommand) { // 撤掉全部挂单 let mut pd_amount = Decimal::ZERO; let mut pk_amount = Decimal::ZERO; debug!(?self.local_orders); for client_id in self.local_orders.keys() { let order = self.local_orders.get(client_id).unwrap(); // 命令生成 let key = format!("Cancel{}", client_id); let value = vec![order.client_id.clone(), order.order_id.clone()]; command.cancel.insert(key, value); // 统计部分 if order.side == "pk".to_string() { pk_amount += order.amount; } else if order.side == "pd".to_string() { pd_amount += order.amount; } } debug!(?pd_amount, ?pk_amount); // 批量挂单 let need_close_long = self.pos.long_pos - pd_amount; let need_close_short = self.pos.short_pos - pk_amount; debug!(?need_close_long, ?need_close_short); // 做多仓位平仓 if need_close_long * self.mp > self._min_amount_value { let mut amount = need_close_long; // 现货要对数量精度进行限定处理 if self.exchange.contains("spot") { amount = utils::fix_amount(amount, self.step_size); } let price = utils::fix_price(self.mp, self.tick_size); let client_id = utils::generate_client_id(Some(self.broker_id.clone())); let value = vec![ amount.to_string(), "pd".to_string(), price.to_string(), client_id.to_string() ]; command.limits_close.insert(client_id.clone(), value); debug!(?self.pos.long_pos, ?self.mp, ?need_close_long, ?command) } // 做空仓位平仓 if need_close_short * self.mp > self._min_amount_value { let mut amount = need_close_short; if self.exchange.contains("spot") { amount = utils::fix_amount(amount, self.step_size); } let price = utils::fix_price(self.mp, self.tick_size); let client_id = utils::generate_client_id(Some(self.broker_id.clone())); let value = vec![ amount.to_string(), "pk".to_string(), price.to_string(), client_id.to_string() ]; command.limits_close.insert(client_id.clone(), value); debug!(?self.pos.short_pos, ?self.mp, ?need_close_short, ?command) } } // 检查是否完成准备,注意:原文是未准备完成返回true!!!!!!!!!!!!!!!!!!! pub fn check_ready(&mut self) -> bool { if self.is_ready { return true; } let pre_hot:i64 = 10 * 1000; if !self.mp.eq(&Decimal::ZERO) && self.local_time - self.local_start_time > pre_hot { self.is_ready = true; debug!(?self.mp, ?self.local_time, ?self.local_start_time, ?pre_hot); info!("策略预热完毕,可以执行后续逻辑!") } return false; } // 接近整点时刻 不允许报单 防止下单bug pub fn check_allow_post_open(&self) -> bool { let local_time_second = self.local_time / 1000; let diff_time = local_time_second % (60 * 60); return diff_time > 30 && diff_time < 3570; } // 平仓订单处理命令 #[instrument(skip(self, command), level="TRACE")] pub fn _post_close(&self, command: &mut OrderCommand) { 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 self.local_orders.keys() { let order = self.local_orders.get(order_client_id).unwrap(); let key = format!("Cancel{}", (*order_client_id).clone()); 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 self.local_orders.keys() { let order = self.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).clone()); let value = vec![order.client_id.clone(), order.order_id.clone()]; command.cancel.insert(key.clone(), value.clone()); } } } 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_open.insert(order_client_id, order.clone()); 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_open.insert(order_client_id, order.clone()); debug!(?command); } } } } else { if self.pos.long_pos > Decimal::ZERO { 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_open.insert(order_client_id, order.clone()); debug!(?command); } if self.pos.short_pos > Decimal::ZERO { 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() ]; debug!(?command); } } } // 生成取消订单的指令 #[instrument(skip(self, command), level="TRACE")] pub fn _cancel_open(&self, command: &mut OrderCommand) { debug!(?command); // 挂单范围 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]; for order_client_id in self.local_orders.keys() { let order = self.local_orders.get(order_client_id).unwrap(); let key = format!("Cancel{}", (*order_client_id).clone()); 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 { continue } debug!(?key, ?order.price, ?long_upper, ?long_lower); command.cancel.insert(key.clone(), value.clone()); } // 开空订单处理 if order.side == "kk".to_string() { // 在价格范围内时不处理 if order.price > short_lower && order.price < short_upper { continue } debug!(?key, ?order.price, ?short_lower, ?short_upper); command.cancel.insert(key.clone(), value.clone()); } } } // 超时触发查单信号 #[instrument(skip(self, command), level="TRACE")] pub fn _check_local_orders(&mut self, command: &mut OrderCommand) { debug!(?command); // 超时检测 if self.local_time - self._check_local_orders_time < self._check_local_orders_interval { return; } // 查单指令生成主逻辑 for client_id in self.local_orders.keys() { let check_some = self.in_check.get(client_id); // 如果在查单队列中,不需要再添加 if let Some(_) = check_some { continue; } let order = self.local_orders.get(client_id).unwrap(); // 没有超过10s的订单,不需要检查 if self.local_time - order.local_time < self._check_local_orders_interval { continue; } let key = format!("Check{}", client_id.clone()); let value = vec![ client_id.clone(), order.order_id.clone(), ]; command.check.insert(key, value); self.in_check.insert(client_id.clone(), self.local_time); info!("查询订单:{:?}", client_id.clone()); debug!(?command); } // 维护查单队列 self._release_in_check(); // 更新查单时间 self._check_local_orders_time = self.local_time; } // 开单指令生成逻辑 #[instrument(skip(self, command), level="TRACE")] pub fn _post_open(&mut self, command: &mut OrderCommand) { debug!(?command); // 开仓逻辑检测,主要是检测整点开仓逻辑 if !self.check_allow_post_open() { return; } // 报单时延检测 if self.local_time - self.post_open_time < self.post_open_interval { return; } // 挂单范围获取 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 = vec![]; let mut sell_price_list: Vec = vec![]; let mut buy_value = Decimal::ZERO; let mut sell_value = Decimal::ZERO; for client_id in self.local_orders.keys() { let order = self.local_orders.get(client_id).unwrap(); if order.side == "kd".to_string() { buy_price_list.push(order.price); buy_value += order.amount * order.price; } if order.side == "kk".to_string() { sell_price_list.push(order.price); sell_value += order.amount * order.price; } } // 计算可开价值 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); // 挂多单 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 = 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.clone(), order); // 报单时间更新 self.post_open_time = self.local_time; } } } // 挂空单 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 = 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.clone(), order); // 报单时间更新 self.post_open_time = self.local_time; } } } } // 定时打印 pub fn on_time_print(&mut self) { if self.local_time - self._print_time < self._print_interval { return; } // 记录上次打印时间 self._print_time = self.local_time; if !self._is_print { return; } // 准备好了的话就不打印预热中了 if self.is_ready { return; } info!("预热中"); } // 在满足条件后,返回非空command,否则返回一个空的command。原文的onTime。 pub fn on_time(&mut self, trader_msg: &TraderMsg) -> OrderCommand { self.on_time_print(); let mut command = OrderCommand::new(); // 更新逻辑数据出错时,不进行后面的逻辑处理 if !self._update_data(trader_msg) { return command; } // 检查是否准备充分 if !self.check_ready() { return command; } // 刷新多空双方持仓比例 self._pos_rate(); // 生成开仓、平仓等相关价格 self.generate_dist(); // 下单指令处理逻辑 self._cancel_open(&mut command); // 撤单命令处理 self._post_open(&mut command); // 限价单命令处理 self._post_close(&mut command); // 平仓单命令处理 self._check_local_orders(&mut command); // 固定时间检查超时订单 self._update_in_cancel(&mut command); // 更新撤单队列,是一个filter self._check_request_limit(&mut command); // 限制频率,移除不合规则之订单,是一个filter self._refresh_request_limit(); // 刷新频率限制 self._update_request_num(&mut command); // 统计刷新频率 return command; } } #[cfg(test)] mod tests { use rust_decimal::Decimal; use rust_decimal_macros::dec; use tracing::{debug}; use crate::model::{OrderInfo, TraderMsg}; use crate::params::Params; use crate::strategy::Strategy; #[test] fn on_time_test() { global::log_utils::init_log_with_debug(); let params = Params::new("config.toml").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(), }); trader_msg.cash = dec!(1000); trader_msg.coin = Decimal::ZERO; strategy.is_ready = true; strategy.equity = dec!(1000); strategy.lever_rate = Decimal::ONE; debug!("{:?}", strategy.on_time(&trader_msg)); } }