Hummingbot 架构 - 第一部分¶

引言¶
Hummingbot 是一个模块化框架,用于构建高度可靠且高性能的交易机器人。尽管官方 Hummingbot 软件包已经允许你在多个加密货币交易所上运行高频交易策略,但其底层框架可以自由扩展,以构建自定义策略、自定义市场连接器等。
在本篇博客中,我们将讨论 Hummingbot 中一些关键的架构特性及其设计背后的原理。
历史与动机¶

在 Hummingbot 成为开源项目之前,它曾是一个专有的量化交易机器人,在 2017 年至 2018 年左右用于交易加密货币,名为 Falcon。当时,Falcon 是基于现成的开源组件构建的。然而,这种方法很快暴露出了一些问题:
- 
可靠性 加密货币交易所的 API 通常不可靠,经常超时或返回各种错误。除此之外,API 的动态行为也可能带来问题。例如,通过 REST API 调用取消订单后,如果你立即使用另一个 REST API 查询该订单,交易所 API 可能仍会将其返回为“活跃”、“已取消”或“不存在”。 许多初学者认为交易机器人不过是交易所 API 封装加上一些策略逻辑的组合。但实际上,在真正投入资金之前,你需要处理大量异常情况和边界情况。 新手开发者最常见的问题是:他们构建了一个简单的交易机器人,看起来几个小时内运行正常——但一旦遇到第一个 API 错误,或交易所出现首次意外的动态行为,这个机器人就会崩溃,或者继续错误地交易。无论哪种情况,如果用户没有时刻监控机器人,都可能造成重大经济损失——而这恰恰违背了使用交易机器人的初衷。 一个具备生产质量的系统应当能够在互联网中断或交易所服务异常的情况下持续运行,并在网络和交易所功能恢复后自动修复自身状态。 
- 
状态跟踪 实际的量化交易远不止读取信号并下单那么简单。在做出合理决策之前,你还必须对自己的账户状态和当前挂单进行大量跟踪。 例如,根据信号判断某一时刻比特币价格极具吸引力——但如果我当前已经持有多头仓位,那么我的交易机器人就不应再采取任何行动。盲目发出更多买入订单,可能会因余额不足而导致 API 错误,或者导致对比特币的非预期敞口增加,比如使投资者无意中持有 100% 比特币的投资组合。 另一个例子是:当我在市场上有未成交的挂单时,市场条件突然变得对这些订单不利。假设交易机器人已发送请求取消这些订单,但交易所过于繁忙导致请求失败。一个仅仅发出取消 API 请求后就不再关注用户实际市场持仓的简单机器人,可能会给用户造成巨大的资本损失。 一个具备生产质量的交易机器人必须能够追踪用户在市场中的整体持仓变化,并能决定接下来如何处理这些订单(例如稍后重试取消)。 
- 
延迟 量化交易的风格多种多样——有些是高频交易,有些则不是。然而在加密货币市场中,经常会出现显著的价格波动(5% 或更高),但这些波动可能仅持续几十秒甚至更短时间。因此,即使对于通常不被视为“高频”的策略而言,能够捕捉到快速的价格变动,并在机会消失前加以利用,往往也具有极大的优势。 一个简单的交易机器人通常依赖轮询交易所的 API 来获取价格、订单簿和余额更新。这可能需要数秒,甚至超过一分钟,具体取决于 API 限速或 REST API 响应数据大小等因素。 一个具备生产级质量的交易机器人应尽可能使用流式 API(例如 WebSocket),并在流式 API 不稳定或不可用时,仍能退回到使用 REST API。 
- 
回测性能 最后,对于专业交易员和对冲基金来说,能够使用高分辨率数据进行策略回测通常是一个关键需求。 许多现有的开源回测框架要么仅针对低分辨率的每日 K 线设计,要么使用与现代数据科学工具集集成不佳的语言编写。 Hummingbot 从底层设计上就支持高性能地处理和模拟高分辨率的订单簿数据,并且使用 Python 和 Cython 编写,便于接入 Python 丰富的数据科学和机器学习工具生态系统。 
时钟¶

