当前位置: 首页 > news >正文

【Mquant】6:构建价差套利(二)

文章目录

  • 1. 上节回顾
  • 2. 本节内容
  • 3. 统计套利
    • 3.1 构建均值回归策略
  • 4. 实践
    • 4.1 选择交易标的
    • 4.2 确定交易规则
    • 4.3 策略代码
    • 4.4 策略回测
  • 5.实盘交易


1. 上节回顾

【Mquant】5:构建价差套利(一)介绍了价差套利的原理和跨期套利的概念。同时,提到了价差套利存在的一定风险,并且介绍了跨期套利的原理和分类、投研分析和价差特征分析的方法。

2. 本节内容

本文将带领读者从零开始,逐步构建一个完整的价差实战套利策略。这将是一个循序渐进的过程,旨在帮助读者了解价差套利的核心概念和实施步骤。

首先重点讲解价差套利策略的构建过程。这将涉及选择适合价差套利的市场和交易品种,确定合适的套利时机和条件,并建立相应的交易规则和执行策略。使用一些常用的技术工具和指标,帮助读者进行价差分析和套利机会的识别。

在策略构建的过程中,本文将提供详细的实例和案例分析,以便读者能够更好地理解和应用所学知识。同时,作者还会强调风险管理的重要性,分享一些有效的风险控制方法,并提供实用的建议和技巧。

本文最后将引导读者进行模拟交易和实盘操作,以验证和优化所构建的价差套利策略。通过实际的交易实践,读者将能够更好地理解市场动态和实际执行的挑战,并逐步提升自身的交易技能和经验。

3. 统计套利

在统计套利中,常用的方法包括配对交易(Pairs Trading)均值回归策略(Mean Reversion)、协整关系交易(Cointegration Trading)等。这些方法基于统计学的原理,利用价格或资产之间的相对价差、均值偏离或协整关系等统计指标来确定交易信号和执行策略。

在实践中,统计套利中的某些策略可以包含价差套利的元素,例如配对交易和均值回归策略经常涉及价格差异的利用。同时,统计套利的一些方法和工具,如协整关系的分析和模型构建,也可以为价差套利提供理论支持和辅助分析。

3.1 构建均值回归策略

本文着手于构建一个均值回归策略,通过理论和实践相结合的方式,帮助读者快速构建一套属于自己的交易策略,构造一个均值回归策略涉及以下步骤:

  1. 选择资产:首先,选择您感兴趣的资产或市场,可以是股票、期货、外汇等。确保选择的资产存在明显的价格波动和均值回归的趋势。

  2. 确定均值:通过历史数据计算资产的均值。常见的方法是使用移动平均线(如简单移动平均线或指数加权移动平均线)来估计资产价格的均值。

  3. 计算偏离度:计算资产价格相对于均值的偏离度。可以使用标准差、百分位数或其他统计指标来度量价格的偏离程度。

  4. 确定交易信号:根据偏离度确定交易信号。当价格偏离均值超过一定阈值时,产生交易信号。例如,当价格偏离均值超过一个标准差时,可以认为价格过度偏离,产生反向交易信号。

  5. 确定交易规则:定义具体的交易规则,包括入场点、出场点和止损点。例如,当价格偏离均值达到一定程度时,进入反向头寸;当价格回归到均值附近时,平仓并获利。

  6. 风险管理:制定有效的风险管理策略,包括设置止损点、控制仓位大小和分散投资等。确保风险可控,并考虑交易成本和流动性等因素。

  7. 回测和优化:使用历史数据进行回测,评估策略的表现,并进行必要的优化。调整参数和交易规则,以提高策略的盈利能力和稳定性。

  8. 实盘交易:在回测和优化后,将策略应用到实盘交易中。始终密切监控市场情况和策略表现,并根据需要进行调整和优化。

4. 实践

4.1 选择交易标的

