Example #1
0
class LocalLooper():
    message_box = {-1: "超出下单限制", -2: "超出涨跌价格", -3: "未成交", -4: "资金不足"}

    def __init__(self, logger, risk=None):
        """ 需要构建完整的成交回报以及发单报告,在account里面需要存储大量的存储 """

        # 活跃报单数量
        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.order_buffer = dict()

        self.date = None
        # 行情
        self.price = None

    def get_trades(self):
        return list(self.traded_order_mapping.values())

    def update_strategy(self, strategy):
        setattr(strategy, "action", Action(self))
        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)
        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,
                      time=order_data.time,
                      order_id=order_data.order_id,
                      symbol=order_data.symbol,
                      exchange=order_data.exchange)
        return p

    def send_order(self, order_req):
        """ 发单的操作"""
        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 intercept_gateway(self, data):
        """ 拦截网关 同时这里应该返回相应的水平"""
        if isinstance(data, OrderRequest):
            """ 发单请求处理 """
            result = self.match_deal(self._generate_order_data_from_req(data))
            if isinstance(result, TradeData):
                """ 将成交单通过日志接口暴露出去"""
                # self.logger.info(dumps(result))
                self.logger.info(
                    f"成交时间: {str(result.time)}, 成交价格{str(result.price)}, 成交笔数: {str(result.volume)},"
                    f" 成交方向: {str(result.direction.value)},行为: {str(result.offset.value)}"
                )
                self.traded_order_mapping[result.order_id] = result

            else:
                self.logger.info(self.message_box[result])
        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)
                    return 1
            return 0

    def match_deal(self, data: OrderData) -> int or TradeData:
        """ 撮合成交
            维护一个返回状态
            -1: 超出下单限制
            -2: 超出涨跌价格
            -3: 未成交
            -4: 资金不足
            p : 成交回报

            todo: 处理冻结 ??

        """
        if self.params.get("deal_pattern") == "match":
            """ 撮合成交 """
            # todo: 是否可以模拟一定量的市场冲击响应? 以靠近更加逼真的回测效果 ????

        elif self.params.get("deal_pattern") == "price":
            """ 见价成交 """
            # 先判断价格和手数是否满足限制条件
            if data.volume > self.params.get(
                    "single_order_limit"
            ) or self.today_volume > self.params.get("single_day_limit"):
                """ 超出限制 直接返回不允许成交 """
                return -1
            if data.price < self.drop_price or data.price > self.upper_price:
                """ 超出涨跌价格 """
                return -2

            # 发单立即冻结
            self.account.update_frozen(data)

            # 进行成交判断
            long_c = self.price.low_price if self.price.low_price is not None else self.price.ask_price_1
            short_c = self.price.high_price if self.price.low_price is not None else self.price.bid_price_1
            long_b = self.price.open_price if self.price.low_price is not None else long_c
            short_b = self.price.open_price if self.price.low_price is not None else short_c
            long_cross = data.direction == Direction.LONG and data.price >= long_c > 0
            short_cross = data.direction == Direction.SHORT and data.price <= short_c and short_c > 0

            # 处理未成交的单子
            for order in self.pending:
                index = self.pending.index(order)
                long_cross = data.direction == Direction.LONG and order.price >= long_c > 0
                short_cross = data.direction == Direction.SHORT and order.price <= short_c and short_c > 0
                if not long_cross and not short_cross:
                    """ 不成交 """
                    continue
                if long_cross:
                    order.price = min(order.price, long_b)
                else:
                    order.price = max(order.price, short_b)
                trade = self._generate_trade_data_from_order(order)
                order.status = Status.ALLTRADED
                [
                    api(deepcopy(order))
                    for api in self.strategy_mapping.values()
                ]
                [api(trade) for api in self.strategy_mapping.values()]
                self.pending.remove(order)

                # 成交,移除冻结
                self.account.update_frozen(order=order, reverse=True)
                self.update_account_margin(trade)

            if not long_cross and not short_cross:
                # 未成交单, 提交到pending里面去
                self.pending.append(data)
                return -3

            if long_cross:
                data.price = min(data.price, long_b)
            else:
                data.price = max(data.price, short_b)
            """ 判断账户资金是否足以支撑成交 """
            if self.account.is_traded(data):
                """ 调用API生成成交单 """
                # 同时这里需要处理是否要进行
                p = self._generate_trade_data_from_order(data)
                self.account.update_trade(p)
                """ 调用strategy的on_trade """
                [api(p) for api in self.strategy_mapping.values()]
                self.today_volume += data.volume
                # 已经成交,同时移除冻结
                self.account.update_frozen(p, reverse=True)
                self.update_account_margin(p)

                return p
            else:
                """ 当前账户不足以支撑成交 """
                return -4
        else:
            raise TypeError("未支持的成交机制")

    def update_account_margin(self, p):
        if p.offset == Offset.OPEN:
            self.account.update_margin(p, reverse=True)
        else:
            self.account.update_margin(p)

    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"))

    def __call__(self, *args, **kwargs):
        """ 回测周期 """
        p_data, params = args
        self.price = p_data
        self.datetime = p_data.datetime
        self.__init_params(params)
        if p_data.type == "tick":
            [api(p_data) for api in self.strategy_mapping.values()]
        if p_data.type == "bar":
            [api(p_data) for api in self.strategy_mapping.values()]
        # 更新接口的日期
        self.date = p_data.datetime.date()
        # 穿过接口日期检查
        self.account.via_aisle()
