终于,终于,终于!!!!
博主千辛万苦把CTP 8193心跳超时导致程序崩溃的问题解决了!!!!!经历了一天一夜的测试后,第二天亲眼看到CTP返回了8193错误后,我的程序没有崩溃!第二天早上9:00一到,又自己正常运行了。敲开心~~~
博主的血泪史,就是搜遍国内外全网,不止博主一个人遇到8193后程序崩溃,但只有提问8193错误的,没有一个回答8193错误的解决方法。所以博主就决定了,一旦等到博主解决8193错误那一日,就一定要把解决方法公诸于世!
先说一说博主的8193错误导致崩溃的原因。
众所周知,8193错误就是CTP当中的
,一旦引发,ctp就会调用回调函数“onFrontDisconnected”,返回错误码(目前博主见得最多的就是4097和8193)。
这个时候根据上期所官方CTP_API接口原文,API会自动重新连接,客户端理论上可以不做任何处理。
但是!!博主要说,这边所谓的不做处理,是指不用做任何重新连接前置机的处理。
CTP虽然会帮我们自动重连前置机(前提是,你之前注册的前置列表里的前置机可以连接得上,注册多个前置机方法详见博主的另外一篇文章:),但是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
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!
错误版本——————————割割巫——————————————————
通过这样的处理,即使CTP报8193或者4097,我们的程序也不会崩溃,可以等待CTP重连。
一般来说,CTP前置机最不稳定的时间为傍晚4:30-7:00,这段时间是各方前置机维护关机时段,如果程序要过夜的话,这段时间不能因为CTP前置机不稳定而宕机是最关键的。
还要提醒一句,大家过夜的时候要登出CTP,正确登出CTP的方法在使用logOut之后,要release你的Api。
具体代码看我这篇文章,关键词搜索:“self.api.release()”