选择交易标的核心是确保选择的资产存在明显的价格波动和均值回归的趋势

  1. 使用统计指标来评估价格的波动性和均值回归的趋势。常用的指标包括标准差、平均绝对偏差(Mean Absolute Deviation)、波动率等。较高的波动性和明显的均值回归特征可能表明资产适合均值回归策略。

  2. 协整性分析:对于多个相关资产,可以进行协整性分析。协整关系是指一组资产的价格在长期内存在稳定的线性关系。如果资产之间存在协整关系,并且价格偏离协整关系时会发生均值回归,那么这些资产可能适合均值回归策略。

  3. 历史数据分析:通过对资产的历史价格数据进行分析,评估价格的波动性和均值回归的趋势。观察价格的波动范围、频率和幅度,以及价格是否有向均值回归的倾向。

在上节内容【Mquant】5:构建价差套利(一)基础上,这一章节我们对btc数据又进行了一系列的统计学分析,以说明均值回归策略适用于什么周期,用什么指标来量化。

import numpy as np
import pandas as pddf = pd.read_csv("spread_data.csv")
# 计算价格波动性指标
std = np.std(df['spread'])  # 标准差
mad = np.mean(np.abs(df['spread'] - np.mean(df['spread'])))  # 平均绝对偏差
volatility = std / np.mean(df['spread'])  # 波动率(标准差与均值的比率)# 计算均值回归指标
mean = np.mean(df['spread'])  # 均值
rolling_mean = df['spread'].rolling(window=10).mean()  # 移动平均线# 输出结果
print("价格波动性指标:")
print("标准差:", std)
print("平均绝对偏差:", mad)
print("波动率:", volatility)print("\n均值回归指标:")
print("均值:", mean)

在这里插入图片描述
当波动率大于0.337或者绝对偏差大于94.8的时候开启均值回归策略。

4.2 确定交易规则

在这里插入图片描述
确定均值,常用的方法可以是ma均线,可以是机器学习拟合过去一段时间的回归曲线,可以是布林通道。确定交易信号,均值回归策略核心是认为相同品种的价差最终会走向价值回归,那么就在价差大的时候开仓,价差小的时候平仓。

4.3 策略代码

import pandas as pd
import plotly.express as px# 读取数据,设置时间戳索引
df_data = pd.read_csv("spread_data.csv")
df_data.index = pd.DatetimeIndex(df_data["datetime"])
df = pd.DataFrame({"spread": df_data["spread"]})
df = df.resample("5min").last()
df.dropna(inplace=True)# 设置策略参数
window = 20
dev = 3
# 计算均线和上下轨
df["ma"] = df["spread"].rolling(window).mean()
df["std"] = df["spread"].rolling(window).std()
df["up"] = df["ma"] + df["std"] * dev
df["down"] = df["ma"] - df["std"] * dev# 抛弃NA数值
df.dropna(inplace=True)# 计算目标仓位
target = 0
target_data = []for ix, row in df.iterrows():# 没有仓位if not target:if row.spread >= row.up:target = -1elif row.spread <= row.down:target = 1# 多头仓位elif target > 0:if row.spread >= row.ma:target = 0# 空头仓位else:if row.spread <= row.ma:target = 0# 记录目标仓位target_data.append(target)df["target"] = target_data
# 计算仓位
df["pos"] = df["target"].shift(1)
# 计算盈亏
df["change"] = df["spread"].diff()
df["pnl"] = df["change"] * df["pos"]
df["balance"] = df["pnl"].cumsum()# 绘制净值曲线
px.line(df["balance"])

在这里插入图片描述

从目前回测曲线上来看,这个策略表现还不错,但是没有计算手续费和滑点,为了更精准的回测出我们构建的策略表现到底如何,下面采用veighna自带的价差交易回测引擎来进行回测。

4.4 策略回测

  1. 在vnpy_spreadtrading(位置位于:External Libraries\site-packages)目录下的strategies下创建boll_spread_strategy.py策略文件
    在这里插入图片描述
