#include "xlt_quote_demo.h"

#include <cstdio>
#include <cstring>
#include <unistd.h>
#include "efh_quote_decoder.h"
#include "xlt_quote_decoder.h"

using namespace x1;
using namespace x1::efh;

XLTQuoteDemo::XLTQuoteDemo() {
    api_ = XLTQuoteApi::create();
}

const char* XLTQuoteDemo::api_version() const {
    return api_->get_api_version();
}

uint32_t XLTQuoteDemo::trading_day() const {
    return api_->get_trading_day();
}

uint64_t XLTQuoteDemo::next_request_id() {
    request_id_++;
    return request_id_.load();
}

bool XLTQuoteDemo::initialize() {
    // 配置
    XLTQuoteConfig config;
    config.set_trading_day(20260310);                   ///< （必填）设置交易日，格式YYYYMMDD，如20230901。用户需明确指定交易日，生产环境为当前交易日，测试环境为测试柜台指定的交易日。若与柜台交易日不一致，登录将报错。
    config.set_local_addr(                              ///< （必填）设置本地网卡IP地址。生产环境中，quote对应网卡建议为solarflare网卡，solarflare网卡将自动使用ef_vi，以便达到最优的性能，非solarflare网卡则回退到内核sock模式
        "192.168.24.170",                           ///< agw 连接agw的本地网卡IP，生产环境需为AGW接入网段IP
        "192.168.20.170"                           ///< quote 接收行情的本地网卡IP，生产环境需为行情网段IP
        );
    config.add_agw_addr("192.168.5.215", 6060);    ///< （必填）添加网关地址, ip_addr agw IP地址, port agw端口。请向券商获取agw地址和端口，可重复调用添加多个agw地址，每次登录会随机选取当前可用的agw
    config.set_use_level2(true);                        ///<  开启level2，默认不启用level2。
    // API初始化，在使用之前调用，只需调用一次。
    if (!api_->initialize(&config, this)) {
        auto error_info = api_->get_last_error();
        printf("initialize failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type,
                    error_info.error_code,
                    error_info.error_msg);
        return false;
    }
    printf("initialize success\n");
    return true;
}

bool XLTQuoteDemo::login() {
    if (session_id_ != 0) {
        api_->logout(session_id_);
    }
    session_id_ = 0;
    login_success_.store(false);
    login_finished_.store(false);

    // 登录账号信息
    const auto request_id = next_request_id();                ///< request_id，用于定位本次请求
    const uint8_t client_id = 1;                                      ///< 客户端标识ID（委托源ID），用户可自定义，取值为[0, 255]。
                                                                  ///< 在本次登录session中进行的委托以及回报，系统都将打上此client_id标记，用于用户进行委托隔离。
    char pub_key_base64[256] = "XXXXXXXXXXXXXX";                ///< 用户公钥(长度64字节)，v1.5暂未启用
    char account_name[CONST_ACC_NAME_LEN] = "88888888888801";   ///< 交易账号
    char password[256] = "12345678";                            ///< 登录密码

    // 登录请求
    const bool result = api_->login(request_id, client_id, account_name, password, pub_key_base64);
    if (!result) {
        auto error_info = api_->get_last_error();
        printf("login failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type,
                    error_info.error_code,
                    error_info.error_msg);
        return false;
    }

    // 等待登录响应
    while (!login_finished_.load()) {
        usleep(1000);
    }
    return login_success_.load();
}

bool XLTQuoteDemo::subscribe_quote() {
    quote_thread_t quote_threads[3] ={};

    // 行情接收线程一，绑定到CPU0，订阅XLIGHT上交所和深交所L1行情
    quote_threads[0].cpu_affinity = 3; // 绑定到CPU3
    quote_threads[0].busy_poll = false; // 纯忙等模式(仅efvi模式有效)，默认false。建议efvi模式且绑核时开启。
    quote_threads[0].mc_count = 1;
    // quote_threads[0].mc_types[0] = QuoteMCType::XLT_SSE_L1; // XLIGHT上交所L1行情
    quote_threads[0].mc_types[0] = QuoteMCType::XLT_SZSE_L1; // XLIGHT深交所L1行情

    // 行情接收线程二，绑定到CPU1，订阅盛立上交所level2行情
    quote_threads[1].cpu_affinity = 4; // 绑定到CPU4
    quote_threads[1].busy_poll = true; // 纯忙等模式(仅efvi模式有效)，默认false。建议efvi模式且绑核时开启。
    quote_threads[1].mc_count = 5;
    quote_threads[1].mc_types[0] = QuoteMCType::HPF_SSE_L2_STOCK_SNAP; // 盛立上交所L2股票快照頻道组播组
    quote_threads[1].mc_types[1] = QuoteMCType::HPF_SSE_L2_STOCK_TICK; // 盛立上交所L2股票逐笔頻道组播组
    quote_threads[1].mc_types[2] = QuoteMCType::HPF_SSE_L2_INDEX; // 盛立上交所L2指数快照頻道组播组
    quote_threads[1].mc_types[3] = QuoteMCType::HPF_SSE_L2_BOND_SNAP; // 盛立上交所L2债券快照頻道组播组
    quote_threads[1].mc_types[4] = QuoteMCType::HPF_SSE_L2_BOND_TICK; // 盛立上交所L2债券逐笔頻道组播组

    // // 行情接收线程三，绑定到CPU7，订阅盛立深交所level2行情
    quote_threads[2].cpu_affinity = 5; // 绑定到CPU5
    quote_threads[2].busy_poll = true; // 纯忙等模式(仅efvi模式有效)，默认false。建议efvi模式且绑核时开启。
    quote_threads[2].mc_count = 5;
    quote_threads[2].mc_types[0] = QuoteMCType::HPF_SZSE_L2_STOCK_SNAP; // 盛立深交所L2股票快照頻道组播组
    quote_threads[2].mc_types[1] = QuoteMCType::HPF_SZSE_L2_STOCK_TICK; // 盛立深交所L2股票逐笔頻道组播组
    quote_threads[2].mc_types[2] = QuoteMCType::HPF_SZSE_L2_INDEX; // 盛立深交所L2指数快照頻道组播组
    quote_threads[2].mc_types[3] = QuoteMCType::HPF_SZSE_L2_BOND_SNAP; // 盛立深交所L2债券快照頻道组播组
    quote_threads[2].mc_types[4] = QuoteMCType::HPF_SZSE_L2_BOND_TICK; // 盛立深交所L2债券逐笔頻道组播组

    if (!api_->subscribe_quote(session_id_, 3, quote_threads, RX_MODE_AUTO)) {
        auto error_info = api_->get_last_error();
        printf("subscribe_quote failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type,
                    error_info.error_code,
                    error_info.error_msg);
        return false;
    }
    printf("subscribe_quote request send success\n");
    return true;
}

