跳至内容

开发帮助

本页面面向 Freqtrade 开发者,以及希望为 Freqtrade 代码库或文档做贡献的人,或想要了解自己所运行应用程序源代码的用户。

我们欢迎所有贡献,包括错误报告、缺陷修复、文档改进、功能增强以及创意建议。我们在 GitHub跟踪问题,并且在 Discord 上设有开发频道,您可以在其中提问。

文档

文档位于 https://freqtrade.io,每个新增功能的 PR 都必须附带相应的文档。

文档的特殊字段(如注释框等)可在此处 找到

要在本地测试文档,请使用以下命令。

pip install -r docs/requirements-docs.txt
mkdocs serve

这将启动一个本地服务器(通常在端口 8000),以便您查看文档是否符合预期。

开发者环境设置

要配置开发环境,您可以使用提供的 DevContainer,或运行 setup.sh 脚本并在被询问“是否要安装开发依赖 [y/N]?”时输入“y”。或者(例如,如果您的系统不受 setup.sh 脚本支持),请按照手动安装流程操作,并运行 pip3 install -r requirements-dev.txt,然后运行 pip3 install -e .[all]

这将安装开发所需的所有工具,包括 pytestruffmypycoveralls

然后通过运行 pre-commit install 安装 git 钩子脚本,以便在提交前对更改进行本地验证。这可以避免在 CI 上浪费等待时间,因为一些基本的格式检查会在本地机器上完成。

在提交 Pull Request 之前,请先熟悉我们的 贡献指南

Devcontainer 环境设置

最快最简单的方法是使用带有远程容器扩展的 VSCode。这使得开发者能够启动机器人并使用所有必需的依赖项,而无需在本地机器上安装任何 Freqtrade 特定的依赖项。

Devcontainer 依赖项

有关 远程容器扩展 的更多信息,请参考其官方文档。

测试

新代码应包含基本的单元测试。根据功能的复杂程度,评审人员可能会要求更深入的单元测试。如有需要,Freqtrade 团队可提供编写优质测试的协助和指导(但请不要期望他人为您编写测试)

如何运行测试

在项目根目录下使用 pytest 命令来运行所有可用的测试用例,以确认你的本地环境已正确配置。

功能分支

测试应在 developstable 分支上通过。其他分支可能尚在开发中,测试可能还未正常工作。

在测试中检查日志内容

Freqtrade 使用两种主要方法在测试中检查日志内容:log_has()log_has_re()(用于使用正则表达式检查动态日志消息)。这些方法可在 conftest.py 中找到,并可在任何测试模块中导入使用。

一个示例检查如下所示:

from tests.conftest import log_has, log_has_re

def test_method_to_test(caplog):
    method_to_test()

    assert log_has("This event happened", caplog)
    # Check regex with trailing number ...
    assert log_has_re(r"This dynamic event happened and produced \d+", caplog)

调试配置

为了调试 freqtrade,我们推荐使用 VSCode(安装 Python 扩展),并使用以下启动配置(位于 .vscode/launch.json 中)。具体细节会因环境而异,但此配置可帮助你快速开始。

{
    "name": "freqtrade trade",
    "type": "debugpy",
    "request": "launch",
    "module": "freqtrade",
    "console": "integratedTerminal",
    "args": [
        "trade",
        // Optional:
        // "--userdir", "user_data",
        "--strategy", 
        "MyAwesomeStrategy",
    ]
},

命令行参数可以添加到 "args" 数组中。该方法也可用于调试策略,只需在策略代码中设置断点即可。

PyCharm 也可以采用类似的配置——将 freqtrade 作为模块名,并将命令行参数设置为 "parameters"。

正确使用虚拟环境

当使用虚拟环境时(你应该这样做),请确保编辑器使用的是正确的虚拟环境,以避免出现问题或出现“未知导入”错误。

VSCode

你可以在 VSCode 中通过命令 "Python: Select Interpreter" 来选择正确的环境,该命令会显示插件检测到的所有环境。如果你的环境未被自动识别,也可以手动指定路径。

PyCharm

在 PyCharm 中,你可以在 "Run/Debug Configurations" 窗口中选择合适的环境。Pycharm debug configuration

启动目录

