Article Blog Image

监控期货全市场的套利机会

实践

本文中将带领您基于DevelStudio完成一个国内期货全市场的跨期套利监控小程序,并实现日常监控报告生产。

套利,通常指在某种实物资产或金融资产拥有两个价格的情况下,以较低的价格买进,较高的价格卖出,从而获取丰厚的收益。

某支股票同时在伦敦和纽约交易所上市,同股同权,但是在纽约标价10美元,在伦敦却标价12美元,投资者就可以在纽约买进,到伦敦卖出。 又比如在货币交易上进行套利,如借入利息较低的货币为融资,买进高息货币以赚取汇差或利差,例如,日元放款利息长年维持在近乎零的水平,而南非或巴西等国因其通胀严重,利息常年维持在高水平。 如果借入日圆来购买南非币或巴西里拉,在不考虑汇差情况下,也能赚取丰厚的利差。

而跨期套利是套利策略的一种,是在同一期货品种的不同月份合约上建立数量相等、方向相反的交易头寸,最后以对冲或交割方式结束交易、获得收益的方式。 我们以中国期货市场为例,当前有几十种不同类型的合约在交易,而每种合约有多个不同交割月份的独立合约。如何能有效发现市场中可能存在的套利机会?

下面的内容中将带您通过一个Python小程序实现全市场合约的跨期套利监控。

数据准备

在编写市场监控程序之前,您需要先部署DataCenter进行数据采集,收集足够的行情数据。如果您已经完成了DataCenter的部署,可直接跳过并阅读下一小节。

启动DataCenter

$ mkdir /home/thunder-ticks
$ sudo docker run --privileged -d -p 70:80 -v /home/thunder-ticks:/thunder-ticks thundertrader/datacenter:1.81

随后我们登录DataCenter控制面板http://127.0.0.1:70并配置CTP行情采集插件,即可完成部署。地址http://127.0.0.1:70就是数据中心的地址,后面小结会用到。

具体的使用方法可以参考文档DataCenter使用文档

启动DevelStudio

$ mkdir /home/workspace
$ sudo docker --privileged -d run -p 8080:80 -p 8081:81 -p 8082:82 -v /home/workspace:/workspace thundertrader/develstudio:1.81

通过上述命令,我们启动了DevelStudio容器并分别在8080/8081/8082三个端口绑定了VSCode/Jupyter Notebook以及Nginx下载目录,并且将新建的目录/home/workspace映射到容器中的/workspace

具体的使用方法可以参考文档DevelStudio使用文档

计算价差

完整的代码您可以在https://github.com/thunder-trader/thunder-trader-101下载。

我们打开http://127.0.0.1:8080/进入VSCode(默认密码为thunderpass),在/workspace目录下新建一个名为arbitrage_plot.py的脚本文件。

首先我们定义一些程序执行的关键参数

REPORT_PATH = "/publish/future_price_diff"
DATA_CENTER = "http://192.168.3.44:70"
CONFIGURATION = """[{"enable":1,"sfit":"a"},{"enable":1,"sfit":"au"},{"enable":1,"sfit":"IF"},{"enable":1,"sfit":"ag"}]"""

其中REPORT_PATH是我们存储监控报告的地方。在上述启动命令中,DevelStudio默认会在8082端口绑定Nginx下载服务,并且将/publish目录作为根目录,所以我们只需要将生成的报告文件放置在该目录下,就可以通过http://127.0.0.1:8082/进行下载查看。

DATA_CENTER是数据中心的地址,而CONFIGURATION是期望进行计算的合约类型,您可以将较为关注的合约添加到其中。

首先我们通过read_configuration函数从配置中读取到需要计算价差的合约类型,例如上述列举的a/IF/ag/au等。对于每一个合约类型,我们需要找到该合约类型下面的当前所有有效合约。

instrument_ids = []
# Load instrument meta data from /thunder-data/instruments.conf
# This data file will be update every day from thunder-trader.com
instrument_records = pythunder.system.load_instrument_information_from_file("/thunder-data/instruments.config")
# Find all instrument names of this specified future type
pattern = "%s\\d+$" % (future_type)
for name, value in instrument_records.items():
    if (re.match(pattern, value.get_instrument_id())):
        instrument_ids.append(value.get_unique_name())

我们可以通过load_instrument_information_from_file函数载入所有合约的信息,该函数的主要原理是读取指定的合约信息文件(/thunder-data/instruments.config),并解析其中的合约信息。 DevelStudio中的合约信息文件会被日级别周期性从官网更新,确保合约信息的准备性以及实时性。 arbitrage-monitor-1.png

