从指数角度看天量天价地量地价

2022年5月10日 987点热度 2人点赞 0条评论

这篇文章原先是发表在我的订阅号里的,现在给它搬回博客上。

做交易有一句话叫做天量天价,地量地价。这句话被很多交易者挂在嘴边,事实是否果然如此?我们从指数角度对量价关系做一个简单量化,来进行印证。

量化量价关系,涉及一个统计学概念:百分位数(percentile)。百分位数是个体样本在总体样本中所处的位置,举个例子,如果我们以一个班的总体成绩为样本,其90百分位数则意味,全班有90%的人分数在这个分数以下,10%的人分数在这个分数以上,以此类推。投资领域,通常会用百分位数来衡量估值水平,50百分位数即为通常所说的中位数。

这个概念是后续分析的前提,我们要进行数学上的量价关系印证,思路如下:

1、首先找到地量或天量发生后对应的收盘价格(或一段样本的收盘价均值)

2、然后以样本期内所有的收盘价格为序列,计算地量或天量发生后,对应收盘价格(或均值)在总体收盘价序列中所处的百分位。

预期中,如果地量发生后,其收盘价格(或收盘价均值)对应较低的百分位,并且天量发生后,其收盘价格(或收盘价均值)对应较高的百分位,那么我们就是可以判断“天量天价,地量地价”这个逻辑成立。除了成交量极值区间,中间的样本不做研究,因为常态的成交量分布和价格分布没有明显的线性关系,我们只考虑极值发生时,量价这两个因子之间的对应关系。

接下来,我们将以上证指数,沪深300,上证50,中证500这几个主流宽基指数进行逻辑验证,不考虑个股是因为单只股票的成交量有可能会因为主力对敲而失真,而指数不会,其量价关系必然是市场整体合力的结果。

接下来让我们开始,用到的分析工具是numpy科学计算工具包,这是做量化分析甚至深度学习的基础技术栈,如果您对编程过程不感兴趣,可以直接拉到最后看结果。

首先,导入需要用到的包,并定义指数字典,调取各个指数近2年的日线数据(500个交易日),sprider是我自己写的数据接口工具包。

import spider
import numpy as np
code_name = {
                'sh000001':'上证指数',
                'sh000300':'沪深300指数',
                'sh000016':'上证50指数',
                'sh000905':'中证500指数'
}

for code in code_name:
    df = spider.get_k_bars(code,240,500,3,{'qfq_day':'None'})

这里我们做了一个循环,遍历标的字典,获取各个指数2年的日线数据(500个交易日)df,接下来计算极值及对应的发生日期,方便在K线图上观察印证:

min_volume = df['volume'].min()
max_volume = df['volume'].max()
print ('\r')
print (f'{code_name[code]}量价分析:')
print ('\r')
print (f'最小成交量: {min_volume}')
print (f'最大成交量: {max_volume}')
min_volume_close = float(df.loc[df['volume']==min_volume,'close'])
min_volume_close_date = df.loc[df['volume']==min_volume,'day'].values[0]
max_volume_close = float(df.loc[df['volume']==max_volume,'close'])
max_volume_close_date = df.loc[df['volume']==max_volume,'day'].values[0]
print (f'最小成交量对应收盘价: {min_volume_close}')
print (f'最小成交量发生日期: {min_volume_close_date}')
print (f'最大成交量对应收盘价: {max_volume_close}')
print (f'最大成交量发生日期: {max_volume_close_date}')

OK,这里我们得到最小/最大成交量值与对应收盘价及日期,然后,我们将整个收盘价序列转化为numpy工具包对应的格式,并计算收盘价格序列对应的50中位数,作为参考:

close_array = np.array(df['close'])
close_percentile = np.percentile(close_array,50)
print (f'收盘价中位数: {round(close_percentile,3)}')

接下来,我们就要计算,最小/最大成交量发生时,对应收盘价格在整体收盘价格序列中所处的百分位,我们预期中,最小成交量的收盘价对应较低的百分位,最大成交量的收盘价对应较高的百分位。这里我们需要定义一个函数,输入序列series和百分位数x,计算百分位数x对应series的百分位,算法用二分法实现:

def cal_x_percentile(series :np.array, x:float) -> float:
    """
    二分法求元素百分位数 x 在序列series中所处的百分位
    """
    min, max = 0, 1
    while True:
        m = (min+max)/2
        if np.percentile(series, 100*m) >= x:
            max = m
        elif np.percentile(series, 100*m) < x:
            min = m
        if np.abs(min-max) <= 0.000001:
            break
    return m

接下来,我们利用刚写的函数,计算成交量极值发生时所产生的收盘价格,对应整体样本收盘价序列的百分位并打印:

min_volume_percentile=cal_x_percentile(close_array,min_volume_close)
max_volume_percentile=cal_x_percentile(close_array,max_volume_close)
print(f'最小成交量对应的收盘价百分位数为:{min_volume_percentile:.2%}')
print(f'最大成交量对应的收盘价百分位数为:{max_volume_percentile:.2%}')

上面的分析数据基于极值产生的这一个点,为了逻辑的严谨,我们再看看取一个区间样本均值对应的百分位,这里我们将数据按成交量进行排序,定义1个period周期,比如20,取这个排序头尾各period个样本,计算其对应的收盘价均值,用这个均值做百分位计算,看看能得到什么样的结果,与极值点计算的结果在逻辑印证上是否冲突,代码如下:

period=20
sorted_df=df.sort_values(by='volume',ascending=True,ignore_index=True)
min_volume_close_mean = sorted_df.head(period)['close'].mean()
max_volume_close_mean = sorted_df.tail(period)['close'].mean()
print (f'最低{period}个成交量对应的收盘价均值为: {round(min_volume_close_mean,2)}')
print (f'最高{period}个成交量,对应的收盘价均值为: {round(max_volume_close_mean,2)}')
min_volume_mean_percentile=cal_x_percentile(close_array,min_volume_close_mean)
max_volume_mean_percentile=cal_x_percentile(close_array,max_volume_close_mean)
print (f'最低{period}个成交量,收盘价均值对应的百分位数为: {min_volume_mean_percentile:.2%}')
print (f'最高{period}个成交量,收盘价均值对应的百分位数为: {max_volume_mean_percentile:.2%}')
print ('\r')

得到的计算结果如下:

经过上述分析,我们得到一个比较直观的结果:

在过去两年的样本期内,各大宽基指数的低成交量确实对应着价格的低百分位,而较高的成交量对应的百分位数则没有那么高,其中沪深300指数,上证50指数的放量均对应着指数中位数附近,也就是说这两个指数并没有明显的高位放量现象。

从分析结果看,指数上,低成交量确实可以作为一个底部区间的判断因子,但高成交量缺并不一定对应着顶部区间。

QThinker

前地产从业者,假装是个程序员,热爱编程与交易 自研QThinker量化交易框架

文章评论