#include <XeleSecuritiesTraderApi.h>
#include <cstdio>
#include <string>
#include <cstdarg>
#include <map>
#include <cstring>
#include <unistd.h>
#include <iostream>
#include <sstream>
#include <fstream>
#include "sys/socket.h"

/*
 * 此处是可以进行合约、买卖方向、价格的选择，当demo运行时选择了手动填时，需要按照提示手动输入这些参数，然后demo就会使用输入的合约、方向、价格报单
 * */
struct Securities{
  bool manual_input;
  std::string securities;
  char direction;
  double price;
  unsigned int volumn;
  bool isBatch; ///是否批量报单，若是，则需输入批量报单参数
  ///批量报单类型 ('1'表示等量拆单 '2'表示递减拆单 '3'表示多证券委托组合)
  TXeleBatchType                  BatchType;
  ///单笔上限(等量拆单和递减拆单类型使用,等量拆单时表示单笔报单数量，递减拆单时表示递减起始值)
  TXeleVolumeType                 MaxOrderQty;
  ///递减数量(递减拆单类型使用)
  TXeleVolumeType                 DecreaseQty;
  ///多证券委托组合数量，表示多证券委托的数量（多证券委托组合使用）
  TXeleVolumeType                 BatchOrderQty;
};

Securities g_manual_input_secutities;

#define PRINT_INFO(fmt, ...) printf(fmt,  ##__VA_ARGS__); printf("\n")

#define MAX_QUERY_NUM 500

///报单管理，demo中管理报单状态，成交数量，报单数量等信息
struct OrderManager{
  std::string SecuritiesID;
  char OrderStatus;
  unsigned int Volume;
  unsigned int TradeVolume;
  unsigned int LeavesVolume;
  double UseMoney;
};

///账户资金管理，demo中只管理了可用资金，在使用时可以根据需要选择感兴趣的信息
struct AccountFund{
  ///可用资金
  double AvailableFund;
};

///账户持仓管理，demo中只管理了可用持仓，在使用时可以根据需要选择感兴趣的信息
struct AccountPosition{
  ///可用持仓数量
  unsigned long AvailablePosition;
};

XeleSecuritiesTraderApi* pUserApi = nullptr;

//demo维护的全局请求ID
unsigned int g_RequestID = 0;
//demo希望操作的合约号
std::string g_SecuritiesID;
std::string g_accountID;
std::string g_password;
double g_fundamt;
char g_direction;
int g_node;
char g_market;
void init_g_param(){
  std::string tmp;
  PRINT_INFO("input accountID : ");
  std::cin >> g_accountID;
  PRINT_INFO("input passWord : ");
  std::cin >> g_password;
  PRINT_INFO("input node : ");
  std::cin >> tmp;
  g_node = std::stoi(tmp);
  PRINT_INFO("input market : ");
  std::cin >> tmp;
  g_market = tmp[0];
  g_fundamt = 100.33;
  g_direction = XELE_TRANSFER_DIR_SS_2_SZ;
};

class ChameleonDemo {
 public:
  ChameleonDemo() = default;
  explicit ChameleonDemo(const std::string& value){
    value_=value;
  };
  explicit ChameleonDemo(double d){
    std::stringstream s;
    s<<d;
    value_=s.str();
  };
  explicit ChameleonDemo(const char* c){
    value_=c;
  };

 public:
  operator std::string() const{
    return value_;
  };
  operator double() const{
    return atof(value_.c_str());
  };
  operator char () const{
    return value_[0];
  };
  operator int () const{
    return atoi(value_.c_str());
  };
 private:
  std::string value_;
};

std::string trim(std::string const& source, char const* delims = " \t\r\n") {
  std::string result(source);
  std::string::size_type index = result.find_last_not_of(delims);
  if(index != std::string::npos)
    result.erase(++index);

  index = result.find_first_not_of(delims);
  if(index != std::string::npos)
    result.erase(0, index);
  else
    result.erase();
  return result;
}

class ConfigFileDemo {
 private:
  std::map<std::string,ChameleonDemo> content_;
  ChameleonDemo prefixChameleon_;

 public:
  explicit ConfigFileDemo(std::string const& configFilePath){
    std::ifstream file(configFilePath.c_str());

    if(!file){
      PRINT_INFO("config param file not exist,path [%s]",configFilePath.c_str());
      exit(0);
    }

    std::string line;
    std::string name;
    std::string value;
    std::string inSection;
    unsigned long posEqual;
    while (std::getline(file,line)) {

      if (! line.length()) continue;

      if (line[0] == '#') continue;
      if (line[0] == ';') continue;

      if (line[0] == '[') {
        inSection=trim(line.substr(1,line.find(']')-1));
        continue;
      }

      posEqual=line.find('=');
      name  = trim(line.substr(0,posEqual));
      value = trim(line.substr(posEqual+1));

      content_[inSection+'/'+name]=ChameleonDemo(value);
    }
  };

  ChameleonDemo const& Value(std::string const& entry) const{
    auto ci = content_.find('/' + entry);
    if(ci == content_.end()){
      if(entry == "PcPrefix"){
        return prefixChameleon_;
      }

      PRINT_INFO("key [%s] not exist",entry.c_str());
      exit(0);
    } else{
      return ci->second;
    }
  };

  ChameleonDemo const& Value(std::string const& section, std::string const& entry) const{
    auto ci = content_.find(section + '/' + entry);

    if (ci == content_.end()){
      PRINT_INFO("key [%s] not exist",entry.c_str());
      exit(0);
    }

    return ci->second;
  };
  ChameleonDemo const& Value(std::string const& section, std::string const& entry, double value){
    try {
      return Value(section, entry);
    } catch(const char *) {
      return content_.insert(std::make_pair(section+'/'+entry, ChameleonDemo(value))).first->second;
    }
  };
  ChameleonDemo const& Value(std::string const& section, std::string const& entry, std::string const& value){
    try {
      return Value(section, entry);
    } catch(const char *) {
      return content_.insert(std::make_pair(section+'/'+entry, ChameleonDemo(value))).first->second;
    }
  };
};

class DemoSpi : public XeleSecuritiesTraderSpi{
 public:
  DemoSpi(){
    PRINT_INFO("This Is Test Manager And Counter Interface");
  };
  ~DemoSpi() override{
    PRINT_INFO("This Is Test Manager And Counter Interface End");
  };

  //表明具有资金调拨的相关权限
  bool canFundTransfer{};
  //表明具有柜台查询权限
  bool canQuery{};
  //表明具有柜台报、撤单权限
  bool canOrder{};
  //用户自己管理的本地最大编号，在每次登录柜台成功后，会得到当前柜台的本地最大编号，在报撤单时，可以使用该字段来进行报撤单管理
  unsigned int maxUserLocalID{};

  //可以使用用户本地报单编号（UserLocalID）来管理报单，也可以使用柜台报单编号（OrderSysID）来管理报单
  /*
   * UserLocalID:该字段是由用户维护的全局标记，全局递增，艾科柜台会进行该字段合法性检查。用户可以使用该字段来唯一标记本地的一条报、撤单信息
   * OrderSysID:该字段是艾科柜台维护全局标记，用户可以使用该字段来唯一标记本地的一条报、撤单信息；作用同UserLocalID。
   * demo中使用OrderSysID来进行管理
   * */
  std::map<unsigned int,OrderManager> orderManagerMap;

  //平台可用竞价网关（格式是1,2,3等以逗号分隔)
  std::string bidGateWays;

  //平台可用债券网关（格式是1,2,3等以逗号分隔)
  std::string bondGateWays;

  //本地维护的账户资金信息
  std::map<std::string,AccountFund> accountFundMap;

  //本地维护的账户持仓信息，包含账户下不同合约的持仓信息
  std::map<std::string,AccountPosition> accountPositionMap;

