CTP-API开发系列之八:报撤单代码实现

CTP-API开发系列之八:报撤单代码实现

前情回顾

在前面的章节,分享了交易API认证登录以及基础数据查询的代码流程(CTP-API开发系列之六:交易登录及查询流程),并介绍了报撤单及回报顺序(CTP-API开发系列之七:报撤单及回报顺序),这节继续分享报单、撤单相关的代码实现。

函数实现

在之前代码基础上,新增以下类成员变量:

# 登录成功,从响应结构体中获取,用于报单跟踪
FrontID = 0
SessionID = 0
# 报单引用编号:递增,使用时需转成字符串
MyOrderRefInt = 1000

# key: str(FrontID) + "_" + str(SessionID) + "_" + OrderRef
# value: CThostFtdcInputOrderField
MyOrders = {}

缓存FrontID 和 SessionID

def OnRspUserLogin(self, pRspUserLogin: 'CThostFtdcRspUserLoginField', pRspInfo: 'CThostFtdcRspInfoField', nRequestID: 'int', bIsLast: 'bool') -> "void":
	log.info("OnRspUserLogin: " + api_struct_serialize(pRspUserLogin))

	if not pRspInfo.ErrorID:
		# 登录成功,从响应结构体中获取,用于报单跟踪
		self.FrontID = pRspUserLogin.FrontID
		self.SessionID = pRspUserLogin.SessionID

报单函数实现

### 具体枚举值见CTP-API头文件ThostFtdcUserApiDataType.h
def ReqorderfieldInsert(self, InstrumentID, EXCHANGEID, DIRECTION, OFFSET, PRICE, VOLUME):
	"""报单操作"""
	orderfield = api.CThostFtdcInputOrderField()
	orderfield.BrokerID = BROKERID
	orderfield.UserID = USERID
	orderfield.InvestorID = USERID

	orderfield.OrderRef = self.getOrderRef()  # 报单编号:从1000递增,使用时需转成字符串
	orderfield.ExchangeID = EXCHANGEID
	orderfield.InstrumentID = InstrumentID
	orderfield.Direction = DIRECTION
	orderfield.CombOffsetFlag = OFFSET
	orderfield.LimitPrice = PRICE
	orderfield.OrderPriceType = api.THOST_FTDC_OPT_LimitPrice # 限价
	orderfield.VolumeTotalOriginal = VOLUME

	orderfield.ContingentCondition = api.THOST_FTDC_CC_Immediately
	orderfield.TimeCondition = api.THOST_FTDC_TC_GFD
	orderfield.VolumeCondition = api.THOST_FTDC_VC_AV
	orderfield.CombHedgeFlag = api.THOST_FTDC_HF_Speculation
	orderfield.ForceCloseReason = api.THOST_FTDC_FCC_NotForceClose
	orderfield.IsAutoSuspend = 0

	# 本地报单记录
	key = f"{self.FrontID}_{self.SessionID}_{orderfield.OrderRef}"
	self.MyOrders[key] = orderfield

	self.tapi.ReqOrderInsert(orderfield, 0)
	log.info("send ReqOrderInsert key: " + key)
	log.info("send ReqOrderInsert: " + api_struct_serialize(orderfield))

撤单函数实现

撤单有两种方式,官方文档推荐使用第一种

  1. 第一种方法,使用OrderSysID撤单
  2. 第二种方法,使用FrontID+SessionID+OrderRef撤单
def ReqorderfieldAction(self,OrderSysID,ExchangeID,InstrumentID):
	"""撤单操作(第一种方式OrderSysID)"""
	orderfield = api.CThostFtdcInputOrderActionField()
	orderfield.BrokerID = BROKERID
	orderfield.UserID = USERID
	orderfield.InvestorID = USERID

	orderfield.OrderSysID = OrderSysID
	orderfield.ExchangeID = ExchangeID
	orderfield.InstrumentID = InstrumentID
	orderfield.ActionFlag = api.THOST_FTDC_AF_Delete
	self.tapi.ReqOrderAction(orderfield, 0)
	log.info('send ReqOrderAction: ' +  api_struct_serialize(orderfield))

调用示例

报单(形成挂单)

在 OnRspQryInstrument 合约查询回调函数中,合约查询完成后调用(bIsLast==True)

# rb2405 合约:买入开仓 3手 委托价格3600(低于最新价形成挂单,用于撤单)
self.ReqorderfieldInsert("rb2405", "SHFE", api.THOST_FTDC_DEN_Buy, api.THOST_FTDC_OF_Open, 3600, 3)

对挂单进行撤单

在 OnRtnOrder 报单回报函数中,对上述产生的挂单-“未成交”状态的单子进行撤单操作

