Date Tags 量化

接着上篇:用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:

  1. 根据成交量和持仓量的变化算出仓位变化性质
  2. 根据价格算出多空
  3. 根据仓位变化性质和多空算出那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}
        },
}