如何设置交易滑点?精确到 tick 测算期货冲击成本(附源码)

分享一篇不错的文章:

我们在非撮合回测模式下,因为无法获知交易价格当时的真实盘口价差、挂单数量,常主观设定一个滑点均值,比如针对螺纹钢等合约,设置 1 跳,针对某些交易不活跃的品种,设置 2 跳。

但是这种近乎拍脑袋的方法并不精确。我们今天尝试通过简单的辅助工具,实现尽可能接近准确的 tick 级别滑点设置,代码已写好,不用编程也可获得结果

“过价成交”与“撮合成交”

主流程序化交易软件如 TB、文华财经、MC 等,提供了“过价成交”这种限价单来模拟历史交易,只要价格在近日 k 线区间以内,都模拟本次交易成交。这种方法效率高,回测简便,可以实现大周期 k 线频率下的 bar 内交易。

null

小时线上,一笔空单盈利,平仓点位在日内某点“过价成交”

一些精确的回测系统,如某些交易者自己开发的系统可以实现 tick 级别撮合回测,但是速度极慢,如果进行多组回测(如参数优化场景)难以应对。

null

MT5 等软件提供的市场深度界面,可以看到每个价位挂单深度

所以两种模式都有优劣,针对不同场景应该有不同选择侧重。

通过调取 tick 价格,获知盘口真实情况

我们的计算需要获取大量 tick 数据,才能满足最近一个阶段,比如 1 年的滑点细节,所以 tb 等软件提供的 tick 数据并不够用。较为稳妥的方案是,通过聚宽获取免费数据,并在其在线式研究平台 python 环境下,不用开发模型,直接分析数据。

null

聚宽改版了,logo 改出了强迫症的味道

以聚宽为例,获取 tick 数据的核心函数是:

get_ticks(security, end_dt, start_dt=None, count=None, fields=['time', 'current', 'high', 'low', 'volume', 'money']),期货部分, 支持 2010-01-01 至今的 tick 数据,提供买一卖一数据,这数据量基本足够用。

我们看到该函数需要传入的参数有:

security合约代码

end_dt最终时间

start_dt起始时间

count数量

fields获取字段,期货中我们一般获取 ['time','a1_p','b1_p','current'] 字段,分别是时间、ask 卖出报价、bid 买入报价、current 当前最新价

针对部分编程爱好者,我们简单解读程序,如果没兴趣也可直接跳过本段。首先针对一次获取多个合约的需求,我们定义的函数,应该允许在 security 合约代码字段传输多个品种,所以函数也针对 security 字段来建立一个 for 循环。

null

Python 代码实际上也很简洁

循环内获得交易时间日期、获得该品种 minpoint(最小波动量,以便统一结果量刚为跳数)。通过 get_ticks 函数获得每日价格,计算得到 ['a1_p'] 减去 ['b1_p'] 这个字段,这就是每天的 tick 级别买卖价差。

紧接着计算一个每日均值:spread_mean,这样我们获得了每个品种每日滑点, dict 套 dict 数据格式。为了让你不在聚宽购买研究内存,我们做了内存清理。

考虑到某些 tick 错误数据,可能导致 tick 级别买卖价差过大,我们在最终绘图的 dataframe 中做了过滤,大于 10 跳的部分都删除,小于 0 跳的部分也删除(理论上 ask 一定高于 bid,所以不应该出现负值)。

**future_basic_info ** 函数仅用于获取合约单位,报价单位,和 MinPoint 最小变动价格,这是一个将聚源数据库封装接口后的函数,原接口非常难用,所以封装一遍,这个函数在期货模型里也可以用。

不同板块品种滑点初探

运行时首先要传入品种简称,如这样,我们传入黑色系的几个品种:

test_ins_list = ['RB','HC','I','J','ZC']

然后将这个品种 list,传入主函数 cal_mean_spread,并传入要查询的截至日期,和日期长度(如这里查询截止 3 月 8 日数据,过去 220 天):

cal_mean_spread(test_ins_list,'2019-03-08',220)

null

结果如下:

