class LocalLooper: message_box = {-1: "超出下单限制", -2: "超出涨跌价格", -3: "未成交", -4: "资金不足"} def __init__(self, app_signal, app): """ 需要构建完整的成交回报以及发单报告, 在account里面需要存储大量的存储 在我们此处实现过程也通过调用事件引擎来进行调用 """ self.app = app # 活跃报单数量 self.change_month_record = {} self.pending = [] self.sessionid = random.randint(1000, 10000) self.frontid = random.randint(10001, 500000) # 日志输出器 self.app_signal = app_signal # 策略池子 self.strategy_mapping = dict() # 覆盖里面的action和logger属性 # 涨跌停价格 self.upper_price = 99999 self.drop_price = 0 # 风控/risk control todo:完善 self.params = dict(deal_pattern="match", single_order_limit=10, single_day_limit=100, today_exchange=['INE', "SHFE"]) # 账户属性 self.account = Account(self) self.order_ref = 0 # 发单的ref集合 self.order_ref_set = set() # 已经order_id ---- 成交单 self.traded_order_mapping = {} # 已经order_id --- 报单 self.order_id_pending_mapping = {} # 当日成交笔数, 需要如果是第二天的数据,那么需要被清空 self.today_volume = 0 self.pre_close_price = dict() # 所有的报单数量 self.order_buffer = dict() self.date = None # 行情 self.data_entity = None self.if_next_day = False self.data_type = "bar" self.price_mapping = dict() # 仓位详细 self.position_detail = dict() def get_trades(self): return list(self.traded_order_mapping.values()) def on_event(self, type, data): event = Event(type=type, data=data) if type == EVENT_BAR or type == EVENT_TICK: import ctpbee.signals as signals signal = getattr(signals.common_signals, f"{type}_signal") else: signal = getattr(self.app_signal, f"{type}_signal") signal.send(event) def enable_extension(self, name): if name in self.strategy_mapping.keys(): self.strategy_mapping.get(name).active = True else: return def suspend_extension(self, name): if name in self.strategy_mapping.keys(): self.strategy_mapping.get(name).active = False else: return def update_risk(self, risk): self.risk = risk def _generate_order_data_from_req(self, req: OrderRequest): """ 将发单请求转换为发单数据 """ self.order_ref += 1 order_id = f"{self.frontid}-{self.sessionid}-{self.order_ref}" return req._create_order_data(gateway_name="looper", order_id=order_id, time=self.datetime) def _generate_trade_data_from_order(self, order_data: OrderData): """ 将orderdata转换成成交单 """ p = TradeData(price=order_data.price, istraded=order_data.volume, volume=order_data.volume, tradeid=str(uuid.uuid1()), offset=order_data.offset, direction=order_data.direction, gateway_name=order_data.gateway_name, order_time=order_data.time, time=self.datetime, order_id=order_data.order_id, symbol=order_data.symbol, exchange=order_data.exchange) return p def send_order(self, order_req: OrderRequest): """ 发单的操作 """ if order_req.volume == 0: return 0 return self.intercept_gateway(order_req) def _cancel(self, cancel_req): """ 撤单机制 """ self.intercept_gateway(cancel_req) def cancel_order(self, cancel_req: CancelRequest, **kwargs): rx = [] for x in self.pending: if x.order_id == cancel_req.order_id: x.status = Status.CANCELLED # 回调 on_order做处理 self.on_event(EVENT_ORDER, deepcopy(x)) # 移除掉冻结 使得成为可能 self.account.pop_order(x) # 更新仓位信息 self.account.position_manager.update_order(x) rx.append(x) for x in rx: self.pending.remove(x) def cancel_all(self): self.pending.clear() return 1 def intercept_gateway(self, data): """ 拦截网关 同时这里应该返回相应的水平""" if isinstance(data, OrderRequest): """ 发单请求处理 """ order_data = self._generate_order_data_from_req(data) result, reason = self.account.is_traded(order=order_data) if result: self.pending.append(order_data) order_data.status = Status.NOTTRADED self.on_event(EVENT_ORDER, order_data) self.account.update_account_from_order(order_data) return 1 else: self.on_event( EVENT_ERROR, f"账户报单可用不足, 报单基础信息: " f"{order_data.local_symbol} volume: {order_data.volume}" f" price: {order_data.price} {order_data.offset.value}{order_data.direction.value}" f" 出现原因: {reason} 当前报单队列存在 {len(self.pending)}") return 0 else: pass def match_deal(self): """ 撮合成交 维护一个返回状态 -1: 超出下单限制 -2: 超出涨跌价格 -3: 未成交 -4: 资金不足 p : 成交回报 todo: 处理冻结 ?? """ rc = [] for data in self.pending: px = "".join(filter(str.isalpha, data.local_symbol)) nx = "".join(filter(str.isalpha, self.data_entity.local_symbol)) if nx != px: # 针对多品种,实现拆分。 更新当前的价格,确保多个 continue if self.params.get("deal_pattern") == "match": """ 撮合成交 """ # todo: 是否可以模拟一定量的市场冲击响应? 以靠近更加逼真的回测效果 ???? """ 调用API生成成交单 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) """ 这里按照市价进行匹配成交 """ trade.price = self.data_entity.last_price self.on_event( EVENT_LOG, f"--> {trade.local_symbol} 成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}" ) self.account.update_trade(trade) """ 调用strategy的on_trade """ rc.append(data) data.status = Status.ALLTRADED self.on_event(EVENT_ORDER, data=data) self.on_event(EVENT_TRADE, data=trade) self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume continue if self.params.get("deal_pattern") == "umatch": """ 撮合成交 """ # todo: 是否可以模拟一定量的市场冲击响应? 以靠近更加逼真的回测效果 ???? """ 调用API生成成交单 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) """ 这里按照市价进行匹配成交 """ self.on_event( EVENT_LOG, f"--> {trade.local_symbol} 成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}" ) self.account.update_trade(trade) """ 调用strategy的on_trade """ rc.append(data) data.status = Status.ALLTRADED self.on_event(EVENT_ORDER, data=data) self.on_event(EVENT_TRADE, data=trade) self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume continue elif self.params.get("deal_pattern") == "price": """ 限价成交 """ # 先判断价格和手数是否满足限制条件 # 进行成交判断 long_c = self.data_entity.low_price if self.data_type == "bar" else self.data_entity.ask_price_1 short_c = self.data_entity.high_price if self.data_type == "bar" is not None else self.data_entity.bid_price_1 long_cross = data.direction == Direction.LONG and 0 < long_c <= data.price short_cross = data.direction == Direction.SHORT and data.price <= short_c and short_c > 0 if long_cross: """ 判断账户资金是否足以支撑成交 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) self.on_event( EVENT_LOG, data= f"成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}" ) """ 调用strategy的on_trade """ rc.append(data) data.status = Status.ALLTRADED self.account.update_trade(trade) self.on_event(EVENT_ORDER, data=deepcopy(data)) self.on_event(EVENT_TRADE, data=deepcopy(trade)) self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume continue elif short_cross: """ 调用API生成成交单 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) self.on_event( EVENT_LOG, data= f"成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}" ) """ 调用strategy的on_trade """ rc.append(data) data.status = Status.ALLTRADED self.account.update_trade(trade) self.on_event(EVENT_ORDER, data=deepcopy(data)) self.on_event(EVENT_TRADE, data=deepcopy(trade)) self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume continue continue else: raise TypeError("未支持的成交机制") for data in rc: self.pending.remove(data) def _init_params(self, params): """ 回测参数设置 """ self.params.update(params) """ 更新接口参数设置 """ self.params.update(params) """ 更新账户策略参数 """ self.account.update_params(params) def init_params(self, params): """ 初始化参数设置 """ if not isinstance(params, dict): raise AttributeError("回测参数类型错误,请检查是否为字典") self._init_params(params.get("LOOPER")) @staticmethod def auth_time(time): if 15 < time.hour <= 20 or 3 <= time.hour <= 8: return False else: return True def __call__(self, *args, **kwargs): """ 回测周期 """ entity = args[0] # 日期不相等时, 更新前日结算价格 if self.account.date is None: self.account.date = self.date if self.date is None: self.date = entity.datetime.date() if not self.auth_time(entity.datetime): return self.data_type = entity.type # 回测的时候自动更新策略的日期 try: seconds = (entity.datetime - self.datetime).seconds if seconds >= 60 * 60 * 4 and (entity.datetime.hour >= 21 or ( (14 <= self.datetime.hour <= 15) and entity.datetime.date() != self.datetime.date())): self.account.settle(entity.datetime.date()) # 针对于账户的实现 我们需要将昨仓转换为今仓 self.app.recorder.position_manager.covert_to_yesterday_holding( ) for local_symbol, price in self.price_mapping.items(): self.pre_close_price[local_symbol] = price # 结算完触发初始化函数 self.on_event(EVENT_INIT_FINISHED, True) except KeyError: pass except AttributeError: pass self.data_entity = entity self.change_month_record["".join( filter(str.isalpha, entity.local_symbol.split(".")[0]))] = entity # 维护一个最新的价格 self.price_mapping[self.data_entity.local_symbol] = self.data_entity.close_price if entity.type == "bar" \ else self.data_entity.last_price if self.pre_close_price.get(self.data_entity.local_symbol) is None: self.pre_close_price[ self.data_entity. local_symbol] = self.data_entity.last_price if entity.type == "tick" else self.data_entity.close_price self.datetime = entity.datetime self.match_deal() if entity.type == "tick": self.account.position_manager.update_tick( self.data_entity, self.pre_close_price[self.data_entity.local_symbol]) self.app.recorder.position_manager.update_tick( self.data_entity, self.pre_close_price[self.data_entity.local_symbol]) self.on_event(EVENT_TICK, TickData(**entity)) if entity.type == "bar": self.account.position_manager.update_bar( self.data_entity, self.pre_close_price[self.data_entity.local_symbol]) self.app.recorder.position_manager.update_bar( self.data_entity, self.pre_close_price[self.data_entity.local_symbol]) self.on_event(EVENT_BAR, BarData(**entity)) if entity.datetime.hour >= 21: """if hour > 21, switch to next trade day""" index = trade_dates.index(str(entity.datetime.date())) self.date = datetime.strptime(trade_dates[index + 1], "%Y-%m-%d").date() else: if str(entity.datetime.date()) not in trade_dates: last_day = entity.datetime + timedelta(days=-1) self.date = datetime.strptime( trade_dates[trade_dates.index(str(last_day.date())) + 1], "%Y-%m-%d").date() else: self.date = entity.datetime.date() # 穿过接口日期检查 self.account.via_aisle() def get_entity_from_alpha(self, alpha): return self.change_month_record.get(alpha)
class LocalLooper(): message_box = {-1: "超出下单限制", -2: "超出涨跌价格", -3: "未成交", -4: "资金不足"} def __init__(self, logger, risk=None): """ 需要构建完整的成交回报以及发单报告,在account里面需要存储大量的存储 """ # 活跃报单数量 self.change_month_record = {} self.pending = [] self.sessionid = random.randint(1000, 10000) self.frontid = random.randint(10001, 500000) # 日志输出器 self.logger = logger # 策略池子 self.strategy_mapping = dict() # 覆盖里面的action和logger属性 # 涨跌停价格 self.upper_price = 99999 self.drop_price = 0 # 风控/risk control todo:完善 self.risk = risk self.params = dict(deal_pattern="match", single_order_limit=10, single_day_limit=100, today_exchange=['INE', "SHFE"]) # 账户属性 self.account = Account(self) self.order_ref = 0 # 发单的ref集合 self.order_ref_set = set() # 已经order_id ---- 成交单 self.traded_order_mapping = {} # 已经order_id --- 报单 self.order_id_pending_mapping = {} # 当日成交笔数, 需要如果是第二天的数据,那么需要被清空 self.today_volume = 0 self.pre_close_price = dict() # 所有的报单数量 self.order_buffer = dict() self.date = None # 行情 self.data_entity = None self.if_next_day = False self.data_type = "bar" self.price_mapping = dict() # 仓位详细 self.position_detail = dict() self.action = Action(self) def get_trades(self): return list(self.traded_order_mapping.values()) def update_strategy(self, strategy): setattr(strategy, "action", self.action) setattr(strategy, "logger", self.logger) setattr(strategy, "info", self.logger.info) setattr(strategy, "debug", self.logger.debug) setattr(strategy, "error", self.logger.error) setattr(strategy, "warning", self.logger.warning) setattr(strategy, "app", self) self.strategy_mapping[strategy.name] = strategy def enable_extension(self, name): if name in self.strategy_mapping.keys(): self.strategy_mapping.get(name).active = True else: return def suspend_extension(self, name): if name in self.strategy_mapping.keys(): self.strategy_mapping.get(name).active = False else: return def update_risk(self, risk): self.risk = risk def _generate_order_data_from_req(self, req: OrderRequest): """ 将发单请求转换为发单数据 """ self.order_ref += 1 order_id = f"{self.frontid}-{self.sessionid}-{self.order_ref}" return req._create_order_data(gateway_name="looper", order_id=order_id, time=self.datetime) def _generate_trade_data_from_order(self, order_data: OrderData): """ 将orderdata转换成成交单 """ p = TradeData(price=order_data.price, istraded=order_data.volume, volume=order_data.volume, tradeid=str(uuid.uuid1()), offset=order_data.offset, direction=order_data.direction, gateway_name=order_data.gateway_name, order_time=order_data.time, time=self.datetime, order_id=order_data.order_id, symbol=order_data.symbol, exchange=order_data.exchange) return p def send_order(self, order_req: OrderRequest): """ 发单的操作 """ if order_req.volume == 0: return 0 return self.intercept_gateway(order_req) def _cancel(self, cancel_req): """ 撤单机制 """ self.intercept_gateway(cancel_req) def cancel(self, order_id): for x in self.pending: if x.order_id == order_id: self.pending.remove(x) return 1 return 0 def cancel_all(self): self.pending.clear() return 1 def intercept_gateway(self, data): """ 拦截网关 同时这里应该返回相应的水平""" if isinstance(data, OrderRequest): """ 发单请求处理 """ order_data = self._generate_order_data_from_req(data) if self.account.is_traded(order=order_data): self.pending.append(order_data) self.account.update_account_from_order(order_data) return 1 else: self.logger.info("报单可用不足") self.logger.debug(f"close_profit: {self.account.close_profit}") self.logger.debug(f"margin: {self.account.margin}") self.logger.debug(f"pre_balance: {self.account.pre_balance}") self.logger.debug(f"float_pnl: {self.account.float_pnl}") self.logger.debug( f"frozen_margin: {self.account.frozen_margin}") self.logger.debug(f"available: {self.account.available}") return 0 if isinstance(data, CancelRequest): """ 撤单请求处理 """ for order in self.pending: if data.order_id == order.order_id: order = deepcopy(order) [api(order) for api in self.strategy_mapping.values()] self.pending.remove(order) self.account.pop_order(order) return 1 return 0 def match_deal(self): """ 撮合成交 维护一个返回状态 -1: 超出下单限制 -2: 超出涨跌价格 -3: 未成交 -4: 资金不足 p : 成交回报 todo: 处理冻结 ?? """ for data in self.pending: px = "".join(filter(str.isalpha, data.local_symbol)) nx = "".join(filter(str.isalpha, self.data_entity.local_symbol)) if nx != px: # 针对多品种,实现拆分。 更新当前的价格,确保多个 continue if self.params.get("deal_pattern") == "match": """ 撮合成交 """ # todo: 是否可以模拟一定量的市场冲击响应? 以靠近更加逼真的回测效果 ???? if self.account.is_traded(data): """ 调用API生成成交单 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) self.logger.info( f"--> {trade.local_symbol} 成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}, " f"账户净值: {self.account.balance} 保证金: {self.account.margin} 账户剩余可用: {self.account.available} 此时队列中的单子: {len(self.pending)}" ) self.account.update_trade(trade) """ 调用strategy的on_trade """ self.pending.remove(data) data.status = Status.ALLTRADED [ api(deepcopy(data)) for api in self.strategy_mapping.values() ] [api(trade) for api in self.strategy_mapping.values()] self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume else: print("单子没成交哦") continue elif self.params.get("deal_pattern") == "price": """ 见价成交 """ # 先判断价格和手数是否满足限制条件 if data.price < self.drop_price or data.price > self.upper_price: """ 超出涨跌价格 """ continue # 进行成交判断 long_c = self.data_entity.low_price if self.data_type == "bar" else self.data_entity.ask_price_1 short_c = self.data_entity.high_price if self.data_type == "bar" is not None else self.data_entity.bid_price_1 # long_b = self.data_entity.open_price if self.data_type == "bar" is not None else long_c # short_b = self.data_entity.open_price if self.data_type == "bar" is not None else short_c long_cross = data.direction == Direction.LONG and 0 < long_c <= data.price short_cross = data.direction == Direction.SHORT and data.price <= short_c and short_c > 0 if long_cross: """ 判断账户资金是否足以支撑成交 """ if self.account.is_traded(data): """ 调用API生成成交单 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) self.logger.info( f"成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}" ) self.account.update_trade(trade) """ 调用strategy的on_trade """ self.pending.remove(data) data.status = Status.ALLTRADED [ api(deepcopy(data)) for api in self.strategy_mapping.values() ] [api(trade) for api in self.strategy_mapping.values()] self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume if short_cross: if self.account.is_traded(data): """ 调用API生成成交单 """ # 同时这里需要处理是否要进行 trade = self._generate_trade_data_from_order(data) self.logger.info( f"成交时间: {str(trade.time)}, 成交价格{str(trade.price)}, 成交笔数: {str(trade.volume)}," f" 成交方向: {str(trade.direction.value)},行为: {str(trade.offset.value)}" ) self.account.update_trade(trade) """ 调用strategy的on_trade """ self.pending.remove(data) data.status = Status.ALLTRADED [ api(deepcopy(data)) for api in self.strategy_mapping.values() ] [api(trade) for api in self.strategy_mapping.values()] self.traded_order_mapping[trade.order_id] = trade self.today_volume += data.volume continue else: """ 当前账户不足以支撑成交 """ continue else: raise TypeError("未支持的成交机制") def init_params(self, params): """ 回测参数设置 """ self.params.update(params) """ 更新接口参数设置 """ self.params.update(params) """ 更新账户策略参数 """ self.account.update_params(params) def __init_params(self, params): """ 初始化参数设置 """ if not isinstance(params, dict): raise AttributeError("回测参数类型错误,请检查是否为字典") [ strategy.init_params(params.get("strategy")) for strategy in self.strategy_mapping.values() ] self.init_params(params.get("looper")) @staticmethod def auth_time(time): if 15 < time.hour <= 20 or 3 <= time.hour <= 8: return False else: return True def __call__(self, *args, **kwargs): """ 回测周期 """ entity, params = args # 日期不相等时, 更新前日结算价格 if self.account.date is None: self.account.date = self.date if self.date is None: self.date = entity.datetime.date() if not self.auth_time(entity.datetime): return self.data_type = entity.type # 回测的时候自动更新策略的日期 if entity.datetime.hour > 21: dt = entity.datetime + timedelta(days=1) else: dt = entity.datetime [ setattr(x, "date", str(dt.date())) for x in self.strategy_mapping.values() ] self.__init_params(params) try: seconds = (entity.datetime - self.datetime).seconds if seconds >= 60 * 60 * 4 and (entity.datetime.hour >= 21 or ( (14 <= self.datetime.hour <= 15) and entity.datetime.date() != self.datetime.date())): self.logger.warning("结算数据: " + str(self.account.date)) self.account.settle(entity.datetime.date()) if self.data_entity is None: self.pre_close_price[ self.data_entity. local_symbol] = entity.close_price if entity.type == "bar" else entity.last_price else: self.pre_close_price[ self.data_entity.local_symbol] = self.data_entity.close_price if entity.type == "bar" \ else self.data_entity.last_price # 结算完触发初始化函数 [x.on_init(entity) for x in self.strategy_mapping.values()] except KeyError: pass except AttributeError: pass self.data_entity = entity self.change_month_record["".join( filter(str.isalpha, entity.local_symbol.split(".")[0]))] = entity # 维护一个最新的价格 self.price_mapping[self.data_entity.local_symbol] = self.data_entity.close_price if entity.type == "bar" \ else self.data_entity.last_price if self.pre_close_price.get(self.data_entity.local_symbol) is None: self.pre_close_price[ self.data_entity. local_symbol] = self.data_entity.last_price if entity.type == "tick" else self.data_entity.close_price self.datetime = entity.datetime self.match_deal() if entity.type == "tick": [api(entity) for api in self.strategy_mapping.values()] self.account.position_manager.update_tick( self.data_entity, self.pre_close_price[self.data_entity.local_symbol]) if entity.type == "bar": [api(entity) for api in self.strategy_mapping.values()] self.account.position_manager.update_bar( self.data_entity, self.pre_close_price[self.data_entity.local_symbol]) # 更新接口的日期 self.date = entity.datetime.date() # 穿过接口日期检查 self.account.via_aisle() def get_entity_from_alpha(self, alpha): return self.change_month_record.get(alpha)