import logging

# 配置日志
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')


import py_xlt_api as xlight
import threading


class MySpi(xlight.XLTTradeSpi):
    def __init__(self, xapi=None, next_request_id_func=None):
        xlight.XLTTradeSpi.__init__(self)
        self.last_login_session_id = 0
        self.login_event = threading.Event()
        self.xapi = xapi
        self.next_request_id_func = next_request_id_func

    def on_disconnect(self, session_id: int):
        """
        断线回调
        断线之后触发回调，用户需要主动重新login
        :param session_id 会话ID。
        :return None
        """
        logging.info("[on_disconnect]: {}".format(session_id))

    def on_login(self, session_id: int, request_id: int, error_info: xlight.xlt_error_info_t):
        """
        登录回调
        :param session_id 本次登录生成的会话ID。如果登录失败则为0。
        :param request_id 此消息响应函数对应的请求ID。
        :param error_info 登录请求发生错误时返回的错误信息，当error_info为空，或者error_info.error_code为0时，表明没有错误。
        :return None
        """
        if error_info and error_info.error_code != 0:
            logging.error("[on_login][request_id={}]Login is failed! error_code={}, sub_error_code={}, error_msg={}"
                         .format(request_id, error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            self.last_login_session_id = 0
        else:
            logging.info("[on_login][request_id={}]Login is success! session_id={}"
                         .format(request_id, session_id))
            self.last_login_session_id = session_id
        self.login_event.set()

    def on_order_end(self, session_id: int, order_end: xlight.xlt_order_end_t):
        """
        订单状态通知
        :param session_id 会话ID。
        :param order_end 订单结束通知
        :return None
        """
        logging.info("on_order_end session_id: {}, sequence: {}, xid: {}, report_num: {}".format(
            session_id,
            order_end.sequence,
            order_end.xid,
            int(order_end.report_num)
        ))

    def on_order_response(self, session_id: int, order_response: xlight.xlt_order_response_t):
        """
        申报成功响应
        :param session_id 会话ID。
        :param order_response 申报成功响应
        :return None
        """
        logging.info("on_order_response session_id: {}, sequence: {}, report_id: {}, xid: {}, order_status: {}, "
                     "leaves_quantity: {}, cancel_quantity: {}".format(
            session_id,
            order_response.sequence,
            order_response.report_id,
            order_response.xid,
            int(order_response.order_status),
            order_response.leaves_quantity,
            order_response.cancel_quantity
        ))

    def on_cancel_response(self, session_id: int, cancel_response: xlight.xlt_cancel_response_t):
        """
        撤单成功响应
        :param session_id 会话ID。
        :param cancel_response 撤单成功响应
        :return None
        """
        logging.info("on_cancel_response session_id: {}, sequence: {}, report_id: {}, xid: {}, origin_xid: {}, "
                     "order_status: {}, cancel_quantity: {}".format(
            session_id,
            cancel_response.sequence,
            cancel_response.report_id,
            cancel_response.xid,
            cancel_response.origin_xid,
            int(cancel_response.origin_order_status),
            cancel_response.cancel_quantity
        ))

    def on_trade_report(self, session_id: int, trade_report: xlight.xlt_trade_report_t):
        """
        成交回报通知
        :param session_id: 会话ID
        :param trade_report: 成交回报
        :return:
        """
        logging.info("on_trade_report session_id: {}, sequence: {}, report_id: {}, xid: {}, order_status: {}, price: {}, "
                     "quantity: {}, leaves_quantity: {}".format(
            session_id,
            trade_report.sequence,
            trade_report.report_id,
            trade_report.xid,
            int(trade_report.order_status),
            trade_report.price,
            trade_report.quantity,
            trade_report.leaves_quantity
        ))

    def on_order_error_response(self, session_id: int, order_error_response: xlight.xlt_order_error_response_t):
        """
        委托失败通知（新订单委托失败和撤单失败）
        :param session_id: 会话ID
        :param order_error_response: 委托失败通知
        :return:
        """
        logging.info("on_order_error_response session_id: {}, sequence: {}, report_id: {}, xid: {}, origin_xid: {}, "
                     "error_code: {}, sub_error_code: {}, error_msg: {}".format(
            session_id,
            order_error_response.sequence,
            order_error_response.report_id,
            order_error_response.xid,
            order_error_response.origin_xid,
            order_error_response.error_info.error_code,
            order_error_response.error_info.sub_error_code,
            order_error_response.error_info.error_msg
        ))

    def on_raw_report(self, session_id: int, client_id: int, xid: int, report_id: bytes, sequence: int, exchange_index: int, report_size: int, raw_report: bytes):
        """
        原始回报通知
        raw_report为交易所原始报文，需要用户根据交易所报文规范进行解析
        :param session_id: 会话ID
        :param client_id: 委托源标识，用户登录时自定义
        :param xid: 报单标识
        :param report_id: 回报标识 (bytes, 32字节)
        :param sequence: 消息序号(大于0有效)
        :param exchange_index: 市场
        :param report_size: 回报长度
        :param raw_report: 原始回报 (bytes)
        :return:
        """
        logging.info("on_raw_report session_id: {}, exchange_index: {}, report_size: {}, raw_report_len: {}, raw_report_type: {}".format(
            session_id,
            int(exchange_index),
            report_size,
            len(raw_report) if raw_report else 0,
            type(raw_report)
        ))

    def on_query_positions(self, session_id: int, request_id: int, positions: xlight.xlt_position_info_t, data_count: int, is_last: bool, error_info: xlight.xlt_error_info_t):
        """
        持仓信息查询响应
        :param session_id 会话id，登录时得到
        :param request_id 此消息响应函数对应的请求ID
        :param positions 本次响应返回的持仓信息列表，个数由data_count决定
        :param data_count 本次响应返回的持仓信息个数
        :param is_last 此消息响应函数是否为request_id这条请求所对应的最后一个响应，当为最后一个的时候为true，如果为false，表示还有其他后续消息响应
        :param error_info 查询账户持仓发生错误时返回的错误信息，当error_info为空，或者error_info.error_code为0时，表明没有错误
        """
        logging.info("on_query_positions session_id: {} request_id: {} data_count: {} is_last: {}".format(
            session_id, request_id, data_count, is_last))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        py_positions = xlight.PositionArray.frompointer(positions)
        for i in range(data_count):
            pos:xlight.xlt_position_info_t = py_positions[i]
            logging.info("Position {}: security_code: {}, position_security_type: {}, exchange_index: {}, "
                         "initial_quantity: {}, total_quantity: {}, sellable_quantity: {}, frozen_quantity: {}".format(
                i + 1,
                pos.security_code,
                int(pos.position_security_type),
                int(pos.exchange_index),
                pos.initial_quantity,
                pos.total_quantity,
                pos.sellable_quantity,
                pos.frozen_quantity
            ))

    def on_query_assets(self, session_id: int, request_id: int, assets: xlight.xlt_asset_info_t, data_count: int, is_last: bool, error_info: xlight.xlt_error_info_t):
        """
        资产查询响应
        :param session_id 会话id，登录时得到
        :param request_id 此消息响应函数对应的请求ID
        :param assets 本次响应返回的资金信息列表，个数由data_count决定
        :param data_count 本次响应返回的资金信息个数
        :param is_last 此消息响应函数是否为request_id这条请求所对应的最后一个响应，当为最后一个的时候为true，如果为false，表示还有其他后续消息响应
        :param error_info 查询账户资产发生错误时返回的错误信息，当error_info为空，或者error_info.error_code为0时，表明没有错误
        """
        logging.info("on_query_assets session_id: {} request_id: {} data_count: {} is_last: {}".format(
            session_id, request_id, data_count, is_last))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        py_assets = xlight.AssetArray.frompointer(assets)
        for i in range(data_count):
            asset:xlight.xlt_asset_info_t = py_assets[i]
            logging.info("Asset {}: available_amount: {}, initial_amount: {}, withholding_amount: {}, fund_buy_amount: {}, "
                         "fund_buy_fee: {}, fund_sell_amount: {}, fund_sell_fee: {}".format(
                i + 1,
                asset.available_amount,
                asset.initial_amount,
                asset.withholding_amount,
                asset.fund_buy_amount,
                asset.fund_buy_fee,
                asset.fund_sell_amount,
                asset.fund_sell_fee
            ))


    def on_query_orders(self, session_id: int, request_id: int, order_infos: xlight.xlt_order_info_t, data_count: int, is_last: bool, error_info: xlight.xlt_error_info_t):
        """
        查询报单响应
        :param session_id 会话id，登录时得到
        :param request_id 此消息响应函数对应的请求ID
        :param order_infos 本次响应返回的订单信息，个数由data_count决定
        :param data_count 本次响应返回的数据条数
        :param is_last 此消息响应函数是否为request_id这条请求所对应的最后一个响应，当为最后一个的时候为true，如果为false，表示还有其他后续消息响应
        :param error_info 查询账户资产发生错误时返回的错误信息，当error_info为空，或者error_info.error_code为0时，表明没有错误
        """
        logging.info("on_query_orders session_id: {} request_id: {} data_count: {} is_last: {}".format(
            session_id, request_id, data_count, is_last))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        py_orders = xlight.OrderArray.frompointer(order_infos)
        for i in range(data_count):
            order:xlight.xlt_order_info_t = py_orders[i]
            logging.info("Order {}: xid: {}, origin_xid: {}, order_status: {}, security_code: {}, "
                         "exchange_index: {}, side: {}, business_type: {}, order_price: {}, "
                         "order_quantity: {}, order_amount: {}, order_fee: {}, traded_quantity: {}, traded_amount: {}, "
                         "traded_fee: {}, traded_fix_fee: {}, traded_count: {}, leaves_quantity: {}, cancel_quantity: {}, "
                         "insert_time: {}, traded_time: {}, error_code: {}, sub_error_code: {}, error_msg: {}".format(
                i + 1,
                order.xid,
                order.origin_xid,
                int(order.order_status),
                order.security_code,
                int(order.exchange_index),
                int(order.side),
                int(order.business_type),
                order.order_price,
                order.order_quantity,
                order.order_amount,
                order.order_fee,
                order.trade_quantity,
                order.trade_amount,
                order.trade_fee,
                order.trade_fix_fee,
                order.trade_count,
                order.leaves_quantity,
                order.cancel_quantity,
                order.insert_time,
                order.trade_time,
                order.error_info.error_code,
                order.error_info.sub_error_code,
                order.error_info.error_msg
            ))

    def on_query_orders_by_page(self, session_id: int, request_id: int, order_infos: xlight.xlt_order_info_t, data_count: int, req_count: int, order_sequence: int,
                                query_reference: int, is_last: bool, error_info: xlight.xlt_error_info_t):
        """
        查询报单响应
        :param session_id 会话id，登录时得到
        :param request_id 此消息响应函数对应的请求ID
        :param order_infos 本次响应返回的订单信息，个数由data_count决定
        :param data_count 本次响应返回的数据条数
        :param req_count 分页请求的最大数量
        :param order_sequence 分页请求的当前回报数量
        :param query_reference 当前响应最后一条报单信息所对应的查询索引，需要记录下来，在进行下一次分页查询的时候需要用到
        :param is_last 此消息响应函数是否为request_id这条请求所对应的最后一个响应，当为最后一个的时候为true，如果为false，表示还有其他后续消息响应
        :param error_info 查询账户资产发生错误时返回的错误信息，当error_info为空，或者error_info.error_code为0时，表明没有错误
        """
        logging.info("on_query_orders_by_page session_id: {} request_id: {} data_count: {} req_count: {}, rsp_count: {}, "
                     "query_reference: {} is_last: {}".format(
            session_id, request_id, data_count, req_count, order_sequence, query_reference, is_last))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        py_orders = xlight.OrderArray.frompointer(order_infos)
        for i in range(data_count):
            order:xlight.xlt_order_info_t = py_orders[i]
            logging.info("Order {}: xid: {}, origin_xid: {}, order_status: {}, security_code: {}, "
                         "exchange_index: {}, side: {}, business_type: {}, order_price: {}, "
                         "order_quantity: {}, order_amount: {}, order_fee: {}, traded_quantity: {}, traded_amount: {}, "
                         "traded_fee: {}, traded_fix_fee: {}, traded_count: {}, leaves_quantity: {}, cancel_quantity: {}, "
                         "insert_time: {}, traded_time: {}, error_code: {}, sub_error_code: {}, error_msg: {}".format(
                i + 1,
                order.xid,
                order.origin_xid,
                int(order.order_status),
                order.security_code,
                int(order.exchange_index),
                int(order.side),
                int(order.business_type),
                order.order_price,
                order.order_quantity,
                order.order_amount,
                order.order_fee,
                order.trade_quantity,
                order.trade_amount,
                order.trade_fee,
                order.trade_fix_fee,
                order.trade_count,
                order.leaves_quantity,
                order.cancel_quantity,
                order.insert_time,
                order.trade_time,
                order.error_info.error_code,
                order.error_info.sub_error_code,
                order.error_info.error_msg
            ))

        if is_last:
            # 当页请求返回结束
            if order_sequence < req_count:
                # 当页返回数量小于请求数量，说明已经查询到最后的数据
                logging.info("All orders got!")
            else:
                # 可能还有数据，继续查询
                if self.xapi and self.next_request_id_func:
                    order_query_by_page_param = xlight.xlt_query_by_page_param_t()
                    order_query_by_page_param.req_count = 10
                    order_query_by_page_param.reference = query_reference
                    if self.xapi.query_orders_by_page(self.last_login_session_id, self.next_request_id_func(), order_query_by_page_param) > 0:
                        logging.error("Query orders info request send failed!!")
        else:
            # 当页请求未结束，还有后续数据
            logging.info("More orders to come, waiting for next page...")

    def on_query_trades(self, session_id: int, request_id: int, trade_reports: xlight.xlt_trade_report_t, data_count: int, is_last: bool, error_info: xlight.xlt_error_info_t):
        """
        查询成交响应
        :param session_id 会话id，登录时得到
        :param request_id 此消息响应函数对应的请求ID
        :param trade_reports 本次响应返回的成交信息，个数由data_count决定
        :param data_count 本次响应返回的数据条数
        :param is_last 此消息响应函数是否为request_id这条请求所对应的最后一个响应，当为最后一个的时候为true，如果为false，表示还有其他后续消息响应
        :param error_info 查询账户资产发生错误时返回的错误信息，当error_info为空，或者error_info.error_code为0时，表明没有错误
        """
        logging.info("on_query_trades session_id: {} request_id: {} data_count: {} is_last: {}".format(
            session_id, request_id, data_count, is_last))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        py_trades = xlight.TradeArray.frompointer(trade_reports)
        for i in range(data_count):
            trade:xlight.xlt_trade_report_t = py_trades[i]
            logging.info("Trade {}: sequence: {}, report_id: {}, xid: {}, order_status: {}, price: {}, "
                         "quantity: {}, leaves_quantity: {}".format(
                i + 1,
                trade.sequence,
                trade.report_id,
                trade.xid,
                int(trade.order_status),
                trade.price,
                trade.quantity,
                trade.leaves_quantity
            ))

    def on_query_static_quote_info(self, session_id: int, request_id: int, static_quote_full_infos: xlight.xlt_static_quote_full_info_t, data_count: int, is_last: bool, error_info: xlight.xlt_error_info_t):
        """
        静态行情查询响应
        :param session_id:
        :param request_id:
        :param static_quote_full_infos:
        :param data_count:
        :param is_last:
        :param error_info:
        :return:
        """
        logging.info("on_query_static_quote_info session_id: {} request_id: {} data_count: {} is_last: {}".format(
            session_id, request_id, data_count, is_last))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        py_infos = xlight.StaticQuoteInfoArray.frompointer(static_quote_full_infos)
        for i in range(data_count):
            info: xlight.xlt_static_quote_full_info_t = py_infos[i]
            logging.info("Static info: {}, "
                         "exchange_index: {}, "
                         "security_code: {}, "
                         "security_name: {}, "
                         "security_type: {}, "
                         "is_registration: {}, "
                         "is_vie: {}, "
                         "is_noprofit: {}, "
                         "is_weighted_voting_rights: {}, "
                         "is_have_price_limit: {}, "
                         "upper_limit_price: {}, "
                         "lower_limit_price: {}, "
                         "pre_close_price: {}, "
                         "price_tick: {}, "
                         "bid_qty_upper_limit: {}, "
                         "bid_qty_lower_limit: {}, "
                         "bid_qty_unit: {}, "
                         "ask_qty_upper_limit: {}, "
                         "ask_qty_lower_limit: {}, "
                         "ask_qty_unit: {}, "
                         "market_bid_qty_upper_limit: {}, "
                         "market_bid_qty_lower_limit: {}, "
                         "market_bid_qty_unit: {}, "
                         "market_ask_qty_upper_limit: {}, "
                         "market_ask_qty_lower_limit: {}, "
                         "market_ask_qty_unit: {}, "
                         "security_status: {}".format(
                i,
                int(info.exchange_index),
                info.get_security_code(),
                info.get_security_name(),
                int(info.security_type),
                info.is_registration,
                info.is_vie,
                info.is_noprofit,
                info.is_weighted_voting_rights,
                info.is_have_price_limit,
                info.upper_limit_price,
                info.lower_limit_price,
                info.pre_close_price,
                info.price_tick,
                info.bid_qty_upper_limit,
                info.bid_qty_lower_limit,
                info.bid_qty_unit,
                info.ask_qty_upper_limit,
                info.ask_qty_lower_limit,
                info.ask_qty_unit,
                info.market_bid_qty_upper_limit,
                info.market_bid_qty_lower_limit,
                info.market_bid_qty_unit,
                info.market_ask_qty_upper_limit,
                info.market_ask_qty_lower_limit,
                info.market_ask_qty_unit,
                info.get_security_status()
            ))

    def on_query_sse_l1_index(self, session_id: int, request_id: int, sse_l1_index: xlight.sse_l1_index_t, error_info: xlight.xlt_error_info_t):
        """
        查询上证L1指数响应
        :param session_id:
        :param request_id:
        :param sse_l1_index:
        :param error_info:
        :return:
        """
        logging.info("on_query_sse_l1_index session_id: {} request_id: {}".format(
            session_id, request_id))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        if not sse_l1_index:
            return
        logging.info("security_type: {}".format(sse_l1_index.security_type))
        logging.info("sending_time: {}".format(sse_l1_index.sending_time))
        logging.info("trad_ses_mode: {}".format(sse_l1_index.trad_ses_mode))
        logging.info("trade_date: {}".format(sse_l1_index.trade_date))
        logging.info("last_update_time: {}".format(sse_l1_index.last_update_time))
        logging.info("md_stream_id: {}".format(sse_l1_index.get_md_stream_id()))
        logging.info("security_code: {}".format(sse_l1_index.get_security_code()))
        logging.info("prev_close_px: {}".format(sse_l1_index.prev_close_px))
        logging.info("total_volume_traded: {}".format(sse_l1_index.total_volume_traded))
        logging.info("num_trades: {}".format(sse_l1_index.num_trades))
        logging.info("total_value_traded: {}".format(sse_l1_index.total_value_traded))
        logging.info("trading_phase_code: {}".format(sse_l1_index.get_trading_phase_code()))
        logging.info("last_px: {}".format(sse_l1_index.last_px))
        logging.info("open_px: {}".format(sse_l1_index.open_px))
        logging.info("close_px: {}".format(sse_l1_index.close_px))
        logging.info("high_px: {}".format(sse_l1_index.high_px))
        logging.info("low_px: {}".format(sse_l1_index.low_px))

    def on_query_sse_l1_snapshot(self, session_id: int, request_id: int, sse_l1_snapshot: xlight.sse_l1_snapshot_t, error_info: xlight.xlt_error_info_t):
        """
        查询上证L1快照响应
        :param session_id:
        :param request_id:
        :param sse_l1_snapshot:
        :param error_info:
        :return:
        """
        logging.info("on_query_sse_l1_snapshot session_id: {} request_id: {}".format(
            session_id, request_id))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        if not sse_l1_snapshot:
            return
        logging.info("security_type: {}".format(sse_l1_snapshot.security_type))
        logging.info("sending_time: {}".format(sse_l1_snapshot.sending_time))
        logging.info("trad_ses_mode: {}".format(sse_l1_snapshot.trad_ses_mode))
        logging.info("trade_date: {}".format(sse_l1_snapshot.trade_date))
        logging.info("last_update_time: {}".format(sse_l1_snapshot.last_update_time))
        logging.info("md_stream_id: {}".format(sse_l1_snapshot.get_md_stream_id()))
        logging.info("security_code: {}".format(sse_l1_snapshot.get_security_code()))
        logging.info("prev_close_px: {}".format(sse_l1_snapshot.prev_close_px))
        logging.info("total_volume_traded: {}".format(sse_l1_snapshot.total_volume_traded))
        logging.info("num_trades: {}".format(sse_l1_snapshot.num_trades))
        logging.info("total_value_traded: {}".format(sse_l1_snapshot.total_value_traded))
        logging.info("trading_phase_code: {}".format(sse_l1_snapshot.get_trading_phase_code()))
        logging.info("last_px: {}".format(sse_l1_snapshot.last_px))
        logging.info("open_px: {}".format(sse_l1_snapshot.open_px))
        logging.info("close_px: {}".format(sse_l1_snapshot.close_px))
        logging.info("settle_px: {}".format(sse_l1_snapshot.settle_px))
        logging.info("high_px: {}".format(sse_l1_snapshot.high_px))
        logging.info("low_px: {}".format(sse_l1_snapshot.low_px))
        logging.info("iopv: {}".format(sse_l1_snapshot.iopv))
        logging.info("pre_iopv: {}".format(sse_l1_snapshot.pre_iopv))
        logging.info("option_ref_px: {}".format(sse_l1_snapshot.option_ref_px))
        logging.info("option_notional_amount: {}".format(sse_l1_snapshot.option_notional_amount))
        logging.info("pre_settle_px: {}".format(sse_l1_snapshot.pre_settle_px))
        logging.info("option_open_qty: {}".format(sse_l1_snapshot.option_open_qty))
        for i in range(5):
            logging.info("bid_px[{}]: {}".format(i, sse_l1_snapshot.get_bid_px(i)))
            logging.info("bid_volume[{}]: {}".format(i, sse_l1_snapshot.get_bid_volume(i)))
        for i in range(5):
            logging.info("offer_px[{}]: {}".format(i, sse_l1_snapshot.get_offer_px(i)))
            logging.info("offer_volume[{}]: {}".format(i, sse_l1_snapshot.get_offer_volume(i)))

    def on_query_szse_l1_index(self, session_id: int, request_id: int, szse_l1_index: xlight.szse_l1_index_t, error_info: xlight.xlt_error_info_t):
        """
        查询深证L1指数响应
        :param session_id:
        :param request_id:
        :param szse_l1_index:
        :param error_info:
        :return:
        """
        logging.info("on_query_szse_l1_index session_id: {} request_id: {}".format(
            session_id, request_id))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        if not szse_l1_index:
            return
        logging.info("channel_no: {}".format(szse_l1_index.channel_no))
        logging.info("orig_time: {}".format(szse_l1_index.orig_time))
        logging.info("md_stream_id: {}".format(szse_l1_index.get_md_stream_id()))
        logging.info("security_code: {}".format(szse_l1_index.get_security_code()))
        logging.info("security_id_source: {}".format(szse_l1_index.get_security_id_source()))
        logging.info("trading_phase_code: {}".format(szse_l1_index.get_trading_phase_code()))
        logging.info("prev_close_px: {}".format(szse_l1_index.prev_close_px))
        logging.info("num_trades: {}".format(szse_l1_index.num_trades))
        logging.info("total_volume_trade: {}".format(szse_l1_index.total_volume_trade))
        logging.info("total_value_trade: {}".format(szse_l1_index.total_value_trade))
        logging.info("last_index: {}".format(szse_l1_index.last_index))
        logging.info("prev_close_index: {}".format(szse_l1_index.prev_close_index))
        logging.info("open_index: {}".format(szse_l1_index.open_index))
        logging.info("high_index: {}".format(szse_l1_index.high_index))
        logging.info("low_index: {}".format(szse_l1_index.low_index))
        logging.info("close_index: {}".format(szse_l1_index.close_index))

    def on_query_szse_l1_snapshot(self, session_id: int, request_id: int, szse_l1_snapshot: xlight.szse_l1_snapshot_t, error_info: xlight.xlt_error_info_t):
        """
        查询深证L1快照响应
        :param session_id:
        :param request_id:
        :param szse_l1_snapshot:
        :param error_info:
        :return:
        """
        logging.info("on_query_szse_l1_snapshot session_id: {} request_id: {}".format(
            session_id, request_id))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        if not szse_l1_snapshot:
            return
        logging.info("channel_no: {}".format(szse_l1_snapshot.channel_no))
        logging.info("orig_time: {}".format(szse_l1_snapshot.orig_time))
        logging.info("md_stream_id: {}".format(szse_l1_snapshot.get_md_stream_id()))
        logging.info("security_code: {}".format(szse_l1_snapshot.get_security_code()))
        logging.info("security_id_source: {}".format(szse_l1_snapshot.get_security_id_source()))
        logging.info("trading_phase_code: {}".format(szse_l1_snapshot.get_trading_phase_code()))
        logging.info("prev_close_px: {}".format(szse_l1_snapshot.prev_close_px))
        logging.info("num_trades: {}".format(szse_l1_snapshot.num_trades))
        logging.info("total_volume_trade: {}".format(szse_l1_snapshot.total_volume_trade))
        logging.info("total_value_trade: {}".format(szse_l1_snapshot.total_value_trade))
        logging.info("last_px: {}".format(szse_l1_snapshot.last_px))
        logging.info("open_px: {}".format(szse_l1_snapshot.open_px))
        logging.info("high_px: {}".format(szse_l1_snapshot.high_px))
        logging.info("low_px: {}".format(szse_l1_snapshot.low_px))
        logging.info("px_gain: {}".format(szse_l1_snapshot.px_gain))
        logging.info("px_gain_2: {}".format(szse_l1_snapshot.px_gain_2))
        logging.info("weighted_avg_bid_px: {}".format(szse_l1_snapshot.weighted_avg_bid_px))
        logging.info("total_bid_qty: {}".format(szse_l1_snapshot.total_bid_qty))
        logging.info("weighted_avg_offer_px: {}".format(szse_l1_snapshot.weighted_avg_offer_px))
        logging.info("total_offer_qty: {}".format(szse_l1_snapshot.total_offer_qty))
        logging.info("pe_ratio_1: {}".format(szse_l1_snapshot.pe_ratio_1))
        logging.info("pe_ratio_2: {}".format(szse_l1_snapshot.pe_ratio_2))
        logging.info("nav: {}".format(szse_l1_snapshot.nav))
        logging.info("iopv: {}".format(szse_l1_snapshot.iopv))
        logging.info("warrant_premium: {}".format(szse_l1_snapshot.warrant_premium))
        logging.info("limit_up_px: {}".format(szse_l1_snapshot.limit_up_px))
        logging.info("limit_down_px: {}".format(szse_l1_snapshot.limit_down_px))
        logging.info("option_open_qty: {}".format(szse_l1_snapshot.option_open_qty))
        logging.info("option_break_px: {}".format(szse_l1_snapshot.option_break_px))
        for i in range(5):
            logging.info("bid_px[{}]: {}".format(i, szse_l1_snapshot.get_bid_px(i)))
            logging.info("bid_volume[{}]: {}".format(i, szse_l1_snapshot.get_bid_volume(i)))
        for i in range(5):
            logging.info("offer_px[{}]: {}".format(i, szse_l1_snapshot.get_offer_px(i)))
            logging.info("offer_volume[{}]: {}".format(i, szse_l1_snapshot.get_offer_volume(i)))

    def on_query_szse_l1_snapshot_bond(self, session_id: int, request_id: int, szse_l1_snapshot_bond: xlight.szse_l1_snapshot_bond_t, error_info: xlight.xlt_error_info_t):
        """
        查询深证L1债券快照响应
        :param session_id:
        :param request_id:
        :param szse_l1_snapshot_bond:
        :param error_info:
        :return:
        """
        logging.info("on_query_szse_l1_snapshot_bond session_id: {} request_id: {}".format(
            session_id, request_id))
        if error_info and error_info.error_code != 0:
            logging.error("Error: error_code: {}, sub_error_code: {}, error_msg: {}".format(
                error_info.error_code, error_info.sub_error_code, error_info.error_msg))
            return
        if not szse_l1_snapshot_bond:
            return
        logging.info("channel_no: {}".format(szse_l1_snapshot_bond.channel_no))
        logging.info("orig_time: {}".format(szse_l1_snapshot_bond.orig_time))
        logging.info("md_stream_id: {}".format(szse_l1_snapshot_bond.get_md_stream_id()))
        logging.info("security_code: {}".format(szse_l1_snapshot_bond.get_security_code()))
        logging.info("security_id_source: {}".format(szse_l1_snapshot_bond.get_security_id_source()))
        logging.info("trading_phase_code: {}".format(szse_l1_snapshot_bond.get_trading_phase_code()))
        logging.info("prev_close_px: {}".format(szse_l1_snapshot_bond.prev_close_px))
        logging.info("num_trades: {}".format(szse_l1_snapshot_bond.num_trades))
        logging.info("total_volume_trade: {}".format(szse_l1_snapshot_bond.total_volume_trade))
        logging.info("total_value_trade: {}".format(szse_l1_snapshot_bond.total_value_trade))
        logging.info("last_px: {}".format(szse_l1_snapshot_bond.last_px))
        logging.info("open_px: {}".format(szse_l1_snapshot_bond.open_px))
        logging.info("close_px: {}".format(szse_l1_snapshot_bond.close_px))
        logging.info("high_px: {}".format(szse_l1_snapshot_bond.high_px))
        logging.info("low_px: {}".format(szse_l1_snapshot_bond.low_px))
        logging.info("weighted_avg_px: {}".format(szse_l1_snapshot_bond.weighted_avg_px))
        logging.info("px_gain: {}".format(szse_l1_snapshot_bond.px_gain))
        logging.info("px_gain_2: {}".format(szse_l1_snapshot_bond.px_gain_2))
        logging.info("weighted_avg_bid_px: {}".format(szse_l1_snapshot_bond.weighted_avg_bid_px))
        logging.info("total_bid_qty: {}".format(szse_l1_snapshot_bond.total_bid_qty))
        logging.info("weighted_avg_offer_px: {}".format(szse_l1_snapshot_bond.weighted_avg_offer_px))
        logging.info("total_offer_qty: {}".format(szse_l1_snapshot_bond.total_offer_qty))
        logging.info("weighted_avg_ir_gain: {}".format(szse_l1_snapshot_bond.weighted_avg_ir_gain))
        logging.info("weighted_avg_prev_ir: {}".format(szse_l1_snapshot_bond.weighted_avg_prev_ir))
        logging.info("match_last_px: {}".format(szse_l1_snapshot_bond.match_last_px))
        logging.info("auction_volume_trade: {}".format(szse_l1_snapshot_bond.auction_volume_trade))
        logging.info("auction_value_trade: {}".format(szse_l1_snapshot_bond.auction_value_trade))
        for i in range(6):
            logging.info("sub_trading_phase_code[{}]: {}".format(i, szse_l1_snapshot_bond.get_sub_trading_phase_code(i)))
        for i in range(5):
            logging.info("bid_px[{}]: {}".format(i, szse_l1_snapshot_bond.get_bid_px(i)))
            logging.info("bid_volume[{}]: {}".format(i, szse_l1_snapshot_bond.get_bid_volume(i)))
        for i in range(5):
            logging.info("offer_px[{}]: {}".format(i, szse_l1_snapshot_bond.get_offer_px(i)))
            logging.info("offer_volume[{}]: {}".format(i, szse_l1_snapshot_bond.get_offer_volume(i)))

    def on_request_timeout(self, session_id: int, request_id: int):
        """
        请求超时回调
        :param session_id 会话ID。
        :param request_id 此消息响应函数对应的请求ID。
        :return None
        """
        logging.info("[on_request_timeout][session_id={}] request_id={}".format(session_id, request_id))

    def on_order_timeout(self, session_id: int, xid: int):
        """
        请求超时回调
        订单超时通知是指在指定时间内未收到订单的成交回报或状态更新时触发的通知，不代表委托失败，需要用户自行查询确认订单状态。
        :param session_id 会话ID。
        :param xid 超时的订单标识
        :return None
        """
        logging.info("[on_order_timeout][session_id={}] xid={}".format(session_id, xid))
