第三章 单Agent智能体设计:基于强化学习的交易Agent

好,咱们进入实战环节。

前面两章我们聊了多Agent系统的整体架构,还有数据怎么在各Agent之间流转。今天这一章,我打算把镜头拉近,聚焦到最核心的一个单元——单Agent智能体

说白了,就是怎么设计一个能自己学会交易的“大脑”。

我个人习惯把强化学习交易Agent的设计拆成三块:状态空间动作空间奖励函数。这三块搞定了,Agent的基本骨架就有了。咱们一个一个来拆。

3.1 状态空间设计:Agent能看到什么?

状态空间,就是Agent观察市场的“眼睛”。你给它看什么,它就学什么。给少了,它看不清;给多了,它容易学歪。

我在项目中遇到过一个问题:一开始我把所有技术指标都塞进去,MACD、RSI、布林带、成交量……结果模型训练了三天,收益曲线还是跟心电图似的。后来我意识到,信息冗余比信息不足更可怕

一个合理的状态空间,我建议包含以下几类:

  • 价格数据:过去N步的收盘价、最高价、最低价。这是最原始的信号。
  • 成交量数据:过去N步的成交量。量价配合,才能看出资金意图。
  • 技术指标:选2-3个就够了。我个人常用的是RSI和ATR。RSI看超买超卖,ATR看波动率。
  • 持仓状态:当前仓位、当前盈亏。Agent得知道自己“兜里有多少钱”。
  • 市场环境:比如大盘指数涨跌、行业板块热度。这个看你的策略定位。

嗯,这里要注意:状态空间一定要归一化。价格从100涨到101,和从1000涨到1001,意义完全不同。我习惯用Z-score或者Min-Max归一化,把数据压到[-1, 1]之间。

核心原则:状态空间要“够用但不过剩”。你想想看,一个交易员盯盘时,真正关注的也就那几个数字。Agent也一样。

3.2 动作空间设计:Agent能做什么?

动作空间,就是Agent的“手”。它决定了Agent怎么跟市场互动。

你可能会想,动作空间不就是“买、卖、持有”吗?对,但也没那么简单。

我见过很多新手把动作空间设计成离散的:

  • 动作0:清仓
  • 动作1:买入10%仓位
  • 动作2:买入20%仓位
  • ……

这样做有个问题:动作空间太大。如果仓位步长是1%,那就有100个动作。Agent学起来很慢。

我个人更推荐连续动作空间。比如:

  • 动作输出是一个实数,范围[-1, 1]
  • 负数表示卖出,正数表示买入
  • 绝对值表示仓位比例

举个例子:输出0.3,就是买入30%仓位;输出-0.8,就是卖出80%仓位。这样Agent只需要学一个数,效率高很多。

避坑指南:我曾经把动作空间设计成“买入/卖出固定股数”,结果Agent学会了在低价股上频繁交易,因为手续费占比小。后来改成按仓位比例,这个问题就解决了。

3.3 奖励函数设计:Agent为什么而学?

奖励函数,是Agent的“老师”。它告诉Agent什么行为是好的,什么行为是坏的。

这是整个设计里最微妙的部分。奖励函数设计得好,Agent自己就能找到赚钱的路子;设计得不好,Agent可能会学会一些“歪门邪道”。

我常用的奖励函数框架是这样的:

reward = alpha * 收益率 + beta * 夏普比率 - gamma * 换手率 - delta * 最大回撤

解释一下:

  • 收益率:最直接的奖励。但只追求收益率,Agent会变得很激进。
  • 夏普比率:衡量风险调整后的收益。这个指标能让Agent更“稳健”。
  • 换手率:惩罚频繁交易。手续费和滑点都是成本。
  • 最大回撤:惩罚大幅亏损。这个很重要,不然Agent可能为了赚10%而冒亏50%的风险。

alpha、beta、gamma、delta是权重系数。我一般从alpha=1.0, beta=0.5, gamma=0.1, delta=0.2开始调。

注意:奖励函数不要设计得太复杂。我见过有人加了十几个惩罚项,结果Agent直接“躺平”了——因为不管怎么做都是负奖励,它干脆不交易了。

3.4 核心逻辑框架图

下面这张图,是我自己画的一个单Agent交易系统的核心逻辑。你看一眼就能明白整个流程:

单Agent强化学习交易系统核心逻辑 市场环境 价格、成交量、指标 状态空间 归一化后的特征向量 交易Agent 策略网络(PPO/DQN) 动作空间 买入/卖出/持有 奖励函数 收益 - 风险 - 成本 环境更新(下一时间步) 实线:正向流程 虚线:反馈循环 核心:状态→动作→奖励→更新

3.5 一个简单的代码示例

光说不练假把式。我写了一个最小化的交易Agent代码框架,用的是PPO算法。你感受一下:

import numpy as np
import torch
import torch.nn as nn

class TradingAgent:
    def __init__(self, state_dim, action_dim):
        # 策略网络
        self.policy = nn.Sequential(
            nn.Linear(state_dim, 128),
            nn.ReLU(),
            nn.Linear(128, 64),
            nn.ReLU(),
            nn.Linear(64, action_dim),
            nn.Tanh()  # 输出范围[-1, 1]
        )
        self.optimizer = torch.optim.Adam(self.policy.parameters(), lr=1e-4)
    
    def get_action(self, state):
        # state: 归一化后的状态向量
        state_tensor = torch.FloatTensor(state).unsqueeze(0)
        action = self.policy(state_tensor)
        return action.item()  # 返回仓位比例
    
    def update(self, states, actions, rewards):
        # 简化版PPO更新
        # 实际项目中需要计算优势函数、clip等
        loss = -torch.mean(rewards * torch.log(self.policy(states)))
        self.optimizer.zero_grad()
        loss.backward()
        self.optimizer.step()

# 使用示例
agent = TradingAgent(state_dim=10, action_dim=1)
state = np.random.randn(10)  # 模拟状态
action = agent.get_action(state)
print(f"建议仓位比例: {action:.2f}")

这段代码很糙,但骨架是对的。你把它跑起来,就能看到一个Agent在“思考”了。

3.6 一些实战中的坑

最后,我分享几个我在实战中踩过的坑:

  • 过拟合历史数据:Agent可能在回测中表现完美,但一到实盘就崩。我习惯在训练时加入随机噪声,或者用多个时间段的数据做交叉验证。
  • 奖励延迟:有时候一笔交易要持有好几天才能看到收益。这时候需要设计“中间奖励”,比如持仓期间的风险控制奖励。
  • 动作平滑:Agent可能今天满仓,明天空仓,后天又满仓。这种高频切换在实盘中手续费和滑点会吃掉所有利润。我一般会在奖励函数里加一个“动作变化惩罚”。

我的建议:先从最简单的版本开始。状态空间用5个特征,动作空间用连续值,奖励函数只用收益率+夏普比率。跑通了再慢慢加东西。别一上来就想搞个“全能Agent”。

好了,单Agent的设计思路就这些。你想想看,其实核心就是三个问题:看什么?做什么?为什么? 把这三个问题想清楚,Agent就能开始学习了。


专注资料整理