使用Python计算股票期货,量化程序化交易

827 阅读12分钟

如果将期货市场定义战场,那多方和空方就是在战场上厮杀的士兵。

因此及时了解双方在战场上的战况尤为重要。知己知彼,百战不殆!

一、初识tick数据

在行情每个期货行情软件上,右下角有一个长方形的窗口。这个长方形的窗口每跳动一次就是一个tick,跳动一次什么意思?也就是在这个tick上面有人在开、平操作。请看下图:

file

北京:成交时间

价格:当前最新成交的价格

现手:当前tick成交量(双边)

仓差:当前tick上,多空开平争夺后导致了整个合约的持仓量增加、减少或者不变这三种情况

性质:这需要考虑多方面来考量,下面给大家介绍重点介绍

二、计算多空博弈量。

要想计算某个品种的多空持仓量,必须经过以下四步骤:

1、判断并记录不带方向的开、平(如:开仓,平仓,双平,双开,换手,未知)共六种

2、判断并记录价格成交的方向(如:向上,向下,不变)共三种方向

3、根据上述两步判断并记录带方向的开平(如:多开,空开,多平,空平,双开,双平,多换,空换,未知开,未知平,未知换)

4、根据第三步记录的带方向的开平,计算当前tick和全部数据的多空 开平换量(如:多开量,空开量,多平量,空平量,未知开量,未知平量,未知换量)

file

计算多空开平量只需要经过上面四个步骤就可以将其计算出,下面将根据每一步详细介绍判断和计算,并用 Python 实现计算的全过程。

准备工作

1、首先作者使用的软件是Pycharm 。

2、导入数据处理利器‘pandas’,2D绘图包 'matplotlib'。

3、数据的准备:

导入tick数据,RB1910.csv文件,并将数据转换为DataFrame数据格式。并将相应的数据存入每个列表中(习惯用列表处理)。

import pandas as pd
import matplotlib.pyplot as plt
import talib
plt.rcParams['font.sans-serif']=['SimHei']# 用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False # 用来正常显示负号

count = 6000 # 多空量计算周期
RB_data_csv =pd.read_csv('RB1910.csv',encoding='UTF-8')
RB_data = pd.DataFrame(RB_data_csv)

# -------------------------------------数据清洗
time = []
check_close = []
position = []
volume = []
close = []
sellshort_1 = []
buy_1 = []
for i in range(len(RB_data['时间'])):
    time.append(RB_data['时间'][i])
    position.append(RB_data['持仓量'][i])
    volume.append(RB_data['现手'][i])
    close.append(RB_data['最新价'][i])
    sellshort_1.append(RB_data['卖一'][i])
    buy_1.append(RB_data['买一'][i])

file

运行结果:

file

注意:上述中虽然有现手,不能直接使用因为某平台导出的数据是累加的成交量。因此我们要用:

现手 = 当前tick的成交量-上一tick成交量

同理:

仓差=当前tick的持仓量-上一tick的持仓量

4、计算,现手和仓差。

Now_hand.append(volume[i]-volume[i-1])
Entrepot.append(position[i] - position[i - 1])

完整代码:

#-------------------计算仓差和现手
Now_hand = []       # ----现手
Entrepot = []  # ----仓差
trade_type = []      # 计算出不带方向的开平(未知,换手,双开,双平,平仓,开仓)

for i in range(len(volume)):
    if i ==0 :
        Now_hand.append(0)
        Entrepot.append(0)
        trade_type.append(i)
    if i>0 :
        Now_hand.append(volume[i]-volume[i-1])
        Entrepot.append(position[i] - position[i - 1])

我们打印一下看看我们的数据长什么样吧!

file

数据:展示一个tick数据

|----------------------------------------|

开始时间: 20190514090001.0

结束时间: 20190514150000.0

----------------------------------------|

最新价:, 3674.0

卖一价: 3674.0

买一价: 3672.0

现手: 1716.0

仓差: -156.0

|----------------------------------------|

Ok!我们文章所要用到的数据已经完成收集。下面正式进入第一步。

第一步,不带方向的开平

判断并记录不带方向的开、平(如:开仓,平仓,双平,双开,换手,未知)共六种:

如图所示:

file