bool XLTQuoteDemo::query_static_quote_info() {
    // 查询静态行情信息
    if (!api_->query_static_quote_info(session_id_, next_request_id())) {
        auto error_info = api_->get_last_error();
        printf("query_static_quote_info failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type,
                    error_info.error_code,
                    error_info.error_msg);
        return false;
    }
    printf("query_static_quote_info request send success\n");
    return true;
}

bool XLTQuoteDemo::request_rebuild_tick_quote(ExchangeIndex exchange_index, int16_t channel_no,
                                              int64_t begin_sequence, int64_t end_sequence) {
    xlt_rebuild_tick_quote_req_t request{};
    request.exchange_index = exchange_index;
    request.channel_no = channel_no;
    request.begin_sequence = begin_sequence;
    request.end_sequence = end_sequence;

    if (!api_->request_rebuild_tick_quote(session_id_, next_request_id(), &request)) {
        auto error_info = api_->get_last_error();
        printf("request_rebuild_tick_quote failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type, error_info.error_code, error_info.error_msg);
        return false;
    }
    printf("request_rebuild_tick_quote success: { exchange_index = %d, channel_no = %d, begin_sequence = %ld, end_sequence = %ld }\n",
                (int)exchange_index, channel_no, begin_sequence, end_sequence);
    return true;
}

// ---------------- XLTQuoteSpi 回调实现 ----------------

void XLTQuoteDemo::on_disconnect(uint64_t session_id) {
    printf("on_disconnect: { session_id = %lu }\n", session_id);
    session_id_ = 0;
    login_success_.store(false);
    login_finished_.store(false);
    // 在此重新发送登录请求
}

void XLTQuoteDemo::on_login(uint64_t session_id, uint64_t request_id, const xlt_error_info_t* error_info) {
    if (error_info && error_info->error_code_type != 0) {
        printf("on_login failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info->error_code_type,
                    error_info->error_code,
                    error_info->error_msg);
        // 登录失败
        login_success_.store(false);
    }
    else {
        printf("on_login success: { session_id = %lu, request_id = %lu }\n", session_id, request_id);
        // 登录成功
        session_id_ = session_id;
        login_success_.store(true);
    }
    login_finished_.store(true);
}

void XLTQuoteDemo::on_query_static_quote_info(uint64_t session_id, uint64_t request_id,
                                              const XLTStaticQuoteList* static_quotes,
                                              bool is_last,
                                              const xlt_error_info_t* error_info) {
    const size_t data_count = static_quotes ? static_quotes->data_count() : 0;
    printf("on_query_static_quote_info: { session_id = %lu, request_id = %lu, data_count = %zu, is_last = %d }\n",
                session_id, request_id, data_count, (int)is_last);

    if (error_info && error_info->error_code_type != 0) {
        printf("  error: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info->error_code_type,
                    error_info->error_code,
                    error_info->error_msg);
        return;
    }

    for (size_t i = 0; i < data_count; ++i) {
        const auto* info = static_quotes->get(static_cast<int>(i));
        if (info->swap_price > 0)
        printf("  [%zu]: { list_date = %u, exchange_index = %d, security_code = %s, security_name = %s, security_type = %d, "
                    "is_registration = %d, is_vie = %d, is_noprofit = %d, is_weighted_voting_rights = %d, "
                    "is_have_price_limit = %d, upper_limit_price = %ld, lower_limit_price = %ld, "
                    "pre_close_price = %ld, price_tick = %ld, "
                    "bid_qty_upper_limit = %d, bid_qty_lower_limit = %d, bid_qty_unit = %d, "
                    "ask_qty_upper_limit = %d, ask_qty_lower_limit = %d, ask_qty_unit = %d, "
                    "market_bid_qty_upper_limit = %d, market_bid_qty_lower_limit = %d, market_bid_qty_unit = %d, "
                    "market_ask_qty_upper_limit = %d, market_ask_qty_lower_limit = %d, market_ask_qty_unit = %d, "
                    "security_status = %.*s, "
                    "underlying_code = %s, swap_flag = %d, swap_price = %ld, maturity_date = %u }\n",
                    i,
                    info->list_date, (int)info->exchange_index, info->security_code, info->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,
                    8, info->security_status,
                    info->underlying_code, (int)info->swap_flag, info->swap_price, info->maturity_date
                );
    }
}

void XLTQuoteDemo::on_quote(x1::QuoteMCType mc_type, const void* msg_data, uint32_t msg_len) {
    printf("on_quote: { mc_type = %d, msg_len = %u }\n", (int)mc_type, msg_len);
    const EFHQuoteItem* item;
    // 演示盛立行情解码
    EFHQuoteDecoder decoder(mc_type, msg_data, msg_len);
    while ((item = decoder.get_next()) != nullptr) {
        switch ((uint8_t)item->data_type) {
        case SSE_LEV2_SNAP_MSG_TYPE: {
            auto* snap = item->data.sse_lev2_snap;
            on_report_efh_sse_lev2_snap(snap);
            break;
        }
        case SSE_LEV2_TICK_MERGE_MSG_TYPE: {
            auto* tick = item->data.sse_lev2_tick_merge;
            on_report_efh_sse_lev2_tick_merge(tick);
            break;
        }
        case SSE_LEV2_IDX_MSG_TYPE: {
            auto* idx = item->data.sse_lev2_idx;
            on_report_efh_sse_lev2_idx(idx);
            break;
        }
        case SSE_LEV2_BOND_SNAP_MSG_TYPE: {
            auto* bond = item->data.sse_lev2_bond_snap;
            on_report_efh_sse_lev2_bond_snap(bond);
            break;
        }
        case SSE_LEV2_BOND_TICK_MSG_TYPE: {
            auto* bond = item->data.sse_lev2_bond_tick;
            on_report_efh_sse_lev2_bond_tick(bond);
            break;
        }
        case SZE_LEV2_SNAP_MSG_TYPE: {
            auto* snap = item->data.sze_lev2_snap;
            on_report_efh_sze_lev2_snap(snap);
            break;
        }
        case SZE_LEV2_ORDER_MSG_TYPE:{
            auto* order = item->data.sze_lev2_order;
            auto* exe = item->data.sze_lev2_exe;
            on_report_efh_sze_lev2_tick(item->data_type, order, exe);
            break;
        }
        case SZE_LEV2_EXE_MSG_TYPE: {
            auto* order = item->data.sze_lev2_order;
            auto* exe = item->data.sze_lev2_exe;
            on_report_efh_sze_lev2_tick(item->data_type, order, exe);
            break;
        }
        case SZE_LEV2_IDX_MSG_TYPE: {
            auto* idx = item->data.sze_lev2_idx;
            on_report_efh_sze_lev2_idx(idx);
            break;
        }
        case SZE_LEV2_BOND_SNAP_MSG_TYPE: {
            auto* bond = item->data.sze_lev2_bond_snap;
            on_report_efh_sze_lev2_bond_snap(bond);
            break;
        }
        case SZE_LEV2_BOND_ORDER_MSG_TYPE: {
            auto* order = item->data.sze_lev2_bond_order;
            auto* exe = item->data.sze_lev2_bond_exe;
            on_report_efh_sze_lev2_bond_tick(item->data_type, order, exe);
            break;
        }
        case SZE_LEV2_BOND_EXE_MSG_TYPE: {
            auto* order = item->data.sze_lev2_bond_order;
            auto* exe = item->data.sze_lev2_bond_exe;
            on_report_efh_sze_lev2_bond_tick(item->data_type, order, exe);
            break;
        }
        }
    }

    // 演示xlight行情
    const XLTQuoteItem* xlt_item;
    XLTQuoteDecoder xlt_decoder(mc_type, msg_data, msg_len);
    while ((xlt_item = xlt_decoder.get_next()) != nullptr) {
        switch (xlt_item->data_type) {
        case QuoteMsgType::SSE_L1_INDEX: {
            auto* idx = xlt_item->data.sse_l1_index;
            on_sse_l1_index(idx);
            break;
        }
        case QuoteMsgType::SSE_L1_SNAPSHOT: {
            auto* snap = xlt_item->data.sse_l1_snapshot;
            on_sse_l1_snapshot(snap);
            break;
        }
        case QuoteMsgType::SZSE_L1_INDEX: {
            auto* idx = xlt_item->data.szse_l1_index;
            on_szse_l1_index(idx);
            break;
        }
        case QuoteMsgType::SZSE_L1_SNAPSHOT: {
            auto* snap = xlt_item->data.szse_l1_snapshot;
            on_szse_l1_snapshot(snap);
            break;
        }
        case QuoteMsgType::SZSE_L1_BOND_SNAPSHOT: {
            auto* bond = xlt_item->data.szse_l1_snapshot_bond;
            on_szse_l1_snapshot_bond(bond);
            break;
        }
        default:
            break;
        }
    }
}

void XLTQuoteDemo::on_report_efh_sse_lev2_snap(const sse_hpf_lev2* p_snap) {
    if (!p_snap) return;
    printf("sse_l2_snap: { symbol = %s, quote_update_time = %u, last_price = %u, total_quantity = %llu, total_value = %llu }\n",
           p_snap->m_symbol, p_snap->m_quote_update_time, p_snap->m_last_price,
           p_snap->m_total_quantity, p_snap->m_total_value);
}

void XLTQuoteDemo::on_report_efh_sse_lev2_tick_merge(const sse_hpf_tick_merge* p_tick) {
    if (!p_tick) return;
    printf("sse_l2_tick_merge: { symbol = %s, tick_time = %u, tick_index = %llu, tick_type = %c, price = %u, quantity = %llu }\n",
           p_tick->m_symbol, p_tick->m_tick_time, p_tick->m_tick_index,
           p_tick->m_tick_type, p_tick->m_price, p_tick->m_quantity);
}

void XLTQuoteDemo::on_report_efh_sse_lev2_idx(const sse_hpf_idx* p_idx) {
    if (!p_idx) return;
    printf("sse_l2_idx: { symbol = %s, quote_update_time = %u, last_price = %u, total_quantity = %llu, total_value = %llu }\n",
           p_idx->m_symbol, p_idx->m_quote_update_time, p_idx->m_last_price,
           p_idx->m_total_quantity, p_idx->m_total_value);
}

void XLTQuoteDemo::on_report_efh_sse_lev2_bond_snap(const sse_hpf_bond_snap* p_bond_snap) {
    if (!p_bond_snap) return;
    printf("sse_l2_bond_snap: { symbol = %s, quote_update_time = %u, last_price = %u, total_quantity = %llu, total_value = %llu }\n",
           p_bond_snap->m_symbol, p_bond_snap->m_quote_update_time, p_bond_snap->m_last_price,
           p_bond_snap->m_total_quantity, p_bond_snap->m_total_value);
}

void XLTQuoteDemo::on_report_efh_sse_lev2_bond_tick(const sse_hpf_bond_tick* p_bond_tick) {
    if (!p_bond_tick) return;
    printf("sse_l2_bond_tick: { symbol = %s, tick_time = %u, tick_index = %u, tick_type = %c, price = %u, quantity = %llu }\n",
           p_bond_tick->m_symbol, p_bond_tick->m_tick_time, p_bond_tick->m_tick_index,
           p_bond_tick->m_tick_type, p_bond_tick->m_price, p_bond_tick->m_quantity);
}

void XLTQuoteDemo::on_report_efh_sze_lev2_snap(const sze_hpf_lev2* p_snap) {
    printf("sze_l2_snap: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
           "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
           "trading_status = %u, total_trade_num = %llu, total_quantity = %llu, total_value = %llu, "
           "pre_close_price = %u, last_price = %u, open_price = %u, day_high_price = %u, day_low_price = %u, "
           "today_close_price = %u, total_bid_weighted_avg_price = %u, total_bid_quantity = %llu, "
           "total_ask_weighted_avg_price = %u, total_ask_quantity = %llu, "
           "lpv = %u, iopv = %u, upper_limit_price = %u, low_limit_price = %u, open_interest = %u }\n",
           SZE_SYMBOL_LEN, p_snap->m_head.m_symbol, p_snap->m_head.m_sequence, p_snap->m_head.m_message_type, p_snap->m_head.m_security_type,
           p_snap->m_head.m_quote_update_time, p_snap->m_head.m_channel_num, p_snap->m_head.m_sequence_num,
           p_snap->m_trading_status, p_snap->m_total_trade_num, p_snap->m_total_quantity, p_snap->m_total_value,
           p_snap->m_pre_close_price, p_snap->m_last_price, p_snap->m_open_price, p_snap->m_day_high_price, p_snap->m_day_low_price,
           p_snap->m_today_close_price, p_snap->m_total_bid_weighted_avg_price, p_snap->m_total_bid_quantity,
           p_snap->m_total_ask_weighted_avg_price, p_snap->m_total_ask_quantity,
           p_snap->m_lpv, p_snap->m_iopv, p_snap->m_upper_limit_price, p_snap->m_low_limit_price, p_snap->m_open_interest);

    for (int i = 0; i < SZE_LEV2_DEPTH; ++i) {
        printf("  bid[%d]: { price = %u, quantity = %llu }, ask[%d]: { price = %u, quantity = %llu }\n",
               i, p_snap->m_bid_unit[i].m_price, p_snap->m_bid_unit[i].m_quantity,
               i, p_snap->m_ask_unit[i].m_price, p_snap->m_ask_unit[i].m_quantity);
    }
}

void XLTQuoteDemo::on_report_efh_sze_lev2_tick(int msg_type, const sze_hpf_order* p_order, const sze_hpf_exe* p_exe) {
    if (msg_type == SZE_LEV2_ORDER_MSG_TYPE && p_order) {
        printf("sze_l2_order: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
               "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
               "order_price = %u, order_quantity = %llu, side_flag = %c, order_type = %c }\n",
               SZE_SYMBOL_LEN, p_order->m_head.m_symbol, p_order->m_head.m_sequence, p_order->m_head.m_message_type, p_order->m_head.m_security_type,
               p_order->m_head.m_quote_update_time, p_order->m_head.m_channel_num, p_order->m_head.m_sequence_num,
               p_order->m_order_price, p_order->m_order_quantity, p_order->m_side_flag, p_order->m_order_type);
    } else if (msg_type == SZE_LEV2_EXE_MSG_TYPE && p_exe) {
        printf("sze_l2_exe: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
               "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
               "trade_buy_num = %lld, trade_sell_num = %lld, trade_price = %u, trade_quantity = %lld, trade_type = %c }\n",
               SZE_SYMBOL_LEN, p_exe->m_head.m_symbol, p_exe->m_head.m_sequence, p_exe->m_head.m_message_type, p_exe->m_head.m_security_type,
               p_exe->m_head.m_quote_update_time, p_exe->m_head.m_channel_num, p_exe->m_head.m_sequence_num,
               p_exe->m_trade_buy_num, p_exe->m_trade_sell_num, p_exe->m_trade_price, p_exe->m_trade_quantity, p_exe->m_trade_type);
    }
}

void XLTQuoteDemo::on_report_efh_sze_lev2_idx(const sze_hpf_idx* p_idx) {
    printf("sze_l2_idx: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
           "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
           "total_trade_num = %llu, total_quantity = %llu, total_value = %llu, "
           "last_price = %u, pre_close_price = %u, open_price = %u, day_high_price = %u, day_low_price = %u, today_close_price = %u }\n",
           SZE_SYMBOL_LEN, p_idx->m_head.m_symbol, p_idx->m_head.m_sequence, p_idx->m_head.m_message_type, p_idx->m_head.m_security_type,
           p_idx->m_head.m_quote_update_time, p_idx->m_head.m_channel_num, p_idx->m_head.m_sequence_num,
           p_idx->m_total_trade_num, p_idx->m_total_quantity, p_idx->m_total_value,
           p_idx->m_last_price, p_idx->m_pre_close_price, p_idx->m_open_price, p_idx->m_day_high_price, p_idx->m_day_low_price, p_idx->m_today_close_price);
}

void XLTQuoteDemo::on_report_efh_sze_lev2_bond_snap(const sze_hpf_bond_snap* p_bond_snap) {
    printf("sze_l2_bond_snap: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
           "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
           "trading_status = %u, total_trade_num = %llu, total_quantity = %llu, total_value = %llu, "
           "pre_close_price = %u, last_price = %u, open_price = %u, day_high_price = %u, day_low_price = %u, "
           "today_close_price = %u, total_trade_weighted_avg_price = %u, "
           "fluctuation_1 = %d, fluctuation_2 = %d, "
           "total_bid_weighted_avg_price = %u, total_bid_quantity = %llu, "
           "total_ask_weighted_avg_price = %u, total_ask_quantity = %llu, "
           "weighted_avg_price_BP = %d, weighted_avg_pre_close_price = %u, auction_trade_last_price = %u, "
           "auction_volume_trade = %llu, auction_value_trade = %llu }\n",
           SZE_SYMBOL_LEN, p_bond_snap->m_head.m_symbol, p_bond_snap->m_head.m_sequence, p_bond_snap->m_head.m_message_type, p_bond_snap->m_head.m_security_type,
           p_bond_snap->m_head.m_quote_update_time, p_bond_snap->m_head.m_channel_num, p_bond_snap->m_head.m_sequence_num,
           p_bond_snap->m_trading_status, p_bond_snap->m_total_trade_num, p_bond_snap->m_total_quantity, p_bond_snap->m_total_value,
           p_bond_snap->m_pre_close_price, p_bond_snap->m_last_price, p_bond_snap->m_open_price, p_bond_snap->m_day_high_price, p_bond_snap->m_day_low_price,
           p_bond_snap->m_today_close_price, p_bond_snap->m_total_trade_weighted_avg_price,
           p_bond_snap->m_fluctuation_1, p_bond_snap->m_fluctuation_2,
           p_bond_snap->m_total_bid_weighted_avg_price, p_bond_snap->m_total_bid_quantity,
           p_bond_snap->m_total_ask_weighted_avg_price, p_bond_snap->m_total_ask_quantity,
           p_bond_snap->m_weighted_avg_price_BP, p_bond_snap->m_weighted_avg_pre_close_price, p_bond_snap->m_auction_trade_last_price,
           p_bond_snap->m_auction_volume_trade, p_bond_snap->m_auction_value_trade);
    for (int i = 0; i < SZE_LEV2_DEPTH; ++i) {
        printf("  bid[%d]: { price = %u, quantity = %llu }, ask[%d]: { price = %u, quantity = %llu }\n",
               i, p_bond_snap->m_bid_unit[i].m_price, p_bond_snap->m_bid_unit[i].m_quantity,
               i, p_bond_snap->m_ask_unit[i].m_price, p_bond_snap->m_ask_unit[i].m_quantity);
    }
}

void XLTQuoteDemo::on_report_efh_sze_lev2_bond_tick(int msg_type, const sze_hpf_bond_order* p_order,
    const sze_hpf_bond_exe* p_exe) {
    if (msg_type == SZE_LEV2_BOND_ORDER_MSG_TYPE && p_order) {
        printf("sze_l2_bond_order: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
               "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
               "price = %u, quantity = %llu, side = %c, order_type = %c }\n",
               SZE_SYMBOL_LEN, p_order->m_head.m_symbol, p_order->m_head.m_sequence, p_order->m_head.m_message_type, p_order->m_head.m_security_type,
               p_order->m_head.m_quote_update_time, p_order->m_head.m_channel_num, p_order->m_head.m_sequence_num,
               p_order->m_price, p_order->m_quantity, p_order->m_side, p_order->m_order_type);
    } else if (msg_type == SZE_LEV2_BOND_EXE_MSG_TYPE && p_exe) {
        printf("sze_l2_bond_exe: { symbol = %.*s, sequence = %u, message_type = %u, security_type = %u, "
               "quote_update_time = %llu, channel_num = %u, sequence_num = %llu, "
               "buy_num = %lld, sell_num = %lld, price = %u, quantity = %lld, type = %c }\n",
               SZE_SYMBOL_LEN, p_exe->m_head.m_symbol, p_exe->m_head.m_sequence, p_exe->m_head.m_message_type, p_exe->m_head.m_security_type,
               p_exe->m_head.m_quote_update_time, p_exe->m_head.m_channel_num, p_exe->m_head.m_sequence_num,
               p_exe->m_buy_num, p_exe->m_sell_num, p_exe->m_price, p_exe->m_quantity, p_exe->m_type);
    }
}

void XLTQuoteDemo::on_sse_l1_index(const xlt_sse_l1_index_t* sse_l1_index) {
    if (!sse_l1_index) return;
    printf("on_sse_l1_index: { security_code = %.*s, security_type = %u, sending_time = %llu, trad_ses_mode = %u, "
           "trade_date = %u, last_update_time = %u, md_stream_id = %.*s, "
           "prev_close_px = %llu, total_volume_traded = %llu, num_trades = %llu, total_value_traded = %llu, "
           "trading_phase_code = %.*s, last_px = %llu, open_px = %llu, close_px = %llu, high_px = %llu, low_px = %llu }\n",
           CONST_STR_SECURITY_CODE_LEN, sse_l1_index->security_code, sse_l1_index->security_type,
           (unsigned long long)sse_l1_index->sending_time, sse_l1_index->trad_ses_mode,
           sse_l1_index->trade_date, sse_l1_index->last_update_time, 5, sse_l1_index->md_stream_id,
           (unsigned long long)sse_l1_index->prev_close_px, (unsigned long long)sse_l1_index->total_volume_traded,
           (unsigned long long)sse_l1_index->num_trades, (unsigned long long)sse_l1_index->total_value_traded,
           8, sse_l1_index->trading_phase_code,
           (unsigned long long)sse_l1_index->last_px, (unsigned long long)sse_l1_index->open_px,
           (unsigned long long)sse_l1_index->close_px, (unsigned long long)sse_l1_index->high_px, (unsigned long long)sse_l1_index->low_px);
}

void XLTQuoteDemo::on_sse_l1_snapshot(const xlt_sse_l1_snapshot_t* sse_l1_snapshot) {
    if (!sse_l1_snapshot) return;
    printf("on_sse_l1_snapshot: { security_code = %.*s, security_type = %u, sending_time = %llu, trad_ses_mode = %u, "
           "trade_date = %u, last_update_time = %u, md_stream_id = %.*s, "
           "prev_close_px = %llu, total_volume_traded = %llu, num_trades = %llu, total_value_traded = %llu, "
           "trading_phase_code = %.*s, last_px = %llu, open_px = %llu, close_px = %llu, settle_px = %llu, "
           "high_px = %llu, low_px = %llu, pre_iopv = %llu, option_ref_px = %llu, "
           "option_notional_amount = %llu, pre_settle_px = %llu, option_open_qty = %llu }\n",
           CONST_STR_SECURITY_CODE_LEN, sse_l1_snapshot->security_code, sse_l1_snapshot->security_type,
           (unsigned long long)sse_l1_snapshot->sending_time, sse_l1_snapshot->trad_ses_mode,
           sse_l1_snapshot->trade_date, sse_l1_snapshot->last_update_time, 5, sse_l1_snapshot->md_stream_id,
           (unsigned long long)sse_l1_snapshot->prev_close_px, (unsigned long long)sse_l1_snapshot->total_volume_traded,
           (unsigned long long)sse_l1_snapshot->num_trades, (unsigned long long)sse_l1_snapshot->total_value_traded,
           8, sse_l1_snapshot->trading_phase_code,
           (unsigned long long)sse_l1_snapshot->last_px, (unsigned long long)sse_l1_snapshot->open_px,
           (unsigned long long)sse_l1_snapshot->close_px, (unsigned long long)sse_l1_snapshot->settle_px,
           (unsigned long long)sse_l1_snapshot->high_px, (unsigned long long)sse_l1_snapshot->low_px,
           (unsigned long long)sse_l1_snapshot->pre_iopv, (unsigned long long)sse_l1_snapshot->option_ref_px,
           (unsigned long long)sse_l1_snapshot->option_notional_amount, (unsigned long long)sse_l1_snapshot->pre_settle_px,
           (unsigned long long)sse_l1_snapshot->option_open_qty);
    for (int i = 0; i < 5; ++i) {
        printf("  bid[%d]: { px = %llu, volume = %llu }, offer[%d]: { px = %llu, volume = %llu }\n",
               i, (unsigned long long)sse_l1_snapshot->bid_px[i], (unsigned long long)sse_l1_snapshot->bid_volume[i],
               i, (unsigned long long)sse_l1_snapshot->offer_px[i], (unsigned long long)sse_l1_snapshot->offer_volume[i]);
    }
}

void XLTQuoteDemo::on_szse_l1_index(const xlt_szse_l1_index_t* szse_l1_index) {
    if (!szse_l1_index) return;
    printf("on_szse_l1_index: { security_code = %.*s, channel_no = %u, orig_time = %lld, "
           "md_stream_id = %.*s, security_id_source = %.*s, trading_phase_code = %.*s, "
           "prev_close_px = %lld, num_trades = %lld, total_volume_trade = %lld, total_value_trade = %lld, "
           "last_index = %lld, prev_close_index = %lld, open_index = %lld, high_index = %lld, low_index = %lld, close_index = %lld }\n",
           CONST_STR_SECURITY_CODE_LEN, szse_l1_index->security_code, szse_l1_index->channel_no, (long long)szse_l1_index->orig_time,
           3, szse_l1_index->md_stream_id, 4, szse_l1_index->security_id_source, 8, szse_l1_index->trading_phase_code,
           (long long)szse_l1_index->prev_close_px, (long long)szse_l1_index->num_trades,
           (long long)szse_l1_index->total_volume_trade, (long long)szse_l1_index->total_value_trade,
           (long long)szse_l1_index->last_index, (long long)szse_l1_index->prev_close_index,
           (long long)szse_l1_index->open_index, (long long)szse_l1_index->high_index,
           (long long)szse_l1_index->low_index, (long long)szse_l1_index->close_index);
}

void XLTQuoteDemo::on_szse_l1_snapshot(const xlt_szse_l1_snapshot_t* szse_l1_snapshot) {
    if (!szse_l1_snapshot) return;
    printf("on_szse_l1_snapshot: { security_code = %.*s, channel_no = %u, orig_time = %lld, "
           "md_stream_id = %.*s, security_id_source = %.*s, trading_phase_code = %.*s, "
           "prev_close_px = %lld, num_trades = %lld, total_volume_trade = %lld, total_value_trade = %lld, "
           "last_px = %lld, open_px = %lld, high_px = %lld, low_px = %lld, "
           "px_gain = %lld, px_gain_2 = %lld, "
           "weighted_avg_bid_px = %lld, total_bid_qty = %lld, weighted_avg_offer_px = %lld, total_offer_qty = %lld, "
           "pe_ratio_1 = %lld, pe_ratio_2 = %lld, nav = %lld, iopv = %lld, warrant_premium = %lld, "
           "limit_up_px = %lld, limit_down_px = %lld, option_open_qty = %lld, option_break_px = %lld }\n",
           CONST_STR_SECURITY_CODE_LEN, szse_l1_snapshot->security_code, szse_l1_snapshot->channel_no, (long long)szse_l1_snapshot->orig_time,
           3, szse_l1_snapshot->md_stream_id, 4, szse_l1_snapshot->security_id_source, 8, szse_l1_snapshot->trading_phase_code,
           (long long)szse_l1_snapshot->prev_close_px, (long long)szse_l1_snapshot->num_trades,
           (long long)szse_l1_snapshot->total_volume_trade, (long long)szse_l1_snapshot->total_value_trade,
           (long long)szse_l1_snapshot->last_px, (long long)szse_l1_snapshot->open_px,
           (long long)szse_l1_snapshot->high_px, (long long)szse_l1_snapshot->low_px,
           (long long)szse_l1_snapshot->px_gain, (long long)szse_l1_snapshot->px_gain_2,
           (long long)szse_l1_snapshot->weighted_avg_bid_px, (long long)szse_l1_snapshot->total_bid_qty,
           (long long)szse_l1_snapshot->weighted_avg_offer_px, (long long)szse_l1_snapshot->total_offer_qty,
           (long long)szse_l1_snapshot->pe_ratio_1, (long long)szse_l1_snapshot->pe_ratio_2,
           (long long)szse_l1_snapshot->nav, (long long)szse_l1_snapshot->iopv, (long long)szse_l1_snapshot->warrant_premium,
           (long long)szse_l1_snapshot->limit_up_px, (long long)szse_l1_snapshot->limit_down_px,
           (long long)szse_l1_snapshot->option_open_qty, (long long)szse_l1_snapshot->option_break_px);
    for (int i = 0; i < 5; ++i) {
        printf("  bid[%d]: { px = %lld, volume = %lld }, offer[%d]: { px = %lld, volume = %lld }\n",
               i, (long long)szse_l1_snapshot->bid_px[i], (long long)szse_l1_snapshot->bid_volume[i],
               i, (long long)szse_l1_snapshot->offer_px[i], (long long)szse_l1_snapshot->offer_volume[i]);
    }
}

void XLTQuoteDemo::on_szse_l1_snapshot_bond(const xlt_szse_l1_snapshot_bond_t* szse_l1_snapshot_bond) {
    if (!szse_l1_snapshot_bond) return;
    printf("on_szse_l1_snapshot_bond: { security_code = %.*s, channel_no = %u, orig_time = %lld, "
           "md_stream_id = %.*s, security_id_source = %.*s, trading_phase_code = %.*s, "
           "prev_close_px = %lld, num_trades = %lld, total_volume_trade = %lld, total_value_trade = %lld, "
           "last_px = %lld, open_px = %lld, close_px = %lld, high_px = %lld, low_px = %lld, "
           "weighted_avg_px = %lld, px_gain = %lld, px_gain_2 = %lld, "
           "weighted_avg_bid_px = %lld, total_bid_qty = %lld, weighted_avg_offer_px = %lld, total_offer_qty = %lld, "
           "weighted_avg_ir_gain = %lld, weighted_avg_prev_ir = %lld, "
           "match_last_px = %lld, auction_volume_trade = %lld, auction_value_trade = %lld }\n",
           CONST_STR_SECURITY_CODE_LEN, szse_l1_snapshot_bond->security_code, szse_l1_snapshot_bond->channel_no, (long long)szse_l1_snapshot_bond->orig_time,
           3, szse_l1_snapshot_bond->md_stream_id, 4, szse_l1_snapshot_bond->security_id_source, 8, szse_l1_snapshot_bond->trading_phase_code,
           (long long)szse_l1_snapshot_bond->prev_close_px, (long long)szse_l1_snapshot_bond->num_trades,
           (long long)szse_l1_snapshot_bond->total_volume_trade, (long long)szse_l1_snapshot_bond->total_value_trade,
           (long long)szse_l1_snapshot_bond->last_px, (long long)szse_l1_snapshot_bond->open_px,
           (long long)szse_l1_snapshot_bond->close_px, (long long)szse_l1_snapshot_bond->high_px, (long long)szse_l1_snapshot_bond->low_px,
           (long long)szse_l1_snapshot_bond->weighted_avg_px, (long long)szse_l1_snapshot_bond->px_gain, (long long)szse_l1_snapshot_bond->px_gain_2,
           (long long)szse_l1_snapshot_bond->weighted_avg_bid_px, (long long)szse_l1_snapshot_bond->total_bid_qty,
           (long long)szse_l1_snapshot_bond->weighted_avg_offer_px, (long long)szse_l1_snapshot_bond->total_offer_qty,
           (long long)szse_l1_snapshot_bond->weighted_avg_ir_gain, (long long)szse_l1_snapshot_bond->weighted_avg_prev_ir,
           (long long)szse_l1_snapshot_bond->match_last_px, (long long)szse_l1_snapshot_bond->auction_volume_trade,
           (long long)szse_l1_snapshot_bond->auction_value_trade);
    for (int i = 0; i < 5; ++i) {
        printf("  bid[%d]: { px = %lld, volume = %lld }, offer[%d]: { px = %lld, volume = %lld }\n",
               i, (long long)szse_l1_snapshot_bond->bid_px[i], (long long)szse_l1_snapshot_bond->bid_volume[i],
               i, (long long)szse_l1_snapshot_bond->offer_px[i], (long long)szse_l1_snapshot_bond->offer_volume[i]);
    }
}

bool XLTQuoteDemo::query_level1_quote(ExchangeIndex exchange_index, const char* security_code) {
    if (!api_->query_level1_quote(session_id_, next_request_id(), exchange_index, security_code)) {
        auto error_info = api_->get_last_error();
        printf("query_level1_quote failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type,
                    error_info.error_code,
                    error_info.error_msg);
        return false;
    }
    printf("query_level1_quote request send success: { exchange_index = %d, security_code = %s }\n",
                (int)exchange_index, security_code);
    return true;
}

bool XLTQuoteDemo::query_all_level1_quote() {
    if (!api_->query_all_level1_quote(session_id_, next_request_id())) {
        auto error_info = api_->get_last_error();
        printf("query_all_level1_quote failed: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info.error_code_type,
                    error_info.error_code,
                    error_info.error_msg);
        return false;
    }
    printf("query_all_level1_quote request send success\n");
    return true;
}

void XLTQuoteDemo::on_query_level1_quote(uint64_t session_id, uint64_t request_id,
                                          const xlt_level1_quote_t* level1_quote,
                                          const xlt_error_info_t* error_info) {
    printf("on_query_level1_quote: { session_id = %lu, request_id = %lu }\n", session_id, request_id);

    if (error_info && error_info->error_code_type != 0) {
        printf("  error: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
                    error_info->error_code_type,
                    error_info->error_code,
                    error_info->error_msg);
        return;
    }

    if (level1_quote) {
        print_level1_quote(level1_quote);
    }
}

void XLTQuoteDemo::on_query_all_level1_quote(uint64_t session_id, uint64_t request_id,
                                              const XLTLevel1QuoteList* level1_quotes,
                                              bool is_last,
                                              const xlt_error_info_t* error_info) {
    const size_t data_count = level1_quotes ? level1_quotes->data_count() : 0;
    static uint64_t counter = 0;
    counter += data_count;
    printf("on_query_all_level1_quote: { session_id = %lu, request_id = %lu, data_count = %zu, is_last = %d, total = %lu }\n",
                session_id, request_id, data_count, (int)is_last, counter);


    // if (error_info && error_info->error_code != 0) {
    //     printf("  error: { error_code_type = %lu, error_code = %lu, error_msg = %s }\n",
    //                 error_info->error_code,
    //                 error_info->error_code,
    //                 error_info->error_msg);
    //     return;
    // }
    //
    // for (size_t i = 0; i < data_count; ++i) {
    //     const auto* quote = level1_quotes->get(static_cast<int>(i));
    //     if (quote) {
    //         printf("  [%zu]:\n", i);
    //         print_level1_quote(quote);
    //     }
    // }
}

void XLTQuoteDemo::print_level1_quote(const xlt_level1_quote_t* level1_quote) {
    if (!level1_quote) return;

    switch (level1_quote->quoteMsgType) {
    case QuoteMsgType::SSE_L1_INDEX: {
        const auto& q = level1_quote->l1_quote.sse_l1_index;
        printf("    sse_l1_index: { security_code = %.*s, security_type = %u, sending_time = %llu, trad_ses_mode = %u, "
               "trade_date = %u, last_update_time = %u, md_stream_id = %.*s, "
               "prev_close_px = %llu, total_volume_traded = %llu, num_trades = %llu, total_value_traded = %llu, "
               "trading_phase_code = %.*s, last_px = %llu, open_px = %llu, close_px = %llu, high_px = %llu, low_px = %llu }\n",
               CONST_STR_SECURITY_CODE_LEN, q.security_code, q.security_type,
               (unsigned long long)q.sending_time, q.trad_ses_mode,
               q.trade_date, q.last_update_time, 5, q.md_stream_id,
               (unsigned long long)q.prev_close_px, (unsigned long long)q.total_volume_traded,
               (unsigned long long)q.num_trades, (unsigned long long)q.total_value_traded,
               8, q.trading_phase_code,
               (unsigned long long)q.last_px, (unsigned long long)q.open_px,
               (unsigned long long)q.close_px, (unsigned long long)q.high_px, (unsigned long long)q.low_px);
        break;
    }
    case QuoteMsgType::SSE_L1_SNAPSHOT: {
        const auto& q = level1_quote->l1_quote.sse_l1_snapshot;
        printf("    sse_l1_snapshot: { security_code = %.*s, security_type = %u, sending_time = %llu, trad_ses_mode = %u, "
               "trade_date = %u, last_update_time = %u, md_stream_id = %.*s, "
               "prev_close_px = %llu, total_volume_traded = %llu, num_trades = %llu, total_value_traded = %llu, "
               "trading_phase_code = %.*s, last_px = %llu, open_px = %llu, close_px = %llu, settle_px = %llu, "
               "high_px = %llu, low_px = %llu, pre_iopv = %llu, option_ref_px = %llu, "
               "option_notional_amount = %llu, pre_settle_px = %llu, option_open_qty = %llu }\n",
               CONST_STR_SECURITY_CODE_LEN, q.security_code, q.security_type,
               (unsigned long long)q.sending_time, q.trad_ses_mode,
               q.trade_date, q.last_update_time, 5, q.md_stream_id,
               (unsigned long long)q.prev_close_px, (unsigned long long)q.total_volume_traded,
               (unsigned long long)q.num_trades, (unsigned long long)q.total_value_traded,
               8, q.trading_phase_code,
               (unsigned long long)q.last_px, (unsigned long long)q.open_px,
               (unsigned long long)q.close_px, (unsigned long long)q.settle_px,
               (unsigned long long)q.high_px, (unsigned long long)q.low_px,
               (unsigned long long)q.pre_iopv, (unsigned long long)q.option_ref_px,
               (unsigned long long)q.option_notional_amount, (unsigned long long)q.pre_settle_px,
               (unsigned long long)q.option_open_qty);
        for (int i = 0; i < 5; ++i) {
            printf("      bid[%d]: { px = %llu, volume = %llu }, offer[%d]: { px = %llu, volume = %llu }\n",
                   i, (unsigned long long)q.bid_px[i], (unsigned long long)q.bid_volume[i],
                   i, (unsigned long long)q.offer_px[i], (unsigned long long)q.offer_volume[i]);
        }
        break;
    }
    case QuoteMsgType::SZSE_L1_INDEX: {
        const auto& q = level1_quote->l1_quote.szse_l1_index;
        printf("    szse_l1_index: { security_code = %.*s, channel_no = %u, orig_time = %lld, "
               "md_stream_id = %.*s, security_id_source = %.*s, trading_phase_code = %.*s, "
               "prev_close_px = %lld, num_trades = %lld, total_volume_trade = %lld, total_value_trade = %lld, "
               "last_index = %lld, prev_close_index = %lld, open_index = %lld, high_index = %lld, low_index = %lld, close_index = %lld }\n",
               CONST_STR_SECURITY_CODE_LEN, q.security_code, q.channel_no, (long long)q.orig_time,
               3, q.md_stream_id, 4, q.security_id_source, 8, q.trading_phase_code,
               (long long)q.prev_close_px, (long long)q.num_trades,
               (long long)q.total_volume_trade, (long long)q.total_value_trade,
               (long long)q.last_index, (long long)q.prev_close_index,
               (long long)q.open_index, (long long)q.high_index,
               (long long)q.low_index, (long long)q.close_index);
        break;
    }
    case QuoteMsgType::SZSE_L1_SNAPSHOT: {
        const auto& q = level1_quote->l1_quote.szse_l1_snapshot;
        printf("    szse_l1_snapshot: { security_code = %.*s, channel_no = %u, orig_time = %lld, "
               "md_stream_id = %.*s, security_id_source = %.*s, trading_phase_code = %.*s, "
               "prev_close_px = %lld, num_trades = %lld, total_volume_trade = %lld, total_value_trade = %lld, "
               "last_px = %lld, open_px = %lld, high_px = %lld, low_px = %lld, "
               "px_gain = %lld, px_gain_2 = %lld, "
               "weighted_avg_bid_px = %lld, total_bid_qty = %lld, weighted_avg_offer_px = %lld, total_offer_qty = %lld, "
               "pe_ratio_1 = %lld, pe_ratio_2 = %lld, nav = %lld, iopv = %lld, warrant_premium = %lld, "
               "limit_up_px = %lld, limit_down_px = %lld, option_open_qty = %lld, option_break_px = %lld }\n",
               CONST_STR_SECURITY_CODE_LEN, q.security_code, q.channel_no, (long long)q.orig_time,
               3, q.md_stream_id, 4, q.security_id_source, 8, q.trading_phase_code,
               (long long)q.prev_close_px, (long long)q.num_trades,
               (long long)q.total_volume_trade, (long long)q.total_value_trade,
               (long long)q.last_px, (long long)q.open_px,
               (long long)q.high_px, (long long)q.low_px,
               (long long)q.px_gain, (long long)q.px_gain_2,
               (long long)q.weighted_avg_bid_px, (long long)q.total_bid_qty,
               (long long)q.weighted_avg_offer_px, (long long)q.total_offer_qty,
               (long long)q.pe_ratio_1, (long long)q.pe_ratio_2,
               (long long)q.nav, (long long)q.iopv, (long long)q.warrant_premium,
               (long long)q.limit_up_px, (long long)q.limit_down_px,
               (long long)q.option_open_qty, (long long)q.option_break_px);
        for (int i = 0; i < 5; ++i) {
            printf("      bid[%d]: { px = %lld, volume = %lld }, offer[%d]: { px = %lld, volume = %lld }\n",
                   i, (long long)q.bid_px[i], (long long)q.bid_volume[i],
                   i, (long long)q.offer_px[i], (long long)q.offer_volume[i]);
        }
        break;
    }
    case QuoteMsgType::SZSE_L1_BOND_SNAPSHOT: {
        const auto& q = level1_quote->l1_quote.szse_l1_snapshot_bond;
        printf("    szse_l1_snapshot_bond: { security_code = %.*s, channel_no = %u, orig_time = %lld, "
               "md_stream_id = %.*s, security_id_source = %.*s, trading_phase_code = %.*s, "
               "prev_close_px = %lld, num_trades = %lld, total_volume_trade = %lld, total_value_trade = %lld, "
               "last_px = %lld, open_px = %lld, close_px = %lld, high_px = %lld, low_px = %lld, "
               "weighted_avg_px = %lld, px_gain = %lld, px_gain_2 = %lld, "
               "weighted_avg_bid_px = %lld, total_bid_qty = %lld, weighted_avg_offer_px = %lld, total_offer_qty = %lld, "
               "weighted_avg_ir_gain = %lld, weighted_avg_prev_ir = %lld, "
               "match_last_px = %lld, auction_volume_trade = %lld, auction_value_trade = %lld }\n",
               CONST_STR_SECURITY_CODE_LEN, q.security_code, q.channel_no, (long long)q.orig_time,
               3, q.md_stream_id, 4, q.security_id_source, 8, q.trading_phase_code,
               (long long)q.prev_close_px, (long long)q.num_trades,
               (long long)q.total_volume_trade, (long long)q.total_value_trade,
               (long long)q.last_px, (long long)q.open_px,
               (long long)q.close_px, (long long)q.high_px, (long long)q.low_px,
               (long long)q.weighted_avg_px, (long long)q.px_gain, (long long)q.px_gain_2,
               (long long)q.weighted_avg_bid_px, (long long)q.total_bid_qty,
               (long long)q.weighted_avg_offer_px, (long long)q.total_offer_qty,
               (long long)q.weighted_avg_ir_gain, (long long)q.weighted_avg_prev_ir,
               (long long)q.match_last_px, (long long)q.auction_volume_trade,
               (long long)q.auction_value_trade);
        for (int i = 0; i < 5; ++i) {
            printf("      bid[%d]: { px = %lld, volume = %lld }, offer[%d]: { px = %lld, volume = %lld }\n",
                   i, (long long)q.bid_px[i], (long long)q.bid_volume[i],
                   i, (long long)q.offer_px[i], (long long)q.offer_volume[i]);
        }
        break;
    }
    default:
        printf("    unknown quote_msg_type: { type = %d }\n", (int)level1_quote->quoteMsgType);
        break;
    }
}

void XLTQuoteDemo::on_request_rebuild_tick_quote_error(uint64_t session_id, uint64_t request_id,
                                                 const xlt_error_info_t* error_info) {
    if (error_info) {
        printf("on_request_rebuild_tick_quote failed: { error_code_type = %lu, error_code = %lu, error_msg = %s, session_id = %lu, request_id = %lu }\n",
                    error_info->error_code_type, error_info->error_code, error_info->error_msg, session_id, request_id);
    }
}

void XLTQuoteDemo::on_rebuild_tick_quote(uint64_t session_id, uint64_t request_id,
                                         ExchangeIndex exchange_index, QuoteMsgType msg_type, const xlt_tick_quote_union_t* tick_quote) {
    switch (msg_type) {
    case QuoteMsgType::SSE_L2_TICK: {
        const auto& t = tick_quote->sse_l2_tick;
        printf("rebuild_sse_l2_tick: { security_code = %s, biz_index = %ld, channel = %d, tick_time = %d, "
                    "tick_type = %c, buy_order_no = %ld, sell_order_no = %ld, price = %d, qty = %ld, "
                    "trade_money = %ld, tick_bs_flag = %.8s }\n",
                    t.security_code, t.biz_index, t.channel, t.tick_time,
                    t.tick_type, t.buy_order_no, t.sell_order_no, t.price, t.qty,
                    t.trade_money, t.tick_bs_flag);
        break;
    }
    case QuoteMsgType::SSE_L2_BOND_TICK: {
        const auto& t = tick_quote->sse_l2_bond_tick;
        printf("rebuild_sse_l2_bond_tick: { security_code = %s, tick_index = %ld, channel = %d, tick_time = %d, "
                    "tick_type = %c, buy_order_no = %ld, sell_order_no = %ld, price = %d, qty = %ld, "
                    "trade_money = %ld, tick_bs_flag = %.8s }\n",
                    t.security_code, t.tick_index, t.channel, t.tick_time,
                    t.tick_type, t.buy_order_no, t.sell_order_no, t.price, t.qty,
                    t.trade_money, t.tick_bs_flag);
        break;
    }
    case QuoteMsgType::SZSE_L2_ORDER: {
        const auto& o = tick_quote->szse_l2_order;
        printf("rebuild_szse_l2_order: { security_code = %s, channel_no = %d, appl_seq_num = %ld, "
                    "price = %ld, qty = %ld, side = %c, transact_time = %ld, ord_type = %c }\n",
                    o.security_code, o.channel_no, o.appl_seq_num,
                    o.price, o.qty, o.side, o.transact_time, o.ord_type);
        break;
    }
    case QuoteMsgType::SZSE_L2_TRADE: {
        const auto& t = tick_quote->szse_l2_trade;
        printf("rebuild_szse_l2_trade: { security_code = %s, channel_no = %d, appl_seq_num = %ld, "
                    "bid_appl_seq_num = %ld, offer_appl_seq_num = %ld, price = %ld, qty = %ld, "
                    "exec_type = %c, transact_time = %ld }\n",
                    t.security_code, t.channel_no, t.appl_seq_num,
                    t.bid_appl_seq_num, t.offer_appl_seq_num, t.price, t.qty,
                    t.exec_type, t.transact_time);
        break;
    }
    default:
        printf("rebuild_tick_quote unknown: { msg_type = %d, exchange_index = %d }\n", (int)msg_type, (int)exchange_index);
        break;
    }
}