from vnpy.trader.utility import BarGenerator, ArrayManager
from vnpy_spreadtrading import (SpreadStrategyTemplate,SpreadAlgoTemplate,SpreadData,OrderData,TradeData,TickData,BarData
)
from vnpy.trader.constant import Intervalclass BollSpreadStrategy(SpreadStrategyTemplate):""""""author = "mossloo"ma_window = 20ma_dev = 3max_pos = 1payup = 0.001interval = 5spread_pos = 0.0ma_up = 0.0ma_down = 0.0ma = 0.0parameters = ["ma_window","ma_dev","max_pos","payup","interval"]variables = ["spread_pos","ma_up","ma_down","ma"]def __init__(self,strategy_engine,strategy_name: str,spread: SpreadData,setting: dict):""""""super().__init__(strategy_engine, strategy_name, spread, setting)self.bg = BarGenerator(self.on_spread_bar, window=5, on_window_bar=self.on_5min_spread_bar,interval=Interval.MINUTE)self.am = ArrayManager()def on_init(self):"""Callback when strategy is inited."""self.write_log("策略初始化")self.load_bar(10)def on_start(self):"""Callback when strategy is started."""self.write_log("策略启动")def on_stop(self):"""Callback when strategy is stopped."""self.write_log("策略停止")self.put_event()def on_spread_data(self):"""Callback when spread price is updated."""tick = self.get_spread_tick()self.on_spread_tick(tick)def on_spread_tick(self, tick: TickData):"""Callback when new spread tick data is generated."""self.bg.update_tick(tick)def on_spread_bar(self, bar: BarData):self.bg.update_bar(bar)def on_5min_spread_bar(self, bar: BarData):"""Callback when spread bar data is generated."""self.stop_all_algos()self.am.update_bar(bar)if not self.am.inited:returnself.ma = self.am.sma(self.ma_window)dev = self.am.std(self.ma_window)self.ma_up = self.ma_dev * dev + self.maself.ma_down = self.ma - self.ma_dev * devif not self.spread_pos:if bar.close_price >= self.ma_up:self.start_short_algo(bar.close_price - 10,self.max_pos,payup=self.payup,interval=self.interval)elif bar.close_price <= self.ma_down:self.start_long_algo(bar.close_price + 10,self.max_pos,payup=self.payup,interval=self.interval)elif self.spread_pos < 0:if bar.close_price <= self.ma:self.start_long_algo(bar.close_price + 10,abs(self.spread_pos),payup=self.payup,interval=self.interval)else:if bar.close_price >= self.ma:self.start_short_algo(bar.close_price - 10,abs(self.spread_pos),payup=self.payup,interval=self.interval)self.put_event()def on_spread_pos(self):"""Callback when spread position is updated."""self.spread_pos = self.get_spread_pos()self.put_event()def on_spread_algo(self, algo: SpreadAlgoTemplate):"""Callback when algo status is updated."""passdef on_order(self, order: OrderData):"""Callback when order status is updated."""passdef on_trade(self, trade: TradeData):"""Callback when new trade data is received."""passdef stop_open_algos(self):""""""if self.buy_algoid:self.stop_algo(self.buy_algoid)if self.short_algoid:self.stop_algo(self.short_algoid)def stop_close_algos(self):""""""if self.sell_algoid:self.stop_algo(self.sell_algoid)if self.cover_algoid:self.stop_algo(self.cover_algoid)
  1. 继续打开jupyter notebook
from vnpy.trader.optimize import OptimizationSetting
from vnpy_spreadtrading.backtesting import BacktestingEngine
from vnpy_spreadtrading.strategies.boll_spread_strategy import (BollSpreadStrategy
)
from vnpy_spreadtrading.base import LegData, SpreadData
from datetime import datetime
from vnpy.trader.constant import Intervalsymbol_1 = "BTCUSDT_240329.BINANCE"
symbol_2 = "BTCUSDT_231229.BINANCE"spread = SpreadData(name="BTC-Spread",legs=[LegData(symbol_1), LegData(symbol_2)],variable_symbols={"A": symbol_1, "B": symbol_2},variable_directions={"A": 1, "B": -1},price_formula="A-B",trading_multipliers={symbol_1: 1, symbol_2: 1},active_symbol=symbol_1,min_volume=1,compile_formula=False                          # 回测时不编译公式,compile_formula传False,从而支持多进程优化
)engine = BacktestingEngine()
engine.set_parameters(spread=spread,interval=Interval.MINUTE,start=datetime(2021, 6, 10),end=datetime(2023, 11, 7),rate=0.0004,slippage=0.02,size=1,pricetick=0.02,capital=1_000_000,
)engine.add_strategy(BollSpreadStrategy, {})
engine.load_data()
engine.run_backtesting()
df = engine.calculate_result()
engine.calculate_statistics()
engine.show_chart()