本说明假设你已经克隆了代码仓库,并且编辑器是从仓库根目录启动的(即 pyproject.toml 文件位于仓库的顶层目录)。

错误处理

Freqtrade 的所有异常都继承自 FreqtradeException。然而,这个通用错误类不应直接使用,而是应使用多个专用的子异常类。

以下是异常继承结构的概览:

+ FreqtradeException
|
+---+ OperationalException
|   |
|   +---+ ConfigurationError
|
+---+ DependencyException
|   |
|   +---+ PricingError
|   |
|   +---+ ExchangeError
|       |
|       +---+ TemporaryError
|       |
|       +---+ DDosProtection
|       |
|       +---+ InvalidOrderException
|           |
|           +---+ RetryableOrderError
|           |
|           +---+ InsufficientFundsError
|
+---+ StrategyError

插件

交易对列表

你有一个很棒的新交易对选择算法的想法想要尝试?太好了。希望你也愿意将此贡献回上游项目。

无论你的动机是什么——本文将帮助你开始开发一个新的交易对列表处理器。

首先,查看 VolumePairList 处理器,并最好将其复制为你的新交易对列表处理器的文件名。

这是一个简单的处理器,但可以很好地作为开发起点的示例。

接下来,修改处理器的类名(最好与模块文件名保持一致)。

基类提供了一个交易所实例(self._exchange)、一个交易对列表管理器(self._pairlistmanager),以及主配置(self._config)、交易对列表专用配置(self._pairlistconfig)和在交易对列表链中的绝对位置。

        self._exchange = exchange
        self._pairlistmanager = pairlistmanager
        self._config = config
        self._pairlistconfig = pairlistconfig
        self._pairlist_pos = pairlist_pos

提示

别忘了在 constants.py 文件中的 AVAILABLE_PAIRLISTS 变量下注册你的交易对列表,否则将无法被选择。

现在,我们来逐步了解需要实现的方法:

交易对列表配置

交易对列表处理器链的配置在机器人配置文件的 "pairlists" 元素中完成,该元素是一个数组,包含链中每个交易对列表处理器的配置参数。

按照惯例,使用 "number_assets" 来指定交易对列表中保留的最大交易对数量。请遵循此约定,以确保用户操作的一致性。

可根据需要配置其他参数。例如,VolumePairList 使用 "sort_key" 来指定排序依据——但请自由设定你的优秀算法所需的各种参数,使其更具成功性和动态性。

short_desc

返回一个用于 Telegram 消息的描述。

该描述应包含交易对列表处理器的名称,以及一个简短说明,包含资产数量。请遵循格式 "交易对列表名称 - 前/后 X 个交易对"

gen_pairlist

如果该交易对列表处理器可用作链中的首个处理器(即定义初始交易对列表,后续所有处理器将基于此列表进行处理),则需重写此方法。例如 StaticPairListVolumePairList

此方法在机器人每次迭代时都会被调用(仅当该处理器位于链的第一个位置时)——因此,对于计算或网络开销较大的操作,建议实现缓存机制。

它必须返回生成的交易对列表(该列表随后将传递给交易对列表处理器链中的其他处理器)。

验证是可选的,父类提供了 verify_blacklist(pairlist)_whitelist_for_active_markets(pairlist) 方法来进行默认过滤。如果你将结果限制在一定数量的交易对内,请使用这些方法,以确保最终结果不会比预期更短。

filter_pairlist

此方法由交易对列表管理器在每次调用时依次对链中的每个交易对列表处理器进行调用。

此方法在机器人每次迭代时都会被调用——因此,对于计算或网络开销较大的操作,建议实现缓存机制。

它接收一个交易对列表(可能是之前处理器处理后的结果)以及 tickers(即预先获取的 get_tickers() 结果)。

基类中的默认实现会简单地对列表中的每个交易对调用 _validate_pair() 方法,但你可以重写此方法。因此,你应要么在你的交易对列表处理器中实现 _validate_pair(),要么重写 filter_pairlist() 来执行其他逻辑。

如果重写了此方法,则必须返回处理后的交易对列表(该列表将传递给链中的下一个交易对列表处理器)。