Example #2
0
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)
Example #3
0
class LocalLooper():
    def __init__(self, logger, strategy=None, risk=None):
        """ 需要构建完整的成交回报以及发单报告,在account里面需要存储大量的存储 """

        self.pending = collections.deque()
        self.sessionid = random.randint(1000, 10000)
        self.frontid = random.randint(10001, 500000)

        # 日志输出器
        self.logger = logger

        self.strategy = strategy
        # 覆盖里面的action和logger属性
        # 涨跌停价格
        self.upper_price = 9999
        self.drop_price = 0

        # 风控/risk control todo:完善
        self.risk = risk
        self.params = dict(
            deal_pattern="match",
            single_order_limit=10,
            single_day_limit=100,
        )
        # 账户属性
        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.order_buffer = dict()

    def update_strategy(self, strategy):
        self.strategy = strategy
        setattr(self.strategy, "action", Action(self))
        setattr(self.strategy, "logger", self.logger)
        setattr(self.strategy, "info", self.logger.info)
        setattr(self.strategy, "debug", self.logger.debug)
        setattr(self.strategy, "error", self.logger.error)
        setattr(self.strategy, "warning", self.logger.warning)

    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)

    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,
                      trade_id=uuid.uuid1(),
                      gateway_name=order_data.gateway_name,
                      time=order_data.time,
                      order_id=order_data.order_id)
        return p

    def send_order(self, order_req):
        """ 发单的操作"""
        self.intercept_gateway(order_req)

    def cancel(self, cancel_req):
        """ 撤单机制 """
        self.intercept_gateway(cancel_req)

    def intercept_gateway(self, data):
        """ 拦截网关 同时这里应该返回相应的水平"""
        if isinstance(data, OrderRequest):
            """ 发单请求处理 """
            result = self.match_deal(self._generate_order_data_from_req(data))
            if result:
                """ 将成交单通过日志接口暴露出去"""
                self.logger.info(dumps(result))
        if isinstance(data, CancelRequest):
            """ 撤单请求处理 """
            # todo: 处理日志接口

    def match_deal(self, data: OrderData):
        """ 撮合成交 """
        if self.params.get("deal_pattern") == "match":
            """ 撮合成交 """
            # todo: 是否可以模拟一定量的市场冲击响应? 以靠近更加逼真的回测效果 ????

        elif self.params.get("deal_pattern") == "price":
            """ 见价成交 """
            # 先判断价格和手数是否满足限制条件
            if data.volume > self.params.get(
                    "single_order_limit"
            ) or self.today_volume > self.params.get("single_day_limit"):
                """ 超出限制 直接返回不允许成交 """
                return None
            if data.price < self.drop_price or data.price > self.upper_price:
                """ 超出涨跌价格 """
                return None
            """ 判断账户资金是否足以支撑成交 """
            if self.account.is_traded(data):
                """ 调用API生成成交单 """
                p = self._generate_trade_data_from_order(data)
                self.account.update_trade(p)
                """ 调用strategy的on_trade """
                self.strategy.on_trade(p)
                self.today_volume += data.volume
                return p
            else:
                """ 当前账户不足以支撑成交 """
                self.logger.error("资金不足啦!")
                return None
        else:
            raise TypeError("未支持的成交机制")

    def init_params(self, params):
        """ 回测参数设置 """
        # todo: 本地设置回测参数

        self.params.update(params)

    def __init_params(self, params):
        """ 初始化参数设置  """
        if not isinstance(params, dict):
            raise AttributeError("回测参数类型错误,请检查是否为字典")
        self.strategy.init_params(params.get("strategy"))
        self.init_params(params.get("looper"))

    def __call__(self, *args, **kwargs):
        """ 回测周期 """
        p_data: Bumblebee = args[0]
        params = args[1]
        self.__init_params(params)
        if p_data.type == "tick":
            self.strategy.on_tick(tick=p_data.to_tick())

        if p_data.type == "bar":
            self.strategy.on_bar(tick=p_data.to_bar())
