资源加载中... loading...

浅谈商品期货中的高频交易(二):盘口高频因子建模

Author: ianzeng123, Created: 2024-02-20 11:58:13, Updated: 2024-02-28 21:43:36

img

叠甲:本文仅做商品期货高频策略的探索性尝试,与实际专业高频策略还具有一定差距,请各位量化大拿轻喷~~

上节课我们介绍了商品期货高频交易中使用到的订单薄数据。订单薄数据在高频交易中可以有很多的用途,例如在高频套利研究来说,可以用来直接获取价差,当价差超过一定阈值的时候,进行套利的交易;另外订单薄数据可以用来预测短期价格走势,当然直接使用原始数据肯定是不合适的。我们需要对原始的数据进行一些处理,发掘出有效的特征,有点类似于数据分析中的特征工程处理。在分析订单簿的研究中,一种角度是通过价差、深度、宽度、斜率、订单不平衡等限价订单簿的静态指标,这些特征变量可以用来预测短期价格走势。今天我们将要介绍对订单薄数据进行一些特征的提取和构建。

首先导入我们通过优宽实盘收集的,一个完整交易日(2024-1-30: 21:00 至 2023-1-31: 15:00)的燃油主力合约的数据(fu2403),可以看到一共包含38691条数据(基本上每秒两条)。

image

由于使用的是上期所的数据,所以包含5档的行情。这里我们以当天开盘第一条数据为例,对五档的挂单数据进行可视化的展示。其中绿色代表买盘,红色代表卖盘,具体柱状图数值代表挂单数量。接下来我们就要来根据这些数据进行一些特征挖掘的工作。

image

  1. 价差

在订单薄(Order Book)中,价差是指买卖盘中最优买价和最优卖价之间的差异。最优买价是指当前市场上买家愿意支付的最高价格(买一价),而最优卖价是指卖家愿意出售的最低价格(卖一价)。价差通常用于衡量市场的流动性和市场的紧张程度。

$ 价差 = (最优卖价 - 最优买价)$

价差的大小可以提供一些关于市场流动性和交易活动水平的信息。通常,较小的价差表示市场较为流动,而较大的价差可能暗示着市场的紧张或流动性不足。高频交易和做市商通常会关注和利用价差的变动来执行交易策略。

对价差进行频率统计,结果发现价差为-1出现的频率为38561,价差为-2出现的频率为130,证明燃油主力合约的流动性很好,很少出现-2的价差,一旦出现也会立即修正。

价差 频率
-1.0 38561
-2.0 130
  1. 深度不平衡

$ \text{QR}_i = \frac{\text{买档成交量}_i - \text{卖档成交量}_i}{\text{买档成交量}_i + \text{卖档成交量}_i} $

深度不平衡(Depth Imbalance)的公式用于衡量订单薄的深度在买卖两侧的不平衡程度。其中的QR1 到 QR5分别表示订单薄中第1档到第5档的深度不平衡。公式中,分子是买方深度和卖方深度之差,分母是它们之和。

如果深度不平衡值为正,说明买方深度大于卖方深度;如果为负,则说明卖方深度大于买方深度。值越大表示不平衡程度越大。深度不平衡可用于观察市场中买卖双方的力量对比。在一些交易策略中,深度不平衡可能作为交易信号的一部分,指导交易者判断市场走势。

QR1 QR2 QR3 QR4 QR5
mean -0.005516 0.007754 0.007070 -0.024988 -0.012989
std 0.400887 0.226708 0.237198 0.236547 0.171665
min -0.995000 -0.966330 -0.827957 -0.889610 -0.800000
25% -0.292561 -0.148594 -0.157175 -0.206349 -0.122807
50% 0.000000 0.006834 0.000000 -0.044619 -0.016736
75% 0.279279 0.153983 0.177434 0.156463 0.100917
max 0.993921 0.890909 0.795207 0.649460 0.682171

根据统计特征分析结果,从QR1(一档行情)到QR5(五档行情),标准差逐渐减小,而最小值和最大值的范围也逐渐缩小,可能表明一档盘口行情是最为激烈的,随着行情深度的增加,挂单数量波动性逐渐平稳,交易活动在更深的档口上趋于更为稳定。

  1. 宽度不平衡

