策略研发过程中,除了纯粹的买卖技术指标之外,还有一类信号用于判断当前行情是否应该入场。 比如:大盘或个股持续处于波动性中,没有明显方向性,拉锯状态,此时大多数趋势类策略都会反复开仓,止损,开仓,止损,导致稳定的小额亏损。
01,基础配置信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import 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,时间交易参数配置 1 2 3 4 5 6 7 8 9 10 11 12 13 seed = 42 symbol = '002594.XSHE' metric = 'total_return' start_date = datetime(2020 , 1 , 1 , tzinfo=pytz.utc) end_date = datetime(2023 ,1 ,1 , tzinfo=pytz.utc) time_buffer = timedelta(days=100 ) freq = '1D' vbt.settings.portfolio['init_cash' ] = 10000. vbt.settings.portfolio['fees' ] = 0.0025 vbt.settings.portfolio['slippage' ] = 0.0025
b,获取行情和行情mask 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 cols = ['Open' , 'High' , 'Low' , 'Close' , 'Volume' ] 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)wobuf_mask = (ohlcv_wbuf.index >= start_date) & (ohlcv_wbuf.index <= end_date) ohlcv = ohlcv_wbuf.loc[wobuf_mask, :] print ("ohlcv.shape:" ,ohlcv.shape)ohlcv.vbt.ohlcv.plot().show_svg()
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)时才开仓 大致效果如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 import plotly.graph_objects as gofrom plotly.subplots import make_subplotsstd_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] 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,等小柱子,计算小柱子,均值方差等状态。
结论:效果尚可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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] 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 ) 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趋势性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import plotly.graph_objects as gofrom plotly.subplots import make_subplotsstd_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] diff_close_wbuf = ohlcv_wbuf['Close' ] - ohlcv_wbuf['Close' ].rolling(window=int (20 /5 )).mean().shift(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] 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 ) fig.update_layout(height=600 , width=800 , title_text="蜡烛图与移动平均线" ) fig.show()