コード例 #1
0
ファイル: macd.py プロジェクト: dongxiao999999/futuquant
    def context_setting(self):
        """
        API trading and quote context setting
        :returns: trade context, quote context
        """
        if self.unlock_password == "":
            raise Exception("请先配置交易解锁密码! password: {}".format(self.unlock_password))

        quote_ctx = OpenQuoteContext(host=self.api_svr_ip, port=self.api_svr_port)

        if 'HK.' in self.stock:
            trade_ctx = OpenHKTradeContext(host=self.api_svr_ip, port=self.api_svr_port)
            if self.trade_env == 0:
                ret_code, ret_data = trade_ctx.unlock_trade(self.unlock_password)
                if ret_code == 0:
                    print('解锁交易成功!')
                else:
                    print("请求交易解锁失败, 请确认解锁密码! password: {}".format(self.unlock_password))
        elif 'US.' in self.stock:
            if self.trade_env != 0:
                raise Exception("美股交易接口不支持仿真环境 trade_env: {}".format(self.trade_env))
            trade_ctx = OpenUSTradeContext(host=self.api_svr_ip, port=self.api_svr_port)
        else:
            raise Exception("stock输入错误 stock: {}".format(self.stock))

        return quote_ctx, trade_ctx
コード例 #2
0
ファイル: macd.py プロジェクト: yuchou/futuquant
    def context_setting(self):
        """
        API trading and quote context setting
        :returns: trade context, quote context
        """
        if self.unlock_password == "":
            raise Exception("请先配置交易解锁密码! password: {}".format(self.unlock_password))

        quote_ctx = OpenQuoteContext(host=self.api_svr_ip, port=self.api_svr_port)

        if 'HK.' in self.stock:
            trade_ctx = OpenHKTradeContext(host=self.api_svr_ip, port=self.api_svr_port)
            if self.trade_env == 0:
                ret_code, ret_data = trade_ctx.unlock_trade(self.unlock_password)
                if ret_code == 0:
                    print('解锁交易成功!')
                else:
                    print("请求交易解锁失败, 请确认解锁密码! password: {}".format(self.unlock_password))
        elif 'US.' in self.stock:
            if self.trade_env != 0:
                raise Exception("美股交易接口不支持仿真环境 trade_env: {}".format(self.trade_env))
            trade_ctx = OpenUSTradeContext(host=self.api_svr_ip, port=self.api_svr_port)
        else:
            raise Exception("stock输入错误 stock: {}".format(self.stock))

        return quote_ctx, trade_ctx
コード例 #3
0
 def __init__(self, host, port, trd_env, market, unlock_path=ct.FUTU_PATH):
     if market != ct.CN_MARKET_SYMBOL and market != ct.US_MARKET_SYMBOL and market != ct.HK_MARKET_SYMBOL:
         raise Exception("not supported market:%s" % market)
     if ct.CN_MARKET_SYMBOL == market:
         self.trd_ctx = OpenCNTradeContext(host, port)
     elif ct.US_MARKET_SYMBOL == market:
         self.trd_ctx = OpenUSTradeContext(host, port)
     else:
         self.trd_ctx = OpenHKTradeContext(host, port)
     self.trd_env = trd_env
     self.acc_id = self.get_acc_id()
     self.unlock_pwd = self.get_unlock_pwd(fpath=unlock_path)
     self._status = True
     self._lock = threading.Lock()