检验一下是否正确:下面我在某行情软件上截取的一段tick成交数据,然后对应上述的6中方法随机检验一下。

file

第一笔:现手 26 ,仓差 +2  性质 多开;

第二笔:现手 6   ,仓差  -2  性质 空平;

根据上面判断公式得到结果:示范两笔,后面的感兴趣可以自行计算。

第一笔:仓差>0 and 现手 != 仓差  ,判断为‘开’。不带多空方向

第二笔:仓差<0 and 现手+ 仓差  !=0 ,判断为‘平’。不带多空方向

代码:

for i in range(len(volume)):
    if i ==0 :
        Now_hand.append(0)
        Entrepot.append(0)
        trade_type.append(i)
    if i>0 :
        Now_hand.append(volume[i]-volume[i-1])
        Entrepot.append(position[i] - position[i - 1])

        if Entrepot[i] == 0 and Now_hand[i]==0:
            trade_type.append('未知')
        if Entrepot[i] == 0 and Now_hand[i]>0:
            trade_type.append('换手')
        if Entrepot[i] > 0 and Now_hand[i] == Entrepot[i]:
            trade_type.append('双开')
        if Entrepot[i] > 0 and Now_hand[i]!=Entrepot[i]:
            trade_type.append('开仓')
        if Entrepot[i] < 0 and Now_hand[i]+Entrepot[i]==0:
            trade_type.append('双平')
        if Entrepot[i] < 0 and Now_hand[i]+Entrepot[i]!=0:
            trade_type.append('平仓')
print('trade_type:', trade_type[0:3])
print('Now_hand:', Now_hand[0:3])
print('Entrepot:', Entrepot[0:3])

运行结果:

file

紧接着下面第二步。

第二步,计算成交方向

判断并记录价格成交的方向(如:向上,向下,不变)共三种方向:

向上:最新价>=卖一价 & 最新价>=前卖一价

向下:最新价<=买一价 & 最新价<=买卖一价

不变:其他

file

代码:

#------------------------计算方向
trade_direction = [] # ----价格成交方向( 向上,向下,不变)

for i in range(len(Now_hand)):
    if i == 0:
        trade_direction.append(0)  # 方向
    if i > 0:
        if close[i]>=sellshort_1[i-1]:
            trade_direction.append('向上')
        elif close[i]>=sellshort_1[i]:
            trade_direction.append('向上')

        else:
            if close[i] <= buy_1[i - 1]:
                trade_direction.append('向下')
            elif close[i] <= buy_1[i]:
                trade_direction.append('向下')
            else:
                trade_direction.append('不变')

至此,第二步已经完成。下面我们就能结合前两步判断出当前tick成交的带有方向的开平了,一起来吧!

第三步,带方向的开平

根据上述两步判断并记录带方向的开平(如:多开,空开,多平,空平,双开,双平,多换,空换,未知开,未知平,未知换)

根据第三步的阐述,我们现在就能够判断出当前tick 到底是多开,空开,还是多换,空换等等。

首先我们看一下判断公式:请点开大图查看。

file

开仓:

多开=开仓+向上;空开=开仓+向下;未知开=开仓+不变;

平仓:

多平=平仓+向下;空平=平仓+向上;未知平=平仓+不变;

换手:

多换=换手+向上;空换=换手+向下;未知换=换手+不变;

双开:

双开=双开+向上、双开+向下、双开+不变;

双平:

双平=双平+向上、双平+向下、双平+不变;

举例:

多开=开仓(仓差>0 and 现手!=仓差)+向上(最新价>=卖一价 or 最新价>=前卖一价)

括号内的判断方式在前两步已经阐述过了,这里不在重复,如果不明白请反复看前两步。

代码:

#--------------------带方向的开平
trade_netrue = []    # ----带方向的开平(多空换,未知换;双开;双平;空平多平,未知平;多开空开,未知开)

