Administrator
2024-05-10 273428fe5a4fe4c0e5200dff007c7684153d331b
可转债仿真交易
1 文件已重命名
6个文件已修改
3个文件已添加
1748 ■■■■■ 已修改文件
cb_main.py 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
huaxin_client/cb/l2_client_for_cb.py 补丁 | 查看 | 原始文档 | blame | 历史
huaxin_client/cb/test_trade.py 540 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
huaxin_client/cb/trade_client_for_cb.py 522 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
huaxin_client/cb/trade_manager.py 616 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
huaxin_client/command_manager.py 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
log_module/log.py 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
test/test.py 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
trade/huaxin_trade_api.py 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
utils/tool.py 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
cb_main.py
@@ -3,7 +3,7 @@
"""
import constant
constant.LOG_DIR = "logs_cb"
from huaxin_client import l2_client_for_cb
from huaxin_client.cb import l2_client_for_cb
from utils import middle_api_protocol
if __name__ == '__main__':
huaxin_client/cb/l2_client_for_cb.py
huaxin_client/cb/test_trade.py
New file
@@ -0,0 +1,540 @@
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import traderapi
''' 注意: 如果提示找不到_tradeapi 且与已发布的库文件不一致时,可自行重命名为_tradeapi.so (windows下为_tradeapi.pyd)'''
# 投资者账户
InvestorID = "00032047"
'''
该默认账号为共用连通测试使用,自有测试账号请到n-sight.com.cn注册并从个人中心获取交易编码,不是网站登录密码,不是手机号
实盘交易时,取客户号,请注意不是资金账号或咨询技术支持
'''
# 操作员账户
UserID = "00032047"  # 同客户号保持一致即可
# 资金账户
AccountID = "00032047"  # 以Req(TradingAccount)查询的为准
# 登陆密码
Password = "59009218"  # N视界注册模拟账号的交易密码,不是登录密码
DepartmentID = "0003"  # 生产环境默认客户号的前4位
SSE_ShareHolderID = 'A00032047'  # 不同账号的股东代码需要接口ReqQryShareholderAccount去查询
SZ_ShareHolderID = '700032047'  # 不同账号的股东代码需要接口ReqQryShareholderAccount去查询
class TraderSpi(traderapi.CTORATstpTraderSpi):
    def __init__(self, api):
        traderapi.CTORATstpTraderSpi.__init__(self)
        self.__api = api
        self.__req_id = 0
        self.__front_id = 0
        self.__session_id = 0
    def OnFrontConnected(self) -> "void":
        print('OnFrontConnected')
        # 获取终端信息
        self.__req_id += 1
        ret = self.__api.ReqGetConnectionInfo(self.__req_id)
        if ret != 0:
            print('ReqGetConnectionInfo fail, ret[%d]' % ret)
    def OnFrontDisconnected(self, nReason: "int") -> "void":
        print('OnFrontDisconnected: [%d]' % nReason)
    def OnRspGetConnectionInfo(self, pConnectionInfoField: "CTORATstpConnectionInfoField",
                               pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('inner_ip_address[%s]' % pConnectionInfoField.InnerIPAddress)
            print('inner_port[%d]' % pConnectionInfoField.InnerPort)
            print('outer_ip_address[%s]' % pConnectionInfoField.OuterIPAddress)
            print('outer_port[%d]' % pConnectionInfoField.OuterPort)
            print('mac_address[%s]' % pConnectionInfoField.MacAddress)
            # 请求登录
            login_req = traderapi.CTORATstpReqUserLoginField()
            # 支持以用户代码、资金账号和股东账号方式登录
            # (1)以用户代码方式登录
            login_req.LogInAccount = UserID
            login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_UserID
            # (2)以资金账号方式登录
            # login_req.DepartmentID = DepartmentID
            # login_req.LogInAccount = AccountID
            # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_AccountID
            # (3)以上海股东账号方式登录
            # login_req.LogInAccount = SSE_ShareHolderID
            # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_SHAStock
            # (4)以深圳股东账号方式登录
            # login_req.LogInAccount = SZSE_ShareHolderID
            # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_SZAStock
            # 支持以密码和指纹(移动设备)方式认证
            # (1)密码认证
            # 密码认证时AuthMode可不填
            # login_req.AuthMode = traderapi.TORA_TSTP_AM_Password
            login_req.Password = Password
            # (2)指纹认证
            # 非密码认证时AuthMode必填
            # login_req.AuthMode = traderapi.TORA_TSTP_AM_FingerPrint
            # login_req.DeviceID = '03873902'
            # login_req.CertSerial = '9FAC09383D3920CAEFF039'
            # 终端信息采集
            # UserProductInfo填写终端名称
            login_req.UserProductInfo = 'pyapidemo'
            # 按照监管要求填写终端信息
            login_req.TerminalInfo = 'PC;IIP=000.000.000.000;IPORT=00000;LIP=x.xx.xxx.xxx;MAC=123ABC456DEF;HD=XXXXXXXXXX'
            # 以下内外网IP地址若不填则柜台系统自动采集,若填写则以终端填值为准报送
            # login_req.MacAddress = '5C-87-9C-96-F3-E3'
            # login_req.InnerIPAddress = '10.0.1.102'
            # login_req.OuterIPAddress = '58.246.43.50'
            self.__req_id += 1
            ret = self.__api.ReqUserLogin(login_req, self.__req_id)
            if ret != 0:
                print('ReqUserLogin fail, ret[%d]' % ret)
        else:
            print('GetConnectionInfo fail, [%d] [%d] [%s]!!!' % (
            nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspUserLogin(self, pRspUserLoginField: "traderapi.CTORATstpRspUserLoginField",
                       pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('Login success! [%d]' % nRequestID)
            self.__front_id = pRspUserLoginField.FrontID
            self.__session_id = pRspUserLoginField.SessionID
            if 0:
                # 修改密码
                req_field = traderapi.CTORATstpUserPasswordUpdateField()
                req_field.UserID = UserID
                req_field.OldPassword = Password
                req_field.NewPassword = '123456'
                self.__req_id += 1
                ret = self.__api.ReqUserPasswordUpdate(req_field, self.__req_id)
                if ret != 0:
                    print('ReqUserPasswordUpdate fail, ret[%d]' % ret)
                return
            if 0:
                # 查询合约
                req_field = traderapi.CTORATstpQrySecurityField()
                # 以下字段不填表示不设过滤条件,即查询全部合约
                # req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
                # req_field.SecurityID = '600000'
                self.__req_id += 1
                ret = self.__api.ReqQrySecurity(req_field, self.__req_id)
                if ret != 0:
                    print('ReqQrySecurity fail, ret[%d]' % ret)
            if 1:
                # 查询投资者
                req_field = traderapi.CTORATstpQryInvestorField()
                # 以下字段不填表示不设过滤条件
                # req_field.InvestorID = InvestorID
                self.__req_id += 1
                ret = self.__api.ReqQryInvestor(req_field, self.__req_id)
                if ret != 0:
                    print('ReqQryInvestor fail, ret[%d]' % ret)
            if 1:
                # 查询股东账号
                req_field = traderapi.CTORATstpQryShareholderAccountField()
                # 以下字段不填表示不设过滤条件,即查询所有股东账号
                # req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
                self.__req_id += 1
                ret = self.__api.ReqQryShareholderAccount(req_field, self.__req_id)
                if ret != 0:
                    print('ReqQryShareholderAccount fail, ret[%d]' % ret)
            if 1:
                # 查询资金账号
                req_field = traderapi.CTORATstpQryTradingAccountField()
                # 以下字段不填表示不设过滤条件,即查询所有资金账号
                req_field.InvestorID = InvestorID
                req_field.DepartmentID = DepartmentID
                req_field.AccountID = AccountID
                self.__req_id += 1
                ret = self.__api.ReqQryTradingAccount(req_field, self.__req_id)
                if ret != 0:
                    print('ReqQryTradingAccount fail, ret[%d]' % ret)
            if 1:
                # 查询报单
                req_field = traderapi.CTORATstpQryOrderField()
                # 以下字段不填表示不设过滤条件,即查询所有报单
                # req_field.SecurityID = '600000'
                # req_field.InsertTimeStart = '09:35:00'
                # req_field.InsertTimeEnd = '10:00:00'
                # IsCancel字段填1表示只查询可撤报单
                # req_field.IsCancel = 1
                self.__req_id += 1
                ret = self.__api.ReqQryOrder(req_field, self.__req_id)
                if ret != 0:
                    print('ReqQryOrder fail, ret[%d]' % ret)
            if 1:
                # 查询持仓
                req_field = traderapi.CTORATstpQryPositionField()
                # 以下字段不填表示不设过滤条件,即查询所有持仓
                # req_field.SecurityID = '600000'
                self.__req_id += 1
                ret = self.__api.ReqQryPosition(req_field, self.__req_id)
                if ret != 0:
                    print('ReqQryPosition fail, ret[%d]' % ret)
            if 0:
                # 请求报单
                req_field = traderapi.CTORATstpInputOrderField()
                req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
                req_field.ShareholderID = SSE_ShareHolderID
                req_field.SecurityID = '600000'
                req_field.Direction = traderapi.TORA_TSTP_D_Buy
                req_field.VolumeTotalOriginal = 100
                '''
                上交所支持限价指令和最优五档剩撤、最优五档剩转限两种市价指令,对于科创板额外支持本方最优和对手方最优两种市价指令和盘后固定价格申报指令
                深交所支持限价指令和立即成交剩余撤销、全额成交或撤销、本方最优、对手方最优和最优五档剩撤五种市价指令
                限价指令和上交所科创板盘后固定价格申报指令需填写报单价格,其它市价指令无需填写报单价格
                以下以上交所限价指令为例,其它指令参考开发指南相关说明填写OrderPriceType、TimeCondition和VolumeCondition三个字段:
                '''
                req_field.LimitPrice = 7.29
                req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_LimitPrice
                req_field.TimeCondition = traderapi.TORA_TSTP_TC_GFD
                req_field.VolumeCondition = traderapi.TORA_TSTP_VC_AV
                '''
                OrderRef为报单引用,类型为整型,该字段报单时为选填
                若不填写,则系统会为每笔报单自动分配一个报单引用
                若填写,则需保证同一个TCP会话下报单引用严格单调递增,不要求连续递增,至少需从1开始编号
                '''
                # req_field.OrderRef = 1
                '''
                InvestorID为选填,若填写则需保证填写正确
                Operway为委托方式,根据券商要求填写,无特殊说明置空即可
                终端自定义字段,终端可根据需要填写如下字段的值,该字段值不会被柜台系统修改,在报单回报和查询报单时返回给终端
                '''
                # req_field.SInfo = 'sinfo'
                # req_field.IInfo = 123
                '''
                其它字段置空
                '''
                self.__req_id += 1
                ret = self.__api.ReqOrderInsert(req_field, self.__req_id)
                if ret != 0:
                    print('ReqOrderInsert fail, ret[%d]' % ret)
            if 0:
                # 请求撤单
                req_field = traderapi.CTORATstpInputOrderActionField()
                req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
                req_field.ActionFlag = traderapi.TORA_TSTP_AF_Delete
                # 撤单支持以下两种方式定位原始报单:
                # (1)报单引用方式
                # req_field.FrontID = self.__front_id
                # req_field.SessionID = self.__session_id
                # req_field.OrderRef = 1
                # (2)系统报单编号方式
                req_field.OrderSysID = '110019400000005'
                # OrderActionRef报单操作引用,用法同报单引用,可根据需要选填
                '''
                终端自定义字段,终端可根据需要填写如下字段的值,该字段值不会被柜台系统修改,在查询撤单时返回给终端
                '''
                # req_field.SInfo = 'sinfo'
                # req_field.IInfo = 123
                '''
                委托方式字段根据券商要求填写,无特殊说明置空即可
                其它字段置空
                '''
                self.__req_id += 1
                ret = self.__api.ReqOrderAction(req_field, self.__req_id)
                if ret != 0:
                    print('ReqOrderAction fail, ret[%d]' % ret)
            if 0:
                # 查询集中交易资金
                req_field = traderapi.CTORATstpReqInquiryJZFundField()
                req_field.DepartmentID = DepartmentID
                req_field.AccountID = AccountID
                req_field.CurrencyID = traderapi.TORA_TSTP_CID_CNY
                self.__req_id += 1
                ret = self.__api.ReqInquiryJZFund(req_field, self.__req_id)
                if ret != 0:
                    print('ReqInquiryJZFund fail, ret[%d]' % ret)
            if 0:
                # 资金转移(包括资金调拨和银证转账)
                req_field = traderapi.CTORATstpInputTransferFundField()
                req_field.DepartmentID = DepartmentID
                req_field.AccountID = AccountID
                req_field.CurrencyID = traderapi.TORA_TSTP_CID_CNY
                req_field.Amount = 100000.0
                '''
                转移方向:
                TORA_TSTP_TRNSD_MoveIn表示资金从集中交易柜台调拨至快速交易柜台
                TORA_TSTP_TRNSD_MoveOut表示资金从快速交易柜台调拨至集中交易柜台
                TORA_TSTP_TRNSD_StockToBank表示证券快速交易系统资金转入银行,即出金
                TORA_TSTP_TRNSD_BankToStock表示银行资金转入证券快速交易系统,即入金
                以下说明各场景下字段填值:
                '''
                # (1)资金从集中交易柜台调拨至快速交易柜台
                req_field.TransferDirection = traderapi.TORA_TSTP_TRNSD_MoveIn
                # (2)资金从快速交易柜台调拨至集中交易柜台
                # req_field.TransferDirection = traderapi.TORA_TSTP_TRNSD_MoveOut
                # (3)证券快速交易系统资金转入银行,需填写银行代码和资金密码
                # req_field.TransferDirection = traderapi.TORA_TSTP_TRNSD_StockToBank
                # req_field.BankID = traderapi.TORA_TSTP_BKID_CCB
                # req_field.AccountPassword = '123456'
                # (4)银行资金转入证券快速交易系统,需填写银行代码和银行卡密码
                # req_field.TransferDirection = traderapi.TORA_TSTP_TRNSD_BankToStock
                # req_field.BankID = traderapi.TORA_TSTP_BKID_CCB
                # req_field.BankPassword = '123456'
                '''
                申请流水号ApplySerial字段为选填字段
                若不填写则柜台系统会自动生成一个申请流水号
                若填写则需保证同一个TCP会话下申请流水号不重复
                '''
                # req_field.ApplySerial = 1
                self.__req_id += 1
                ret = self.__api.ReqTransferFund(req_field, self.__req_id)
                if ret != 0:
                    print('ReqTransferFund fail, ret[%d]', ret)
            if 0:
                '''登出,目前登出成功连接会立即被柜台系统断开,终端不会收到OnRspUserLogout应答
                连接断开后接口内部会触发重新连接,为不使连接成功后又触发重新登录,需终端做好逻辑控制
                一般情况下若希望登出,直接调用Release接口即可,释放成功连接将被终端强制关闭,Release接口调用注意事项见下文说明
                '''
                req_field = traderapi.CTORATstpUserLogoutField()
                self.__req_id += 1
                ret = self.__api.ReqUserLogout(req_field, self.__req_id)
                if ret != 0:
                    print('ReqUserLogout fail, ret[%d]' % ret)
        else:
            print('Login fail!!! [%d] [%d] [%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
        return
    def OnRspUserPasswordUpdate(self, pUserPasswordUpdateField: "CTORATstpUserPasswordUpdateField",
                                pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('OnRspUserPasswordUpdate: OK! [%d]' % nRequestID)
        else:
            print('OnRspUserPasswordUpdate: Error! [%d] [%d] [%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspOrderInsert(self, pInputOrderField: "CTORATstpInputOrderField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('OnRspOrderInsert: OK! [%d]' % nRequestID)
        else:
            print('OnRspOrderInsert: Error! [%d] [%d] [%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspOrderAction(self, pInputOrderActionField: "CTORATstpInputOrderActionField",
                         pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('OnRspOrderAction: OK! [%d]' % nRequestID)
        else:
            print('OnRspOrderAction: Error! [%d] [%d] [%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspInquiryJZFund(self, pRspInquiryJZFundField: "CTORATstpRspInquiryJZFundField",
                           pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('OnRspInquiryJZFund: OK! [%d] [%.2f] [%.2f]'
                  % (nRequestID, pRspInquiryJZFundField.UsefulMoney, pRspInquiryJZFundField.FetchLimit))
        else:
            print('OnRspInquiryJZFund: Error! [%d] [%d] [%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspTransferFund(self, pInputTransferFundField: "CTORATstpInputTransferFundField",
                          pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            print('OnRspTransferFund: OK! [%d]' % nRequestID)
        else:
            print('OnRspTransferFund: Error! [%d] [%d] [%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRtnOrder(self, pOrderField: "CTORATstpOrderField") -> "void":
        print(
            'OnRtnOrder: InvestorID[%s] SecurityID[%s] OrderRef[%d] OrderLocalID[%s] LimitPrice[%.2f] VolumeTotalOriginal[%d] OrderSysID[%s] OrderStatus[%s]'
            % (pOrderField.InvestorID, pOrderField.SecurityID, pOrderField.OrderRef, pOrderField.OrderLocalID,
               pOrderField.LimitPrice, pOrderField.VolumeTotalOriginal, pOrderField.OrderSysID,
               pOrderField.OrderStatus))
    def OnRtnTrade(self, pTradeField: "CTORATstpTradeField") -> "void":
        print(
            'OnRtnTrade: TradeID[%s] InvestorID[%s] SecurityID[%s] OrderRef[%d] OrderLocalID[%s] Price[%.2f] Volume[%d]'
            % (pTradeField.TradeID, pTradeField.InvestorID, pTradeField.SecurityID,
               pTradeField.OrderRef, pTradeField.OrderLocalID, pTradeField.Price, pTradeField.Volume))
    def OnRtnMarketStatus(self, pMarketStatusField: "CTORATstpMarketStatusField") -> "void":
        print('OnRtnMarketStatus: MarketID[%s] MarketStatus[%s]'
              % (pMarketStatusField.MarketID, pMarketStatusField.MarketStatus))
    def OnRspQrySecurity(self, pSecurityField: "CTORATstpSecurityField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            print(
                'OnRspQrySecurity[%d]: SecurityID[%s] SecurityName[%s] MarketID[%s] OrderUnit[%s] OpenDate[%s] UpperLimitPrice[%.2f] LowerLimitPrice[%.2f]'
                % (nRequestID, pSecurityField.SecurityID, pSecurityField.SecurityName, pSecurityField.MarketID,
                   pSecurityField.OrderUnit, pSecurityField.OpenDate, pSecurityField.UpperLimitPrice,
                   pSecurityField.LowerLimitPrice))
        else:
            print('查询合约结束[%d] ErrorID[%d] ErrorMsg[%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryInvestor(self, pInvestorField: "CTORATstpInvestorField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            print('OnRspQryInvestor[%d]: InvestorID[%s]  Operways[%s]'
                  % (nRequestID, pInvestorField.InvestorID,
                     pInvestorField.Operways))
        else:
            print('查询投资者结束[%d] ErrorID[%d] ErrorMsg[%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryShareholderAccount(self, pShareholderAccountField: "CTORATstpShareholderAccountField",
                                   pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int",
                                   bIsLast: "bool") -> "void":
        if bIsLast != 1:
            print('OnRspQryShareholderAccount[%d]: InvestorID[%s] ExchangeID[%s] ShareholderID[%s]'
                  % (nRequestID, pShareholderAccountField.InvestorID, pShareholderAccountField.ExchangeID,
                     pShareholderAccountField.ShareholderID))
        else:
            print('查询股东账户结束[%d] ErrorID[%d] ErrorMsg[%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryTradingAccount(self, pTradingAccountField: "CTORATstpTradingAccountField",
                               pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            print(
                'OnRspQryTradingAccount[%d]: DepartmentID[%s] InvestorID[%s] AccountID[%s] CurrencyID[%s] UsefulMoney[%.2f] FetchLimit[%.2f]'
                % (nRequestID, pTradingAccountField.DepartmentID, pTradingAccountField.InvestorID,
                   pTradingAccountField.AccountID, pTradingAccountField.CurrencyID,
                   pTradingAccountField.UsefulMoney, pTradingAccountField.FetchLimit))
        else:
            print('查询资金账号结束[%d] ErrorID[%d] ErrorMsg[%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryOrder(self, pOrderField: "CTORATstpOrderField", pRspInfoField: "CTORATstpRspInfoField",
                      nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            print(
                'OnRspQryOrder[%d]: SecurityID[%s] OrderLocalID[%s] OrderRef[%d] OrderSysID[%s] VolumeTraded[%d] OrderStatus[%s] OrderSubmitStatus[%s], StatusMsg[%s]'
                % (nRequestID, pOrderField.SecurityID, pOrderField.OrderLocalID, pOrderField.OrderRef,
                   pOrderField.OrderSysID,
                   pOrderField.VolumeTraded, pOrderField.OrderStatus, pOrderField.OrderSubmitStatus,
                   pOrderField.StatusMsg))
        else:
            print('查询报单结束[%d] ErrorID[%d] ErrorMsg[%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryPosition(self, pPositionField: "CTORATstpPositionField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            print('OnRspQryPosition[%d]: InvestorID[%s] SecurityID[%s] HistoryPos[%d] TodayBSPos[%d] TodayPRPos[%d]'
                  % (nRequestID, pPositionField.InvestorID, pPositionField.SecurityID, pPositionField.HistoryPos,
                     pPositionField.TodayBSPos, pPositionField.TodayPRPos))
        else:
            print('查询持仓结束[%d] ErrorID[%d] ErrorMsg[%s]'
                  % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
if __name__ == '__main__':
    # 打印接口版本号
    print("TradeAPI Version:::" + traderapi.CTORATstpTraderApi_GetApiVersion())
    # 创建接口对象
    # pszFlowPath为私有流和公有流文件存储路径,若订阅私有流和公有流且创建多个接口实例,每个接口实例应配置不同的路径
    # bEncrypt为网络数据是否加密传输,考虑数据安全性,建议以互联网方式接入的终端设置为加密传输
    api = traderapi.CTORATstpTraderApi.CreateTstpTraderApi('./flow', False)
    # 创建回调对象
    spi = TraderSpi(api)
    # 注册回调接口
    api.RegisterSpi(spi)
    if 1:  # 模拟环境,TCP 直连Front方式
        # 注册单个交易前置服务地址
        TD_TCP_FrontAddress = "tcp://210.14.72.21:4400"  # 仿真交易环境
        # TD_TCP_FrontAddress="tcp://210.14.72.15:4400" #24小时环境A套
        # TD_TCP_FrontAddress="tcp://210.14.72.16:9500" #24小时环境B套
        api.RegisterFront(TD_TCP_FrontAddress)
        # 注册多个交易前置服务地址,用逗号隔开 形如: api.RegisterFront("tcp://10.0.1.101:6500,tcp://10.0.1.101:26500")
        print("TD_TCP_FensAddress[sim or 24H]::%s\n" % TD_TCP_FrontAddress)
    else:  # 模拟环境,FENS名字服务器方式
        TD_TCP_FensAddress = "tcp://210.14.72.21:42370";  # 模拟环境通用fens地址
        '''********************************************************************************
        * 注册 fens 地址前还需注册 fens 用户信息,包括环境编号、节点编号、Fens 用户代码等信息
        * 使用名字服务器的好处是当券商系统部署方式发生调整时外围终端无需做任何前置地址修改
        * *****************************************************************************'''
        fens_user_info_field = traderapi.CTORATstpFensUserInfoField()
        fens_user_info_field.FensEnvID = "stock"  # 必填项,暂时固定为“stock”表示普通现货柜台
        fens_user_info_field.FensNodeID = "sim"  # 必填项,生产环境需按实际填写,仿真环境为sim
        fens_user_info_field.FensNodeID, = "24a"  # 必填项,生产环境需按实际填写,24小时A套环境为24a
        # fens_user_info_field.FensNodeID="24b" #必填项,生产环境需按实际填写,24小时B套环境为24b
        api.RegisterFensUserInfo(fens_user_info_field)
        api.RegisterNameServer(TD_TCP_FensAddress)
        # 注册名字服务器地址,支持多服务地址逗号隔开 形如:api.RegisterNameServer('tcp://10.0.1.101:52370,tcp://10.0.1.101:62370')
        print("TD_TCP_FensAddress[%s]::%s\n" % (fens_user_info_field.FensNodeID, TD_TCP_FensAddress))
    # 订阅私有流
    api.SubscribePrivateTopic(traderapi.TORA_TERT_QUICK)
    # 订阅公有流
    api.SubscribePublicTopic(traderapi.TORA_TERT_QUICK)
    '''**********************************
    *    TORA_TERT_RESTART, 从日初开始
    *    TORA_TERT_RESUME, 从断开时候开始
    *    TORA_TERT_QUICK, 从最新时刻开始
    *************************************'''
    # 启动接口
    api.Init()
    # 等待程序结束
    input()
    # 释放接口对象
    api.Release()
huaxin_client/cb/trade_client_for_cb.py
New file
@@ -0,0 +1,522 @@
# -*- coding: utf-8 -*-
import concurrent.futures
import time
from huaxin_client import constant
import traderapi
from huaxin_client.cb import trade_manager
from huaxin_client.cb.trade_manager import TradeSimpleApi
# 正式账号
from huaxin_client.command_manager import TradeCommandManager
from log_module import async_log_util
from log_module.log import logger_local_huaxin_trade_debug as logger, logger_system, logger_local_huaxin_trade_debug, logger_info
TEST_TRADE = True
########B类########
UserID = '388000013349'
# 登陆密码
Password = '110808'
# 投资者账户
InvestorID = '388000013349'
# 经济公司部门代码
DepartmentID = '0003'
# 资金账户
AccountID = '388000013349'
# 沪市股东账号
SSE_ShareHolderID = 'A641420991'
# 深市股东账号
SZSE_ShareHolderID = '0345104949'
LOCAL_IP = constant.LOCAL_IP
FRONT_ADDRESS = "tcp://192.168.84.31:6500"
FRONT_ADDRESS1 = "tcp://192.168.84.32:26500"
if TEST_TRADE:
    # # 仿真
    # from mylog import logger_trade_debug
    #
    UserID = '00032047'
    # 登陆密码
    Password = '59009218'
    # 投资者账户
    InvestorID = '00032047'
    # 经济公司部门代码
    DepartmentID = '0003'
    # 资金账户
    AccountID = '00032047'
    # 沪市股东账号
    SSE_ShareHolderID = 'A00032047'
    # 深市股东账号
    SZSE_ShareHolderID = '700032047'
class TraderSpi(traderapi.CTORATstpTraderSpi):
    def __init__(self, api, callback):
        traderapi.CTORATstpTraderSpi.__init__(self)
        self.__api= api
        self.__front_id = 0
        self.__session_id = 0
        self.__data_callback = callback
        self.__temp_order_list_dict = {}
        self.__temp_position_list_dict = {}
        self.__temp_money_account_list_dict = {}
        self.call_back_thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=10)
    def OnFrontConnected(self) -> "void":
        logger_info.debug('Trader OnFrontConnected')
        # 获取终端信息
        TradeSimpleApi.req_id += 1
        ret = self.__api.ReqGetConnectionInfo(TradeSimpleApi.req_id)
        if ret != 0:
            logger_info.info('ReqGetConnectionInfo fail, ret[%d]' % ret)
    def OnFrontDisconnected(self, nReason: "int") -> "void":
        logger_info.info('OnFrontDisconnected: [%d]' % nReason)
    def OnRspGetConnectionInfo(self, pConnectionInfoField: "CTORATstpConnectionInfoField",
                               pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            logger.info('inner_ip_address[%s]' % pConnectionInfoField.InnerIPAddress)
            logger.info('inner_port[%d]' % pConnectionInfoField.InnerPort)
            logger.info('outer_ip_address[%s]' % pConnectionInfoField.OuterIPAddress)
            logger.info('outer_port[%d]' % pConnectionInfoField.OuterPort)
            logger.info('mac_address[%s]' % pConnectionInfoField.MacAddress)
            # 请求登录
            login_req = traderapi.CTORATstpReqUserLoginField()
            # 支持以用户代码、资金账号和股东账号方式登录
            # (1)以用户代码方式登录
            login_req.LogInAccount = UserID
            login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_UserID
            # (2)以资金账号方式登录
            # login_req.DepartmentID = DepartmentID
            # login_req.LogInAccount = AccountID
            # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_AccountID
            # (3)以上海股东账号方式登录
            # login_req.LogInAccount = SSE_ShareHolderID
            # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_SHAStock
            # (4)以深圳股东账号方式登录
            # login_req.LogInAccount = SZSE_ShareHolderID
            # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_SZAStock
            # 支持以密码和指纹(移动设备)方式认证
            # (1)密码认证
            # 密码认证时AuthMode可不填
            # login_req.AuthMode = traderapi.TORA_TSTP_AM_Password
            login_req.Password = Password
            # (2)指纹认证
            # 非密码认证时AuthMode必填
            # login_req.AuthMode = traderapi.TORA_TSTP_AM_FingerPrint
            # login_req.DeviceID = '03873902'
            # login_req.CertSerial = '9FAC09383D3920CAEFF039'
            # 终端信息采集
            # UserProductInfo填写终端名称
            login_req.UserProductInfo = 'jiabei'
            # 按照监管要求填写终端信息
            login_req.TerminalInfo = f'PC;IIP=NA;IPORT=NA;LIP={LOCAL_IP};MAC=5C6F69CC2B40;HD=004bc76004aff0882b9052ba0eb00506;@jiabei'
            # 以下内外网IP地址若不填则柜台系统自动采集,若填写则以终端填值为准报送
            # login_req.MacAddress = '5C-87-9C-96-F3-E3'
            # login_req.InnerIPAddress = '10.0.1.102'
            # login_req.OuterIPAddress = '58.246.43.50'
            TradeSimpleApi.req_id += 1
            ret = self.__api.ReqUserLogin(login_req,  TradeSimpleApi.req_id)
            if ret != 0:
                logger.info('ReqUserLogin fail, ret[%d]' % ret)
        else:
            logger_info.info('GetConnectionInfo fail, [%d] [%d] [%s]!!!' % (
                nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspUserLogin(self, pRspUserLoginField: "CTORATstpRspUserLoginField", pRspInfoField: "CTORATstpRspInfoField",
                       nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            logger_info.info('Login success! [%d]' % nRequestID)
            self.__front_id = pRspUserLoginField.FrontID
            self.__session_id = pRspUserLoginField.SessionID
            # TradeSimpleApi.set_login_info(self.__session_id, self.__front_id)
            # if 1:
            #     # 查询股东账号
            #     req_field = traderapi.CTORATstpQryShareholderAccountField()
            #
            #     # 以下字段不填表示不设过滤条件,即查询所有股东账号
            #     # req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
            #
            #     TradeSimpleApi.req_id += 1
            #     ret = api.ReqQryShareholderAccount(req_field, TradeSimpleApi.req_id)
            #     if ret != 0:
            #         logger_info.info('ReqQryShareholderAccount fail, ret[%d]' % ret)
        else:
            logger_info.info('Login fail!!! [%d] [%d] [%s]'
                             % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspUserPasswordUpdate(self, pUserPasswordUpdateField: "CTORATstpUserPasswordUpdateField",
                                pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        if pRspInfoField.ErrorID == 0:
            logger.info('OnRspUserPasswordUpdate: OK! [%d]' % nRequestID)
        else:
            logger.info('OnRspUserPasswordUpdate: Error! [%d] [%d] [%s]'
                        % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspOrderInsert(self, pInputOrderField: "CTORATstpInputOrderField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int") -> "void":
        try:
            if pRspInfoField.ErrorID == 0:
                async_log_util.info(logger_local_huaxin_trade_debug,
                                    '[%d] OnRspOrderInsert: OK! [%d]' % (round(time.time() * 1000), nRequestID))
            else:
                async_log_util.error(logger_local_huaxin_trade_debug,
                                     f"OnRspOrderInsert 报单出错:{pRspInfoField.ErrorID}-{pRspInfoField.ErrorMsg}")
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_ORDER, nRequestID,
                                                  {"sinfo": pInputOrderField.SInfo,
                                                   "orderStatus": -1,
                                                   "orderStatusMsg": pRspInfoField.ErrorMsg})
        except:
            pass
    # 撤单响应
    def OnRspOrderAction(self, pInputOrderActionField: "CTORATstpInputOrderActionField",
                         pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        try:
            if pRspInfoField.ErrorID == 0:
                async_log_util.info(logger_local_huaxin_trade_debug, 'OnRspOrderAction: OK! [%d]' % nRequestID)
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_CANCEL_ORDER, nRequestID,
                                                  {"sinfo": pInputOrderActionField.SInfo,
                                                   "orderSysID": pInputOrderActionField.OrderSysID,
                                                   "cancel": 1})
            else:
                async_log_util.info(logger_local_huaxin_trade_debug, 'OnRspOrderAction: Error! [%d] [%d] [%s]'
                                    % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_CANCEL_ORDER, nRequestID,
                                                  {"sinfo": pInputOrderActionField.SInfo,
                                                   "orderSysID": pInputOrderActionField.OrderSysID,
                                                   "cancel": 0, "errorID": pRspInfoField.ErrorID,
                                                   "errorMsg": pRspInfoField.ErrorMsg})
        except:
            pass
    # 撤单错误回报
    def OnErrRtnOrderAction(self, pInputOrderActionField: "CTORATstpInputOrderActionField",
                            pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        try:
            if pInputOrderActionField and pRspInfoField:
                async_log_util.info(logger_local_huaxin_trade_debug, 'OnErrRtnOrderAction: Error! [%d] [%d] [%d] [%s]'
                                    % (nRequestID, pInputOrderActionField.OrderSysID,
                                       pRspInfoField.ErrorID,
                                       pRspInfoField.ErrorMsg))
        except:
            async_log_util.info(logger_local_huaxin_trade_debug, "OnErrRtnOrderAction: 撤单出错")
    def OnRspInquiryJZFund(self, pRspInquiryJZFundField: "CTORATstpRspInquiryJZFundField",
                           pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        # try:
        #     if pRspInfoField.ErrorID == 0:
        #         logger.info('OnRspInquiryJZFund: OK! [%d] [%.2f] [%.2f]'
        #                     % (nRequestID, pRspInquiryJZFundField.UsefulMoney, pRspInquiryJZFundField.FetchLimit))
        #     else:
        #         logger.info('OnRspInquiryJZFund: Error! [%d] [%d] [%s]'
        #                     % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
        # except:
        #     pass
        pass
    def OnRspTransferFund(self, pInputTransferFundField: "CTORATstpInputTransferFundField",
                          pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
        # try:
        #     if pRspInfoField.ErrorID == 0:
        #         logger.info('OnRspTransferFund: OK! [%d]' % nRequestID)
        #     else:
        #         logger.info('OnRspTransferFund: Error! [%d] [%d] [%s]'
        #                     % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
        # except:
        #     pass
        pass
    def OnRtnOrder(self, pOrderField: "CTORATstpOrderField") -> "void":
        try:
            async_log_util.info(logger_local_huaxin_trade_debug,
                                '[%d] OnRtnOrder: SInfo[%s] InvestorID[%s] SecurityID[%s] OrderRef[%d] OrderLocalID[%s] LimitPrice[%.2f] VolumeTotalOriginal[%d] OrderSysID[%s] OrderStatus[%s] InsertTime[%s]'
                                % (round(time.time() * 1000), pOrderField.SInfo, pOrderField.InvestorID,
                                   pOrderField.SecurityID,
                                   pOrderField.OrderRef, pOrderField.OrderLocalID,
                                   pOrderField.LimitPrice, pOrderField.VolumeTotalOriginal, pOrderField.OrderSysID,
                                   pOrderField.OrderStatus, pOrderField.InsertTime))
            if pOrderField.OrderStatus == traderapi.TORA_TSTP_OST_Unknown:
                pass
            #     if queue_trade_w_l2_r is not None:
            #         queue_trade_w_l2_r.put_nowait(
            #             json.dumps({"type": "listen_volume", "data": {"code": pOrderField.SecurityID,
            #                                                           "volume": pOrderField.VolumeTotalOriginal}}).encode(
            #                 'utf-8'))
            else:
                order_data = {"sinfo": pOrderField.SInfo, "securityID": pOrderField.SecurityID,
                              "orderLocalID": pOrderField.OrderLocalID,
                              "direction": pOrderField.Direction, "orderSysID": pOrderField.OrderSysID,
                              "insertTime": pOrderField.InsertTime, "insertDate": pOrderField.InsertDate,
                              "acceptTime": pOrderField.AcceptTime, "cancelTime": pOrderField.CancelTime,
                              "limitPrice": pOrderField.LimitPrice, "accountID": pOrderField.AccountID,
                              "orderRef": pOrderField.OrderRef, "turnover": pOrderField.Turnover,
                              "volume": pOrderField.VolumeTotalOriginal, "volumeTraded": pOrderField.VolumeTraded,
                              "orderStatus": pOrderField.OrderStatus,
                              "orderSubmitStatus": pOrderField.OrderSubmitStatus,
                              "statusMsg": pOrderField.StatusMsg}
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_ORDER, 0, order_data)
        except Exception as e:
            async_log_util.error(logger_local_huaxin_trade_debug, "OnRtnOrder 出错")
        except:
            async_log_util.error(logger_local_huaxin_trade_debug, "OnRtnOrder 出错")
    def OnRtnTrade(self, pTradeField: "CTORATstpTradeField") -> "void":
        try:
            async_log_util.info(logger_local_huaxin_trade_debug,
                                'OnRtnTrade: TradeID[%s] InvestorID[%s] SecurityID[%s] OrderRef[%d] OrderLocalID[%s] Price[%.2f] Volume[%d]'
                                % (pTradeField.TradeID, pTradeField.InvestorID, pTradeField.SecurityID,
                                   pTradeField.OrderRef, pTradeField.OrderLocalID, pTradeField.Price,
                                   pTradeField.Volume))
        except:
            pass
    def OnRtnMarketStatus(self, pMarketStatusField: "CTORATstpMarketStatusField") -> "void":
        # TORA_TSTP_MKD_SHA(1): 上海A股
        # TORA_TSTP_MKD_SZA(2): 深圳A股
        # TORA_TSTP_MKD_BJMain(a):北京主板
        # TORA_TSTP_MST_UnKnown(  # ):未知
        # TORA_TSTP_MST_BeforeTrading(0): 开盘前
        # TORA_TSTP_MST_Continous(1): 连续交易
        # TORA_TSTP_MST_Closed(2): 收盘
        # TORA_TSTP_MST_OpenCallAuction(3): 开盘集合竞价
        try:
            logger.info('OnRtnMarketStatus: MarketID[%s] MarketStatus[%s]'
                        % (pMarketStatusField.MarketID, pMarketStatusField.MarketStatus))
        except:
            pass
    def OnRspQrySecurity(self, pSecurityField: "CTORATstpSecurityField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            logger.info(
                'OnRspQrySecurity[%d]: SecurityID[%s] SecurityName[%s] MarketID[%s] OrderUnit[%s] OpenDate[%s] UpperLimitPrice[%.2f] LowerLimitPrice[%.2f]'
                % (nRequestID, pSecurityField.SecurityID, pSecurityField.SecurityName, pSecurityField.MarketID,
                   pSecurityField.OrderUnit, pSecurityField.OpenDate, pSecurityField.UpperLimitPrice,
                   pSecurityField.LowerLimitPrice))
        else:
            logger.info('查询合约结束[%d] ErrorID[%d] ErrorMsg[%s]'
                        % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryInvestor(self, pInvestorField: "CTORATstpInvestorField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        if bIsLast != 1:
            logger.info('OnRspQryInvestor[%d]: InvestorID[%s] InvestorName[%s] Operways[%s]'
                        % (nRequestID, pInvestorField.InvestorID, pInvestorField.InvestorName,
                           pInvestorField.Operways))
        else:
            logger.info('查询投资者结束[%d] ErrorID[%d] ErrorMsg[%s]'
                        % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryShareholderAccount(self, pShareholderAccountField: "CTORATstpShareholderAccountField",
                                   pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int",
                                   bIsLast: "bool") -> "void":
        if bIsLast != 1:
            logger_local_huaxin_trade_debug.info(
                'OnRspQryShareholderAccount[%d]: InvestorID[%s] ExchangeID[%s] ShareholderID[%s]'
                % (nRequestID, pShareholderAccountField.InvestorID, pShareholderAccountField.ExchangeID,
                   pShareholderAccountField.ShareholderID))
        else:
            logger.info('查询股东账户结束[%d] ErrorID[%d] ErrorMsg[%s]'
                        % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspQryTradingAccount(self, pTradingAccountField: "CTORATstpTradingAccountField",
                               pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int", bIsLast: "bool") -> "void":
        try:
            if nRequestID not in self.__temp_money_account_list_dict:
                self.__temp_money_account_list_dict[nRequestID] = []
            if bIsLast != 1:
                self.__temp_money_account_list_dict[nRequestID].append(
                    {"departmentID": pTradingAccountField.DepartmentID, "investorID": pTradingAccountField.InvestorID,
                     "accountID": pTradingAccountField.AccountID, "currencyID": pTradingAccountField.CurrencyID,
                     "usefulMoney": round(pTradingAccountField.UsefulMoney, 2),
                     "frozenCash": round(pTradingAccountField.FrozenCash, 2),
                     "fetchLimit": round(pTradingAccountField.FetchLimit, 2),
                     "preDeposit": round(pTradingAccountField.PreDeposit, 2)})
                # logger.info(
                #     'OnRspQryTradingAccount[%d]: DepartmentID[%s] InvestorID[%s] AccountID[%s] CurrencyID[%s] UsefulMoney[%.2f] FetchLimit[%.2f]'
                #     % (nRequestID, pTradingAccountField.DepartmentID, pTradingAccountField.InvestorID,
                #        pTradingAccountField.AccountID, pTradingAccountField.CurrencyID,
                #        pTradingAccountField.UsefulMoney, pTradingAccountField.FetchLimit))
            else:
                results = self.__temp_money_account_list_dict.pop(nRequestID)
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_LIST_MONEY, nRequestID,
                                                  results)
                # logger.info('查询资金账号结束[%d] ErrorID[%d] ErrorMsg[%s]'
                #             % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
        except:
            pass
    def OnRspQryOrder(self, pOrderField: "CTORATstpOrderField", pRspInfoField: "CTORATstpRspInfoField",
                      nRequestID: "int", bIsLast: "bool") -> "void":
        try:
            if nRequestID not in self.__temp_order_list_dict:
                self.__temp_order_list_dict[nRequestID] = []
            if not bIsLast:
                # logger.info(
                #     'OnRspQryOrder[%d]: SecurityID[%s] OrderLocalID[%s] Direction[%s] OrderRef[%d] OrderSysID[%s] VolumeTraded[%d] OrderStatus[%s] OrderSubmitStatus[%s], StatusMsg[%s]'
                #     % (nRequestID, pOrderField.SecurityID, pOrderField.OrderLocalID, pOrderField.Direction,
                #        pOrderField.OrderRef, pOrderField.OrderSysID,
                #        pOrderField.VolumeTraded, pOrderField.OrderStatus, pOrderField.OrderSubmitStatus,
                #        pOrderField.StatusMsg))
                self.__temp_order_list_dict[nRequestID].append(
                    {"securityID": pOrderField.SecurityID, "orderLocalID": pOrderField.OrderLocalID,
                     "direction": pOrderField.Direction, "orderSysID": pOrderField.OrderSysID,
                     "insertTime": pOrderField.InsertTime, "insertDate": pOrderField.InsertDate,
                     "acceptTime": pOrderField.AcceptTime, "cancelTime": pOrderField.CancelTime,
                     "limitPrice": pOrderField.LimitPrice, "accountID": pOrderField.AccountID,
                     "turnover": pOrderField.Turnover, "orderRef": pOrderField.OrderRef,
                     "volume": pOrderField.VolumeTotalOriginal,
                     "volumeTraded": pOrderField.VolumeTraded, "orderStatus": pOrderField.OrderStatus,
                     "orderSubmitStatus": pOrderField.OrderSubmitStatus, "statusMsg": pOrderField.StatusMsg})
            else:
                # logger.info('查询报单结束[%d] ErrorID[%d] ErrorMsg[%s]'
                #             % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_LIST_DELEGATE, nRequestID,
                                                  self.__temp_order_list_dict[nRequestID])
                self.__temp_order_list_dict.pop(nRequestID)
        except:
            pass
    def OnRspQryPosition(self, pPositionField: "CTORATstpPositionField", pRspInfoField: "CTORATstpRspInfoField",
                         nRequestID: "int", bIsLast: "bool") -> "void":
        try:
            if nRequestID not in self.__temp_position_list_dict:
                self.__temp_position_list_dict[nRequestID] = []
            if bIsLast != 1:
                self.__temp_position_list_dict[nRequestID].append(
                    {"investorID": pPositionField.InvestorID, "tradingDay": pPositionField.TradingDay,
                     "securityName": pPositionField.SecurityName,
                     "securityID": pPositionField.SecurityID, "historyPos": pPositionField.HistoryPos,
                     "historyPosFrozen": pPositionField.HistoryPosFrozen,
                     "todayBSPos": pPositionField.TodayBSPos, "todayBSPosFrozen": pPositionField.TodayBSPosFrozen,
                     "historyPosPrice": pPositionField.HistoryPosPrice, "totalPosCost": pPositionField.TotalPosCost,
                     "prePosition": pPositionField.PrePosition, "availablePosition": pPositionField.AvailablePosition,
                     "currentPosition": pPositionField.CurrentPosition, "openPosCost": pPositionField.OpenPosCost,
                     "todayCommission": pPositionField.TodayCommission,
                     "todayTotalBuyAmount": pPositionField.TodayTotalBuyAmount,
                     "todayTotalSellAmount": pPositionField.TodayTotalSellAmount})
            else:
                # logger.info('查询持仓结束[%d] ErrorID[%d] ErrorMsg[%s]'
                #             % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_LIST_POSITION, nRequestID,
                                                  self.__temp_position_list_dict[nRequestID])
                self.__temp_position_list_dict.pop(nRequestID)
        except:
            pass
    # 成交回报,参数pTradeField是一个CTORATstpTradeField类对象
    def OnRtnTrade(self, pTradeField: "CTORATstpTradeField") -> "void":
        pass
        # logger.info("OnRtnTrade")
    # 查询成交响应,参数pTradeField是一个CTORATstpTradeField类对象
    def OnRspQryTrade(self, pTradeField: "CTORATstpTradeField", pRspInfoField: "CTORATstpRspInfoField",
                      nRequestID: "int", bIsLast: "bool") -> "void":
        try:
            # logger.info("查询成交响应")
            pass
            if nRequestID not in self.__temp_order_list_dict:
                self.__temp_order_list_dict[nRequestID] = []
            if not bIsLast:
                self.__temp_order_list_dict[nRequestID].append(
                    {"tradeID": pTradeField.TradeID, "securityID": pTradeField.SecurityID,
                     "orderLocalID": pTradeField.OrderLocalID,
                     "direction": pTradeField.Direction, "orderSysID": pTradeField.OrderSysID,
                     "price": pTradeField.Price,
                     "tradeTime": pTradeField.TradeTime,
                     "volume": pTradeField.Volume, "tradeDate": pTradeField.TradeDate,
                     "tradingDay": pTradeField.TradingDay,
                     "pbuID": pTradeField.PbuID, "accountID": pTradeField.AccountID})
            else:
                self.call_back_thread_pool.submit(self.__data_callback, trade_manager.TYPE_LIST_TRADED, nRequestID,
                                                  self.__temp_order_list_dict[nRequestID])
                self.__temp_order_list_dict.pop(nRequestID)
        except:
            pass
def run(queue_strategy_w_trade_r, queue_result):
    """
    交易运行
    :param queue_strategy_w_trade_r:
    :return:
    """
    # -----------初始化交易环境---------------------
    trade_manager.set_result_read_queue(queue_result)
    api = traderapi.CTORATstpTraderApi.CreateTstpTraderApi('./flow', False)
    # 创建回调对象
    spi = TraderSpi(api, trade_manager.traderapi_callback)
    # 注册回调接口
    api.RegisterSpi(spi)
    # 注册多个交易前置服务地址,用逗号隔开
    # api.RegisterFront('tcp://10.0.1.101:6500,tcp://10.0.1.101:26500')
    # 注册名字服务器地址,支持多服务地址逗号隔开
    # api.RegisterNameServer('tcp://10.0.1.101:52370')
    # api.RegisterNameServer('tcp://10.0.1.101:52370,tcp://10.0.1.101:62370')
    if not TEST_TRADE:  # 模拟环境,TCP 直连Front方式
        # 注册单个交易前置服务地址
        ##B类服务器##
        logger.info(f"注册交易地址:{FRONT_ADDRESS}/{FRONT_ADDRESS1}")
        api.RegisterFront(FRONT_ADDRESS)  # 正式环境主地址
        api.RegisterFront(FRONT_ADDRESS1)  # 正式环境备用地址
        ##A类服务器##
        # api.RegisterFront("tcp://10.224.123.143:6500")  # 正式环境主地址
        # api.RegisterFront("tcp://10.224.123.147:26500")  # 正式环境备用地址
        # TD_TCP_FrontAddress = "tcp://210.14.72.21:4400"  # 仿真交易环境
        # TD_TCP_FrontAddress = "tcp://210.14.72.15:4400"  # 24小时环境A套
        # TD_TCP_FrontAddress="tcp://210.14.72.16:9500" #24小时环境B套
        # api.RegisterFront(TD_TCP_FrontAddress)
        # 注册多个交易前置服务地址,用逗号隔开 形如: api.RegisterFront("tcp://10.0.1.101:6500,tcp://10.0.1.101:26500")
        # print("TD_TCP_FensAddress[sim or 24H]::%s\n" % TD_TCP_FrontAddress)
    else:  # 模拟环境,FENS名字服务器方式
        TD_TCP_FrontAddress = "tcp://210.14.72.21:4400"  # 仿真交易环境
        # TD_TCP_FrontAddress="tcp://210.14.72.15:4400" #24小时环境A套
        # TD_TCP_FrontAddress="tcp://210.14.72.16:9500" #24小时环境B套
        api.RegisterFront(TD_TCP_FrontAddress)
        # 注册多个交易前置服务地址,用逗号隔开 形如: api.RegisterFront("tcp://10.0.1.101:6500,tcp://10.0.1.101:26500")
        print("TD_TCP_FensAddress[sim or 24H]::%s\n" % TD_TCP_FrontAddress)
    # 订阅私有流
    api.SubscribePrivateTopic(traderapi.TORA_TERT_QUICK)
    # 订阅公有流
    api.SubscribePublicTopic(traderapi.TORA_TERT_QUICK)
    # 启动接口
    api.Init()
    data_callback = trade_manager.MyTradeActionCallback(UserID, Password, SZSE_ShareHolderID, SSE_ShareHolderID, api)
    # 不需要运行命令解析
    tradeCommandManager = TradeCommandManager()
    tradeCommandManager.init(
        data_callback,
        queue_strategy_w_trade_r)
    logger_info.debug("全部初始化完成")
    tradeCommandManager.run(True)
    while True:
        time.sleep(2)
if __name__ == "__main__":
    pass
huaxin_client/cb/trade_manager.py
New file
@@ -0,0 +1,616 @@
"""
华鑫交易管理
"""
import concurrent.futures
import json
import logging
import time
import multiprocessing
import traderapi
from huaxin_client.client_network import SendResponseSkManager
from huaxin_client.command_manager import TradeActionCallback
from log_module import async_log_util
from log_module.log import logger_trade, logger_local_huaxin_trade_debug
from utils import tool, socket_util
ENABLE_ORDER = True
TYPE_ORDER = 0
TYPE_CANCEL_ORDER = 1
TYPE_LIST_DELEGATE = 2
TYPE_LIST_TRADED = 3
TYPE_LIST_POSITION = 4
TYPE_LIST_MONEY = 5
# 成交
TYPE_DEAL = 6
__queue_result: multiprocessing.Queue = None
def set_result_read_queue(queue_result):
    """
    设置结果读取队列
    :param queue_result:
    :return:
    """
    global __queue_result
    __queue_result = queue_result
def __send_response(type, data_bytes):
    sk = SendResponseSkManager.create_send_response_sk()
    try:
        data_bytes = socket_util.load_header(data_bytes)
        sk.sendall(data_bytes)
        result, header_str = socket_util.recv_data(sk)
        result = json.loads(result)
        if result["code"] != 0:
            raise Exception(result['msg'])
    finally:
        sk.close()
def send_response(data, type, _client_id, _request_id, show_log=True):
    if show_log:
        async_log_util.debug(logger_local_huaxin_trade_debug, f"回调返回内容:{data}")
    __queue_result.put_nowait(data)
# 交易反馈回调
def __traderapi_callback(type, req_id, data):
    async_log_util.info(logger_local_huaxin_trade_debug, "回调:type-{} req_id-{}", type, req_id)
    key = req_id
    if type == TYPE_ORDER or type == TYPE_CANCEL_ORDER:
        key = data["sinfo"]
    try:
        if req_rid_dict and key in req_rid_dict:
            temp_params = req_rid_dict.pop(key)
            client_id, request_id = temp_params[0], temp_params[1]
            # 本地订单号-系统订单号映射
            if len(temp_params) >= 4 and type == TYPE_ORDER:
                order_ref = temp_params[3]
                data["orderRef"] = order_ref
            async_log_util.info(logger_local_huaxin_trade_debug, "API回调 request_id-{}", request_id)
            # 测试
            # send_response(
            #     json.dumps({"type": "response", "data": {"code": 0, "data": data}, "client_id": client_id,
            #                 "request_id": request_id}), type, client_id, request_id, temp_params[2])
            send_response(
                json.dumps({"type": "response", "data": {"code": 0, "data": data}, "client_id": client_id,
                            "request_id": request_id}), type, client_id, request_id)
            async_log_util.info(logger_local_huaxin_trade_debug, "API回调结束 req_id-{} request_id-{}", req_id, request_id)
        else:
            async_log_util.info(logger_local_huaxin_trade_debug, "非API回调 req_id-{}", req_id)
            send_response(
                json.dumps({"type": "trade_callback", "data": {"code": 0, "data": data, "type": type}}),
                type,
                None,
                req_id)
    except Exception as e:
        logging.exception(e)
# 采用异步回调
def traderapi_callback(type, req_id, data):
    __traderapi_callback(type, req_id, data)
req_rid_dict = {}
class TradeSimpleApi:
    req_id = 0
    __buy_sinfo_set = set()
    __sell_sinfo_set = set()
    __cancel_buy_sinfo_set = set()
    __cancel_sell_sinfo_set = set()
    def __init__(self, UserID, Password, SZSE_ShareHolderID, SSE_ShareHolderID, api: traderapi.CTORATstpTraderApi):
        """
        :param SZSE_ShareHolderID: 深证投资者代码
        :param SSE_ShareHolderID: 上证投资者代码
        :param api: 交易接口
        """
        self.UserID = UserID
        self.Password = Password
        self.SZSE_ShareHolderID = SZSE_ShareHolderID
        self.SSE_ShareHolderID = SSE_ShareHolderID
        self.api = api
    @classmethod
    def set_login_info(cls, session_id, front_id):
        cls.__session_id = session_id
        cls.__front_id = front_id
    # sinfo char(32)
    def buy(self, code, count, price, sinfo, order_ref, shadow_price=None):
        if not ENABLE_ORDER:
            return
        if sinfo in self.__buy_sinfo_set:
            raise Exception(f'下单请求已经提交:{sinfo}')
        async_log_util.info(logger_trade, f"{code}华鑫本地真实下单开始")
        async_log_util.info(logger_local_huaxin_trade_debug,
                            f"进入买入方法:code-{code} sinfo-{sinfo} order_ref-{order_ref}")
        self.__buy_sinfo_set.add(sinfo)
        self.req_id += 1
        # 请求报单
        req_field = traderapi.CTORATstpInputOrderField()
        # TORA_TSTP_EXD_COMM(0): 通用(内部使用)
        # TORA_TSTP_EXD_SSE(1): 上海交易所
        # TORA_TSTP_EXD_SZSE(2): 深圳交易所
        # TORA_TSTP_EXD_HK(3): 香港交易所
        # TORA_TSTP_EXD_BSE(4): 北京证券交易所
        if tool.get_market_type(code) == tool.MARKET_TYPE_SZSE:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SZSE
            req_field.ShareholderID = self.SZSE_ShareHolderID
        elif tool.get_market_type(code) == tool.MARKET_TYPE_SSE:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
            req_field.ShareholderID = self.SSE_ShareHolderID
        # 证券代码
        req_field.SecurityID = code
        req_field.Direction = traderapi.TORA_TSTP_D_Buy
        req_field.VolumeTotalOriginal = count
        req_field.SInfo = sinfo
        req_field.OrderRef = order_ref
        '''
        上交所支持限价指令和最优五档剩撤、最优五档剩转限两种市价指令,对于科创板额外支持本方最优和对手方最优两种市价指令和盘后固定价格申报指令
        深交所支持限价指令和立即成交剩余撤销、全额成交或撤销、本方最优、对手方最优和最优五档剩撤五种市价指令
        限价指令和上交所科创板盘后固定价格申报指令需填写报单价格,其它市价指令无需填写报单价格
        以下以上交所限价指令为例,其它指令参考开发指南相关说明填写OrderPriceType、TimeCondition和VolumeCondition三个字段:
        '''
        req_field.LimitPrice = price
        req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_LimitPrice
        req_field.TimeCondition = traderapi.TORA_TSTP_TC_GFD
        req_field.VolumeCondition = traderapi.TORA_TSTP_VC_AV
        '''
        OrderRef为报单引用,类型为整型,该字段报单时为选填
        若不填写,则系统会为每笔报单自动分配一个报单引用
        若填写,则需保证同一个TCP会话下报单引用严格单调递增,不要求连续递增,至少需从1开始编号
        '''
        # req_field.OrderRef = 1
        '''
        InvestorID为选填,若填写则需保证填写正确
        Operway为委托方式,根据券商要求填写,无特殊说明置空即可
        终端自定义字段,终端可根据需要填写如下字段的值,该字段值不会被柜台系统修改,在报单回报和查询报单时返回给终端
        '''
        # req_field.SInfo = 'sinfo'
        # req_field.IInfo = 123
        '''
        其它字段置空
        '''
        # 给L2发送消息
        ret = self.api.ReqOrderInsert(req_field, self.req_id)
        if ret != 0:
            raise Exception('ReqOrderInsert fail, ret[%d]' % ret)
        # 常态化监听不需要单独设置
        # if queue_other_w_l2_r is not None:
        #     queue_other_w_l2_r.put_nowait(
        #         json.dumps({"type": "listen_volume", "data": {"code": code,
        #                                                       "volume": count}}).encode(
        #             'utf-8'))
        async_log_util.info(logger_trade, f"{code}华鑫本地真实下单结束")
        # --------------------------------影子订单--------------------------------
        if shadow_price:
            if order_ref:
                # 下一个影子订单
                shadow_order_ref = order_ref + 1
                shadow_sinfo = f"s_b_{order_ref}"
                req_field.LimitPrice = shadow_price
                req_field.SInfo = shadow_sinfo
                req_field.OrderRef = shadow_order_ref
                req_field.VolumeTotalOriginal = 100
                self.req_id += 1
                ret = self.api.ReqOrderInsert(req_field, self.req_id)
                if ret != 0:
                    raise Exception('ReqOrderInsert fail, ret[%d]' % ret)
                # 影子订单撤单
                # 撤掉影子单
                shadow_cancel_order_ref = shadow_order_ref + 1
                # 深证停留50ms上证停留200ms
                delay_s = 0.05 if code.find("00") == 0 else 0.2
                self.cancel_buy(code, f"s_c_{shadow_order_ref}", order_sys_id=None,
                                order_ref=shadow_order_ref,
                                order_action_ref=None, delay_s=delay_s)
        return ret
    # 撤买
    def cancel_buy(self, code, sinfo, order_sys_id=None, order_ref=None, order_action_ref=None, delay_s=0.0):
        if delay_s > 0:
            time.sleep(delay_s)
        if sinfo in self.__cancel_buy_sinfo_set:
            raise Exception(f'撤单请求已经提交:{sinfo}')
        async_log_util.info(logger_local_huaxin_trade_debug,
                            f"进入撤单方法:code-{code} order_sys_id-{order_sys_id}  order_ref-{order_ref} sinfo-{sinfo}")
        self.__cancel_buy_sinfo_set.add(sinfo)
        self.req_id += 1
        # 请求撤单
        req_field = traderapi.CTORATstpInputOrderActionField()
        if code.find('00') == 0:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SZSE
        elif code.find('60') == 0:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
        req_field.ActionFlag = traderapi.TORA_TSTP_AF_Delete
        # 撤单支持以下两种方式定位原始报单:
        # (1)报单引用方式
        # req_field.FrontID = self.__front_id
        # req_field.SessionID = self.__session_id
        # req_field.OrderRef = 1
        # (2)系统报单编号方式
        if order_sys_id:
            req_field.OrderSysID = order_sys_id
        elif order_ref is not None:
            req_field.OrderRef = order_ref
            req_field.SessionID = self.__session_id
            req_field.FrontID = self.__front_id
        if order_action_ref:
            req_field.OrderActionRef = order_action_ref
        # OrderActionRef报单操作引用,用法同报单引用,可根据需要选填
        '''
        终端自定义字段,终端可根据需要填写如下字段的值,该字段值不会被柜台系统修改,在查询撤单时返回给终端
        '''
        req_field.SInfo = sinfo
        # req_field.IInfo = 123
        '''
        委托方式字段根据券商要求填写,无特殊说明置空即可
        其它字段置空
        '''
        ret = self.api.ReqOrderAction(req_field, self.req_id)
        if ret != 0:
            raise Exception('ReqOrderAction fail, ret[%d]' % ret)
        return
    # 卖
    def sell(self, code, count, price, price_type, sinfo, order_ref=None):
        if sinfo in self.__sell_sinfo_set:
            raise Exception(f'下单请求已经提交:{sinfo}')
        self.__sell_sinfo_set.add(sinfo)
        self.req_id += 1
        # 请求报单
        req_field = traderapi.CTORATstpInputOrderField()
        # TORA_TSTP_EXD_COMM(0): 通用(内部使用)
        # TORA_TSTP_EXD_SSE(1): 上海交易所
        # TORA_TSTP_EXD_SZSE(2): 深圳交易所
        # TORA_TSTP_EXD_HK(3): 香港交易所
        # TORA_TSTP_EXD_BSE(4): 北京证券交易所
        if tool.get_market_type(code) == tool.MARKET_TYPE_SZSE:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SZSE
            req_field.ShareholderID = self.SZSE_ShareHolderID
        elif tool.get_market_type(code) == tool.MARKET_TYPE_SSE:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
            req_field.ShareholderID = self.SSE_ShareHolderID
        # 证券代码
        req_field.SecurityID = code
        req_field.Direction = traderapi.TORA_TSTP_D_Sell
        req_field.VolumeTotalOriginal = count
        req_field.SInfo = sinfo
        '''
        上交所支持限价指令和最优五档剩撤、最优五档剩转限两种市价指令,对于科创板额外支持本方最优和对手方最优两种市价指令和盘后固定价格申报指令
        深交所支持限价指令和立即成交剩余撤销、全额成交或撤销、本方最优、对手方最优和最优五档剩撤五种市价指令
        限价指令和上交所科创板盘后固定价格申报指令需填写报单价格,其它市价指令无需填写报单价格
        以下以上交所限价指令为例,其它指令参考开发指南相关说明填写OrderPriceType、TimeCondition和VolumeCondition三个字段:
        '''
        print('卖 price', price, price_type)
        if price and price > 0:
            req_field.LimitPrice = price
        if price_type == 1:
            req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_AnyPrice
        elif price_type == 2:
            req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_LimitPrice
        elif price_type == 3:
            req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_BestPrice
        elif price_type == 4:
            req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_FixPrice
        elif price_type == 5:
            req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_FiveLevelPrice
        elif price_type == 6:
            req_field.OrderPriceType = traderapi.TORA_TSTP_OPT_HomeBestPrice
        req_field.TimeCondition = traderapi.TORA_TSTP_TC_GFD
        req_field.VolumeCondition = traderapi.TORA_TSTP_VC_AV
        if order_ref:
            req_field.OrderRef = order_ref
        '''
        OrderRef为报单引用,类型为整型,该字段报单时为选填
        若不填写,则系统会为每笔报单自动分配一个报单引用
        若填写,则需保证同一个TCP会话下报单引用严格单调递增,不要求连续递增,至少需从1开始编号
        '''
        # req_field.OrderRef = 1
        '''
        InvestorID为选填,若填写则需保证填写正确
        Operway为委托方式,根据券商要求填写,无特殊说明置空即可
        终端自定义字段,终端可根据需要填写如下字段的值,该字段值不会被柜台系统修改,在报单回报和查询报单时返回给终端
        '''
        # req_field.SInfo = 'sinfo'
        # req_field.IInfo = 123
        '''
        其它字段置空
        '''
        ret = self.api.ReqOrderInsert(req_field, self.req_id)
        if ret != 0:
            raise Exception('ReqOrderInsert fail, ret[%d]' % ret)
        return
    # 撤卖
    def cancel_sell(self, code, order_sys_id, sinfo):
        if sinfo in self.__cancel_sell_sinfo_set:
            raise Exception(f'撤单请求已经提交:{sinfo}')
        self.__cancel_sell_sinfo_set.add(sinfo)
        self.req_id += 1
        # 请求撤单
        req_field = traderapi.CTORATstpInputOrderActionField()
        if code.find('00') == 0:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SZSE
        elif code.find('60') == 0:
            req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
        req_field.ActionFlag = traderapi.TORA_TSTP_AF_Delete
        # 撤单支持以下两种方式定位原始报单:
        # (1)报单引用方式
        # req_field.FrontID = self.__front_id
        # req_field.SessionID = self.__session_id
        # req_field.OrderRef = 1
        # (2)系统报单编号方式
        req_field.OrderSysID = order_sys_id
        # OrderActionRef报单操作引用,用法同报单引用,可根据需要选填
        '''
        终端自定义字段,终端可根据需要填写如下字段的值,该字段值不会被柜台系统修改,在查询撤单时返回给终端
        '''
        req_field.SInfo = sinfo
        # req_field.IInfo = 123
        '''
        委托方式字段根据券商要求填写,无特殊说明置空即可
        其它字段置空
        '''
        ret = self.api.ReqOrderAction(req_field, self.req_id)
        if ret != 0:
            raise Exception('ReqOrderAction fail, ret[%d]' % ret)
        return
    # 查询当日可撤销的委托
    def list_delegate_orders(self, is_cancel):
        self.req_id += 1
        req_id = self.req_id
        req_field = traderapi.CTORATstpQryOrderField()
        # 以下字段不填表示不设过滤条件,即查询所有报单
        # req_field.SecurityID = '600000'
        req_field.InsertTimeStart = '09:15:00'
        req_field.InsertTimeEnd = '15:00:00'
        # IsCancel字段填1表示只查询可撤报单
        if is_cancel:
            req_field.IsCancel = 1
        # req_field.SInfo = sinfo
        ret = self.api.ReqQryOrder(req_field, req_id)
        if ret != 0:
            raise Exception('ReqQryOrder fail, ret[%d]' % ret)
        return req_id
    # 查询当日成交的订单
    def list_traded_orders(self):
        self.req_id += 1
        req_id = self.req_id
        req_field = traderapi.CTORATstpQryTradeField()
        ret = self.api.ReqQryTrade(req_field, req_id)
        if ret != 0:
            raise Exception('ReqQryTrade fail, ret[%d]' % ret)
        return req_id
    # 查询持仓
    def list_positions(self):
        self.req_id += 1
        req_id = self.req_id
        req_field = traderapi.CTORATstpQryPositionField()
        ret = self.api.ReqQryPosition(req_field, req_id)
        if ret != 0:
            raise Exception('ReqQryPosition fail, ret[%d]' % ret)
        return req_id
    # 查询资金账户
    def get_money_account(self):
        self.req_id += 1
        req_id = self.req_id
        req_field = traderapi.CTORATstpQryTradingAccountField()
        req_field.CurrencyID = traderapi.TORA_TSTP_CID_CNY
        ret = self.api.ReqQryTradingAccount(req_field, req_id)
        if ret != 0:
            raise Exception('ReqQryTradingAccount fail, ret[%d]' % ret)
        return req_id
    def login(self):
        # 请求登录
        login_req = traderapi.CTORATstpReqUserLoginField()
        # 支持以用户代码、资金账号和股东账号方式登录
        # (1)以用户代码方式登录
        login_req.LogInAccount = self.UserID
        login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_UserID
        # (2)以资金账号方式登录
        # login_req.DepartmentID = DepartmentID
        # login_req.LogInAccount = AccountID
        # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_AccountID
        # (3)以上海股东账号方式登录
        # login_req.LogInAccount = SSE_ShareHolderID
        # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_SHAStock
        # (4)以深圳股东账号方式登录
        # login_req.LogInAccount = SZSE_ShareHolderID
        # login_req.LogInAccountType = traderapi.TORA_TSTP_LACT_SZAStock
        # 支持以密码和指纹(移动设备)方式认证
        # (1)密码认证
        # 密码认证时AuthMode可不填
        # login_req.AuthMode = traderapi.TORA_TSTP_AM_Password
        login_req.Password = self.Password
        # (2)指纹认证
        # 非密码认证时AuthMode必填
        # login_req.AuthMode = traderapi.TORA_TSTP_AM_FingerPrint
        # login_req.DeviceID = '03873902'
        # login_req.CertSerial = '9FAC09383D3920CAEFF039'
        # 终端信息采集
        # UserProductInfo填写终端名称
        login_req.UserProductInfo = 'jiabei'
        # 按照监管要求填写终端信息
        login_req.TerminalInfo = 'PC;IIP=123.112.154.118;IPORT=50361;LIP=192.168.118.107;MAC=54EE750B1713FCF8AE5CBD58;HD=TF655AY91GHRVL'
        # 以下内外网IP地址若不填则柜台系统自动采集,若填写则以终端填值为准报送
        # login_req.MacAddress = '5C-87-9C-96-F3-E3'
        # login_req.InnerIPAddress = '10.0.1.102'
        # login_req.OuterIPAddress = '58.246.43.50'
        TradeSimpleApi.req_id += 1
        ret = self.api.ReqUserLogin(login_req, TradeSimpleApi.req_id)
        if ret != 0:
            raise Exception('ReqUserLogin fail, ret[%d]' % ret)
class MyTradeActionCallback(TradeActionCallback):
    trade_thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=30)
    def __init__(self, UserID, Password, SZSE_ShareHolderID, SSE_ShareHolderID, api: traderapi.CTORATstpTraderApi):
        self.__tradeSimpleApi = TradeSimpleApi(UserID, Password, SZSE_ShareHolderID, SSE_ShareHolderID, api)
    def OnTrade(self, client_id, request_id, sk, type_, data):
        if type_ == 1:
            async_log_util.info(logger_local_huaxin_trade_debug,
                                f"\n---------------------\n请求下单:client_id-{client_id} request_id-{request_id}  data:{data}")
            # 下单
            # 1-买 2-卖
            direction = data["direction"]
            code = data["code"]
            volume = data["volume"]
            price = data["price"]
            sinfo = data["sinfo"]
            order_ref = data.get("order_ref")
            shadow_price = data.get("shadow_price")
            blocking = data.get("blocking")
            if direction == 1:
                async_log_util.info(logger_trade, f"{code}华鑫本地开始下单")
                # 买
                try:
                    if blocking:
                        req_rid_dict[sinfo] = (client_id, request_id, sk, order_ref)
                    # threading.Thread(target=lambda: self.__tradeSimpleApi.buy(code, volume, price, sinfo, order_ref),
                    #                  daemon=True).start()
                    self.trade_thread_pool.submit(self.__tradeSimpleApi.buy, code, volume, price, sinfo, order_ref,
                                                  shadow_price)
                    async_log_util.info(logger_trade, f"{code}华鑫本地下单线程结束")
                except Exception as e:
                    send_response(json.dumps({"code": 1, "msg": str(e)}), TYPE_ORDER, client_id,
                                  request_id)
                async_log_util.info(logger_local_huaxin_trade_debug,
                                    f"买入结束:code-{code} sinfo-{sinfo}")
            elif direction == 2:
                try:
                    price_type = data["price_type"]
                    if blocking:
                        req_rid_dict[sinfo] = (client_id, request_id, sk)
                    self.__tradeSimpleApi.sell(code, volume, price, price_type, sinfo, order_ref)
                    print("sell", req_rid_dict)
                except Exception as e:
                    logging.exception(e)
                    send_response(json.dumps({"code": 1, "msg": str(e)}), TYPE_ORDER, client_id,
                                  request_id)
        elif type_ == 2:
            async_log_util.info(logger_local_huaxin_trade_debug,
                                f"\n---------------------\n请求撤单:client_id-{client_id} request_id-{request_id} data-{data}")
            # 撤单
            direction = data["direction"]
            code = data["code"]
            orderSysID = data.get("orderSysID")
            orderRef = data.get("orderRef")
            orderActionRef = data.get("orderActionRef")
            sinfo = data["sinfo"]
            if direction == 1:
                # 撤买
                try:
                    if not orderSysID and orderRef is None:
                        raise Exception("没有找到系统订单号或者报单引用")
                    req_rid_dict[sinfo] = (client_id, request_id, sk)
                    self.trade_thread_pool.submit(
                        lambda: self.__tradeSimpleApi.cancel_buy(code, sinfo, order_sys_id=orderSysID,
                                                                 order_ref=orderRef, order_action_ref=orderActionRef))
                    async_log_util.info(logger_local_huaxin_trade_debug,
                                        f"撤单结束:code-{code} order_sys_id-{orderSysID} sinfo-{sinfo}")
                except Exception as e:
                    send_response(json.dumps({"code": 1, "msg": str(e)}), TYPE_CANCEL_ORDER, client_id,
                                  request_id)
            elif direction == 2:
                # 撤卖
                try:
                    req_rid_dict[sinfo] = (client_id, request_id, sk)
                    self.__tradeSimpleApi.cancel_sell(code, orderSysID, sinfo)
                except Exception as e:
                    send_response(json.dumps({"code": 1, "msg": str(e)}), TYPE_CANCEL_ORDER, client_id,
                                  request_id)
    def OnDealList(self, client_id, request_id, sk):
        async_log_util.info(logger_local_huaxin_trade_debug, f"请求成交列表:client_id-{client_id} request_id-{request_id}")
        try:
            # print("开始请求成交列表")
            req_id = self.__tradeSimpleApi.list_traded_orders()
            req_rid_dict[req_id] = (client_id, request_id, sk)
        except Exception as e:
            logging.exception(e)
            SendResponseSkManager.send_error_response("common", request_id, client_id, str(e))
    def OnDelegateList(self, client_id, request_id, sk, is_cancel):
        async_log_util.info(logger_local_huaxin_trade_debug, f"请求委托列表:client_id-{client_id} request_id-{request_id}")
        try:
            req_id = self.__tradeSimpleApi.list_delegate_orders(is_cancel)
            req_rid_dict[req_id] = (client_id, request_id, sk)
        except Exception as e:
            send_response(json.dumps({"code": 1, "msg": str(e)}), "common", client_id,
                          request_id)
    def OnMoney(self, client_id, request_id, sk):
        async_log_util.info(logger_local_huaxin_trade_debug, f"请求账户:client_id-{client_id} request_id-{request_id}")
        try:
            req_id = self.__tradeSimpleApi.get_money_account()
            req_rid_dict[req_id] = (client_id, request_id, sk)
        except Exception as e:
            send_response(json.dumps({"code": 1, "msg": str(e)}), "common", client_id,
                          request_id)
    def OnPositionList(self, client_id, request_id, sk):
        async_log_util.info(logger_local_huaxin_trade_debug, f"请求持仓:client_id-{client_id} request_id-{request_id}")
        try:
            req_id = self.__tradeSimpleApi.list_positions()
            req_rid_dict[req_id] = (client_id, request_id, sk)
        except Exception as e:
            send_response(json.dumps({"code": 1, "msg": str(e)}), "common", client_id,
                          request_id)
    def OnTest(self, client_id, request_id, data, sk):
        send_response(
            json.dumps({"type": "response", "data": {"code": 0, "data": data}, "client_id": client_id,
                        "request_id": request_id}), type, client_id, request_id, show_log=False, sk=sk)
huaxin_client/command_manager.py
@@ -8,7 +8,7 @@
import threading
from log_module import async_log_util
from log_module.log import logger_local_huaxin_trade_debug, logger_trade, logger_local_huaxin_contact_debug
from log_module.log import logger_local_huaxin_trade_debug, logger_trade, logger_local_huaxin_contact_debug, logger_info
MSG_TYPE_HEART = "heart"
# 命令信息
@@ -119,6 +119,8 @@
                        _type = val["type"]
                        if _type != "test":
                            async_log_util.info(logger_local_huaxin_contact_debug, f"接受到信息: {val}")
                            # TODO 测试
                            logger_info.info(f"接受到信息: {val}")
                        cls.process_command(_type, None, val)
                except Exception as e:
                    async_log_util.exception(logger_local_huaxin_trade_debug, e)
log_module/log.py
@@ -45,8 +45,8 @@
                   rotation="00:00", compression="zip", enqueue=True)
        # 显示在控制台
        # logger.add(sys.stdout,
        #           filter=lambda record: record["extra"].get("name") == "l2_trade", enqueue=True)
        logger.add(sys.stdout,
                  filter=lambda record: record["extra"].get("name") == "info", enqueue=True)
        logger.add(self.get_path("l2", "l2_trade_cancel"),
                   filter=lambda record: record["extra"].get("name") == "l2_trade_cancel",
@@ -327,6 +327,8 @@
__mylogger = MyLogger()
logger_info =  __mylogger.get_logger("info")
logger_trade_gui = __mylogger.get_logger("trade_gui")
logger_trade = __mylogger.get_logger("trade")
logger_trade_delegate = __mylogger.get_logger("delegate")
test/test.py
@@ -1,8 +1,29 @@
from code_atrribute.history_k_data_util import JueJinHttpApi
from huaxin_client import l2_client_for_cb
from utils import tool
import logging
import multiprocessing
import time
from huaxin_client.cb import trade_client_for_cb
from log_module.log import logger_info
from trade import huaxin_trade_api
if __name__ == "__main__":
    ffresults = l2_client_for_cb.get_subscript_codes()
    try:
        q_s_w_t_r, q_s_r_t_w = multiprocessing.Queue(), multiprocessing.Queue()
        huaxin_trade_api.run_pipe_trade(q_s_r_t_w, q_s_w_t_r)
        tradeProcess = multiprocessing.Process(
            target= trade_client_for_cb.run,
            args=(q_s_w_t_r, q_s_r_t_w))
        tradeProcess.start()
        time.sleep(3)
        print("获取委托结果", huaxin_trade_api.get_delegate_list())
    print("最终的数量", len(ffresults))
        time.sleep(2)
        print("获取持仓结果", huaxin_trade_api.get_position_list(True))
    except Exception as e:
        logging.exception(e)
    finally:
        logger_info.debug("程序结束")
trade/huaxin_trade_api.py
@@ -441,7 +441,7 @@
def get_delegate_list(can_cancel=True, blocking=True, timeout=TIMEOUT):
    request_id = __request(ClientSocketManager.CLIENT_TYPE_DELEGATE_LIST,
                           {"type": ClientSocketManager.CLIENT_TYPE_DELEGATE_LIST,
                            "can_cancel": 1 if can_cancel else 0}, blocking=blocking, is_pipe=is_pipe_channel_normal())
                            "can_cancel": 1 if can_cancel else 0}, blocking=blocking, is_pipe=is_pipe_channel_normal(), is_trade=True)
    return __read_response(request_id, blocking, timeout=timeout)
@@ -450,7 +450,7 @@
def get_deal_list(blocking=True, timeout=TIMEOUT):
    request_id = __request(ClientSocketManager.CLIENT_TYPE_DEAL_LIST,
                           {"type": ClientSocketManager.CLIENT_TYPE_DEAL_LIST}, blocking=blocking,
                           is_pipe=is_pipe_channel_normal())
                           is_pipe=is_pipe_channel_normal(), is_trade=True)
    return __read_response(request_id, blocking, timeout=timeout)
@@ -466,7 +466,7 @@
def get_money(blocking=True):
    request_id = __request(ClientSocketManager.CLIENT_TYPE_MONEY,
                           {"type": ClientSocketManager.CLIENT_TYPE_MONEY}, blocking=blocking,
                           is_pipe=is_pipe_channel_normal())
                           is_pipe=is_pipe_channel_normal(), is_trade=True)
    return __read_response(request_id, blocking)
utils/tool.py
@@ -321,6 +321,27 @@
    return None
# 深证
MARKET_TYPE_SZSE = 1
# 上证
MARKET_TYPE_SSE = 0
# 未知
MARKET_TYPE_UNKNOWN = -1
def get_market_type(code):
    """
    根据股票代码
    :param code:
    :return:
    """
    if code.find("00") == 0 or code.find("30") == 0 or code.find("12") == 0:
        return MARKET_TYPE_SZSE
    elif code.find("60") == 0 or code.find("68") == 0 or code.find("11") == 0:
        return MARKET_TYPE_SSE
    else:
        return MARKET_TYPE_UNKNOWN
if __name__ == "__main__":
    pass