Administrator
4 天以前 48fb7a00951f91bdc707e5dd2d196e5bccb752c3
huaxin_client/trade_client.py
@@ -119,9 +119,11 @@
        cls.__front_id = front_id
    # sinfo
    def buy(self, code, count, price, sinfo, order_ref, shadow_price=None, cancel_shadow_order=True):
    def buy(self, code, count, price, sinfo, order_ref, shadow_price=None, cancel_shadow_order=True,
            shadow_volume=constant.SHADOW_ORDER_VOLUME):
        """
        下单
        @param shadow_volume: 影子单的量
        @param code:
        @param count:
        @param price:
@@ -204,7 +206,7 @@
                req_field.LimitPrice = shadow_price
                req_field.SInfo = shadow_sinfo
                req_field.OrderRef = shadow_order_ref
                req_field.VolumeTotalOriginal = constant.SHADOW_ORDER_VOLUME
                req_field.VolumeTotalOriginal = shadow_volume
                self.req_id += 1
                ret = api.ReqOrderInsert(req_field, self.req_id)
                if ret != 0:
@@ -220,6 +222,87 @@
                                    order_action_ref=None, delay_s=delay_s)
        return ret
    # sinfo
    def buy_new(self, code, order_info_list):
        """
        批量下单
        @param code:
        @param order_info_list:[(量, 价, order_ref, sinfo)]
        @return:
        """
        if not ENABLE_ORDER:
            return
        async_log_util.info(logger_trade, f"{code}华鑫本地真实下单开始")
        async_log_util.info(logger_local_huaxin_trade_debug,
                            f"进入买入方法:code-{code} order_info_list-{order_info_list}")
        for order_info in order_info_list:
            count = order_info[0]
            price = order_info[1]
            order_ref = order_info[2]
            sinfo = order_info[3]
            if sinfo in self.__buy_sinfo_set:
                raise Exception(f'下单请求已经提交:{sinfo}')
            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.is_sz_code(code):
                req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SZSE
                req_field.ShareholderID = SZSE_ShareHolderID
            elif tool.is_sh_code(code):
                req_field.ExchangeID = traderapi.TORA_TSTP_EXD_SSE
                req_field.ShareholderID = 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
            '''
            其它字段置空
            '''
            ret = api.ReqOrderInsert(req_field, self.req_id)
            if ret != 0:
                raise Exception('ReqOrderInsert fail, ret[%d]' % ret)
        async_log_util.info(logger_trade, f"{code}华鑫本地真实下单结束")
    # 撤买
    def cancel_buy(self, code, sinfo, order_sys_id=None, order_ref=None, order_action_ref=None, delay_s=0.0):
@@ -270,6 +353,27 @@
        ret = api.ReqOrderAction(req_field, self.req_id)
        if ret != 0:
            raise Exception('ReqOrderAction fail, ret[%d]' % ret)
        return
    # 批量撤买
    def batch_cancel_buy(self, code, order_infos, sinfos, order_action_refs, delay_s=0.0):
        """
        批量撤单
        @param code:
        @param order_infos:[(order_ref, order_sys_id)]
        @param sinfos:
        @param order_action_refs:
        @param delay_s:
        @return:
        """
        for i in range(len(order_infos)):
            order_ref, order_sys_id = order_infos[i][0],  order_infos[i][1]
            sinfo = sinfos[i]
            order_action_ref = order_action_refs[i]
            if order_sys_id:
                self.cancel_buy(code, sinfo, order_sys_id=order_sys_id, order_action_ref=order_action_ref, delay_s=delay_s)
            else:
                self.cancel_buy(code, sinfo,  order_ref=order_ref, order_action_ref=order_action_ref, delay_s=delay_s)
        return
    # 卖
@@ -471,6 +575,9 @@
        # 终端信息采集
        # UserProductInfo填写终端名称
        login_req.UserProductInfo = 'jiabei'
        login_req.DynamicPassword = 'rxoB195F'
        # 按照监管要求填写终端信息
        login_req.TerminalInfo = 'PC;IIP=123.112.154.118;IPORT=50361;LIP=192.168.118.107;MAC=54EE750B1713FCF8AE5CBD58;HD=TF655AY91GHRVL'
        # 以下内外网IP地址若不填则柜台系统自动采集,若填写则以终端填值为准报送
@@ -511,11 +618,11 @@
    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)
            logger_trade.info('inner_ip_address[%s]' % pConnectionInfoField.InnerIPAddress)
            logger_trade.info('inner_port[%d]' % pConnectionInfoField.InnerPort)
            logger_trade.info('outer_ip_address[%s]' % pConnectionInfoField.OuterIPAddress)
            logger_trade.info('outer_port[%d]' % pConnectionInfoField.OuterPort)
            logger_trade.info('mac_address[%s]' % pConnectionInfoField.MacAddress)
            # 请求登录
            login_req = traderapi.CTORATstpReqUserLoginField()
@@ -549,8 +656,10 @@
            # 终端信息采集
            # UserProductInfo填写终端名称
            login_req.UserProductInfo = 'jiabei'
            login_req.DynamicPassword = 'rxoB195F'
            # 按照监管要求填写终端信息
            login_req.TerminalInfo = f'PC;IIP=NA;IPORT=NA;LIP={LOCAL_IP};MAC=5C6F69CC2B40;HD=004bc76004aff0882b9052ba0eb00506;@jiabei'
            # a0:36:9f:ea:fb:bc
            login_req.TerminalInfo = f'PC;IIP=NA;IPORT=NA;LIP={LOCAL_IP};MAC=A0369FEAFBBC;HD=00e3aeeed512b6782d0043b96480e04e;@jiabei'
            # 以下内外网IP地址若不填则柜台系统自动采集,若填写则以终端填值为准报送
            # login_req.MacAddress = '5C-87-9C-96-F3-E3'
            # login_req.InnerIPAddress = '10.0.1.102'
@@ -559,15 +668,15 @@
            TradeSimpleApi.req_id += 1
            ret = api.ReqUserLogin(login_req, TradeSimpleApi.req_id)
            if ret != 0:
                logger.info('ReqUserLogin fail, ret[%d]' % ret)
                logger_trade.info('ReqUserLogin fail, ret[%d]' % ret)
        else:
            logger.info('GetConnectionInfo fail, [%d] [%d] [%s]!!!' % (
            logger_trade.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('Login success! [%d]' % nRequestID)
            logger_trade.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)
@@ -591,8 +700,8 @@
                TradeSimpleApi().get_money_account()
        else:
            logger.info('Login fail!!! [%d] [%d] [%s]'
                        % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
            logger_trade.info('Login fail!!! [%d] [%d] [%s]'
                              % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
    def OnRspUserPasswordUpdate(self, pUserPasswordUpdateField: "CTORATstpUserPasswordUpdateField",
                                pRspInfoField: "CTORATstpRspInfoField", nRequestID: "int") -> "void":
@@ -645,7 +754,7 @@
                            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]'
                async_log_util.info(logger_local_huaxin_trade_debug, 'OnErrRtnOrderAction: Error! [%s] [%s] [%s] [%s]'
                                    % (nRequestID, pInputOrderActionField.OrderSysID,
                                       pRspInfoField.ErrorID,
                                       pRspInfoField.ErrorMsg))
@@ -820,7 +929,8 @@
                     "turnover": pOrderField.Turnover, "orderRef": pOrderField.OrderRef,
                     "volume": pOrderField.VolumeTotalOriginal,
                     "volumeTraded": pOrderField.VolumeTraded, "orderStatus": pOrderField.OrderStatus,
                     "orderSubmitStatus": pOrderField.OrderSubmitStatus, "statusMsg": pOrderField.StatusMsg})
                     "orderSubmitStatus": pOrderField.OrderSubmitStatus, "statusMsg": pOrderField.StatusMsg,"sinfo": pOrderField.SInfo
                     })
            else:
                # logger.info('查询报单结束[%d] ErrorID[%d] ErrorMsg[%s]'
                #             % (nRequestID, pRspInfoField.ErrorID, pRspInfoField.ErrorMsg))
@@ -907,26 +1017,45 @@
            # 1-买 2-卖
            direction = data["direction"]
            code = data["code"]
            volume = data["volume"]
            price = data["price"]
            sinfo = data["sinfo"]
            # 老版本下单
            volume = data.get("volume")
            price = data.get("price")
            order_ref = data.get("order_ref")
            shadow_price = data.get("shadow_price")
            blocking = data.get("blocking")
            shadow_volume = data.get("shadow_volume")
            cancel_shadow = data.get("cancel_shadow")
            # 新版下单
            order_info_list = data.get("order_info_list")
            blocking = data.get("blocking")
            if cancel_shadow is None:
                cancel_shadow = True
            if shadow_volume is None:
                shadow_volume = constant.SHADOW_ORDER_VOLUME
            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)
                        if not order_info_list:
                            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=shadow_price, cancel_shadow=cancel_shadow)
                    if not order_info_list:
                        self.trade_thread_pool.submit(self.__tradeSimpleApi.buy, code, volume, price, sinfo, order_ref,
                                                      shadow_price, cancel_shadow, shadow_volume)
                    else:
                        if order_info_list:
                            for i in range(len(order_info_list)):
                                order_info = order_info_list[i]
                                order_info_list[i] = (order_info[0], order_info[1], order_info[2], f"{sinfo}_{i}")
                        if blocking:
                            for x in order_info_list:
                                req_rid_dict[x[3]] = (client_id, request_id, sk, x[2])
                        self.trade_thread_pool.submit(self.__tradeSimpleApi.buy_new, code, order_info_list)
                    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,
@@ -954,18 +1083,34 @@
            orderSysID = data.get("orderSysID")
            orderRef = data.get("orderRef")
            orderActionRef = data.get("orderActionRef")
            sinfo = data["sinfo"]
            sinfo = data.get("sinfo")
            # =====批量撤单采用此种方法======
            # [(orderRef, orderSysID)]
            orderInfos = data.get("orderInfos")
            orderActionRefs = data.get("orderActionRefs")
            sinfos = data.get("sinfos")
            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}")
                    if orderInfos:
                        # 批量撤买
                        if len(orderInfos) != len(orderActionRefs) or len(orderInfos) != len(sinfos):
                            raise Exception("批量撤单:订单数量与orderActionRefs/sinfos数量不匹配")
                        req_rid_dict[sinfo] = (client_id, request_id, sk)
                        self.trade_thread_pool.submit(
                            lambda: self.__tradeSimpleApi.batch_cancel_buy(code, orderInfos, sinfos, orderActionRefs))
                        async_log_util.info(logger_local_huaxin_trade_debug,
                                            f"批量撤单结束:code-{code} order_infos-{orderInfos} sinfos-{sinfos} order_action_refs-{orderActionRefs}")
                    else:
                        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)
@@ -1111,6 +1256,7 @@
        key = data["sinfo"]
    try:
        if req_rid_dict and key in req_rid_dict:
            # TODO 处理批量下单
            temp_params = req_rid_dict.pop(key)
            client_id, request_id = temp_params[0], temp_params[1]
            # 本地订单号-系统订单号映射
@@ -1196,5 +1342,4 @@
    # while True:
    #     time.sleep(1)
    run()
    input()