宽度不平衡更多的基于挂单买价和挂单卖价之间的间隔,在商品期货中,各档位挂单买价和挂单卖价之间的差距都为1,因此不具有实际的预测意义。

  1. 订单不平衡

订单不平衡(Volume Order Imbalance,简称VOI)是一个用于反映市场供需关系和价格买卖压力的指标。它通过比较买盘(bid)和卖盘(ask)的成交量变化,来衡量市场上买卖盘力量的不平衡情况。

$\text{订单不平衡} = \frac{\text{买盘总量} - \text{卖盘总量}}{\text{买盘总量} + \text{卖盘总量}}$

当 order_imbalance 为正值时,表示买盘总委托量大于卖盘总委托量,暗示市场上存在更多的买方需求;相反当 order_imbalance 为负值时,表示卖盘总委托量大于买盘总委托量,表明市场上存在更多的卖方供应;最后当order_imbalance 在0附近波动时,可能表示市场上的买卖委托量相对平衡。这个指标有助于反映市场的供需关系,提供了有关市场中买方和卖方力量对比的信息。

image

对订单不平衡指标进行频率图绘制,结果表明基本上呈现略微右偏的趋势,在一定程度上说明卖盘总委托量大于买盘总委托量的,但这并不一定意味着市场必然朝着下降趋势发展。订单不平衡指标反映了市场供需关系和价格买卖压力,但它本身并没有直接指示价格的上升或下降。

  1. 交易量订单流不平衡

为更细化订单不平衡的变化,我们还可以引入交易量订单流不平衡指标(Volume Order Imbalance,以下简称VOI)。这是一种衡量在研究特定时间段内,最优买卖价格上的委托量增量之差的方法。它反映了在最优买卖价格上的投资行为所呈现的供需关系。VOI通过比较最优买卖价格处的委托量变化,提供了关于市场中买方和卖方力量不平衡的信息。

订单不平衡(Volume Order Imbalance,VOI)的计算公式如下:

image

$\text{VOI} = \text{deltaVbid} - \text{deltaVask}$

其中,deltaVbid 代表相邻两个时间点买盘(bid)成交量的变化;deltaVask 代表相邻两个时间点卖盘(ask)成交量的变化;VOI 是计算得到的订单不平衡指标,它表示买盘和卖盘的成交量变化之间的差异。

当 VOI 为正值时,表示买盘成交量的变化大于卖盘成交量的变化,表明市场上存在更多的买盘需求;当 VOI 为负值时,表示卖盘成交量的变化大于买盘成交量的变化,表明市场上存在更多的卖盘供应。通过这个指标,可以大致了解市场上的供需关系和买卖压力,有助于分析市场结构、市场偏好和投资者行为等方面的信息。

这里我们对VOI值进行频率图绘制,结果呈现较为明显的以0为均值的对称分布,表明在总体上,买盘压力和卖盘压力是一致的。但是具体的买盘压力/卖盘压力,和价格走势的变化,我们后续还可以进行进一步的分析。

image

上述呢,就是用来衡量买卖失衡的一些指标。另外我们还可以导入买卖压力指标。

  1. 买卖压力指标

参考股票证券的买卖压力指标,我们尝试构建商品期货的买卖压力指标。该指标是通过下面公式计算得到的,这里的M是中间价,首先我们计算权重公式,这个公式目的是为每个价格层分配一个相对权重,考虑了该价格层与中间价的差异。这样,价格越接近中间价的价格层在计算中将具有较大的权重,反映了它们对市场的影响较大。

$W_i = \frac{M / (p_i - M)}{ {\textstyle \sum_{i}^{n}} M/(p_i - M) }$

接下来我们计算卖盘(sell)和买盘(buy)的压力,通过将每个卖盘/买盘价格层的成交量乘以相应的权重,再将所有价格层的结果相加得到。 $press_{sell} = {\textstyle \sum_{i}^{n}} {vol_i} \frac{M / (p_i - M)}{ {\textstyle \sum_{i}^{n}} M/(p_i - M) }$

$press_{buy} = {\textstyle \sum_{i}^{n}} {vol_i} \frac{M / (M - p_i)}{ {\textstyle \sum_{i}^{n}} M/(M - p_i) }$

最后通过计算卖盘压力和买盘压力的对数差异得到最终的买卖压力指标。 $P = log(press_{sell}) - log(press_{buy})$

