python CTP 8193 心跳超时程序崩溃解决方法——8193错误

终于,终于,终于!!!!

博主千辛万苦把CTP 8193心跳超时导致程序崩溃的问题解决了!!!!!经历了一天一夜的测试后,第二天亲眼看到CTP返回了8193错误后,我的程序没有崩溃!第二天早上9:00一到,又自己正常运行了。敲开心~~~

博主的血泪史,就是搜遍国内外全网,不止博主一个人遇到8193后程序崩溃,但只有提问8193错误的,没有一个回答8193错误的解决方法。所以博主就决定了,一旦等到博主解决8193错误那一日,就一定要把解决方法公诸于世!

先说一说博主的8193错误导致崩溃的原因。

众所周知,8193错误就是CTP当中的,一旦引发,ctp就会调用回调函数“onFrontDisconnected”,返回错误码(目前博主见得最多的就是4097和8193)。

这个时候根据上期所官方CTP_API接口原文,API会自动重新连接,客户端理论上可以不做任何处理。

python CTP 8193 心跳超时程序崩溃解决方法——8193错误_第1张图片

但是!!博主要说,这边所谓的不做处理,是指不用做任何重新连接前置机的处理。

CTP虽然会帮我们自动重连前置机(前提是,你之前注册的前置列表里的前置机可以连接得上,注册多个前置机方法详见博主的另外一篇文章:https://blog.csdn.net/mooncrystal123/article/details/83269296),但是CTP不会帮我们重新登录!

然而,CTP的业务API必须要登陆后才能使用!登录后才能使用!!登陆后才能使用!!!

重要的事情说三遍。

业务API就是下单啊,查询账户余额啊,查询市场最新tick行情啊等等等…………

所以博主的崩溃来自于两个方面

1. 前期

只注册了一个前置机,前置机坏掉后,无法重连,导致下单查询的业务线程无法获得必须的返回结果属性,导致后续业务逻辑报attributeError失败,崩溃。

2. 后期

注册了一堆前置机,但是重连后没登录,导致业务逻辑线程进入死循环,线程无法自动停止。

注册了一堆前置机,但是登陆方法写在了CTP的回调函数当中,然后登陆失败,导致程序崩溃。

 

所以,综上所述,博主经过多番实验找到了办法。

首先,最重要的就是在回调函数中不加任何重新登录的方法,只负责打印或者记录CTP断连后onFrontDisconnected返回的错误码,然后等待CTP自动重连,重连成功后CTP会调用onFrontConnected。

注:self.logoutCode是博主自设的用来鉴别是手动登出时导致的断连还是CTP自身原因导致的断连。

    def onFrontDisconnected(self,n):
        # n的type是int
        print(n)
        # 手动登出
        if self.logoutCode == 1:
            self.onFrontDisconnectedCode = 0
        else:
            self.onFrontDisconnectedCode = n
    def onFrontConnected(self):
        """服务器连接"""
        print("onFrontDisconnectedCode: " + str(self.onFrontDisconnectedCode))

CTP调用回调函数的时候,会自动开启一个新线程来处理,我们称之为A线程。

然后,我们要做的就是把重新登录的方法写在我们程序运行的线程里面,默认就是B线程。

下面就是我自己封装的CTP类的断连检查+重新登录的方法。

注意!!以下方法已经在2019-5-17日进行修改了!!

博主对不起大家啊~~4月的时候做测试,查出来重登的方法有点问题,然后我自己程序修改了,但是一直没有修改博文,今天发现看的人真的挺多的,心想不能再继续放着旧代码不管了。大家按照新的方法来,保证行。

2019-5-17 正确版本:————————————

def reloginCheck(self):
    # 查看前置机是不是自己断连过
    if self.api.onFrontDisconnectedCode != 0:
       stat = 0
       '''在这里登录用self.api.reqUserLogin没用的……要在业务线程里用业务线程里对应的用来下单的那个类去登录'''
        # 前置机没有断连过
    else:
        stat = 1
    print('relogstat: '+str(stat))
    return stat

——————————————————————————

以下是错误版本!!注意!!

 错误版本—————————————————————————割割巫————————————————————

# 断连状态检查:给所有的业务类方法前加上去
    def reloginCheck(self):
        stat = 0
        # 查看前置机是不是自己断连过
        if self.api.onFrontDisconnectedCode != 0:
            loginReq = {}  # 创建一个空字典
            loginReq['UserID'] = self.userid
            loginReq['Password'] = self.password
            # loginReq['BrokerID'] = '9999'
            loginReq['BrokerID'] = self.brokerid
            self.reqid = self.reqid + 1  # 请求数必须保持唯一性
            self.api.reqUserLogin(loginReq, self.reqid)
            sleep(1)
            if hasattr(self.api,'loginErrorID'):
                if self.api.loginErrorID == 0:
                    stat = 1
        # 前置机没有断连过
        else:
            stat = 1
        print('relogstat: '+str(stat))
        return stat

 错误版本—————————————————————————割割巫————————————————————

正确版和错误版的区别讲解:

在错误版中,博主用self.api.reqUserLogin进行重登,这个举动是没有任何意义的!!

还是犯了我前文说的“没有在业务线程B中进行登录”的错误!!

为什么没有意义??因为你在线程B中进行下单操作的那个类的名字不叫“self.api”,而是叫做(以博主自己的例子来说):sarProjectTest.Api !!!!!

真正有意义的重登就是回到业务线程B中,先用正确版的reloginCheck在进行业务环节前检查一下,返回stat值,如果stat = 0 ,就说明被断连了,这时,跳过业务逻辑,用sarProjectTest.Api.Login()进行重新登录!!然后再回上去重新进行业务逻辑(如果你使用的是While),或者等待下一轮循环(如果你使用的是For)。

具体代码如下:

 

这个方法在业务逻辑中插入的位置:

2019-5-17 修改:正确版———————以买开为例———————

看到当中的“frontStat”的检查了吗?? 和frontStat 不等于1 时进行重登的方法了吗?用的是之前被前置机踢下线的那个具体的类:sarProjectTest.Api

登录方法Login()就是之前自己封装过的login,反正就是你之前咋登陆的,这里你就咋登陆。

    # single买开
    def singleBuyOpen(self):
        optSuccess = 0
        optcode = 0
        while optSuccess == 0:
           frontStat = sarProjectTest.Api.reloginCheck()
           # 检查CTP是否断连
           if frontStat != 1:
               logSuccessStat = sarProjectTest.Api.Login()
           else:
               logSuccessStat = 1
           if logSuccessStat != 0:
              sarProjectTest.Api.BuyOpen(self.id, self.num)
              orderResult = self.ordertools.orderInsertJudge(sarProjectTest.Api.api, self.num, 1)
              optSuccess = orderResult['optSuccess']
            if optSuccess == 0:
               print('下单失败')
               self.apptools.logGenerate('买开下单失败')
               longing = lastLonging
            else:
               # 重登失败
               print('下单simnow重登失败')
               self.apptools.logGenerate('下单simnow重登失败')
               sarProjectTest.ctpLogStat = 0
               break


        if optSuccess == 1:
           self.postCat = "买入开仓"
           optcode = 1

        result = {'optcode':optcode,'optSuccess':optSuccess}
        return result

——以上是正确版——————————————————————————————————

 

错误版本——————————割割巫——————————————————


# 判断simnow是否开启
if simnowStat == 1:
    frontStat = sarProjectTest.Api.reloginCheck()
    # 检查CTP是否断连
    if frontStat == 1:
        while optSuccess == 0:
            sarProjectTest.Api.BuyOpen(self.id, num)
            orderResult = self.ordertools.orderInsertJudge(sarProjectTest.Api.api,num,1)
            optSuccess = orderResult['optSuccess']
            if optSuccess == 0:
                print('下单失败')
                self.apptools.logGenerate('买开下单失败')
# 未开启simnow
    else:
        optSuccess = 1
--------------------- 
作者:mooncrystal123 
来源:CSDN 
原文:https://blog.csdn.net/mooncrystal123/article/details/83269296 
版权声明:本文为博主原创文章,转载请附上博文链接!

错误版本——————————割割巫——————————————————

通过这样的处理,即使CTP报8193或者4097,我们的程序也不会崩溃,可以等待CTP重连。

一般来说,CTP前置机最不稳定的时间为傍晚4:30-7:00,这段时间是各方前置机维护关机时段,如果程序要过夜的话,这段时间不能因为CTP前置机不稳定而宕机是最关键的。

还要提醒一句,大家过夜的时候要登出CTP,正确登出CTP的方法在使用logOut之后,要release你的Api。

具体代码看我这篇文章,关键词搜索:“self.api.release()”

https://blog.csdn.net/mooncrystal123/article/details/83269296

好啦,今日份的分享完毕~~

你可能感兴趣的:(Python,CTP)