13. Grid Trading Strategy
Reference source: docs/_joinquant_migration_source/Example_13_classic grid trading.ipynb First Markdown cell.
This strategy is a classic grid trading strategy. The strategy runs on the stock 000651.SZ Gree Electric. When the strategy runs for the first time, it buys 1000 shares and holds them. At the same time, the current buying price (rounded to 0.1 yuan) is set as the benchmark grid, and the selling grid and buying grid are calculated according to the grid size. Next, the strategy runs every 5 minutes. When the stock price touches the buying grid or selling grid, a trading signal is generated, and the grid is refreshed:
For example:
Assume that the current benchmark grid is 30 yuan, the grid size is 0.5 yuan, the selling grid is 30.5 yuan, and the buying grid is 29.5 yuan
When the price touches the selling grid of 30.5 yuan, a selling signal is generated, 200 shares are sold, and the benchmark grid is updated to 30.5 yuan. At the same time, the new selling grid of 31 yuan and the buying grid of 30 yuan are calculated
Backtesting target: 000651.SZ Gree Electric
Backtesting period: January 1, 2023 to March 1, 2023
Please note that since the strategy grid parameters will be refreshed after each trade, this strategy can only be backtested in a stepwise manner. To facilitate the generation of trading signals, the strategy uses the VS signal type
import qteasy as qt
import pandas as pd
import numpy as np
from qteasy import Parameter, StgData
class GridTrade(qt.RuleIterator):
def __init__(self):
super().__init__(
name='GridTrade',
description='网格交易策略,价格触发网格后交易并更新基准网格',
pars=[
Parameter((0.2, 2.0), name='grid_size', par_type='float', value=0.5),
Parameter((100, 1000), name='trade_batch', par_type='int', value=200),
Parameter((0.0, 10000.0), name='base_grid', par_type='float', value=0.0),
],
data_types=StgData('close', freq='5min', asset_type='ANY', window_length=60),
use_latest_data_cycle=False,
)
def realize(self):
# 读取当前保存的策略参数,首次运行时base_grid参数为0,此时买入1000股并设置当前价格为基准网格
grid_size, trade_batch, base_grid = self.get_pars('grid_size', 'trade_batch', 'base_grid')
# 读取最新价格
price = self.get_data('close_ANY_5min')[-1]
# 计算当前价格与当前网格的偏离程度,判断是否产生交易信号
if base_grid <= 0.01:
# 基准网格尚未设置,此时为首次运行,首次买入1000股并设置基准网格为当前价格(精确到0.1元)
result = float(trade_batch * 5)
base_grid = np.round(price / 0.1) * 0.1
elif price - base_grid > grid_size:
# 触及卖出网格线,产生卖出信号
result = -float(trade_batch) # 交易信号等于交易数量,必须使用VS信号类型
# 重新计算基准网格
base_grid += grid_size
elif base_grid - price > grid_size:
# 触及买入网格线,产生买入信号
result = float(trade_batch)
# 重新计算基准网格
base_grid -= grid_size
else:
result = 0.0
# 使用新的基准网格更新交易参数
self.par_values = (grid_size, trade_batch, base_grid)
return result
alpha = GridTrade()
op = qt.Operator(alpha, signal_type='VS') # 交易信号等于交易数量,必须使用VS信号类型
op.op_type = 'stepwise' # 需要动态更新策略参数,必须使用'stepwise'交易类型
res = qt.run(op, mode=1,
asset_type='E',
asset_pool='000651.SZ',
benchmark_asset='000651.SZ',
trade_batch_size=100,
sell_batch_size=1,
trade_log=True,
invest_start='20230101',
invest_end='20230301',
backtest_price_adj='none',
)
print()
The backtest results are as follows:
====================================
| |
| BACK TESTING RESULT |
| |
====================================
qteasy running mode: 1 - History back testing
time consumption for operate signal creation: 0.0 ms
time consumption for operation back looping: 839.9 ms
investment starts on 2023-01-03 09:30:00
ends on 2023-02-28 15:00:00
Total looped periods: 0.2 years.
-------------operation summary:------------
Only non-empty shares are displayed, call
"loop_result["oper_count"]" for complete operation summary
Sell Cnt Buy Cnt Total Long pct Short pct Empty pct
000651.SZ 13 8 21 93.0% 0.0% 7.0%
Total operation fee: ¥ 63.36
total investment amount: ¥ 100,000.00
final value: ¥ 104,070.64
Total return: inf%
Avg Yearly return: inf%
Skewness: 3.79
Kurtosis: 52.19
Benchmark return: 9.23%
Benchmark Yearly return: 77.74%
------strategy loop_results indicators------
alpha: inf
Beta: inf
Sharp ratio: -inf
Info ratio: 0.005
250 day volatility: 0.003
Max drawdown: 0.80%
peak / valley: 2023-01-30 / 2023-02-06
recovered on: 2023-02-20
===========END OF REPORT=============