def OnRtnOrder(self, pOrder: 'CThostFtdcOrderField') -> "void":
	log.info("OnRtnOrder: " + api_struct_serialize(pOrder))

	key = f"{pOrder.FrontID}_{pOrder.SessionID}_{pOrder.OrderRef}".replace(' ', '')
	# 本地报单回推流
	if key in self.MyOrders.keys():
		log.info(f'local order: {key}')
		log.info(f' InstrumentID:{pOrder.InstrumentID},Direction:{pOrder.Direction},CombOffsetFlag:{pOrder.CombOffsetFlag},OrderSubmitStatus:{pOrder.OrderSubmitStatus},VolumeTraded:{pOrder.VolumeTraded},VolumeTotal:{pOrder.VolumeTotal},LimitPrice:{pOrder.LimitPrice},OrderStatus:{pOrder.OrderStatus},StatusMsg:{pOrder.StatusMsg}')

		## 如果未成交,5秒后主动撤单
		if pOrder.OrderStatus is api.THOST_FTDC_OST_NoTradeQueueing:
			thread = threading.Thread(target=self.ReqorderfieldAction, args=(pOrder.OrderSysID,pOrder.ExchangeID,pOrder.InstrumentID))
			thread.start()
			thread.join()

		## 如果已撤单,修改价格重新报单
		if pOrder.OrderStatus is api.THOST_FTDC_OST_Canceled:
			# rb2405 买入开仓 3手 委托价格3630(高于最新价立即成交,从OnRtnTrade可以看到最终成交价格为3628
			self.ReqorderfieldInsert("rb2405", "SHFE", api.THOST_FTDC_DEN_Buy, api.THOST_FTDC_OF_Open, 3630, 3)
	# 外部报单回推流
	else:
		log.info('outside order: ' + key)
		log.info(f' InstrumentID:{pOrder.InstrumentID},Direction:{pOrder.Direction},CombOffsetFlag:{pOrder.CombOffsetFlag},OrderSubmitStatus:{pOrder.OrderSubmitStatus},VolumeTraded:{pOrder.VolumeTraded},VolumeTotal:{pOrder.VolumeTotal},LimitPrice:{pOrder.LimitPrice},OrderStatus:{pOrder.OrderStatus},StatusMsg:{pOrder.StatusMsg}')

报单(立即成交)

同样还是在 OnRtnOrder 报单回报函数中,对上述“已撤单”成功后,进行新的报单操作。

以下是simnow快期3软件上,这两笔委托单的截图,方便对照验证:

注意事项

第二笔报单价格3630,最终撮合成交的价格是3628。最终成交价格在OnRtnOrder 是没有的,需要在 OnRtnTrade 成交回报推送函数中获得

def OnRtnTrade(self, pTrade: 'CThostFtdcTradeField') -> "void":
	log.info("OnRtnTrade: " + api_struct_serialize(pTrade))

委托、成交映射关系

1.报单前唯一标识维护:FrontID+SessionID+OrderRef,并进行缓存
# 本地报单记录
key = f"{self.FrontID}_{self.SessionID}_{orderfield.OrderRef}"
self.MyOrders[key] = orderfield
如下日志截图中两笔单子的唯一标识分别是:“1_-172308391_1000”和“1_-172308391_1001”

2.在OnRtnOrder函数中可以找到这三个字段:FrontID+SessionID+OrderRef
同时会有 ExchangeID、OrderSysID 等字段(当报单被交易所拒绝后,交易所不会分配OrderSysID,此时仍要使用第1组序号跟踪报单)

3.在OnRtnOrder函数中没有第1组序号,需要使用2中的 ExchangeID、OrderSysID,跟踪报单最终的成交价(Price)

分笔成交情况

在simnow环境中不会产生分笔成交的情况,在这里我从实盘环境中,做了一个分笔成交的情况,重点需要关注OnRtnOrder函数中的OrderStatus、VolumeTraded、VolumeTotal字段的变化情况,以及OnRtnTrade函数中Volume字段的变化情况。

以下截图报了一笔10手的单子,OnRtnOrder函数回调了7次;OnRtnTrade 函数回调了4次:
在这里插入图片描述

/// 具体枚举值见CTP-API头文件ThostFtdcUserApiDataType.h
/
///TFtdcOrderStatusType是一个报单状态类型
/
///全部成交
#define THOST_FTDC_OST_AllTraded '0'
///部分成交还在队列中
#define THOST_FTDC_OST_PartTradedQueueing '1'
///部分成交不在队列中
#define THOST_FTDC_OST_PartTradedNotQueueing '2'
///未成交还在队列中
#define THOST_FTDC_OST_NoTradeQueueing '3'
///未成交不在队列中
#define THOST_FTDC_OST_NoTradeNotQueueing '4'
///撤单
#define THOST_FTDC_OST_Canceled '5'
///未知
#define THOST_FTDC_OST_Unknown 'a'
///尚未触发
#define THOST_FTDC_OST_NotTouched 'b'
///已触发
#define THOST_FTDC_OST_Touched 'c'
typedef char TThostFtdcOrderStatusType;

/
///TFtdcOrderSubmitStatusType是一个报单提交状态类型
/
///已经提交
#define THOST_FTDC_OSS_InsertSubmitted '0'
///撤单已经提交
#define THOST_FTDC_OSS_CancelSubmitted '1'
///修改已经提交
#define THOST_FTDC_OSS_ModifySubmitted '2'
///已经接受
#define THOST_FTDC_OSS_Accepted '3'
///报单已经被拒绝
#define THOST_FTDC_OSS_InsertRejected '4'
///撤单已经被拒绝
#define THOST_FTDC_OSS_CancelRejected '5'
///改单已经被拒绝
#define THOST_FTDC_OSS_ModifyRejected '6'
typedef char TThostFtdcOrderSubmitStatusType;

日志截图

在这里插入图片描述

下节预告

交易相关常用的功能基本已经实现完成,上述报撤单对期权也是支持的,只需要传递不同的期权合约参数。后面会分享行权和自对冲相关业务的代码。

相比交易API的功能,行情API的功能就要简单的多了,后面在分享行情API代码的基础上,会与交易API的demo进行结合,实现一个简易的自动化交易程序(当行情满足一定条件自动进行报单,并按照设定的条件自动进行止盈止损)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值