刚接触CTP开发, 和simnow的仿真账户,用的是python开发。
有时会出现simnow的前置机中途宕机的情况,然后程序就崩溃了。一直在找更换前置机重连的方法,现在找到了,就是在一开始init()之前,同时注册很多个前置机,这样,init()之后,ctp会自己找一个最快的,宕机重连后,ctp会在我们之前注册的一系列前置机中逐个尝试重连。
以下是博主自己再封装的ctp类的登录方法,因为登录后起始要确认结算单,才可以下单,所以我也封在里面了。其中那段循环就是同时注册很多个前置机。
2019/3/15修改后的登录方法。
登录
def Login(self):
i = 0
stat = 0
self.reqid = 0
# 在C++环境中创建MdApi对象,传入参数是希望用来保存.con文件的地址,测试通过
self.api.createFtdcTraderApi("")
# 设置数据流重传方式,测试通过,私有通讯方式
self.api.subscribePrivateTopic(2)
self.api.subscribePublicTopic(2)
# 注册前置机地址,测试通过
for i in range(len(self.registFront)):
self.api.registerFront(self.registFront[i])
# 初始化api,连接前置机,测试通过
self.api.init()
sleep(1)
loginReq = {} # 创建一个空字典
loginReq['UserID'] = self.userid
loginReq['Password'] = self.password
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:
self.SettlementInfoConfirm()
stat = 1
print('stat: '+ str(stat))
return stat
2018/11/9修改:
经过博主一番摸索后:
断连后再登录新的前置机后,一定要重新登录才可以进行正常的下单,查询等业务操作。
但是重新登录的方法不能写在回调函数里面!
从ctp官方文档中可以知道,当因为以下原因,导致前置机断连后,CTP会每隔几秒就自己重连,服务端不用做任何重连的处理。
但是根据博主自己的亲身经验,CTP自动重连会其实是在一个线程A里,这个线程会由CTP自动生成并启动,而博主自己的策略运行其实是在线程B里。因此重新登录的方法如果写在了A线程里,只会引起程序出现系统错误,进而程序崩溃。而且所有的业务操作都在B线程里进行,而B线程实际上并没有登录,因此就算A登录成功,B也做不了什么。
下面送上我自己摸索出来的新方法。
也就是说,在回调函数onFrontConnected里面加入断连后重新登录的判断,是否断连可以从onFrontDistconnected函数里面返回的n判断。
以下是博主自己修改后回调函数里写的方法,大家可以拿来参考:
onFrontDisconnected回调函数
不管是程序手动登出还是CTP自己断连,都会调用onFrontDisconnected函数。博主用self.logoutCode和self.onFrontDisconnectedCode判断是手动登出还是自动断连,如果是手动登出,self.logoutCode会变成1,这样CTP返回的n,就会被重新赋值为0.
def onFrontDisconnected(self,n):
# n的type是int
print(n)
# 手动登出
if self.logoutCode == 1:
self.onFrontDisconnectedCode = 0
else:
self.onFrontDisconnectedCode = n
这样就可以为后面是否需要重新登录做依据。
2019/3/15修改:——————————————————————————————
其中的self.logoutCode 是我自己封装的logout函数中赋值的,为了在登出后不会自动重连前置机,必须要在self.api.reqUserLogout方法后加入:
self.api.release()
def logOut(self,userID,borkerID):
logoutReq = {}
logoutReq['BrokerID'] = borkerID
logoutReq["UserID"] = userID
self.reqid = self.reqid + 1
# 传递登出状态
self.api.logoutCode = 1
#
self.api.reqUserLogout(logoutReq, self.reqid)
sleep(1)
# 用来释放本次接口,防止重连前置机
self.api.release()
为什么要这么做?
因为CTP的前置机到了16:30之后18:30之前就开始陆续进入维护状态,如果这个时候程序还连着前置机,就会频繁出现4097,8193断连错误,然后程序不停的接收到CTP的断连重连请求,不需要10分钟,程序就会崩溃。因此,非交易时间尤其是收盘后,必须彻底登出CTP,一般来说19:00之后再登录就没问题了。
正确的登出返回状态:
————————————————————————————————————————————————————
2. onFrontConnected回调函数
def onFrontConnected(self):
“”“服务器连接”""
print("onFrontDisconnectedCode: " + str(self.onFrontDisconnectedCode))
博主修改过后,在onFrontConnected里面除了打印self.onFrontDisconnectedCode,其它啥也不做。
回调函数都在TestTdApi(TdApi)这个类里面,我自己的方法都在另一个类XXX里面。
接下来,是我自己XXX里重连的方法:
3. 断连状态检查方法(2018/3/15日已修改)
2019/3/15修改:——————————————————————————————
这个方法里只可以写检查,不可以写重新登录的方法。
# 断连状态检查:给所有的业务类方法前加上去
def reloginCheck(self):
stat = 0
# 查看前置机是不是自己断连过
if self.api.onFrontDisconnectedCode != 0:
stat = 0
# 前置机没有断连过
else:
stat = 1
print('relogstat: '+str(stat))
return stat
以上方法的原理:
在自己的XXX类里实例化TestTdApi类,实例的名字之为api,然后查看onFrontDisconnectedCode属性是不是0,如果不是0,肯定是因为CTP断连。
把这个方法放在需要登录的业务方法前面。尤其是,如果stat是0,那就不进行业务操作,尤其是下单,因为博主的下单时如果没因为博主设定特点条件而导致下单不成功,就会一直循环下单直到成功为止。所以在前面加一个断连判断对博主尤为重要。
举一个插这个方法的例子:
在策略运行的线程B中:
# 判断simnow是否开启
if simnowStat == 1:
# optSuccess 是下单操作成功嘛
while optSuccess == 0:
# 先判断前置机是否断连
frontStat = sarProjectTest.Api.reloginCheck()
self.apptools.logGenerate('frontStat : ' + str(frontStat))
# 不等于1代表断了,此时CTP会自动重连前置机,但是我们需要在业务逻辑里重新登录
# 我用的是sarProjectTest类下的Api属性进行ctp下单操作的,所以这里也用这个属性进行重登
if frontStat != 1:
# 返回登录是否成功
logSuccessStat = sarProjectTest.Api.Login()
else:
logSuccessStat = 1
# 不等于0就代表重登成功了,就可以下单
if logSuccessStat != 0:
sarProjectTest.Api.BuyOpen(self.id, self.num)
orderResult = self.ordertools.orderInsertJudge(sarProjectTest.Api.api,self.num,1)
optSuccess = orderResult[‘optSuccess’]
# 下单失败,while会继续循环
if optSuccess == 0:
print(‘下单失败’)
self.apptools.logGenerate(‘买开下单失败’)
longing = lastLonging
# 如果重登还失败,就不下单了
else:
# 重登失败
print(‘下单simnow重登失败’)
self.apptools.logGenerate(‘下单simnow重登失败’)
sarProjectTest.ctpLogStat = 0
break
frontStat就是断连检查的结果,如果等于1才进行下单操作。
重登的行为一定要发生在主程序也就是线程B中。你之前用哪个类登录下单的,在线程B重登时,用哪个类进行Login操作。
参考文献
多个前置机注册。
我是从这个帖子里获得启发的:
http://www.oceantribe.org/xf/index.php?threads/28431/
8193错误也就是心跳超时错误导致的程序崩溃,已经解决。稍后再另一片博文里面https://blog.csdn.net/mooncrystal123/article/details/83894659
vn.py
《Quicklib程序化交易框架www.quicklib.cn》
http://www.mdshare.cn/comm/topic/2750/
《期货跟单软件视频教学4集》
《开户中国期货低佣金开户》
《mdshare财经数据接口包》
《某python量化交易框架性能评测》
《QuicklibTrade A股行情接口,Level2接口》
python量化交易
《优秀量化资源导航》
《TradeApi A股程序化交易接口》
《酷操盘手期货跟单软件》