• etf动量轮动+大盘择时:年化30%的策略


    原创文章第111篇,专注“个人成长与财富自由、世界运作的逻辑, AI量化投资”。

    今天重点来探索一下elegantRL。

    昨天的文章金融强化学习与finRL开发包里介绍了finRL的源码结构,背后的强化学习框架是elegantRL。

    聚宽平台上有一个“动量轮动+RSRS”择时,在咱们自己的AI量化平台上复现一下。

    01 策略思路

    我们之前用的动量是ROC(20)就是20日收益率。这里的动量定义为 20日收盘价的“斜率”——就是线性回归的斜率。“斜率”取最大的K支,构建组合。然后RSRS(18,600)对市场择时,如果RSRS信号为buy,则按上述动量组合调仓,若RSRS信号为SELL,则平仓。

    02 动量计算

    动量定义为 20日收盘价的“斜率”——就是线性回归的斜率。

    qlib原本的表达式,使用了cpython,这里我们使用np.polyfit即可实现。

    class Slope(Rolling):
    
        def __init__(self, feature, N):
            super(Slope, self).__init__(feature, N, "slope")
    
        def _load_internal(self, instrument):
            def calc_slope(x):
                x = x / x[0]  # 这里做了一个“归一化”
                slope = np.polyfit(range(len(x)), x, 1)[0]
                return slope
    
            series = self.feature.load(instrument)
            result = series.rolling(self.N, min_periods=2).apply(calc_slope)
            series = pd.Series(result, index=series.index)
            return series

    这是单序列的线性回归,RSRS是双序列的线性回归。目前没有找到办法rolling,导致性能很差。

    调用的代码如下:

    fields += ['Slope($close,20)']
    names += ['mom_slope']
    
    fields += ["Ref($close,-1)/$close - 1"]
    names += ['label']
    
    all = Dataloader().load_one_df(['000300.SH'], names, fields)

    如此即可以实现代码最大程度的复用了。

    02 排序算子之topK

    按某一个因子的顺序,选择前K个进行持仓的算子。

    class SelectTopK:
        def __init__(self, K=1, order_by='order_by', b_ascending=False):
            self.K = K
            self.order_by = order_by
            self.b_ascending = b_ascending
    
        def __call__(self, context):
            stra = context['strategy']
            features = context['features']
    
            if self.order_by not in features.columns:
                logger.error('排序字段{}未计算'.format(self.order_by))
                return
    
            bar = get_current_bar(context)
            if bar is None:
                logger.error('取不到bar')
                return True
            bar.sort_values(self.order_by, ascending=self.b_ascending, inplace=True)
    
            selected = []
            pre_selected = None
            if 'selected' in context:
                pre_selected = context['selected']
                del context['selected']
    
            for code in list(bar.code):
                if pre_selected:
                    if code in pre_selected:
                        selected.append(code)
                else:
                    selected.append(code)
                if len(selected) >= self.K:
                    break
            context['selected'] = selected

    有了算子之后,都是代码模块。

    不择时的收益率如下:

    如果把“斜率”换成“20日ROC”,收益率小一点,但回撤大不少。

    03  大盘择时

    大盘使用沪深300的RSRS给大盘择时。

    大盘择时的逻辑为:若择时指标为“买”,则按原计划操作,若大盘择时为“卖”,则全部平仓退场。

    class PickTime:
        def __init__(self, benchmark='000300.SH', signal='signal'):
            self.benchmark = benchmark
            #self.buy = self.buy
            self.signal = signal
    
        def __call__(self, context):
            stra = context['strategy']
            extra = context['extra']
            df = extra[self.benchmark]
    
            if self.signal not in df.columns:
                logger.error('择时信号不存在')
                return True
    
            curr_date = stra.get_current_dt()
            if curr_date not in df.index:
                logger.error('日期不存在{}'.format(curr_date))
                return None
    
            bar = df.loc[curr_date]
            if type(bar) is pd.Series:
                bar = bar.to_frame().T
    
            if bar[self.signal][0]:
                logger.info('择时信号显示,平仓所有。')
                context['selected'] = []
    

    04 组合成策略

    积木式开发不需要写策略代码,这个非常方便,而且所有的算子均可复用。

    e = BacktraderEngine(init_cash=1000000, benchmark='399006.SZ', start=datetime(2014, 1, 1))
    e.add_features(symbols, names, fields)
    e.add_extra('000300.SH', fields=['RSRS($high,$low,18,600)', '$RSRS_beta<0.8'], names=['RSRS', 'signal'])
    
    from engine.strategy.algos import SelectTopK, PickTime, WeightEqually
    
    e.run_algo_strategy([SelectTopK(K=1), PickTime(), WeightEqually()])
    e.analysis(pyfolio=False)
    

    斜率版本可以改善最大回撤:

    明天继续加上卡曼滤波,以及换成真实的ETF(今天是两支指数)。

    代码与数据均上传至星球,可前往量化专栏下载。

    每天代码,每周研报复现。

    【每周研报复现】基于阻力支撑相对强度(RSRS)的市场择时

    我的开源项目及知识星球

  • 相关阅读:
    iOS 借助定位实现“保活”策略
    MSTP + Eth-Trunk配置实验 华为实验手册
    freeRTOS学习(一)
    Linux openvino 环境搭建遇见的问题
    ADAS和ADS有什么区别?
    成功解决NotImplementedError: cannot instantiate ‘WindowsPath‘ on your system
    iClient for Leaflet实现动态绘圆的几何查询
    使用nsenter调试k8s网络
    图书管理小练习
    SpringBoot 自动配置
  • 原文地址:https://blog.csdn.net/weixin_38175458/article/details/127968366