import time import traceback, utils import model import logging, logging.handlers from decimal import Decimal from decimal import ROUND_HALF_UP, ROUND_FLOOR class Strategy: ''' 策略逻辑 ''' def __init__(self, params: model.Config, is_print=0): self.params = params self.exchange = self.params.exchange self.broker_id = params.broker_id self.logger = self.get_logger() #### 实盘 ex = self.params.exchange pair = self.params.pair self.trade_name = ex + '@' + pair #### 参考 refex = self.params.refexchange refpair = self.params.refpair if len(refex) != len(refpair): print("参考盘口数不等于参考品种数 退出") return self.ref_num = len(refex) self.ref_name = [] for i in range(self.ref_num): name = refex[i] + '@' + refpair[i] self.ref_name.append(name) #### maker mode self.maker_mode = 'free' #### self._print_time = 0 self.local_orders = dict() self.pos = model.Position() self.long_hold_value = 0.0 self.short_hold_value = 0.0 self.equity = 0.0 self.coin = 0.0 self.cash = 0.0 self.start_equity = 0.0 self.start_coin = 0.0 self.start_cash = 0.0 self.max_equity = 0.0 self.local_profit = 0.0 self.total_amount = 0.0 self.ready = 0 self._is_print = is_print self._min_amount_value = 30.0 # 最小下单额 防止下单失败 self._max_amount_value = 10000.0 # 最大下单额 防止下单过重 平不掉就很悲剧 self.local_time = time.time() self.local_start_time = time.time() self.interval = float(self.params.interval) self.mp = None self.bp = None self.ap = None self.ref_price = 0.0 self.ref_bp = 0.0 self.ref_ap = 0.0 self.stepSize = 1e-10 self.tickSize = 1e-10 self.maxPos = 0.0 self.profit = 0.0 self.daily_return = 0.0 #### self.mp_ewma = None self.adjust_leverrate = 1.0 #### 持仓偏差 self.long_pos_bias = None self.short_pos_bias = None self.long_hold_rate = 0.0 self.short_hold_rate = 0.0 #### 时间相关参数 self.leverrate = float(self.params.leverrate) # 最大仓位 if "spot" in self.exchange:self.leverrate = min(self.leverrate, 1.0) self._print_time = time.time() self._start_time = time.time() self.request_num = 0 # 记录请求次数 self.request_order_num = 0 # 记录下单次数 self._print_interval = 5 # 打印信息时间间隔 #### 距离范围 self.open_dist = None self.close_dist = None #### 查单频率 self._check_local_orders_time = time.time() self._check_local_orders_interval = 10.0 #### 内部限頻 try: self.place_order_limit = float(self.params.place_order_limit) except: self.place_order_limit = 0 self.request_limit_check_time = time.time() self.request_limit_check_interval = 10.0 self.limit_requests_num = utils.get_limit_requests_num_per_second( self.params.exchange, self.place_order_limit) * self.request_limit_check_interval self.limit_order_requests_num = utils.get_limit_order_requests_num_per_second( self.params.exchange, self.place_order_limit) * self.request_limit_check_interval #### 网络请求频率 self._req_num_per_window = 0 # 开仓下单间隔 均匀下单机会 self.post_open_time = time.time() self.post_open_interval = 1/utils.get_limit_order_requests_num_per_second(self.params.exchange) #### 策略参数 # 距离类参数 self.trade_close_dist = 0.00001 # 基础挂单距离 self.trade_open_dist = 0.01 # 基础挂单距离 #### 时间类参数 # 撤单限頻队列 强制等待 防止频繁发起撤单 self.in_cancel = dict() self.cancel_wait_interval = 0.2 # 查单限頻队列 强制等待 self.in_check = dict() self.check_wait_interval = 10.0 # ref index self.ref_index = 0 # predict self.predict = 0.0 self.predict_alpha = 0.0 # post side self.post_side = 0 # trade vol self.trade_vol_24h = 0.0 # grid num self.grid = float(self.params.grid) def get_logger(self): logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # log to txt formatter = logging.Formatter('[%(asctime)s] - %(levelname)s - %(message)s') handler = logging.handlers.RotatingFileHandler(f"log.log",maxBytes=1024*1024) handler.setLevel(logging.DEBUG) handler.setFormatter(formatter) logger.addHandler(handler) return logger # TODO 目前看来是开仓前的一些准备,对本地数据状态进行更新 # @utils.timeit def _update_data(self, data:model.TraderMsg): '''更新本地数据''' try: # 更新信息 # orders self.local_orders.clear() self.local_orders.update(data.orders) # position if self.pos.longPos != data.position.longPos: self.pos.longPos = data.position.longPos self.pos.longAvg = data.position.longAvg if self.pos.shortPos != data.position.shortPos: self.pos.shortPos = data.position.shortPos self.pos.shortAvg = data.position.shortAvg # TODO data.market里面是组合盘口信息,所以此处的bp和ap都是从组合盘口里拿出来的 # bp ap self.bp = data.market[utils.BP_INDEX] self.ap = data.market[utils.AP_INDEX] # trade mp self.mp = (self.bp+self.ap)*0.5 ########### 动态杠杆调节 ########### if self.mp_ewma == None: self.mp_ewma = self.mp else: self.mp_ewma = self.mp_ewma*0.999 + self.mp*0.001 if self.mp > self.mp_ewma: # 增加杠杆 self.adjust_leverrate = 1.0 else: # 降低杠杆 self.adjust_leverrate = 0.8 ########### 当前持仓价值 ########### self.long_hold_value = self.pos.longPos * self.mp self.short_hold_value = self.pos.shortPos * self.mp # TODO 177-186计算单边最大持仓数量 ########### 现货 ########### if 'spot' in self.exchange: ### 计算总保证金情况 self.max_long_value = self.start_cash * self.leverrate * self.adjust_leverrate self.max_short_value = self.start_coin * self.leverrate * self.adjust_leverrate * self.mp ########### 合约 ########### else: ### 计算总保证金情况 self.max_long_value = self.equity * self.leverrate * self.adjust_leverrate self.max_short_value = self.max_long_value # TODO 做市模式识别 ###### maker mode ###### if self.ref_name[self.ref_index] == self.trade_name: self.maker_mode = 'free' else: self.maker_mode = 'follow' ###### ref price ###### if data.ref_price == None: self.ref_bp = self.bp self.ref_ap = self.ap self.ref_price = self.mp else: self.ref_bp = data.ref_price[self.ref_index][0] self.ref_ap = data.ref_price[self.ref_index][1] self.ref_price = (self.ref_bp+self.ref_ap)*0.5 # spread self.predict = utils.clip(data.predict*self.predict_alpha, -self.trade_open_dist, self.trade_open_dist) # is base spread normal ? can take but can't move too far # if abs(self.ref_price - self.mp)/self.mp > self.trade_open_dist*3: # back to pure market making strategy # self.ref_price = self.mp # equity 当前账户可用cash和coin self.coin = data.coin self.cash = data.cash if self.mp: self.equity = data.cash + data.coin * self.mp # max equity if self.equity > self.max_equity: self.max_equity = self.equity self.total_amount = float(utils.fix_amount(self.equity * self.leverrate * self.adjust_leverrate / self.mp, self.stepSize)) if self.total_amount == 0.0: if self._is_print: self.logger.error("总可开数量太少") # max pos maxPos = max([ self.pos.longPos, self.pos.shortPos ]) * self.mp // (self.equity if self.equity > 0 else 99999999) if maxPos > self.maxPos: self.maxPos = maxPos return 1 except: if self._is_print: self.logger.error(traceback.format_exc()) return 0 # @utils.timeit def _print_summary(self): ''' 打印状态信息 耗时700us ''' msg = '>>> ' msg += '盘口 ' + self.exchange + ' ' msg += '品种 ' + self.params.pair + ' ' msg += '现价 ' + str(round(self.mp, 6)) + ' ' msg += '定价 ' + str(round(self.ref_price, 6)) + ' ' msg += '偏差 ' + str(round((self.ref_price-self.mp)/self.mp*100, 2)) + '% ' msg += '净值 ' + str(round(self.equity, 3)) + ' ' msg += 'Cash ' + str(round(self.cash, 3)) + ' ' msg += 'Coin ' + str(round(self.coin*self.mp, 3)) + ' ' msg += '推算利润 ' + str(self.local_profit) + ' ' self.profit = round( (self.equity - self.start_equity) / self.start_equity * 100, 3) if self.start_equity > 0 else 0 msg += '盈亏 ' + str(self.profit) + '% ' msg += '多杠杆' + str( round( self.pos.longPos * self.mp / (self.equity if self.equity > 0 else 99999999), 3)) + ' ' self.long_pos_bias = None if self.pos.longPos > 0.0: self.long_pos_bias = round(100 - 100 * self.pos.longAvg / self.mp, 2) msg += '浮盈' + str(self.long_pos_bias) + '% ' else: msg += '浮盈 None ' msg += '空杠杆' + str( round( self.pos.shortPos * self.mp / (self.equity if self.equity > 0 else 99999999), 3)) + ' ' self.short_pos_bias = None if self.pos.shortPos > 0.0: self.short_pos_bias = round(100 * self.pos.shortAvg / self.mp - 100, 2) msg += '浮盈' + str(self.short_pos_bias) + '% ' else: msg += '浮盈 None ' msg += '杠杆' + str(self.leverrate) + ' 动态 ' + str(self.adjust_leverrate) + ' ' msg += '最大' + str(self.maxPos) + ' ' msg += '请求' + str(self._req_num_per_window) + ' 上限 ' + str(self.limit_order_requests_num) + '次/10s ' run_time = time.time() - self._start_time self.daily_return = round(self.profit / 100 / run_time * 86400, 5) msg += '日化' + str(self.daily_return) + ' ' msg += '当前参数 ' + \ ' 开仓 ' + str(round(self.trade_open_dist,6)) + \ ' 平仓 ' + str(round(self.trade_close_dist,6)) + \ ' 方向 ' + str(self.post_side) + \ ' 参考 ' + self.ref_name[self.ref_index] + \ ' 模式 ' + self.maker_mode + \ ' 预测 ' + str(round(self.predict,5)) + \ ' 预估24H成交额 ' + str(self.trade_vol_24h) + 'W' + \ ' 实时优化 ' + str(self.params.backtest) # 写入日志 if self._is_print: self.logger.info(msg) #### 本地订单状态列表 #### o_num = len(self.local_orders) self.logger.info(f"挂单列表 共{o_num}单") for cid in self.local_orders: i = self.local_orders[cid] msg = i['symbol'] + ' ' + str(i['client_id']) + ' ' + i['side'] + ' 杠杆: ' + \ str(round(i['amount'] * self.mp / (self.equity if self.equity > 0 else 99999999), 3)) + 'x 价值:' + \ str(round(i['amount'] * self.mp,2)) + 'u' + ' 价格: ' + \ str(i['price']) + ' 偏离:' + \ str(round((i['price'] - self.mp) / self.mp * 100, 3)) + '%' if self._is_print: self.logger.info(msg) self.logger.info("撤单列表") if len(self.in_cancel) > 0: if self._is_print: self.logger.info(self.in_cancel) self.logger.info("查单列表") if len(self.in_check) > 0: if self._is_print: self.logger.info(self.in_check) # def fix_amount(self, amount): # '''修补数量向下取整''' # return float(Decimal(str(amount)).quantize(Decimal(str(self.stepSize)), ROUND_FLOOR)) # def fix_price(self, price): # '''修补价格四舍五入''' # return float(Decimal(str(price)).quantize(Decimal(str(self.tickSize)), ROUND_HALF_UP)) def _cancel_targit_side_orders(self, order_side=["kd","kk","pd","pk"]): '''清理指定类型挂单''' signals = dict() # 撤销指定类型挂单 for cid in self.local_orders: i = self.local_orders[cid] if i["side"] in order_side: cid = i['client_id'] oid = i['order_id'] signals[f'Cancel{cid}'] = [cid, oid] return signals def _close_all(self): ''' 清空所有挂单和仓位保持休眠状态 ''' signals = dict() # 撤销全部挂单 pd_amount = 0.0 pk_amount = 0.0 for cid in self.local_orders: i = self.local_orders[cid] cid = i['client_id'] oid = i['order_id'] if i['side'] == 'pk': pk_amount += i['amount'] elif i['side'] == 'pd': pd_amount += i['amount'] signals[f'Cancel{cid}'] = [cid, oid] # 批量挂单 signals['Limits_close'] = [] need_close_long = self.pos.longPos - pd_amount need_close_short = self.pos.shortPos - pk_amount if "spot" in self.exchange: if need_close_long * self.mp > self._min_amount_value: amount = need_close_long price = utils.fix_price(self.mp, self.tickSize) amount = utils.fix_amount(amount, self.stepSize) signals['Limits_close'].append([amount, 'pd', price, utils.get_cid(self.broker_id)]) if need_close_short * self.mp > self._min_amount_value: amount = need_close_short price = utils.fix_price(self.mp, self.tickSize) amount = utils.fix_amount(amount, self.stepSize) signals['Limits_close'].append([amount, 'pk', price, utils.get_cid(self.broker_id)]) else: if need_close_long > 0: # sell price = utils.fix_price(self.mp, self.tickSize) amount = need_close_long if amount * self.mp > self._min_amount_value: signals['Limits_close'].append([amount, 'pd', price,utils.get_cid(self.broker_id)]) if need_close_short > 0: # buy price = utils.fix_price(self.mp, self.tickSize) amount = need_close_short if amount * self.mp > self._min_amount_value: signals['Limits_close'].append([amount, 'pk', price,utils.get_cid(self.broker_id)]) return signals def _post_close(self): ''' 处理平仓 ''' # 准备命令 signals = dict() signals['Limits_close'] = [] # 撤掉危险挂单 pdAmount = 0.0 pdOrderNum = 0 pkAmount = 0.0 pkOrderNum = 0 # 计算 long_upper = self.close_dist[0] long_lower = self.close_dist[1] short_lower = self.close_dist[2] short_upper = self.close_dist[3] # # 获取当前挂单 for cid in self.local_orders: i = self.local_orders[cid] if i['side'] == 'pk': c1 = i['price'] > long_upper c2 = i['price'] < long_lower if c1 or c2: signals[f'Cancel{cid}'] = [i['client_id'], i['order_id']] # else: pkAmount += i['amount'] pkOrderNum += 1 elif i['side'] == 'pd': c1 = i['price'] < short_lower c2 = i['price'] > short_upper if c1 or c2: signals[f'Cancel{cid}'] = [i['client_id'], i['order_id']] # else: pdAmount += i['amount'] pdOrderNum += 1 need_cancel_all_close = 0 if abs(pdAmount - self.pos.longPos) * self.mp > self._min_amount_value or \ abs(pkAmount - self.pos.shortPos) * self.mp > self._min_amount_value: need_cancel_all_close = 1 if need_cancel_all_close: for cid in self.local_orders: i = self.local_orders[cid] if i['side'] in ['pk','pd']: signals[f'Cancel{cid}'] = [i['client_id'], i['order_id']] ####################### 检查是否需要挂平仓单 if 'spot' in self.exchange: ### 需要平多的价值大于最小交易价值 执行平多逻辑 if self.pos.longPos * self.mp > self._min_amount_value: if pdOrderNum == 0: # 需要更新平仓挂单 price = (short_lower + short_upper)*0.5 price = utils.clip(price, self.bp*0.9995, self.ap*1.03) price = utils.fix_price(price, self.tickSize) amount = self.pos.longPos amount = utils.fix_amount(amount, self.stepSize) if float(amount) * float(price) > self._min_amount_value: signals['Limits_close'].append([ amount, 'pd', price, utils.get_cid(self.broker_id) ]) if self.pos.shortPos > self._min_amount_value: if pkOrderNum == 0: # 需要更新平仓挂单 price = (long_upper + long_lower)*0.5 price = utils.clip(price, self.bp*0.97, self.ap*1.0005) price = utils.fix_price(price, self.tickSize) amount = self.pos.shortPos amount = utils.fix_amount(amount, self.stepSize) if float(amount) * float(price) > self._min_amount_value: signals['Limits_close'].append([ amount, 'pk', price, utils.get_cid(self.broker_id) ]) else: if self.pos.longPos > 0.0: # 正常平多 if pdOrderNum == 0: # 需要更新平仓挂单 price = short_lower*0.5 + short_upper*0.5 price = utils.clip(price, self.bp*0.9995, self.ap*1.03) price = utils.fix_price(price, self.tickSize) signals['Limits_close'].append([ self.pos.longPos, 'pd', price, utils.get_cid(self.broker_id) ]) if self.pos.shortPos > 0.0: # 正常平空 if pkOrderNum == 0: # 需要更新平仓挂单 price = long_upper*0.5 + long_lower*0.5 price = utils.clip(price, self.bp*0.97, self.ap*1.0005) price = utils.fix_price(price, self.tickSize) signals['Limits_close'].append([ self.pos.shortPos, 'pk', price, utils.get_cid(self.broker_id) ]) return signals def _cancel_open(self): ''' 撤销开仓 ''' # 准备命令 signals = dict() # 计算挂单范围 long_upper = self.open_dist[0] # long upper--------高风险 long_lower = self.open_dist[1] # long lower--------低风险 short_lower = self.open_dist[2] # short lower------高风险 short_upper = self.open_dist[3] # short upper------低风险 # # 获取当前挂单 for cid in self.local_orders: i = self.local_orders[cid] if i['side'] == 'kd': # TODO 在挂单范围之外时 # 撤开多单 c1 = i['price'] > long_upper c2 = i['price'] < long_lower # c3 = i['price'] > long_upper*(1-self.long_hold_rate) + long_lower*self.long_hold_rate # if c1 or c2 or c3: if c1 or c2: signals[f'Cancel{cid}'] = [i['client_id'], i['order_id']] elif i['side'] == 'kk': # TODO 在挂单范围之外时 # 撤开空单 c1 = i['price'] < short_lower c2 = i['price'] > short_upper # c3 = i['price'] < short_lower*(1-self.short_hold_rate) + short_upper*self.short_hold_rate # if c1 or c2 or c3: if c1 or c2: signals[f'Cancel{cid}'] = [i['client_id'], i['order_id']] return signals # @utils.timeit def _post_open(self): ''' 处理开仓 开仓要一直挂 ''' # 准备命令 signals = dict() signals['Limits_open'] = [] # 计算挂单范围 long_upper = self.open_dist[0] long_lower = self.open_dist[1] short_lower = self.open_dist[2] short_upper = self.open_dist[3] # # 获取当前挂单 buyP = [] sellP = [] buy_value = 0 sell_value = 0 for cid in self.local_orders: i = self.local_orders[cid] if i['side'] in ['kd']: buyP.append(i['price']) buy_value += i['amount'] * i['price'] if i['side'] in ['kk']: sellP.append(i['price']) sell_value += i['amount'] * i['price'] # TODO 计算可开价值 ########### 现货 ########### if 'spot' in self.exchange: ### 计算当前持币和持u ### coin_value = self.coin * self.mp * self.leverrate * self.adjust_leverrate # 可卖价值 cash_value = self.cash * self.leverrate * self.adjust_leverrate # 可买价值 long_free_value = min(cash_value, self.max_long_value) - buy_value short_free_value = min(coin_value, self.max_short_value) - sell_value ########### 合约 ########### else: ### 合约只有已有仓位和开仓订单会占用保证金 long_free_value = self.max_long_value - self.long_hold_value - buy_value short_free_value = self.max_short_value - self.short_hold_value - sell_value ####################################### one_hand_long_value = self.max_long_value / self.grid * 0.99 one_hand_short_value = self.max_short_value / self.grid * 0.99 ############## 单层挂单 ################# ########### 挂多单 ########### if self.post_side >= 0: if len(buyP) == 0: # TODO 计算开单价格,并限定范围,要求价格有效,截取小数位数。 # 1 targit_buy_price = long_upper * 0.5 + long_lower * 0.5 targit_buy_price = utils.clip(targit_buy_price, self.bp*0.97, self.ap*1.0005) targit_buy_price = utils.fix_price(targit_buy_price, self.tickSize) value = min(one_hand_long_value, long_free_value) amount = utils.fix_amount(value/self.mp, self.stepSize) amount_value = float(amount) * self.mp # TODO 下单前最后检测 if amount_value >= self._min_amount_value and amount_value <= long_free_value: signals['Limits_open'].append([amount, 'kd', targit_buy_price, utils.get_cid(self.broker_id)]) ########### 挂空单 ########### if self.post_side <= 0: if len(sellP) == 0: # TODO 计算开单价格,并限定范围,要求价格有效,截取小数位数。 # 1 targit_sell_price = short_lower * 0.5 + short_upper * 0.5 targit_sell_price = utils.clip(targit_sell_price, self.bp*0.9995, self.ap*1.03) targit_sell_price = utils.fix_price(targit_sell_price, self.tickSize) value = min(one_hand_short_value, short_free_value) amount = utils.fix_amount(value/self.mp, self.stepSize) amount_value = float(amount) * self.mp # TODO 下单前最后检测 if amount_value >= self._min_amount_value and amount_value <= short_free_value: signals['Limits_open'].append([amount, 'kk', targit_sell_price, utils.get_cid(self.broker_id)]) return signals # @utils.timeit def gen_dist(self, open, close, pos_rate, ref_bp, ref_ap, predict, grid=1, mode='free'): ''' Input: 开仓距离 平仓距离 参考价格 产生挂单位置 4 3 1 2 用预测调近挂单距离很危险 ''' ########################### mp = (ref_bp+ref_ap)*0.5 buy_start = mp sell_start = mp ########################### # 持有多仓时 平仓sell更近 持有空仓时 平仓buy 更近 avoid = min(0.0005, close * 0.5) # 平仓位置偏移可以适当大一点 # 平仓位置 close_dist = [ buy_start * ( 1 + predict - close + avoid), # buy upper buy_start * ( 1 + predict - close - avoid), # buy lower sell_start * ( 1 + predict + close - avoid), # sell lower sell_start * ( 1 + predict + close + avoid), # sell upper ] ###################################################### if mode == 'free': # 自由做市 buy_start = ref_bp sell_start = ref_ap elif mode == 'follow': # 跟随做市 mp = (ref_bp+ref_ap)*0.5 buy_start = mp sell_start = mp else: # 跟随做市 mp = (ref_bp+ref_ap)*0.5 buy_start = mp sell_start = mp ########################### ########################### # 持有多仓时 开仓buy更远 持有空仓时 开仓sell 更远 avoid = min(0.001, open * 0.05) # 开仓位置偏移可以适当小一点 # 持仓偏移 buy_shift = 1 + pos_rate[0] * grid sell_shift = 1 + pos_rate[1] * grid # 保护窗口 open_dist = [ buy_start * ( 1 + predict - open * buy_shift + avoid), # buy upper buy_start * ( 1 + predict - open * buy_shift - avoid), # buy lower sell_start * ( 1 + predict + open * sell_shift - avoid), # sell lower sell_start * ( 1 + predict + open * sell_shift + avoid), # sell upper ] ########################### return open_dist, close_dist def _update_request_num(self, signals): '''统计请求次数''' if 'Limits_open' in signals: self.request_num += len(signals['Limits_open']) self.request_order_num += len(signals['Limits_open']) if 'Limits_close' in signals: self.request_num += len(signals['Limits_close']) self.request_order_num += len(signals['Limits_close']) for i in signals: if 'Cancel' in i: self.request_num += 1 elif 'Check' in i: self.request_num += 1 def _check_request_limit(self, signals): '''根据平均请求次数限制开仓下单''' if self.request_num > self.limit_requests_num: return dict() elif self.request_num >= self.limit_requests_num * 0.5 or self.request_order_num >= self.limit_order_requests_num*0.8: new_signals = dict() for order_name in signals: if 'Limits_open' in order_name: pass elif 'Limits_close' in order_name and self.request_order_num >= self.limit_order_requests_num: pass else: new_signals[order_name] = signals[order_name] return new_signals else: return signals def _check_local_orders(self): '''超过时间限制触发查单信号''' signals = dict() if self.local_time - self._check_local_orders_time >= self._check_local_orders_interval: for cid in self.local_orders: # 如果没在查单队列中 if cid not in self.in_check: # 超过10s没动的订单 进行检查 if self.local_time - self.local_orders[cid]["localtime"] > self._check_local_orders_interval: signals[f"Check{cid}"] = [cid, self.local_orders[cid]['order_id']] self.in_check[cid] = self.local_time if self._is_print: self.logger.debug(f"查询订单 {cid}") # 维护查单队列 self._release_in_check() # 更新查单时间 self._check_local_orders_time = self.local_time return signals def _release_in_check(self): '''检查是否正在撤单''' new_dict = dict() for cid in self.in_check: # 等待超过后移除正在撤单队列 if self.local_time - self.in_check[cid] <= self.check_wait_interval: new_dict[cid] = self.in_check[cid] self.in_check = new_dict def _release_in_cancel(self): '''检查是否正在撤单''' new_dict = dict() for cid in self.in_cancel: # 等待超过后移除正在撤单队列 if self.local_time - self.in_cancel[cid] <= self.cancel_wait_interval: new_dict[cid] = self.in_cancel[cid] self.in_cancel = new_dict def _update_in_cancel(self, signals): ''' 新增正在撤单 检查撤单队列 释放过时限制 ''' new_signals = dict() for i in signals: if 'Cancel' in i: cid = signals[i][0] need_limit_cancel = 1 # 判断是否在挂单表中 if cid in self.local_orders: # 判断是否在订单创建100ms内 if self.local_time - self.local_orders[cid]['createtime'] < 0.1: # 解除撤单限制 need_limit_cancel = 0 if need_limit_cancel: # 增加撤单限制 if cid not in self.in_cancel: self.in_cancel[cid] = self.local_time new_signals[i] = signals[i] else: new_signals[i] = signals[i] ### 释放撤单限制 self._release_in_cancel() return new_signals def _refresh_request_limit(self): if self.local_time - self.request_limit_check_time >= self.request_limit_check_interval: self._req_num_per_window = self.request_num self.request_num = 0 self.request_order_num = 0 self.request_limit_check_time = self.local_time def _pos_rate(self): '''获取持仓比例 0~1''' long_hold_rate = 0.0 short_hold_rate = 0.0 if self.max_long_value > 0.0: long_hold_rate = self.long_hold_value/self.max_long_value if self.max_short_value > 0.0: short_hold_rate = self.short_hold_value/self.max_short_value # print(long_hold_rate, short_hold_rate) self.long_hold_rate = long_hold_rate self.short_hold_rate = short_hold_rate def check_ready(self): '''检查准备''' pre_hot = 10 if int(self.params.backtest): pre_hot = utils.BACKTEST_PREHOT_SECOND if self.ready != 1: if isinstance(self.mp, float) and self.local_time - self.local_start_time > pre_hot: self.ready = 1 if self._is_print: print('预热完毕') return 1 else: return 0 def check_allow_post_open(self): ''' 检查是否允许报单 ''' ### 接近整点时刻 不允许报单 防止下单bug ### diff_time = self.local_time % 3600 if diff_time < 30 or diff_time > 3570: return 0 ######################################## return 1 def onExit(self, data): ''' 全撤全平 准备退出 ''' try: # 更新状态 if self._update_data(data): # 检查是否准备充分 if self.check_ready(): return dict() # 交易模式 signals = self._close_all() # 更新撤单队列 signals = self._update_in_cancel(signals) # 交易模式 signals = self._check_request_limit(signals) # 统计请求频率 self._update_request_num(signals) return signals except: traceback.print_exc() def onSleep(self, data): ''' 全撤 不再下新订单了 防止影响check_position执行 ''' try: # 更新状态 if self._update_data(data): # 检查是否准备充分 if self.check_ready(): return dict() # 交易模式 signals = self._cancel_targit_side_orders() # 更新撤单队列 signals = self._update_in_cancel(signals) # 交易模式 signals = self._check_request_limit(signals) # 统计请求频率 self._update_request_num(signals) return signals except: traceback.print_exc() # @utils.timeit def onTime(self, data): ''' call on time ''' try: # 定时打印 if self.local_time - self._print_time > self._print_interval: self._print_time = self.local_time if self._is_print: if self.ready: pass else: self.logger.info("预热中") # 更新状态 if self._update_data(data): # TODO 我吐了啊,准备不充分才执行下面的,准备充分是直接return # 检查是否准备充分 if self.check_ready(): return dict() ###### 关键操作 ###### # 更新挂单距离 self._pos_rate() open_dist, close_dist = self.gen_dist( open=self.trade_open_dist, close=self.trade_close_dist, pos_rate=[self.long_hold_rate, self.short_hold_rate], ref_bp=self.ref_bp, ref_ap=self.ref_ap, predict=self.predict, grid=self.grid, mode=self.maker_mode, ) self.open_dist = \ [ utils.fix_price(open_dist[0], self.tickSize), utils.fix_price(open_dist[1], self.tickSize), utils.fix_price(open_dist[2], self.tickSize), utils.fix_price(open_dist[3], self.tickSize), ] self.close_dist = \ [ utils.fix_price(close_dist[0], self.tickSize), utils.fix_price(close_dist[1], self.tickSize), utils.fix_price(close_dist[2], self.tickSize), utils.fix_price(close_dist[3], self.tickSize), ] # 获取开平仓指令 signals = dict() # 获取撤单信号 signals.update(self._cancel_open()) # 获取开仓信号 整点时刻前后不报单 if self.check_allow_post_open(): # TODO 报单延迟检测 if self.local_time - self.post_open_time > self.post_open_interval: self.post_open_time = self.local_time # TODO 开仓指令获取 signals.update(self._post_open()) # 获取平仓信号 signals.update(self._post_close()) # 每隔固定时间检查超时订单 signals.update(self._check_local_orders()) # 更新撤单队列 signals = self._update_in_cancel(signals) # 限制频率 signals = self._check_request_limit(signals) # 刷新频率限制 self._refresh_request_limit() # 统计请求频率 self._update_request_num(signals) ########################## return signals except: traceback.print_exc() if self._is_print:self.logger.error(traceback.format_exc())