for i in range(len(Now_hand)):
    if i == 0:
        trade_netrue.append(0)  # 开平性质
    if i > 0:
        #'换手'
        if trade_type[i]=='换手' and trade_direction[i]=='向上':
            trade_netrue.append('多换')
        elif trade_type[i]=='换手' and trade_direction[i]=='向下':
            trade_netrue.append('空换')

        #'双开'
        elif trade_type[i]=='双开' and trade_direction[i]=='向上':
            trade_netrue.append('双开')
        elif trade_type[i]=='双开' and trade_direction[i]=='向下':
            trade_netrue.append('双开')
        elif trade_type[i]=='双开' and trade_direction[i]=='不变':
            trade_netrue.append('双开')

        #'双平'
        elif trade_type[i]=='双平' and trade_direction[i]=='向上':
            trade_netrue.append('双平')
        elif trade_type[i]=='双平' and trade_direction[i]=='向下':
            trade_netrue.append('双平')
        elif trade_type[i]=='双平' and trade_direction[i]=='不变':
            trade_netrue.append('双平')

        #'平仓'
        elif trade_type[i]=='平仓' and trade_direction[i]=='向上':
            trade_netrue.append('空平')
        elif trade_type[i]=='平仓' and trade_direction[i]=='向下':
            trade_netrue.append('多平')
        #'多开'
        elif trade_type[i]=='开仓' and trade_direction[i]=='向上':
            trade_netrue.append('多开')
        elif trade_type[i]=='开仓' and trade_direction[i]=='向下':
            trade_netrue.append('空开')
        else:
            if trade_type[i]=='换手' and trade_direction[i]=='不变':
                trade_netrue.append('未知换')
            elif trade_type[i]=='平仓' and trade_direction[i]=='不变':
                trade_netrue.append('未知平')
            elif trade_type[i]=='开仓' and trade_direction[i]=='不变':
                trade_netrue.append('未知开')

直到现在我们的第三步已经完美的完成啦,并且已经知道了当前成交的这一笔是多开还是空开或者其他带方向的开平。

由于最后一步计算开平仓量有点绕,就单独用个大点来介绍,详情请往下看吧。

第四步、计算开平仓量及可视化

再计算之前,我们得了解一下对手单;

作者理解的对手单:即当前tick发生时,主动成交方的对立方的开平量。后面会有例子具体介绍,举个简单例子什么对立方:性质,空平 。

主动成交方:空平方

对立方:主动方与对立方(多平)=双平,主动方与对立方(空开)=空换 等等,而对立方的开平量就是我们所说的对手单

1、计算开平量。

要计算当前所有数据的多空开平量,必须要知道单个tick里面所包含的多空开平量,然后汇总。接下来我会用一个模板来计算,直接按照模板来计算。其实很简单,我们只需要根据‘性质’来判断参与者。

本文所用的方法也是按照这个来模板区分的

s =abs(Entrepot[i]) / 2 o = Now_hand[i] / 2 - s

仔细看会发现一规律:

s =abs(Entrepot[i]) / 2 o = Now_hand[i] / 2 - s

知道了每个tick的参与者,在目前也不能计算出真实的对手单的数量。我们还需要最后一步。设两个变量分别是:S,O 。先不用管这S,O各自啥意思,等到后面再解释,按照步骤来就行。

s =abs(Entrepot[i]) / 2 o = Now_hand[i] / 2 - s

代码:

s =abs(Entrepot[i]) / 2 o = Now_hand[i] / 2 - s

那怎利用这两个变量来计算出真实的对手单呢?下面直接看案例吧!

举个例子:

---------------------------------------

性质: 空平;    对手:空开(O),多平(S);    主动方:空平(现手 / 2)

性质: 空开;    对手:空平(O),多开(S);    主动方:空开(现手 / 2)

---------------------------------------

性质: 多平;    对手:多开(O),空平(S);    主动方:多平(现手 / 2)

性质: 多开;    对手:多平(O),空开(S);    主动方:多开(现手 / 2)

--------------------------------

性质: 空换;    空开(现手 / 2),空平(|现手 | / 2);

性质: 多换;    多开(现手 / 2),多平(现手 / 2);

--------------------------------

性质: 双开;    多开(现手 / 2),空开(现手 / 2);

性质: 双平;    多平(现手 / 2),空平(现手 / 2);

--------------------------------

性质: 未知;    对手:未知;

--------------------------------

实验一下吧!

例如:

现手 24 ,仓差 -8 ,性质 空平;

分析:

主动方:空平单量= 现手 / 2 =12(手)

