AI应用 量化交易强化学习DRLPython深度学习

强化学习在量化交易中的应用实践

如果说监督学习是”依葫芦画瓢”,强化学习就是”在摔打中成长”。让 AI 在模拟市场环境中反复试错,自主学习交易策略。

为什么强化学习适合交易

交易的RL本质

交易天然是强化学习问题:

Agent(交易者)
   ↓ action(买入/卖出/持有)
Environment(市场)
   ↓ reward(盈亏/夏普比率)
   ↓ state(价格/指标/持仓)
Agent ← 学习如何最大化累计收益

三大范式对比

范式监督学习无监督学习强化学习
学习信号标签(涨/跌)数据结构奖励(收益)
反馈时机即时-延迟
探索利用核心挑战
序列决策不考虑不考虑天生支持
与交易匹配度⭐⭐⭐⭐⭐

强化学习基础回顾

核心要素

# RL的核心抽象
class TradingEnv:
    """交易环境 - RL的交互接口"""

    def reset(self):
        """重置环境,返回初始状态"""
        return initial_state

    def step(self, action):
        """
        执行动作,返回:
        - next_state: 下一状态
        - reward: 即时奖励
        - done: 是否结束
        - info: 额外信息
        """
        return next_state, reward, done, info

    @property
    def action_space(self):
        """动作空间: 买入/卖出/持有 或 仓位比例[-1,1]"""
        pass

    @property
    def observation_space(self):
        """状态空间: 价格、指标、持仓等"""
        pass

常用算法速览

值函数方法:
  Q-Learning → DQN → Double DQN → Dueling DQN

策略梯度方法:
  REINFORCE → Actor-Critic → A2C/A3C → PPO

适合交易的:
  PPO(稳定、样本效率高)
  SAC(处理连续动作空间)
  TD3(减少过估计)

实战:构建交易RL环境

自定义 Gym 环境

import gymnasium as gym
from gymnasium import spaces
import numpy as np

class StockTradingEnv(gym.Env):
    """单标的股票交易环境"""

    def __init__(self, df, initial_balance=100000,
                 transaction_cost=0.001, max_position=1.0):
        super().__init__()

        self.df = df.reset_index(drop=True)
        self.initial_balance = initial_balance
        self.transaction_cost = transaction_cost
        self.max_position = max_position

        # 动作空间:仓位变化 [-1, 0, 1](全卖/不变/全买)
        self.action_space = spaces.Box(
            low=-1.0, high=1.0, shape=(1,), dtype=np.float32
        )

        # 状态空间:[价格比例, 收益率, RSI, MACD, 波动率, 仓位]
        self.observation_space = spaces.Box(
            low=-np.inf, high=np.inf, shape=(6,), dtype=np.float32
        )

        self.reset()

    def reset(self, seed=None, options=None):
        super().reset(seed=seed)

        self.current_step = 0
        self.balance = self.initial_balance
        self.position = 0.0  # 当前持仓市值
        self.portfolio_value = self.balance

        return self._get_observation(), {}

    def step(self, action):
        # 解析动作
        target_position = float(np.clip(action[0], -1.0, 1.0))

        # 计算交易
        current_price = self._get_price()
        position_change = target_position - self.position

        # 执行交易(含手续费)
        cost = abs(position_change) * self.transaction_cost
        self.position = target_position
        self.balance -= position_change * current_price + cost

        # 更新组合价值
        new_portfolio_value = self.balance + self.position * current_price
        reward = (new_portfolio_value - self.portfolio_value) / self.portfolio_value
        self.portfolio_value = new_portfolio_value

        # 移动到下一步
        self.current_step += 1
        done = self.current_step >= len(self.df) - 1

        return self._get_observation(), reward, done, False, {
            'portfolio_value': self.portfolio_value,
            'position': self.position,
            'reward': reward
        }

    def _get_observation(self):
        row = self.df.iloc[self.current_step]
        return np.array([
            row['Close'] / row['sma_50'] - 1,  # 价格偏离均线
            row['returns_5d'],                  # 5日收益率
            (row['rsi'] - 50) / 50,             # RSI标准化
            row['macd'] / row['Close'] * 100,   # MACD比例
            row['volatility_20d'],              # 20日波动率
            self.position / self.portfolio_value if self.portfolio_value > 0 else 0
        ], dtype=np.float32)

    def _get_price(self):
        return self.df.iloc[self.current_step]['Close']

奖励函数设计

