跳转至内容

Hummingbot 架构 - 第一部分

引言

Hummingbot 是一个用于构建高可靠性和高性能交易机器人的模块化框架。虽然官方的 Hummingbot 包已经允许您在多个加密货币交易所上运行高频交易策略,但底层框架可以自由扩展,以构建自定义策略、自定义市场连接器等。

在这篇博客文章中,我们将讨论 Hummingbot 中的一些关键架构特性,以及其设计背后的基本原理。

历史和动机

在 Hummingbot 成为开源项目之前,它是一个专有的量化交易机器人,在 2017 年和 2018 年左右用于交易加密货币,名为 Falcon。当时,Falcon 是使用现成的开源组件构建的。然而,这种做法很快就出现了一些问题:

  1. 可靠性

    加密货币交易所 API 通常不可靠,经常超时或返回各种错误。除此之外,交易所 API 的动态行为也可能带来问题。例如,在通过 REST API 调用取消订单后 - 如果您立即用另一个 REST API 调用来查询该订单,交易所 API 仍可能返回该订单为存活、已取消或不存在。

    交易机器人初学者通常认为交易机器人只是交易所 API 包装器和一些策略逻辑的简单组合。实际上,在投入实际资金之前,您需要处理许多错误情况和边缘情况。

    交易机器人初学者遇到的最常见问题是,他们拥有一个看似运行良好的简单交易机器人 - 但一旦遇到第一次 API 错误或交易所第一次意外的动态行为,该交易机器人要么崩溃要么继续错误地交易。在这两种情况下,如果用户没有时刻监控机器人,他都可能损失大量资金 - 这使使用交易机器人的初衷变得毫无意义。

    生产质量的机器人应该能够在互联网或交易所中断的情况下继续运行,并且一旦网络和交易所功能恢复,应该能够自动恢复。

  2. 状态跟踪

    实际的量化交易远不止于读取信号和下单。在做出明智决策之前,您还需要对当前账户和活跃订单进行大量跟踪。

    例如,根据信号显示,比特币的价格在某个时刻可能非常有吸引力 - 但如果我的当前头寸已经是做多比特币,那么我的交易机器人就不需要做任何事情。盲目发出更多买单可能会因为没有足够的余额来购买更多比特币而简单地导致 API 错误,或者可能会因为交易者并不打算拥有 100% 比特币组合而导致比特币的意外风险敞口。

    另一个例子是,如果我在市场上有活跃订单,而市场条件突然变得对这些订单不利会发生什么。假设交易机器人发出了取消这些订单的请求。但也许交易所太忙,这些请求返回了错误。一个简单的交易机器人只是发出取消 API 请求并忘记用户的实际市场头寸,可能会给用户造成巨大的资本损失。

    生产质量的交易机器人必须跟踪用户的整体市场头寸发生了什么,并应该能够决定接下来对这些订单做什么(例如,稍后重试撤销)。

  3. 延迟

    量化交易风格有很多 - 有些是高频交易,有些则不是。然而在加密货币市场中 - 经常出现显著的价格波动(5% 或更多),但仅持续几十秒或更短时间。因此,即使对于通常不被视为"高频"的策略 - 能够抓住快速价格变动并在机会消失前加以利用,通常仍然具有很大优势。

    一个简单制作的交易机器人通常依赖轮询交易所 API 来获取价格、订单簿和余额更新。这些操作可能需要多秒,或者甚至超过一分钟,具体取决于 API 速率限制或 REST API 响应的大小等因素。

    生产级的交易机器人应该能够利用流式 API(如 WebSocket)(如果可用),但当流式 API 变得不可靠或不可用时,仍保留使用 REST API 作为备用的能力。

  4. 回测性能

    最后,对于专业交易员和对冲基金而言,能够使用高分辨率数据回测策略通常是一个关键问题。

    许多现有的开源回测框架要么仅针对低分辨率的日 K 线设计;要么使用无法很好地与现代数据科学工具集集成的语言编写。

    Hummingbot 从头开始设计,能够以高性能处理和模拟高分辨率订单簿数据;并使用 Python 和 Cython 编写,以便访问 Python 丰富的数据科学和机器学习工具生态系统。

时钟

来自 hummingbot.core.clock 模块的 Clock 类,是驱动所有其他主要 Hummingbot 组件活动和操作的中心组件 - 如市场连接器和策略。

所有主要的 Hummingbot 组件,包括市场连接器和策略类,都派生自 hummingbot.core.time_iterator 模块的 TimeIterator

在每次时钟滴答(默认每秒发生一次)时,Clock 会通过调用它们的 c_tick() 方法来通知其每个子 TimeIterator 对象。

每次时钟滴答的通知顺序与通过 Clock.add_iterator()TimeIterator 对象添加到 Clock 的顺序相同。这使得 TimeIterator 对象之间的数据依赖关系得以实现。例如,如果策略对象依赖于来自市场连接器的最新市场信息,则先用市场连接器调用 Clock.add_iterator() 再用策略对象,将保证市场连接器始终在策略对象之前更新。

参考:

https://github.com/hummingbot/hummingbot/blob/master/hummingbot/core/clock.pyx

https://github.com/hummingbot/hummingbot/blob/master/hummingbot/core/time_iterator.pyx

市场连接器

市场连接器处理加密货币交易所与 Hummingbot 端进行交易决策的策略对象之间的所有网络操作。

你可以将市场连接器视为在 Hummingbot 内部运行的自动化股票经纪人。当策略对象想要获取特定订单规模的最新报价时,它询问市场连接器;订单簿有多厚?询问市场连接器;我在交易所的当前资产余额是多少?询问市场连接器;想要下限价买单?询问市场连接器。