在这里插入图片描述
在这里插入图片描述
对于这个策略而言,我们亏钱的原因在于手续费太高了,由于我们策略使用的是市价单,加密市场对于市价单收取的手续费要远远高于限价单,所以这个策略的手续费在万分之四左右,如果能手续费降低到万分之一,加上百分之五十的返佣,到万分之0.5,那么这个策略的夏普比能到7.83。
在这里插入图片描述
策略还可以进行参数优化,找到参数平原地带,后续选择较优的参数跑模拟盘验证。

setting = OptimizationSetting()
setting.set_target("sharpe_ratio")
setting.add_parameter("ma_window", 10, 30, 1)
setting.add_parameter("ma_dev", 1, 3, 1)engine.run_ga_optimization(setting)

5.实盘交易

很显然,我们上面的程序并不能直接上实盘交易,但如果我们手续费非常低的情况下如万分之一,或者遇到极端行情,某些币种现货和期货之间的价差非常大,比如前段时间的luna,还有这段时间的mask,只要我们设置好程序,仍然可以在极端行情下赚取到稳定的资金,这也是我们学习量化交易的原因,只有长久稳定的赚钱,才能在市场立于不败之地。下一章节,我将带领大家优化策略,回测,实盘部署。

相关文章:

  • .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
  • 活用package.json脚本,用node拷贝文件到指定目录
  • AR眼镜硬件解决方案_AR/VR智能眼镜安卓主板芯片方案介绍
  • 计算机毕设 基于大数据的服务器数据分析与可视化系统 -python 可视化 大数据
  • 【数据结构】树与二叉树(四):满二叉树、完全二叉树及其性质
  • YOLOv5算法改进(22)— 更换主干网络MobileNetv3 + 添加CA注意力机制
  • KiKi知道了什么是质数,他现在想知道所有三位整数中,有多少个质数
  • viple进阶2:打印九九乘法表
  • SLAM从入门到精通(被忽视的基础图像处理)
  • STM32笔记—DMA
  • 2023年十大地推拉新接单平台和网推接单平台,都是一手单
  • mac电脑系统清理软件CleanMyMac X2024破解版下载
  • MySQL的备份恢复
  • Flink SQL DataGen Connector 示例
  • Git使用规范指南
  • 分享一款快速APP功能测试工具
  • 【comparator, comparable】小总结
  • 【跃迁之路】【735天】程序员高效学习方法论探索系列(实验阶段492-2019.2.25)...
  • ECMAScript入门(七)--Module语法
  • Git初体验
  • Java 实战开发之spring、logback配置及chrome开发神器(六)
  • MYSQL 的 IF 函数
  • npx命令介绍
  • Python连接Oracle
  • react-native 安卓真机环境搭建
  • Redash本地开发环境搭建
  • Sublime text 3 3103 注册码
  • Vue 动态创建 component
  • 创建一个Struts2项目maven 方式
  • 分享一份非常强势的Android面试题
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 关于List、List?、ListObject的区别
  • 基于Vue2全家桶的移动端AppDEMO实现
  • 前端技术周刊 2019-02-11 Serverless
  • 实习面试笔记
  • 使用parted解决大于2T的磁盘分区
  • 新书推荐|Windows黑客编程技术详解
  • 用jQuery怎么做到前后端分离
  • 最简单的无缝轮播
  • gunicorn工作原理
  • scrapy中间件源码分析及常用中间件大全
  • ​linux启动进程的方式
  • ​一些不规范的GTID使用场景
  • # Panda3d 碰撞检测系统介绍
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (蓝桥杯每日一题)love
  • (论文阅读23/100)Hierarchical Convolutional Features for Visual Tracking
  • (十八)用JAVA编写MP3解码器——迷你播放器
  • (原創) 如何使用ISO C++讀寫BMP圖檔? (C/C++) (Image Processing)
  • (转)linux 命令大全
  • (转载)微软数据挖掘算法:Microsoft 时序算法(5)
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • (最简单,详细,直接上手)uniapp/vue中英文多语言切换
  • ***测试-HTTP方法