开发帮助 (Development Help)
本页面面向 Freqtrade 的开发人员、希望为 Freqtrade 代码库或文档做出贡献的人员,以及希望了解他们所运行应用程序源代码的人员。
欢迎所有的贡献、错误报告、错误修复、文档改进、增强功能和创意。我们在 GitHub 上 跟踪问题,并在 Discord 上设有一个开发频道,您可以在那里提问。
文档 (Documentation)
文档位于 https://freqtrade.io,每个新功能的 PR 都需要提供相应的文档。
有关文档特殊字段(如笔记框等)的信息可以从 此处 找到。
要在本地测试文档,请使用以下命令。
pip install -r docs/requirements-docs.txt
mkdocs serve这将启动一个本地服务器(通常在 8000 端口),以便您可以查看一切是否符合您的预期。
开发者设置 (Developer setup)
要配置开发环境,您可以使用提供的 开发容器 (DevContainer),或使用 setup.sh 脚本并在询问“是否要安装用于开发的依赖项 [y/N]?”时回答“y”。 或者(例如,如果您的系统不支持 setup.sh 脚本),遵循手动安装过程并运行 pip3 install -r requirements-dev.txt —— 然后运行 pip3 install -e .[all]。
这将安装开发所需的所有工具,包括 pytest、ruff、mypy 和 coveralls。
运行以下命令安装 git hook 脚本:
pre-commit install这些 pre-commit 脚本会在每次提交前自动检查您的更改。 如果发现任何格式问题,提交将失败并提示修复。 这减少了不必要的 CI 失败,减轻了维护负担,并提高了代码质量。
必要时,您可以使用 pre-commit run -a 手动运行检查。
在开启拉取请求 (pull request) 之前,请先熟悉我们的 贡献指南 (Contributing Guidelines)。
开发容器设置 (Devcontainer setup)
开始使用的最快最简单的方法是使用带有远程容器扩展的 VSCode。 这使开发人员能够启动带有所有必需依赖项的机器人,而无需在本地机器上安装任何 freqtrade 特定的依赖项。
开发容器依赖项 (Devcontainer dependencies)
有关 远程容器扩展 的更多信息,最好参考其文档。
测试 (Tests)
新代码应由基础单元测试覆盖。根据功能的复杂程度,审查者可能会要求更深入的单元测试。 如有必要,Freqtrade 团队可以协助并指导编写好的测试(但请不要指望有人为您编写测试)。
如何运行测试 (How to run tests)
在根目录下使用 pytest 运行所有可用的测试案例,并确认您的本地环境设置正确。
功能分支 (Feature branches)
测试预计在 develop 和 stable 分支上通过。其他分支可能是正在进行中的工作,测试可能尚无法运行。
检查测试中的日志内容 (Checking log content in tests)
Freqtrade 使用 2 种主要方法来检查测试中的日志内容: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)
# 检查带有末尾数字的正则...
assert log_has_re(r"This dynamic event happened and produced \d+", caplog)调试配置 (Debug configuration)
要调试 freqtrade,我们推荐使用带有 Python 扩展的 VSCode,并使用以下启动配置(位于 .vscode/launch.json)。 细节显然会因设置而异 —— 但这应该足以让您开始。
{
"name": "freqtrade trade",
"type": "debugpy",
"request": "launch",
"module": "freqtrade",
"console": "integratedTerminal",
"args": [
"trade",
// 可选:
// "--userdir", "user_data",
"--strategy",
"MyAwesomeStrategy",
]
}命令行参数可以添加到 "args" 数组中。 这种方法也可以用于调试策略,方法是在策略中设置断点。
对于 PyCharm 也可以采用类似的设置 —— 使用 freqtrade 作为模块名称,并将命令行参数设置为“参数 (parameters)”。
正确使用虚拟环境 (venv)
使用虚拟环境时(您应该这么做),请确保您的编辑器使用的是正确的虚拟环境,以避免问题或“未知导入”错误。
VSCode
您可以使用命令“Python: Select Interpreter”在 VSCode 中选择正确的环境 —— 这将向您显示扩展检测到的环境。 如果您的环境未被检测到,您也可以手动选择路径。
PyCharm
在 PyCharm 中,您可以在“运行/调试配置 (Run/Debug Configurations)”窗口中选择适当的环境。 
启动目录 (Startup directory)
这假设您已经检出了仓库,并且编辑器是在仓库根目录级别启动的(因此 pyproject.toml 位于仓库的顶层)。
错误处理 (ErrorHandling)
Freqtrade 的异常都继承自 FreqtradeException。 然而,不应直接使用这个通用的错误类。相反,存在多个专门的子异常。
以下是异常继承体系的概览:
+ FreqtradeException
|
+---+ OperationalException
| |
| +---+ ConfigurationError
|
+---+ DependencyException
| |
| +---+ PricingError
| |
| +---+ ExchangeError
| |
| +---+ TemporaryError
| |
| +---+ DDosProtection
| |
| +---+ InvalidOrderException
| |
| +---+ RetryableOrderError
| |
| +---+ InsufficientFundsError
|
+---+ StrategyError插件 (Plugins)
交易对列表 (Pairlists)
您有一个关于新交易对选择算法的好主意并想尝试一下?太棒了。 希望您也想将此贡献回上游。
无论您的动机是什么 —— 这都应该能让您开始尝试开发一个新的交易对列表处理器 (Pairlist Handler)。
首先,看一下 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_posTIP
别忘了在 constants.py 的变量 AVAILABLE_PAIRLISTS 下注册您的交易对列表 —— 否则它将无法被选择。
现在,让我们逐步了解需要采取行动的方法:
交易对列表配置 (Pairlist configuration)
交易对列表处理器链的配置在机器人的配置文件中,位于 "pairlists" 元素中,这是链中每个交易对列表处理器的配置参数数组。
按照惯例,"number_assets" 用于指定交易对列表中要保留的最大交易对数量。请遵循此项以确保一致的用户体验。
可以根据需要配置其他参数。例如,VolumePairList 使用 "sort_key" 来指定排序值 —— 尽情根据您的出色算法指定任何成功且动态所需的参数。
short_desc
返回用于 Telegram 消息的描述。
这应包含处理器的名称,以及包含资产数量的简短描述。请遵循格式 "PairlistName - top/bottom X pairs"。
gen_pairlist
如果该处理器可以用作链中的主处理器,则覆盖此方法,定义最初的交易对列表,然后由链中的所有处理器处理。例如 StaticPairList 和 VolumePairList。
该方法在机器人的每次迭代中被调用(仅当处理器位于第一位时) —— 因此请考虑为计算/网络密集型计算实现缓存。
它必须返回结果交易对列表(然后该列表可能会传递到链中的下一个处理器)。
验证是可选的,父类公开了 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]:
# 生成动态白名单
pairs = self._calculate_pairlist(pairlist, tickers)
return pairs保护 (Protections)
最好阅读 保护文档 以了解保护功能。 本指南面向希望开发新保护措施的开发人员。
任何保护都不应直接使用 datetime,而应使用提供的 date_now 变量进行日期计算。这保留了对保护进行回测的能力。
编写新的保护
最好复制一个现有的保护措施作为参考示例。
实现新的保护 (Implementation of a new protection)
所有保护实现必须将 IProtection 作为 parent class(父类)。 因此,它们必须实现以下方法:
short_desc()global_stop()stop_per_pair()。
global_stop() 和 stop_per_pair() 必须返回一个 ProtectionReturn 对象,该对象由以下部分组成:
- 锁定交易对 - 布尔值
- 锁定截止时间 - datetime - 该交易对应被锁定到什么时候(将向上取整到下一个新 K 线)
- 原因 - 字符串,用于日志记录和存储在数据库中
- 锁定方向 (lock_side) - 'long'、'short' 或 '*'。
until(截止时间)部分应使用提供的 calculate_lock_end() 方法计算。
所有保护都应使用 "stop_duration" / "stop_duration_candles" 来定义一个交易对(或所有交易对)应被锁定多久。 其内容通过 self._stop_duration 被提供给每个保护。
如果您的保护需要回溯期,请使用 "lookback_period" / "lockback_period_candles" 来保持所有保护的一致性。
全局停用 vs. 局部停用 (Global vs. local stops)
保护措施可以有 2 种不同的方式来在有限时间内停止交易:
- 按交易对 (local)
- 为所有交易对 (globally)
保护 - 按交易对 (local)
实现按交易对方式的保护必须设置 has_local_stop=True。 每当一笔交易平仓(离场订单完成)时,stop_per_pair() 方法都会被调用。
保护 - 全局保护 (global)
这些保护应跨所有交易对执行评估,并相应地锁定所有交易对的交易(称为全局 PairLock)。 全局保护必须设置 has_global_stop=True 才能被评估为全局停用。 每当一笔交易平仓(离场订单完成)时,global_stop() 方法都会被调用。
保护 - 计算锁定结束时间 (calculating lock end time)
保护应根据它考虑的最后一笔交易来计算锁定结束时间。 这避免了如果回溯期长于实际锁定周期而导致的重新锁定。
IProtection 父类在 calculate_lock_end() 中为此提供了一个辅助方法。
实现一个新的交易所 (Implement a new Exchange - WIP)
INFO
本节尚在编写中 (WIP),并非测试新交易所与 Freqtrade 配合使用的完整指南。
INFO
在运行以下任何测试之前,请确保使用的是 CCXT 的最新版本。 您可以通过在激活的虚拟环境中运行 pip install -U ccxt 来获取最新版本的 ccxt。 这些测试不支持原生 Docker,但可用的开发容器将支持所有必需的操作以及最终必要的更改。
CCXT 支持的大多数交易所都应能开箱即用。
如果您需要实现特定的交易所类,这些类位于 freqtrade/exchange 源码文件夹中。您还需要将导入添加到 freqtrade/exchange/__init__.py 中,以使加载逻辑感知到新的交易所。 我们建议查看现有的交易所实现以了解可能需要什么。
WARNING
实现和测试某个交易所可能会经历大量的尝试和错误,因此请记住这一点。 您还应该具备一定的开发经验,因为这不是一项初学者的任务。
要快速测试交易所的公共端点,请在 tests/exchange_online/conftest.py 中为您的交易所添加配置,并使用 pytest --longrun tests/exchange_online/test_ccxt_compat.py 运行这些测试。 成功完成这些测试是一个很好的基础(实际上是一项要求),但这些测试并不能保证交易所功能完全正确,因为这仅测试了公共端点,没有测试私有端点(如生成订单或类似操作)。
还可以尝试使用 freqtrade download-data 下载较长时间范围(数个月)的数据,并验证数据下载是否正确(没有缺漏,实际下载了指定的时间范围)。
这些是将交易所列为“支持 (Supported)”或“社区测试过 (Community tested)”(列在首页上)的前提条件。 以下是“额外”项,它们能让交易所更完善(功能齐全)—— 但对于上述两类并不是绝对必需的。
待完成的其他测试/步骤:
- 验证
fetch_ohlcv()提供的数据 —— 并最终为该交易所调整ohlcv_candle_limit - 检查 L2 订单簿限制范围(API 文档)—— 并根据需要进行相应设置
- 检查余额是否正确显示 (*)
- 创建市价单 (*)
- 创建限价单 (*)
- 取消订单 (*)
- 完成交易(入场 + 离场)(*)
- 比较交易所和机器人之间的结果计算
- 确保费用应用正确(对照交易所检查数据库)
(*) 需要交易所的 API 密钥和余额。
交易所止损 (Stoploss On Exchange)
检查新交易所是否通过其 API 支持“交易所止损订单 (Stoploss on Exchange orders)”。
由于 CCXT 尚未提供“交易所止损”的统一封装,我们需要自己实现特定于交易所的参数。最好参考 binance.py 来了解这一点的示例实现。您需要查阅交易所 API 文档,了解具体如何实现。CCXT 的 Issue 也可能大有帮助,因为其他人可能已经为他们的项目实现了类似的东西。
不完整的 K 线 (Incomplete candles)
在抓取 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() # 使用您正在测试的交易所
timeframe = "1d"
pair = "BTC/USDT" # 确保使用该交易所上存在的交易对!
raw = ct.fetch_ohlcv(pair, timeframe=timeframe)
# 转换为 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 线(如上例所示)。 另一种方法是连续多次运行此命令,观察交易量是否在变化(而日期保持不变)。
更新币安缓存的杠杆层级 (Update binance cached leverage tiers)
更新杠杆层级应定期进行 —— 并且需要一个启用期货的功能齐全的身份验证帐户。
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()
# 假设在仓库根运行。
file = Path('freqtrade/exchange/binance_leverage_tiers.json')
json.dump(dict(sorted(lev_tiers.items())), file.open('w'), indent=2)该文件随后应贡献回上游,以便其他人也能从中受益。
更新示例 Notebook (Updating example notebooks)
为了保持 Jupyter notebook 与文档同步,在更新示例 notebook 后应运行以下操作:
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/zh/strategy_analysis_example.md回测文档结果 (Backtest documentation results)
要生成回测输出,请使用以下命令:
# 假设有一个专门的用户目录用于此输出
freqtrade create-userdir --userdir user_data_bttest/
# 设置 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持续集成 (Continuous integration)
这记录了为 CI 管道做出的一些决定。
- CI 在所有操作系统变体上运行,包括 Linux (Ubuntu)、macOS 和 Windows。
- 为分支
stable和develop构建 Docker 镜像,并作为多架构构建,通过同一个标签支持多个平台。 - 包含绘图依赖项的 Docker 镜像也可以通过
stable_plot和develop_plot获得。 - Docker 镜像包含一个文件
/freqtrade/freqtrade_commit,其中包含该镜像基于的提交。 - 每周通过计划任务运行一次完整的 Docker 镜像重构。
- 部署在 Ubuntu 上运行。
- 只有所有测试都通过,PR 才能合并到
stable或develop。
创建一个发布版本 (Creating a release)
文档的这一部分针对维护者,展示了如何创建一个发布版本。
创建发布分支 (Create release branch)
INFO
确保 stable 分支是最新的!
首先,选择大约一周前的提交(为了不包括最后添加的内容到发布中)。
# 创建新分支
git checkout -b new_release <commitid>确定在该提交与当前状态之间是否进行了关键的错误修复,并最终择友挑选(cherry-pick)这些修复。
- 将发布分支 (stable) 合并到此分支中。
- 编辑
freqtrade/__init__.py并添加与当前日期匹配的版本(例如 2025 年 7 月版本为2025.7)。如果该月需要进行第二次发布,微版本号可以是2025.7.1。版本号必须遵循 PEP0440 允许的版本,以避免推送到 pypi 时失败。 - 提交此部分。
- 将该分支推送到远程仓库,并针对 stable 分支 创建一个 PR。
- 将开发版本更新为遵循模式
2025.8-dev的下一个版本。
从 git commit 创建变更日志 (Create changelog from git commits)
# 需要在合并/拉取该分支之前完成。
git log --oneline --no-decorate --no-merges stable..new_release为了保持发布日志简短,最好将完整的 git 变更日志封装到可折叠的详情 (details) 部分中。
<details>
<summary>展开完整变更日志</summary>
... 完整的 git 变更日志
</details>FreqUI 发布 (FreqUI release)
如果 FreqUI 有实质性的更新,请确保在合并发布分支之前创建一个发布版本。 在合并发布之前,确保发布版本上的 freqUI CI 已完成并通过。
创建 GitHub 发布/标签 (Create github release / tag)
一旦针对 stable 分支的 PR 被合并(最好在合并后立即):
- 在 GitHub UI 中使用“Draft a new release”按钮(在发布子栏目下)。
- 使用指定的版本号作为标签。
- 使用 "stable" 作为参考(此步骤在上述 PR 合并后进行)。
- 使用上述变更日志作为发布说明(以代码块形式)。
- 使用以下代码片段作为新发布内容:
发布模板
# 更改亮点 (Highlighted changes)
- ...
## 如何更新 (How to update)
与往常一样,您可以使用以下命令之一更新您的机器人:
### docker-compose
```bash
docker-compose pull
docker-compose up -d
```
### 通过设置脚本安装 (Installation via setup script)
``` bash
# 停用虚拟环境并在其中运行
./setup.sh --update
```
### 普通原生安装 (Plain native installation)
``` bash
git pull
pip install -U -r requirements.txt
```
<details>
<summary>展开完整变更日志</summary>
```
<在此处粘贴您的变更日志>
```
</details>发布 (Releases)
pypi
手动发布
此过程已作为 GitHub Actions 的一部分自动化。 手动推送到 pypi 应是不必要的。
手动发布
要手动创建 pypi 发布版本,请运行以下命令:
额外要求:wheel、twine(用于上传),具有相应权限的 pypi 帐户。
pip install -U build
python -m build --sdist --wheel
# 用于 pypi 测试(检查安装的某些更改是否有效)
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
# 用于生产环境:
twine upload dist/*请不要将非发布版本推送到生产或真实的 pypi 实例中。
