用 FinRL 复现 NeurIPS 2018 论文:从数据工程到 DRL 量化交易智能体的完整工作流

发布时间:2026/9/21 1:37:10
用 FinRL 复现 NeurIPS 2018 论文:从数据工程到 DRL 量化交易智能体的完整工作流 金融科技强化学习人工智能【免费下载链接】FinRLFinRL®: Financial Reinforcement Learning. 项目地址https://gitcode.com/gh_mirrors/fi/FinRL-Library点击查看免费下载本指南以 FinRL 开源仓库中的 Stock_NeurIPS2018 系列为蓝本系统讲解如何用强化学习RL复现 NeurIPS 2018 论文《Practical Deep Reinforcement Learning Approach for Stock Trading》的完整流程。该系列由三个 Jupyter Notebook 构成数据准备 → 智能体训练 → 回测对比覆盖 OHLCV 行情获取、技术指标与波动率特征工程、OpenAI Gym 风格交易环境构建、A2C/PPO/TD3/SAC 等 5 种 DRL 算法训练以及均值方差优化MVO与道琼斯指数DJIA双基线回测。读完本文你将掌握一条可端到端复现、可迁移到自定义股票池的 DRL 量化研究流水线并理解 FinRL 分层架构数据层、环境层、智能体层背后的实现细节。一、任务背景与仓库定位Stock_NeurIPS2018 系列位于仓库的 finrl/applications/Stock_NeurIPS2018 目录是 FinRL 官方提供的论文复现型应用示例之一。其核心思想是把学术论文中的 RL 交易流程转化为可运行的工程代码让研究者能用同一套框架在真实股票数据上完成训练 - 回测 - 对比闭环。Stock_NeurIPS2018/ ├── README.md # 三步流程总览本文主体 ├── Stock_NeurIPS2018_1_Data.ipynb # Step I: 数据下载与预处理 ├── Stock_NeurIPS2018_2_Train.ipynb # Step II: 构建环境并训练 DRL 智能体 └── Stock_NeurIPS2018_3_Backtest.ipynb # Step III: 回测与基线对比三个 Notebook 各自对应一个独立阶段前一阶段的输出train_data.csv、trade_data.csv、trained_models/下的模型 zip是后一阶段的输入串联后即构成完整的量化研究流水线。二、Step I数据获取与特征工程Stock_NeurIPS2018_1_Data.ipynb第一步的目标是生成两份 CSV训练集train_data.csv与交易回测集trade_data.csv仓库中还提供了对应的样例数据文件。Notebook 输出显示下载 30 只道指成分股从 2009-01-01 到 2021-10-29 的日线数据得到 94301 行 × 8 列的原始 DataFrame。2.1 安装依赖Notebook 首先通过 pip 安装所需依赖其中pyportfolioopt用于第三步的均值方差优化基线!pip install swig !pip install wrds !pip install pyportfolioopt ## install finrl library !pip install githttps://github.com/AI4Finance-Foundation/FinRL.git2.2 OHLCV 数据与两种获取方式OHLCV即 Open开盘、High最高、Low最低、Close收盘、Volume成交量是股票时间序列中最核心的数值信息载体交易者可以据此推断动量、市场情绪与趋势。Notebook 演示了两种获取方式方式一直接使用 yfinance 库import yfinance as yf aapl_df_yf yf.download(tickersaapl, start2020-01-01, end2020-01-31)方式二使用 FinRL 封装的 YahooDownloaderfrom finrl.meta.preprocessor.yahoodownloader import YahooDownloader aapl_df_finrl YahooDownloader(start_date2020-01-01, end_date2020-01-31, ticker_list[aapl]).fetch_data()两种方式返回的数据格式不同。FinRL 的 YahooDownloader 做了面向后续流水线的标准化改造源码中的fetch_data()与_adjust_prices()揭示了这些差异列名标准化Date/Adj Close/Close/...统一重命名为小写date/close/open/high/low/volume/tic用复权价替代收盘价_adjust_prices()用调整因子adj adjcp / close对 open/high/low/close 全部进行复权修正消除拆股与分红影响新增星期列day周一 0 … 周五 4为智能体提供时间上下文特征清洗缺失数据dropna()删除空值行并按date tic排序。输出为 20 行 × 8 列的 DataFrame列结构为date, open, high, low, close, volume, tic, day。2.3 股票池与时间窗口划分本示例的股票池是道琼斯工业平均指数DJIA的 30 只成分股仓库 config_tickers.py 中定义了DOW_30_TICKER列表AXP、AMGN、AAPL、BA、CAT、CSCO…。训练与回测时间窗按论文惯例划分——用 2009-2020 的长期数据训练用 2020-07 之后的未来数据做样本外回测from finrl import config_tickers TRAIN_START_DATE 2009-01-01 TRAIN_END_DATE 2020-07-01 TRADE_START_DATE 2020-07-01 TRADE_END_DATE 2021-10-29 df_raw YahooDownloader(start_dateTRAIN_START_DATE, end_dateTRADE_END_DATE, ticker_listconfig_tickers.DOW_30_TICKER).fetch_data()2.4 特征工程技术指标、VIX 与湍流指数原始 OHLCV 不能直接作为 RL 状态需要经过 FeatureEngineer 加工成信息丰富的特征空间。Notebook 的构造参数如下from finrl.config import INDICATORS from finrl.meta.preprocessor.preprocessors import FeatureEngineer fe FeatureEngineer(use_technical_indicatorTrue, tech_indicator_listINDICATORS, use_vixTrue, use_turbulenceTrue, user_defined_featureFalse) processed fe.preprocess_data(df_raw)对应源码中preprocess_data()的调用链依次完成四件事clean_data()按日期透视出完整收盘价矩阵剔除某日停牌/未上市导致缺失过多的股票add_technical_indicator()基于stockstats包计算趋势类技术指标。仓库 config.py 中默认的 8 个指标为macdMACD、boll_ub/boll_lb布林带上/下轨、rsi_30相对强弱、cci_30顺势指标、dx_30动向指数、close_30_sma/close_60_sma30/60 日均线add_vix()下载恐慌指数^VIX并合并到每个交易日反映市场整体风险情绪add_turbulence()计算湍流指数。源码calculate_turbulence()以 252 个交易日约一年为滚动窗口估计收益的协方差矩阵再通过马氏距离度量当日市场偏离正常状态的极端程度——这是论文为应对 2007-2008 式金融危机而引入的风险控制机制。预处理后每个样本包含 18 列8 个原始字段 day 8 个技术指标 vix turbulence。为满足训练环境对每个交易日出现全部股票的要求Notebook 还执行了笛卡尔积补全 缺失值填 0 的操作import itertools list_ticker processed[tic].unique().tolist() list_date list(pd.date_range(processed[date].min(), processed[date].max()).astype(str)) combination list(itertools.product(list_date, list_ticker)) processed_full pd.DataFrame(combination, columns[date, tic]).merge( processed, on[date, tic], howleft) processed_full processed_full[processed_full[date].isin(processed[date])] processed_full processed_full.sort_values([date, tic]).fillna(0)2.5 按时间窗切分并落盘最后用 preprocessors.py 中的data_split()按日期区间切分数据。该函数同时完成按[date, tic]排序并将索引重置为日期编码factorize使后续环境可以直接按day索引取数from finrl.meta.preprocessor.preprocessors import data_split train data_split(processed_full, TRAIN_START_DATE, TRAIN_END_DATE) trade data_split(processed_full, TRADE_START_DATE, TRADE_END_DATE) print(len(train)) # 83897 print(len(trade)) # 9715 train.to_csv(train_data.csv) trade.to_csv(trade_data.csv)三、Step II构建 Gym 环境并训练 DRL 智能体Stock_NeurIPS2018_2_Train.ipynb第二步将处理好的数据转化为标准的强化学习交互环境并用 Stable Baselines 3 训练深度强化学习DRL智能体。3.1 RL 形式化状态、动作、奖励Notebook 先用教科书式的三要素定义了交易问题的 MDP状态 s智能体对市场环境的感知即历史价格与上一节生成的技术指标。智能体通过回放历史数据与环境交互学习动作 a每个状态下允许采取的操作。单标的场景可定义a ∈ {−1, 0, 1}分别表示卖出、持有、买入当操作涉及多股时扩展为a ∈ {−k, …, −1, 0, 1, …, k}例如 买入 10 股 AAPL 记为10、卖出 10 股 记为−10奖励函数 r(s, a, s′)奖励是引导智能体学习更优策略的激励信号本任务取组合价值的变动量r(s, a, s′) v′ − v其中v′与v分别是新状态s′与旧状态s下的组合价值。3.2 从 DataFrame 到 StockTradingEnvFinRL 的核心环境实现是 env_stocktrading.py 中的StockTradingEnv它继承gym.Env并提供 OpenAI Gym 风格接口。Notebook 中先根据股票数与指标数推导状态空间维度from finrl.meta.env_stock_trading.env_stocktrading import StockTradingEnv stock_dimension len(train.tic.unique()) state_space 1 2*stock_dimension len(INDICATORS)*stock_dimension print(fStock Dimension: {stock_dimension}, State Space: {state_space}) # 输出: Stock Dimension: 29, State Space: 291状态空间公式1 2×N 8×N的含义是1 个当前现金 2N 个价格类字段每只股票的价格与持股数量 8N 个技术指标每只股票 8 个指标。随后构造环境参数并实例化buy_cost_list sell_cost_list [0.001] * stock_dimension num_stock_shares [0] * stock_dimension env_kwargs { hmax: 100, # 单次单资产最大交易额手数上限 initial_amount: 1000000, # 初始资金 100 万美元 num_stock_shares: num_stock_shares, # 每只股票初始持仓为 0 buy_cost_pct: buy_cost_list, # 买入费率 0.1% sell_cost_pct: sell_cost_list,# 卖出费率 0.1% state_space: state_space, # 状态维度 291 stock_dim: stock_dimension, # 股票数 29 tech_indicator_list: INDICATORS, # 8 个技术指标 action_space: stock_dimension, # 动作维度与股票数一致 reward_scaling: 1e-4 # 奖励缩放系数 } e_train_gym StockTradingEnv(dftrain, **env_kwargs) env_train, _ e_train_gym.get_sb_env() # 包装为 DummyVecEnv从 env_stocktrading.py 的源码可见关键设计动作空间定义为spaces.Box(low-1, high1, shape(action_space,))即每个维度输出 −1 到 1 之间的连续值再结合hmax换算为具体股数交易成本通过buy_cost_pct/sell_cost_pct在买卖函数中扣除reward_scaling1e-4将量级过大的组合收益差缩放以稳定神经网络训练。get_sb_env()将单个环境包装为 Stable Baselines 3 所需的DummyVecEnv向量化环境。3.3 训练 A2C 智能体含 DDPG/PPO/TD3/SAC 模板DRL 算法统一由 stablebaselines3/models.py 中的DRLAgent封装内部维护MODELS {a2c: A2C, ddpg: DDPG, td3: TD3, sac: SAC, ppo: PPO}并把仓库 config.py 中的默认超参A2C_PARAMS、PPO_PARAMS、DDPG_PARAMS、TD3_PARAMS、SAC_PARAMS注入各算法。Notebook 用开关变量控制训练哪些算法from finrl.agents.stablebaselines3.models import DRLAgent from stable_baselines3.common.logger import configure from finrl.config import INDICATORS, TRAINED_MODEL_DIR, RESULTS_DIR if_using_a2c True if_using_ddpg False if_using_ppo False if_using_td3 False if_using_sac False agent DRLAgent(envenv_train) model_a2c agent.get_model(a2c) # 配置日志输出stdout CSV TensorBoard tmp_path RESULTS_DIR /a2c new_logger_a2c configure(tmp_path, [stdout, csv, tensorboard]) model_a2c.set_logger(new_logger_a2c) trained_a2c agent.train_model(modelmodel_a2c, tb_log_namea2c, total_timesteps50000) if if_using_a2c else None trained_a2c.save(TRAINED_MODEL_DIR /agent_a2c) if if_using_a2c else NoneNotebook 中 A2C 的实际超参为{n_steps: 5, ent_coef: 0.01, learning_rate: 0.0007}与 config.py 中A2C_PARAMS一致。训练过程会打印每个 rollout 的policy_loss、value_loss、reward等指标一个完整 episode 结束时还会输出总资产、总奖励、总交易成本、交易次数与 Sharpe 比率。其余四种算法同样提供了完整模板可复制粘贴后翻转开关启用算法关键超参config.py 默认默认训练步数A2Cn_steps5, ent_coef0.01, learning_rate0.000750,000PPOn_steps2048, ent_coef0.01, learning_rate0.00025, batch_size64200,000DDPGbatch_size128, buffer_size50000, learning_rate0.00150,000TD3batch_size100, buffer_size1000000, learning_rate0.00150,000SACbatch_size64, buffer_size100000, learning_rate0.0001, learning_starts100, ent_coefauto_0.170,000训练完成后模型以.zip格式保存到trained_models/目录本地路径为./trained_modelsColab 为/content/trained_models。本仓库的 cryptocurrency_trading 等应用目录也提供了预训练模型如actor.pth作为参照。四、Step III回测与双基线对比Stock_NeurIPS2018_3_Backtest.ipynb第三步加载训练好的模型在样本外数据trade_data.csv上回测并与两个经典基线对比均值方差优化组合MVO与道琼斯指数DJIA。4.1 加载模型并进行样本外交易先把trade_data.csv与训练好的模型放在本目录读取数据并设置开关变量然后通过A2C.load()加载模型from stable_baselines3 import A2C, DDPG, PPO, SAC, TD3 trade pd.read_csv(trade_data.csv) trade trade.set_index(trade.columns[0]); trade.index.names [] trained_a2c A2C.load(trained_models/agent_a2c) if if_using_a2c else None随后构造与训练阶段相同参数的回测环境但额外启用湍流阈值风控——turbulence_threshold70且风险指标列设为vix即当市场波动VIX 相关湍流指标超过阈值时强制清仓这是论文应对极端行情的核心风控手段e_trade_gym StockTradingEnv(dftrade, turbulence_threshold70, risk_indicator_colvix, **env_kwargs) df_account_value_a2c, df_actions_a2c DRLAgent.DRL_prediction( modeltrained_a2c, environmente_trade_gym) if if_using_a2c else (None, None)DRLAgent.DRL_prediction()见 models.py的流程是重置向量化环境后逐日调用model.predict(obs)得到动作、执行env.step(action)推进一天直至终止打印 hit end!最后返回账户价值序列与动作序列。Notebook 强调为了充分利用数据应周期性重训如按季度/月度/周度本例只在 2009-01 至 2020-07 的样本内调参一次回测区间拉长后存在一定的 alpha 衰减。4.2 基线一均值方差优化MVONotebook 先用process_df_for_mvo()将长表date × tic透视成宽表每行一个交易日、每列一只股票的收盘价再用StockReturnsComputing()计算资产日收益率序列进而得到均值向量meanReturns与协方差矩阵covReturns。最后借助安装阶段引入的pyportfolioopt求最大夏普组合权重from pypfopt.efficient_frontier import EfficientFrontier ef_mean EfficientFrontier(meanReturns, covReturns, weight_bounds(0, 0.5)) raw_weights_mean ef_mean.max_sharpe() cleaned_weights_mean ef_mean.clean_weights() mvo_weights np.array([1000000 * cleaned_weights_mean[i] for i in range(29)])weight_bounds(0, 0.5)限制单只股票权重在 0~50%max_sharpe()求解最大化夏普比率的资产配置最后按初始资金 100 万美元折算成各股票的初始买入市值。组合净值曲线由Portfolio_Assets TradeData Initial_Portfolio计算得到。4.3 基线二道琼斯指数DJIA用YahooDownloader下载dji在回测区间的收盘价并以回测起始日的收盘价为基准归一化到 100 万与 DRL 智能体的初始资金对齐从而保证三者在同一量纲下可比df_dji YahooDownloader(start_dateTRADE_START_DATE, end_dateTRADE_END_DATE, ticker_list[dji]).fetch_data() df_dji df_dji[[date,close]] fst_day df_dji[close][0] dji pd.merge(df_dji[date], df_dji[close].div(fst_day).mul(1000000), howouter, left_indexTrue, right_indexTrue).set_index(date)4.4 汇总净值曲线并可视化将各智能体账户价值、MVO 净值与 DJIA 净值按日期外连接合并缺失值用bfill回填得到统一的结果表result pd.DataFrame() if if_using_a2c: result pd.merge(result, df_result_a2c, howouter, left_indexTrue, right_indexTrue) # ... 其余算法同理 result pd.merge(result, MVO_result, howouter, left_indexTrue, right_indexTrue) result pd.merge(result, dji, howouter, left_indexTrue, right_indexTrue).fillna(methodbfill) col_name [A2C, DDPG, PPO, TD3, SAC][:len(result.columns)-2] [Mean Var, djia] result.columns col_name plt.rcParams[figure.figsize] (15, 5) plt.figure() result.plot()Notebook 中仅启用 A2C 的运行结果显示在 2020-07 至 2021-10 的样本外区间336 个交易日A2C 智能体净值从 100 万增长至约 186 万MVO 基线约 153 万DJIA 约 139 万但要注意这是单次实验的演示结果并非对算法优劣的普适结论——回测结果受超参、时间窗、费率与市场状态影响显著应保持审慎解读。五、从示例到自定义策略流水线复用要点该系列的工程价值在于每一阶段都可独立替换换股票池修改 config_tickers.py 中的 ticker 列表或直接向YahooDownloader(ticker_list[...])传入自定义代码如需其他数据源仓库 data_processors 还提供了 Alpaca、CCXT、EODHD、JoinQuant、WRDS 等多种实现换特征在FeatureEngineer中增删tech_indicator_list参考 config.py 的INDICATORS或开启user_defined_featureTrue添加自定义特征列需同步更新state_space公式换算法/超参通过agent.get_model(ppo, model_kwargsPPO_PARAMS)传入自定义超参字典仓库还提供了 ElegantRL 与 Ray RLlib 的接入实现换回测区间与风控调整TRADE_START_DATE/TRADE_END_DATE与turbulence_threshold观察不同市场状态下的策略表现延伸应用同一套环境层 - 智能体层结构被复用于 portfolio_allocation、cryptocurrency_trading、high_frequency_trading 等应用环境层的其他实现见 env_stock_trading 与 env_portfolio_allocation 目录。六、总结Stock_NeurIPS2018 系列展示了 FinRL 将论文方法论落地的完整链路YahooDownloader负责标准化的 OHLCV 获取FeatureEngineer完成技术指标、VIX 与湍流指数的特征工程StockTradingEnv提供带交易成本、风控与奖励缩放的市场模拟器DRLAgent统一封装 5 种 Stable Baselines 3 算法最后以 MVO 与 DJIA 为基线完成样本外回测。这套数据层 → 环境层 → 智能体层 → 回测层的分层设计也正是 FinRL 三层架构文档 所倡导的核心思想可平滑迁移到研究者自定义的股票池、特征体系与交易场景中。赞分享金融科技强化学习人工智能【免费下载链接】FinRLFinRL®: Financial Reinforcement Learning. 项目地址https://gitcode.com/gh_mirrors/fi/FinRL-Library点击查看免费下载相关推荐FinRL-Library论文复现指南NeurIPS 2018股票交易完整教程FinRL Library论文复现指南NeurIPS 2018股票交易完整教程 想要快速掌握深度强化学习在股票交易中的应用吗FinRL Library为初学金融科技强化学习人工智能终极FinRL实战指南重现NeurIPS 2018获奖交易策略的完整步骤终极FinRL实战指南重现NeurIPS 2018获奖交易策略的完整步骤 FinRL是一个专门为金融交易设计的强化学习框架让普通用户也能轻松实现智能交易策略金融科技强化学习人工智能Pixelle-Video教程4步把一句话变成成片短视频Pixelle Video教程4步把一句话变成成片短视频 每周要更新短视频但你不会剪辑、也没空拍摄Pixelle Video 是一个 AI 全自动短视频引金融科技强化学习人工智能上一篇如何快速上手BitCPM4-CANN-3B-unquantized从环境搭建到首次微调的完整教程下一篇BiliTools跨平台哔哩哔哩工具箱终极指南一键下载B站所有资源创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考