10.投资组合优化实战
投资组合优化是量化交易中一个经典而复杂的问题。与前几章讨论的单股票或多股票交易策略不同,这里的核心挑战不再是判断买卖时机,而是如何在多个资产之间动态分配资金权重。想象手里有一笔钱,面对几十只股票的备选池,每天需要决定每只股票配置多少比例的资金,这本质上是一个连续的决策过程。
传统方法如马科维茨的均值-方差模型(MPT)依赖历史收益率和协方差矩阵,计算静态最优权重。但市场瞬息万变,静态配置难以适应动态环境。深度强化学习提供了一种新思路:将组合优化建模为马尔可夫决策过程,让智能体在模拟市场中自主学习最优的权重调整策略。
组合优化问题数学建模
问题定义与MDP框架
投资组合优化问题可以形式化为一个马尔可夫决策过程(MDP)。与多股票交易环境不同,这里的动作空间不再是买卖信号,而是每只股票的资金权重向量。
状态空间的设计需要捕捉市场动态和组合特征。FinRL 的 PortfolioOptimizationEnv 采用了一个三维结构:(features, stocks, time_window)。具体来说,状态包含:
- 协方差矩阵:量化股票间的风险关联
- 技术指标:MACD、RSI、CCI、ADX 等
- 历史价格序列:通过时间窗口捕捉时序模式
这种设计让智能体不仅能看到单只股票的特征,还能理解整个组合的风险结构。协方差矩阵尤其重要,它反映了不同资产价格联动的风险,是传统组合理论的基石。
动作空间定义为每只股票的资金权重向量 W_t = [w_0, w_1, ..., w_n],其中 w_0 代表现金比例,w_i 代表第 i 只股票的权重。所有权重之和为 1,且每个分量在 [0,1] 区间内。环境会自动对动作进行 softmax 归一化,确保满足约束条件。
奖励函数的设计直接决定了智能体的学习目标。FinRL 采用对数收益率形式:
r_t = ln(V_t / V_{t-1})
其中 V_t 是 t 时刻的组合总价值。这种设计有几个优点:首先,对数收益率具有时间可加性,便于计算长期累积收益;其次,它能惩罚价值下跌,奖励价值增长;最后,这种形式与金融理论中的连续复利概念一致。
与传统MPT的对比
现代投资组合理论(MPT)通过求解二次规划问题来最大化夏普比率。但这种方法有几个局限:一是依赖历史数据计算期望收益和协方差,对未来预测能力有限;二是无法灵活融入技术指标等非结构化信息;三是权重调整频率低,通常是月度或季度再平衡。
强化学习方法的优势在于:能够处理高维状态空间,融合多种信息源;支持高频动态调整;通过试错学习非线性策略。当然,这也带来了新的挑战:训练不稳定、过拟合风险、计算成本高等。
PortfolioOptimizationEnv环境使用
环境初始化与配置
FinRL 提供了专门的 PortfolioOptimizationEnv 类来模拟组合优化场景。使用前需要准备符合格式的数据,环境会从 DataFrame 中读取时间序列数据。
数据格式要求包含以下列:
date:时间戳tic:股票代码close:收盘价- 其他特征列:如 high、low 等
环境的核心参数包括:
env = PortfolioOptimizationEnv(
df=data, # 数据DataFrame
stock_dim=30, # 股票数量
hmax=100, # 最大交易股数
initial_amount=1000000, # 初始资金
transaction_cost_pct=0.001, # 交易成本
reward_scaling=1e-4, # 奖励缩放因子
state_space=30, # 状态空间维度
action_space=30, # 动作空间维度
tech_indicator_list=['macd', 'rsi', 'cci', 'adx'], # 技术指标列表
turbulence_threshold=140, # 市场动荡阈值
lookback=252 # 回溯期
)
lookback 参数定义了时间窗口长度,默认 252 个交易日(约一年)。这个窗口决定了状态中包含多少历史信息,是平衡计算效率和策略记忆能力的关键超参。
状态与动作处理机制
环境的 step 方法实现了核心逻辑。当智能体输出动作 actions 后,环境会执行以下流程:
首先,对动作进行归一化处理。如果动作向量之和不为 1,环境会自动应用 softmax 函数:
norm_actions = (np.array(actions) - np.array(actions).min()) / (np.array(actions) - np.array(actions).min()).sum()
weights = norm_actions
然后,计算组合收益。假设前一天权重为 W_{t-1},当天权重调整为 W_t,价格变化为 P_t,则组合收益为:
portfolio_return = sum(((self.data.close.values / last_day_memory.close.values) - 1) * weights)
接着,更新组合价值:
new_portfolio_value = self.portfolio_value * (1 + portfolio_return)
self.portfolio_value = new_portfolio_value
最后,返回新的状态。状态由协方差矩阵和技术指标拼接而成:
self.state = np.append(np.array(self.covs),
[self.data[tech].values.tolist() for tech in self.tech_indicator_list],
axis=0)
这种设计确保了状态包含足够的市场信息,同时保持了计算效率。协方差矩阵通过滚动窗口计算,反映了近期市场波动特征。
环境使用示例
完整的使用流程包括数据下载、特征工程、环境创建和训练。FinRL 提供了 EnvSetup 类简化环境配置:
from finrl.env.environment import EnvSetup
# 初始化环境配置
env_setup = EnvSetup(
stock_dim=30,
state_space=30,
hmax=100,
initial_amount=1000000,
transaction_cost_pct=0.001,
tech_indicator_list=['macd', 'rsi', 'cci', 'adx']
)
# 创建训练环境
env_train = env_setup.create_env_training(
data=train_data,
env_class=StockPortfolioEnv
)
训练完成后,可以在测试集上评估策略表现。环境会自动记录每日组合价值和权重变化,便于后续分析。
Policy Gradient组合权重优化
策略梯度算法原理
FinRL 的 PolicyGradient 类实现了专门针对组合优化的策略梯度算法。与传统 DDPG 不同,这个算法没有独立的 Critic 网络,而是直接使用组合价值作为价值函数。
算法核心思想是:通过蒙特卡洛采样估计策略梯度,沿着能提升组合价值的方向更新网络参数。具体步骤如下:
- 初始化:创建策略网络和经验回放缓冲区
- 交互采样:在每个 episode 中,智能体与环境交互,收集状态、动作、奖励序列
- 批量更新:每收集
batch_size个时间步后,从缓冲区采样序列数据 - 价值计算:计算价值函数
V = Σ ln(μ_t * (W_t · P_t)),其中μ_t是交易成本因子 - 梯度上升:根据价值函数对策略网络进行梯度上升更新
这种设计有几个特点:一是采用序列采样而非随机采样,保持了时间依赖性;二是全利用(full-exploit)策略,训练过程中不添加探索噪声;三是直接使用组合价值作为优化目标,避免了价值函数近似的误差。
网络架构与实现
策略网络通常采用卷积结构处理三维状态输入。EIIE(Ensemble of Identical Independent Evaluators)是经典架构,它使用一维卷积层分别处理每只股票的时间序列,然后聚合输出权重。
from finrl.agents.portfolio_optimization.models import DRLAgent
from finrl.agents.portfolio_optimization.architectures import EIIE
# 配置策略梯度参数
model_kwargs = {
"lr": 0.01, # 学习率
"policy": EIIE, # 网络架构
"batch_size": 128, # 批量大小
"episodes": 10 # 训练轮数
}
# 配置网络超参
policy_kwargs = {
"k_size": 4, # 卷积核大小
"time_window": 50 # 时间窗口
}
# 创建模型
model = DRLAgent(train_env).get_model("pg", model_kwargs, policy_kwargs)
# 训练模型
DRLAgent.train_model(model, episodes=10)
训练过程中,智能体会逐步学习到有效的权重分配模式。通常,训练初期权重变化剧烈,后期趋于稳定。可以通过监控组合价值的增长曲线判断训练效果。
超参数调优要点
策略梯度算法对超参数敏感,几个关键参数需要特别注意:
学习率(lr):控制策略更新步长。太大导致不稳定,太小收敛慢。建议从 0.01 开始,根据训练曲线调整。
批量大小(batch_size):影响梯度估计的方差。组合优化问题中,较大的批量(如 128-256)通常更稳定,因为能更好地捕捉时间序列的统计特性。
时间窗口(time_window):决定状态的历史深度。太短无法捕捉长期趋势,太长增加计算负担。50-252 个交易日是常见选择。
奖励缩放(reward_scaling):原始组合价值可能很大,需要缩放至合适范围(如 1e-4 量级)以保证数值稳定性。
可以使用 Optuna 或 Ray Tune 进行自动化超参搜索,FinRL 的教程中有相关示例。
EI3架构与高级策略
EIIE架构回顾
EIIE(Ensemble of Identical Independent Evaluators)是组合优化领域的开创性工作。其核心设计是:对每只股票使用相同的卷积网络独立处理,最后通过 softmax 层输出权重。
网络结构通常包括:
- 输入层:
(features, stocks, time_window) - 一维卷积层:分别处理每只股票的时间序列
- 特征聚合层:合并所有股票的特征
- 输出层:生成权重向量
这种架构的优势在于参数共享,减少了模型复杂度,同时保持了处理多资产的能力。但缺点是忽略了股票间的相互影响,所有股票使用相同的特征提取器。
EI3架构创新
EI3(Inception-based Ensemble of Identical Independent Evaluators)在 EIIE 基础上引入了 Inception 模块,支持多尺度时间特征聚合。这是受到 Google Inception 网络启发,通过不同大小的卷积核捕捉不同频率的市场模式。
EI3 的关键改进:
- 多尺度卷积:并行使用 3x3、5x5、7x7 等不同大小的卷积核
- 特征融合:将不同尺度的特征图拼接后送入后续层
- 残差连接:缓解梯度消失问题,支持更深网络
这种设计让智能体能同时捕捉短期波动和长期趋势,对非平稳的金融时间序列更有效。实验表明,EI3 在波动率较大的市场环境中表现优于 EIIE。
高级策略技巧
除了网络架构,还有一些实用技巧可以提升策略表现:
集成学习:训练多个不同随机种子的模型,对输出权重取平均。这种方法能降低方差,提高鲁棒性。FinRL 的教程中有集成策略的完整实现。
风险约束:在奖励函数中加入波动率惩罚项:
reward = portfolio_return - λ * portfolio_variance
其中 λ 是风险厌恶系数。这种调整能让策略更保守,降低最大回撤。
动态时间窗口:根据市场波动率自适应调整 lookback 参数。高波动时期使用更长的历史数据,低波动时期缩短窗口以提高响应速度。
交易成本建模:虽然简化示例通常忽略交易成本,但实盘必须考虑。可以在奖励函数中加入:
transaction_cost = sum(abs(weights - last_weights)) * cost_rate
reward = raw_reward - transaction_cost
这些技巧需要在 PortfolioOptimizationEnv 中自定义奖励函数和环境逻辑。
基准对比与风险分析
回测流程实现
FinRL 使用 PyFolio 库进行回测分析。完整的回测流程包括:
from finrl.trade.backtest import backtest_strat, baseline_strat
from pyfolio import timeseries
# 生成策略收益序列
df_daily_return, df_actions = DRLAgent.DRL_prediction(
model=model_a2c,
test_data=test_data,
test_env=env_trade,
test_obs=obs_trade
)
# 计算绩效指标
DRL_strat = backtest_strat(df_daily_return)
perf_func = timeseries.perf_stats
perf_stats_all = perf_func(
returns=DRL_strat,
factor_returns=DRL_strat,
positions=None,
transactions=None,
turnover_denom="AGB"
)
print("==============DRL Strategy Stats===========")
print(perf_stats_all)
关键绩效指标包括:
- 年化收益率:策略的年化回报水平
- 累计收益率:整个回测期的总回报
- 年化波动率:收益的标准差,衡量风险
- 夏普比率:风险调整后的收益,(252**0.5)*mean/std
- 最大回撤:从峰值到谷底的最大损失
- 卡玛比率:年化收益/最大回撤
基准策略对比
合理的基准对比是评估策略有效性的关键。常用基准包括:
买入持有(Buy & Hold):等权重买入所有股票并持有到期。这是最简单的被动策略,但在牛市中往往难以超越。
道琼斯工业平均指数(DJIA):市场指数基准,代表整体市场表现。
等权重组合:每天将资金均等分配给所有股票。这种策略自动实现"高抛低吸"的再平衡效果。
对比代码示例:
# 获取基准数据
dji_data, dji_strat = baseline_strat(
ticker='^DJI',
start_date='2019-01-01',
end_date='2020-12-01'
)
# 绘制对比图
import pyfolio
with pyfolio.plotting.plotting_context(font_scale=1.1):
pyfolio.create_full_tear_sheet(
returns=DRL_strat,
benchmark_rets=dji_strat,
set_context=False
)
风险分析深度解读
除了常规指标,还需要关注几个风险维度:
波动率聚集:检查高收益波动是否集中在特定时期。可以通过滚动窗口计算 30 天波动率,观察其时间序列特征。
回撤持续时间:最大回撤的幅度固然重要,但恢复时间同样关键。一个 20% 回撤如果用 3 个月恢复,比用 1 年恢复的策略更优。
换手率分析:过高的换手率意味着高昂的交易成本。计算日均换手率:
turnover = np.mean(np.abs(df_actions.diff()).sum(axis=1))
如果换手率超过 100%,说明策略交易过于频繁,实盘可能不可行。
权重集中度:检查策略是否过度集中持仓。计算权重的赫芬达尔指数:
hhi = np.mean((df_actions**2).sum(axis=1))
指数接近 1 说明集中度高,风险大;接近 1/n 说明分散性好。
市场环境适应性:将回测期划分为牛市、熊市、震荡市,分别计算策略表现。优秀的策略应在不同市场环境下都能取得正收益,至少控制回撤。
通过这些分析,可以全面评估策略的优劣,为参数调整和模型改进提供方向。记住,实盘部署前必须在多个不同时间段和数据集上验证策略的稳健性。
投资组合优化是 FinRL 框架中最具挑战性的应用场景之一。从数学建模到环境配置,从算法实现到绩效评估,每个环节都需要仔细推敲。本章介绍了核心的 PortfolioOptimizationEnv 环境、策略梯度算法以及 EIIE/EI3 架构,并详细讲解了回测分析方法。
下一章将探讨如何通过向量环境和并行化技术加速训练过程,这对于处理大规模资产组合和长周期数据尤为重要。高性能训练不仅能缩短研发周期,还能支持更复杂的模型架构和超参数搜索。