年化收益 21%:lightGBM的WFA滚动训练,使用qlib的alpha158因子集

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

开始之前,先说说感受。

把整个框架与思路都在社群里开源出来,就是希望大家看懂思路,而不是拿一两个策略。说实话,投资哪有这种高确定性的“圣杯”

投资是一个“概率”游戏。我们要做的,尽量提升决策的正概率而已。因此,多一些有效因子,模型判断更准确一些。所以,方法论和思路比一个策略本身要重要得多。这是我们这个社群的目标——以盈利为目的开发策略,而且是批量的有效实战策略。

微信群现在的讨论氛围还不错,还没有加入的成员请扫码进入。一般加入之初,我都有发私信给大家,如果没有,请再星球里私信我。

今天要抽空实现“前向滚动”训练回测

从20日动量来看,我们选择29个行业作为候选集合是对的。

因为市场是按行业分化的,尤其是市场情绪震荡有分歧之时:

29个行业等权回测:

年化收益 0.075,最大回测 -0.318,夏普比 0.441。

等权加周再平衡:

年化收益 0.146,最大回测 -0.283,夏普比 0.748。再平衡适合相关性低的组合,正当的行业比较明显。

使用lightGBM的WFA滚动训练

年化收益 0.219,最大回测 -0.259,夏普比 0.861。

etfs = [
        '159870.SZ',
        '512400.SH',
        '515220.SH',
        '515210.SH',
        '516950.SH',
        '562800.SH',

        '515170.SH',
        '512690.SH',
        '159996.SZ',
        '159865.SZ',
        '159766.SZ',

        '515950.SH',
        '159992.SZ',
        '159839.SZ',
        '512170.SH',
        '159883.SZ',

        '512980.SH',
        '159869.SZ',
        '515050.SH',
        '515000.SH',
        '515880.SH',
        '512480.SH',
        '515230.SH',

        '512670.SH',
        '515790.SH',
        '159757.SZ',
        '516110.SH',

        '512800.SH',
        '512200.SH',
    ]

import numpy as np
import pandas as pd
from tqdm import tqdm
from engine.env import Env


from engine.datafeed.dataset import DataSet, AlphaLit, Alpha
ds = DataSet(etfs, start_date='20190101', handler=Alpha(), cache=True)

# ds = DataSet(['000300.SH', '399006.SZ'], start_date='20120101', handler=Alpha())
env = Env(ds.data)

from engine.context import ExecContext
from engine.algo.algos import *
from engine.algo.algo_model import ModelWFA
from engine.models.gbdt_l2r import LGBModel

model = LGBModel(load_model=False, feature_cols=ds.get_feature_names())

env.set_algos([
    RunWeekly(),
    ModelWFA(model=model),
    SelectTopK(K=2, order_by='pred_score', b_ascending=False),
    WeightEqually()
])

env.backtest_loop()
env.show_results()

下面是因子集的定义代码:

class Alpha(AlphaBase):
    def __init__(self):
        pass

    @staticmethod
    def parse_config_to_fields():
        # ['CORD30', 'STD30', 'CORR5', 'RESI10', 'CORD60', 'STD5', 'LOW0',
        # 'WVMA30', 'RESI5', 'ROC5', 'KSFT', 'STD20', 'RSV5', 'STD60', 'KLEN']
        fields = []
        names = []

        # kbar
        fields += [
            "(close-open)/open",
            "(high-low)/open",
            "(close-open)/(high-low+1e-12)",
            "(high-greater(open, close))/open",
            "(high-greater(open, close))/(high-low+1e-12)",
            "(less(open, close)-low)/open",
            "(less(open, close)-low)/(high-low+1e-12)",
            "(2*close-high-low)/open",
            "(2*close-high-low)/(high-low+1e-12)",
        ]
        names += [
            "KMID",
            "KLEN",
            "KMID2",
            "KUP",
            "KUP2",
            "KLOW",
            "KLOW2",
            "KSFT",
            "KSFT2",
        ]

        # =========== price ==========
        feature = ["OPEN", "HIGH", "LOW", "CLOSE"]
        windows = range(5)
        for field in feature:
            field = field.lower()
            fields += ["shift(%s, %d)/close" % (field, d) if d != 0 else "%s/close" % field for d in windows]
            names += [field.upper() + str(d) for d in windows]

        # ================ volume ===========
        fields += ["shift(volume, %d)/(volume+1e-12)" % d if d != 0 else "volume/(volume+1e-12)" for d in windows]
        names += ["VOLUME" + str(d) for d in windows]

        # ================= rolling ====================
        windows = [5, 10, 20, 30, 60]
        fields += ["shift(close, %d)/close" % d for d in windows]
        names += ["ROC%d" % d for d in windows]

        fields += ["mean(close, %d)/close" % d for d in windows]
        names += ["MA%d" % d for d in windows]

        fields += ["std(close, %d)/close" % d for d in windows]
        names += ["STD%d" % d for d in windows]

        fields += ["slope(close, %d)/close" % d for d in windows]
        names += ["BETA%d" % d for d in windows]

        fields += ["max(high, %d)/close" % d for d in windows]
        names += ["MAX%d" % d for d in windows]

        fields += ["min(low, %d)/close" % d for d in windows]
        names += ["MIN%d" % d for d in windows]

        fields += ["corr(close/shift(close,1), log(volume/shift(volume, 1)+1), %d)" % d for d in windows]
        names += ["CORD%d" % d for d in windows]

        fields += ['close/shift(close,20)-1']
        names += ['roc_20']
        return fields, names

所有代码,数据均打包发布至星球,请大家前往下载:

我的开源项目及知识星球

欢迎大家提建议,bug,可以在专属微信群里发起讨论。

猜你喜欢

转载自blog.csdn.net/weixin_38175458/article/details/131223829