python CTP 更换前置机重连

修改历史:

1. 2018/11/9

2. 2019/3/15

刚接触CTP开发,博主用的是VNPY的接口和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判断。

以下是博主自己修改后回调函数里写的方法,大家可以拿来参考:

1. 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操作。

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

参考文献

1. 多个前置机注册。

我是从这个帖子里获得启发的:

http://www.oceantribe.org/xf/index.php?threads/28431/

8193错误也就是心跳超时错误导致的程序崩溃,已经解决。稍后再另一片博文里面https://blog.youkuaiyun.com/mooncrystal123/article/details/83894659

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mooncrystal123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值