接着上篇:用CTP接口实现期货交易明细分析(1)
上一篇文章发出了代码,这一篇文章分析一下为什么这么写。
这张图把我整个分析过程画出来了,从提出疑问到解决。那我就按上图的顺序一个一个来把问题解决掉。
成交明细需要哪些数据?
从期软上找吧,结果就是图上显示的那些数据。
Tick数据长啥样?
把SDK下载下来什么都知道了,我是直接在vn.py项目当中找的头文件,在ThostFtdcUserApiStruct.h文件中找到名为CThostFtdcDepthMarketDataField的结构体,里面的解释已经很详细了。
我把下面段代码放在了python文件的注释里面了,方便编写时查看
///深度行情
struct CThostFtdcDepthMarketDataField
{
///交易日
TThostFtdcDateType TradingDay;
///合约代码
TThostFtdcInstrumentIDType InstrumentID;
///交易所代码
TThostFtdcExchangeIDType ExchangeID;
///合约在交易所的代码
TThostFtdcExchangeInstIDType ExchangeInstID;
///最新价
TThostFtdcPriceType LastPrice;
///上次结算价
TThostFtdcPriceType PreSettlementPrice;
///昨收盘
TThostFtdcPriceType PreClosePrice;
///昨持仓量
TThostFtdcLargeVolumeType PreOpenInterest;
///今开盘
TThostFtdcPriceType OpenPrice;
///最高价
TThostFtdcPriceType HighestPrice;
///最低价
TThostFtdcPriceType LowestPrice;
///数量
TThostFtdcVolumeType Volume;
///成交金额
TThostFtdcMoneyType Turnover;
///持仓量
TThostFtdcLargeVolumeType OpenInterest;
///今收盘
TThostFtdcPriceType ClosePrice;
///本次结算价
TThostFtdcPriceType SettlementPrice;
///涨停板价
TThostFtdcPriceType UpperLimitPrice;
///跌停板价
TThostFtdcPriceType LowerLimitPrice;
///昨虚实度
TThostFtdcRatioType PreDelta;
///今虚实度
TThostFtdcRatioType CurrDelta;
///最后修改时间
TThostFtdcTimeType UpdateTime;
///最后修改毫秒
TThostFtdcMillisecType UpdateMillisec;
///申买价一
TThostFtdcPriceType BidPrice1;
///申买量一
TThostFtdcVolumeType BidVolume1;
///申卖价一
TThostFtdcPriceType AskPrice1;
///申卖量一
TThostFtdcVolumeType AskVolume1;
///申买价二
TThostFtdcPriceType BidPrice2;
///申买量二
TThostFtdcVolumeType BidVolume2;
///申卖价二
TThostFtdcPriceType AskPrice2;
///申卖量二
TThostFtdcVolumeType AskVolume2;
///申买价三
TThostFtdcPriceType BidPrice3;
///申买量三
TThostFtdcVolumeType BidVolume3;
///申卖价三
TThostFtdcPriceType AskPrice3;
///申卖量三
TThostFtdcVolumeType AskVolume3;
///申买价四
TThostFtdcPriceType BidPrice4;
///申买量四
TThostFtdcVolumeType BidVolume4;
///申卖价四
TThostFtdcPriceType AskPrice4;
///申卖量四
TThostFtdcVolumeType AskVolume4;
///申买价五
TThostFtdcPriceType BidPrice5;
///申买量五
TThostFtdcVolumeType BidVolume5;
///申卖价五
TThostFtdcPriceType AskPrice5;
///申卖量五
TThostFtdcVolumeType AskVolume5;
///当日均价
TThostFtdcPriceType AveragePrice;
///业务日期
TThostFtdcDateType ActionDay;
};
Tick间哪些数据会发生变化?
为什么要关注发生变化了的数据呢?我想是因为在编写tick级策略时,当每个tick数据到来时肯定是在发生了变化的数据上做文章,所以关注这一块数据肯定是没错的,而且看期软的成交明细数据,每一条记录的每一种数据都在发生变化,对的,变化才意味着价值。
怎么去找?其实是可以直接拿期软里面显示的数据来得到结果的,但是我为了科学的去得到结果,我还是在程序里面是写了一个函数去作比对,少拍脑袋多用科学的方式找数据会比较好。
def CompareDepthMarketData(self, data):
"""做tick数据的前后比较"""
if self.PreDepthMarketData is not None:
for key, value in self.PreDepthMarketData.items():
if value != data[key]:
print key + ': pre->' + str(value) + " now->" + str(data[key])
self.PreDepthMarketData = data
我在实盘时打了很多的数据,然后综合期软明细数据的需求,得出了图中的结果。
哪些数据可以应用到成交明细的显示?
其实就是上一步分析出来的数据,但是成交性质可没有哦?请看下一节
# 最终需要的tick方向 (output1)
tick_type_enum = enum(OPENLONG="OpenLong", OPENSHORT="OpenShort", OPENDOUBLE="OpenDouble",
CLOSELONG="CloseLong", CLOSESHORT="CloseShort", CLOSEDOUBLE="CloseDouble",
EXCHANGELONG="ExchangeLong", EXCHANGESHORT="ExchangeShort",
OPENUNKOWN="OpenUnkown", CLOSEUNKOWN="CloseUnkown", EXCHANGEUNKOWN="ExchangeUnkown",
UNKOWN="Unkown", NOCHANGE="NoChange")
成交性质是啥?有现成的数据可以直接使用吗?
这里就会涉及到一些期货的基础知识了,我不引述太多。我直接说结论:
成交性质有8种
多开(OpenLong),空开(OpenShort),双开(OpenDouble),多平(CloseLong),空平(CloseShort),双平(CloseDouble),多换(ExchangeLong),空换(ExchangeShort)
如果是股票就简单多了,但是期货中引入了开仓和平仓的概念,所以这一块会复杂一些,话说我花了几个晚上的时间才搞清楚。
举一个 双平 和 多平 的实例的实例(商品期货,成交量是双边统计),其他就可以引申(这里可以分析出持仓量和成交量的关系):
成交2 持仓 -2 双平
就是说,有2手成交(成交量),这2手的仓差都是“—”的,也就是都是平仓单,其中1手多头平仓,1手空头平仓。(双边统计?)
双平意思好理解,这个都懂,但红色和蓝色是有区别的,红色的是空头主动平仓,和挂单平多仓的成交,对价格上涨有促进作用;蓝色的是多头主动平仓,对价格下跌有好处。举个通俗的例子:你有两手多玉米,想平仓,此时状态:
卖1:1767
买1:1766
你如果想立刻平仓,可以按报1756市价成交,如果正好卖给那个平空仓的,就显示这种状态:
价格 现手 状态 仓差
1766 4 双平(绿色) -4
内盘增加4,外盘不动
你也可以挂单平仓,可以报1767,这样要等一会,因为执行价格优先,平仓优先,时间优先的原则
如果你的卖出去时是一个空头主动平仓买走的
显示这种状态:
价格 现手 状态 仓差
1767 4 双平(红色) -4
这时是外盘增加4,内盘不动
成交 92 持仓 -62 多平
如果是双边统计,那么应该是92/2=46手单边,主动发起的动作是多平,而且为46手,这样对手单肯定也为46手,加起来就是92手,这就是双边统计,两边的成交都算做成交量,这点和股票和股指期货是不一样的。
这时候想想,哪种类型的单可以和多平进行成交?
多开 和 空平 (我怎么知道?看此段分析的最后)
如果对手单全部是多开,这样持仓量是不变的,成交性质应该是多换。但是这里持仓量少了62手,除了多平会导致减少46手,对手单也导致减少了(62-46)=16手,这里可以判定肯定会有空平的单子夹在里面,有多少呢?
X 多开手数 Y 空平手数
x + y = 46
x - y = -62 + 46 = -16
x = 15
y = 31
附:我怎么知道多平的对手单由多开和空平组成?
这里是我自己的一个小诀窍,其实期货买卖只有4种动作:
多开(买合约),多平(卖合约),空开(卖合约),空平(买合约)
这样多平是卖合约,对手就是买合约:多开和空平
持仓量变化和成交量有什么关系?
持仓量变化有3种(input1)
开仓(持仓量增加),空仓(持仓量减少),换仓(持仓量不变)
@staticmethod
def get_open_interest_delta_forward(open_interest_delta, volume_delta):
"""根据成交量的差和持仓量的差来获取仓位变化的方向
return: open_interest_delta_forward_enum
"""
if open_interest_delta == 0 and volume_delta == 0:
local_open_interest_delta_forward = open_interest_delta_forward_enum.NONE
elif open_interest_delta == 0 and volume_delta > 0:
local_open_interest_delta_forward = open_interest_delta_forward_enum.EXCHANGE
elif open_interest_delta > 0:
if open_interest_delta - volume_delta == 0:
local_open_interest_delta_forward = open_interest_delta_forward_enum.OPENFWDOUBLE
else:
local_open_interest_delta_forward = open_interest_delta_forward_enum.OPEN
elif open_interest_delta < 0:
if open_interest_delta + volume_delta == 0:
local_open_interest_delta_forward = open_interest_delta_forward_enum.CLOSEFWDOUBLE
else:
local_open_interest_delta_forward = open_interest_delta_forward_enum.CLOSE
return local_open_interest_delta_forward
持仓量与成交量的关系貌似在上一节已经分析出来了,但是持仓量和成交价格(多空)的关系又如何呢?如果我现在通过持仓量的增加来判断是开仓,没问题,那是多开还是空开呢?这时候需要了解下期货订单撮合成交的机制。
参考:期货的撮合交易是如何成交的,市价单及限价单的成交机制? - 量化交易 - 知乎
简单来讲就是(input2):
- 价格在ask1 price或者ask1 price之上,则为买合约(多开,空平)
- 价格在bid1 price或者bid1 price之下,则为卖合约(多平,空开)
清晰了之后才能用代码写出来,注意我做这个计算时,是以上个tick的数据为准,再以该tick数据为参考,由于我们的tick只是快照,这个算法其实是不准确的。
@staticmethod
def get_order_forward(last_price, ask_price1, bid_price1, pre_last_price, pre_ask_price1, pre_bid_price1):
"""获取成交的区域,根据当前tick的成交价和上个tick的ask和bid价格进行比对
return: order_forward_enum
"""
if TickAnalysis.float_bigger_equal(last_price, pre_ask_price1):
local_order_forward = order_forward_enum.UP
elif TickAnalysis.float_smaller_equal(last_price, pre_bid_price1):
local_order_forward = order_forward_enum.DOWN
else:
if TickAnalysis.float_bigger_equal(last_price, ask_price1):
local_order_forward = order_forward_enum.UP
elif TickAnalysis.float_smaller_equal(last_price, bid_price1):
local_order_forward = order_forward_enum.DOWN
else:
local_order_forward = order_forward_enum.MIDDLE
return local_order_forward
其实分析到这里,我们可以发现从成交量,持仓量,还有成交价格可以算出那8种成交性质,代码请看本文最后一段。
对手单是什么?为什么要分析他,期软上都没有?
啥呀?上面的两部分已经把这个问题已经分解完了,我还说啥...为什么要分析他?
我是这么想的,这个行业是精细活,不分析清楚我不舒服。哈哈
# 只与计算对手单的组成相关,只有4种tick类型才需要计算对手单的组成
handicap_dict = {tick_type_enum.OPENLONG: {opponent_key_enum.OPPOSITE: tick_type_enum.CLOSELONG,
opponent_key_enum.SIMILAR: tick_type_enum.OPENSHORT},
tick_type_enum.OPENSHORT: {opponent_key_enum.OPPOSITE: tick_type_enum.CLOSESHORT,
opponent_key_enum.SIMILAR: tick_type_enum.OPENLONG},
tick_type_enum.CLOSELONG: {opponent_key_enum.OPPOSITE: tick_type_enum.OPENLONG,
opponent_key_enum.SIMILAR: tick_type_enum.CLOSESHORT},
tick_type_enum.CLOSESHORT: {opponent_key_enum.OPPOSITE: tick_type_enum.OPENSHORT,
opponent_key_enum.SIMILAR: tick_type_enum.CLOSELONG}
}
用Python的字典把对手单的对应关系做了描述
最后这部分代码是核心,简单来讲就是 f (input1, input2) = output1:
- 根据成交量和持仓量的变化算出仓位变化性质
- 根据价格算出多空
- 根据仓位变化性质和多空算出那8种成交性质
# 根据 open_interest_delta_forward_enum 和 order_forward_enum 计算出tick类型的字典
tick_type_cal_dict = {
open_interest_delta_forward_enum.NONE:
{
order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.NOCHANGE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE},
order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.NOCHANGE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE},
order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.NOCHANGE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
},
open_interest_delta_forward_enum.EXCHANGE:
{
order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.EXCHANGELONG,
tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.EXCHANGESHORT,
tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.EXCHANGEUNKOWN,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
},
open_interest_delta_forward_enum.OPENFWDOUBLE:
{
order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENDOUBLE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENDOUBLE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENDOUBLE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
},
open_interest_delta_forward_enum.OPEN:
{
order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENLONG,
tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENSHORT,
tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.OPENUNKOWN,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
},
open_interest_delta_forward_enum.CLOSEFWDOUBLE:
{
order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEDOUBLE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEDOUBLE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEDOUBLE,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
},
open_interest_delta_forward_enum.CLOSE:
{
order_forward_enum.UP: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSESHORT,
tick_type_key_enum.TICKCOLOR: tick_color_enum.RED},
order_forward_enum.DOWN: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSELONG,
tick_type_key_enum.TICKCOLOR: tick_color_enum.GREEN},
order_forward_enum.MIDDLE: {tick_type_key_enum.TICKTYPE: tick_type_enum.CLOSEUNKOWN,
tick_type_key_enum.TICKCOLOR: tick_color_enum.WHITE}
},
}
Comments
comments powered by Disqus