Clock 类位于 hummingbot.core.clock 模块中,是驱动 Hummingbot 所有其他主要组件(如市场连接器和策略)运行的核心组件。
所有主要的 Hummingbot 组件,包括市场连接器和策略类,都继承自 hummingbot.core.time_iterator 模块中的 TimeIterator。
每个时钟滴答(默认每秒一次),Clock 都会通过调用其各个子对象 TimeIterator 的 c_tick() 方法来通知它们。
每次时钟滴答的通知顺序,与 TimeIterator 对象通过 Clock.add_iterator() 添加到 Clock 中的顺序一致。这使得 TimeIterator 对象之间的数据依赖关系得以实现。例如,如果一个策略对象依赖于市场连接器提供的最新市场信息,那么在添加时先将市场连接器加入 Clock,再添加策略对象,即可确保市场连接器总是在策略对象之前被更新。
参考:
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 正处于交易高峰期,其 API 服务器响应严重延迟,甚至偶尔完全无法响应新的 API 请求。此时,如果 Hummingbot 的某个策略对象希望在此情况下创建一个限价买入订单,市场连接器应当如何处理?
由于当时无法保证交易所 API 会对订单创建请求做出响应,因此我们无法确切知道订单是否已被成功提交到市场。如果你查看 BinanceExchange.create_order() 函数,会发现该交易所连接器会在向交易所 API 提交订单之前就开始跟踪该订单。

这样做的目的是确保即使后续立即执行的 self.query_api() 调用超时或失败,只要订单实际上已被交易所接受,Hummingbot 也不会丢失对该订单的追踪。
当 Binance 市场连接器处理订单取消和订单状态更新时,也采取了其他类似的预防措施,以确保不会遗漏上游策略对象所下订单的重要信息。这使得策略对象可以专注于交易决策,而市场连接器负责处理在市场上创建和跟踪未成交订单的所有操作细节。
低延迟¶
除了应对交易所不稳定并需优雅处理之外,加密货币交易的另一个关键因素是速度。
Hummingbot 的市场连接器被设计为优先使用可用的最低延迟数据源,对于大多数中心化交易所而言,这一数据源就是 WebSocket。让我们再次以 Binance 市场连接器为例,具体来看位于 hummingbot.connector.exchange.binance.binance_api_order_book_data_source 模块中的 BinanceAPIOrderBookDataSource。

上述代码展示了币安市场连接器如何通过 WebSocket 接收来自币安的订单簿变更消息,以计算订单簿深度和价格。这意味着依赖于币安市场连接器的策略对象能够看到实时的价格和订单簿深度,而不是延迟的市场快照。
网关 API¶
Hummingbot 基于 Python 和 Cython 构建,这通常足以与基于 Web API 的中心化交易所进行交互。然而,去中心化交易所(DEX)通常需要(或更佳支持)使用第三方库来与交易所协议进行交互。而这些库并不总是有 Python 版本。
Hummingbot 通过网关 API 架构解决了这一问题。网关 API 通常是一个运行在与 Hummingbot 相同计算机上的 Docker 容器,它托管了与去中心化交易所接口所需的外部库和/或网络节点。然后,它暴露一个加密且经过身份验证的 HTTPS API 端点,使相应的 Hummingbot 市场连接器能够与去中心化交易所协议进行通信。
以 Balancer DEX 连接器为例。连接器中的几乎所有操作——无论是获取订单价格等市场数据、查询钱包余额还是下单——都会通过BalancerConnector._api_request()方法执行。当你查看该方法时,会发现它实际上只是将所有工作委托给一个网关 API 端点。

网关 API 的源代码可以在我们的 gateway-api 仓库中找到(https://github.com/hummingbot/gateway)。例如,以下是网关 API 侧如何实现 balancer/sell API 端点的示例。

参考:
https://github.com/hummingbot/hummingbot/blob/master/hummingbot/connector/connector_base.pyx
https://github.com/hummingbot/gateway-api/blob/master/src/routes/balancer.route.js
结论¶
至此,我们完成了 Hummingbot 架构系列的第一部分。到目前为止,我们讨论了:
- 为何状态检查、可靠性和低延迟对于自动化交易机器人至关重要。
- Hummingbot 中的主要组件如何通过Clock协同同步工作。
- Hummingbot 市场连接器中如何实现订单状态跟踪、可靠性和低延迟功能。
- 我们如何通过网关 API 架构支持 DEX 协议。
在接下来的 Hummingbot 架构系列文章中,我们将深入探讨 Hummingbot 的策略类——自动化交易机器人的“大脑”。我们还将讨论 Hummingbot 中一些对开发者调试和检查机器人内部状态非常有用的功能,以及 Hummingbot 社区如何为项目做出贡献。
敬请期待。
 
                