  //更新本地维护的持仓、资金等信息
  void updateAccountPosition(const std::string& SecuritiesID,const char Direction,const unsigned int OrgSysID){
    //update本地的持仓信息
    if(accountPositionMap.find(SecuritiesID) != accountPositionMap.end()){
      if(Direction == XELE_ORDER_BUY){
        accountPositionMap[SecuritiesID].AvailablePosition += orderManagerMap[OrgSysID].TradeVolume;
      } else if(Direction == XELE_ORDER_SELL){
        accountPositionMap[SecuritiesID].AvailablePosition -= orderManagerMap[OrgSysID].TradeVolume;
      }
    }

    orderManagerMap.erase(OrgSysID);
  };


  void updateAccountInfo(const std::string& AccountID,const std::string& SecuritiesID,const char Direction,const unsigned int Volume,const double LimitPrice){
    //报单出现异常后，恢复本地的持仓信息
    if(accountPositionMap.find(SecuritiesID) != accountPositionMap.end()){
      if(Direction == XELE_ORDER_BUY){
        accountPositionMap[SecuritiesID].AvailablePosition += Volume;
      } else if(Direction == XELE_ORDER_SELL){
        accountPositionMap[SecuritiesID].AvailablePosition -= Volume;
      }
    }

    //报单出现异常后，恢复本地的资金信息
    if(Direction == XELE_ORDER_BUY){
      accountFundMap[AccountID].AvailableFund += Volume * LimitPrice;
    } else if(Direction == XELE_ORDER_SELL){
      accountFundMap[AccountID].AvailableFund -= Volume * LimitPrice;
    }
  };

 private:
  ///////////////////////////////
  /////// 系统相关 //////////////
  //////////////////////////////

