def get_account_apis():
    openapi_client = TradeClient(client_config, logger=logger)
    openapi_client.get_managed_accounts()
    # 获取订单
    openapi_client.get_orders()
    # 获取持仓
    openapi_client.get_positions()
    # 获取资产
    openapi_client.get_assets()
Esempio n. 2
0
def get_account_apis():
    openapi_client = TradeClient(client_config, logger=logger)
    openapi_client.get_managed_accounts()
    # 获取订单
    openapi_client.get_orders()
    # 获取未成交订单
    # openapi_client.get_open_orders()
    # 获取已成交订单
    # openapi_client.get_filled_orders(start_time='2019-05-01', end_time='2019-05-21')
    # 获取持仓
    openapi_client.get_positions()
    # 获取资产
    openapi_client.get_assets()
def get_account_apis():
    openapi_client = TradeClient(client_config, logger=logger)
    openapi_client.get_managed_accounts()
    # 获取订单
    openapi_client.get_orders()
    # 获取未成交订单
    # openapi_client.get_open_orders()
    # 获取已成交订单
    # openapi_client.get_filled_orders(start_time='2019-05-01', end_time='2019-05-21')

    # 获取订单成交记录, 仅适用于综合账户
    transactions = openapi_client.get_transactions(symbol='AAPL', sec_type=SecurityType.STK, start_time=1641398400000,
                                                   end_time=1642398400000)
    # transactions = openapi_client.get_transactions(symbol='CL2201', sec_type=SecurityType.FUT)
    # transactions = openapi_client.get_transactions(order_id=24844739769009152)
    # transactions = openapi_client.get_transactions(symbol='BABA', sec_type='OPT', strike=121, expiry='20220121',
    #                put_call='CALL')

    # 获取持仓
    openapi_client.get_positions()
    # 获取资产
    openapi_client.get_assets()
    # 综合/模拟账户获取资产
    openapi_client.get_prime_assets()