验证是可选的,父类提供了 verify_blacklist(pairlist)_whitelist_for_active_markets(pairlist) 方法来执行默认过滤。如果你将结果限制为一定数量的交易对,请使用这些方法,以确保最终结果不会少于预期数量。

VolumePairList 中,这实现了不同的排序方法,并进行早期验证,从而仅返回预期数量的交易对。

示例
    def filter_pairlist(self, pairlist: list[str], tickers: dict) -> List[str]:
        # Generate dynamic whitelist
        pairs = self._calculate_pairlist(pairlist, tickers)
        return pairs

保护机制

最好先阅读 保护机制文档 来理解其工作原理。本指南主要面向希望开发新保护机制的开发者。

任何保护机制都不应直接使用 datetime,而应使用提供的 date_now 变量进行日期计算。这可以保留回测保护机制的能力。

编写新的保护机制

建议复制一个现有的保护机制作为参考示例。

新保护机制的实现

所有保护机制的实现都必须以 IProtection 作为父类。因此,它们必须实现以下方法:

  • short_desc()
  • global_stop()
  • stop_per_pair()

global_stop()stop_per_pair() 必须返回一个 ProtectionReturn 对象,该对象包含以下内容:

  • 锁定交易对 — 布尔值
  • 锁定至 — datetime — 交易对应被锁定到何时(时间将向上取整至下一个新 K 线)
  • 原因 — 字符串,用于日志记录和数据库存储
  • 锁定方向 — long、short 或 '*'。

其中 until 部分应使用提供的 calculate_lock_end() 方法进行计算。

所有保护机制都应使用 "stop_duration" / "stop_duration_candles" 来定义交易对(或所有交易对)应被锁定多长时间。该配置内容会作为 self._stop_duration 提供给每个保护机制。

如果你的保护机制需要回溯周期,请使用 "lookback_period" / "lookback_period_candles" 以保持所有保护机制的一致性。

全局停止与局部停止

保护机制可以通过两种不同的方式在有限时间内停止交易:

  • 按交易对(局部)
  • 对所有交易对(全局)
保护机制 — 按交易对

采用按交易对方式的保护机制必须设置 has_local_stop=Truestop_per_pair() 方法将在每次交易结束(退出订单完成)时被调用。

保护机制 — 全局保护

这类保护机制应在所有交易对上进行评估,因此会锁定所有交易对的交易(称为全局交易对锁定)。全局保护机制必须设置 has_global_stop=True 才会被评估是否触发全局停止。global_stop() 方法将在每次交易结束(退出订单完成)时被调用。

保护机制 — 计算锁定结束时间

保护机制应根据其考虑的最后一次交易来计算锁定结束时间。这可以避免在回溯周期长于实际锁定周期时重新锁定。

IProtection 父类为此提供了一个辅助方法 calculate_lock_end()


实现一个新的交易所(进行中)

注意

本节为进行中的工作,不是关于如何使用 Freqtrade 测试新交易所的完整指南。

注意

在运行以下任何测试之前,请确保使用最新版本的 CCXT。您可以通过在激活的虚拟环境中运行 pip install -U ccxt 来获取最新版本的 ccxt。这些测试不支持原生 Docker,但可用的开发容器将支持所有必需的操作以及最终必要的更改。

大多数由 CCXT 支持的交易所开箱即用。

如果您需要实现特定的交易所类,这些类位于 freqtrade/exchange 源代码文件夹中。您还需要将导入添加到 freqtrade/exchange/__init__.py,以便加载逻辑能够识别新交易所。
我们建议参考现有的交易所实现,以了解可能需要的内容。

警告

实现和测试一个交易所可能涉及大量试错,请注意这一点。您还应具备一定的开发经验,因为这不是一项适合初学者的任务。

要快速测试交易所的公共端点,请将您的交易所配置添加到 tests/exchange_online/conftest.py,并使用 pytest --longrun tests/exchange_online/test_ccxt_compat.py 运行这些测试。成功完成这些测试是一个良好的基础(实际上是必需的),但这些测试并不能保证交易所功能完全正确,因为它们仅测试了公共端点,而未测试私有端点(如生成订单等)。

还请尝试使用 freqtrade download-data 下载较长时间范围(数月)的数据,并验证数据是否正确下载(无缺失,实际下载了指定的时间范围)。

这些是将交易所列为“支持”或“社区测试”(在主页上列出)的前提条件。以下是“附加项”,可使交易所功能更完善(功能完整),但对于上述两类并非绝对必要。

需要完成的额外测试/步骤:

  • 验证 fetch_ohlcv() 提供的数据,并根据需要调整此交易所的 ohlcv_candle_limit
  • 检查 L2 订单簿的限制范围(API 文档),并根据需要进行设置
  • 检查余额是否正确显示 (*)
  • 创建市价单 (*)
  • 创建限价单 (*)
  • 取消订单 (*)
  • 完成交易(入场 + 出场)(*)
    • 比较交易所与机器人之间的结果计算
    • 确保手续费正确应用(核对数据库与交易所)

(*) 需要交易所的 API 密钥和余额。

交易所止损单

检查新交易所是否通过其 API 支持交易所止损单。

由于 CCXT 目前尚未提供对交易所止损单的统一支持,我们需要自行实现特定于交易所的参数。可以参考 binance.py 中的示例实现。你需要查阅交易所 API 的文档,了解具体如何实现。CCXT Issues 也可能提供很大帮助,因为其他人可能已在他们的项目中实现了类似功能。

不完整的 K 线

在获取 K 线(OHLCV)数据时,我们可能会得到不完整的 K 线(取决于交易所)。为了说明这一点,我们将使用日线("1d")来简化说明。我们通过 API(ct.fetch_ohlcv())查询指定周期的数据,并查看最后一条记录的日期。如果该条目发生变化或显示的是“不完整”K 线的日期,则应将其删除,因为不完整的 K 线会导致技术指标误判——指标通常假设传入的都是已完成的 K 线,否则会产生大量错误的买入信号。因此,默认情况下我们会移除最后一个 K 线,假设它是不完整的。

要检查新交易所的行为,可以使用以下代码片段:

import ccxt
from datetime import datetime, timezone
from freqtrade.data.converter import ohlcv_to_dataframe
ct = ccxt.binance()  # Use the exchange you're testing
timeframe = "1d"
pair = "BTC/USDT"  # Make sure to use a pair that exists on that exchange!
raw = ct.fetch_ohlcv(pair, timeframe=timeframe)

# convert to dataframe
df1 = ohlcv_to_dataframe(raw, timeframe, pair=pair, drop_incomplete=False)

print(df1.tail(1))
print(datetime.now(timezone.utc))
                         date      open      high       low     close  volume  
499 2019-06-08 00:00:00+00:00  0.000007  0.000007  0.000007  0.000007   26264344.0  
2019-06-09 12:30:27.873327

输出将显示来自交易所的最后一条记录以及当前的 UTC 日期。如果日期显示的是同一天,则可认为最后一个 K 线不完整,应当删除(保持交易所类中的设置 "ohlcv_partial_candle" 不变 / 为 True)。否则,将 "ohlcv_partial_candle" 设置为 False 以保留 K 线(如上面示例所示)。另一种方法是连续多次运行此命令,并观察成交量是否在变化(而日期保持不变)。

更新币安缓存的杠杆等级

应定期更新杠杆等级——这需要一个已认证且已启用期货功能的账户。

import ccxt
import json
from pathlib import Path

exchange = ccxt.binance({
    'apiKey': '<apikey>',
    'secret': '<secret>',
    'options': {'defaultType': 'swap'}
    })
_ = exchange.load_markets()

lev_tiers = exchange.fetch_leverage_tiers()

# Assumes this is running in the root of the repository.
file = Path('freqtrade/exchange/binance_leverage_tiers.json')
json.dump(dict(sorted(lev_tiers.items())), file.open('w'), indent=2)

然后应将此文件向上游贡献,以便其他人也能从中受益。

更新示例笔记本

为了保持 Jupyter 笔记本与文档同步,在更新示例笔记本后应运行以下命令。

jupyter nbconvert --ClearOutputPreprocessor.enabled=True --inplace freqtrade/templates/strategy_analysis_example.ipynb
jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to markdown freqtrade/templates/strategy_analysis_example.ipynb --stdout > docs/strategy_analysis_example.md

回测文档结果

要生成回测输出,请使用以下命令:

