Skip to content

开发帮助

本页面旨在向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]

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

然后运行pre-commit install安装git hook脚本,这样在提交前就能在本地验证你的更改。这样做可以避免等待CI,因为一些基本的格式检查已经在你的机器上完成了。

在提出拉取请求之前,请先阅读我们的贡献指南

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)
    # 使用尾随数字的正则表达式进行检查 ...
    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”窗口中选择适当的环境。

Pycharm debug configuration

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进行处理,则覆盖此方法。StaticPairListVolumePairList是示例。

此方法在每次迭代机器人时调用(仅当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镜像是针对stabledevelop分支构建的,构建为多体系结构构建,通过相同的标签支持多平台。
  • 包含绘图依赖关系的Docker镜像也可用于stable_plotdevelop_plot
  • Docker镜像包含一个文件/freqtrade/freqtrade_commit,其中包含此镜像所基于的提交。
  • 完整的Docker镜像重建是通过时间表每周运行一次。
  • 部署在Ubuntu上运行。
  • ta-lib二进制文件包含在build_helpers目录中,以避免与外部不可用性相关的失败。
  • 所有测试必须通过才能将PR合并到stabledevelop上。

创建一个发布版本

此部分文档面向维护者,展示了如何创建一个发布版本。### 创建发布分支

!!!注意 确保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实例。