2026-02-18 11:41:01 +08:00

171 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
from typing import Any, Dict
import numpy as np
from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner
from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv, Positions
logger = logging.getLogger(__name__)
class MyCoolRLModel(ReinforcementLearner):
"""
针对高波动资产 (如 PEPE) 优化的强化学习模型。
核心改进:
1. 移除入场奖励,引入开仓成本惩罚。
2. 引入非线性盈亏奖励,鼓励捕捉大趋势。
3. 严厉惩罚浮亏持仓(抗单行为)。
4. 增加对大户操纵的识别和防御机制。
"""
class MyRLEnv(Base5ActionRLEnv):
"""
自定义环境,重写 calculate_reward 以适应动量交易和大户操纵防御。
"""
def calculate_reward(self, action: int) -> float:
"""
奖励函数的示例。这是用户可能希望
注入自己创意的一个函数。
警告!
此函数是功能展示,旨在展示尽可能多的
环境控制功能。它还设计用于在小型计算机上快速运行。
这是一个基准,*不* 用于生产环境。
:param action: int = 智能体为当前K线做出的动作。
:return:
float = 给智能体当前步骤的奖励(用于优化
神经网络中的权重)
"""
# 首先,如果动作无效,则惩罚
if not self._is_valid(action):
self.tensorboard_log("invalid", category="actions")
return -2.0
# 获取核心状态数据
pnl = self.get_unrealized_profit() # 当前浮动盈亏 (百分比,如 0.01 代表 1%)
# 获取持仓时间 (K线数量)
trade_duration = 0
if self._last_trade_tick is not None:
trade_duration = self._current_tick - self._last_trade_tick
# 读取配置中的最大持仓时间,默认 100 根 K 线
max_trade_duration = self.rl_config.get("max_trade_duration_candles", 100)
# 奖励累加器
reward = 0.0
# =========================================================
# 场景 A: 决定入场 (Long Enter / Short Enter)
# =========================================================
if action in (Actions.Long_enter.value, Actions.Short_enter.value):
if self._position == Positions.Neutral:
# 入场给予轻微惩罚,避免过度交易,但不要太重
# 从 -0.05 改为 -0.01,让模型更愿意尝试入场
return -0.01
# =========================================================
# 场景 B: 观望 (Neutral)
# =========================================================
if action == Actions.Neutral.value and self._position == Positions.Neutral:
# 空仓观望给予轻微惩罚,鼓励模型寻找机会
# 从 0 改为 -0.001,避免模型一直观望
return -0.001
# =========================================================
# 场景 C: 持仓中 (Holding) - 每一根 K 线都会触发
# =========================================================
if self._position in (Positions.Short, Positions.Long):
# 如果当前动作是继续持有 (Neutral)
if action == Actions.Neutral.value:
# 1. 时间管理:希望持仓,但不要超时
# - 前50%时间:轻微奖励,鼓励持仓捕捉趋势
# - 50%-80%时间:轻微惩罚
# - 超过80%时间:惩罚加重
time_ratio = trade_duration / max_trade_duration
if time_ratio < 0.5:
# 前半段时间:轻微奖励,鼓励持仓
time_reward = 0.005 * (1 - time_ratio * 2)
reward += time_reward
elif time_ratio < 0.8:
# 50%-80%时间:轻微惩罚
time_penalty = -0.01 * ((time_ratio - 0.5) / 0.3)
reward += time_penalty
else:
# 超过80%时间:惩罚加重,提醒模型该离场了
time_penalty = -0.02 * ((time_ratio - 0.8) / 0.2) - 0.01
reward += time_penalty
# 2. 浮动盈亏反馈 (关键!)
if pnl > 0:
# 浮盈:给予微弱的正反馈,鼓励拿住趋势
# 使用 log 函数让奖励增长平缓,避免模型过于贪婪而不止盈
reward += np.log(1 + pnl) * 0.5
else:
# 浮亏:给予惩罚,但不要太严厉
# 从 abs(pnl) * 2.0 改为 abs(pnl) * 1.0
reward -= (abs(pnl) * 1.0)
# 3. 止损惩罚加速
# 如果浮亏超过 3%,给予额外的惩罚,但不要太严厉
if pnl < -0.03:
reward -= 0.5 # 从 -1.0 改为 -0.5
# 4. 【新增】检测是否在大户操纵中被套
# 如果刚入场不久就出现大幅反向波动,可能是被大户收割
if trade_duration < 5 and pnl < -0.01:
# 入场后5根K线内就亏损超过1%,很可能是被大户骗了
reward -= 1.0 # 从 -3.0 改为 -1.0,不要太严厉
# =========================================================
# 场景 D: 离场结算 (Exit)
# =========================================================
if (action == Actions.Long_exit.value and self._position == Positions.Long) or \
(action == Actions.Short_exit.value and self._position == Positions.Short):
# 基础因子
factor = 10.0
# 【核心修改 3】非线性结算奖励
if pnl > 0:
# 盈利:奖励 = PnL * 因子
# 如果这笔交易是大赚 (比如 > 2%),因子翻倍
if pnl > 0.02:
factor *= 2.0
# 最终奖励
total_reward = pnl * factor
# 额外奖励:如果这笔交易很快就赚了钱(高效率)
if trade_duration < (max_trade_duration * 0.2):
total_reward *= 1.5
# 【新增】奖励快速脱离陷阱
# 如果是因被大户收割而被迫止损,但及时离场,给予一定奖励
# 这鼓励模型快速识别并逃离陷阱
if trade_duration < 5 and pnl < 0 and pnl > -0.02:
# 在5根K线内止损亏损小于2%,说明及时逃离了陷阱
total_reward += 0.5 # 给予小奖励,奖励快速止损
return float(total_reward)
else:
# 亏损:惩罚 = PnL * 因子
# 亏损时的惩罚系数通常要比盈利系数大,模拟"损失厌恶"
# 这会让模型非常忌惮亏损离场
loss_factor = 15.0
# 【新增】区分不同类型的亏损
# 如果是被大户收割导致的快速亏损,惩罚可以适当减轻
# 因为这是市场环境问题,而非模型判断错误
if trade_duration < 5 and pnl < -0.02:
# 快速大幅亏损,可能是被大户收割
loss_factor = 10.0 # 减轻惩罚,因为这是市场陷阱
else:
loss_factor = 15.0 # 正常惩罚
return float(pnl * loss_factor)
return float(reward)