我们看到,黑色系品种,铁矿石滑点最为稳定,螺纹钢其次,他们的买卖价差均值都在 1 左右。焦炭可能由于波动量大,某些时候,可以达到较高的滑点,但是仔细观察坐标轴,全日均值的极值点,还是在 1.5 以内,并没有很大的冲击成本。

null

有色金属中,铜铝锌稳定在 1 跳左右,这些品种虽然趋势不好做,但是可能套保和套利盘较多,流动性不成本问题。pb 铅较高,依然稳定在 1.5 以内,ni 镍常会冲击到 2 个滑点,毕竟其价格高,虽然趋势活跃,但是接近 10 万元的价格产生几个 10 元的价差,还是很合理的。sn 锡比较夸张了,4~5 个滑点是常有的,也是我亏钱最多的品种,值得纪念。

null

农产品的稳定性超乎我们之前的偏见,虽然趋势不强,但是交易活跃,盘口流动性并不成问题,没有出现显著断档,本次测试的大部分品种都在 1 跳左右,2 跳以内的滑点。这应该也是套利和做市商的功劳,特别是后者提供连续流动性更为重要。

null

化工品中,除除了 PP 表现活跃,其他几位同学还凑合。滑点基本在 1.2 个以内,所以还是那句老话,其实商品期货的滑点并不是很高,尤其是每日平均滑点。虽然我常和朋友开玩笑说这里“池浅王八多”,但毕竟 40 个品种分享 5000 亿保证金,加上每年 200 万亿的交易额(2018 年数据),流动性还是充裕的。

null

金融期货情况有些不可控,股指期货被阉割后,流动性有时候成问题,这也是很多日内模型无法运转的原因,我们观察发现 IC 流动性长期不足,滑点较大。在股指期货逐步放开后,可以控制在平均 4 跳,而之前则有可能到 8~10 跳。IF 和 IH 可以控制在平均 2~3 跳,之前也要 4 跳左右。反而是国债期货的流动性很棒,可以控制在 2 跳之内。

null

但是这里提醒各位读者:不同的价格,跳数含义完全不同。高价品种出现较高跳数完全合理,其本质上对应的冲击成本价值,和低价成本出现 1 到 2 跳效果类似。比如在 NI 镍品种设置 5 跳的一个日内模型表现尚可,在 I 铁矿上如果加到 2 跳滑点,就会严重折损效果,因为铁矿 1 跳对应 50 元,镍 1 跳对应 10 元。

多头滑点和空头滑点

多头和空头忍受的滑点是不同的,我们在行情软件上,看到的价格实际上是本文的 get_tick 函数提取到的 current 价格,而你的模型一般会按照此价格计算,如果达到条件,就报单,紧接着就是判断是否成交的过程,报单价格太低肯定不行,先价格优先,还要时间优先。所以报单价格要足够高(买入为例)。

我们深入到股指期货内部,分别使用 ['a1_p'] - ['current'] 计算买入滑点,使用 ['current'] - ['b1_p'] 计算卖出滑点,得到结果如下:

null

null

蓝线是中间价格(最新价格),黄线是滑点,绿色横线是日内滑点均值。

我们选择了 2 月 25 日这个大涨日,测试 IF 主力合约的滑点情况,该函数深入到每日内,可以看到情况依然乐观,充裕的流动性让买卖滑点大部分集中在 -5 跳到 +10 跳区间内,但是在快速变动的行情中,滑点也快速增加到 10 跳以上,也有可能是我们的行情并非 tick 而是每秒 2 个快照导致的,此时追价交易就有点可怕了。

空头滑点比多头略小,毕竟是大涨行情中,做空容易,但是差异很小,在极端变化时,滑点依然被拉的比较大,需要警惕。我们提醒大家一些日内突破模型,在某些关键点位上,滑点会比你想象中更大,而在其他价位上,滑点又很小,甚至吃到负滑点。

文件已经保存成为一个 **《滑点测试——powered by 量化投资训练营.ipynb》python 代码记事本,传到聚宽研究平台 **,即可运行。感谢南开大学刘健涛同学完成了其中主要代码设计。本次我们没有分析订单深度,你可以在程序上改版获得。

链接:

https://pan.baidu.com/s/1Rp5UomHbxQNrcj4ps4JjfA

提取码:

dm01