强化学习¶
安装大小
强化学习的依赖项包括诸如torch
之类的大型软件包,应在./setup.sh -i
期间明确要求,回答 "y" 并回答 "Do you also want dependencies for freqai-rl (~700mb additional space required) [y/N]?" 这个问题。
偏好使用 Docker 的用户应确保他们使用的是带有 _freqairl
后缀的 Docker 镜像。
背景和术语¶
什么是RL以及FreqAI为什么需要它?¶
强化学习包括两个重要组件,即agent(代理程序)和训练环境。在代理训练期间,代理程序通过历史数据以蜡烛为单位移动,每次执行一组动作:多头进场、多头退出、空头进场、空头退出、中性。在这个训练过程中,环境追踪这些动作的性能,并根据用户自定义的calculate_reward()
函数对代理程序进行奖励(如果用户愿意,我们提供了默认的奖励,供用户进行构建详细信息请参见此处)。奖励被用来训练神经网络中的权重。
FreqAI RL实现的第二个重要组件是使用state信息。状态信息在每个步骤中被输入到网络中,包括当前收益、当前仓位和当前交易持续时间。这些信息用于在训练环境中训练代理程序,并在干扰/实际操作中加强代理程序(这个功能在回测中不可用)。FreqAI + Freqtrade对于这种增强机制来说是一个完美的匹配,因为这些信息在实时部署中可以很容易地获得。
强化学习是 FreqAI 的一个自然发展,因为它增加了一层适应性和市场反应能力,这是分类器和回归器无法匹敌的。然而,分类器和回归器具有强大的预测能力,而 RL 没有,因为训练不当的 RL 代理程序可能会找到 "作弊" 和 "诡计" 来最大化奖励,而实际上并没有赢得任何交易。因此,RL 比典型的分类器和回归器更复杂,需要更高水平的理解。
RL界面¶
在当前框架中,我们的目标是通过常见的 "预测模型" 文件来暴露训练环境,这是一个继承自BaseReinforcementLearner
对象的用户自定义类(例如freqai/prediction_models/ReinforcementLearner
)。在这个用户类内部,可以通过 MyRLEnv
对 RL 环境进行自定义,如下所示详细信息请参见此处。
我们预计大多数用户将把精力集中在创造性设计calculate_reward()
函数上详细信息请参见此处,同时保持其余环境不变。其他用户可能根本不会触摸环境,他们只会使用已存在于 FreqAI 中的配置设置和功能强大的特征工程。同时,我们也允许高级用户完全创建自己的模型类。该框架基于stable_baselines3(torch)和OpenAI gym构建,用于实现基本环境类。但总体而言,模型类是很好隔离的。因此,可以很容易地将竞争的库集成到现有框架中。对于环境而言,它继承自gym.Env
,这意味着要切换到另一个库,必须编写一个全新的环境。
重要考虑因素¶
如上所述,代理在人工交易"环境"中进行“训练”。在我们的情况下,该环境可能与实际的Freqtrade回测环境很相似,但实际上它们是不同的。事实上,强化学习训练环境要简化得多。它不包括任何复杂的策略逻辑,例如custom_exit
、custom_stoploss
、杠杆控制等回调函数。相反,强化学习环境是市场真实情况的一个非常“原始”的表示,代理有自由选择学习策略(即:止损、获利等),这由calculate_reward()
强制执行。因此,重要的是要考虑到代理训练环境与真实世界不完全相同。
运行强化学习¶
设置和运行强化学习模型与运行回归器或分类器相同。在命令行上必须定义相同的两个标志,--freqaimodel
和--strategy
:
freqtrade trade --freqaimodel ReinforcementLearner --strategy MyRLStrategy --config config.json
其中,ReinforcementLearner
将使用freqai/prediction_models/ReinforcementLearner
中的模板化ReinforcementLearner
(或位于user_data/freqaimodels
中的自定义定义的模型)。而策略则遵循与典型回归器相同的基本特征工程,采用feature_engineering_*
。不同之处在于目标的创建,强化学习不需要目标。然而,FreqAI要求在动作列中设置一个默认(中性)值:
def set_freqai_targets(self, dataframe, **kwargs) -> DataFrame:
"""
*Only functional with FreqAI enabled strategies*
Required function to set the targets for the model.
All targets must be prepended with `&` to be recognized by the FreqAI internals.
More details about feature engineering available:
https://www.freqtrade.io/en/latest/freqai-feature-engineering:参数 df: 策略数据框,其中将接收目标
用法示例:dataframe["&-target"] = dataframe["close"].shift(-1) / dataframe["close"]
"""
# 对于 RL,没有直接设置的目标。这是一个填充值(中性值)
# 直到代理发送一个动作。
dataframe["&-action"] = 0
return dataframe
大部分函数与typical Regressors相同,但下面的函数展示了策略如何将原始价格数据传递给代理,以便它在训练环境中获取原始 OHLCV 数据:
def feature_engineering_standard(self, dataframe: DataFrame, **kwargs) -> DataFrame:
# RL 模型需要以下特征
dataframe[f"%-raw_close"] = dataframe["close"]
dataframe[f"%-raw_open"] = dataframe["open"]
dataframe[f"%-raw_high"] = dataframe["high"]
dataframe[f"%-raw_low"] = dataframe["low"]
return dataframe
最后,没有明确的“label”要创建 - 相反,需要分配 &-action
列,其中包含在 populate_entry/exit_trends()
中访问时代理的动作。
在本例中,中性动作为 0。这个值应该与所使用的环境保持一致。FreqAI 提供了两个环境,两个环境都使用 0 作为中性动作。
当用户意识到没有要设置的标签后,他们很快就会理解代理正在做出“独立”的进出决策。这使得策略构建变得非常简单。进出信号是以整数形式由代理提供的 - 直接用于决定策略的进出:
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
enter_long_conditions = [df["do_predict"] == 1, df["&-action"] == 1]
if enter_long_conditions:
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]
if enter_short_conditions:
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: DataFrame, metadata: dict) -> DataFrame:
exit_long_conditions = [df["do_predict"] == 1, df["&-action"] == 2]
if exit_long_conditions:
df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1
exit_short_conditions = [df["do_predict"] == 1, df["&-action"] == 4]
if exit_short_conditions:
df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1
return df
重要的是要考虑到&-action
取决于他们选择使用的环境。上面的示例展示了5个操作,其中0表示中性,1表示进入做多,2表示退出做多,3表示进入做空,4表示退出做空。
配置强化学习器¶
为了配置强化学习器
,在freqai
配置中必须存在以下字典:
"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
决定了代理程序在其人工环境中通过蜡烛图数据训练模型的权重的次数。model_type
是一个字符串,用于选择stable_baselines(外部链接)中可用的模型之一。
注意
如果要尝试continual_learning
,则应将主freqai
配置字典中的该值设置为true
。这将告诉强化学习库从以前模型的最终状态继续训练新模型,而不是每次重新训练时都从头开始训练新模型。!!! 注意
请记住,通用的model_training_parameters
字典应包含特定model_type
的所有模型超参数自定义。例如,PPO
参数可以在这里找到。
创建自定义奖励函数¶
非生产使用
警告!
Freqtrade源代码中提供的奖励函数是展示尽可能多的环境控制功能的功能展示,旨在在小型计算机上快速运行。这是一个基准测试,不适用于实际生产环境。请注意,您需要创建自己的custom_reward()
函数或使用由Freqtrade源代码外的其他用户构建的模板。
当您开始修改策略和预测模型时,您会很快意识到强化学习器和回归/分类器之间的一些重要区别。首先,策略不会设置目标值(没有标签!)。相反,您在MyRLEnv
类内部设置calculate_reward()
函数(请参见下文)。prediction_models/ReinforcementLearner.py
内提供了一个默认的calculate_reward()
来展示创建奖励所需的构建模块,但是这并不适用于实际生产环境。用户必须创建自己的自定义强化学习模型类,或者使用来自Freqtrade源代码外部的预构建模型,并将其保存到user_data/freqaimodels
目录下。在calculate_reward()
函数内部,您可以表达对市场的创造性理论。例如,您可以在代理进行一次盈利交易时奖励代理,而在代理进行一次亏损交易时惩罚代理。或者,您希望奖励代理进入交易,并惩罚代理在交易中停留时间过长。下面我们展示如何计算这些奖励的示例:
提示
最好的奖励函数是连续可微分且具有良好缩放的函数。换句话说,对罕见事件添加一个大的负惩罚并不是一个好主意,神经网络无法学习这个函数。相反,最好的做法是对常见事件添加一个小的负惩罚。这将帮助代理快速学习。此外,您可以通过根据某些线性/指数函数将惩罚与严重性相匹配来改善奖励/惩罚的连续性。换句话说,您可以随着交易持续时间的增加缓慢增加惩罚的规模。这比单个大的惩罚发生在单个时间点更好。
from freqtrade.freqai.prediction_models.ReinforcementLearner import ReinforcementLearner
from freqtrade.freqai.RL.Base5ActionRLEnv import Actions, Base5ActionRLEnv, Positions
class MyCoolRLModel(ReinforcementLearner):
"""
用户创建的强化学习预测模型。
将此文件保存到`freqtrade/user_data/freqaimodels`目录中
然后使用以下命令运行:
freqtrade trade --freqaimodel MyCoolRLModel --config config.json --strategy SomeCoolStrat这里用户可以覆盖`IFreqaiModel`继承树中的任何函数。对于强化学习来说,最重要的是用户可以在这里覆盖`MyRLEnv`(见下文),以定义自定义的`calculate_reward()`函数,或者覆盖环境的任何其他部分。
这个类还允许用户覆盖IFreqaiModel树的任何其他部分。例如,用户可以覆盖`def fit()`、`def train()`、`def predict()`等函数,以对这些过程进行细致控制。
另一个常见的覆盖可能是`def data_cleaning_predict()`函数,用户可以在这里对数据处理流程进行细致控制。
"""
class MyRLEnv(Base5ActionRLEnv):
"""
用户制作的自定义环境。这个类继承自BaseEnvironment和gym.Env。用户可以覆盖这些父类中的任何函数。下面是一个用户自定义的`calculate_reward()`函数的示例。
警告!
这个函数是为了展示尽可能多的环境控制功能而设计的。它还被设计成在小型计算机上快速运行。这是一个基准测试,*不适用于实时生产*。
"""
def calculate_reward(self, action: int) -> float:
# 首先,如果动作无效,则惩罚
if not self._is_valid(action):
return -2
pnl = self.get_unrealized_profit()
factor = 100
pair = self.pair.replace(':', '')
# 您可以使用数据框中的特征值
# 假设已在策略中生成了偏移RSI指标。
rsi_now = self.raw_features[f"%-rsi-period_10_shift-1_{pair}_"
f"{self.config['timeframe']}"].iloc[self._current_tick]
# 奖励代理进入交易
if (action in (Actions.Long_enter.value, Actions.Short_enter.value)
and self._position == Positions.Neutral):
if rsi_now < 40:
factor = 40 / rsi_now
else:
factor = 1
return 25 * factor
# 阻止代理不进入交易
if action == Actions.Neutral.value and self._position == Positions.Neutral:
return -1
max_trade_duration = self.rl_config.get('max_trade_duration_candles', 300)
trade_duration = self._current_tick - self._last_trade_tick
if trade_duration <= max_trade_duration:
factor *= 1.5
elif trade_duration > max_trade_duration:
factor *= 0.5
# 阻止持仓
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:
if pnl > self.profit_aim * self.rr:
factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2)
return float(pnl * factor)
# 平掉空头
if action == Actions.Short_exit.value and self._position == Positions.Short:
if pnl > self.profit_aim * self.rr:
factor *= self.rl_config['model_reward_parameters'].get('win_reward_factor', 2)
return float(pnl * factor)
return 0.
使用Tensorboard¶
强化学习模型受益于跟踪训练指标。FreqAI已经集成了Tensorboard,允许用户跟踪所有货币和所有重新训练中的训练和评估性能。通过以下命令激活Tensorboard:
tensorboard --logdir user_data/models/unique-id
其中unique-id
是在freqai
配置文件中设置的identifier
。必须在单独的shell中运行此命令,以便在浏览器中查看输出,地址为127.0.0.1:6006(6006是Tensorboard使用的默认端口)。
自定义日志记录¶
FreqAI还提供了一个内置的情节摘要记录器,名为self.tensorboard_log
,用于向Tensorboard日志中添加自定义信息。默认情况下,此函数已经在环境内的每个步骤中调用一次,用于记录代理的动作。在每个情节结束时,所有步骤累计的值将被报告,并重置所有指标为0,准备下一个情节的开始。
self.tensorboard_log
也可以在环境的任何地方使用,例如可以将其添加到calculate_reward
函数中,以收集关于奖励各部分的调用频率的更详细信息:
class MyRLEnv(Base5ActionRLEnv):
"""
用户自定义环境。此类继承自BaseEnvironment和gym.Env。
用户可以覆盖这些父类中的任何函数。这是一个用户自定义的`calculate_reward()`函数的示例。
"""
def calculate_reward(self, action: int) -> float:
if not self._is_valid(action):
self.tensorboard_log("invalid")
return -2
注意
self.tensorboard_log()
函数仅用于跟踪增量对象,即训练环境内的事件和动作。如果感兴趣的事件是一个浮点数,则可以将浮点数作为第二个参数传递,例如self.tensorboard_log("float_metric1", 0.23)
。在这种情况下,指标值不会被累加。
选择基本环境¶
FreqAI提供了三个基本环境,分别是Base3ActionRLEnvironment
、Base4ActionEnvironment
和Base5ActionEnvironment
。根据名称可以看出,这些环境是为了能够在3、4或5个操作中选择的代理准备的。Base3ActionEnvironment
是最简单的,代理可以选择保持(hold)、长(long)或短(short)。这个环境也可以用于仅限多头(long-only)机器人(它会自动遵循策略中的can_short
标志),其中长是进入条件,短是退出条件。与此同时,在Base4ActionEnvironment
中,代理可以选择进入多头、进入空头、保持中立或退出持仓。最后,在Base5ActionEnvironment
中,代理拥有与Base4相同的操作,但不同之处在于它将退出操作分为退出多头和退出空头。环境选择带来的主要变化包括:
calculate_reward
中可用的操作- 用户策略使用的操作
所有由FreqAI提供的环境都继承自一个与操作/持仓无关的环境对象,称为BaseEnvironment
,其中包含了所有共享的逻辑。这种架构的设计旨在易于定制。最简单的定制是calculate_reward()
(详见这里)。然而,这些定制可以进一步扩展到环境内部的任何函数中。您只需在预测模型文件中的MyRLEnv
中覆盖这些函数即可实现。或者,对于更高级的定制,鼓励创建一个完全从BaseEnvironment
继承的全新环境。
注意
仅有Base3ActionRLEnv
可以进行仅限多头的训练/交易(设置用户策略属性can_short = False
)。