该指标的正负值反映了市场上买方和卖方的压力对比。正值可能暗示着买方力量较强,而负值可能暗示卖方力量较强。

我们对该指标进行统计分析,根据频率直方图,结果发现该指标呈现较为显著的钟型正态分布,接着我们利用QQ-plot进行正态分布验证,结果显示除去尾部个别数据点,基本上符合正态分布。

image

上述就是我们初步构建的高频因子指标,并初步分析了各类指标的统计学特征。当然我们指标的构建更多的参考是股票市场,然而股票市场和期货市场还是具有一定的区别,因此指标在移植的过程中需要考虑商品期货市场的特征,大家可以提出更多的宝贵意见,从而构建更多更有效的指标,去对价格趋势进行更好的预测。

import numpy as np

def calculate_features(df):
    # 买卖价差特征
    df['spread'] = df['bid_price1'] - df['ask_price1']
    
    # 深度不平衡特征
    for i in range(1, 6):
        bid_col = f'bid_volume{i}'
        ask_col = f'ask_volume{i}'
        qr_col = f'QR{i}'
        df[qr_col] = (df[bid_col] - df[ask_col]) / (df[bid_col] + df[ask_col])

    df[['QR1', 'QR2', 'QR3', 'QR4', 'QR5']] = df[['QR1', 'QR2', 'QR3', 'QR4', 'QR5']].fillna(0)

    # 订单不平衡特征
    bid_volumes = df[[f'bid_volume{i}' for i in range(1, 6)]]
    ask_volumes = df[[f'ask_volume{i}' for i in range(1, 6)]]
    df['bid_sizes'] = bid_volumes.sum(axis=1)
    df['ask_sizes'] = ask_volumes.sum(axis=1)
    df['order_imbalance'] = (df['bid_sizes'] - df['ask_sizes']) / (df['bid_sizes'] + df['ask_sizes'])

    # 交易量订单流不平衡
    df['deltaVask'] = np.where(df['ask_price1'] > df['ask_price1'].shift(1), 0,
                               np.where(df['ask_price1'] == df['ask_price1'].shift(1),
                                        df['ask_volume1'] - df['ask_volume1'].shift(1),
                                        df['ask_volume1']))
    
    df['deltaVbid'] = np.where(df['bid_price1'] < df['bid_price1'].shift(1), 0,
                               np.where(df['bid_price1'] == df['bid_price1'].shift(1),
                                        df['bid_volume1'] - df['bid_volume1'].shift(1),
                                        df['bid_volume1']))
    
    df['VOI'] = df['deltaVbid'] - df['deltaVask']

    # 买卖压力指标
    n1, n2 = 1, 5

    _ = np.arange(n1, n2 + 1)

    bench_prices = (df['ask_price1'] + df['bid_price1']) / 2

    def unit_calc(bench_price):
        bid_d = [bench_price / (bench_price - df["bid_price%s" % s]) for s in _]
        bid_denominator = sum(bid_d)
        bid_weights = [d / bid_denominator for d in bid_d]
        press_buy = sum([df["bid_volume%s" % s] * w for s, w in zip(_, bid_weights)])

        ask_d = [bench_price / (df['ask_price%s' % s] - bench_price) for s in _]
        ask_denominator = sum(ask_d)
        ask_weights = [d / ask_denominator for d in ask_d]
        press_sell = sum([df['ask_volume%s' % s] * w for s, w in zip(_, ask_weights)])

        return (np.log(press_buy) - np.log(press_sell)).replace([-np.inf, np.inf], np.nan)

    result = unit_calc(bench_prices)
    df['price_weighted_pressure'] = result
    
    # 成交量增加值
    df['volume_add'] = df.volume.diff()

    # 持仓量增加值
    df['interest_add'] = df.open_interest.diff()

    return df

参考资料:

  1. 买卖压力失衡——利用高频数据拓展盘口数据

  2. 中信建投-因子深度研究系列:高频量价选股因子初探

  3. 【高频】Level-2高频数据的实证研究(一)

  4. Ticker Trend短期趋势的一些高频因子测试结果

本系列课程旨在为大家介绍高频交易在商品期货量化交易中的应用,其他相关文章请点击下面链接:


更多内容