拿到所有具体合约名称后,我们载入对应的合约Tick行情数据。

for item in instrument_ids:
    result[item] = ptu.load_from_data_center(item, start.strftime("%Y%m%d"), end.strftime("%Y%m%d"), DATA_CENTER)

在上述的代码中,load_from_data_center函数可以实现从数据中心载入数据。 arbitrage-monitor-2.png

在所有行情数据都被载入之后,我们还无法直接计算价差以及生成报告,因为虽然所有数据都属于同一个合约类型,但是不同交割时间的合约行情数据都是独立记录的。 arbitrage-monitor-3.png 我们在计算跨期价差之前,需要首先对行情数据进行对齐(align_ticks)。同时,为了让绘制的图标更加直观,我们在1分钟内只采样一个点。align_ticks函数可以完成ticks数据相关对齐和采样:

def align_ticks(data):
    h = []
    names = set(data.keys())
    for k, v in data.items():
        for tick in v:
            heapq.heappush(h, (tick.timestamp, "%s.%s" % (tick.tick_type, tick.instrument_id), tick.last_price, tick.datetime))
    heapq.heapify(h)
    result = dict()
    timestamp = []
    window = lambda t : int(t[0] / 60000)

    while len(h) != 0:
        sample = dict()
        tick = heapq.heappop(h)
        sample[tick[1]] = tick[2]
        window_index = window(tick)
        while len(h) != 0 and window(h[0]) == window_index:
            tick = heapq.heappop(h)
            sample[tick[1]] = tick[2]
        for i in names.difference(sample.keys()):
            sample[i] = float("nan")

        for k, v in sample.items():
            if k not in result:
                result[k] = []
            result[k].append(v)
        timestamp.append(window_index * 60000)
    return result

基于已经对齐的行情数据,我们计算所有的隔月价差,比如对于RB合约,我们会计算下面的价差:

  • price diff of sfit.future.rb2505 & sfit.future.rb2506
  • price diff of sfit.future.rb2506 & sfit.future.rb2507
  • price diff of sfit.future.rb2507 & sfit.future.rb2508
  • price diff of sfit.future.rb2508 & sfit.future.rb2509
  • price diff of sfit.future.rb2509 & sfit.future.rb2510
  • price diff of sfit.future.rb2510 & sfit.future.rb2511
  • price diff of sfit.future.rb2511 & sfit.future.rb2512
  • price diff of sfit.future.rb2512 & sfit.future.rb2601
  • price diff of sfit.future.rb2601 & sfit.future.rb2602
  • price diff of sfit.future.rb2602 & sfit.future.rb2603
  • price diff of sfit.future.rb2603 & sfit.future.rb2604

完整的代码您可以在https://github.com/thunder-trader/thunder-trader-101下载。

当程序编辑完成后,我们在Terminal中运行:

python3 /workspace/arbitrage_plot.py

当程序执行完成后,我们可以在/publish文件夹下面看到所有生成的价差报告。 arbitrage-monitor-publish.png

您也可以在WEB浏览器中直接进行查看,访问http://127.0.0.1:8082/即可看到/publish文件夹下面的内容。 arbitrage-monitor-web-publish.png

定时执行

为了让我们的监控小程序能够每天执行一次,我们执行crontab -e添加一个定时任务,确保每天上午8:00程序能够自动扫描市场数据并生成最新的报告。

0 8 * * * python3 /workspace/arbitrage_plot.py 1>/dev/null 2>&1

报告分析

白银期货跨期价差。 arbitrage-monitor-ag.png 从图中可以看到白银期货的价差在几个月之内非常稳定,几乎没有大的波动,这也代表白银合约基本不存在跨期套利的机会。


螺纹钢期货跨期价差。 arbitrage-monitor-rb.png 从图中可以看到螺纹钢期货的跨期价差与合约有关,2507合约与2508合约的价差明显存在趋势,不满足套利交易的条件,而2511与2512合约的价差较为稳定,但是存在一定波动,这样的价差可能存在套利的机会。


豆二期货跨期价差 arbitrage-monitor-b.png


玉米期货跨期价差 arbitrage-monitor-c.png 豆二期货与玉米期货的价差存在明显的趋势,不适合套利策略。

总结

DevelStudio是一个开放式策略研发环境,在DevelStudio中集成了PyThunder以及多种常见的数据分析工具箱。欢迎您在www.thunder-trader.com官网下载使用。

Tags: