策略研发过程中,除了纯粹的买卖技术指标之外,还有一类信号用于判断当前行情是否应该入场。
比如:大盘或个股持续处于波动性中,没有明显方向性,拉锯状态,此时大多数趋势类策略都会反复开仓,止损,开仓,止损,导致稳定的小额亏损。
01,基础配置信息
#conda envs:vectorbt_envimport warningsimport vectorbt as vbtimport numpy as npimport pandas as pdfrom datetime import datetime, timedeltaimport pytzfrom dateutil.parser import parseimport ipywidgets as widgetsfrom copy import deepcopyfrom tqdm import tqdmimport imageiofrom IPython import displayimport plotly.graph_objects as goimport itertoolsimport dateparserimport gcimport mathfrom tools import dbtools
warnings.filterwarnings("ignore")
pd.set_option('display.max_rows',500)pd.set_option('display.max_columns',500)pd.set_option('display.width',1000)02,行情获取和可视化
a,时间交易参数配置
# Enter your parameters hereseed = 42symbol = '002594.XSHE'metric = 'total_return'
start_date = datetime(2020, 1, 1, tzinfo=pytz.utc) # time period for analysis, must be timezone-awareend_date = datetime(2023,1,1, tzinfo=pytz.utc)time_buffer = timedelta(days=100) # buffer before to pre-calculate SMA/EMA, best to set to max windowfreq = '1D'
vbt.settings.portfolio['init_cash'] = 10000. # 100$vbt.settings.portfolio['fees'] = 0.0025 # 0.25%vbt.settings.portfolio['slippage'] = 0.0025 # 0.25%b,获取行情和行情mask
# Download data with time buffercols = ['Open', 'High', 'Low', 'Close', 'Volume']# ohlcv_wbuf = vbt.YFData.download(symbol, start=start_date-time_buffer, end=end_date).get(cols)
ohlcv_wbuf=dbtools.MySQLData.download(symbol).get() # 自带工具类查询assert(~ohlcv_wbuf.empty)ohlcv_wbuf = ohlcv_wbuf.astype(np.float64)
print("ohlcv_wbuf.shape:",ohlcv_wbuf.shape)print("ohlcv_wbuf.columns:",ohlcv_wbuf.columns)
# Create a copy of data without time bufferwobuf_mask = (ohlcv_wbuf.index >= start_date) & (ohlcv_wbuf.index <= end_date) # mask without buffer
ohlcv = ohlcv_wbuf.loc[wobuf_mask, :]
print("ohlcv.shape:",ohlcv.shape)
# Plot the OHLC dataohlcv.vbt.ohlcv.plot().show_svg() # 绘制蜡烛图# remove show_svg() to display interactive chart!ohlcv_wbuf.shape: (978, 5)ohlcv_wbuf.columns: Index(['Open', 'High', 'Low', 'Close', 'Volume'], dtype='object')ohlcv.shape: (728, 5)
03,朴素std,stdma
最简单的过滤方法是计算std,然后再计算标准差的ma,当std>ma(std),说明波动增大,反之波动减小。趋势策略当std>ma(std)时才开仓
大致效果如下
import plotly.graph_objects as gofrom plotly.subplots import make_subplots
# 计算收盘价的标准差std_close_wbuf = ohlcv_wbuf['Close'].rolling(window=20).std()# 计算标准差的移动平均std_close_ma_wbuf = std_close_wbuf.rolling(window=20).mean()
std_close=std_close_wbuf[wobuf_mask]std_close_ma=std_close_ma_wbuf[wobuf_mask]
# 创建一个子图布局:2行1列fig = make_subplots(rows=4, cols=1, shared_xaxes=True, vertical_spacing=0.02)
# 第一行添加蜡烛图ohlcv_fig = ohlcv[['Open', 'High', 'Low', 'Close', 'Volume']].vbt.ohlcv.plot()fig.add_trace(ohlcv_fig.data[0], row=1, col=1) # 添加蜡烛图数据fig.add_trace(ohlcv_fig.data[1], row=2, col=1) # 添加交易量柱状图
# 第二行添加两个移动平均线fig.add_trace(go.Scatter(x=ohlcv.index, y=std_close, mode='lines', name='SMA 20'), row=3, col=1)fig.add_trace(go.Scatter(x=ohlcv.index, y=std_close_ma, mode='lines', name='SMA 50'), row=3, col=1)
# 创建条件指标std_indicator = (std_close > std_close_ma ).astype(int)# 添加条件指标图fig.add_trace(go.Scatter(x=std_indicator.index, y=std_indicator, mode='lines', name='STD Condition', fill='tozeroy'), row=4, col=1)
# 更新图表的布局设置fig.update_layout(height=600, width=800, title_text="蜡烛图与移动平均线")fig.show()
最左侧的过滤效果并不佳,最左侧虽然满足过滤条件,但是图示上看,价格波动其实很小。
04,自适应的std,stdma(波动率的top30%过滤),
在基础std,stdma基础上,增加动态过滤功能,通过设置一个合适的gateway_ma
定义:std_close > std_close_ma 且 std_close_ma > gateway_ma 为有效(意思是通过过滤条件)
那么有效数据在 整个数据比例,称为”有效” 率。
如果不考虑,std_close_ma_wbuf > gateway_ma
显然 “有效”率 是一个固定的数字。比如40%。
而实际我们希望有效率降低到目标有效率,target_ratio,比如30%,意味着要降低10%的有效率。
此时就要通过提高gateway_ma,来让有效的不在有效。从而丢弃部分std_close_ma较小的时间区间,保留波动性相对较大的部分。

05,价格变动的一致性(动量)
除了考虑波动性,还需要考虑价格的变动是否有一致的方向。单纯的波动性增加,但价格突上突下无法形成显著方向,依然不行,所以期望过滤掉价格无方向的时间区间。
可以考虑如下的思路
20diff: 比如,t->T-20,t-1->T-21,等小柱子,计算小柱子,均值方差等状态。
结论:效果尚可。
# 计算收盘价的标准差diff_close_wbuf = ohlcv_wbuf['Close'].diff(periods=20)# 计算标准差的移动平均diff_close_ma_wbuf = diff_close_wbuf.rolling(window=20).mean()
diff_close=diff_close_wbuf[wobuf_mask]diff_close_ma=diff_close_ma_wbuf[wobuf_mask]
# 创建一个子图布局:2行1列fig = make_subplots(rows=4, cols=1, shared_xaxes=True, vertical_spacing=0.02)
# 第一行添加蜡烛图ohlcv_fig = ohlcv[['Open', 'High', 'Low', 'Close', 'Volume']].vbt.ohlcv.plot()fig.add_trace(ohlcv_fig.data[0], row=1, col=1) # 添加蜡烛图数据fig.add_trace(ohlcv_fig.data[1], row=2, col=1) # 添加交易量柱状图
# 第二行添加两个移动平均线fig.add_trace(go.Scatter(x=ohlcv.index, y=diff_close, mode='lines', name='SMA 20'), row=3, col=1)fig.add_trace(go.Scatter(x=ohlcv.index, y=diff_close_ma, mode='lines', name='SMA 50'), row=3, col=1)
# 创建条件指标diff_indicator = ((diff_close > diff_close_ma )&(diff_close_ma>200*0.05)).astype(int) #20日达到5% 认为趋势性才足够大# 添加条件指标图fig.add_trace(go.Scatter(x=diff_indicator.index, y=diff_indicator, mode='lines', name='STD Condition', fill='tozeroy'), row=4, col=1)
# 更新图表的布局设置fig.update_layout(height=600, width=800, title_text="蜡烛图与移动平均线")fig.show()
06,价格变动的一致性,优化,过去值平滑处理,T-20用ma(T,20/4=5)代替
缺点t->T-20,取值也取决于T-20,所以t->T-20,的过去价格T-20,用ma后的价格替代,等于做了平滑。
结论:噪点变少,
另一种变体,重新定义均线为
T-ma(T)[T-20]

07,结合上面2指标,对比同列
std波动性,diff20趋势性
import plotly.graph_objects as gofrom plotly.subplots import make_subplots
# 计算收盘价的标准差std_close_wbuf = ohlcv_wbuf['Close'].rolling(window=20).std()# 计算标准差的移动平均std_close_ma_wbuf = std_close_wbuf.rolling(window=20).mean()
std_close=std_close_wbuf[wobuf_mask]std_close_ma=std_close_ma_wbuf[wobuf_mask]
# 计算收盘价diffdiff_close_wbuf = ohlcv_wbuf['Close'] - ohlcv_wbuf['Close'].rolling(window=int(20/5)).mean().shift(20)# 计算收盘价diff的移动平均diff_close_ma_wbuf = diff_close_wbuf.rolling(window=20).mean()
diff_close=diff_close_wbuf[wobuf_mask]diff_close_ma=diff_close_ma_wbuf[wobuf_mask]
# 创建一个子图布局:2行1列fig = make_subplots(rows=4, cols=1, shared_xaxes=True, vertical_spacing=0.02)
# 第一行添加蜡烛图ohlcv_fig = ohlcv[['Open', 'High', 'Low', 'Close', 'Volume']].vbt.ohlcv.plot()fig.add_trace(ohlcv_fig.data[0], row=1, col=1) # 添加蜡烛图数据fig.add_trace(ohlcv_fig.data[1], row=2, col=1) # 添加交易量柱状图
# 第二行添加两个移动平均线fig.add_trace(go.Scatter(x=ohlcv.index, y=std_close, mode='lines', name='SMA 20'), row=3, col=1)fig.add_trace(go.Scatter(x=ohlcv.index, y=std_close_ma, mode='lines', name='SMA 50'), row=3, col=1)
fig.add_trace(go.Scatter(x=ohlcv.index, y=diff_close, mode='lines', name='diff 20'), row=4, col=1)fig.add_trace(go.Scatter(x=ohlcv.index, y=diff_close_ma, mode='lines', name='diff 50'), row=4, col=1)
# # 创建条件指标# std_indicator = (std_close > std_close_ma ).astype(int)# # 添加条件指标图# fig.add_trace(go.Scatter(x=std_indicator.index, y=std_indicator, mode='lines', name='STD Condition', fill='tozeroy'), row=4, col=1)
# 更新图表的布局设置fig.update_layout(height=600, width=800, title_text="蜡烛图与移动平均线")fig.show()
部分信息可能已经过时