赞
踩
CTP行情API推送的行情快照中,有TradingDay和ActionDay两个表示日期的字段,分别表示交易日和实际日期。因为夜盘的交易时段是属于下一天白天的日盘的交易日的,因此如果是2024年4月12日(星期五)晚上的夜盘,则它属于4月15日(下个星期一)的交易日。而实际上夜盘中不同交易所的设置规则不尽相同,具体值如下表所示。
TradingDay | ActionDay | |
上期所/能源中心(SHFE/INE) | 20240415下周一 | 20240412周五 |
大商所/广期所(DCE/GFEX) | 20240415下周一 | 20240415下周一 |
郑商所(CZCE) | 20240412周五 | 20240412周五 |
可以看出,只有上期所/能源中心的两种日期,是与真实值相一致的。开发时可取用它们的合约的行情快照中的日期作为交易日和实际日期。另外,SimNow环境里所有交易所都是使用和上期所相同的规则。
另一个取巧一些的办法是,取用程序所在本地机器的系统日期作为实际日期,而由于夜盘是晚上9点开盘,因此交易日取用"当前时间 + 4小时"的日期(需特殊处理周五-周一的情况),这个交易日的取值对于日盘也是有效的。
如果使用交易API,则可以从交易API的登录响应数据中获取交易日,虽然行情API中的登录响应中也有交易日,但它是空值。交易API在有账户登录成功以后,也可以使用静态函数 GetTradingDay() 获取交易日。
交易API中的一些接口中,例如OnRtnTrade成交通知,OnRtnOrder报单通知等,也有TradingDay字段,它们都是准确的。
交易API的OnRtnTrade成交通知中,有成交日期TradeDate字段,它本意是表示成交的实际日期,但和上面的ActionDay一样,TradeDate各个交易所的设置规则不一样,可使用上述相同的办法来处理成交实际日期。
订单有三种key,都可以用来撤单,这里直接就说是哪三种key。
第一种方法,使用 ExchangeID + OrderSysID 撤单(推荐使用哦)
CTP未来版本的API(7.0版本,还未发布),在报单和撤单时,强制需要填写ExchangeID交易所代码,因此这种撤单方法暗合未来的CTP需求,实际只需要程序保存维护OrderSysID这一个字段。
OrderSysID报单编号是一个字符串,内容是数字,例如" 012865",即可能是右对齐的形式。生产环境里,不同的交易所的规则稍有不同,例如有的交易所是没有右对齐的,而且也不一定都是只有6位数字。不建议对它的空格部分进行截断处理,完整的保存使用才是最好的。同时,CTP报单的第一个OnRtnOrder回报里面,报单编号是空值,这个是无法拿来撤单的。
第二种方法,使用 FrontID + SessionID + OrderRef 撤单
用这种方法撤单时,实际还需要填写InstrumentID合约代码。
部分交易所在订单被交易所接收之前(即报单还在被CTP柜台处理中还没送到交易所时),不支持这种撤单方式。
FrontID 和 SessionID是报出这个订单的会话的编号信息,在OnRtnOrder报单通知中可以获取到。当以后新版本的CTP发布时,这种撤单方法还需要添加填入ExchangeID字段。
OrderRef报单引用和上面说的OrderSysID报单编号类似,都是"读作字符串,写作整数"。如果需要自行维护OrderRef,则需要保持递增,否则报单会失败,提示"重复的报单"。另外需要指出的是,OrderRef本质是一个有符号32位整数(有测试发现部分期货公司CTP柜台使用的是64位整数,但建议仍按32位整数来处理),是允许有负数的,超出它的范围( -2147483468 到 2147483467 )的数字,可能会被CTP服务器端判定为0从而不符合规则导致报单失败。
第三种方法,使用 ExchangeID + TraderID + OrderLocalID 撤单
TraderID是交易所交易员代码,并不是TradeID成交编号,注意它多出一个"r"字母。
除了上述的三种可用于撤单的key之外,如果是为了将收到的OnRtnOrder报单通知和发出的订单请求对应起来,其实还可以用RequestID字段一键对应。这个整数字段在OnRtnOrder的报单数据中也有返回,返回的值与报单时填写的RequestID值相同。需注意的是,RequestID不是nRequestID(报单请求函数的参数,很多CTP请求函数都有nRequestID参数),别把二者混淆!报单失败时的响应函数OnRspOrderInsert的参数中,nRequestID会出现,值与报单时填写的nRequestID值相同。
RequestID != nRequestID
但是,用RequestID来对应,虽然简单好用,却有一个缺点(同时也是优点?)——CTP允许有重复的RequestID。因此如果程序没有维护好,在下单时使用了重复的RequestID,就会产生重复的key无法将报单通知和请求一一对应了。同时,只用RequestID也无法区分同一个账户的不同会话之间的下单。
由上面的第2点引申出一个问题:成交通知OnRtnTrade中,如何将这笔成交和此前的订单关联起来?
OnRtnTrade中,和订单唯一性有关的数据有OrderSysID,OrderRef,TraderID 和 OrderLocalID,但缺失了FrontID + SessionID,因此用第一种和第三种方法都可以直接将成交和订单关联起来,用它们来保存订单以及撤单是更好的选择。
再插一句,OnRtnTrade成交通知中的Volume成交数量,是这笔成交通知的成交的数量,并不是这个订单的总的成交数量,总成交数量可以去查看报单通知OnRtnOrder中的VolumeTraded字段。举个例子,一个订单的委托数量是4手,分成两次(1手和3手)成交,则第一次OnRtnTrade的Volume是1,第一次OnRtnTrade的Volume是3。
如果是组合合约的订单的成交通知,那么会分成两个单腿合约分别推送OnRtnTrade,即推送至少2次OnRtnTrade。具体可查看此前的介绍组合合约交易的文章:CTP组合合约浅析(下)
如果查询合约手续费率时,返回的响应中的合约代码是品种代码,说明这个合约的手续费率是按品种通用的费率设置的。例如,查询"IF2409"合约的手续费率,而返回的是"IF",说明你的沪深300股指2409合约的手续费率是和沪深300股指品种通用的费率。
与此类似的,如果返回的InvestorID是"000000",则说明这个费率是所有投资者通用的费率,经纪商并没有对你这个账号单独设置费率。
另外需指出,查询得到的手续费率,以及保证金率,都是账户最终的费率,而不是交易所标准的费率。交易所标准的保证金率可通过查询合约进行查询,在其响应OnRspQryInstrument中查看多头和空头的保证金率。
查询上述两种费率时,如果填写的合约代码为空值,则返回的是持仓中的各个合约的对应费率。换而言之,如果要知道市场所有合约的费率,则需要逐个合约查询费率。
大商所和郑商所和广期所:先开先平,优先平昨。但如果平今手续费有减免,则优先平今。
中金所:同上,先开先平,优先平昨。但是,计算平仓的手续费时,按优先平今仓的规则来计算手续费。
上期所和能源中心:自己下单时指定平今仓还是平昨仓。今仓和昨仓各自的内部也是先开先平。
拿一个中金所的股指期货举例:
简单的假设IF2409合约,开仓和平昨仓是20元每手,而平今仓是300元每手。
账户有IF2409多头昨仓2手。今天先继续开仓IF2409买入1手。
这样就总共有3手持仓,其中2手是昨仓,1手是今仓。
此时卖出平仓1手。根据上面的规则,这平掉的1手是昨仓。
因此还剩下1手昨仓和1手今仓。
但是,收取的手续费是按平今来计算的。
然后继续卖出平仓1手。根据上面的规则,这平掉的1手仍是昨仓。
因此还剩下0手昨仓和1手今仓。
由于在此前的1手平仓计算手续费时,已经按平今来计算了,今仓已经平仓完了,所以此次平仓就按平昨来计算手续费。
因此手续费是 20 (开仓) + 300 (平今) + 20 (平昨) = 340 元。
可以用这种方式来理解:在计算手续费的系统中,也有一套虚拟持仓,不过它的维护规则是优先平今仓,今仓平完以后再平昨仓。真正的持仓系统和这套虚拟持仓系统并行运行,后者只是为了算手续费来使用的。
真实持仓数量 | 虚拟持仓数量 | 总手续费 | |||
昨仓数 | 今仓数 | 昨仓数 | 今仓数 | ||
初始持仓 | 2 | 0 | 2 | 0 | 0 |
买入开仓1手 之后 | 2 | 1 | 2 | 1 | 20 |
卖出平仓1手 之后 | 1 | 1 | 2 | 0 | 20+300 |
再卖出平仓1手 之后 | 0 | 1 | 1 | 0 | 20+300+20 |
因此,如果是为了把股指期货的手续费算准,可以用这套"计算手续费专用"的虚拟持仓系统和它的维护规则。
欢迎加入QQ群736174420一起沟通交流CTP API的使用!~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。