分享

十年回报155%,最大回撤20%:实证多指数动量轮动模型

 AI量化实验室 2023-10-12 发布于北京

持续行动1期 54/100,“AI技术应用于量化投资研究”。

昨天的文章相当于是backtrader的"hello world",直接感受一下bt的使用。

今天开始,我们要来实战一下,“轮动”模型是量化中经典的范式。

如果仅交易一个标的,比如一只股票或者指数,那么叫“择时”模型。

“择时”是所有模型里最难的。大家知道金融数据里噪声多,择时就是“预测”,难度很高。

“轮动”模型天然就是一个投资组合策略,本身组合的波动就在下降(标的池里的指数相关性越低,效果越好,比如沪深300+标普500+日经225等)。

“轮动”模型除了交易信号之外,还有明确的排序信号

以“动量”为例,比如动量(20)>0.02的发出买入信号,但我们选择动量最大的K支才真正进行交易——所谓“骑最快的马”的逻辑。

01 数据准备

传统有大小盘轮动,行业轮动等。

我们以A股,美股为例进行演示。

feed = CSVDatafeed()
codes = ['000300.SH','SPX']
for code in codes:
feed.add_data(code, DATA_DIR_CSV.joinpath('{}.csv'.format(code)))
df = feed.get_df(code)
df = to_backtrader_dataframe(df)
print(df)

A股沪深300指数:

美股标普500指数:

02 指标计算

我们考虑一个简单的策略:动量轮动

20日动量>0.02时买入,20日动量<0时卖出,每次最多持有一支,超过一支时取动量大者。

我们只需要计算一个指标就是“20日动量”,是买卖规则指标,也是排序指标。

class StrategyRotation(bt.Strategy):
params = dict(
period=20, # 动量周期
)

def __init__(self):
# 指标计算
self.inds = {}
for data in self.datas:
self.inds[data] = bt.ind.ROC(data, period=self.p.period)

03 交易规则

判断to_buy即ROC(20)>0.02,判断to_sell即ROC(20)<0,当前持仓>0

# 计算to_buy,判断roc>0.02
#
计算to_sell,判断roc<0
#
判断当前已经持仓
to_buy = []
to_sell = []
holding = []
for data, roc in self.inds.items():
if roc[0] > 0.02:
to_buy.append(data)

if roc[0] < 0:
to_sell.append(data)

if self.getposition(data).size > 0:
holding.append(data)

对于已经持仓且要得到卖出信号的清仓:

for sell in to_sell:
if self.getposition(sell).size > 0:
logger.debug('清仓'+sell.p.name)
self.close(sell)

计算新的待持仓组合 new_hold=  to_buy+holding-to_sell

new_hold = list(set(to_buy + holding))
for data in to_sell:
if data in new_hold:
new_hold.remove(data)

等权重分配仓位:

# 等权重分配 todo: 已持仓的应应该不变,对cash对新增的等权分配
weight = 1 / len(new_hold)
for data in new_hold:
self.order_target_percent(data, weight)

04 风险/收益分析

核心指标:年化收益、波动率、最大回撤、夏普比。

分析器需要一个个添加:

self.cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='AnnualReturn')
self.cerebro.addanalyzer(bt.analyzers.SharpeRatio, riskfreerate=0.0, annualize=True, _name='SharpeRatio')
self.cerebro.addanalyzer(bt.analyzers.DrawDown, _name='DrawDown')

回测完成后打印出来:

print("--------------- AnnualReturn -----------------")
print(strat.analyzers.AnnualReturn.get_analysis())
print("--------------- SharpeRatio -----------------")
print(strat.analyzers.SharpeRatio.get_analysis())
print("--------------- DrawDown -----------------")
print(strat.analyzers.DrawDown.get_analysis())

总体回报率155%,最大回撤20.6%。

backtrader写一个策略比较简单。

遗留问题,与benchmark对比,以及如何更加优雅的可视化。

内置的matplotlib的绘图有一些局限,网上有不同的解法,这个我们需要单独起一个篇幅来说明。

按以往经验,不用仓促造轮子,网上有成熟的解决方案,学习,改造成适应我们的版本。

总结:backtrader是一个成熟的框架,有成熟的生态,包括各种案例等。后续会围绕这个基础框架开展智能量化。

心得:

如果你要开一家包子铺,重点就关注如何做出好吃的包子,更快更好地做出好包子。不要去研究面粉怎么来的,小麦怎么种的,蒸笼是如何做出来的,竹子是如何生长的。也许它们之间有些许的联系,但是竹子对包子的影响,远不及你把调料好好研究一番——直面结果而去,站在巨人的肩膀上这很重要。

但你可能会说,我需要研究“综述”,才知道这个行业的全貌。也没问题,抓住主线,之于包子,最重要就是口味;之于量化,最重要就是策略。你要落地策略的时候,也许先碰到了pyalgotrade,那就用好,功能不够了,qlib或backtrader,甚至是joinquant或者bigquant这样的网上平台,反正终会遇到,但要最快的速度渡过这一步。

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多