Esempio n. 4
0
class TigerGateway(BaseGateway):
    """"""
    default_setting = {
        "tiger_id": "",
        "account": "",
        "standard_account": "",
        "private_key": '',
    }

    def __init__(self, event_engine):
        """Constructor"""
        super(TigerGateway, self).__init__(event_engine, "TIGER")

        self.tiger_id = ""
        self.account = ""
        self.standard_account = ""
        self.paper_account = ""
        self.language = ""

        self.client_config = None
        self.quote_client = None
        self.push_client = None

        self.local_id = 1000000
        self.tradeid = 0

        self.active = False
        self.queue = Queue()
        self.pool = None

        self.ID_TIGER2VT = {}
        self.ID_VT2TIGER = {}
        self.ticks = {}
        self.trades = set()
        self.contracts = {}
        self.symbol_names = {}

    def run(self):
        """"""
        while self.active:
            try:
                func, args = self.queue.get(timeout=0.1)
                func(*args)
            except Empty:
                pass

    def add_task(self, func, *args):
        """"""
        self.queue.put((func, [*args]))

    def connect(self, setting: dict):
        """"""
        self.private_key = setting['private_key']
        self.tiger_id = setting["tiger_id"]
        self.account = setting["account"]
        self.standard_account = setting["standard_account"]
        self.paper_account = setting["account"]
        self.languege = Language.zh_CN

        # Start thread pool for REST call
        self.active = True
        self.pool = Pool(5)
        self.pool.apply_async(self.run)

        # Put connect task into quque.
        self.init_client_config()
        self.add_task(self.connect_quote)
        self.add_task(self.connect_trade)
        self.add_task(self.connect_push)

    def init_client_config(self, sandbox=True):
        """"""
        self.client_config = TigerOpenClientConfig(sandbox_debug=sandbox)
        self.client_config.private_key = self.private_key
        self.client_config.tiger_id = self.tiger_id
        self.client_config.account = self.account
        self.client_config.standard_account = self.standard_account
        self.client_config.paper_account = self.paper_account
        self.client_config.language = self.language

    def connect_quote(self):
        """
        Connect to market data server.
        """
        try:
            self.quote_client = QuoteClient(self.client_config)
            self.symbol_names = dict(
                self.quote_client.get_symbol_names(lang=Language.zh_CN))
            self.query_contract()
        except ApiException:
            self.write_log("查询合约失败")
            return

        self.write_log("行情接口连接成功")
        self.write_log("合约查询成功")

    def connect_trade(self):
        """
        Connect to trade server.
        """
        self.trade_client = TradeClient(self.client_config)
        try:
            self.add_task(self.query_order)
            self.add_task(self.query_position)
            self.add_task(self.query_account)
        except ApiException:
            self.write_log("交易接口连接失败")
            return

        self.write_log("交易接口连接成功")

    def connect_push(self):
        """
        Connect to push server.
        """
        protocol, host, port = self.client_config.socket_host_port
        self.push_client = PushClient(host, port, (protocol == 'ssl'))
        self.push_client.connect(
            self.client_config.tiger_id, self.client_config.private_key)

        self.push_client.quote_changed = self.on_quote_change
        self.push_client.asset_changed = self.on_asset_change
        self.push_client.position_changed = self.on_position_change
        self.push_client.order_changed = self.on_order_change

        self.write_log("推送接口连接成功")

    def subscribe(self, req: SubscribeRequest):
        """"""
        self.push_client.subscribe_quote([req.symbol])
        self.push_client.subscribe_asset()
        self.push_client.subscribe_position()
        self.push_client.subscribe_order()

    def on_quote_change(self, tiger_symbol: str, data: list, trading: bool):
        """"""
        data = dict(data)
        symbol, exchange = convert_symbol_tiger2vt(tiger_symbol)

        tick = self.ticks.get(symbol, None)
        if not tick:
            tick = TickData(
                symbol=symbol,
                exchange=exchange,
                gateway_name=self.gateway_name,
                datetime=datetime.now(),
                name=self.symbol_names[symbol],
            )
            self.ticks[symbol] = tick

        tick.datetime = datetime.fromtimestamp(data["latest_time"] / 1000)
        tick.pre_close = data.get("prev_close", 0)
        tick.last_price = data.get("latest_price", 0)
        tick.volume = data.get("volume", 0)
        tick.open_price = data.get("open", 0)
        tick.open_price = data.get("open", 0)
        tick.high_price = data.get("high", 0)
        tick.low_price = data.get("low", 0)
        tick.ask_price_1 = data.get("ask_price", 0)
        tick.bid_price_1 = data.get("bid_price", 0)
        tick.ask_volume_1 = data.get("ask_size", 0)
        tick.bid_volume_1 = data.get("bid_size", 0)

        self.on_tick(copy(tick))

    def on_asset_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        if "net_liquidation" not in data:
            return

        account = AccountData(
            accountid=tiger_account,
            balance=data["net_liquidation"],
            frozen=0.0,
            gateway_name=self.gateway_name,
        )
        self.on_account(account)

    def on_position_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        symbol, exchange = convert_symbol_tiger2vt(data["origin_symbol"])

        pos = PositionData(
            symbol=symbol,
            exchange=exchange,
            direction=Direction.NET,
            volume=int(data["quantity"]),
            frozen=0.0,
            price=data["average_cost"],
            pnl=data["unrealized_pnl"],
            gateway_name=self.gateway_name,
        )
        self.on_position(pos)

    def on_order_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        print("委托推送", data["origin_symbol"],
              data["order_id"], data["filled"], data["status"])
        symbol, exchange = convert_symbol_tiger2vt(data["origin_symbol"])
        status = PUSH_STATUS_TIGER2VT[data["status"]]

        order = OrderData(
            symbol=symbol,
            exchange=exchange,
            orderid=self.ID_TIGER2VT.get(
                str(data["order_id"]), self.get_new_local_id()),
            direction=Direction.NET,
            price=data.get("limit_price", 0),
            volume=data["quantity"],
            traded=data["filled"],
            status=status,
            time=datetime.fromtimestamp(
                data["order_time"] / 1000).strftime("%H:%M:%S"),
            gateway_name=self.gateway_name,
        )
        self.on_order(order)

        if status == Status.ALLTRADED:
            self.tradeid += 1

            trade = TradeData(
                symbol=symbol,
                exchange=exchange,
                direction=Direction.NET,
                tradeid=self.tradeid,
                orderid=self.ID_TIGER2VT[str(data["order_id"])],
                price=data["avg_fill_price"],
                volume=data["filled"],
                time=datetime.fromtimestamp(
                    data["trade_time"] / 1000).strftime("%H:%M:%S"),
                gateway_name=self.gateway_name,
            )
            self.on_trade(trade)

    def get_new_local_id(self):
        self.local_id += 1
        return self.local_id

    def send_order(self, req: OrderRequest):
        """"""
        local_id = self.get_new_local_id()
        order = req.create_order_data(local_id, self.gateway_name)

        self.on_order(order)
        self.add_task(self._send_order, req, local_id)
        return order.vt_orderid

    def _send_order(self, req: OrderRequest, local_id):
        """"""
        currency = config_symbol_currency(req.symbol)
        try:
            contract = self.trade_client.get_contracts(
                symbol=req.symbol, currency=currency)[0]
            order = self.trade_client.create_order(
                account=self.account,
                contract=contract,
                action=DIRECTION_VT2TIGER[req.direction],
                order_type=ORDERTYPE_VT2TIGER[req.type],
                quantity=int(req.volume),
                limit_price=req.price,
            )
            self.ID_TIGER2VT[str(order.order_id)] = local_id
            self.ID_VT2TIGER[local_id] = str(order.order_id)

            self.trade_client.place_order(order)
            print("发单:", order.contract.symbol,
                  order.order_id, order.quantity, order.status)

        except:  # noqa
            traceback.print_exc()
            self.write_log("发单失败")
            return

    def cancel_order(self, req: CancelRequest):
        """"""
        self.add_task(self._cancel_order, req)

    def _cancel_order(self, req: CancelRequest):
        """"""
        try:
            order_id = self.ID_VT2TIGER[req.orderid]
            data = self.trade_client.cancel_order(order_id=order_id)
        except ApiException:
            self.write_log(f"撤单失败:{req.orderid}")

        if not data:
            self.write_log('撤单成功')

    def query_contract(self):
        """"""
        # HK Stock

        symbols_names_HK = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.HK)
        contract_names_HK = DataFrame(
            symbols_names_HK, columns=['symbol', 'name'])

        contractList = list(contract_names_HK["symbol"])
        i, n = 0, len(contractList)
        result = pd.DataFrame()
        while i < n:
            i += 500
            c = contractList[i - 500:i]
            r = self.quote_client.get_trade_metas(c)
            result = result.append(r)

        contract_detail_HK = result.sort_values(by="symbol", ascending=True)
        contract_HK = pd.merge(
            contract_names_HK, contract_detail_HK, how='left', on='symbol')

        for ix, row in contract_HK.iterrows():
            contract = ContractData(
                symbol=row["symbol"],
                exchange=Exchange.SEHK,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                pricetick=row["min_tick"],
                net_position=True,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract

        # US Stock
        symbols_names_US = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.US)
        contract_US = DataFrame(symbols_names_US, columns=['symbol', 'name'])

        for ix, row in contract_US.iterrows():
            contract = ContractData(
                symbol=row["symbol"],
                exchange=Exchange.SMART,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                pricetick=0.001,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract

        # CN Stock
        symbols_names_CN = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.CN)
        contract_CN = DataFrame(symbols_names_CN, columns=['symbol', 'name'])

        for ix, row in contract_CN.iterrows():
            symbol = row["symbol"]
            symbol, exchange = convert_symbol_tiger2vt(symbol)

            contract = ContractData(
                symbol=symbol,
                exchange=exchange,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                pricetick=0.001,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract

    def query_account(self):
        """"""
        try:
            assets = self.trade_client.get_assets()
        except ApiException:
            self.write_log("查询资金失败")
            return

        for i in assets:
            account = AccountData(
                accountid=self.account,
                balance=i.summary.net_liquidation,
                frozen=0.0,
                gateway_name=self.gateway_name,
            )

            self.on_account(account)

    def query_position(self):
        """"""
        try:
            position = self.trade_client.get_positions()
        except ApiException:
            self.write_log("查询持仓失败")
            return

        for i in position:
            symbol, exchange = convert_symbol_tiger2vt(i.contract.symbol)

            pos = PositionData(
                symbol=symbol,
                exchange=exchange,
                direction=Direction.NET,
                volume=int(i.quantity),
                frozen=0.0,
                price=i.average_cost,
                pnl=float(i.unrealized_pnl),
                gateway_name=self.gateway_name,
            )

            self.on_position(pos)

    def query_order(self):
        """"""
        try:
            data = self.trade_client.get_orders()
            data = sorted(data, key=lambda x: x.order_time, reverse=False)
        except:  # noqa
            traceback.print_exc()
            self.write_log("查询委托失败")
            return

        self.process_order(data)
        self.process_deal(data)

    def close(self):
        """"""
        self.active = False

        if self.push_client:
            self.push_client.disconnect()

    def process_order(self, data):
        """"""
        for i in data:
            symbol, exchange = convert_symbol_tiger2vt(str(i.contract))
            local_id = self.get_new_local_id()

            order = OrderData(
                symbol=symbol,
                exchange=exchange,
                orderid=local_id,
                direction=Direction.NET,
                price=i.limit_price if i.limit_price else 0.0,
                volume=i.quantity,
                traded=i.filled,
                status=STATUS_TIGER2VT[i.status],
                time=datetime.fromtimestamp(
                    i.order_time / 1000).strftime("%H:%M:%S"),
                gateway_name=self.gateway_name,
            )
            self.ID_TIGER2VT[str(i.order_id)] = local_id
            self.on_order(order)

        self.ID_VT2TIGER = {v: k for k, v in self.ID_TIGER2VT.items()}
        print("原始委托字典", self.ID_TIGER2VT)
        print("原始反向字典", self.ID_VT2TIGER)

    def process_deal(self, data):
        """
        Process trade data for both query and update.
        """
        for i in data:
            if i.status == ORDER_STATUS.PARTIALLY_FILLED or i.status == ORDER_STATUS.FILLED:
                symbol, exchange = convert_symbol_tiger2vt(str(i.contract))
                self.tradeid += 1

                trade = TradeData(
                    symbol=symbol,
                    exchange=exchange,
                    direction=Direction.NET,
                    tradeid=self.tradeid,
                    orderid=self.ID_TIGER2VT[str(i.order_id)],
                    price=i.avg_fill_price,
                    volume=i.filled,
                    time=datetime.fromtimestamp(
                        i.trade_time / 1000).strftime("%H:%M:%S"),
                    gateway_name=self.gateway_name,
                )

                self.on_trade(trade)
class TigerGateway(BaseGateway):
    """"""
    default_setting = {
        "tiger_id": "",
        "account": "",
        " server ": [" standard ", " global ", " simulation "],
        "private_key": "",
    }

    exchanges = [Exchange.SEHK, Exchange.SMART, Exchange.SSE, Exchange.SZSE]

    def __init__(self, event_engine):
        """Constructor"""
        super(TigerGateway, self).__init__(event_engine, "TIGER")

        self.tiger_id = ""
        self.account = ""
        self.server = ""
        self.language = ""

        self.client_config = None
        self.quote_client = None
        self.push_client = None

        self.local_id = 1000000
        self.tradeid = 0

        self.active = False
        self.queue = Queue()
        self.pool = None

        self.ID_TIGER2VT = {}
        self.ID_VT2TIGER = {}
        self.ticks = {}
        self.trades = set()
        self.contracts = {}
        self.symbol_names = {}

        self.push_connected = False
        self.subscribed_symbols = set()

    def run(self):
        """"""
        while self.active:
            try:
                func, args = self.queue.get(timeout=0.1)
                func(*args)
            except Empty:
                pass

    def add_task(self, func, *args):
        """"""
        self.queue.put((func, [*args]))

    def connect(self, setting: dict):
        """"""
        self.private_key = setting["private_key"]
        self.tiger_id = setting["tiger_id"]
        self.server = setting[" server "]
        self.account = setting["account"]
        self.languege = Language.zh_CN

        # Start thread pool for REST call
        self.active = True
        self.pool = Pool(5)
        self.pool.apply_async(self.run)

        # Put connect task into quque.
        self.init_client_config()
        self.add_task(self.connect_quote)
        self.add_task(self.connect_trade)
        self.add_task(self.connect_push)

    def init_client_config(self, sandbox=False):
        """"""
        self.client_config = TigerOpenClientConfig(sandbox_debug=sandbox)
        self.client_config.private_key = self.private_key
        self.client_config.tiger_id = self.tiger_id
        self.client_config.account = self.account
        self.client_config.language = self.language

    def connect_quote(self):
        """
        Connect to market data server.
        """
        try:
            self.quote_client = QuoteClient(self.client_config)
            self.symbol_names = dict(
                self.quote_client.get_symbol_names(lang=Language.zh_CN))
            self.query_contract()
        except ApiException:
            self.write_log(" queries contract failure ")
            return

        self.write_log(" quotes interfacing success ")
        self.write_log(" contract query succeeds ")

    def connect_trade(self):
        """
        Connect to trade server.
        """
        self.trade_client = TradeClient(self.client_config)
        try:
            self.add_task(self.query_order)
            self.add_task(self.query_position)
            self.add_task(self.query_account)
        except ApiException:
            self.write_log(" transaction interface connection failure ")
            return

        self.write_log(" successful transaction interface ")

    def connect_push(self):
        """
        Connect to push server.
        """
        protocol, host, port = self.client_config.socket_host_port
        self.push_client = PushClient(host, port, (protocol == "ssl"))

        self.push_client.quote_changed = self.on_quote_change
        self.push_client.asset_changed = self.on_asset_change
        self.push_client.position_changed = self.on_position_change
        self.push_client.order_changed = self.on_order_change
        self.push_client.connect_callback = self.on_push_connected

        self.push_client.connect(self.client_config.tiger_id,
                                 self.client_config.private_key)

    def subscribe(self, req: SubscribeRequest):
        """"""
        self.subscribed_symbols.add(req.symbol)

        if self.push_connected:
            self.push_client.subscribe_quote([req.symbol])

    def on_push_connected(self):
        """"""
        self.push_connected = True
        self.write_log(" push interfacing success ")

        self.push_client.subscribe_asset()
        self.push_client.subscribe_position()
        self.push_client.subscribe_order()

        self.push_client.subscribe_quote(list(self.subscribed_symbols))

    def on_quote_change(self, tiger_symbol: str, data: list, trading: bool):
        """"""
        data = dict(data)
        symbol, exchange = convert_symbol_tiger2vt(tiger_symbol)

        tick = self.ticks.get(symbol, None)
        if not tick:
            tick = TickData(
                symbol=symbol,
                exchange=exchange,
                gateway_name=self.gateway_name,
                datetime=datetime.now(),
                name=self.symbol_names[symbol],
            )
            self.ticks[symbol] = tick

        tick.datetime = datetime.fromtimestamp(int(data["timestamp"]) / 1000)
        tick.pre_close = data.get("prev_close", tick.pre_close)
        tick.last_price = data.get("latest_price", tick.last_price)
        tick.volume = data.get("volume", tick.volume)
        tick.open_price = data.get("open", tick.open_price)
        tick.high_price = data.get("high", tick.high_price)
        tick.low_price = data.get("low", tick.low_price)
        tick.ask_price_1 = data.get("ask_price", tick.ask_price_1)
        tick.bid_price_1 = data.get("bid_price", tick.bid_price_1)
        tick.ask_volume_1 = data.get("ask_size", tick.ask_volume_1)
        tick.bid_volume_1 = data.get("bid_size", tick.bid_volume_1)

        self.on_tick(copy(tick))

    def on_asset_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        if "net_liquidation" not in data:
            return

        account = AccountData(
            accountid=tiger_account,
            balance=data["net_liquidation"],
            frozen=0.0,
            gateway_name=self.gateway_name,
        )
        self.on_account(account)

    def on_position_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        symbol, exchange = convert_symbol_tiger2vt(data["origin_symbol"])

        pos = PositionData(
            symbol=symbol,
            exchange=exchange,
            direction=Direction.NET,
            volume=int(data["quantity"]),
            frozen=0.0,
            price=data["average_cost"],
            pnl=data["unrealized_pnl"],
            gateway_name=self.gateway_name,
        )
        self.on_position(pos)

    def on_order_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        symbol, exchange = convert_symbol_tiger2vt(data["origin_symbol"])
        status = STATUS_TIGER2VT[data["status"]]

        order = OrderData(
            symbol=symbol,
            exchange=exchange,
            orderid=self.ID_TIGER2VT.get(str(data["order_id"]),
                                         self.get_new_local_id()),
            direction=Direction.NET,
            price=data.get("limit_price", 0),
            volume=data["quantity"],
            traded=data["filled"],
            status=status,
            time=datetime.fromtimestamp(data["order_time"] /
                                        1000).strftime("%H:%M:%S"),
            gateway_name=self.gateway_name,
        )
        self.ID_TIGER2VT[str(data["order_id"])] = order.orderid
        self.on_order(order)

        if status == Status.ALLTRADED:
            self.tradeid += 1

            trade = TradeData(
                symbol=symbol,
                exchange=exchange,
                direction=Direction.NET,
                tradeid=self.tradeid,
                orderid=self.ID_TIGER2VT[str(data["order_id"])],
                price=data["avg_fill_price"],
                volume=data["filled"],
                time=datetime.fromtimestamp(data["trade_time"] /
                                            1000).strftime("%H:%M:%S"),
                gateway_name=self.gateway_name,
            )
            self.on_trade(trade)

    def get_new_local_id(self):
        self.local_id += 1
        return self.local_id

    def send_order(self, req: OrderRequest):
        """"""
        local_id = self.get_new_local_id()
        order = req.create_order_data(local_id, self.gateway_name)

        self.on_order(order)
        self.add_task(self._send_order, req, local_id)
        return order.vt_orderid

    def _send_order(self, req: OrderRequest, local_id):
        """"""
        currency = config_symbol_currency(req.symbol)
        try:
            contract = self.trade_client.get_contracts(symbol=req.symbol,
                                                       currency=currency)[0]
            order = self.trade_client.create_order(
                account=self.account,
                contract=contract,
                action=DIRECTION_VT2TIGER[req.direction],
                order_type=ORDERTYPE_VT2TIGER[req.type],
                quantity=int(req.volume),
                limit_price=req.price,
            )
            self.ID_TIGER2VT[str(order.order_id)] = local_id
            self.ID_VT2TIGER[local_id] = str(order.order_id)

            self.trade_client.place_order(order)

        except:  # noqa
            traceback.print_exc()
            self.write_log(" billed failure ")
            return

    def cancel_order(self, req: CancelRequest):
        """"""
        self.add_task(self._cancel_order, req)

    def _cancel_order(self, req: CancelRequest):
        """"""
        try:
            order_id = self.ID_VT2TIGER[req.orderid]
            data = self.trade_client.cancel_order(order_id=order_id)
        except ApiException:
            self.write_log(f" withdrawals failure :{req.orderid}")

        if not data:
            self.write_log(" withdrawals success ")

    def query_contract(self):
        """"""
        # HK Stock

        symbols_names_HK = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.HK)
        contract_names_HK = DataFrame(symbols_names_HK,
                                      columns=["symbol", "name"])

        contractList = list(contract_names_HK["symbol"])
        i, n = 0, len(contractList)
        result = pd.DataFrame()
        while i < n:
            i += 50
            c = contractList[i - 50:i]
            r = self.quote_client.get_trade_metas(c)
            result = result.append(r)

        contract_detail_HK = result.sort_values(by="symbol", ascending=True)
        contract_HK = pd.merge(contract_names_HK,
                               contract_detail_HK,
                               how="left",
                               on="symbol")

        for ix, row in contract_HK.iterrows():
            contract = ContractData(
                symbol=row["symbol"],
                exchange=Exchange.SEHK,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                min_volume=row["lot_size"],
                pricetick=row["min_tick"],
                net_position=True,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract

        # US Stock
        symbols_names_US = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.US)
        contract_US = DataFrame(symbols_names_US, columns=["symbol", "name"])

        for ix, row in contract_US.iterrows():
            contract = ContractData(
                symbol=row["symbol"],
                exchange=Exchange.SMART,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                min_volume=100,
                pricetick=0.001,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract

        # CN Stock
        symbols_names_CN = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.CN)
        contract_CN = DataFrame(symbols_names_CN, columns=["symbol", "name"])

        for ix, row in contract_CN.iterrows():
            symbol = row["symbol"]
            symbol, exchange = convert_symbol_tiger2vt(symbol)

            contract = ContractData(
                symbol=symbol,
                exchange=exchange,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                min_volume=100,
                pricetick=0.001,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract

    def query_account(self):
        """"""
        try:
            assets = self.trade_client.get_assets()
        except ApiException:
            self.write_log(" queries funds fail ")
            return

        for i in assets:
            account = AccountData(
                accountid=self.account,
                balance=i.summary.net_liquidation,
                frozen=0.0,
                gateway_name=self.gateway_name,
            )

            self.on_account(account)

    def query_position(self):
        """"""
        try:
            position = self.trade_client.get_positions()
        except ApiException:
            self.write_log(" queries positions fail ")
            return

        for i in position:
            symbol, exchange = convert_symbol_tiger2vt(i.contract.symbol)

            pos = PositionData(
                symbol=symbol,
                exchange=exchange,
                direction=Direction.NET,
                volume=int(i.quantity),
                frozen=0.0,
                price=i.average_cost,
                pnl=float(i.unrealized_pnl),
                gateway_name=self.gateway_name,
            )

            self.on_position(pos)

    def query_order(self):
        """"""
        try:
            data = self.trade_client.get_orders()
            data = sorted(data, key=lambda x: x.order_time, reverse=False)
        except:  # noqa
            traceback.print_exc()
            self.write_log(" inquiry commission failed ")
            return

        self.process_order(data)
        self.process_deal(data)

    def close(self):
        """"""
        self.active = False

        if self.push_client:
            self.push_client.disconnect()

    def process_order(self, data):
        """"""
        for i in data:
            symbol, exchange = convert_symbol_tiger2vt(str(i.contract))
            local_id = self.get_new_local_id()

            order = OrderData(
                symbol=symbol,
                exchange=exchange,
                orderid=local_id,
                direction=Direction.NET,
                price=i.limit_price if i.limit_price else 0.0,
                volume=i.quantity,
                traded=i.filled,
                status=STATUS_TIGER2VT[i.status],
                time=datetime.fromtimestamp(i.order_time /
                                            1000).strftime("%H:%M:%S"),
                gateway_name=self.gateway_name,
            )
            self.ID_TIGER2VT[str(i.order_id)] = local_id
            self.on_order(order)

        self.ID_VT2TIGER = {v: k for k, v in self.ID_TIGER2VT.items()}

    def process_deal(self, data):
        """
        Process trade data for both query and update.
        """
        for i in data:
            if i.status == OrderStatus.PARTIALLY_FILLED or i.status == OrderStatus.FILLED:
                symbol, exchange = convert_symbol_tiger2vt(str(i.contract))
                self.tradeid += 1

                trade = TradeData(
                    symbol=symbol,
                    exchange=exchange,
                    direction=Direction.NET,
                    tradeid=self.tradeid,
                    orderid=self.ID_TIGER2VT[str(i.order_id)],
                    price=i.avg_fill_price,
                    volume=i.filled,
                    time=datetime.fromtimestamp(i.trade_time /
                                                1000).strftime("%H:%M:%S"),
                    gateway_name=self.gateway_name,
                )

                self.on_trade(trade)
Esempio n. 6
0
class Test:
    def __init__(self):
        self.client_config = get_client_config()
        self.trade_client = TradeClient(self.client_config)
        self.openapi_client = QuoteClient(self.client_config, logger=logger)
        # 初始化 pushclient

        protocol, host, port = self.client_config.socket_host_port
        self.push_client = PushClient(host, port, use_ssl=(protocol == 'ssl'))
        self.push_client.connect(self.client_config.tiger_id, self.client_config.private_key)



    def trade(self):
        stock = stock_contract(symbol='AAPL', currency='USD')
        option = option_contract(identifier='AAPL  190927P00200000')

        emu_account = []
        # stock_contract = trade_client.get_contracts(symbol='FB')[0]

        account = self.trade_client.get_managed_accounts()
        print(account)
        emu_account = account[1]
        account = self.client_config.paper_account
        assets = self.trade_client.get_assets(account=account)
        print(assets)
        posinfo = self.trade_client.get_positions(account=account)
        print(posinfo)
        stock_order = market_order(account=account,   # 下单账户,可以使用标准、环球、或模拟账户
                                  contract = option,       # 第1步中获取的合约对象
                                  action = 'BUY',
                                  quantity = 1)
        print(stock_order)
        self.trade_client.place_order(stock_order)
        print(stock_order)

# 直接本地构造contract对象。 期货 contract 的构造方法请参考后面的文档
    def get_option_quote(self):
        symbol = 'AAPL'
        expirations = self.openapi_client.get_option_expirations(symbols=[symbol])
        if len(expirations) > 1:
            print(expirations)
            expiry = int(expirations[expirations['symbol'] == symbol].at[0, 'timestamp'])
            chains = self.openapi_client.get_option_chain(symbol, expiry)
            print(chains)
        for index,row in chains.iterrows():
            print(row["identifier"], row["strike"], row["put_call"])
        briefs = self.openapi_client.get_option_briefs(['AAPL  190927P00200000'])
        print(briefs)
        bars = self.openapi_client.get_option_bars(['AAPL  190927P00200000'])
        print(bars)
        ticks = self.openapi_client.get_option_trade_ticks(['AAPL  190927P00200000'])
        print(ticks)
        for index,row in ticks.iterrows():
            print(row["identifier"], row["time"], row["price"])

    def on_quote_changed(self, symbol, items, hour_trading):
        print(symbol, items, hour_trading)


    def test1(self):
        info = app.openapi_client.get_briefs(symbols)
        print(info)

    def run(self):
        option_trade_ticks = self.openapi_client.get_option_trade_ticks(['AAPL  190927P00200000'])
        print(option_trade_ticks)

    def subscribe(self):
        # self.push_client.connect(self.client_config.tiger_id, self.client_config.private_key)
        self.push_client.quote_changed = self.on_quote_changed
        self.push_client.subscribe_quote(symbols=['AAPL  190927P00200000', 'GOOG', 'FB'], quote_key_type=QuoteKeyType.ALL)
        self.push_client.subscribe_asset()
        time.sleep(30)
        # self.push_client.unsubscribe_quote(['AAPL', 'GOOG'])

    def subscribe2(self):

        self.push_client.quote_changed = self.on_quote_changed
        self.push_client.subscribe_quote(symbols=['aapl'], quote_key_type=QuoteKeyType.ALL)
        self.push_client.subscribe_asset()

    def on_changed(self, symbol, items, hour_trading):
        print(symbol, items, hour_trading)
        data = dict(items)
        latest_price = data.get('latest_price')
        volume = data.get('volume')


    def query_subscribed(self):
        def on_subscribed_symbols(symbols, focus_keys, limit, used):
            print(symbols, focus_keys, limit, used)

        self.push_client.subscribed_symbols = on_subscribed_symbols
        self.push_client.query_subscribed_quote()
Esempio n. 7
0
class Tiger:

    # default logger
    defualt_logger = logging.getLogger('bc_tiger_logger')

    # init
    def __init__(self,
                 account_type,
                 config,
                 sandbox_debug=False,
                 logger_name=None,
                 open_time_adj=0,
                 close_time_adj=0):

        # get logger
        self.logger = Tiger.defualt_logger if (
            logger_name is None) else logging.getLogger(logger_name)

        # read user info, position record from local files
        self.__user_info = io_util.read_config(file_path=config['tiger_path'],
                                               file_name='user_info.json')
        self.__position_record = io_util.read_config(
            file_path=config['config_path'],
            file_name='tiger_position_record.json')
        self.record = self.__position_record[account_type].copy()
        self.eod_api_key = config['api_key']['eod']

        # set account, account type
        self.account = self.__user_info[account_type]
        self.account_type = account_type

        # initialize client_config
        self.client_config = TigerOpenClientConfig(sandbox_debug=sandbox_debug)
        self.client_config.private_key = read_private_key(
            config['tiger_path'] + self.__user_info['private_key_name'])
        self.client_config.tiger_id = str(self.__user_info['tiger_id'])
        self.client_config.language = Language.en_US
        self.client_config.account = self.account

        # get quote/trade clients, assets, positions
        self.quote_client = QuoteClient(self.client_config)
        self.trade_client = TradeClient(self.client_config)
        self.positions = self.trade_client.get_positions(account=self.account)
        self.assets = self.trade_client.get_assets(account=self.account)

        # get market status and trade time
        self.update_trade_time(open_time_adj=open_time_adj,
                               close_time_adj=close_time_adj)

        # update position record
        self.synchronize_position_record(config=config)

        self.logger.info(f'[tiger]: Tiger instance created: {logger_name}')

    # get user info
    def get_user_info(self):
        return self.__user_info

    # get position record
    def get_position_record(self):
        return self.__position_record

    # synchronize position record with real position status
    def synchronize_position_record(self, config):

        account_type = self.account_type

        # initialize position record for symbols that not in position record
        init_cash = config['trade']['init_cash'][account_type]
        pool = config['selected_sec_list'][config['trade']['pool']
                                           [account_type]]
        for symbol in pool:
            if symbol not in self.record.keys():
                self.record[symbol] = {'cash': init_cash, 'position': 0}

        # get real position (dict)
        position_dict = dict([(x.contract.symbol, x.quantity)
                              for x in self.positions])

        # compare position record with real position
        record_conflicted = False
        for symbol in self.record.keys():

            if symbol not in pool:
                continue

            # update position in record
            record_position = self.record[symbol]['position']
            current_position = 0 if (
                symbol not in position_dict.keys()) else position_dict[symbol]
            if current_position != record_position:
                record_conflicted = True
                if current_position > 0:
                    self.record[symbol] = {
                        'cash': 0,
                        'position': current_position
                    }
                else:
                    self.record[symbol] = {'cash': init_cash, 'position': 0}
                self.logger.error(
                    f'[{account_type[:4]}]: {symbol} position({current_position}) rather than ({record_position}), reset record'
                )

        # add record for position that not recorded
        for symbol in [
                x for x in position_dict.keys()
                if (x in pool and x not in self.record.keys())
        ]:
            record_conflicted = True
            self.record[symbol] = {
                'cash': 0,
                'position': position_dict[symbol]
            }
            self.logger.error(
                f'[{account_type[:4]}]: {symbol} position({position_dict[symbol]}) not in record, add record'
            )

        # update __position_record
        if record_conflicted:
            self.__position_record[self.account_type] = self.record.copy()
            io_util.create_config_file(config_dict=self.__position_record,
                                       file_path=config['config_path'],
                                       file_name='tiger_position_record.json')

    # update position for an account
    def update_position_record(self,
                               config,
                               init_cash=None,
                               init_position=None,
                               start_time=None,
                               end_time=None,
                               is_print=True):

        # set default values
        init_cash = config['trade']['init_cash'][self.account_type] if (
            init_cash is None) else init_cash
        init_position = 0 if (init_position is None) else init_position
        start_time = self.trade_time['pre_open_time'].strftime(
            format="%Y-%m-%d %H:%M:%S") if (start_time is None) else start_time
        end_time = self.trade_time['post_close_time'].strftime(
            format="%Y-%m-%d %H:%M:%S") if (end_time is None) else end_time

        try:

            # get today filled orders
            orders = self.trade_client.get_filled_orders(start_time=start_time,
                                                         end_time=end_time)

            # update position records
            for order in orders:
                symbol = order.contract.symbol
                action = order.action
                quantity = order.quantity - order.remaining
                commission = order.commission
                avg_fill_price = order.avg_fill_price

                # init record if not exist
                if symbol not in self.record.keys():
                    self.record[symbol] = {
                        'cash': init_cash,
                        'position': init_position
                    }
                record_cash = self.record[symbol]['cash']
                record_position = self.record[symbol]['position']

                # calculate new cash and position
                if action == 'BUY':
                    cost = avg_fill_price * quantity + commission
                    new_cash = record_cash - cost
                    new_position = record_position + quantity

                elif action == 'SELL':
                    acquire = avg_fill_price * quantity - commission
                    new_cash = record_cash + acquire
                    new_position = record_position - quantity

                else:
                    new_cash = record_cash
                    new_position = record_position

                # update record
                if new_cash >= 0 and new_position >= 0:
                    self.record[symbol]['cash'] = new_cash
                    self.record[symbol]['position'] = new_position
                    if is_print:
                        self.logger.info(
                            f'[{self.account_type[:4]}]: updating position record for {symbol} {record_cash, record_position} -> {new_cash, new_position}'
                        )

            # update __position_record
            # self.record['updated'] = datetime.datetime.now().strftime(format="%Y-%m-%d %H:%M:%S")
            self.__position_record = io_util.read_config(
                file_path=config['config_path'],
                file_name='tiger_position_record.json')
            self.__position_record[self.account_type] = self.record.copy()
            self.__position_record['updated'][
                self.account_type] = datetime.datetime.now().strftime(
                    format="%Y-%m-%d %H:%M:%S")
            io_util.create_config_file(config_dict=self.__position_record,
                                       file_path=config['config_path'],
                                       file_name='tiger_position_record.json')

        except Exception as e:
            self.logger.exception(
                f'[erro]: fail updating position records for {self.account_type}, {e}'
            )

    # update portfolio for an account
    def update_portfolio_record(self,
                                config,
                                position_summary=None,
                                is_print=True):

        # get position summary
        if position_summary is None:
            position_summary = self.get_position_summary(get_briefs=False)
        position_summary.set_index('symbol', inplace=True)
        position_summary = position_summary.round(2)

        # get asset summary
        net_value = 0
        market_value = 0
        cash = 0
        asset_summary = self.get_asset_summary()
        if len(asset_summary) > 0:
            net_value = asset_summary.loc[0, 'net_value']
            market_value = asset_summary.loc[0, 'holding_value']
            cash = asset_summary.loc[0, 'cash']

        # post process
        if market_value == float('inf'):
            market_value = position_summary['market_value'].sum().round(2)

        # load portfolio record
        portfolio_record = io_util.read_config(file_path=config['config_path'],
                                               file_name='portfolio.json')
        old_net_value = portfolio_record['tiger'][self.account_type].get(
            'net_value')
        support = portfolio_record['tiger'][self.account_type].get(
            'portfolio').get('support')
        resistant = portfolio_record['tiger'][self.account_type].get(
            'portfolio').get('resistant')

        # update portfolio record for current account
        portfolio_record['tiger'][
            self.account_type]['portfolio'] = position_summary.to_dict()
        portfolio_record['tiger'][
            self.account_type]['portfolio']['support'] = {}
        portfolio_record['tiger'][
            self.account_type]['portfolio']['resistant'] = {}

        quantity = portfolio_record['tiger'][
            self.account_type]['portfolio'].get('quantity')
        if quantity is not None:
            if support is not None:
                for symbol in quantity.keys():
                    portfolio_record['tiger'][self.account_type]['portfolio'][
                        'support'][symbol] = support.get(symbol)

            if resistant is not None:
                for symbol in quantity.keys():
                    portfolio_record['tiger'][self.account_type]['portfolio'][
                        'resistant'][symbol] = resistant.get(symbol)

        portfolio_record['tiger'][
            self.account_type]['market_value'] = market_value
        portfolio_record['tiger'][self.account_type]['net_value'] = net_value
        portfolio_record['tiger'][self.account_type]['cash'] = cash
        portfolio_record['tiger'][
            self.account_type]['updated'] = datetime.datetime.now().strftime(
                format="%Y-%m-%d %H:%M:%S")
        io_util.create_config_file(config_dict=portfolio_record,
                                   file_path=config['config_path'],
                                   file_name='portfolio.json')

        # print
        if is_print:
            self.logger.info(
                f'[{self.account_type[:4]}]: net value {old_net_value} --> {net_value}'
            )

    # get summary of positions
    def get_position_summary(self, get_briefs=False):

        try:
            # update positions
            self.positions = self.trade_client.get_positions(
                account=self.client_config.account)

            # convert positions(list) to dataframe
            if len(self.positions) > 0:
                result = {
                    'symbol': [],
                    'quantity': [],
                    'average_cost': [],
                    'market_price': []
                }
                for pos in self.positions:
                    result['symbol'].append(pos.contract.symbol)
                    result['quantity'].append(pos.quantity)
                    result['average_cost'].append(pos.average_cost)
                    result['market_price'].append(pos.market_price)
                result = pd.DataFrame(result)

                # get briefs for stocks in positions
                if get_briefs:
                    status = io_util.get_stock_briefs(
                        symbols=[x.contract.symbol for x in self.positions],
                        source='eod',
                        period='1d',
                        interval='1m',
                        api_key=self.eod_api_key)
                    result = pd.merge(result,
                                      status,
                                      how='left',
                                      left_on='symbol',
                                      right_on='symbol')
                    result['rate'] = round(
                        (result['latest_price'] - result['average_cost']) /
                        result['average_cost'], 2)
                    result = result[[
                        'symbol', 'quantity', 'average_cost', 'latest_price',
                        'rate', 'latest_time'
                    ]]
                else:
                    result.rename(columns={'market_price': 'latest_price'},
                                  inplace=True)
                    result['rate'] = round(
                        (result['latest_price'] - result['average_cost']) /
                        result['average_cost'], 2)
                    result['latest_time'] = None

                # calculate market value
                result['market_value'] = result['quantity'] * result[
                    'latest_price']

            else:
                result = pd.DataFrame({
                    'symbol': [],
                    'quantity': [],
                    'average_cost': [],
                    'latest_price': [],
                    'rate': [],
                    'market_value': [],
                    'latest_time': []
                })

        except Exception as e:
            result = pd.DataFrame({
                'symbol': [],
                'quantity': [],
                'average_cost': [],
                'latest_price': [],
                'rate': [],
                'market_value': [],
                'latest_time': []
            })
            self.logger.exception(f'[erro]: can not get position summary: {e}')

        return result

    # get summary of assets
    def get_asset_summary(self, print_summary=False):

        # update assets
        self.assets = self.trade_client.get_assets(
            account=self.client_config.account)
        asset = self.assets[0]
        result = {
            'account': [asset.account],
            'net_value': [asset.summary.net_liquidation],
            'holding_value': [asset.summary.gross_position_value],
            'cash': [asset.summary.cash],
            'available_casg': [asset.summary.available_funds],
            'pnl': [asset.summary.realized_pnl],
            'holding_pnl': [asset.summary.unrealized_pnl]
        }

        if print_summary:
            summary = f'''
      账户: {asset.account}({asset.summary.currency}):
      总资产: {asset.summary.net_liquidation}
      现金: {asset.summary.cash} (可用 {asset.summary.available_funds})
      持仓市值: {asset.summary.gross_position_value}
      日内交易次数: {asset.summary.day_trades_remaining}
      已实现盈亏: {asset.summary.realized_pnl}
      未实现盈亏: {asset.summary.unrealized_pnl}
      '''
            print(summary)

        return pd.DataFrame(result)

    # get available money
    def get_available_cash(self):

        # get available cash for real accounts
        self.assets = self.trade_client.get_assets(
            account=self.client_config.account)
        available_cash = self.assets[0].summary.cash

        return available_cash

    # get quantity of symbol currently in the position
    def get_in_position_quantity(self, symbol, get_briefs=False):

        # initialize affordable quantity
        quantity = 0

        # get position summary
        position = self.get_position_summary(get_briefs=get_briefs)
        if len(position) > 0:
            position = position.set_index('symbol')
            if symbol in position.index:
                quantity = position.loc[symbol, 'quantity']

        return quantity

    # check whether it is affordable to buy certain amount of a stock
    def get_affordable_quantity(self, symbol, cash=None, trading_fee=3):

        # initialize affordable quantity and available cash
        quantity = 0
        available_cash = self.get_available_cash() if (cash is None) else cash

        # get latest price of stock
        stock_brief = io_util.get_stock_briefs(
            symbols=[symbol],
            source='eod',
            period='1d',
            interval='1m',
            api_key=self.eod_api_key).set_index('symbol')
        latest_price = stock_brief.loc[symbol, 'latest_price']

        # check if it is affordable
        quantity = math.floor((available_cash - trading_fee) / latest_price)

        return quantity

    # idle for specified time and check position in certain frequency
    def idle(self, target_time, check_frequency=600):
        """
    Sleep with a fixed frequency, until the target time

    :param target_time: the target time in datetime.datetime format
    :param check_frequency: the fixed sleep_time 
    :returns: none
    :raises: none
    """
        # get current time
        now = datetime.datetime.now()
        while now < target_time:

            # # get position summary
            # pos = self.get_position_summary()
            # self.logger.info(f'[rate]:----------------------------------------------\n{pos}\n')

            # get current time, calculate difference between current time and target time
            diff_time = round((target_time - now).total_seconds())
            sleep_time = (diff_time + 1) if (
                diff_time <= check_frequency) else check_frequency

            # sleep
            self.logger.info(
                f'[idle]: {now.strftime(format="%Y-%m-%d %H:%M:%S")}: sleep for {sleep_time} seconds'
            )
            time.sleep(sleep_time)

            # update current time
            now = datetime.datetime.now()

        self.logger.info(
            f'[wake]: {now.strftime(format="%Y-%m-%d %H:%M:%S")}: exceed target time({target_time})'
        )

    # update trade time
    def update_trade_time(self,
                          market=Market.US,
                          tz='Asia/Shanghai',
                          open_time_adj=0,
                          close_time_adj=0):

        # get local timezone
        tz = pytz.timezone(tz)

        try:
            # get open_time
            status = self.quote_client.get_market_status(market=market)[0]
            current_status = status.status
            open_time = status.open_time.astimezone(tz).replace(tzinfo=None)
            open_time = open_time + datetime.timedelta(hours=open_time_adj)

            # if program runs after market open, api will return trade time for next trade day,
            # trade time for current trade day need to be calculated manually
            if status.status in ['Trading', 'Post-Market Trading']:
                if open_time.weekday() == 0:
                    open_time = open_time - datetime.timedelta(days=3)
                else:
                    open_time = open_time - datetime.timedelta(days=1)

            # calculate close time, pre_open_time, post_close_time
            close_time = open_time + datetime.timedelta(hours=6.5 +
                                                        close_time_adj)
            pre_open_time = open_time - datetime.timedelta(hours=5.5)
            post_close_time = close_time + datetime.timedelta(hours=4)

            # open and close time of chinese stock market
            a_open_time = pre_open_time + datetime.timedelta(
                hours=9.5 - pre_open_time.hour)
            a_close_time = pre_open_time + datetime.timedelta(
                hours=15 - pre_open_time.hour)

        except Exception as e:
            self.logger.error(e)
            current_status = None
            open_time = None
            close_time = None
            pre_open_time = None
            post_close_time = None

        self.trade_time = {
            'status': current_status,
            'tz': tz,
            'pre_open_time': pre_open_time,
            'open_time': open_time,
            'close_time': close_time,
            'post_close_time': post_close_time,
            'a_open_time': a_open_time,
            'a_close_time': a_close_time
        }

    # update market status
    def update_market_status(self, market=Market.US, return_str=False):

        try:
            # get market status
            status = self.quote_client.get_market_status(market=market)[0]
            self.trade_time['status'] = status.status

            if return_str:
                time_format = '%Y-%m-%d %H:%M'
                pre_open_time = self.trade_time['pre_open_time'].strftime(
                    time_format)
                post_close_time = self.trade_time['post_close_time'].strftime(
                    time_format)

                time_format = '%H:%M'
                open_time = self.trade_time['open_time'].strftime(time_format)
                close_time = self.trade_time['close_time'].strftime(
                    time_format)

                time_str = f'<({pre_open_time}){open_time} -- {close_time}({post_close_time})>'

                return time_str

        except Exception as e:
            self.logger.error(e)

    # buy or sell stocks
    def trade(self,
              symbol,
              action,
              quantity,
              price=None,
              stop_loss=None,
              stop_profit=None,
              print_summary=True):

        trade_summary = ''
        try:

            # construct contract
            contract = stock_contract(symbol=symbol, currency='USD')

            # construct order
            if price is None:
                order_price = 'market'
                order = market_order(account=self.client_config.account,
                                     contract=contract,
                                     action=action,
                                     quantity=quantity)
            else:
                order_price = float(f'{price}')
                order = limit_order(account=self.client_config.account,
                                    contract=contract,
                                    action=action,
                                    quantity=quantity,
                                    limit_price=price)

            # construct trade summary
            trade_summary += f'[{action}]: {symbol} X {quantity} ({order_price})\t'

            # attach order legs
            order_legs = []
            if stop_loss is not None:
                stop_loss_order_leg = order_leg('LOSS',
                                                stop_loss,
                                                time_in_force='GTC')  # 附加止损单
                order_legs.append(stop_loss_order_leg)
            if stop_profit is not None:
                stop_profit_order_leg = order_leg('PROFIT',
                                                  stop_profit,
                                                  time_in_force='GTC')  # 附加止盈单
                order_legs.append(stop_profit_order_leg)
            if len(order_legs) > 0:
                order.order_legs = order_legs

            # place buy order if affordable
            if action == 'BUY':
                affordable_quantity = self.get_affordable_quantity(
                    symbol=symbol)
                if quantity <= affordable_quantity:
                    self.trade_client.place_order(order)
                    trade_summary += f'SUCCEED: {order.id}'
                else:
                    trade_summary += f'FAILED: Not affordable({affordable_quantity}/{quantity})'

            # place sell order if holding enough stocks
            elif action == 'SELL':
                in_position_quantity = self.get_in_position_quantity(symbol)
                if in_position_quantity >= quantity:
                    self.trade_client.place_order(order)
                    trade_summary += f'SUCCEED: {order.id}'
                else:
                    trade_summary += f'FAILED: Not enough stock to sell({in_position_quantity}/{quantity})'

            # other actions
            else:
                trade_summary += f'FAILED: Unknown action({action})'

        except Exception as e:
            trade_summary += f'FAILED: {e}'

        # print trade summary
        if print_summary:
            self.logger.info(trade_summary)

        return trade_summary

    # auto trade according to signals
    def signal_trade(self,
                     signal,
                     money_per_sec,
                     order_type='market',
                     trading_fee=5,
                     pool=None,
                     according_to_record=True,
                     minimum_position=None):

        # set symbol to index
        if len(signal) > 0:
            # signal = signal.rename(columns={'代码':'symbol', '交易信号':'action'})
            # signal = signal.set_index('symbol')

            # filter sec with pool
            if pool is not None:
                filtered_list = [x for x in signal.index if x in pool]
                signal = signal.loc[filtered_list, signal.columns].copy()

        # if signal list is not empty
        if len(signal) > 0:
            # get latest price for signals

            # if order_type == 'market':
            # signal_brief = self.quote_client.get_stock_briefs(symbols=signal.index.tolist()).set_index('symbol')
            # signal_brief = io_util.get_stock_briefs(symbols=signal.index.tolist(), source='eod', period='1d', interval='1m', api_key=self.eod_api_key).set_index('symbol')
            # signal = pd.merge(signal, signal_brief[['latest_price']], how='left', left_index=True, right_index=True)

            # get in-position quantity and latest price for signals
            position = self.get_position_summary(get_briefs=False)
            if len(position) == 0:
                position = pd.DataFrame({'symbol': [], 'quantity': []})
            position = position.set_index('symbol')
            signal = pd.merge(signal,
                              position[['quantity']],
                              how='left',
                              left_index=True,
                              right_index=True).fillna(0)

            # sell
            # get sell signals
            sell_signal = signal.query('action == "s"')
            if len(sell_signal) > 0:
                # go through sell signals
                for symbol in sell_signal.index:
                    # check whether symbol is in positions
                    in_position_quantity = signal.loc[symbol, 'quantity']
                    if in_position_quantity > 0:
                        if order_type == 'limit':
                            price = signal.loc[symbol, 'latest_price']
                        else:
                            price = None
                        trade_summary = self.trade(
                            symbol=symbol,
                            action='SELL',
                            quantity=in_position_quantity,
                            price=price,
                            print_summary=False)
                        self.logger.info(trade_summary)
                    else:
                        self.logger.info(
                            f'[SELL]: {symbol} skipped (not in positions)')
            else:
                self.logger.info(f'[SELL]: no signal')

            # buy
            # get available cash, set minimum position
            available_cash = self.get_available_cash()
            if minimum_position is None:
                minimum_position = money_per_sec

            # get buy signals which not in posiitons yet
            default_money_per_sec = money_per_sec
            buy_signal = signal.query('action == "b"')
            if len(buy_signal) > 0:
                # go through buy signals
                for symbol in buy_signal.index:

                    # break when available cash is below 200
                    if available_cash <= minimum_position:
                        self.logger.info(
                            f'[BUY]: Available cash is too low({available_cash}/{minimum_position}), stop buying'
                        )
                        break

                    # check whether symbol is already in positions
                    in_position_quantity = signal.loc[symbol, 'quantity']
                    if in_position_quantity == 0:
                        # set money used to establish a new position
                        if according_to_record:
                            if (symbol in self.record.keys()) and (
                                    self.record[symbol]['position'] == 0):
                                money_per_sec = self.record[symbol]['cash']
                            else:
                                money_per_sec = default_money_per_sec

                        # check whether there is enough available money
                        money_per_sec = available_cash if (
                            money_per_sec > available_cash) else money_per_sec

                        # calculate quantity to buy
                        quantity = math.floor(
                            (money_per_sec - trading_fee) /
                            signal.loc[symbol, 'latest_price'])
                        if quantity > 0:
                            if order_type == 'limit':
                                price = signal.loc[symbol, 'latest_price']
                            else:
                                price = None
                            trade_summary = self.trade(symbol=symbol,
                                                       action='BUY',
                                                       quantity=quantity,
                                                       price=price,
                                                       print_summary=False)
                            self.logger.info(trade_summary)

                            # update available cash
                            available_cash -= quantity * signal.loc[
                                symbol, 'latest_price']
                        else:
                            self.logger.info(f'[BUY]: not enough money')
                            continue
                    else:
                        self.logger.info(
                            f'[BUY]: {symbol} skipped (already in positions:{in_position_quantity})'
                        )
                        continue
            else:
                self.logger.info(f'[BUY]: no signal')
        else:
            self.logger.info(f'[SKIP]: no signal')

    # stop loss or stop profit or clear all positions
    def cash_out(self,
                 stop_loss_rate=None,
                 stop_profit_rate=None,
                 clear_all=False,
                 print_summary=True):

        # get current position with summary
        position = self.get_position_summary(get_briefs=True)
        if len(position) > 0:

            # set symbol as index
            position = position.set_index('symbol')

            # if clear all positions
            if clear_all:
                cash_out_list = position.index.tolist()
            else:
                stop_loss_list = [] if stop_loss_rate is None else position.query(
                    f'rate < {stop_loss_rate}').index.tolist()
                stop_profit_list = [] if stop_profit_rate is None else position.query(
                    f'rate > {stop_profit_rate}').index.tolist()
                cash_out_list = list(set(stop_loss_list + stop_profit_list))

            # cash out
            if len(cash_out_list) > 0:
                cash_out_position = position.loc[cash_out_list, ].copy()
                self.logger.info(
                    f'[STOP]: LOSS: {stop_loss_list}, PROFIT: {stop_profit_list}'
                )

                for index, row in cash_out_position.iterrows():
                    self.trade(symbol=index,
                               action='SELL',
                               quantity=row['quantity'],
                               print_summary=print_summary)
Esempio n. 8
0
class TigerGateway(BaseGateway):
    """"""
    default_setting = {
        "tiger_id": "",
        "account": "",
        "服务器": ["标准", "环球", "仿真"],
        "private_key": "",
    }
    # 在 VNTRADER 中展示的交易所列表
    exchanges = [
        Exchange.SEHK, Exchange.SMART, Exchange.SSE, Exchange.SZSE,
        Exchange.CFE, Exchange.ECBOT, Exchange.CMECRYPTO, Exchange.CFE,
        Exchange.GLOBEX, Exchange.NYMEX, Exchange.SGX, Exchange.HKFE
    ]

    def __init__(self, event_engine):
        """Constructor"""
        super(TigerGateway, self).__init__(event_engine, "TIGER")

        self.tiger_id = ""
        self.account = ""
        self.server = ""
        self.language = ""

        self.client_config = None
        self.quote_client = None
        self.push_client = None

        self.local_id = 1000000
        self.tradeid = 0

        self.active = False
        self.queue = Queue()
        self.pool = None

        self.ID_TIGER2VT = {}
        self.ID_VT2TIGER = {}
        self.ticks = {}
        self.trades = set()
        self.contracts = {}
        self.symbol_names = {}

        # {symbol NQ1909: (exchange, trading_contract)}
        self.vt_tiger_symbol_map = {}

        self.push_connected = False
        self.subscribed_symbols = set()

    def run(self):
        """"""
        while self.active:
            try:
                func, args = self.queue.get(timeout=0.1)
                func(*args)
            except Empty:
                pass
            except Exception:
                self.write_log('方法%s调用失败,参数为%s' % (func.__name__, args))

    def add_task(self, func, *args):
        """"""
        self.queue.put((func, [*args]))

    def connect(self, setting: dict):
        """"""
        self.private_key = setting["private_key"]
        self.tiger_id = setting["tiger_id"]
        self.server = setting["服务器"]
        self.account = setting["account"]
        self.languege = Language.zh_CN

        # Start thread pool for REST call
        self.active = True
        self.pool = Pool(5)
        self.pool.apply_async(self.run)

        # Put connect task into quque.
        self.init_client_config()
        self.add_task(self.connect_quote)
        self.add_task(self.connect_trade)
        self.add_task(self.connect_push)

    def init_client_config(self, sandbox=SANDBOX):
        """"""
        self.client_config = TigerOpenClientConfig(sandbox_debug=sandbox)
        self.client_config.private_key = self.private_key
        self.client_config.tiger_id = self.tiger_id
        self.client_config.account = self.account
        self.client_config.language = self.language

    def connect_quote(self):
        """
        Connect to market data server.
        """
        try:
            self.quote_client = QuoteClient(self.client_config)
            self.symbol_names = dict(
                self.quote_client.get_symbol_names(lang=Language.zh_CN))
            self.query_contract()
        except ApiException:
            self.write_log("查询合约失败")
            return

        self.write_log("行情接口连接成功")

    def connect_trade(self):
        """
        Connect to trade server.
        """
        self.write_log('查询交易接口')
        self.trade_client = TradeClient(self.client_config)
        try:
            self.add_task(self.query_order)
            self.add_task(self.query_position)
            self.add_task(self.query_account)
        except ApiException:
            self.write_log("交易接口连接失败")
            return

        self.write_log("交易接口连接成功")

    def connect_push(self):
        """
        Connect to push server.
        """
        protocol, host, port = self.client_config.socket_host_port
        self.push_client = PushClient(host, port, (protocol == "ssl"))

        self.push_client.quote_changed = self.on_quote_change
        self.push_client.asset_changed = self.on_asset_change
        self.push_client.position_changed = self.on_position_change
        self.push_client.order_changed = self.on_order_change
        self.push_client.connect_callback = self.on_push_connected
        self.push_client.disconnect_callback = self.on_disconnected

        self.push_client.connect(self.client_config.tiger_id,
                                 self.client_config.private_key)

    def subscribe(self, req: SubscribeRequest):
        """"""
        self.subscribed_symbols.add(req.symbol)

        if self.push_connected:
            self.push_client.subscribe_quote(symbols=[req.symbol],
                                             quote_key_type=QuoteKeyType.ALL)

    def on_push_connected(self):
        """"""
        self.push_connected = True
        self.write_log("推送接口连接成功")

        self.push_client.subscribe_asset(account=self.account)
        self.push_client.subscribe_position(account=self.account)
        self.push_client.subscribe_order(account=self.account)

        self.push_client.subscribe_quote(list(self.subscribed_symbols))

    def on_disconnected(self):
        self.write_log('推送接口断开链接')
        self.push_connected = False

    def on_quote_change(self, tiger_symbol: str, data: list, trading: bool):
        """"""
        data = dict(data)
        symbol, exchange = self.get_vt_symbol_exchange(tiger_symbol)

        # 如果只推送了时间戳,或只推送了timeline,不向策略中推送新的tick事件
        if 'latest_price' not in data and 'bid_price' not in data:
            return

        tick = self.ticks.get(symbol, None)
        if not tick:
            tick = TickData(
                symbol=symbol,
                exchange=exchange,
                gateway_name=self.gateway_name,
                datetime=datetime.now(),
                name=symbol,
            )
            self.ticks[symbol] = tick
        # 本地止损单的设计依赖于limit up 与limit down(张跌停价格)。目前API中没有提供。
        # 所以这里用high low 来代替 limit up 与limit down
        tick.datetime = datetime.fromtimestamp(int(data["timestamp"]) / 1000)
        tick.volume = data.get("volume", tick.volume)
        tick.ask_volume_1 = data.get("ask_size", tick.ask_volume_1)
        tick.bid_volume_1 = data.get("bid_size", tick.bid_volume_1)
        tick.pre_close = data.get("prev_close", tick.pre_close)
        tick.last_price = data.get("latest_price", tick.last_price)
        tick.open_price = data.get("open", tick.open_price)
        tick.high_price = data.get("high", tick.high_price)
        tick.low_price = data.get("low", tick.low_price)
        tick.ask_price_1 = data.get("ask_price", tick.ask_price_1)
        tick.bid_price_1 = data.get("bid_price", tick.bid_price_1)
        tick.limit_down = tick.low_price
        tick.limit_up = tick.high_price

        self.on_tick(copy(tick))

    def on_asset_change(self, tiger_account: str, data: list):
        """"""
        data = dict(data)
        if "net_liquidation" not in data:
            return
        segment = data.get('segment')
        # 环球账户, 只推送summary的信息,含股票期货
        if segment == 'summary' or segment is None:
            account = tiger_account
        # 标准账户有『子账户』的概念, 分别推送股票与期货账户的信息
        elif segment == 'S':
            account = 'Security'
        elif segment == 'C':
            account = 'Commodity'

        account = AccountData(
            accountid=account,
            balance=round(data["net_liquidation"], 2),
            frozen=0.0,
            gateway_name=self.gateway_name,
        )
        self.on_account(account)

    def on_position_change(self, tiger_account: str, data: list):
        """"""
        if tiger_account != self.account:
            return
        data = dict(data)
        # 处理标准与环球账户的差异, 环球的账户的期货信息要从originsymbol中获取,标准的从symbol中获取
        origin_symbol = data.get("origin_symbol")
        if origin_symbol:
            symbol = origin_symbol2symbol(origin_symbol)
        else:
            symbol = data.get('symbol')
        symbol, exchange = self.get_vt_symbol_exchange(symbol)

        pos = PositionData(
            symbol=symbol,
            exchange=exchange,
            direction=Direction.NET,
            volume=int(data["quantity"]),
            frozen=0.0,
            price=round(data["average_cost"], 2),
            pnl=round(data["unrealized_pnl"], 2),
            gateway_name=self.gateway_name,
        )
        self.on_position(pos)

    def on_order_change(self, tiger_account: str, data: list):
        """"""
        # 处理订阅了多个账户的情况
        if tiger_account != self.account:
            return
        data = dict(data)
        origin_symbol = data.get("origin_symbol")
        if origin_symbol:
            symbol = origin_symbol2symbol(origin_symbol)
        else:
            symbol = data.get('symbol')
        symbol, exchange = self.get_vt_symbol_exchange(symbol)
        status = STATUS_TIGER2VT[data["status"]]
        order_time = data.get('order_time')
        order = OrderData(
            symbol=symbol,
            exchange=exchange,
            orderid=self.ID_TIGER2VT.get(str(data["id"]),
                                         self.get_new_local_id()),
            direction=DIRECTION_TIGER2VT[data.get('action')],
            price=data.get("limit_price", 0),
            volume=data["quantity"],
            traded=data["filled"],
            status=status,
            time=datetime.fromtimestamp(order_time / 1000).strftime("%H:%M:%S")
            if order_time else datetime.now().strftime("%H:%M:%S"),
            gateway_name=self.gateway_name,
        )
        self.ID_TIGER2VT[str(data["id"])] = order.orderid
        self.on_order(order)

        if status == Status.ALLTRADED:
            self.tradeid += 1

            trade = TradeData(
                symbol=symbol,
                exchange=exchange,
                direction=DIRECTION_TIGER2VT[data.get('action')],
                tradeid=self.tradeid,
                orderid=self.ID_TIGER2VT[str(data["id"])],
                price=data["avg_fill_price"],
                volume=data["filled"],
                time=datetime.fromtimestamp(order_time /
                                            1000).strftime("%H:%M:%S")
                if order_time else datetime.now().strftime("%H:%M:%S"),
                gateway_name=self.gateway_name,
            )
            self.on_trade(trade)

    def get_new_local_id(self):
        self.local_id += 1
        return self.local_id

    def send_order(self, req: OrderRequest):
        """"""
        local_id = self.get_new_local_id()
        order = req.create_order_data(local_id, self.gateway_name)

        self.on_order(order)
        self.add_task(self._send_order, req, local_id)
        return order.vt_orderid

    def _send_order(self, req: OrderRequest, local_id):
        """"""
        try:  # 主要处理一些API层面的校验带来的异常,如下单价格错误。
            contract = self.get_trading_contract(req.symbol)
            order = self.trade_client.create_order(
                account=self.account,
                contract=contract,
                action=DIRECTION_VT2TIGER[req.direction],
                order_type=ORDERTYPE_VT2TIGER[req.type],
                quantity=int(req.volume),
                limit_price=round(req.price, 2),
            )

            self.trade_client.place_order(order)
            self.ID_TIGER2VT[str(order.id)] = local_id
            self.ID_VT2TIGER[local_id] = str(order.id)
        except Exception:
            # 一些订单会在API层面被拒掉,不会推送订单的回报,这里模拟一个订单回报。
            # 目前gateway 只实现了mkt 和limit 两种类型的订单,使用其他类型的订单也会出现异常。
            # 这时本地的订单已经存在了, 所以返回一个状态为rejected的虚拟订单
            symbol = contract2symbol(order.contract)
            symbol, exchange = self.get_vt_symbol_exchange(symbol)

            mock_order = OrderData(
                symbol=symbol,
                exchange=exchange,
                orderid=local_id,
                direction=DIRECTION_TIGER2VT[order.action],
                price=order.limit_price,
                volume=order.quantity,
                traded=0,
                status=Status.REJECTED,
                time=datetime.now().strftime("%H:%M:%S"),
                gateway_name=self.gateway_name,
            )
            self.on_order(mock_order)
            self.write_log("发单失败")
            traceback.print_exc()

    def cancel_order(self, req: CancelRequest):
        """"""
        self.add_task(self._cancel_order, req)

    def _cancel_order(self, req: CancelRequest):
        """"""
        try:
            id = self.ID_VT2TIGER[req.orderid]
            data = self.trade_client.cancel_order(id=id)
            if not data:
                self.write_log("撤单成功")
        except ApiException:
            self.write_log(f"撤单请求提交失败:{req.orderid}")
            traceback.print_exc()
        except Exception:
            self.write_log('撤单失败,id:%s' % (req.orderid))

    def query_contract(self):
        """"""
        self.write_log('开始查询合约信息')
        # HK Stock
        # 查询的速度太慢了, 注释掉了
        try:
            symbols_names_HK = self.quote_client.get_symbol_names(
                lang=Language.zh_CN, market=Market.HK)
            contract_names_HK = DataFrame(symbols_names_HK,
                                          columns=["symbol", "name"])

            contractList = list(contract_names_HK["symbol"])
            i, n = 0, len(contractList)
            result = DataFrame()
            while i < n:
                i += 50
                c = contractList[i - 50:i]
                r = self.quote_client.get_trade_metas(c)
                result = result.append(r)
                sleep(0.1)
        except:
            self.write_log('查询港股合约失败')

        contract_detail_HK = result.sort_values(by="symbol", ascending=True)
        contract_HK = merge(contract_names_HK,
                            contract_detail_HK,
                            how="left",
                            on="symbol")

        for ix, row in contract_HK.iterrows():
            contract = ContractData(
                symbol=row["symbol"],
                exchange=Exchange.SEHK,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                min_volume=row["lot_size"],
                pricetick=row["min_tick"],
                net_position=True,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.contracts[contract.vt_symbol] = contract
            self.vt_tiger_symbol_map.update({
                contract.symbol:
                (Exchange.SEHK, stock_contract(contract.symbol,
                                               currency='HKD'))
            })

        # US Stock
        symbols_names_US = self.quote_client.get_symbol_names(
            lang=Language.zh_CN, market=Market.US)
        contract_US = DataFrame(symbols_names_US, columns=["symbol", "name"])

        for ix, row in contract_US.iterrows():
            contract = ContractData(
                symbol=row["symbol"],
                exchange=Exchange.SMART,
                name=row["name"],
                product=Product.EQUITY,
                size=1,
                min_volume=1,
                pricetick=0.001,
                gateway_name=self.gateway_name,
            )
            self.on_contract(contract)
            self.vt_tiger_symbol_map.update({
                contract.symbol:
                (Exchange.SMART, stock_contract(contract.symbol,
                                                currency='USD'))
            })
            self.contracts[contract.vt_symbol] = contract
        self.write_log('初始化美股合约完成')

        # Future contracts
        exchanges = self.quote_client.get_future_exchanges(
            sec_type=SecurityType.FUT, lang=Language.zh_CN)
        exchanges_list = exchanges['code']
        contract_futures = DataFrame()
        for e in exchanges_list:
            exchange_contract = self.quote_client.get_future_contracts(
                e, lang=Language.zh_CN)
            if len(exchange_contract) != 0:
                contract_futures = contract_futures.append(exchange_contract)

        for ix, row in contract_futures.iterrows():
            contract = ContractData(
                # symbol 用于查询存储数据 NQ1909
                symbol=row.loc['contract_code'],
                exchange=config_future_exchange(row.loc['exchange']),
                name=row.loc['name'],
                product=Product.FUTURES,
                size=1,
                min_volume=1,
                pricetick=0.001,
                gateway_name=self.gateway_name)
            self.on_contract(contract)
            self.vt_tiger_symbol_map.update({
                contract.symbol: (contract.exchange,
                                  future_contract(
                                      symbol=row.type,
                                      currency=row.currency,
                                      expiry=row.last_trading_date,
                                      exchange=row.exchange,
                                      multiplier=row.multiplier,
                                  ))
            })
            self.contracts[contract.vt_symbol] = contract
        self.write_log('初始化期货合约完成')

    def query_account(self):
        self.write_log('开始查询账户信息')
        try:
            assets = self.trade_client.get_assets(segment=True)
        except ApiException:
            self.write_log("查询资金失败")
            return

        for i in assets:
            account = AccountData(
                accountid=self.account,
                balance=round(i.summary.net_liquidation, 2),
                frozen=0.0,
                gateway_name=self.gateway_name,
            )
            # 下面两个账户仅作vntrader 展示使用。
            # 环球账户的资产信息可以通过 summary 获取,标准账户需要区分股票和期货Segment, 且没有合并的summary信息。
            sec_account = AccountData(
                accountid='Security',
                balance=round(i.segments.get('S').net_liquidation, 2),
                frozen=0.0,
                gateway_name=self.gateway_name,
            )

            com_account = AccountData(
                accountid='Commodity',
                balance=round(i.segments.get('C').net_liquidation, 2),
                frozen=0.0,
                gateway_name=self.gateway_name,
            )

            self.on_account(account)
            self.on_account(sec_account)
            self.on_account(com_account)
        self.write_log('账户信息查询完成')

    def query_position(self):
        """"""
        self.write_log('开始查询持仓信息')
        try:
            # 分别查询股票和期货的持仓
            stock_position = self.trade_client.get_positions(
                sec_type=SecurityType.STK)
            future_position = self.trade_client.get_positions(
                sec_type=SecurityType.FUT)
            positions = stock_position + future_position

        except ApiException:
            self.write_log("查询持仓失败")
            return

        for i in positions:
            try:
                # 标准账户里面的symbol 是origin symbol
                symbol, exchange = self.get_vt_symbol_exchange(
                    contract2symbol(i.contract))

                pos = PositionData(
                    symbol=symbol,
                    exchange=exchange,
                    direction=Direction.NET,
                    volume=int(i.quantity),
                    frozen=0.0,
                    price=i.average_cost,
                    pnl=float(i.unrealized_pnl),
                    gateway_name=self.gateway_name,
                )

                self.on_position(pos)
            except:
                self.write_log('处理持仓失败,symbol: %s' % (i.contract.symbol))
        self.write_log('持仓信息查询完成')

    def query_order(self):
        self.write_log('开始查询历史订单信息')
        try:
            # 需要分别查询股票和期货的订单
            stock_data = self.trade_client.get_orders(
                account=self.account, sec_type=SecurityType.STK)
            future_data = self.trade_client.get_orders(
                account=self.account, sec_type=SecurityType.FUT)
            data = stock_data + future_data
            data = sorted(data, key=lambda x: x.order_time, reverse=False)
        except:
            traceback.print_exc()
            self.write_log("查询订单失败")
            return

        self.process_order(data)
        self.process_deal(data)
        self.write_log('历史订单处理完成')

    def close(self):
        """"""
        self.active = False

        if self.push_client:
            # 退出前先进行退订操作,避免下次打开时的订阅异常
            try:
                self.push_client.unsubscribe_asset()
                self.push_client.unsubscribe_position()
                self.push_client.unsubscribe_order()
                self.push_client.unsubscribe_quote(
                    symbols=self.subscribed_symbols)
                self.push_client.disconnect()
            except:
                pass

    def process_order(self, data):
        """"""
        for i in data:
            try:
                symbol = contract2symbol(i.contract)
                symbol, exchange = self.get_vt_symbol_exchange(symbol)
                local_id = self.get_new_local_id()

                order = OrderData(
                    symbol=symbol,
                    exchange=exchange,
                    orderid=local_id,
                    direction=DIRECTION_TIGER2VT[i.action],
                    price=i.limit_price if i.limit_price else 0.0,
                    volume=i.quantity,
                    traded=i.filled,
                    status=STATUS_TIGER2VT[i.status],
                    time=datetime.fromtimestamp(i.order_time /
                                                1000).strftime("%H:%M:%S"),
                    gateway_name=self.gateway_name,
                )
                self.ID_TIGER2VT[str(i.id)] = local_id
                self.on_order(order)
            except:
                pass

        self.ID_VT2TIGER = {v: k for k, v in self.ID_TIGER2VT.items()}

    def process_deal(self, data):
        """
        Process trade data for both query and update.
        """
        for i in data:
            if i.status == OrderStatus.PARTIALLY_FILLED or i.status == OrderStatus.FILLED:
                try:
                    symbol = contract2symbol(i.contract)
                    symbol, exchange = self.get_vt_symbol_exchange(symbol)
                    self.tradeid += 1

                    trade = TradeData(
                        symbol=symbol,
                        exchange=exchange,
                        direction=DIRECTION_TIGER2VT[i.action],
                        tradeid=self.tradeid,
                        orderid=self.ID_TIGER2VT[str(i.id)],
                        price=i.avg_fill_price,
                        volume=i.filled,
                        time=datetime.fromtimestamp(i.trade_time /
                                                    1000).strftime("%H:%M:%S"),
                        gateway_name=self.gateway_name,
                    )

                    self.on_trade(trade)
                except:
                    pass

    def get_vt_symbol_exchange(self, symbol):
        try:
            exchange = self.vt_tiger_symbol_map.get(symbol)[0]
        except:
            self.write_log('can not get symbol %s' % (symbol))

        return symbol, exchange

    def get_trading_contract(self, symbol):
        """用于下单时获取交易合约
        :param symbol:
        :return:
        """
        try:
            return self.vt_tiger_symbol_map.get(symbol)[1]
        except:
            self.write_log('cannot get traidng contract for symbol %s' %
                           (symbol))