第 8 章 · 05 强化学习交易 本节摘要:强化学习(RL)是 FreqAI 里最强大也最复杂的范式。与回归/分类「给标签让模型学」不同,RL 让一个智能体(agent)在模拟交易环境里自己试错,靠奖励函数学会何时开仓、持有、平仓。本节讲清 RL 的基本概念(agent 与 environment)、三种动作空间环境(Base3/4/5ActionRLEnv)、奖励函数 的设计要点、与 Stable-Baselines3 的集成,以及 RL 特有的状态信息(state info)机制。RL 的门槛高于回归/分类,但它能学到回调难以表达的交易策略。 内容来源:原项目文档 ,源码 ,汉化并套用体系化模板。
本节摘要:强化学习(RL)是 FreqAI 里最强大也最复杂的范式。与回归/分类「给标签让模型学」不同,RL 让一个智能体(agent)在模拟交易环境里自己试错,靠奖励函数学会何时开仓、持有、平仓。本节讲清 RL 的基本概念(agent 与 environment)、三种动作空间环境(Base3/4/5ActionRLEnv)、奖励函数
calculate_reward的设计要点、与 Stable-Baselines3 的集成,以及 RL 特有的状态信息(state info)机制。RL 的门槛高于回归/分类,但它能学到回调难以表达的交易策略。
内容来源:原项目文档
docs/freqai-reinforcement-learning.md,源码freqtrade/freqai/RL/,汉化并套用体系化模板。
⚠️ 风险提示:RL 智能体可能学到「欺骗」奖励函数的「取巧」行为,在不真正盈利的情况下拿高分。默认的奖励函数是功能展示,不是为实盘设计的。你必须自写奖励函数或用社区验证的方案,且充分 Dry-Run。
阅读完本节,你应当能够:
calculate_reward 的设计原则。强化学习的两个主角:
训练循环如下:
与回归/分类的根本区别:RL 不需要标签——没有「正确答案是涨 2%」这种监督信号。取而代之的是奖励函数:你告诉 agent「赚钱奖励、亏钱惩罚、持仓太久扣分」,它自己摸索出能拿高分的交易策略。
💡 为什么 FreqAI + freqtrade 是 RL 的好搭档:RL 训练时能用「状态信息」(当前持仓、当前盈亏、持仓时长)来强化 agent。这些信息在 freqtrade 实盘里随手可得,而在纯研究框架里很难实时获取。FreqAI 把这套机制打通了。
FreqAI 提供三个预制环境,区别在于 agent 能选几个动作:
| 环境 | 动作数 | 动作内容 | 适用 |
|---|---|---|---|
| Base3ActionRLEnv | 3 | 持有 / 做多 / 做空 | 最简单,也支持只做多(can_short=False) |
| Base4ActionRLEnv | 4 | 做多 / 做空 / 持有中性 / 平仓 | 单独平仓动作 |
| Base5ActionRLEnv | 5 | 中性 / 多开 / 多平 / 空开 / 空平 | 最精细,多空各自独立平仓 |
以 Base5ActionRLEnv 为例,动作枚举:
class Actions(Enum): Neutral = 0 # 持有中性(空仓观望) Long_enter = 1 # 开多 Long_exit = 2 # 平多 Short_enter = 3 # 开空 Short_exit = 4 # 平空
策略里直接拿 agent 输出的 &-action 整数做入场/出场:
def populate_entry_trend(self, df, metadata): enter_long_conditions = [df["do_predict"] == 1, df["&-action"] == 1] df.loc[reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]] = (1, "long") enter_short_conditions = [df["do_predict"] == 1, df["&-action"] == 3] df.loc[reduce(lambda x, y: x & y, enter_short_conditions), ["enter_short", "enter_tag"]] = (1, "short") return df def populate_exit_trend(self, df, metadata): df.loc[(df["do_predict"]==1) & (df["&-action"]==2), "exit_long"] = 1 # 平多 df.loc[(df["do_predict"]==1) & (df["&-action"]==4), "exit_short"] = 1 # 平空 return df
⚠️ 只做多的场景:只有
Base3ActionRLEnv支持can_short = False(把做多当入场、做空当出场)。Base4/Base5 默认需要能做空。
RL 策略的特征工程和回归一样,但标签不同——RL 不需要标签,set_freqai_targets 里只填一个中性的 &-action 占位:
def set_freqai_targets(self, dataframe, **kwargs): # RL 没有标签,这是占位,agent 会覆盖它 dataframe["&-action"] = 0 return dataframe
另外,RL 的训练环境需要原始 OHLCV 来驱动行情,所以必须在 feature_engineering_standard 里把原始价格传进去:
def feature_engineering_standard(self, dataframe, **kwargs): # RL 训练环境必需的原始价格 dataframe["%-raw_close"] = dataframe["close"] dataframe["%-raw_open"] = dataframe["open"] dataframe["%-raw_high"] = dataframe["high"] dataframe["%-raw_low"] = dataframe["low"] return dataframe
💡 这意味着 RL 策略的
feature_engineering_standard要同时承担两个职责:给模型喂特征 + 给环境喂原始价格。别忘了 raw 系列。
calculate_reward 决定 agent 学成什么样。这是 RL 最需要创造力、也最容易出问题的地方。内置的默认奖励函数(在 ReinforcementLearner.py 里)是功能展示,不是生产代码——它会故意展示很多环境控制特性,但不保证盈利。
奖励函数的核心原则:
一个自定义奖励函数的骨架(继承 Base5ActionRLEnv):
from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv, Positions class MyCoolRLModel(ReinforcementLearner): class MyRLEnv(Base5ActionRLEnv): def calculate_reward(self, action: int) -> float: if not self._is_valid(action): return -2 # 无效动作重罚 pnl = self.get_unrealized_profit() factor = 100 # 鼓励入场(RSI 低时更鼓励) if action in (Actions.Long_enter.value, Actions.Short_enter.value) \ and self._position == Positions.Neutral: return 25 # 不鼓励一直空仓 if action == Actions.Neutral.value and self._position == Positions.Neutral: return -1 # 持仓太久递增惩罚 trade_duration = self._current_tick - self._last_trade_tick max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300) if self._position in (Positions.Short, Positions.Long) \ and action == Actions.Neutral.value: return -1 * trade_duration / max_trade_duration # 平仓时按盈亏给奖 if action == Actions.Long_exit.value and self._position == Positions.Long: return float(pnl * factor) if action == Actions.Short_exit.value and self._position == Positions.Short: return float(pnl * factor) return 0.
你可以从 self.raw_features 读取特征值来设计更聪明的奖励:
rsi_now = self.raw_features[f"%-rsi-period_10_shift-1_{pair}_{self.config['timeframe']}"].iloc[self._current_tick] if rsi_now < 40: factor = 40 / rsi_now # RSI 越低越鼓励入场
⚠️ 环境不是真实回测:RL 训练环境是高度简化的,不含
custom_exit、custom_stoploss、杠杆控制等复杂策略逻辑。它是一个「原始市场」,agent 靠奖励函数自学止盈止损。所以 agent 在环境里的表现 ≠ 真实回测表现,两者都要验证。
RL 的专属配置在 freqai.rl_config:
"rl_config": { "train_cycles": 25, "add_state_info": true, "max_trade_duration_candles": 300, "max_training_drawdown_pct": 0.02, "cpu_count": 8, "model_type": "PPO", "policy_type": "MlpPolicy", "model_reward_parameters": { "rr": 1, "profit_aim": 0.025 } }
关键参数:
| 参数 | 含义 |
|---|---|
train_cycles |
agent 在环境里循环训练多少轮(乘以数据点数) |
model_type |
Stable-Baselines3 的算法:PPO、A2C、DQN、TRPO、ARS、RecurrentPPO、MaskablePPO |
policy_type |
策略网络结构,默认 MlpPolicy |
net_arch |
网络架构,默认 [128, 128](两个 128 单位共享隐层) |
max_trade_duration_candles |
引导 agent 别持仓超过这个长度(在奖励函数里用) |
max_training_drawdown_pct |
训练中允许的最大回撤,超过就重置环境 |
add_state_info |
把持仓时长、当前盈亏、持仓方向加入特征(只 dry/live,回测自动关) |
cpu_count |
RL 训练线程数 |
randomize_starting_position |
随机化 episode 起点防过拟合 |
drop_ohlc_from_features |
不把归一化 OHLC 传给 agent(OHLC 仍驱动环境) |
💡 持续学习:RL 特别适合开
continual_learning: true——新模型从上一个模型继续训练,而不是每次从零开始。这能让 agent 累积经验。
RL 受益于训练指标追踪。FreqAI 集成了 Tensorboard:
tensorboard --logdir user_data/models/unique-id # 浏览器访问 127.0.0.1:6006
环境内置了 self.tensorboard_log(),每个 step 自动记录 agent 动作分布。你也可以在 calculate_reward 里加自定义计数:
def calculate_reward(self, action: int) -> float: if not self._is_valid(action): self.tensorboard_log("invalid") # 计数无效动作次数 return -2
如果是浮点指标,传第二个参数:self.tensorboard_log("float_metric", 0.23)。
set_freqai_targets 填 &-action=0 占位;feature_engineering_standard 必须传 raw OHLCV 驱动环境。train_cycles、model_type(PPO 等)、max_training_drawdown_pct、add_state_info(只 dry/live)。下一节,我们看自定义模型开发——怎么继承 BaseRegressionModel/BaseClassifierModel,实现
fit/train/predict,注册到user_data/freqaimodels/让--freqaimodel找到它。