网格交易Python实现
网格交易涉及在价格图表上创建一个值网格,当价格穿过网格线时同时开立多头仓位和空头仓位。本文介绍在 Python 中实现网格策略并在历史数据上对其进行回测。
一键发币: SOL | BNB | ETH | BASE | Blast | ARB | OP | POLYGON | AVAX | FTM | OK
本文将探讨网格交易策略——一种简单而有效的方法,不依赖技术指标来确定趋势。该策略是算法交易的绝佳候选者,我们使用 Python 进行的回测表明,在 5 分钟的时间范围内,两个月的数据中权益稳步增长。结果出奇地好,Sharpe比率接近 5.7。
什么是网格交易
网格交易(grid trading)涉及在价格图表上创建一个值网格(value grid)。每当价格穿过网格线时,我们都会同时开立多头仓位和空头仓位。每个开仓的止盈都设置在下一个网格线上。这种策略在价格频繁波动的市场中蓬勃发展,特别适合较短的时间范围。
现在我们将在 Python 中实现网格策略并在历史数据上对其进行回测。
1、设置环境
首先,我们需要通过导入必要的库和下载数据集来设置我们的 Python 环境。我们将使用 yfinance 获取欧元/美元汇率数据,使用 pandas 进行数据处理,使用 numpy 进行数值运算,使用 pandas_ta 进行技术分析指标。
import yfinance as yf
import pandas as pd
import numpy as np
import pandas_ta as ta
# Download the EUR/USD data for the last 59 days with a 5-minute interval
dataF = yf.download("EURUSD=X", start=pd.Timestamp.today() - pd.DateOffset(days=59), end=pd.Timestamp.today(), interval='5m')
导入库:
yfinance (yf)
:一个用于从 Yahoo Finance 下载历史市场数据的 Python 库。pandas (pd)
:一个强大的数据操作和分析库。numpy (np)
:一个用于数值运算的库。pandas_ta (ta)
:一个用于金融数据集的技术分析库。
下载数据:
我们使用 yfinance 下载 2022 年 11 月 19 日至 2023 年 1 月 16 日的 EUR/USD 汇率数据,间隔为 5 分钟。这为我们提供了适合我们的网格交易策略的详细且高频的数据集。
2、创建网格
在此步骤中,我们创建一个值网格,它将确定我们的入场点和出场点。网格是价格图上以规则间隔排列的一系列水平线。每当价格穿过网格线时,我们就会开立新仓位。
grid_distance = 0.005
midprice = 1.065
def generate_grid(midprice, grid_distance, grid_range):
return np.arange(midprice - grid_range, midprice + grid_range, grid_distance)
grid = generate_grid(midprice=midprice, grid_distance=grid_distance, grid_range=0.1)
定义参数:
grid_distance = 0.005
:这定义了每条网格线之间的距离。它代表我们将开立新头寸的价格增量。midprice = 1.065
:这是创建网格的中心价格。它可以设置为当前市场价格或任何其他参考价格。
重要提示:这些值必须根据市场、交易资产和当前分析进行更改,因此每次您从 Yfinance 查询数据时,数据都会更新,并且此策略中的这些参数以及其他参数必须相应更新。
生成网格:
generate_grid
函数创建一系列值,从 midprice - grid_range 到 midprice + grid_range,间隔为 grid_distance。grid_range = 0.1
:这决定了中间价格上方和下方网格的总范围。
创建网格:
grid
数组包含我们将考虑开仓的所有价格水平。这构成了我们的网格交易策略的支柱,使我们能够随着价格变动系统地进入和退出交易。
3、生成交易信号
在此步骤中,我们根据创建的网格生成交易信号。每当价格穿过网格线时,就会生成交易信号。这是通过遍历数据集中的每一行并检查该间隔的最高价或最低价是否穿过任何网格线来完成的。
signal = [0] * len(dataF)
i = 0
for index, row in dataF.iterrows():
for p in grid:
if min(row.Low, row.High) < p and max(row.Low, row.High) > p:
signal[i] = 1
i += 1
dataF["signal"] = signal
dataF[dataF["signal"] == 1]
初始化信号:
signal = [0] * len(dataF)
:我们创建一个长度与数据集相同的零列表。此列表将存储我们的交易信号。
迭代数据:
我们使用 dataF.iterrows()
迭代数据集中的每一行。
对于每一行,我们检查价格是否与任何网格线相交。这是通过将行的最高价和最低价的最小值和最大值与每条网格线进行比较来完成的。
生成信号:
如果网格线相交(即最低价格低于网格线,最高价格高于网格线),我们将信号列表中的相应条目设置为 1。
这表明在该时间间隔生成了交易信号。
将信号添加到 DataFrame:
dataF["signal"] = signal
:我们将信号列表作为新列添加到 DataFrame。dataF[dataF["signal"] == 1]
:这会过滤 DataFrame 以仅显示生成交易信号的行。
通过生成这些交易信号,我们可以确定价格穿过网格线的确切时间点,从而指示潜在的交易入场。这种系统化方法可确保我们捕获定义网格内的所有相关价格变动。
4、准备回测
在此步骤中,我们通过计算平均真实波幅 (ATR) 和定义信号函数来准备回测数据集。ATR 是一种波动性指标,可帮助我们设置动态止损和止盈水平。
dfpl = dataF[:].copy()
def SIGNAL():
return dfpl.signal
dfpl['ATR'] = ta.atr(high=dfpl.High, low=dfpl.Low, close=dfpl.Close, length=16)
dfpl.dropna(inplace=True)
复制 DataFrame:
dfpl = dataF[:].copy()
:我们创建原始数据集的副本,以避免在回测过程中直接修改它。这确保我们的原始数据保持完整。
定义信号函数:
def SIGNAL()
:此函数从我们的 DataFrame 返回信号列。它将在回测期间用于访问生成的交易信号。
计算平均真实范围 (ATR):
dfpl['ATR'] = ta.atr(high=dfpl.High, low=dfpl.Low, close=dfpl.Close, length=16)
:我们使用 16 个间隔的周期计算 ATR。ATR 是市场波动性的衡量标准,有助于设置适当的止损和止盈水平。
high=dfpl.High, low=dfpl.Low, close=dfpl.Close
:这些参数指定用于计算 ATR 的最高价、最低价和收盘价。
清理数据:
dfpl.dropna(inplace=True)
:我们删除任何有缺失值的行,以确保回测过程的完整性。这一步对于准确可靠的回测结果至关重要。
ATR 提供了波动性的动态衡量标准。通过计算 ATR 和定义信号函数,我们为回测框架配备了必要的工具来评估网格交易策略的性能。
5、实施和运行回测
在此步骤中,我们使用回测库实施交易策略并运行回测以评估其性能。我们定义自定义策略类并配置回测参数。
from backtesting import Strategy
from backtesting import Backtest
import backtesting
class MyStrat(Strategy):
mysize = 50
def init(self):
super().init()
self.signal1 = self.I(SIGNAL)
def next(self):
super().next()
slatr = 1.5 * grid_distance # Stop loss distance
TPSLRatio = 0.5 # Take profit to stop loss ratio
if self.signal1 == 1 and len(self.trades) <= 10000:
# Sell position
sl1 = self.data.Close[-1] + slatr
tp1 = self.data.Close[-1] - slatr * TPSLRatio
self.sell(sl=sl1, tp=tp1, size=self.mysize)
# Buy position
sl1 = self.data.Close[-1] - slatr
tp1 = self.data.Close[-1] + slatr * TPSLRatio
self.buy(sl=sl1, tp=tp1, size=self.mysize)
# Running the backtest
bt = Backtest(dfpl, MyStrat, cash=50, margin=1/100, hedging=True, exclusive_orders=False)
stat = bt.run()
stat
导入库:
from backtesting import Strategy, Backtest
:从回测库导入必要的类来定义和运行我们的策略。import backtesting
:导入回测库。
定义策略类:
class MyStrat(Strategy)
:我们通过继承策略类来创建自定义策略类。mysize = 50
:定义每笔交易的头寸规模。
初始化:
def init(self)
:初始化策略。self.signal1 = self.I(SIGNAL)
:使用之前定义的 SIGNAL 函数访问交易信号。
定义交易逻辑:
def next(self)
:定义执行交易的逻辑。slatr = 1.5 * grid_distance
:根据网格距离计算止损距离。TPSLRatio = 0.5
:设置止盈止损比率。
执行交易:
如果生成信号 `(self.signal1 == 1)`且交易数量小于或等于 10,000:
卖出仓位:
sl1 = self.data.Close[-1] + slatr
:将止损设置在当前收盘价上方。tp1 = self.data.Close[-1] - slatr * TPSLRatio
:将止盈设置在当前收盘价下方。self.sell(sl=sl1, tp=tp1, size=self.mysize)
:执行卖单。
买入仓位:
sl1 = self.data.Close[-1] - slatr
:将止损设置在当前收盘价下方。tp1 = self.data.Close[-1] + slatr * TPSLRatio
:将止盈设置在当前收盘价上方。self.buy(sl=sl1, tp=tp1, size=self.mysize)
: 执行买入订单。
运行回测:
bt = Backtest(dfpl, MyStrat, cash=50, margin=1/100, hedging=True, exclusive_orders=False)
: 配置回测参数。cash=50
: 初始现金余额。margin=1/100
: 杠杆率。hedging=True
: 允许对冲。exclusive_orders=False
: 允许重叠订单。stat = bt.run()
: 运行回测并存储统计数据。
因此,通过此回测运行,我们可以评估网格交易策略的表现。 stat 对象包含详细的指标和见解,可帮助我们了解策略的有效性。
6、分析回测结果
:该策略经过 57 天 21 小时的测试,提供了全面的评估期。曝光时间:99.14%,表明该策略几乎一直在市场中。
绩效指标:
- 最终权益:136.02 美元,从 50 美元开始。
- 权益峰值:156.59 美元,显示出更高的收益潜力。
- 回报:172.04%,反映出可观的盈利能力。
- 买入并持有回报:4.23%,表明该策略的卓越表现。
- 年化回报率:37364.62%,由于回测时间短而极高。
- 年化波动率:46309.60%,表明风险高且交易频繁。
风险调整指标:
- 夏普比率:0.81,表明风险调整后的回报适中。
- 索提诺比率:1089.28,表明下行风险管理出色。
- 卡尔玛比率:2194.58,反映相对于最大回撤的高收益。
回撤:
- 最大回撤:-17.03%,跌幅较大但可控。
- 平均回撤:-1.16%,相对较低。
- 最大回撤持续时间:近10天,显示最长的恢复期。
- 平均回撤持续时间:不到7小时,表明恢复很快。
交易活动:
- 交易次数:1698,由于采用网格交易策略,交易频率较高。
- 胜率:73.03%,成功率非常高。
- 最佳交易:0.87%,持续小幅盈利。
- 最差交易:-0.85%,控制损失。
- 平均交易:0.10%,反映许多小额交易的累积效应。
原文链接:Grid Trading with Python: A Simple and Profitable Algorithmic Strategy
DefiPlot翻译整理,转载请标明出处