# Assume a dedicated user directory for this output
freqtrade create-userdir --userdir user_data_bttest/
# set can_short = True
sed -i "s/can_short: bool = False/can_short: bool = True/" user_data_bttest/strategies/sample_strategy.py

freqtrade download-data --timerange 20250625-20250801 --config tests/testdata/config.tests.usdt.json --userdir user_data_bttest/ -t 5m

freqtrade backtesting --config tests/testdata/config.tests.usdt.json -s SampleStrategy --userdir user_data_bttest/ --cache none --timerange 20250701-20250801

持续集成

本文档记录了 CI 流水线中的一些决策。

  • CI 在所有操作系统变体上运行,包括 Linux(Ubuntu)、macOS 和 Windows。
  • stabledevelop 分支构建 Docker 镜像,并以多架构方式构建,通过同一标签支持多个平台。
  • 包含绘图依赖的 Docker 镜像也提供为 stable_plotdevelop_plot
  • Docker 镜像中包含一个文件 /freqtrade/freqtrade_commit,其中记录了该镜像所基于的提交版本。
  • 完整的 Docker 镜像重建每周通过计划任务运行一次。
  • 部署在 Ubuntu 上执行。
  • 所有测试必须通过后,PR 才能合并到 stabledevelop 分支。

创建发布版本

本文档面向维护人员,说明如何创建一个发布版本。

创建发布分支

注意

确保 stable 分支是最新的!

首先,选择一个大约一周前的提交(以避免将最新更改包含在发布中)。

# create new branch
git checkout -b new_release <commitid>

确定在此提交与当前状态之间是否进行了关键的错误修复,并最终将这些修复摘取过来。

  • 将发布分支(stable)合并到此分支中。
  • 编辑 freqtrade/__init__.py,并添加与当前日期匹配的版本号(例如 2019.7 表示 2019 年 7 月)。如果当月需要进行第二次发布,可以使用次版本号,如 2019.7.1。版本号必须遵循 PEP0440 的允许格式,以避免向 pypi 推送时失败。
  • 提交此部分。
  • 将该分支推送到远程仓库,并创建一个针对稳定分支的 PR。
  • 将 develop 分支的版本更新为下一个版本,遵循 2019.8-dev 的格式。

从 git 提交记录生成更新日志

# Needs to be done before merging / pulling that branch.
git log --oneline --no-decorate --no-merges stable..new_release

为了保持发布日志简洁,最好将完整的 git 更新日志包裹在一个可折叠的 details 区块中。

<details>
<summary>Expand full changelog</summary>

... Full git changelog

</details>

FreqUI 发布

如果 FreqUI 有重大更新,请确保在合并发布分支之前创建一次发布。在合并发布分支前,确保 FreqUI 的 CI 构建已完成并通过。

创建 GitHub 发布 / 标签

一旦针对 stable 的 PR 被合并(最好在合并后立即进行):

  • 在 GitHub 界面中使用“Draft a new release”按钮(位于 releases 子部分)。
  • 使用指定的版本号作为标签。
  • 使用 "stable" 作为参考(此步骤在上述 PR 合并后进行)。
  • 使用上述更新日志作为发布说明(以代码块形式)。
  • 使用以下代码片段作为新发布的说明
发布模板
## Highlighted changes

- ...

### How to update

As always, you can update your bot using one of the following commands:

#### docker-compose

```bash
docker-compose pull
docker-compose up -d
```

#### Installation via setup script

```
# Deactivate venv and run 
./setup.sh --update
```

#### Plain native installation

```
git pull
pip install -U -r requirements.txt
```

<details>
<summary>Expand full changelog</summary>

```
<Paste your changelog here>
```

</details>

发布版本

PyPI

手动发布

此流程已作为 GitHub Actions 的一部分自动化。
通常无需手动向 PyPI 推送。

手动发布

如需手动创建 PyPI 发布,请运行以下命令:

额外要求:wheeltwine(用于上传),以及具有适当权限的 PyPI 账户。

pip install -U build
python -m build --sdist --wheel

# For pypi test (to check if some change to the installation did work)
twine upload --repository-url https://test.pypi.org/legacy/ dist/*

# For production:
twine upload dist/*

请勿将非正式版本推送到生产环境 / 真实的 PyPI 实例。