  ///艾科管理中心登录应答,当只有登录管理中心的需求时，收到该回报即可进行管理中心相关接口操作
  void onRspLoginManager(CXeleRspUserLoginManagerField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      canFundTransfer = true;
      PRINT_INFO("now can use manager interface");
    } else{
      PRINT_INFO("login manager error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///艾科柜台登录应答,当需要管理中心接口可用，但是只需求艾科柜台查询接口可用时，收到该回报即可进行操作
  void onRspLogin(CXeleRspUserLoginField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      canQuery = true;
      maxUserLocalID = pRspField->MaxUserLocalID;
      maxUserLocalID += 1;
      PRINT_INFO("now can use query interface");
    } else{
      PRINT_INFO("login counter error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///添加交易链路应答,当收到该回报时，标记艾科柜台报、撤单接口可用
  void onRspInitTrader(CXeleRspInitTraderField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      canOrder = true;
      PRINT_INFO("now can use order interface");
    } else{
      PRINT_INFO("create order link error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///密码更新应答
  void onRspUpdatePwd(CXeleRspUserPasswordUpdateField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      PRINT_INFO("update password success");
      PRINT_INFO("new info:");
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("NewPassword: %s",pRspField->NewPassword);
      PRINT_INFO("OldPassword: %s",pRspField->OldPassword);
    } else{
      PRINT_INFO("UpdatePwd error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///艾科管理中心登出应答
  void onRspLogoutManager(CXeleRspUserLogoutManagerField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test LogoutManager pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      canFundTransfer = false;
      PRINT_INFO("now can't use manager interface");
    } else{
      PRINT_INFO("logout manager error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///艾科柜台登出应答
  void onRspLogout(CXeleRspUserLogoutField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test Logout pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      canQuery = false;
      PRINT_INFO("now can't use query interface");
    } else{
      PRINT_INFO("logout counter error,ErrID[%d],ErrMsg[%s]",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///当客户端与管理中心服务端查询通信连接断开时，该方法被调用。
  ///该回报触发条件为客户主动发起登出请求，或者链路发生了断连。
  ///出现以上情况时，若不是主动发起登出请求，建议先发起登出请求，再重新登录，即可再次使用相关接口
  void onFrontManagerQueryDisconnected(int nReason) override{
    canFundTransfer = false;
    PRINT_INFO("need relogin");
  };

  ///当客户端与服务端查询通信连接断开时，该方法被调用。
  ///该回报触发条件为客户主动发起登出请求，或者链路发生了断连。
  ///出现以上情况时，若不是主动发起登出请求，建议先发起登出请求，再重新登录，即可再次使用相关接口
  void onFrontQueryDisconnected(int nReason) override{
    canQuery = false;
    PRINT_INFO("need relogin");
  };

  ///当客户端与服务端交易通信连接断开时，该方法被调用。
  ///当收到该回调，表示当前连接失去了报、撤单功能.
  ///如需恢复，参考OnFrontQueryDisconnected处理方法
  void onFrontTradeDisconnected(int nReason) override{
    canOrder = false;
    PRINT_INFO("need re-login");

    ///***当盘中发生了断连，可以采取如下的处理方式，来重新获取操作权限***///
    ///当这三种回调被调用时onFrontManagerQueryDisconnected、onFrontQueryDisconnected、onFrontTradeDisconnected可以进行如下异常处理
    ///可以选择三种回调中某一种进行处理
    ///可以在回调函数中处理，也可以进行实时的异常检测另起线程处理
    ///*********************************///
    ///先发送登出请求，取消用户注册权限
    pUserApi->reqLogout(g_accountID.data(),g_RequestID++);
    PRINT_INFO("reqLogout");
    sleep(2);
    ///此处等待所有连接都断开后，再进行下一步处理
    while (true) {
      PRINT_INFO("wait !canOrder && !canQuery && !canFundTransfer");
      sleep(1);
      if(!canOrder && !canQuery && !canFundTransfer)
        break;
    }
    PRINT_INFO("now api disconnect,start re-login");

    ///发送登录请求，重新获取账户权限
    while (pUserApi->reqLogin("./api_config.txt", g_accountID.data(), g_password.data(),
                              g_node, g_market, g_RequestID++)) {
      PRINT_INFO("call reqLogin fail,try again");
      sleep(3);
    }
    PRINT_INFO("relogin send success,now wait a moment,then you can reuse other Api interface");
    ///***登录请求发送完成后，就可以等待onRspLoginManager、onRspLogin、onRspInitTrader回调响应，再调用其他接口***///
  };

  ///api内部消息打印回调
  void onApiMsg(int ret, const char *strFormat, ...) override{
    char strLog[2048]{};
    va_list arglist;
    va_start(arglist, strFormat);
    vsprintf(strLog,strFormat,arglist);
    va_end(arglist);
    //1:error, 0:normal
    if (ret) {
      PRINT_INFO("Error:%s", strLog);
    } else {
      PRINT_INFO("%s", strLog);
    }
  };

  ///////////////////////////////
  /////// 证券、期权公用 /////////
  //////////////////////////////

  ///报单应答
  void onRspInsertOrder(CXeleRspOrderInsertField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //当收取到onRspInsertOrder消息时，表明该条报单信息已经被艾科柜台接收，正在等待处理
    if(pRspInfo->ErrorID == 0){
      //本地记录下当前报单的状态，方便在需要时进行处理
      orderManagerMap[pRspField->OrderSysID].OrderStatus = ODRSTAT_REPORTED;
    } else{
      //当报单应答中发生ErrID不为0的情况，表明该条报单没有通过艾科柜台的风控检查
      //常见的异常为资金或者持仓风控未通过
      //柜台的异常回报信息（CXeleRspInfo）中有该次异常的错误码以及具体原因
      PRINT_INFO("onRspInsertOrder ErrMsg: %s",pRspInfo->ErrorMsg);
      if(orderManagerMap.find(pRspField->OrderSysID) != orderManagerMap.end()){
        //报单出现异常后，修改本地的报单状态
        orderManagerMap[pRspField->OrderSysID].OrderStatus = ODRSTAT_ERROR;
      }
      //报单出现异常后，恢复本地的持仓信息，恢复本地的资金信息
      updateAccountInfo(pRspField->AccountID,pRspField->SecuritiesID,pRspField->Direction,pRspField->Volume,pRspField->LimitPrice);

      canFundTransfer = false;
      canOrder = false;
      canQuery = false;
    }
  };

  ///报单错误回报
  void onErrRtnInsertOrder(CXeleRspOrderInsertField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //当收该条回报信息时，表明该条报单没有通过交易所风控检查，出现了异常
    //常见的异常为资金或者持仓风控未通过
    //柜台的异常回报信息（CXeleRspInfo）中有该次异常的错误码以及具体原因
    PRINT_INFO("ErrRtnInserOrder ErrMsg: %s",pRspInfo->ErrorMsg);
    //本地记录下当前报单的状态，方便在需要时进行处理
    orderManagerMap[pRspField->OrderSysID].OrderStatus = ODRSTAT_ERROR;

    //报单出现异常后，恢复本地的持仓信息，恢复本地的资金信息
    updateAccountInfo(pRspField->AccountID,pRspField->SecuritiesID,pRspField->Direction,pRspField->Volume,pRspField->LimitPrice);

    canFundTransfer = false;
    canOrder = false;
    canQuery = false;
  };

  ///撤单应答
  void onRspCancelOrder(CXeleRspOrderActionField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //当收取到onRspCancelOrder消息时，表明该条撤单信息已经被艾科柜台接收，正在等待处理
    if(pRspInfo->ErrorID == 0){
      //本地记录下当前报单的状态，方便在需要时进行处理
      orderManagerMap[pRspField->OrderSysID].OrderStatus = ODRSTAT_REPORTED;
      //当撤单请求通过艾科柜台和交易所风控检查后，后续被撤单的信息会通过OnRtnOrder接口返回，此时可以进行本地的持仓和资金信息管理
    } else{
      //当撤单应答中发生ErrID不为0的情况，表明该条撤单没有通过艾科柜台的风控检查
      //常见的异常为被撤单不存在，可能已经成交或者被撤单信息填写错误
      //柜台的异常回报信息（CXeleRspInfo）中有该次异常的错误码以及具体原因
      PRINT_INFO("onRspCancelOrder ErrMsg: %s",pRspInfo->ErrorMsg);
      if(orderManagerMap.find(pRspField->OrderSysID) != orderManagerMap.end()){
        orderManagerMap[pRspField->OrderSysID].OrderStatus = ODRSTAT_ERROR;
      }

      //撤单出现异常后，表明该条撤单信息未被响应，被撤单信息未发生任何变化，故不需要对本地维护的资金和持仓信息进行更新
    }
  };

  ///撤单错误回报
  void onErrRtnCancelOrder(CXeleRspOrderActionField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //当收该条回报信息时，表明该条撤单没有通过交易所风控检查，出现了异常
    //常见的异常为被撤单不存在，可能已经成交或者被撤单信息填写错误
    //柜台的异常回报信息（CXeleRspInfo）中有该次异常的错误码以及具体原因
    PRINT_INFO("onErrRtnCancelOrder ErrMsg: %s",pRspInfo->ErrorMsg);
    //本地记录下当前撤单的状态，方便在需要时进行处理
    orderManagerMap[pRspField->OrderSysID].OrderStatus = ODRSTAT_ERROR;
    //撤单出现异常后，表明该条撤单信息未被响应，被撤单信息未发生任何变化，故不需要对本地维护的资金和持仓信息进行更新
  };

  ///报单回报
  void onRtnOrder(CXeleRtnOrderField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //该回报信息会在下列情况发生回调：
    //1、报单被交易所接受
    //2、报单在交易所发生了成交
    //3、撤单请求被交易所响应，返回被撤单信息

    ///用户的持仓管理可以在该回报中进行

    ///报单回报没有异常场景，可以不用判断ErrorID
    PRINT_INFO("  Test reqInsertOrder pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      auto& orderManager = orderManagerMap[pRspField->OrderSysID];
      orderManager.Volume = pRspField->Volume;
      orderManager.TradeVolume = pRspField->TradeVolume;
      orderManager.LeavesVolume = pRspField->LeavesVolume;
      orderManager.OrderStatus = pRspField->OrderStatus;
      if(pRspField->OrderStatus == ODRSTAT_TRADED || pRspField->OrderStatus == ODRSTAT_PACTION){
        updateAccountPosition(pRspField->SecuritiesID,pRspField->Direction,pRspField->OrderSysID);
      }

      //demo test:当报单到达交易所之后,查询报单
      sleep(1);
      if (pRspField->OrderStatus == ODRSTAT_REPORTED) {
        CXeleReqQryOrderField reqQryOrderField{};
        memset(&reqQryOrderField, 0, sizeof(CXeleReqQryOrderField));
        memcpy(reqQryOrderField.SecuritiesID, pRspField->SecuritiesID, strlen(pRspField->SecuritiesID));

        /// 当数据库的报单数量很多时，为了避免查询占用太多时间，除了使用精确查询（使用OrderSysID或者UserLocalId）外，还可以使用分页查询来减少单次查询回报的数量
        /// 查询的起始值，表示从当前条件下的第0条数据开始
        reqQryOrderField.StartNum = 0;
        /// 单次查询的数据量，表示当次查询共100000条数据，艾科柜台最大单次查询数量为10000
        reqQryOrderField.Num = MAX_QUERY_NUM;

        PRINT_INFO("  Test reqQryOrder");
        pUserApi->reqQryOrder(reqQryOrderField, g_RequestID++);
      }

      if (pRspField->OrderStatus == ODRSTAT_ACTIONED) {
        CXeleReqQryTradeField reqQryTradeField{};
        memset(&reqQryTradeField, 0, sizeof(CXeleReqQryTradeField));
        reqQryTradeField.OrderSysID = pRspField->OrderSysID;

        PRINT_INFO("  Test reqCancelOrder pass");
        PRINT_INFO("=======================");
        pUserApi->reqQryTrade(reqQryTradeField, g_RequestID++);
        PRINT_INFO("  Test reqQryTrade");
      }
    }
  };

  ///成交回报
  void onRtnTrade(CXeleRtnTradeField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    ///用户的资金管理可以在该回报中进行

    ///成交回报没有异常场景，可以不用判断ErrorID
    if(pRspInfo->ErrorID == 0){
      auto& orderManager = orderManagerMap[pRspField->OrderSysID];
      if(pRspField->Direction == XELE_ORDER_SELL){
        accountFundMap[pRspField->AccountID].AvailableFund += pRspField->TradeVolume * pRspField->TradePrice;
      } else if(pRspField->Direction == XELE_ORDER_BUY){
        accountFundMap[pRspField->AccountID].AvailableFund -= pRspField->TradeVolume * pRspField->TradePrice;
      }
      orderManager.UseMoney = accountFundMap[pRspField->AccountID].AvailableFund;
    }
  };

  ///报单查询应答
  void onRspQryOrder(CXeleRspQryOrderField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //查询报单，如果该笔报单发生部分成交或者未成交，则进行撤单操作
    if(pRspInfo->ErrorID == 0){
      if(bIsLast){
        if(pRspField->QryAgain){
          /// 当收到最后一条查询回报后，如果QryAgain字段非真，表明当前条件下数据库中的数据没有全部返回，此时可以再次查询
          CXeleReqQryOrderField reqQryOrderField{};
          memset(&reqQryOrderField, 0, sizeof(CXeleReqQryOrderField));
          memcpy(reqQryOrderField.SecuritiesID, pRspField->SecuritiesID, strlen(pRspField->SecuritiesID));
          /// 使用查询回报中的EndNum字段作为下次查询的起始值，重新规定下次查询的数量，即可实现分页查询，直至完全查询结束为止
          reqQryOrderField.StartNum = pRspField->EndNum;
          reqQryOrderField.Num = MAX_QUERY_NUM;
          pUserApi->reqQryOrder(reqQryOrderField, g_RequestID++);
          return ;
        }
        PRINT_INFO("  Test reqQryOrder pass");
        PRINT_INFO("=======================");
        ///批量报单不撤单，避免多线程报撤单导致异常
        if((pRspField->OrderStatus == ODRSTAT_REPORTED || pRspField->OrderStatus == ODRSTAT_PTRADE) && !g_manual_input_secutities.isBatch){
          CXeleReqOrderActionField actionField{};
          //当进行报撤单请求时，需要对请求数据进行清零操作
          memset(&actionField,0,sizeof(CXeleReqOrderActionField));
          actionField.UserLocalID = maxUserLocalID++;
          actionField.OrigSysID = pRspField->OrderSysID;
          memcpy(actionField.BusinessUnit,"Taction",8);
          actionField.OwnerType = XELE_OWNER_PERSONAL_TYPE;
          actionField.Operway = API_OPERWAY;

          PRINT_INFO("  Test reqCancelOrder");
          pUserApi->reqCancelOrder(actionField,g_RequestID++);
        }
      }
    } else {
      PRINT_INFO("onRspQryOrder Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  };

  ///成交查询应答
  void onRspQryTrade(CXeleRspQryTradeField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //查询成交信息回报中会逐笔回报该笔报单的成交信息，对于多次成交的报单，可以通过bIsLast参数来判断回报是否结束
    //如果需要完整的信息，可以通过bIsLast参数来确认是否已经收到完整的查询结果
    //成交信息中含有时间、价格、数量、交易所等信息，可以自行选择感兴趣的信息
    if (pRspInfo->ErrorID == 0) {
      //此处打印每笔成交的价格
      PRINT_INFO("TradePrice %f", pRspField->TradePrice);
      //Example：bIsLast参数用法
      //使用此参数来计算成交均价
      if(g_SecuritiesID == std::string(pRspField->SecuritiesID)){
        static double totalMoney = 0;
        totalMoney += pRspField->TradePrice * pRspField->TradeVolume;
        if (bIsLast) {
          PRINT_INFO("  Test reqQryTrade pass");
          PRINT_INFO("=======================");
          PRINT_INFO("average price: %f", totalMoney / pRspField->TradeVolume);
          totalMoney = 0;
          PRINT_INFO("  Test reqLogout");
          pUserApi->reqLogout(g_accountID.data(), g_RequestID++);
        }
      }
    } else {
      PRINT_INFO("onRspQryTrade Error,Errmsg: %s", pRspInfo->ErrorMsg);
      PRINT_INFO("  Test reqLogout");
      pUserApi->reqLogout(g_accountID.data(), g_RequestID++);
    }
  };

  ///////////////////////////////
  /////// 证券相关 //////////////
  //////////////////////////////

  ///费率(印花税率、过户费率、佣金率、流量费)查询应答
  void onRspQryRate(CXeleRspQryStockFeeField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //佣金费率等会因为不同的合约而产生差异
    //此处打印查询得到的信息
    if(pRspInfo->ErrorID == 0){
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("SecuritiesID: %s",pRspField->SecuritiesID);
      PRINT_INFO("DepartID: %s",pRspField->DepartID);
      PRINT_INFO("StampTaxRate: %f",pRspField->StampTaxRate);
      PRINT_INFO("TransferFee: %f",pRspField->TransferFee);
      PRINT_INFO("CommRate: %f",pRspField->CommRate);
      PRINT_INFO("TrafficFee: %f",pRspField->TrafficFee);
      PRINT_INFO("Market: %c",pRspField->Market);
    } else{
      PRINT_INFO("onRspQryRate Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  };

  ///集中交易资金调入艾科柜台应答
  void onRspInFund(CXeleRspCapTransferField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test reqInFund pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      //得到集中交易资金调入艾科柜台成功的信息后，更新本地的资金信息
      accountFundMap[pRspField->AccountID].AvailableFund += pRspField->Fundamt;
    } else{
      PRINT_INFO("onRspInFund Error[%d],Errmsg: %s",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///集中交易资金调出艾科柜台应答
  void onRspOutFund(CXeleRspCapTransferField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test reqOutFund pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      //得到集中交易资金调出艾科柜台成功的信息后，更新本地的资金信息
      accountFundMap[pRspField->AccountID].AvailableFund -= pRspField->Fundamt;
    } else{
      PRINT_INFO("onRspOutFund Error[%d],Errmsg: %s",pRspInfo->ErrorID,pRspInfo->ErrorMsg);
    }
  };

  ///集中交易资金调拨艾科柜台明细查询应答
  void onRspQryInOutFundRecord(CXeleRspQryCapTransferRecordField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    //该回报中会逐笔打印出金、入金的信息，此处打印查询得到的信心
    if(pRspInfo->ErrorID == 0){
      PRINT_INFO("OrgID: %s", pRspField->OrgID);
      PRINT_INFO("AccountID: %s", pRspField->AccountID);
      PRINT_INFO("Fundamt: %f", pRspField->Fundamt);
      PRINT_INFO("Direction: %c", pRspField->Direction);
      PRINT_INFO("Fundamt: %u", pRspField->Sno);
      PRINT_INFO("Time: %s", pRspField->Time);
      PRINT_INFO("Market: %c", pRspField->Market);
      if(bIsLast){
        PRINT_INFO("-----End-----");
      }
    } else{
      PRINT_INFO("onRspQryInOutFundRecord Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  };

  ///跨柜台资金调拨应答
  void onRspBTFundTransfer(CXeleRspBTCapTransferManagerField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID != 0){
      PRINT_INFO("  Test reqBTFundTransfer fail");
      //柜台间的资金调拨错误，常见的可能是由于资金账号验证不通过,或者对应柜台程序未启动
      PRINT_INFO("onRspBTFundTransfer Error,Errmsg: %s",pRspInfo->ErrorMsg);
      char in;
      PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
      /// 阻塞回报线程时间过长会导致Api心跳超时，此处是为了手动验证柜台功能，客户不应该采用此种方式
      while(std::cin >> in){
        if(in != 'c'){
          exit(0);
        } else {
          CXeleReqQryBTCapTransferManagerField qryBTCapTransferManagerField{};
          //对于数据结构需要在使用前进行清0处理
          memset(&qryBTCapTransferManagerField, 0, sizeof(CXeleReqQryBTCapTransferManagerField));
          memcpy(qryBTCapTransferManagerField.AccountID, g_accountID.data(), g_accountID.length());

          PRINT_INFO("  Test reqQryBTCapTransferRecord");
          pUserApi->reqQryBTFundTransferRecord(qryBTCapTransferManagerField, g_RequestID++);
          break;
        }
      }
    }
  };

  ///跨柜台资金调拨结果返回
  void onRtnBTFundTransfer(CXeleRtnBtCapTransferManagerField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test reqBTFundTransfer pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      if(pRspField->Direction == XELE_TRANSFER_DIR_SS_2_SZ){
        accountFundMap[pRspField->AccountID].AvailableFund -= pRspField->Fundamt;
      } else if(pRspField->Direction == XELE_TRANSFER_DIR_SZ_2_SS){
        accountFundMap[pRspField->AccountID].AvailableFund += pRspField->Fundamt;
      }
    } else{
      PRINT_INFO("onRtnBTFundTransfer Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }

    CXeleReqQryBTCapTransferManagerField qryBTCapTransferManagerField{};
    //对于数据结构需要在使用前进行清0处理
    memset(&qryBTCapTransferManagerField, 0, sizeof(CXeleReqQryBTCapTransferManagerField));
    memcpy(qryBTCapTransferManagerField.AccountID, g_accountID.data(), g_accountID.length());

    PRINT_INFO("  Test reqQryBTCapTransferRecord");
    pUserApi->reqQryBTFundTransferRecord(qryBTCapTransferManagerField, g_RequestID++);
  };

  ///跨柜台资金调拨记录查询应答
  void onRspQryBTFundTransferRecord(CXeleRspQryBTCapTransferManagerField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if (pRspInfo->ErrorID == 0){
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("Fund: %f",pRspField->Fund);
      PRINT_INFO("Direction: %c",pRspField->Direction);
      PRINT_INFO("Status: %c",pRspField->Status);
      PRINT_INFO("isAutoTrans: %c",pRspField->isAutoTrans);

      //当柜台间资金调拨发生了异常时，以下字段用来标记异常资金处理结果
      PRINT_INFO("IsFundReversal: %c",pRspField->IsFundReversal);
      PRINT_INFO("ReversalCounter: %s",pRspField->ReversalCounter);
      PRINT_INFO("IsFundReversal: %c",pRspField->ReversalResult);
    } else {
      PRINT_INFO("onRspBTFundTransferRecord Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }

    if(bIsLast){
      PRINT_INFO("  Test reqQryBTFundTransferRecord pass");
      PRINT_INFO("=======================");
      usleep(200000);
      CXeleReqQryPositionField qryPositionField{};
      memset(&qryPositionField, 0, sizeof(CXeleReqQryPositionField));
      //查询该资金账号下所有可用持仓，更新到本地
      memcpy(qryPositionField.AccountID, g_accountID.data(), g_accountID.length());
      pUserApi->reqQryPosition(qryPositionField, g_RequestID++);
      PRINT_INFO("  Test reqQryPosition");
    }
  };

  ///沪深柜台资金查询应答
  void onRspQryBtFund(CXeleRspQryClientAccountFundManagerField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLase) override{
    PRINT_INFO("  Test reqQryBtFund pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      //该回报中使用Market字段来标记当前结果是上交柜台还是深交柜台或者沪深柜台的总资金
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("AvailableFund: %f",pRspField->AvailableFund);
      PRINT_INFO("AvailableCash: %f",pRspField->AvailableCash);
      PRINT_INFO("FrozeCapital: %f",pRspField->FrozeCapital);
      PRINT_INFO("FrozenFee: %f",pRspField->FrozenFee);
      PRINT_INFO("UsedFee: %f",pRspField->UsedFee);
      PRINT_INFO("InitTotalFund: %f",pRspField->InitTotalFund);
      PRINT_INFO("SellFund: %f",pRspField->SellFund);
      PRINT_INFO("BuyFund: %f",pRspField->BuyFund);
      PRINT_INFO("Market: %c",pRspField->Market);
    } else{
      PRINT_INFO("onRspQryBtFund Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }

    CXeleReqBTCapTransferManagerField btCapTransferManagerField{};
    //对于数据结构需要在使用前进行清0处理
    memset(&btCapTransferManagerField, 0, sizeof(CXeleReqBTCapTransferManagerField));
    memcpy(btCapTransferManagerField.AccountID, g_accountID.data(), g_accountID.length());
    btCapTransferManagerField.Fundamt = g_fundamt;
    btCapTransferManagerField.Direction = g_direction;

    PRINT_INFO("  Test reqBTCapTransfer");
    auto ret = pUserApi->reqBTFundTransfer(btCapTransferManagerField, g_RequestID++);
    if (ret) {
      PRINT_INFO("reqBTCapTransfer interface call fail,ret [%d]", ret);
      exit(0);
    }
  }
  ///用户可用网关信息查询应答(部分券商使用)
  void onRspQryGateWaysRecord(CXeleRspQryAvailableGateWayRecordField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test reqQryGateWaysRecord pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      //该回报中使用Market字段来标记当前结果是上交柜台还是深交柜台或者沪深柜台的总资金
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("Market: %c",pRspField->Market);
      PRINT_INFO("BidGateWays: %s",pRspField->BidGateWays);
      PRINT_INFO("BondGateWays: %s",pRspField->BondGateWays);
      bidGateWays = pRspField->BidGateWays;
      bondGateWays = pRspField->BondGateWays;
    } else{
      PRINT_INFO("onRspQryGateWaysRecord Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  };

  ///程序化风控信息查询应答(部分券商使用)
  void onRspQryPrcProgramInfo(CXeleRspQryPrcProgramInfoField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override {
    PRINT_INFO("  Test onRspQryPrcProgramInfo pass");
    PRINT_INFO("=======================");
    if(pRspInfo->ErrorID == 0){
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("Market: %c",pRspField->Market);
      PRINT_INFO("PrcProgramType: %s",pRspField->PrcProgramType);
      PRINT_INFO("Enable: %c",pRspField->Enable);
      if (pRspField->PrcProgramType == PRCPROGRAM_DAILY_LIMIT) { ///当风控类别为全天报撤笔数限制风控时
        PRINT_INFO("OrderCountLimit: %lu",pRspField->prcProgrmData.dailyLimit.OrderCountLimit);
        PRINT_INFO("CurrentOrderCount: %lu",pRspField->prcProgrmData.dailyLimit.CurrentOrderCount);
        PRINT_INFO("PrcSecuritiesType: %s",pRspField->prcProgrmData.dailyLimit.PrcSecuritiesType);
      } else if (pRspField->PrcProgramType == PRCPROGRAM_RATE_LIMIT) { ///当风控类别为流速风控时
        PRINT_INFO("timeUnit: %lu",pRspField->prcProgrmData.orderRateLimit.timeUnit);
        PRINT_INFO("RateLimit: %lu",pRspField->prcProgrmData.orderRateLimit.RateLimit);
        PRINT_INFO("PrcSecuritiesType: %s",pRspField->prcProgrmData.orderRateLimit.PrcSecuritiesType);
      }
    } else{
      PRINT_INFO("onRspQryPrcProgramInfo Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  };

  ///证券资金查询应答
  void onRspQryFund(CXeleRspQryStockClientAccountField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test reqQryFund pass");
    PRINT_INFO("=======================");
    if (canFundTransfer){
      CXeleReqQryClientAccountFundManagerField qryClientAccountFundManagerField{};
      //对于数据结构需要在使用前进行清0处理
      memset(&qryClientAccountFundManagerField, 0, sizeof(CXeleReqQryClientAccountFundManagerField));
      memcpy(qryClientAccountFundManagerField.AccountID,g_accountID.data(),g_accountID.length());
      qryClientAccountFundManagerField.Market = g_market;
      PRINT_INFO("  Test reqQryBtFund");
      auto manualInput = [&]{
        char in;
        PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
        /// 阻塞回报线程时间过长会导致Api心跳超时，此处是为了手动验证柜台功能，客户不应该采用此种方式
        while(std::cin >> in){
          if(in == 'c'){
            PRINT_INFO("  check if you have management permissions(api_config.txt of URL is connect to management)");
            PRINT_INFO("if no,use Ctrl+c and modify the api_config.txt of URL,then start again");
            PRINT_INFO("then");
            PRINT_INFO("  Test reqQryBtFund");
            PRINT_INFO("Input accountID and check the fund of account is more than 200");
            std::cin >> qryClientAccountFundManagerField.AccountID;
            break;
          } else {
            PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
          }
        }
      };
      if(pRspInfo->ErrorID == 0){
        //demo中使用资金查询结果来更新本地资金
        accountFundMap[pRspField->AccountID].AvailableFund = pRspField->AvailableFund;
        //demo test
        if(accountFundMap[pRspField->AccountID].AvailableFund < 1000){
          PRINT_INFO("Account Available Money too low");
          manualInput();
        }
      } else {
        PRINT_INFO("onRspQryFund Error,Errmsg: %s",pRspInfo->ErrorMsg);
        manualInput();
      }
      pUserApi->reqQryBtFund(qryClientAccountFundManagerField, g_RequestID++);
    } else {
      usleep(200000);
      /// 没有获取到manager的登录权限，不测试manager的接口，直接测试柜台接口
      CXeleReqQryPositionField qryPositionField{};
      memset(&qryPositionField, 0, sizeof(CXeleReqQryPositionField));
      //查询该资金账号下所有可用持仓，更新到本地
      memcpy(qryPositionField.AccountID, g_accountID.data(), g_accountID.length());
      pUserApi->reqQryPosition(qryPositionField, g_RequestID++);
      PRINT_INFO("  Test reqQryPosition");
    }
    usleep(200000);
    CXeleReqQryAvailableGateWayRecordField reqQryGateWayRecordField;
    memset(&reqQryGateWayRecordField, 0, sizeof(CXeleReqQryAvailableGateWayRecordField));
    memcpy(reqQryGateWayRecordField.AccountID, g_accountID.data(), g_accountID.length());
    pUserApi->reqQryGateWaysRecord(reqQryGateWayRecordField, g_RequestID++);
    PRINT_INFO("  Test reqQryGateWaysRecord");
  };

  ///证券持仓查询应答
  void onRspQryPosition(CXeleRspQryStockPositionField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    CXeleReqQrySecuritiesField reqQrySecuritiesField{};
    memset(&reqQrySecuritiesField,0,sizeof(CXeleReqQrySecuritiesField));
    auto mamualInput = [&]{
      char in;
      PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
      /// 阻塞回报线程时间过长会导致Api心跳超时，此处是为了手动验证柜台功能，客户不应该采用此种方式
      while (std::cin >> in){
        if(in == 'c'){
          PRINT_INFO("input securitiesID:");
          if(g_market == XELE_MARKET_SZ){
            PRINT_INFO("suggest securitiesID is 000001");
          } else {
            PRINT_INFO("suggest securitiesID is 600000");
          }
          std::cin >> reqQrySecuritiesField.SecuritiesID;
          break;
        } else {
          PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
        }
      }
    };
    if(pRspInfo->ErrorID == 0){
      //demo中使用持仓查询结果来更新本地持仓
      AccountPosition accountPosition{};
      accountPosition.AvailablePosition = pRspField->AvailablePosition;
      accountPositionMap.insert(std::make_pair(pRspField->SecuritiesID,accountPosition));
      //demo test:查询最后一个持仓的合约信息
      if(bIsLast){
        PRINT_INFO("  Test reqQryPosition pass");
        PRINT_INFO("=======================");
        usleep(200000);
        memcpy(reqQrySecuritiesField.SecuritiesID,pRspField->SecuritiesID,strlen(pRspField->SecuritiesID));
      }
    } else {
      PRINT_INFO("onRspQryPosition Error,Errmsg: %s",pRspInfo->ErrorMsg);
      mamualInput();
    }
    if(bIsLast) {
      pUserApi->reqQrySecurities(reqQrySecuritiesField, g_RequestID++);
      PRINT_INFO("  Test reqQrySecurities");
    }
  };

  ///证券信息查询应答
  void onRspQrySecurities(CXeleRspQryStockSecuritiesField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    PRINT_INFO("  Test reqQrySecurities pass");
    PRINT_INFO("=======================");
    CXeleReqOrderInsertField orderInsertField{};
    memset(&orderInsertField,0,sizeof(CXeleReqOrderInsertField));
    orderInsertField.UserLocalID = maxUserLocalID++;
    orderInsertField.OrderType = XELE_LIMIT_PRICE_TYPE;
    orderInsertField.TimeCondition = XELE_TIMEINFORCE_TYPE_GFD;
    memcpy(orderInsertField.BusinessUnit,"demotest",8);
    char in{};
    auto manualInput = [&]{
      PRINT_INFO("account [%s] security [%s] position [%ld]",g_accountID.c_str(),pRspField->SecuritiesID,accountPositionMap[pRspField->SecuritiesID].AvailablePosition);
      PRINT_INFO("position is 0,need choice other security to operate");
      PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
      /// 阻塞回报线程时间过长会导致Api心跳超时，此处是为了手动验证柜台功能，客户不应该采用此种方式
      while (std::cin >> in){
        if(in == 'c'){
          PRINT_INFO("  Test reqInsertOrder");
          PRINT_INFO("input security of account [%s]",g_accountID.c_str());
          if(g_market == XELE_MARKET_SZ){
            PRINT_INFO("suggest securitiesID is 000001,suggest price is 20.4");
          } else {
            PRINT_INFO("suggest securitiesID is 600000,suggest price is 8.0");
          }
          std::cin >> orderInsertField.SecuritiesID;
          orderInsertField.Direction = XELE_ORDER_BUY;
          PRINT_INFO("input LimitPrice:");
          std::cin >> orderInsertField.LimitPrice;
          orderInsertField.Volume = 1000;
          orderInsertField.SecuritiesType = '0';
          orderInsertField.Operway = '1';

          accountPositionMap[pRspField->SecuritiesID].AvailablePosition -= orderInsertField.Volume;
          break;
        } else {
          PRINT_INFO("if you want continue demo,please manual input 'c',if you want exit,use Ctrl+c");
        }
      }
    };
    if(g_manual_input_secutities.manual_input){
      memcpy(orderInsertField.SecuritiesID,g_manual_input_secutities.securities.c_str(),g_manual_input_secutities.securities.length());
      orderInsertField.Direction = g_manual_input_secutities.direction;
      orderInsertField.LimitPrice = g_manual_input_secutities.price;
      orderInsertField.Volume = g_manual_input_secutities.volumn;

      orderInsertField.SecuritiesType = '0';
      orderInsertField.Operway = API_OPERWAY;

      PRINT_INFO("  Test reqInsertOrder");
      PRINT_INFO("  use manual input info");
      PRINT_INFO("  securitiesId %s",g_manual_input_secutities.securities.c_str());
      PRINT_INFO("  Direction %c",g_manual_input_secutities.direction);
      PRINT_INFO("  LimitPrice %f",g_manual_input_secutities.price);
      PRINT_INFO("  Volume %u",g_manual_input_secutities.volumn);
      PRINT_INFO("  isBatch %d",g_manual_input_secutities.isBatch);
      if(g_manual_input_secutities.isBatch){
        PRINT_INFO("  BatchType %c",g_manual_input_secutities.BatchType);
        PRINT_INFO("  MaxOrderQty %u",g_manual_input_secutities.MaxOrderQty);
        PRINT_INFO("  DecreaseQty %u",g_manual_input_secutities.DecreaseQty);
        PRINT_INFO("  BatchOrderQty %u",g_manual_input_secutities.BatchOrderQty);
      }
      ///仅部分券商支持指定网关报单
      ///批量报单接口（'1'表示等量拆单 '2'表示递减拆单）暂不支持指定网关报单
      if((!bidGateWays.empty() || !bidGateWays.empty())
          && (!g_manual_input_secutities.isBatch || g_manual_input_secutities.BatchType == BatchMuilt)){
        PRINT_INFO("input TXeleExchangeIDType(type is unsigned int):");
        PRINT_INFO("0 represents unspecified,or chose bidGateWay:in(%s)",bidGateWays.c_str());
        if(!bondGateWays.empty()){
          PRINT_INFO("or chose bondGateWay:in(%s)",bondGateWays.c_str());
        }
        unsigned  int exchangeFrontID = 0;
        std::cin >> exchangeFrontID;
        if(exchangeFrontID != 0
               && bidGateWays.find(std::to_string(exchangeFrontID)) == std::string::npos
               && bondGateWays.find(std::to_string(exchangeFrontID)) == std::string::npos){
          PRINT_INFO("invalid  exchangeFrontID[%d]", exchangeFrontID);
          exit(0);
        }
        orderInsertField.ExchangeFrontID = exchangeFrontID;
        PRINT_INFO("  ExchangeFrontID %d",orderInsertField.ExchangeFrontID);
      }

      if(!g_manual_input_secutities.isBatch){
        pUserApi->reqInsertOrder(orderInsertField, g_RequestID++);
      }
      else{
        CXeleReqBatchOrderInsertField batchOrderInsertField;
        memset(&batchOrderInsertField, 0, sizeof(CXeleReqBatchOrderInsertField));
        batchOrderInsertField.BatchType = g_manual_input_secutities.BatchType;
        batchOrderInsertField.MaxOrderQty = g_manual_input_secutities.MaxOrderQty;
        batchOrderInsertField.DecreaseQty = g_manual_input_secutities.DecreaseQty;
        batchOrderInsertField.BatchOrderQty = g_manual_input_secutities.BatchOrderQty;
        batchOrderInsertField.UserLocalID = orderInsertField.UserLocalID;
        memcpy(batchOrderInsertField.SecuritiesID,g_manual_input_secutities.securities.c_str(),g_manual_input_secutities.securities.length());
        batchOrderInsertField.Direction = g_manual_input_secutities.direction;
        batchOrderInsertField.LimitPrice = g_manual_input_secutities.price;
        batchOrderInsertField.Volume = g_manual_input_secutities.volumn;
        batchOrderInsertField.OrderType = XELE_LIMIT_PRICE_TYPE;
        memcpy(batchOrderInsertField.BusinessUnit,"demotest",8);
        if(g_manual_input_secutities.BatchType == BatchMuilt){
          if(batchOrderInsertField.Volume > 1000){
            batchOrderInsertField.Volume = 1000;
          }

          for (int index = 0; index < g_manual_input_secutities.BatchOrderQty; ++index) {
            if(index > 0){
              orderInsertField.UserLocalID = maxUserLocalID++;
            }
            memcpy(&batchOrderInsertField.ReqOrderInsertField[index],&orderInsertField, sizeof(orderInsertField));
          }
        }
        pUserApi->reqInsertBatchOrder(batchOrderInsertField,g_RequestID++);
      }
    } else {
      //demo test:将最后一个持仓合约卖出
      //回报中涉及合约的一些基础信息，可以根据需要选择感兴趣的信息
      PRINT_INFO("SecuritiesID: %s",pRspField->SecuritiesID);
      if(bIsLast){
        if(pRspInfo->ErrorID == 0){

          if(accountPositionMap.find(pRspField->SecuritiesID) != accountPositionMap.end()){
            if(accountPositionMap[pRspField->SecuritiesID].AvailablePosition != 0){
              memcpy(orderInsertField.SecuritiesID,pRspField->SecuritiesID,strlen(pRspField->SecuritiesID));
              orderInsertField.Direction = XELE_ORDER_SELL;
              orderInsertField.LimitPrice = pRspField->PreSettlePrice;
              accountPositionMap[pRspField->SecuritiesID].AvailablePosition > 1000 ? orderInsertField.Volume = 1000 : orderInsertField.Volume = accountPositionMap[pRspField->SecuritiesID].AvailablePosition;
              orderInsertField.SecuritiesType = pRspField->SecuritiesType;
              orderInsertField.Operway = API_OPERWAY;

              accountPositionMap[pRspField->SecuritiesID].AvailablePosition -= orderInsertField.Volume;
            } else {
              manualInput();
            }
          } else {
            manualInput();
          }
        } else{
          manualInput();
        }
        PRINT_INFO("  Test reqInsertOrder");
        pUserApi->reqInsertOrder(orderInsertField,g_RequestID++);
      }
    }
  };

  ///权益查询（目前支持新股、配股额度查询）应答
  void onRspQryRightsAndInterests(CXeleRspQryStockQuotaField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("SecuritiesType: %c",pRspField->SecuritiesType);
      PRINT_INFO("SecuritiesID: %s",pRspField->SecuritiesID);
      PRINT_INFO("StockQuota: %lu",pRspField->StockQuota);
      PRINT_INFO("StockHoldQuota: %lu",pRspField->StockHoldQuota);
      PRINT_INFO("StockAvlQuota: %lu",pRspField->StockAvlQuota);
      PRINT_INFO("MainBoardStockQuota: %lu",pRspField->MainBoardStockQuota);
      PRINT_INFO("TechBoardStockQuota: %lu",pRspField->TechBoardStockQuota);
    } else{
      PRINT_INFO("onRspQryRightsAndInterests Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  };

  ///证券集中交易资金查询响应
  void onRspQryCentralTradingFund(CXeleRspQryCentralTradingFundField *pRspField, CXeleRspInfo *pRspInfo, int nRequestID, bool bIsLast) override{
    if(pRspInfo->ErrorID == 0){
      PRINT_INFO("AccountID: %s",pRspField->AccountID);
      PRINT_INFO("Fundavl: %f",pRspField->Fundavl);
    } else{
      PRINT_INFO("onRspQryCentralTradingFund Error,Errmsg: %s",pRspInfo->ErrorMsg);
    }
  }
};



/****************
*本demo向用户展示了如何构建响应回调、如何创建api请求对象、以及简单的将部分业务逻辑串联起来
*demo提供了测试艾科柜台环境是否搭建完好的功能
*以上的操作并不意味着api的使用场景局限于此，尤其是业务串联部分的处理，需要用户结合具体的需求自行组织代码逻辑
*如果使用时有任何疑问，可以向艾科人员咨询
*****************/

int main(int argc,char* argv[]) {
  /************
   * 解析參數
   * 1.资金帐号 string
   * 2.密码  string
   * 3.节点号 int类型
   * 4.1=上海 2=深圳 char类型
   ************/
  init_g_param();
  /************
   * 输入报单需要的参数
   * 1.证券代码 string 类型
   * 2.买还是卖 1=买入 2=卖出 char类型
   * 3.价格 double 类型
   * 4.报单数量 unsigned int类型
   ************/
  PRINT_INFO(
      "if you want manual input securities,direction,price,volumn,please manual input 'y/Y',if you want auto run,please manual input 'n/N'");
  char input;
  while (std::cin >> input) {
    if (input == 'y' || input == 'Y') {
      PRINT_INFO("input securities(type is std::string):");
      std::cin >> g_manual_input_secutities.securities;
      PRINT_INFO("input direction(type is char):");
      std::cin >> g_manual_input_secutities.direction;
      PRINT_INFO("input price(type is double):");
      std::cin >> g_manual_input_secutities.price;
      PRINT_INFO("input volumn(type is unsigned int):");
      std::cin >> g_manual_input_secutities.volumn;
      PRINT_INFO("input isBatch(type is bool,0 represents not batch,other batch int represents batch):");
      int isBatch = 0;
      std::cin >> isBatch;
      g_manual_input_secutities.isBatch = isBatch;
      if(g_manual_input_secutities.isBatch){
        PRINT_INFO("input BatchType(type is char) :");
        std::cin >> g_manual_input_secutities.BatchType;
        PRINT_INFO("input MaxOrderQty(type unsigned int) :");
        std::cin >> g_manual_input_secutities.MaxOrderQty;
        PRINT_INFO("input DecreaseQty(type unsigned int) :");
        std::cin >> g_manual_input_secutities.DecreaseQty;
        PRINT_INFO("input BatchOrderQty(type unsigned int) :");
        std::cin >> g_manual_input_secutities.BatchOrderQty;
      }

      g_manual_input_secutities.manual_input = true;
      break;
    } else if (input == 'n' || input == 'N') {
      PRINT_INFO("auto run");
      g_manual_input_secutities.manual_input = false;
      break;
    } else {
      PRINT_INFO(
          "if you want manual input securities,direction,price,volumn,please manual input 'y/Y',if you want auto run,please manual input 'n/N'");
    }
  }

  /*********************
  * api的初始化分为以下三步：
  * 1、创建一个接收回报的类，该类继承自XeleSecuritiesTraderSpi，用来接收艾科柜台的回报响应
  * 2、调用createTradeApi接口，创建一个请求类对象，该对象用来向艾科柜台发送操作请求
  * 3、调用registerSpi接口，将回报接收类对象传进请求类对象中
  * api获取操作权限的方式：
  * 1、调用reqLogin接口，传入配置文件路径、资金账户、资金账户密码、账户节点、交易所类型以及本地维护的请求编号即可。
  * 其中，配置文件可以对api进行一些初始化的配置，包括api的连接方式（通过管理中心连接柜台还是直连柜台，这两种方式区别在于是否拥有跨柜台的资金操作权限，后者没有）、
  * api的绑核、心跳设置、socket类型选择、是否有地址、端口映射等
  * 当：
  * 1) 接口onRspLoginManager被回调，并且没有错误消息时，表明当前拥有艾科管理中心的操作权限
  * 2）接口onRspLogin被回调，并且没有错误消息时，表明当前拥有艾科柜台的查询权限
  * 3）接口onRspInitTrader被回调，并且没有错误消息时，表明当前拥有艾科柜台的报、撤单权限

  ****************************/

  //创建一个接收回报的类，该类继承自XeleSecuritiesTraderSpi，用来接收艾科柜台的回报响应
  auto *pUserSpi = new DemoSpi();

  //调用createTradeApi接口，创建一个请求类对象，该对象用来向艾科柜台发送操作请求, 使用时请注意本api非线程安全
  pUserApi = XeleSecuritiesTraderApi::createTraderApi();
  //判断创建是否成功
  if (!pUserApi) {
    PRINT_INFO("Can't Create API");
    exit(0);
  }

  pUserApi->registerSpi(pUserSpi);

  /**
   * 注：
   * 登陆需要等待onRspLogin被调用，并获取CXeleRspUserLoginField：MaxUserLocalID并将MaxUserLocalID++才能进行下一步操作
   * onRspInitTrader被调用代表可以进行报单操作
   * */
  int ret = 0;

  PRINT_INFO("choose the interface you want to call, input reqLogin or reqLoginEx");
  char choose[128]{};
  while (std::cin >> choose) {
    if (strcmp(choose,"reqLogin") == 0) {
      PRINT_INFO("reqLogin");
      ret = pUserApi->reqLogin("./api_config.txt",g_accountID.data(),g_password.data(),
                               g_node, g_market, g_RequestID++);
      break;
    } else if (strcmp(choose,"reqLoginEx") == 0) {
      PRINT_INFO("reqLoginEx");
      ConfigParam param;

      auto m_configFile = new ConfigFileDemo("./api_config.txt");

      param.IsJustConnectManager = (int)m_configFile->Value("IsJustConnectManager") == 1;
      param.HeartBeatInterval = (int)m_configFile->Value("HeartBeatInterval");
      param.HeartBeatTimeOutCnt  = (int)m_configFile->Value("HeartBeatTimeOutCnt");
      param.OrderWarm  = (int)m_configFile->Value("OrderWarm");
      param.SoftRspRcv = (int)m_configFile->Value("SoftRspRcv");
      param.ApiMode = (int)m_configFile->Value("ApiMode");
      param.SuperLog = (int)m_configFile->Value("SuperLog");
      std::string tmp = (std::string) m_configFile->Value("CpuCore");
      auto pos = tmp.find(',');
      param.CpuCore[0] = (int)std::strtol(std::string(tmp,0,pos).c_str(), nullptr,10);
      param.CpuCore[1] = (int)std::strtol(std::string(tmp,pos + 1,tmp.length() - pos).c_str(), nullptr,10);
      param.SocketBlockFlag = (int)m_configFile->Value("SocketBlockFlag");
      param.ManagerURL = (std::string) m_configFile->Value("ManagerURL");
      param.QueryURL = (std::string) m_configFile->Value("QueryURL");
      param.TradeURL = (std::string) m_configFile->Value("TradeURL");
      param.Market = g_market;
      param.OperWay = m_configFile->Value("OperWay");
      param.AppID = (std::string) m_configFile->Value("AppID");
      param.SubClientIndex = g_node;
      param.FlowRebuildFlag = (int)m_configFile->Value("FlowRebuildFlag");
      param.DependentCounterVersion = m_configFile->Value("DependentCounterVersion");
      param.PcPrefix = (std::string) m_configFile->Value("PcPrefix"); //中信新增
      param.AccountId = g_accountID;
      param.Password = g_password;
      PRINT_INFO("Market %c OperWay %c AppID %s ",param.Market,param.OperWay,param.AppID.c_str());

      //param.isAutoGetSysInfo = false;
      //memcpy(param.sysInfo.mac_addr,"2C44FD94526B", 12);
      //memcpy(param.sysInfo.cpu_serial,"E4060300FFFBEBBF", 16);
      //memcpy(param.sysInfo.hostname,"OCALHOST.LOCALDOMAIN", sizeof(param.sysInfo.hostname));
      //memcpy(param.sysInfo.ip_addr,"192.168.4.216", 13);
      //memcpy(param.sysInfo.hard_disk_serial,"50014380279F31C0", 16);

      ret = pUserApi->reqLoginEx(&param,g_RequestID++);
      break;
    } else {
      PRINT_INFO("choose the interface you want to call, inputreqLogin or reqLoginEx");
    }
  }

  if (ret) {
    PRINT_INFO("login error,ret [%d]", ret);
    /// ret可以结合XeleSecuritiesTraderApi.h中ApiReturnValue枚举返回值对应错误来判断常见的异常
    exit(0);
  }

  //此处是demo为了得到当前api可以执行的权限而做的等待，实际运用时可以直接在回调函数中进行操作
  while (true) {
    if (pUserSpi->canOrder) {
      PRINT_INFO("if you want test reconnect,input y/Y,if not input n/N");
      char input;
      while (std::cin >> input) {
        if (input == 'y' || input == 'Y') {
          PRINT_INFO("please create a disconnection scenario (for example: restart counter)");
          pUserSpi->canOrder = false;
          break;
        } else if (input == 'n' || input == 'N') {
          break;
        } else {
          PRINT_INFO("if you want test reconnect,input y/Y,if not input n/N");
        }
      }
      if (pUserSpi->canOrder)
        break;
    }
    sleep(1);
  }

  PRINT_INFO("if you want test cash in out,input y/Y,if not input n/N");
  while (std::cin >> input) {
    if (input == 'y' || input == 'Y') {
      PRINT_INFO("  Test reqOutFund");
      CXeleReqCapTransferField reqCapTransferField;
      memset(&reqCapTransferField,0, sizeof(reqCapTransferField));
      memcpy(reqCapTransferField.AccountID, g_accountID.data(), g_accountID.length());
      reqCapTransferField.Fundamt = g_fundamt;
      pUserApi->reqOutFund(reqCapTransferField, g_RequestID++);
      sleep(2);
      PRINT_INFO("  Test reqInFund");
      pUserApi->reqInFund(reqCapTransferField, g_RequestID++);
      sleep(2);
      break;
    } else if (input == 'n' || input == 'N') {
      break;
    } else {
      PRINT_INFO("if you want test cash in out,input y/Y,if not input n/N");
    }
  }
  /// 测试接口
  PRINT_INFO("  Test Manager And Counter Interface");
  CXeleReqQryClientAccountField qryClientAccountField{};
  /// 注意清空
  memset(&qryClientAccountField, 0, sizeof(CXeleReqQryClientAccountField));
  /// 查询该资金账号下的可用资金，更新到本地
  memcpy(qryClientAccountField.AccountID, g_accountID.data(), g_accountID.length());

  /// 调用成功会跳转到onRspQryFund
  PRINT_INFO("=======================");
  PRINT_INFO("  Test reqQryFund");
  pUserApi->reqQryFund(qryClientAccountField, g_RequestID++);


  //此处是为等待demo中程序走完登出流程
  //此处选择的操作方式只是为了让demo的流程尽量完整，不代表必须使用该种方式
  while (true){
    sleep(1);
    if(!pUserSpi->canFundTransfer) {
      sleep(2);
      break;
    }
  }

  //程序退出，调用release,删除接口对象
  pUserApi->release();
  delete pUserSpi;
  printf("exit success\n");

  return 0;
}
