社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

十年年化50%+的策略如何进化?兼容机器学习流程的量化策略开发,附python代码

七年实现财富自由 • 2 天前 • 11 次点击  
原创内容第922篇,专注智能量化投资、个人成长与财富自由。
咱们星球叫“智能量化实验室“,有一部分智能的工作,落在了AGI星球来实现。
但智能量化框架,还有深挖的空间。
区别于传统量化(backtrader,bt等)都是传统量化。也就是技术分析的”自动化“版本。
比如咱们的动量轮动,排序因子,还是止盈、止损规则。
当然这是有效的,也是可解释的——大家看策略榜的更新就知道了:
然而,这里还没有太多机器学习、深度学习等智能化相关的东西。
策略也不会自主学习和进化。
qlib框架是最接近AI驱动的开源量化框架,设计理念就是机器学习导向。
不过咱们之前也分析过,它不太兼容传统规则量化,而且表达式与自定义的数据存储格式绑定,不容易切换到自己的数据存储。vnpy.alpha在这方面处理得挺好。
import jsonimport shelveimport picklefrom pathlib import Pathfrom datetime import datetime, timedeltafrom collections import defaultdictfrom functools import lru_cache, partialimport numpy as npfrom loguru import loggerimport polars as plfrom constant import Intervalfrom dataset import to_datetime, AlphaDataset, process_drop_na, process_cs_norm, Segmentfrom dataset.datasets.alpha_158 import Alpha158from model import AlphaModelfrom model.models.lgb_model import LgbModelfrom strategy.strategies.equity_demo_strategy import EquityDemoStrategyclass AlphaLab:    def __init__(self, lab_path: str, data_path:str) -> None:        self.lab_path: Path = Path(lab_path)        self.data_path = Path(data_path)        self.dataset_path: Path = self.lab_path.joinpath("dataset")        self.model_path: Path = self.lab_path.joinpath("model")        self.signal_path: Path = self.lab_path.joinpath("signal")        # 创建文件夹        for path in [            self.lab_path,            self.dataset_path,            self.model_path,            self.signal_path        ]:            if not path.exists():                path.mkdir(parents=True)    def save_dataset(self, name: str, dataset: AlphaDataset) -> None:            """Save dataset"""            file_path: Path = self.dataset_path.joinpath(f"{name}.pkl")            with open(file_path, mode="wb"as f:                pickle.dump(dataset, f)    def load_dataset(self, name: str) -> AlphaDataset | None:        """Load dataset"""        file_path: Path = self.dataset_path.joinpath(f"{name}.pkl")        if not file_path.exists():            logger.error(f"Dataset file {name} does not exist")            return None


    
        with open(file_path, mode="rb"as f:            dataset: AlphaDataset = pickle.load(f)            return dataset    def save_model(self, name: str, model: AlphaModel) -> None:        """Save model"""        file_path: Path = self.model_path.joinpath(f"{name}.pkl")        with open(file_path, mode="wb"as f:            pickle.dump(model, f)    def load_model(self, name: str) -> AlphaModel | None:        """Load model"""        file_path: Path = self.model_path.joinpath(f"{name}.pkl")        if not file_path.exists():            logger.error(f"Model file {name} does not exist")            return None        with open(file_path, mode="rb"as f:            model: AlphaModel = pickle.load(f)            return model    def remove_model(self, name: str) -> bool:        """Remove model"""        file_path: Path = self.model_path.joinpath(f"{name}.pkl")        if not file_path.exists():            logger.error(f"Model file {name} does not exist")            return False        file_path.unlink()        return True    def list_all_models(self) -> list[str]:        """List all models"""        return [file.stem for file in self.model_path.glob("*.pkl")]    def save_signal(self, name: str, signal: pl.DataFrame) -> None:        """Save signal"""        file_path: Path = self.signal_path.joinpath(f"{name}.parquet")        signal.write_parquet(file_path)    def load_signal(self, name: str) -> pl.DataFrame | None:        """Load signal"""        file_path: Path = self.signal_path.joinpath(f"{name}.parquet")        if not file_path.exists():            logger.error(f"Signal file {name} does not exist")            return None        return pl.read_parquet(file_path)    def remove_signal(self, name: str) -> bool:        """Remove signal"""        file_path: Path = self.signal_path.joinpath(f"{name}.parquet")        if not file_path.exists():            logger.error(f"Signal file {name} does not exist")            return False        file_path.unlink()        return True    def list_all_signals(self) -> list[str]:        """List all signals"""        return [file.stem for file in


    
 self.model_path.glob("*.parquet")]    def load_bar_df(self,symbols: list[str],                    start: datetime | str='20100101',                    end: datetime | str=datetime.now().strftime('%Y%m%d'),                    extended_days: int=20):        start = to_datetime(start) - timedelta(days=extended_days)        end = to_datetime(end) + timedelta(days=extended_days // 10)        dfs: list = []        for s in symbols:            # Check if file exists            file_path: Path = self.data_path.joinpath(f"{s}.csv")            if not file_path.exists():                logger.error(f"File {file_path} does not exist")                continue            # Open file            df: pl.DataFrame = pl.read_csv(file_path, schema_overrides={'date': pl.Utf8})            # Convert the date column to Date type            df = df.with_columns(                pl.col("date").str.strptime(pl.Date, "%Y%m%d").alias("date")            )            # Filter by date range            df = df.filter((pl.col("date") >= start) & (pl.col("date") <= end))            # Specify data types            df = df.with_columns(                pl.col("open"),  # .round(3).cast(pl.Float32),                pl.col("high"),  # .cast(pl.Float32),                pl.col("low"),  # .cast(pl.Float32),                pl.col("close"),  # .cast(pl.Float32),                pl.col("volume"),  # .cast(pl.Float32),            )            # Check for empty data            if df.is_empty():                continue            dfs.append(df)        #start_dates = [df.get_column("date").min() for df in dfs]        # 找到最近的起始日期        #latest_start_date = max(start_dates)        #print("统一截取的起始日期:", latest_start_date)        # dfs_cut = [        #     df.filter(pl.col("date") >= latest_start_date)        #     for df in dfs        # ]        # Concatenate results        result_df: pl.DataFrame = pl.concat(dfs)        # compare_symbol_dates(result_df,'513500.SH','512890.SH')        return result_df    def calc_exprs(self, df, names, fields):        from dataset.utility import calculate_by_expression        if len(fields) == len(names) and len(names) > 0:            results = []            for name,field in zip(names, fields):                if field == '':continue                #try:                expr = calculate_by_expression(df, field)                #except:                    # print(f'因子表达式有问题,忽略:{field}')                    # import traceback                    # traceback.print_stack()                    # continue                results.append(expr['data'].alias(name))            df = df.with_columns(results)        return df            # self.df = self.df.drop_nans()if __name__ == '__main__':    from strategy import BacktestingEngine    symbols = ['510300.SH','159915.SZ']    lab = AlphaLab(lab_path='./run', data_path=


    
'D:\work\.aitrader_data\quotes_etf')    df = lab.load_bar_df(symbols=symbols, start='20100101', end='20250506',extended_days=10)    print(df)    dataset: AlphaDataset = Alpha158(        df,        train_period=("2010-01-01""2014-12-31"),        valid_period=("2015-01-01""2016-12-31"),        test_period=("2017-01-01""2020-8-31"),    )    # 添加数据预处理器    dataset.add_processor("learn", partial(process_drop_na, names=["label"]))    dataset.add_processor("learn", partial(process_cs_norm, names=["label"], method="zscore"))    # 准备特征和标签数据    name = 'etf轮动'    dataset.prepare_data(filters=None, max_workers=3)    lab.save_dataset(name=name,dataset=dataset)    dataset: AlphaDataset = lab.load_dataset(name)    model: AlphaModel = LgbModel(seed=42)    # # 使用数据集训练模型    model.fit(dataset)    # # 查看模型细节    # model.detail()    lab.save_model(name,model)    model = lab.load_model(name)    #用模型在测试集上预测    pre: np.ndarray = model.predict(dataset, Segment.TEST)    # 加载测试集数据    df_t: pl.DataFrame = dataset.fetch_infer(Segment.TEST)    # 合并预测信号列    df_t = df_t.with_columns(pl.Series(pre).alias("signal"))    # 提取信号数据    signal: pl.DataFrame = df_t["date""symbol""signal"]    dataset.show_signal_performance(signal)    lab.save_signal(name,signal)    # 从文件加载信号数据    signal = lab.load_signal(name)    import bt    from bt.algos import *    from bt_algos_extend import SelectTopK    # create the strategy    s = bt.Strategy('s1', [bt.algos.RunDaily(),                           SelectTopK(signal=signal),                           bt.algos.WeighEqually(),                           bt.algos.Rebalance()])    # 创建回测引擎对象    engine = BacktestingEngine(lab)    # 设置回测参数    engine.set_parameters(        vt_symbols=symbols,        interval=Interval.DAILY,        start=datetime(201711),        end=datetime(202081),        capital=100000000    )    # # 添加策略实例    # setting = {"top_k": 30, "n_drop": 3, "hold_thresh": 3}    # engine.add_strategy(EquityDemoStrategy, setting, signal)    # 执行回测任务    # engine.load_data()    # engine.run_backtesting()    # engine.calculate_result()    # engine.calculate_statistics()    # engine.show_chart()    #dataset.show_feature_performance("rsv_5")
典型的机器学习流程,数据及因子计算,划分数据集,然后有监督学习建模,预测信号,回测,可视化。
通过滚动式的学习,模型是可以持续进化的,这就是核心逻辑:
代码与数据下载:AI量化实验室——2025量化投资的星辰大海
吾日三省吾身
01
《拿铁因素》里三句话:先投资自己,让账户自动运转,从现在起,富有地生活
其实“拿铁因素”本身,只是有效“省钱”的方式。
让账户自动运转才是根本
很幸运这几年构建了自己的投资理财体系,账户可以自动运转。
无论你是通过“节约拿铁因素”去节流,还是通过“一人企业”去开源,这个“杠杆”的金钱系统都有有效地发挥作用。
“从现在起,富有地生活”。——不留遗憾。
如果仅仅指望退休生活,那就是《百万富翁快车道》里看不上的“慢车道”。
“有花堪折直须折,莫待无花空折枝”。
当你三十岁的时候,去买十八岁时想买的东西,去二十岁想去的地方,已经毫无意义。世界上没有那么多的来日方长,只有世事无常,欲买桂花同载酒,终不似少年游。
如何平衡现在和未来呢?既要活在当下,又要长期主义。
其实也特别简单,存下每天收入的1/8以上(先投资自己),然后让复利滚动起来(让账户自动运转)。然后就可以在“当下”,“富有地生活”了。
不必想象自己达到A7.7或者A8,或者等到这一天。
因为你等到这一天,你会发现,其实没有特别根本的变化。
前两句话是如何实现财富自由,而第三句话则是关于为什么这么做?
财务自由并不是目的,财富自由是为了精神自由,能自由,深度去体验这个世界。首先确定对你来说什么是重要的,然后照着去做。比如你想上很久的摄影课、想看很久的演唱会、想去很久的一场旅行……你比你想象的更富有,所以从现在起富有地生活吧,不是在遥远的未来,就从今天开始。
——像你已经财富自由了那样,去生活。
02
大家都各大有各的事情,但多数人终归是普通人。
有人说,潜在的机会在弱联系里,这是有一定的道理。

弱联系的主动维护和激活: 弱连接不会自动产生价值。你需要:

    • 建立连接: 积极参与社交活动、线上社区、行业会议等。

    • 保持可见度: 偶尔互动(如社交媒体点赞评论、节日问候、分享有用信息)。

    • 清晰表达价值: 在适当的时候,让对方知道你的专长和需求(但不要过于功利)。

    • 乐于助人: 在力所能及的范围内帮助弱连接对象,建立好感与互惠基础。

    • 善于倾听和提问: 了解对方的世界,发现潜在的交集。

      “潜在”二字很关键: 并非所有弱连接都会带来直接机会,但弱连接网络大大增加了你接触到潜在机会的可能性。它是一个概率游戏,网络越大越多元,可能性越高。


代码和数据下载:AI量化实验室——2025量化投资的星辰大海

AI量化实验室 星球,已经运行三年多,1700+会员。

aitrader代码,因子表达式引擎、遗传算法(Deap)因子挖掘引等,支持vnpy,qlib,backtrader和bt引擎,内置多个年化30%+的策略,每周五迭代一次,代码和数据在星球全部开源。


点击 “查看原文”,直接访问策略集合

扩展  •  历史文章   

EarnMore(赚得更多)基于RL的投资组合管理框架:一致的股票表示,可定制股票池管理。(附论文+代码)

年化收益200%+的策略集 | 实时板块资金热力图 「aitrader 5.0系统代码发布」

机器学习驱动的策略开发通过流程 | 普通人阶层跃迁的可能路径?

年化30.24%,最大回撤19%,综合动量多因子评分策略再升级(python代码+数据)

三秒钟创建一个年化28%,夏普比1.25的策略(python系统已开放源代码下载)

会员专属策略可以直接下载了,多个十年年化30+%策略集|polars重构因子引擎(代码+数据下载)

6年年化收益46%,最大回撤率为16%的策略(附python代码)

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/183424
 
11 次点击