在撰写本文时,Hummingbot 中内置了总共 23 个市场连接器。你可以在 hummingbot.connector 模块下找到它们。例如:

  • hummingbot.connector.exchange.binance.binance_exchange 是 Binance 的市场连接器模块;

  • hummingbot.connector.connector.uniswap.uniswap_connector 是 Uniswap 的市场连接器模块。

所有市场连接器的基接口可以在模块 hummingbot.connector.connector_base 中找到,类名为 ConnectorBase。该基接口类包含了所有市场连接器必须实现的方法和属性列表,以便在 Hummingbot 中对策略对象可用。

订单跟踪

与类似开源交易库的市场接口不同,Hummingbot 市场连接器被设计为跟踪策略对象创建的订单状态,以便为策略对象提供所有交易行为和更新的连贯画面。

您可以将此与不提供订单跟踪的交易库进行比较 - 交易机器人编写者要么必须自己想出跟踪逻辑;要么在交易所 API 调用延迟或在交易所繁忙交易期间完全失败时,面临机器人下更多订单或取消比需要更多订单的风险。

例如在 Binance 交易所连接器中,您可以在 BinanceExchange.c_start_tracking_order()BinanceExchange.c_stop_tracking_order() 中找到订单跟踪逻辑。这些函数通常在订单被创建、取消以及订单状态更新从交易所 API 到达时被调用。

优雅降级和可靠性

在前一节中我们讨论了当 API 调用延迟或失败时,设计不佳的交易库可能会出错的情况。Hummingbot 市场连接器被设计为在不利的市场或网络条件下仍能可靠工作并优雅地降级功能。

假设我们正处于 Binance 非常繁忙的交易期间,Binance API 服务器遇到严重的延迟,偶尔完全不响应新的 API 请求。假设一个 Hummingbot 策略对象想要在这种情况下创建一个限价买入订单,市场连接器应该做什么?

由于无法保证交易所 API 会在当时给我们一个订单创建 API 调用的响应 - 我们无法可靠地知道订单是否已在市场中下单。如果您查看 BinanceExchange.create_order() 函数,您会发现交易所连接器在提交到交易所 API 之前就开始跟踪订单。

这是为了确保在 self.query_api() 调用(紧接着执行)超时或失败但订单实际上已下单到交易所的情况下,Hummingbot 不会忘记该订单。

当 Binance 市场连接器处理订单取消和订单状态更新时,还采取了其他类似的预防措施,以确保 Binance 市场连接器不会错过上游策略对象下达的订单的重要信息。这使策略对象能够专注于交易决策,而市场连接器处理市场上下单和跟踪未平订单的所有操作细节。

低延迟

除了交易所不稳定和需要优雅处理的需要外,加密货币交易的另一个方面是速度。

Hummingbot 市场连接器被设计为使用最低延迟的数据源,这在大多数中心化交易所上是 web socket,是可用的。让我们再次以 Binance 市场连接器为例。具体来说,让我们看看 hummingbot.connector.exchange.binance.binance_api_order_book_data_source 中的 BinanceAPIOrderBookDataSource

上述代码展示了 Binance 市场连接器如何通过 websocket 从 Binance 接收订单簿变更消息,以计算订单簿深度和价格。这意味着依赖 Binance 市场连接器的策略对象能够看到实时价格和订单簿深度,而不是延迟的市场快照。

Gateway API

Hummingbot 基于 Python 和 Cython,这通常足以与基于 web 的 API 的中心化交易所进行交互。然而,去中心化交易所通常需要(或更好支持)使用第三方库来与交易所协议交互。这些库并不总是以 Python 形式提供。

Hummingbot 通过 Gateway API 架构解决这个问题。Gateway API 通常是一个 Docker 容器,与 Hummingbot 运行在同一台计算机上,它托管了与去中心化交易所接口所需的外部库和/或网络节点。然后它暴露一个加密的、经过身份验证的 HTTPS API 端点,以允许相应的 Hummingbot 市场连接器与去中心化交易所协议接口。

以 Balancer DEX 连接器为例。连接器中的几乎所有操作——无论是获取订单价格等市场数据、获取钱包余额还是下单——都通过BalancerConnector._api_request()方法进行。当你查看该方法时,会发现它实际上只是将所有工作委托给 Gateway API 端点。

Gateway API 源代码可以在我们的 gateway-api 仓库中找到(https://github.com/hummingbot/gateway)。例如,这是 balancer/sell API 端点在 Gateway API 端的实现方式。

参考:

https://github.com/hummingbot/hummingbot/blob/master/hummingbot/connector/connector_base.pyx

https://github.com/hummingbot/hummingbot/blob/master/hummingbot/connector/exchange/binance/binance_exchange.py

https://github.com/hummingbot/hummingbot/blob/master/hummingbot/connector/exchange/binance/binance_api_order_book_data_source.py

https://github.com/hummingbot/hummingbot/blob/master/hummingbot/connector/connector/balancer/balancer_connector.py

https://github.com/hummingbot/gateway-api/blob/master/src/routes/balancer.route.js

结论

这标志着我们 Hummingbot 架构系列第一部分的结束。到目前为止,我们已经讨论了:

  • 为什么状态检查、可靠性和低延迟对自动化交易机器人很重要。
  • Hummingbot 中的主要组件如何通过Clock同步协作。
  • 订单状态跟踪、可靠性和低延迟特性如何在 Hummingbot 市场连接器中实现。
  • 我们如何通过 Gateway API 架构支持 DEX 协议。

在我们的 Hummingbot 架构系列的下一部分中,我们将深入探讨 Hummingbot 策略类——自动化交易机器人的大脑。我们还将讨论 Hummingbot 中一些对开发人员用于调试和检查内部机器人状态有用的特性,以及 Hummingbot 社区如何为项目做出贡献。

请继续关注。