コード例 #4
0
    def connectTrade(self):
        """连接交易功能"""
        # 连接交易接口
        if self.market == 'US':
            self.tradeCtx = OpenUSTradeContext(self.host, self.port)
        else:
            self.tradeCtx = OpenHKTradeContext(self.host, self.port)

        # 继承实现处理器类
        class OrderHandler(TradeOrderHandlerBase):
            """委托处理器"""
            gateway = self  # 缓存Gateway对象

            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(OrderHandler,
                                          self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processOrder(content)
                return RET_OK, content

        class DealHandler(TradeDealHandlerBase):
            """订单簿处理器"""
            gateway = self

            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(DealHandler,
                                          self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processDeal(content)
                return RET_OK, content

        # 只有港股实盘交易才需要解锁
        code, data = self.tradeCtx.unlock_trade(self.password)
        if code == RET_OK:
            self.writeLog(u'交易接口解锁成功')
        else:
            self.writeLog(u'交易接口解锁失败,原因:%s' % data)

        # 设置回调处理对象
        self.tradeCtx.set_handler(OrderHandler())
        self.tradeCtx.set_handler(DealHandler())

        # 启动交易接口
        self.tradeCtx.start()

        self.writeLog(u'交易接口连接成功')
コード例 #5
0
ファイル: futuGateway.py プロジェクト: aaront2000/vnpy
    def connectTrade(self):
        """连接交易功能"""
        # 连接交易接口
        if self.market == 'US':
            self.tradeCtx = OpenUSTradeContext(self.host, self.port)
        else:
            self.tradeCtx = OpenHKTradeContext(self.host, self.port)
            
        # 继承实现处理器类
        class OrderHandler(TradeOrderHandlerBase):
            """委托处理器"""
            gateway = self  # 缓存Gateway对象
            
            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(OrderHandler, self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processOrder(content)
                return RET_OK, content            
            
        class DealHandler(TradeDealHandlerBase):
            """订单簿处理器"""
            gateway = self
            
            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(DealHandler, self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processDeal(content)
                return RET_OK, content  

        # 只有港股实盘交易才需要解锁
        code, data = self.tradeCtx.unlock_trade(self.password)
        if code == RET_OK:
            self.writeLog(u'交易接口解锁成功')
        else:
            self.writeLog(u'交易接口解锁失败,原因:%s' %data)
        
        # 设置回调处理对象
        self.tradeCtx.set_handler(OrderHandler())
        self.tradeCtx.set_handler(DealHandler())        
        
        # 启动交易接口
        self.tradeCtx.start()
        
        self.writeLog(u'交易接口连接成功')
コード例 #6
0
    def __init__(self, env, mod_config):
        self._env = env
        self._mod_config = mod_config
        self._portfolio = None
        self._open_order = []

        self._env.event_bus.add_listener(EVENT.PRE_BEFORE_TRADING, self._pre_before_trading)
        self._env.event_bus.add_listener(EVENT.PRE_AFTER_TRADING, self._pre_after_trading)

        # futu api创建及参数
        self._trade_context = OpenHKTradeContext(self._mod_config.api_svr.ip, self._mod_config.api_svr.port)
        self._trade_envtype = 1  # futu交易 envtype : 0 = 实盘  1 = 仿真
        if IsRuntype_RealTrade():
            self._trade_envtype = 0

        thread_order_check = Thread(target=self._thread_order_check)
        thread_order_check.setDaemon(True)
        thread_order_check.start()
コード例 #7
0
class FutuTrader:
    def __init__(self, host, port, trd_env, market, unlock_path=ct.FUTU_PATH):
        if market != ct.CN_MARKET_SYMBOL and market != ct.US_MARKET_SYMBOL and market != ct.HK_MARKET_SYMBOL:
            raise Exception("not supported market:%s" % market)
        if ct.CN_MARKET_SYMBOL == market:
            self.trd_ctx = OpenCNTradeContext(host, port)
        elif ct.US_MARKET_SYMBOL == market:
            self.trd_ctx = OpenUSTradeContext(host, port)
        else:
            self.trd_ctx = OpenHKTradeContext(host, port)
        self.trd_env = trd_env
        self.acc_id = self.get_acc_id()
        self.unlock_pwd = self.get_unlock_pwd(fpath=unlock_path)
        self._status = True
        self._lock = threading.Lock()

    def __del__(self):
        if self.trd_ctx is not None:
            self.trd_ctx.close()

    def start(self):
        if self.trd_ctx is not None:
            self.trd_ctx.start()

    def get_acc_id(self):
        ret, data = self.trd_ctx.get_acc_list()
        if ret != 0: raise Exception("get accid failed")
        return data.loc[data.trd_env == self.trd_env, 'acc_id'].values[0]

    def get_unlock_pwd(self, fpath=ct.FUTU_PATH):
        with open(fpath) as f:
            infos = json.load(f)
        return infos['unlock_pwd']

    def get_cash(self):
        ret, data = self.trd_ctx.accinfo_query(trd_env=self.trd_env)
        if ret != 0: raise Exception("get cash failed")
        return data['cash'].values[0]

    def set_handler(self, mclass):
        with self._lock:
            self.trd_ctx.set_handler(mclass)

    def get_shares(self):
        mshares = dict()
        ret, data = self.trd_ctx.position_list_query(trd_env=self.trd_env)
        if ret != 0: raise Exception("get shares failed")
        for index, code in data.code.iteritems():
            mshares[code] = data.loc[index, 'qty']
        return mshares

    def get_order(self, id_="", filter_list=list()):
        orders = list()
        ret, data = self.trd_ctx.order_list_query(
            trd_env=TrdEnv.SIMULATE,
            order_id=id_,
            status_filter_list=filter_list)
        if ret != 0:
            raise Exception(
                "get opend order failed. order_id:%s, filter_list:%s" %
                (id_, filter_list))
        if data.empty: return orders
        data = data[[
            'order_id', 'order_type', 'order_status', 'code', 'trd_side',
            'qty', 'price', 'create_time', 'dealt_qty', 'dealt_avg_price',
            'updated_time'
        ]]
        for mdict in data.to_dict("records"):
            orders.append(MOrder(mdict))
        return orders if id_ != "" else orders[0]

    def trade(self, order):
        code = order.getInstrument()
        price = order.getLimitPrice()
        quantity = order.getQuantity()
        type_ = OrderType.NORMAL
        direction = TrdSide.BUY if order.isBuy() else TrdSide.SELL
        ret, data = self.trd_ctx.place_order(price,
                                             quantity,
                                             code,
                                             trd_side=direction,
                                             order_type=type_,
                                             adjust_limit=0,
                                             trd_env=self.trd_env,
                                             acc_id=self.acc_id)
        if ret != 0:
            logger.error("trade failed, ret:%s, data:%s" % (ret, data))
        #logger.info("trade %s success, ret:%s, data:%s" % (code, ret, data))
        return ret, data

    def buy(self, code, price, quantity):
        if self.trd_env == TrdEnv.REAL:
            self.trd_ctx.unlock_trade(password_md5=self.unlock_pwd)
        ret, data = self.trd_ctx.place_order(code=code,
                                             price=price,
                                             qty=quantity,
                                             trd_side=TrdSide.BUY,
                                             order_type=OrderType.NORMAL,
                                             trd_env=self.trd_env)
        return ret, data

    def modify(self, order, operation):
        id_ = order.getId()
        price = order.getLimitPrice()
        quantity = order.getQuantity()
        type_ = order.getOrderType()
        side = order.getOrderDirection()
        ret, data = self.trd_ctx.modify_order(operation,
                                              id_,
                                              quantity,
                                              price,
                                              adjust_limit=0,
                                              trd_env=self.trd_env,
                                              acc_id=self.acc_id)
        return ret, data

    def close(self):
        with self._lock:
            self.trd_ctx.close()
            self._status = False

    def status(self):
        with self._lock:
            return self._status
コード例 #8
0
class FUTUBrokerHK(AbstractBroker):
    """
    FUTUBrokerHK 对象用于对接futu港股的仿真和真实交易
    设计思路:
    1. 帐户的初始资金需要在rqalpha框架下的config中设置 config.base.stock_starting_cash
    不与futu的帐户信息同步, 一方面是不影响长期自动运行时计算的收益率等指标,另一方面也为了控制策略脚本对futu实际帐户资金的占用.
    2. 初始化时会同步一次futu帐户的持仓数据, 后续状态完全由rqalpha框架内部维护状态, 故策略中记录的持仓有可能与用户实际futu帐户不一致
    3. 下单 ,撤单调后,脚本中会定时检查该订单在futu环境中的状态, 产生对应的event事件,可能存在延时。
    """
    def __init__(self, env, mod_config):
        self._env = env
        self._mod_config = mod_config
        self._portfolio = None
        self._open_order = []

        self._env.event_bus.add_listener(EVENT.PRE_BEFORE_TRADING,
                                         self._pre_before_trading)
        self._env.event_bus.add_listener(EVENT.PRE_AFTER_TRADING,
                                         self._pre_after_trading)

        # futu api创建及参数
        self._trade_context = OpenHKTradeContext(self._mod_config.api_svr.ip,
                                                 self._mod_config.api_svr.port)
        self._trade_envtype = 1  # futu交易 envtype : 0 = 实盘  1 = 仿真
        if IsRuntype_RealTrade():
            self._trade_envtype = 0

        thread_order_check = Thread(target=self._thread_order_check)
        thread_order_check.setDaemon(True)
        thread_order_check.start()

    def get_portfolio(self):
        """
        获取投资组合。系统初始化时,会调用此接口,获取包含账户信息、净值、份额等内容的投资组合
        :return: Portfolio
        """
        if self._portfolio is not None:
            return self._portfolio
        self._portfolio = self._init_portfolio()

        if not self._portfolio._accounts:
            raise RuntimeError("accout config error")

        return self._portfolio

    def submit_order(self, order):
        """
        提交订单。在当前版本,RQAlpha 会生成 :class:`~Order` 对象,再通过此接口提交到 Broker。
        TBD: 由 Broker 对象生成 Order 并返回?
        """

        print("FUTUBrokerHK.submit_order:{}".format(order))
        if order.type == ORDER_TYPE.MARKET:
            raise RuntimeError("submit_order not support ORDER_TYPE.MARKET")

        account = self._get_account(order.order_book_id)
        self._env.event_bus.publish_event(
            Event(EVENT.ORDER_PENDING_NEW, account=account, order=order))
        order.active()

        # 发起futu api接口请求
        futu_order_side = 0 if order.side == SIDE.BUY else 1
        futu_order_type = 0  # 港股增强限价单
        ret_code, ret_data = self._trade_context.place_order(
            order.price, order.quantity, order.order_book_id, futu_order_side,
            futu_order_type, self._trade_envtype)

        # 事件通知
        if ret_code != 0:
            order.mark_rejected("futu api req err:{} ".format(ret_code))
            self._env.event_bus.publish_event(
                Event(EVENT.ORDER_CREATION_REJECT,
                      account=account,
                      order=order))
        else:
            futu_order_id = ret_data.loc[0, 'orderid']
            self._open_order.append((futu_order_id, order))
            self._env.event_bus.publish_event(
                Event(EVENT.ORDER_CREATION_PASS, account=account, order=order))
            sleep(0.1)
            self._check_open_orders(futu_order_id)

    def cancel_order(self, order):
        """
        撤单。
        :param order: 订单
        :type order: :class:`~Order`
        """
        account = self._get_account(order.order_book_id)
        futu_order_id = self._get_futu_order_id(order)

        if futu_order_id is None:
            return

        # 立即检查一次订单状态
        self._check_open_orders(futu_order_id)
        if order.is_final():
            return

        self._env.event_bus.publish_event(
            Event(EVENT.ORDER_PENDING_CANCEL, account=account, order=order))
        ret_code, ret_data = self._trade_context.set_order_status(
            0, futu_order_id, self._env)  # 0 = 撤单
        if ret_code != 0:
            self._env.event_bus.publish_event(
                Event(EVENT.ORDER_CANCELLATION_REJECT,
                      account=account,
                      order=order))
        else:
            sleep(0.1)
            self._check_open_orders(futu_order_id)  # 提交请求后,立即再检查一次状态

    def get_open_orders(self, order_book_id=None):
        """
        [Required]
        获得当前未完成的订单。
        :return: list[:class:`~Order`]
        """
        if order_book_id is None:
            return [order for __, order in self._open_orders]
        else:
            return [
                order for __, order in self._open_orders
                if order.order_book_id == order_book_id
            ]

    def _pre_before_trading(self, event):
        print("broker before_trading")

    def _pre_after_trading(self, event):
        # 收盘时清掉未完成的订单

        for __, order in self._open_order:
            order.mark_rejected(
                _(u"Order Rejected: {order_book_id} can not match. Market close."
                  ).format(order_book_id=order.order_book_id))
            account = self._env.get_account(order.order_book_id)
            self._env.event_bus.publish_event(
                Event(EVENT.ORDER_UNSOLICITED_UPDATE,
                      account=account,
                      order=order))
        self._open_orders = []
        print("broker after_trading")

    def _check_open_orders(self, futu_order_id=None):
        if len(self._open_order) == 0:
            return
        ret_code, pd_data = self._trade_context.order_list_query(
            '', self._trade_envtype)
        if ret_code != 0:
            return
        ft_orders = []
        if futu_order_id is not None:
            ft_orders.append(futu_order_id)
        else:
            for (fid, __) in self._open_order:
                ft_orders.append(fid)

        for fid in ft_orders:
            pd_find = pd_data[pd_data.orderid == fid]
            if len(pd_find) != 1:
                continue
            order = self._get_order_by_futu_id(fid)
            account = self._get_account(order.order_book_id)
            if order is None:
                continue

            ct_amount = 0  # 期货用的,期货分平当天的仓位和以前的仓位
            price = order.avg_price  # 分多笔成交下的平均值
            trade = Trade.__from_create__(
                order_id=order.order_id,
                price=price,
                amount=0,
                side=order.side,
                position_effect=order.position_effect,
                order_book_id=order.order_book_id,
                frozen_price=order.frozen_price,
                close_today_amount=ct_amount,
                commission=0.,
                tax=0.,
                trade_id=None)
            trade._commission = 0
            trade._tax = 0

            row = pd_find.iloc[0]
            ft_status = int(row['status'])
            if ft_status == 2 or ft_status == 3:  # 部分成交 | 全部成交
                qty_deal_last = order.quantity - order.unfilled_quantity
                qty_deal_new = int(row['dealt_qty'])
                if qty_deal_last == qty_deal_new:  # 记录的成交数量与上次相同
                    continue
                trade._amount = qty_deal_new - qty_deal_last
                order.fill(trade)
                self._env.event_bus.publish_event(
                    Event(EVENT.TRADE,
                          account=account,
                          trade=trade,
                          order=order))
                if ft_status == 3:
                    self._remove_open_order_by_futu_id(fid)

            elif ft_status == 5:  # 下单失败
                self._env.event_bus.publish_event(
                    Event(EVENT.ORDER_CREATION_REJECT,
                          account=account,
                          order=order))
                self._remove_open_order_by_futu_id(fid)

            elif ft_status == 6:  # 6=已撤单
                order.mark_cancelled(
                    _(u"{order_id} order has been cancelled by user.").format(
                        order_id=order.order_id))
                self._env.event_bus.publish_event(
                    Event(EVENT.ORDER_CANCELLATION_PASS,
                          account=account,
                          order=order))
                self._remove_open_order_by_futu_id(fid)

            elif ft_status == 4 or ft_status == 7:  # 4=已失效  	7=已删除
                reason = _(
                    u"Order Cancelled:  code = {order_book_id} ft_status = {ft_status} "
                ).format(order_book_id=order.order_book_id,
                         ft_status=ft_status)
                order.mark_rejected(reason)
                self._env.event_bus.publish_event(
                    Event(EVENT.ORDER_CREATION_REJECT,
                          account=account,
                          order=order))
                self._remove_open_order_by_futu_id(fid)

            else:
                pass  # 8 = 等待开盘 21= 本地已发送 22=本地已发送,服务器返回下单失败、没产生订单 23=本地已发送,等待服务器返回超时

    def _get_futu_positions(self, env):
        StockPosition = env.get_position_model(
            FUTU_ACCOUNT_TYPE.FUTU_STOCK.name)
        positions = Positions(StockPosition)
        ret, pd_data = self._trade_context.position_list_query(
            self._trade_envtype)
        if ret != 0:
            return None
        for i in range(len(pd_data)):
            row = pd_data.iloc[i]
            code_str = str(row['code'])
            pos_state = {}
            pos_state['order_book_id'] = code_str
            pos_state['quantity'] = int(row['qty'])
            pos_state['avg_price'] = float(row['cost_price'])
            pos_state['non_closable'] = 0
            pos_state['frozen'] = int(row['qty']) - int(row['can_sell_qty'])
            pos_state['transaction_cost'] = 0
            item = positions.get_or_create(code_str)
            item.set_state(pos_state)
        return positions

    def _init_portfolio(self):
        accounts = {}
        config = self._env.config
        start_date = config.base.start_date
        total_cash = 0
        for account_type, stock_starting_cash in six.iteritems(
                config.base.accounts):
            if account_type == FUTU_ACCOUNT_TYPE.FUTU_STOCK.name:
                # stock_starting_cash = config.base.accounts
                if stock_starting_cash == 0:
                    raise RuntimeError(
                        _(u"stock starting cash can not be 0, using `--stock-starting-cash 1000`"
                          ))
                all_positons = self._get_futu_positions(self._env)
                if all_positons is None:
                    raise RuntimeError("_init_portfolio fail")
                StockAccount = self._env.get_account_model(
                    FUTU_ACCOUNT_TYPE.FUTU_STOCK.name)
                accounts[FUTU_ACCOUNT_TYPE.FUTU_STOCK.name] = StockAccount(
                    stock_starting_cash, all_positons)
                total_cash += stock_starting_cash
            else:
                raise NotImplementedError

        return Portfolio(start_date, 1, total_cash, accounts)

    def _get_account(self, order_book_id):
        # account = self._env.get_account(order_book_id)
        # for debug
        account = self._env.portfolio.accounts[
            FUTU_ACCOUNT_TYPE.FUTU_STOCK.name]
        return account

    def _get_futu_order_id(self, order):
        for fid, order_item in self._open_order:
            if order_item is order:
                return fid
        return None

    def _get_order_by_futu_id(self, futu_order_id):
        for fid, order_item in self._open_order:
            if futu_order_id == fid:
                return order_item
        return None

    def _remove_open_order_by_futu_id(self, futu_order_id):
        order = self._get_order_by_futu_id(futu_order_id)
        if order is not None:
            self._open_order.remove((futu_order_id, order))

    def _thread_order_check(self):
        while True:
            if len(self._open_order) == 0:
                print("broker:_thread_order_check None")
                sleep(5)
            else:
                self._check_open_orders()
                sleep(1)
コード例 #9
0
class FutuGateway(VtGateway):
    """富途接口"""

    #----------------------------------------------------------------------
    def __init__(self, eventEngine, gatewayName='FUTU'):
        """Constructor"""
        super(FutuGateway, self).__init__(eventEngine, gatewayName)

        self.quoteCtx = None
        self.tradeCtx = None

        self.host = ''
        self.ip = 0
        self.market = ''
        self.password = ''
        self.env = TrdEnv.SIMULATE  # 默认仿真交易

        self.fileName = self.gatewayName + '_connect.json'
        self.filePath = getJsonPath(self.fileName, __file__)

        self.tickDict = {}
        self.tradeSet = set()  # 保存成交编号的集合,防止重复推送

        self.qryEnabled = True
        self.qryThread = Thread(target=self.qryData)

    #----------------------------------------------------------------------
    def writeLog(self, content):
        """输出日志"""
        log = VtLogData()
        log.gatewayName = self.gatewayName
        log.logContent = content
        self.onLog(log)

    #----------------------------------------------------------------------
    def writeError(self, code, msg):
        """输出错误"""
        error = VtErrorData()
        error.gatewayName = self.gatewayName
        error.errorID = code
        error.errorMsg = msg
        self.onError(error)

    #----------------------------------------------------------------------
    def connect(self):
        """连接"""
        # 载入配置
        try:
            f = open(self.filePath)
            setting = json.load(f)
            self.host = setting['host']
            self.port = setting['port']
            self.market = setting['market']
            self.password = setting['password']
            self.env = setting['env']
        except:
            self.writeLog(u'载入配置文件出错')
            return

        self.connectQuote()
        self.connectTrade()

        self.qryThread.start()

    #----------------------------------------------------------------------
    def qryData(self):
        """初始化时查询数据"""
        # 等待2秒保证行情和交易接口启动完成
        sleep(2.0)

        # 查询合约、成交、委托、持仓、账户
        self.qryContract()
        self.qryTrade()
        self.qryOrder()
        self.qryPosition()
        self.qryAccount()

        # 启动循环查询
        self.initQuery()

    #----------------------------------------------------------------------
    def connectQuote(self):
        """连接行情功能"""
        self.quoteCtx = OpenQuoteContext(self.host, self.port)

        # 继承实现处理器类
        class QuoteHandler(StockQuoteHandlerBase):
            """报价处理器"""
            gateway = self  # 缓存Gateway对象

            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(QuoteHandler,
                                          self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processQuote(content)
                return RET_OK, content

        class OrderBookHandler(OrderBookHandlerBase):
            """订单簿处理器"""
            gateway = self

            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(OrderBookHandler,
                                          self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processOrderBook(content)
                return RET_OK, content

        # 设置回调处理对象
        self.quoteCtx.set_handler(QuoteHandler())
        self.quoteCtx.set_handler(OrderBookHandler())

        # 启动行情
        self.quoteCtx.start()

        self.writeLog(u'行情接口连接成功')

    #----------------------------------------------------------------------
    def connectTrade(self):
        """连接交易功能"""
        # 连接交易接口
        if self.market == 'US':
            self.tradeCtx = OpenUSTradeContext(self.host, self.port)
        else:
            self.tradeCtx = OpenHKTradeContext(self.host, self.port)

        # 继承实现处理器类
        class OrderHandler(TradeOrderHandlerBase):
            """委托处理器"""
            gateway = self  # 缓存Gateway对象

            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(OrderHandler,
                                          self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processOrder(content)
                return RET_OK, content

        class DealHandler(TradeDealHandlerBase):
            """订单簿处理器"""
            gateway = self

            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(DealHandler,
                                          self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processDeal(content)
                return RET_OK, content

        # 只有港股实盘交易才需要解锁
        code, data = self.tradeCtx.unlock_trade(self.password)
        if code == RET_OK:
            self.writeLog(u'交易接口解锁成功')
        else:
            self.writeLog(u'交易接口解锁失败,原因:%s' % data)

        # 设置回调处理对象
        self.tradeCtx.set_handler(OrderHandler())
        self.tradeCtx.set_handler(DealHandler())

        # 启动交易接口
        self.tradeCtx.start()

        self.writeLog(u'交易接口连接成功')

    #----------------------------------------------------------------------
    def subscribe(self, subscribeReq):
        """订阅行情"""
        for data_type in ['QUOTE', 'ORDER_BOOK']:
            code, data = self.quoteCtx.subscribe(subscribeReq.symbol,
                                                 data_type, True)

            if code:
                self.writeError(code, u'订阅行情失败:%s' % data)

    #----------------------------------------------------------------------
    def sendOrder(self, orderReq):
        """发单"""
        side = directionMap[orderReq.direction]
        priceType = OrderType.NORMAL  # 只支持限价单

        # 设置价格调整模式为向内调整(即买入调整后价格比原始价格低)
        if orderReq.direction == DIRECTION_LONG:
            adjustLimit = 0.05
        else:
            adjustLimit = -0.05

        code, data = self.tradeCtx.place_order(orderReq.price,
                                               orderReq.volume,
                                               orderReq.symbol,
                                               side,
                                               priceType,
                                               trd_env=self.env,
                                               adjust_limit=adjustLimit)

        if code:
            self.writeError(code, u'委托失败:%s' % data)
            return ''

        for ix, row in data.iterrows():
            orderID = str(row['order_id'])

        vtOrderID = '.'.join([self.gatewayName, orderID])

        return vtOrderID

    #----------------------------------------------------------------------
    def cancelOrder(self, cancelOrderReq):
        """撤单"""
        code, data = self.tradeCtx.modify_order(ModifyOrderOp.CANCEL,
                                                cancelOrderReq.orderID,
                                                0,
                                                0,
                                                trd_env=self.env)

        if code:
            self.writeError(code, u'撤单失败:%s' % data)
            return

    #----------------------------------------------------------------------
    def qryContract(self):
        """查询合约"""
        for vtProductClass, product in productMap.items():
            code, data = self.quoteCtx.get_stock_basicinfo(
                self.market, product)

            if code:
                self.writeError(code, u'查询合约信息失败:%s' % data)
                return

            for ix, row in data.iterrows():
                contract = VtContractData()
                contract.gatewayName = self.gatewayName

                contract.symbol = row['code']
                contract.vtSymbol = contract.symbol
                contract.name = row['name']
                contract.productClass = vtProductClass
                contract.size = int(row['lot_size'])
                contract.priceTick = 0.001

                self.onContract(contract)

        self.writeLog(u'合约信息查询成功')

    #----------------------------------------------------------------------
    def qryAccount(self):
        """查询账户资金"""
        code, data = self.tradeCtx.accinfo_query(trd_env=self.env, acc_id=0)

        if code:
            self.writeError(code, u'查询账户资金失败:%s' % data)
            return

        for ix, row in data.iterrows():
            account = VtAccountData()
            account.gatewayName = self.gatewayName

            account.accountID = '%s_%s' % (self.gatewayName, self.market)
            account.vtAccountID = '.'.join(
                [self.gatewayName, account.accountID])
            account.balance = float(row['total_assets'])
            account.available = float(row['avl_withdrawal_cash'])

            self.onAccount(account)

    #----------------------------------------------------------------------
    def qryPosition(self):
        """查询持仓"""
        code, data = self.tradeCtx.position_list_query(trd_env=self.env,
                                                       acc_id=0)

        if code:
            self.writeError(code, u'查询持仓失败:%s' % data)
            return

        for ix, row in data.iterrows():
            pos = VtPositionData()
            pos.gatewayName = self.gatewayName

            pos.symbol = row['code']
            pos.vtSymbol = pos.symbol

            pos.direction = DIRECTION_LONG
            pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction])

            pos.position = float(row['qty'])
            pos.price = float(row['cost_price'])
            pos.positionProfit = float(row['pl_val'])
            pos.frozen = float(row['qty']) - float(row['can_sell_qty'])

            if pos.price < 0: pos.price = 0
            if pos.positionProfit > 100000000: pos.positionProfit = 0

            self.onPosition(pos)

    #----------------------------------------------------------------------
    def qryOrder(self):
        """查询委托"""
        code, data = self.tradeCtx.order_list_query("", trd_env=self.env)

        if code:
            self.writeError(code, u'查询委托失败:%s' % data)
            return

        self.processOrder(data)
        self.writeLog(u'委托查询成功')

    #----------------------------------------------------------------------
    def qryTrade(self):
        """查询成交"""
        code, data = self.tradeCtx.deal_list_query(self.env)

        if code:
            self.writeError(code, u'查询成交失败:%s' % data)
            return

        self.processDeal(data)
        self.writeLog(u'成交查询成功')

    #----------------------------------------------------------------------
    def close(self):
        """关闭"""
        if self.quoteCtx:
            self.quoteCtx.close()
        if self.tradeCtx:
            self.tradeCtx.close()

    #----------------------------------------------------------------------
    def initQuery(self):
        """初始化连续查询"""
        if self.qryEnabled:
            # 需要循环的查询函数列表
            self.qryFunctionList = [self.qryAccount, self.qryPosition]

            self.qryCount = 0  # 查询触发倒计时
            self.qryTrigger = 2  # 查询触发点
            self.qryNextFunction = 0  # 上次运行的查询函数索引

            self.startQuery()

    #----------------------------------------------------------------------
    def query(self, event):
        """注册到事件处理引擎上的查询函数"""
        self.qryCount += 1

        if self.qryCount > self.qryTrigger:
            # 清空倒计时
            self.qryCount = 0

            # 执行查询函数
            function = self.qryFunctionList[self.qryNextFunction]
            function()

            # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0
            self.qryNextFunction += 1
            if self.qryNextFunction == len(self.qryFunctionList):
                self.qryNextFunction = 0

    #----------------------------------------------------------------------
    def startQuery(self):
        """启动连续查询"""
        self.eventEngine.register(EVENT_TIMER, self.query)

    #----------------------------------------------------------------------
    def setQryEnabled(self, qryEnabled):
        """设置是否要启动循环查询"""
        self.qryEnabled = qryEnabled

    #----------------------------------------------------------------------
    def processQuote(self, data):
        """报价推送"""
        for ix, row in data.iterrows():
            symbol = row['code']

            tick = self.tickDict.get(symbol, None)
            if not tick:
                tick = VtTickData()
                tick.symbol = symbol
                tick.vtSymbol = tick.symbol
                tick.gatewayName = self.gatewayName
                self.tickDict[symbol] = tick

            tick.date = row['data_date'].replace('-', '')
            tick.time = row['data_time']
            tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]),
                                              '%Y%m%d %H:%M:%S')
            tick.openPrice = row['open_price']
            tick.highPrice = row['high_price']
            tick.lowPrice = row['low_price']
            tick.preClosePrice = row['prev_close_price']

            tick.lastPrice = row['last_price']
            tick.volume = row['volume']

            if 'price_spread' in row:
                spread = row['price_spread']
                tick.upperLimit = tick.lastPrice + spread * 10
                tick.lowerLimit = tick.lastPrice - spread * 10

            newTick = copy(tick)
            self.onTick(newTick)

    #----------------------------------------------------------------------
    def processOrderBook(self, data):
        """订单簿推送"""
        symbol = data['code']

        tick = self.tickDict.get(symbol, None)
        if not tick:
            tick = VtTickData()
            tick.symbol = symbol
            tick.vtSymbol = tick.symbol
            tick.gatewayName = self.gatewayName
            self.tickDict[symbol] = tick

        d = tick.__dict__
        for i in range(5):
            bidData = data['Bid'][i]
            askData = data['Ask'][i]
            n = i + 1

            d['bidPrice%s' % n] = bidData[0]
            d['bidVolume%s' % n] = bidData[1]
            d['askPrice%s' % n] = askData[0]
            d['askVolume%s' % n] = askData[1]

        if tick.datetime:
            newTick = copy(tick)
            self.onTick(newTick)

    #----------------------------------------------------------------------
    def processOrder(self, data):
        """处理委托推送"""
        for ix, row in data.iterrows():
            # 如果状态是已经删除,则直接忽略
            if row['order_status'] == OrderStatus.DELETED:
                continue

            print(row['order_status'])
            order = VtOrderData()
            order.gatewayName = self.gatewayName

            order.symbol = row['code']
            order.vtSymbol = order.symbol

            order.orderID = str(row['order_id'])
            order.vtOrderID = '.'.join([self.gatewayName, order.orderID])

            order.price = float(row['price'])
            order.totalVolume = float(row['qty'])
            order.tradedVolume = float(row['dealt_qty'])
            order.orderTime = row['create_time'].split(' ')[-1]

            order.status = statusMapReverse.get(row['order_status'],
                                                STATUS_UNKNOWN)
            order.direction = directionMapReverse[row['trd_side']]

            self.onOrder(order)

    #----------------------------------------------------------------------
    def processDeal(self, data):
        """处理成交推送"""
        for ix, row in data.iterrows():
            tradeID = str(row['deal_id'])
            if tradeID in self.tradeSet:
                continue
            self.tradeSet.add(tradeID)

            trade = VtTradeData()
            trade.gatewayName = self.gatewayName

            trade.symbol = row['code']
            trade.vtSymbol = trade.symbol

            trade.tradeID = tradeID
            trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])

            trade.orderID = row['order_id']
            trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])

            trade.price = float(row['price'])
            trade.volume = float(row['qty'])
            trade.direction = directionMapReverse[row['trd_side']]

            trade.tradeTime = row['create_time'].split(' ')[-1]

            self.onTrade(trade)
