开发帮助¶
本页面旨在向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 -e .[all]
。
这将安装开发所需的所有工具,包括pytest
、ruff
、mypy
和coveralls
。
然后运行pre-commit install
安装git hook脚本,这样在提交前就能在本地验证你的更改。这样做可以避免等待CI,因为一些基本的格式检查已经在你的机器上完成了。
在提出拉取请求之前,请先阅读我们的贡献指南。
DevContainer配置¶
最快、最简单的方式是使用 VSCode与远程容器扩展。这样开发者就能够 无需 在本地安装任何freqtrade特定的依赖项的情况下,启动机器人并满足所有所需的依赖项。
DevContainer依赖项¶
有关远程容器扩展的更多信息,请参阅文档。
测试¶
新代码应该由基本单元测试来覆盖。根据功能的复杂性,审查人员可能会要求更深入的单元测试。 如果有必要,Freqtrade团队可以提供帮助并指导编写良好的测试(但请不要期望有人为您编写测试)。
如何运行测试¶
在根目录下使用 pytest
来运行所有可用的测试用例,以确认您的本地环境已正确设置。
功能分支
测试应该通过 develop
和 stable
分支。其他分支可能是正在进行中的工作,其中的测试尚未正常工作。
在测试中检查日志内容¶
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)
# 使用尾随数字的正则表达式进行检查 ...
assert log_has_re(r"This dynamic event happened and produced \d+", caplog)
调试配置¶
为了调试freqtrade,我们推荐使用VSCode(带有Python扩展),并使用以下启动配置(位于.vscode/launch.json
中)。
详细信息在不同设置之间可能会有所不同 - 但这应该能帮助你入门。
{
"name": "freqtrade trade",
"type": "python",
"request": "launch",
"module": "freqtrade",
"console": "integratedTerminal",
"args": [
"trade",
// 可选:
// "--userdir", "user_data",
"--strategy",
"MyAwesomeStrategy",
]
},
命令行参数可以添加到"args"
数组中。
该方法也可以用于调试策略,只需在策略内设置断点。
可以使用类似的设置来配置Pycharm - 使用freqtrade
作为模块名称,并将命令行参数设置为"parameters"。
正确使用虚拟环境
使用虚拟环境时(你应该这样做),请确保您的编辑器正在使用正确的虚拟环境,以避免出现问题或"unknown import"错误。
Vscode¶
在VSCode中,您可以使用命令"Python: Select Interpreter"来选择正确的环境 - 这将显示扩展检测到的环境。 如果您的环境未被检测到,您还可以手动选择路径。
Pycharm在PyCharm中,您可以在“Run/Debug Configurations”窗口中选择适当的环境。¶
Startup directory
这假设您已经检出了存储库,并且编辑器在存储库的根目录级别启动(因此setup.py位于存储库的顶级)。
错误处理¶
Freqtrade异常都继承自FreqtradeException
。
然而,这个通用的错误类不应该直接使用。而是存在多个专门的子异常。
下面是异常继承层次结构的概要:
+ FreqtradeException
|
+---+ OperationalException
|
+---+ DependencyException
| |
| +---+ PricingError
| |
| +---+ ExchangeError
| |
| +---+ TemporaryError
| |
| +---+ DDosProtection
| |
| +---+ InvalidOrderException
| |
| +---+ RetryableOrderError
| |
| +---+ InsufficientFundsError
|
+---+ StrategyError
插件¶
Pairlists¶
您对一种新的货币对选择算法有很好的想法?太好了。 希望您也愿意将其贡献给上游。不论你的动机是什么 - 这应该能帮助你开始开发一个新的Pairlist Handler。
首先,看一下VolumePairList Handler,并将该文件复制,并以新的Pairlist Handler的名称进行保存。
这是一个简单的Handler,并且作为一个很好的起点开发的示例。
接下来,修改Handler的类名(最好与模块文件名保持一致)。
基类提供了交易所的实例(self._exchange
),pairlist管理器(self._pairlistmanager
),以及主配置(self._config
),专用于pairlist的配置(self._pairlistconfig
)和pairlist在列表中的绝对位置。
self._exchange = exchange
self._pairlistmanager = pairlistmanager
self._config = config
self._pairlistconfig = pairlistconfig
self._pairlist_pos = pairlist_pos
提示
不要忘记在constants.py
中将你的pairlist注册到AVAILABLE_PAIRLISTS
变量下 - 否则它将无法被选择。
现在,让我们逐个步骤地来看需要采取行动的方法:
Pairlist配置¶
Pairlist Handlers的链式配置在机器人配置文件的"pairlists"
元素中完成,该元素是一个包含每个Pairlist Handlers的配置参数的数组。按照惯例,"number_assets"
被用来指定要在pairlist中保留的最大对数。为了确保一致的用户体验,请遵循这个惯例。
可以根据需要配置其他参数。例如,VolumePairList
使用"sort_key"
来指定排序值 - 但是请随意指定对于您的杰出算法来说必要的任何内容,以确保其成功和灵活性。
short_desc¶
返回用于Telegram消息的描述。
其中应包含Pairlist Handler的名称以及包含资产数量的简短描述。请使用格式"PairlistName - top/bottom X pairs"
。
gen_pairlist¶
如果Pairlist Handler可以作为链条中的首个Pairlist Handler,定义了首个pairlist,然后由链条中的所有Pairlist Handler进行处理,则覆盖此方法。StaticPairList
和VolumePairList
是示例。
此方法在每次迭代机器人时调用(仅当Pairlist Handler位于第一个位置时) - 因此请考虑为计算/网络密集型计算实现缓存。
它必须返回结果pairlist(然后可以传递给Pairlist Handler链条)。
验证是可选的,父类暴露了_verify_blacklist(pairlist)
和_whitelist_for_active_markets(pairlist)
来执行默认的过滤。如果将结果限制为一定数量的对数,请使用此功能,以确保最终结果不会比预期的更短。#### filter_pairlist
此方法由 pairlist 管理器针对链中的每个 Pairlist 处理程序调用。
每次机器人迭代时都会调用此方法 - 因此请考虑对于计算密集或网络密集的任务实现缓存。
它会接收一个 pairlist(可以是之前 pairlist 的结果)和 tickers
,一个预取的 get_tickers()
的版本。
基类中的默认实现只是对 pairlist 中的每个 pair 调用 _validate_pair()
方法,但您可以选择重写它。因此,您应该在 Pairlist 处理程序中实现 _validate_pair()
方法,或者重写 filter_pairlist()
方法以执行其他操作。
如果进行重写,它必须返回生成的 pairlist(该 pairlist 可以传递给链中的下一个 Pairlist 处理程序)。
验证是可选的,父类公开了 _verify_blacklist(pairlist)
和 _whitelist_for_active_markets(pairlist)
来进行默认的过滤。如果您将结果限制为一定数量的 pair,使用这些方法可以确保最终结果不会比预期更短。
在 VolumePairList
类中,这个方法实现了不同的排序方法,并进行早期验证,以确保仅返回预期数量的 pair。
示例¶
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
# 生成动态白名单
pairs = self._calculate_pairlist(pairlist, tickers)
return pairs
保护措施¶
最好阅读保护措施文档以了解保护措施。 本指南针对想要开发新保护措施的开发人员。
任何保护措施都不应直接使用 datetime,而应使用提供的 date_now
变量进行日期计算。这样可以保留回测保护措施的能力。
编写新的保护措施
最好将现有保护措施中的一个复制过来作为范例。
不要忘记在 constants.py
中的 AVAILABLE_PROTECTIONS
变量下注册您的保护措施,否则它将无法选择。
实现新的保护措施¶
所有保护措施的实现都必须具有 IProtection
作为父类。
因此,它们必须实现以下方法:
short_desc()
global_stop()
stop_per_pair()
。
global_stop()
和 stop_per_pair()
必须返回一个 ProtectionReturn 对象,该对象由以下内容组成:
- 锁定交易对 - 布尔值
- 锁定至 - 日期时间 - 应将交易对锁定至何时(将向上舍入到下一个新蜡烛)
- 原因 - 字符串,用于日志记录和数据库存储
- 锁定方向 - 多头、空头或 '*'。
until
部分应使用提供的 calculate_lock_end()
方法计算。所有保护措施都应使用 "stop_duration"
/ "stop_duration_candles"
来定义一个交易对(或所有交易对)应被锁定的时间长度。
每个保护措施都可以通过 self._stop_duration
来访问这个值。
如果您的保护措施需要一个回溯期,请使用 "lookback_period"
/ "lockback_period_candles"
来保持所有保护措施保持一致。
全局和局部止损¶
保护措施有两种不同的方式来限制交易的时间:
- 每个交易对的(局部)限制
- 所有交易对的(全局)限制
保护措施 - 每个交易对¶
实现每个交易对方式的保护措施必须设置 has_local_stop=True
。
每当交易关闭(退出订单完成)时,将调用 stop_per_pair()
方法。
保护措施 - 全局保护¶
这些保护措施应在所有交易对上进行评估,并因此也会禁止所有交易对的交易(称为全局交易对锁定)。
全局保护措施必须设置 has_global_stop=True
以进行全局止损的评估。
每当交易关闭(退出订单完成)时,将调用 global_stop()
方法。
保护措施 - 计算锁定结束时间¶
保护应该根据其考虑的最后一笔交易来计算锁定结束时间。 这样可以避免在回望期长于实际锁定期的情况下重新锁定。
IProtection
父类在calculate_lock_end()
中提供了一个辅助方法。
实现一个新的交易所(开发中)¶
注意
此部分正在开发中,不是完整的如何测试新交易所与Freqtrade的指南。
注意
在运行以下测试之前,请确保使用最新版本的CCXT。
您可以在激活的虚拟环境中运行pip install -U ccxt
来获取最新版本的ccxt。
这些测试不支持原生的docker,但可用的开发容器将支持所有所需的操作和必要的更改。
大多数CCXT支持的交易所都应该可以直接使用。
要快速测试交易所的公共端点,请将您的交易所配置添加到test_ccxt_compat.py
中,并使用pytest --longrun tests/exchange/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() # 使用您正在测试的交易所
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))
日期 开盘 最高 最低 收盘 交易量
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()
# 假设此代码在存储库的根目录中运行。
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
持续集成¶
这里记录了CI流水线的一些决策。
- CI运行在所有操作系统变量上,包括Linux(Ubuntu),macOS和Windows。
- Docker镜像是针对
stable
和develop
分支构建的,构建为多体系结构构建,通过相同的标签支持多平台。 - 包含绘图依赖关系的Docker镜像也可用于
stable_plot
和develop_plot
。 - Docker镜像包含一个文件
/freqtrade/freqtrade_commit
,其中包含此镜像所基于的提交。 - 完整的Docker镜像重建是通过时间表每周运行一次。
- 部署在Ubuntu上运行。
- ta-lib二进制文件包含在build_helpers目录中,以避免与外部不可用性相关的失败。
- 所有测试必须通过才能将PR合并到
stable
或develop
上。
创建一个发布版本¶
此部分文档面向维护者,展示了如何创建一个发布版本。### 创建发布分支
!!!注意
确保stable
分支是最新的!
首先,选择一个约为一周前的提交(不包括最新的发布内容)。
# 创建新的分支
git checkout -b new_release <commitid>
确定在此提交和当前状态之间是否进行了关键的错误修复,并可能选择性地挑选这些修复。
- 将发布分支(stable)合并到此分支。
- 编辑
freqtrade/__init__.py
文件,并添加与当前日期匹配的版本(例如,对于2019年7月,版本可以是2019.7
)。如果需要在同一个月发布第二次版本,则次要版本可以是2019.7.1
。版本号必须遵循PEP0440的允许版本规则,以避免在推送到pypi时失败。 - 提交此部分。
- 推送该分支到远程仓库,并针对stable分支创建一个 PR。
- 将开发版号更新为下一个版本,遵循
2019.8-dev
的模式。
从Git提交中创建更改日志¶
# 在合并/拉取该分支之前需要执行此步骤。
git log --oneline --no-decorate --no-merges stable..new_release
为了保持发布日志的简洁,最好将完整的Git更改日志包装在一个可折叠的详情部分中。
<details>
<summary>展开完整更改日志</summary>... 完整的git更改日志
</details>
### FreqUI 发布
如果 FreqUI 有重大更新,在合并发布分支之前,请确保创建一个发布。在合并发布之前,请确保 freqUI 的 CI 完成并通过。
### 创建 GitHub 发布/标签
一旦针对 stable 分支的 PR 合并(最好在合并之后):
* 在 Github UI(releases 子部分)中使用 "Draft a new release" 按钮。
* 使用指定的版本号作为标签。
* 使用 "stable" 作为引用(在上述 PR 合并之后进行此步骤)。
* 使用上述更改日志作为发布评论(作为代码块)。
* 使用下面的代码片段来创建新的发布
??? 提示 "发布模板"
````
## 强调的更改
- ...
### 如何更新
和往常一样,你可以使用以下命令之一来更新你的机器人:
#### 使用 docker-compose
```bash
docker-compose pull
docker-compose up -d
```
#### 使用安装脚本进行安装
```
# 停用虚拟环境并运行
./setup.sh --update
```
#### 使用普通本地安装
```
git pull
pip install -U -r requirements.txt
```
<details>
<summary>展开完整的变更日志</summary>
```
<在此处粘贴您的变更日志>
```
</details>
````
## 发布
### pypi
!!! 注意
此过程现在已作为Github Actions的一部分自动化。
要创建一个pypi发布,请运行以下命令:
额外要求:`wheel`,`twine`(用于上传),在pypi上具有适当权限的账户。
``` bash
python setup.py sdist bdist_wheel
# 用于pypi测试(检查安装是否有改动)
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
# 用于生产环境:
twine upload dist/*
请不要将非发布版本推送到生产/真实的pypi实例。