光大证券技术择时系列报告之一是RSRS,之二是RSRS推广到行业轮动,之三是放量恰是入市时:成交量择时初探。
这篇研报主要讲的是用交易量来构造一个择时策略。
里面提到,“价涨量先行”,成交量较大时指数大概率上涨。
构造成交量的时间序列排名指标方式如下:
- 数据上除了当日的成交量以外,我们还需取前 N 个交易日的每日成 交量数据,共 N+1 个成交量的值。
- 将该 N+1 个成交量数据按从小到大进行排序,计算当日成交量在这 N+1 个数值中的排名 n,最小即为 1,最大即为 N+1。
- 通过运算 2 * (n-N-1) / N 将当日成交量排名标准化为[-1, 1]内的数 值。
上面是研报提到的构造方式,但是标准化的公式有问题,2 (n-N-1) / N 映射到的区间是[-2,0],应该改为(n-平均数)/平均数,即 2( n-(N-1)/2) /N才对。
择时方法是用上面的构造方式算出来[-1,1]的数去和阈值做判断,其含义其实就是当成交量时序排名处于最大的一段范围内就买入,否则卖出。
核心代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| bars = get_bars(g.security,g.N + 1,unit='1d', fields=['date', 'open','high','low','close','volume'], include_now=False, end_dt=None, fq_ref_date=None) print bars last_day_volume = bars[-1]['volume'] lt_last_day_volume_count = 0 for i in range(0,len(bars)-1): if bars[i]['volume'] < last_day_volume: lt_last_day_volume_count += 1 last_day_volume_rank = lt_last_day_volume_count + 1
std_score = 2.0*(last_day_volume_rank -1 - g.N/2.0)/g.N
if std_score > g.S: g.tc = 7 order_target_value(g.security, context.portfolio.total_value) elif std_score < g.S: order_target_value(g.security, 0)
|
然后里面提到参数(N=35,S=0.8)的择时效果更佳,我们将其 作为沪深 300 成交量时序排名择时策略的参数。中证 500 的参数由样本内表现确定为(N=30, S=0.5)。
我根据研报的策略写的代码应该没什么错,尝试复现了一下,达不到研报中说的收益。不知道是研报注水了,还是我忽略了什么东西,或者是里面隐藏了什么条件。
我的测试结果:
沪深300,N=35 S=0.8
2007-03-05 到 2014-01-01
策略收益 30.87% 策略年化收益 4.13% 基准收益 -7.12% Alpha 0.007 Beta 0.113 Sharpe 0.012 胜率 0.562 盈亏比 1.608 最大回撤 13.43%
中证500, N=30 S=0.5
2007-03-05 到 2014-01-01
策略收益 142.98% 策略年化收益 14.29% 基准收益 52.94% Alpha 0.097 Beta 0.237 Sharpe 0.614 胜率 0.504 盈亏比 1.677 最大回撤 18.19%
本文作者董春秋,首发于公众号春秋金经,http://www.chunqiujinjing.com/
在中证500上面还好一些,年化14% 最大回撤18%,但也没达到研报上写的中证500年化25.5% 最大回撤17%
沪深300上面就更差了,年化才4% 回撤13%,研报上面写的年化15.2% 回撤11%
我这算复现失败了吗?大佬们帮忙看看是哪里不对,还是说研报真的注水了。但按理说不会啊,这篇研报作者是RSRS的作者。
源码:
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| from jqdata import *
def initialize(context): g.security = '000905.XSHG' g.N = 30 g.S = 0.5 set_benchmark(g.security) set_option('use_real_price', True)
set_order_cost(OrderCost(close_tax=0.001, open_commission=0.0003, close_commission=0.0003, min_commission=5), type='stock')
run_daily(before_market_open, time='before_open', reference_security='000300.XSHG') run_daily(market_open, time='open', reference_security='000300.XSHG') run_daily(after_market_close, time='after_close', reference_security='000300.XSHG')
def before_market_open(context): pass
def market_open(context): log.info("market_open") bars = get_bars(g.security,g.N + 1,unit='1d', fields=['date', 'open','high','low','close','volume'], include_now=False, end_dt=None, fq_ref_date=None) print bars last_day_volume = bars[-1]['volume'] lt_last_day_volume_count = 0 for i in range(0,len(bars)-1): if bars[i]['volume'] < last_day_volume: lt_last_day_volume_count += 1 last_day_volume_rank = lt_last_day_volume_count + 1 std_score = 2.0*(last_day_volume_rank -1 - g.N/2.0)/g.N if std_score > g.S: g.tc = 7 order_target_value(g.security, context.portfolio.total_value) elif std_score < g.S: order_target_value(g.security, 0)
def after_market_close(context): log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time()))) trades = get_trades() for _trade in trades.values(): log.info('成交记录:'+str(_trade)) log.info('一天结束') log.info('##############################################################')
|