コード例 #10
0
ファイル: futuGateway.py プロジェクト: aaront2000/vnpy
class FutuGateway(VtGateway):
    """富途接口"""

    #----------------------------------------------------------------------
    def __init__(self, eventEngine, gatewayName='FUTU'):
        """Constructor"""
        super(FutuGateway, self).__init__(eventEngine, gatewayName)
        
        self.quoteCtx = None
        self.tradeCtx = None
        
        self.host = ''
        self.ip = 0
        self.market = ''
        self.password = ''
        self.env = TrdEnv.SIMULATE  # 默认仿真交易
        
        self.fileName = self.gatewayName + '_connect.json'
        self.filePath = getJsonPath(self.fileName, __file__)
        
        self.tickDict = {}
        self.tradeSet = set()       # 保存成交编号的集合,防止重复推送
        
        self.qryEnabled = True
        self.qryThread = Thread(target=self.qryData)
        
    #----------------------------------------------------------------------
    def writeLog(self, content):
        """输出日志"""
        log = VtLogData()
        log.gatewayName = self.gatewayName
        log.logContent = content
        self.onLog(log)
        
    #----------------------------------------------------------------------
    def writeError(self, code, msg):
        """输出错误"""
        error = VtErrorData()
        error.gatewayName = self.gatewayName
        error.errorID = code
        error.errorMsg = msg
        self.onError(error)
        
    #----------------------------------------------------------------------
    def connect(self):
        """连接"""
        # 载入配置
        try:
            f = open(self.filePath)
            setting = json.load(f)
            self.host = setting['host']
            self.port = setting['port']
            self.market = setting['market']
            self.password = setting['password']
            self.env = setting['env']
        except:
            self.writeLog(u'载入配置文件出错')
            return
        
        self.connectQuote()
        self.connectTrade()
        
        self.qryThread.start()
        
    #----------------------------------------------------------------------
    def qryData(self):
        """初始化时查询数据"""
        # 等待2秒保证行情和交易接口启动完成
        sleep(2.0)
        
        # 查询合约、成交、委托、持仓、账户
        self.qryContract()
        self.qryTrade()
        self.qryOrder()
        self.qryPosition()
        self.qryAccount()
        
        # 启动循环查询
        self.initQuery()
        
    #----------------------------------------------------------------------
    def connectQuote(self):
        """连接行情功能"""
        self.quoteCtx = OpenQuoteContext(self.host, self.port)
        
        # 继承实现处理器类
        class QuoteHandler(StockQuoteHandlerBase):
            """报价处理器"""
            gateway = self  # 缓存Gateway对象
            
            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(QuoteHandler, self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processQuote(content)
                return RET_OK, content            
            
        class OrderBookHandler(OrderBookHandlerBase):
            """订单簿处理器"""
            gateway = self
            
            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(OrderBookHandler, self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processOrderBook(content)
                return RET_OK, content
        
        # 设置回调处理对象
        self.quoteCtx.set_handler(QuoteHandler())
        self.quoteCtx.set_handler(OrderBookHandler())
        
        # 启动行情
        self.quoteCtx.start()
        
        self.writeLog(u'行情接口连接成功')
        
    #----------------------------------------------------------------------
    def connectTrade(self):
        """连接交易功能"""
        # 连接交易接口
        if self.market == 'US':
            self.tradeCtx = OpenUSTradeContext(self.host, self.port)
        else:
            self.tradeCtx = OpenHKTradeContext(self.host, self.port)
            
        # 继承实现处理器类
        class OrderHandler(TradeOrderHandlerBase):
            """委托处理器"""
            gateway = self  # 缓存Gateway对象
            
            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(OrderHandler, self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processOrder(content)
                return RET_OK, content            
            
        class DealHandler(TradeDealHandlerBase):
            """订单簿处理器"""
            gateway = self
            
            def on_recv_rsp(self, rsp_str):
                ret_code, content = super(DealHandler, self).on_recv_rsp(rsp_str)
                if ret_code != RET_OK:
                    return RET_ERROR, content
                self.gateway.processDeal(content)
                return RET_OK, content  

        # 只有港股实盘交易才需要解锁
        code, data = self.tradeCtx.unlock_trade(self.password)
        if code == RET_OK:
            self.writeLog(u'交易接口解锁成功')
        else:
            self.writeLog(u'交易接口解锁失败,原因:%s' %data)
        
        # 设置回调处理对象
        self.tradeCtx.set_handler(OrderHandler())
        self.tradeCtx.set_handler(DealHandler())        
        
        # 启动交易接口
        self.tradeCtx.start()
        
        self.writeLog(u'交易接口连接成功')
    
    #----------------------------------------------------------------------
    def subscribe(self, subscribeReq):
        """订阅行情"""
        for data_type in ['QUOTE', 'ORDER_BOOK']:
            code, data = self.quoteCtx.subscribe(subscribeReq.symbol, data_type, True)
            
            if code:
                self.writeError(code, u'订阅行情失败:%s' %data)
        
    #----------------------------------------------------------------------
    def sendOrder(self, orderReq):
        """发单"""
        side = directionMap[orderReq.direction]
        priceType = OrderType.NORMAL        # 只支持限价单
        
        # 设置价格调整模式为向内调整(即买入调整后价格比原始价格低)
        if orderReq.direction ==  DIRECTION_LONG:
            adjustLimit = 0.05
        else:
            adjustLimit = -0.05
            
        code, data = self.tradeCtx.place_order(orderReq.price, orderReq.volume, 
                                               orderReq.symbol, side, priceType, 
                                               trd_env=self.env,
                                               adjust_limit=adjustLimit)
        
        if code:
            self.writeError(code, u'委托失败:%s' %data)
            return ''
        
        for ix, row in data.iterrows():
            orderID = str(row['order_id'])
        
        vtOrderID = '.'.join([self.gatewayName, orderID])
        
        return vtOrderID
        
    #----------------------------------------------------------------------
    def cancelOrder(self, cancelOrderReq):
        """撤单"""
        code, data = self.tradeCtx.modify_order(ModifyOrderOp.CANCEL, cancelOrderReq.orderID, 
                                                0, 0, trd_env=self.env) 
        
        if code:
            self.writeError(code, u'撤单失败:%s' %data)
            return
    
    #----------------------------------------------------------------------
    def qryContract(self):
        """查询合约"""
        for vtProductClass, product in productMap.items():
            code, data = self.quoteCtx.get_stock_basicinfo(self.market, product)
            
            if code:
                self.writeError(code, u'查询合约信息失败:%s' %data)
                return
            
            for ix, row in data.iterrows():
                contract = VtContractData()
                contract.gatewayName = self.gatewayName
                
                contract.symbol = row['code']
                contract.vtSymbol = contract.symbol
                contract.name = row['name']
                contract.productClass = vtProductClass
                contract.size = int(row['lot_size'])
                contract.priceTick = 0.001
                
                self.onContract(contract)
        
        self.writeLog(u'合约信息查询成功')
    
    #----------------------------------------------------------------------
    def qryAccount(self):
        """查询账户资金"""
        code, data = self.tradeCtx.accinfo_query(trd_env=self.env, acc_id=0)
        
        if code:
            self.writeError(code, u'查询账户资金失败:%s' %data)
            return
        
        for ix, row in data.iterrows():
            account = VtAccountData()
            account.gatewayName = self.gatewayName
            
            account.accountID = '%s_%s' %(self.gatewayName, self.market)
            account.vtAccountID = '.'.join([self.gatewayName, account.accountID])
            account.balance = float(row['total_assets'])
            account.available = float(row['avl_withdrawal_cash'])
            
            self.onAccount(account)
        
    #----------------------------------------------------------------------
    def qryPosition(self):
        """查询持仓"""
        code, data = self.tradeCtx.position_list_query(trd_env=self.env, acc_id=0)
        
        if code:
            self.writeError(code, u'查询持仓失败:%s' %data)
            return
            
        for ix, row in data.iterrows():
            pos = VtPositionData()
            pos.gatewayName = self.gatewayName
            
            pos.symbol = row['code']
            pos.vtSymbol = pos.symbol
            
            pos.direction = DIRECTION_LONG
            pos.vtPositionName = '.'.join([pos.vtSymbol, pos.direction])
            
            pos.position = float(row['qty'])
            pos.price = float(row['cost_price'])
            pos.positionProfit = float(row['pl_val'])
            pos.frozen = float(row['qty']) - float(row['can_sell_qty'])
            
            if pos.price < 0: pos.price = 0 
            if pos.positionProfit > 100000000: pos.positionProfit = 0 
            
            self.onPosition(pos)
            
    #----------------------------------------------------------------------
    def qryOrder(self):
        """查询委托"""
        code, data = self.tradeCtx.order_list_query("", trd_env=self.env)
        
        if code:
            self.writeError(code, u'查询委托失败:%s' %data)
            return
        
        self.processOrder(data)
        self.writeLog(u'委托查询成功')
    
    #----------------------------------------------------------------------
    def qryTrade(self):
        """查询成交"""
        code, data = self.tradeCtx.deal_list_query(self.env)
        
        if code:
            self.writeError(code, u'查询成交失败:%s' %data)
            return
        
        self.processDeal(data)
        self.writeLog(u'成交查询成功')
        
    #----------------------------------------------------------------------
    def close(self):
        """关闭"""
        if self.quoteCtx:
            self.quoteCtx.close()
        if self.tradeCtx:
            self.tradeCtx.close()
        
    #----------------------------------------------------------------------
    def initQuery(self):
        """初始化连续查询"""
        if self.qryEnabled:
            # 需要循环的查询函数列表
            self.qryFunctionList = [self.qryAccount, self.qryPosition]
            
            self.qryCount = 0           # 查询触发倒计时
            self.qryTrigger = 2         # 查询触发点
            self.qryNextFunction = 0    # 上次运行的查询函数索引
            
            self.startQuery()
    
    #----------------------------------------------------------------------
    def query(self, event):
        """注册到事件处理引擎上的查询函数"""
        self.qryCount += 1
        
        if self.qryCount > self.qryTrigger:
            # 清空倒计时
            self.qryCount = 0
            
            # 执行查询函数
            function = self.qryFunctionList[self.qryNextFunction]
            function()
            
            # 计算下次查询函数的索引,如果超过了列表长度,则重新设为0
            self.qryNextFunction += 1
            if self.qryNextFunction == len(self.qryFunctionList):
                self.qryNextFunction = 0
    
    #----------------------------------------------------------------------
    def startQuery(self):
        """启动连续查询"""
        self.eventEngine.register(EVENT_TIMER, self.query)
    
    #----------------------------------------------------------------------
    def setQryEnabled(self, qryEnabled):
        """设置是否要启动循环查询"""
        self.qryEnabled = qryEnabled
        
    #----------------------------------------------------------------------
    def processQuote(self, data):
        """报价推送"""
        for ix, row in data.iterrows():
            symbol = row['code']
    
            tick = self.tickDict.get(symbol, None)
            if not tick:
                tick = VtTickData()
                tick.symbol = symbol
                tick.vtSymbol = tick.symbol
                tick.gatewayName = self.gatewayName
                self.tickDict[symbol] = tick
                
            tick.date = row['data_date'].replace('-', '')
            tick.time = row['data_time']
            tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S')
            tick.openPrice = row['open_price']
            tick.highPrice = row['high_price']
            tick.lowPrice = row['low_price']
            tick.preClosePrice = row['prev_close_price']
            
            tick.lastPrice = row['last_price']
            tick.volume = row['volume']
            
            if 'price_spread' in row:
                spread = row['price_spread']
                tick.upperLimit = tick.lastPrice + spread * 10
                tick.lowerLimit = tick.lastPrice - spread * 10
            
            newTick = copy(tick)
            self.onTick(newTick)
    
    #----------------------------------------------------------------------
    def processOrderBook(self, data):
        """订单簿推送"""
        symbol = data['code']
    
        tick = self.tickDict.get(symbol, None)
        if not tick:
            tick = VtTickData()
            tick.symbol = symbol
            tick.vtSymbol = tick.symbol
            tick.gatewayName = self.gatewayName
            self.tickDict[symbol] = tick
        
        d = tick.__dict__
        for i in range(5):
            bidData = data['Bid'][i]
            askData = data['Ask'][i]
            n = i + 1
            
            d['bidPrice%s' %n] = bidData[0]
            d['bidVolume%s' %n] = bidData[1]
            d['askPrice%s' %n] = askData[0]
            d['askVolume%s' %n] = askData[1]
        
        if tick.datetime:
            newTick = copy(tick)
            self.onTick(newTick)
    
    #----------------------------------------------------------------------
    def processOrder(self, data):
        """处理委托推送"""
        for ix, row in data.iterrows():
            # 如果状态是已经删除,则直接忽略
            if row['order_status'] == OrderStatus.DELETED:
                continue
            
            print(row['order_status'])
            order = VtOrderData()
            order.gatewayName = self.gatewayName
            
            order.symbol = row['code']
            order.vtSymbol = order.symbol
            
            order.orderID = str(row['order_id'])
            order.vtOrderID = '.'.join([self.gatewayName, order.orderID])
            
            order.price = float(row['price'])
            order.totalVolume = float(row['qty'])
            order.tradedVolume = float(row['dealt_qty'])
            order.orderTime = row['create_time'].split(' ')[-1]      

            order.status = statusMapReverse.get(row['order_status'], STATUS_UNKNOWN)
            order.direction = directionMapReverse[row['trd_side']]
            
            self.onOrder(order)        
    
    #----------------------------------------------------------------------
    def processDeal(self, data):
        """处理成交推送"""
        for ix, row in data.iterrows():
            tradeID = str(row['deal_id'])
            if tradeID in self.tradeSet:
                continue
            self.tradeSet.add(tradeID)
            
            trade = VtTradeData()
            trade.gatewayName = self.gatewayName
            
            trade.symbol = row['code']
            trade.vtSymbol = trade.symbol
            
            trade.tradeID = tradeID
            trade.vtTradeID = '.'.join([self.gatewayName, trade.tradeID])
            
            trade.orderID = row['order_id']
            trade.vtOrderID = '.'.join([self.gatewayName, trade.orderID])
            
            trade.price = float(row['price'])
            trade.volume = float(row['qty'])
            trade.direction = directionMapReverse[row['trd_side']]
            
            trade.tradeTime = row['create_time'].split(' ')[-1]
            
            self.onTrade(trade)