用轮动方法的策略,会有一个十分隐蔽的**“轮动宽度”**问题,比如一个小市值策略持有10只股票,股票按市值升序排序a0、a2、...、a9,很明显a9就可能更频繁被买进卖出,而a0就不容易被轮动到,有的策略就把选股范围数量+1,这样是降低了a9被交易的频度,但a0就更不容易被轮动到了。这个问题随着持股数量的增加会更加明显。
为解决这个问题,可以设置一个**“轮动宽度”**,持仓股票的名次提升达到设定的**“轮动宽度”**就卖出,这样持仓的股票就获得等权的轮动机会,提高了轮动效率。
我以开心果朋友的一个持股数量很多的策略来实例说明这个方法:
### 原策略克隆自聚宽文章:https://www.joinquant.com/post/39814
### 标题:持仓95只大容量小市值,媲美金元顺安元启
### 作者:开心果
```
增加轮动宽度相关的代码:
g.band = 40 # 增加轮动宽度参数
g.ranking = {} # 增加持仓序号记录
# 比较顺序变化,达到轮动宽度的就卖出
fasters = []
for stock in choice:
if stock in g.ranking:
if choice.index(stock) - g.ranking[stock] >=g.band:
fasters.append(stock)
elif choice.index(stock) < g.ranking[stock]:
g.ranking[stock] = choice.index(stock)
else:
g.ranking[stock] = choice.index(stock)
# Sell
for s in context.portfolio.positions:
if (s not in choice or s in fasters) and (s not in g.high_limit_list):
order_target(s, 0)
if s in g.ranking:
g.ranking.pop(s, None) # 如果键 's' 不存在,不会报错,返回 None
```
用这个方法,比原策略收益提高并不多,但是一个非常稳定的阿尔法收益,逻辑上也更合理。

多位朋友提出如何处理再次买入上次因名次上升而卖出的股票的问题,我加了一个卖出时的名次记录,再买入时,必须满足名次回落达到一定幅度的条件。果然收益有所提升,证明大家提出的问题是有道理的。对比图如下。

代码也更新了,再克隆不扣分。
@Alpha_壶铃 # buy
psize = 1.0/g.stock_num * context.portfolio.total_value
for s in choice:
if s in fasters: continue # 名次提升达到轮动宽度的本周不再买入
if s in g.high_limit_list: continue
if context.portfolio.available_cash < psize:
break
if s not in context.portfolio.positions:
order_value(s, psize)