def calculate_reward(portfolio_value, prev_value, position, returns):
    """复合奖励函数"""

    # 基础收益
    profit_reward = (portfolio_value - prev_value) / prev_value

    # 风险惩罚(夏普比率风格)
    risk_penalty = 0.1 * abs(position) * abs(returns)

    # 交易频率惩罚(减少过度交易)
    trade_penalty = 0.001 * abs(position_change)

    # 回撤惩罚
    drawdown_penalty = 0.1 * max(0, (peak_value - portfolio_value) / peak_value)

    return profit_reward - risk_penalty - trade_penalty - drawdown_penalty

实战:PPO训练

使用 Stable-Baselines3

from stable_baselines3 import PPO
from stable_baselines3.common.vec_env import DummyVecEnv
from stable_baselines3.common.callbacks import EvalCallback
import matplotlib.pyplot as plt

# 创建环境
def make_env(df_train, df_val):
    def _init():
        return StockTradingEnv(df_train)
    return _init

env = DummyVecEnv([make_env(train_df, val_df)])
eval_env = DummyVecEnv([lambda: StockTradingEnv(val_df)])

# PPO模型配置
model = PPO(
    'MlpPolicy',
    env,
    learning_rate=3e-4,
    n_steps=2048,
    batch_size=64,
    n_epochs=10,
    gamma=0.99,        # 折扣因子
    gae_lambda=0.95,   # GAE参数
    clip_range=0.2,    # 裁剪范围
    ent_coef=0.01,     # 熵系数(鼓励探索)
    verbose=1,
    tensorboard_log="./ppo_trading_logs/"
)

# 评估回调
eval_callback = EvalCallback(
    eval_env,
    best_model_save_path='./best_model/',
    log_path='./eval_logs/',
    eval_freq=10000,
    deterministic=True,
    render=False
)

# 训练
model.learn(
    total_timesteps=500_000,
    callback=eval_callback,
    progress_bar=True
)

# 保存模型
model.save("ppo_trading_model")

模型评估

def evaluate_model(model, env, n_episodes=100):
    """评估训练好的RL策略"""
    returns = []
    sharpes = []
    max_drawdowns = []

    for _ in range(n_episodes):
        obs, _ = env.reset()
        done = False
        episode_returns = []

        while not done:
            action, _ = model.predict(obs, deterministic=True)
            obs, reward, done, _, info = env.step(action)
            episode_returns.append(reward)

        daily_returns = np.array(episode_returns)
        returns.append(np.sum(daily_returns))
        sharpes.append(
            np.mean(daily_returns) / np.std(daily_returns) * np.sqrt(252)
        )
        # 计算最大回撤
        cumulative = np.cumprod(1 + daily_returns)
        peak = np.maximum.accumulate(cumulative)
        max_drawdowns.append(np.max((peak - cumulative) / peak))

    print(f"=== 强化学习策略评估 ({n_episodes}回合) ===")
    print(f"平均总收益: {np.mean(returns):.2%}")
    print(f"平均夏普比率: {np.mean(sharpes):.2f}")
    print(f"平均最大回撤: {np.mean(max_drawdowns):.2%}")
    print(f"盈利比率: {np.mean([1 for r in returns if r > 0]):.1%}")

    return returns, sharpes, max_drawdowns

RL量化的挑战与对策

核心挑战

挑战说明对策
过拟合在历史数据上”背题”多资产交叉验证,合理正则化
非平稳性市场特征持续变化在线学习、定期重新训练
稀疏奖励信号太少难以学习设计密集的辅助奖励
样本效率需要大量交互数据优先使用PPO等高效算法
探索风险探索可能导致大幅亏损安全的探索策略、止损约束

实用建议

# 1. 添加动作平滑惩罚(减少剧烈调仓)
action_smoothness_penalty = 0.01 * (action - prev_action)**2

# 2. 使用奖励缩放(稳定训练)
reward_scaled = np.clip(reward / reward_std, -10, 10)

# 3. 多进程并行采样(加速训练)
from stable_baselines3.common.vec_env import SubprocVecEnv
env = SubprocVecEnv([make_env(df) for _ in range(8)])

# 4. 课程学习(从简单到复杂)
# 阶段1:只用少数技术指标
# 阶段2:逐步增加特征数量
# 阶段3:加入交易成本等现实约束

与监督学习结合

监督学习(方向预测)+ 强化学习(仓位管理)

ML模型预测方向概率 → RL决策仓位大小
      ↓                      ↓
   "会涨70%"             "买60%仓位"

这种混合方法在实践中往往比纯RL更稳定。


RL量化交易的魅力在于:它不假设市场遵循什么规律,而是在尝试中学习。但也正因如此,它要求的验证比传统策略更加严格。永远在模拟环境中跑够足够长时间,再考虑真金白银。