Example #4
0
class LocalLooper():
    message_box = {
        -1: "超出下单限制",
        -2: "超出涨跌价格",
        -3: "未成交",
        -4: "资金不足"
    }

    def __init__(self, logger, strategy=None, risk=None):
        """ 需要构建完整的成交回报以及发单报告,在account里面需要存储大量的存储 """

        # 活跃报单数量
        self.pending = collections.deque()

        self.sessionid = random.randint(1000, 10000)
        self.frontid = random.randint(10001, 500000)

        # 日志输出器
        self.logger = logger

        self.strategy = strategy
        # 覆盖里面的action和logger属性
        # 涨跌停价格
        self.upper_price = 9999
        self.drop_price = 0

        # 风控/risk control todo:完善
        self.risk = risk
        self.params = dict(
            deal_pattern="match",
            single_order_limit=10,
            single_day_limit=100,
        )
        # 账户属性
        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.order_buffer = dict()

        self.date = None
        # 行情
        self.price = None

    def update_strategy(self, strategy):
        self.strategy = strategy
        setattr(self.strategy, "action", Action(self))
        setattr(self.strategy, "logger", self.logger)
        setattr(self.strategy, "info", self.logger.info)
        setattr(self.strategy, "debug", self.logger.debug)
        setattr(self.strategy, "error", self.logger.error)
        setattr(self.strategy, "warning", self.logger.warning)

    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)

    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,
                      trade_id=uuid.uuid1(),
                      gateway_name=order_data.gateway_name, time=order_data.time,
                      order_id=order_data.order_id)
        return p

    def send_order(self, order_req):
        """ 发单的操作"""
        self.intercept_gateway(order_req)

    def cancel(self, cancel_req):
        """ 撤单机制 """
        self.intercept_gateway(cancel_req)

    def intercept_gateway(self, data):
        """ 拦截网关 同时这里应该返回相应的水平"""
        if isinstance(data, OrderRequest):
            """ 发单请求处理 """
            result = self.match_deal(self._generate_order_data_from_req(data))
            if result:
                """ 将成交单通过日志接口暴露出去"""
                self.logger.info(dumps(result))
            else:
                self.logger.info(self.message_box[result])
        if isinstance(data, CancelRequest):
            """ 撤单请求处理 
            """
            for order in self.pending:
                if data.order_id == order.order_id:
                    order = deepcopy(order)
                    self.strategy.on_order(order)
                    self.pending.remove(order)
                    return 1
            return 0

    def match_deal(self, data: OrderData) -> int or TradeData:
        """ 撮合成交
            维护一个返回状态
            -1: 超出下单限制
            -2: 超出涨跌价格
            -3: 未成交
            -4: 资金不足
            p : 成交回报

        """
        if self.params.get("deal_pattern") == "match":
            """ 撮合成交 """
            # todo: 是否可以模拟一定量的市场冲击响应? 以靠近更加逼真的回测效果 ????

        elif self.params.get("deal_pattern") == "price":
            """ 见价成交 """
            # 先判断价格和手数是否满足限制条件
            if data.volume > self.params.get("single_order_limit") or self.today_volume > self.params.get(
                    "single_day_limit"):
                """ 超出限制 直接返回不允许成交 """
                return -1
            if data.price < self.drop_price or data.price > self.upper_price:
                """ 超出涨跌价格 """
                return -2

            long_c = self.price.low_price if self.price.low_price is not None else self.price.ask_price_1
            short_c = self.price.high_price if self.price.low_price is not None else self.price.bid_price_1
            long_b = self.price.open_price if self.price.low_price is not None else long_c
            short_b = self.price.open_price if self.price.low_price is not None else short_c
            long_cross = data.direction == Direction.LONG and data.price >= long_c > 0
            short_cross = data.direction == Direction.SHORT and data.price <= short_c and short_c > 0

            # 处理未成交的单子
            for order in self.pending:
                long_cross = data.direction == Direction.LONG and order.price >= long_c > 0
                short_cross = data.direction == Direction.SHORT and order.price <= short_c and short_c > 0
                if not long_cross and not short_cross:
                    """ 不成交 """
                    continue
                if long_cross:
                    order.price = min(order.price, long_b)
                else:
                    order.price = max(order.price, short_b)
                trade = self._generate_trade_data_from_order(order)
                order: OrderData.status = Status.ALLTRADED
                self.strategy.on_order(deepcopy(order))
                self.strategy.on_trade(trade)

                self.pending.remove(order)

            if not long_cross and not short_cross:
                # 未成交单, 提交到pending里面去
                self.pending.append(data)
                return -3
            """ 判断账户资金是否足以支撑成交 """
            if self.account.is_traded(data):
                """ 调用API生成成交单 """
                # 同时这里需要处理是否要进行
                p = self._generate_trade_data_from_order(data)
                self.account.update_trade(p)
                """ 调用strategy的on_trade """
                self.strategy.on_trade(p)
                self.today_volume += data.volume
                return p
            else:
                """ 当前账户不足以支撑成交 """
                return -4
        else:
            raise TypeError("未支持的成交机制")

    def init_params(self, params):
        """ 回测参数设置 """
        # todo: 本地设置回测参数
        self.params.update(params)

    def __init_params(self, params):
        """ 初始化参数设置  """
        if not isinstance(params, dict):
            raise AttributeError("回测参数类型错误,请检查是否为字典")
        self.strategy.init_params(params.get("strategy"))
        self.init_params(params.get("looper"))

    def __call__(self, *args, **kwargs):
        """ 回测周期 """
        p_data, params = args
        self.price = p_data.price
        self.__init_params(params)
        if p_data.type == "tick":
            self.strategy.on_tick(tick=p_data.to_tick())

        if p_data.type == "bar":
            self.strategy.on_bar(tick=p_data.to_bar())
        # 更新接口的日期
        self.date = p_data.datetime.date
Example #5
0
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)