对立方:空开单量= O(现手 / 2-S)=8(手),多平单量= S(|仓差| / 2)=4(手)

因此,当前tick的成交各方单子分别是:12手空平,8手空开,4手多平。反推仓差:-12-4+8=-8。

再例如:

现手24,仓差 0,性质 双平;

分析:双平双开直接 现手/2 计算即可。

空平=现手/2=12手,多平(对方)=现手/2=12手

因此,当前tick的成交各方单子分别是:12手空平,12手多平

当现在为止,已经将全部计算方法讲解完毕,接下来看看用Python 代码如何计算具体的开平量吧!

代码:

其中将未知的开平都用 现手来记录(这里未知的开平不在讨论范围内)。

file

file

file

        elif trade_netrue[i] == '多换':
            # 多开
            trade_buy.append(Now_hand[i] / 2)
            # 空平
            trade_buytocover.append(0 )
            trade_sellshort.append(0)
            # 多平
            trade_sell.append(Now_hand[i] / 2)
            trade_unknown_exchange.append(0)
            trade_unknown_entry.append(0)
            trade_unknown_exit.append(0)
        elif trade_netrue[i] == '双平':
            # 多开
            trade_buy.append(0)
            # 空平
            trade_buytocover.append(Now_hand[i] / 2)
            trade_sellshort.append(0)
            # 多平
            trade_sell.append(Now_hand[i] / 2)
            trade_unknown_exchange.append(0)
            trade_unknown_entry.append(0)
            trade_unknown_exit.append(0)

        elif trade_netrue[i] == '双开':
            # 多开
            trade_buy.append(Now_hand[i] / 2)
            # 空平
            trade_buytocover.append(0)
            trade_sellshort.append(Now_hand[i] / 2)
            # 多平
            trade_sell.append(0)
            trade_unknown_exchange.append(0)
            trade_unknown_entry.append(0)
            trade_unknown_exit.append(0)
        else:

            if trade_netrue[i] == '未知开':
                trade_unknown_entry.append(Now_hand[i])  # 未知开
                trade_unknown_exit.append(0)
                trade_unknown_exchange.append(0)
                trade_buy.append(0)
                trade_buytocover.append(0)
                trade_sellshort.append(0)
                trade_sell.append(0)
            elif trade_netrue[i] == '未知平':
                trade_unknown_exit.append(Now_hand[i])  #未知平
                trade_unknown_exchange.append(0)
                trade_unknown_entry.append(0)
                trade_buy.append(0)
                trade_buytocover.append(0)
                trade_sellshort.append(0)
                trade_sell.append(0)

            elif trade_netrue[i] == '未知换':
                trade_unknown_exchange.append(Now_hand[i])  # 未知换
                trade_unknown_entry.append(0)
                trade_unknown_exit.append(0)
                trade_buy.append(0)
                trade_buytocover.append(0)
                trade_sellshort.append(0)
                trade_sell.append(0)

全部代码已经展示完毕,下面Run一下看看长啥样。

file

运行结果:

file

2、数据可视化。

最后一步了,是不是很激动,别着急下面我们就看一看多空对手单到底长什么样子吧!

首先,我们先看看tick周期最原始的对手单。

file

看着没什么规律,密密麻麻的。我们放大点瞅瞅。

file

原始的图形确实很难看出规律。所以作者直接移动求和,使用df.rolling(count).mean() 求出前count个和这里的count =6000个tick。得到下图,看上去似乎有什么规律,您看出了吗?

file

再放大看看。

file

file

至此,我们已经将整个多空开平量的计算已经完整的讲解完毕了,至于如何应用在量化策略上,还需要读者自身去发现,作者也仅仅是实现计算的过程。

既然,每个tick的多空方向都算出来了,我想计算多空成本线应该不成问题,值得尝试。将其转换为分钟、小时、日线周期可能会有新发现!

文源网络,仅供学习之用,侵删。

在学习Python的道路上肯定会遇见困难,别慌,我这里有一套学习资料,包含40+本电子书,800+个教学视频,涉及Python基础、爬虫、框架、数据分析、机器学习等,不怕你学不会! shimo.im/docs/JWCghr… 《Python学习资料》

关注公众号【Python圈子】,优质文章每日送达。

file