5. V2策略框架核心原理

5.V2策略框架核心原理

Hummingbot 的 V2 策略框架是项目在 2023 年后推出的重大架构升级,它彻底改变了策略的开发、执行和管理方式。如果说 V1 框架是"模板化"的思维,那么 V2 就是"组件化"的思维——把策略拆解成可复用、可组合的乐高积木。这种设计让策略开发从"写死逻辑"变成了"搭积木",极大地提升了灵活性和可维护性。

V2 策略组件架构解析

V2 框架的核心思想是分层解耦。整个架构可以看作一个精密的流水线,数据从交易所流入,经过市场数据层、决策层、执行层,最终变成订单回到市场。每个环节都有明确的职责和接口。

四大核心组件

V2 框架由四个关键组件构成,它们通过定义良好的接口协同工作:

Script(脚本层) 这是策略的入口点和总指挥。一个 V2 Script 通常继承自 StrategyV2Base 类,负责初始化市场连接、加载配置、创建 Controller 和 Executor,并在每个 tick 驱动整个策略运转。最简单的 V2 Script 可能只有几十行代码,复杂的可以管理多个 Controller 实例。

Controller(控制器层) Controller 是策略逻辑的"大脑"。它接收市场数据,做出交易决策,生成 ExecutorAction 指令。V2 提供了多种 Controller 基类:DirectionalTradingControllerBase 用于方向性交易,MarketMakingControllerBase 用于做市策略。开发者可以继承这些基类,重写信号生成方法来实现自定义逻辑。

Executor(执行器层) Executor 是策略的"手脚",负责具体的订单执行和仓位管理。每个 Executor 都是一个独立的状态机,管理从下单到平仓的完整生命周期。V2 提供了 PositionExecutorDCAExecutorGridExecutor 等多种执行器,分别处理不同类型的交易场景。

Market Data Provider(市场数据层) 这是策略的"感官系统",统一封装了 K 线、订单簿、成交数据等市场信息。通过 Candles 组件可以方便地获取历史数据并计算技术指标,而 OrderBookTrades 则提供实时市场快照。

组件交互流程

让我们看看一个典型的 V2 策略是如何运转的:

graph TB
    A[Script.on_tick] --> B[Controller.get_signal]
    B --> C[生成 ExecutorAction]
    C --> D[ExecutorOrchestrator.execute_actions]
    D --> E[创建/更新 Executor]
    E --> F[Executor 管理订单]
    F --> G[Connector 与交易所交互]
    G --> H[Market Data Provider 更新数据]
    H --> B

这个闭环每秒钟可能循环多次,形成一个完整的策略执行回路。关键在于,每个组件只关心自己的职责:Controller 不做订单管理,Executor 不做决策,Script 不做具体交易。这种分离让代码更容易测试、调试和复用。

策略继承关系与执行流程

理解 V2 框架的继承体系,就像理解生物分类学——从门纲目到种,每一层都增加了特定的能力。

继承层次结构

V2 策略的继承链从底层到顶层依次是:

StrategyBase (Cython 基类)
    ↓
StrategyPyBase (Python 策略基类)
    ↓
ScriptStrategyBase (脚本策略基类)
    ↓
StrategyV2Base (V2 策略基类)
    ↓
具体 V2 Script

StrategyBase 是 Hummingbot 最底层的策略类,用 Cython 编写,提供了与交易所连接器交互的核心能力。StrategyPyBase 在此基础上封装了 Python 友好的接口。ScriptStrategyBase 进一步简化了策略开发,引入了 on_tick 方法和事件处理机制。

StrategyV2Base 是 V2 框架的关键创新点。它在 ScriptStrategyBase 之上增加了 Executor 支持、动态配置更新、Controller 集成等现代化特性。所有 V2 Script 都应该继承自这个类。

执行流程详解

一个 V2 策略从启动到运行的完整流程如下:

初始化阶段 当执行 start --script xxx.py --conf xxx.yml 命令时,Hummingbot 首先加载配置文件,解析其中的参数。然后实例化 Script 类,调用其 __init__ 方法。在这个阶段,Script 会初始化 Market Data Provider,创建所需的 Controller 实例,并设置 ExecutorOrchestrator。

市场准备阶段 on_tick 方法开始被调用,但最初几次调用主要用于市场数据预热。ready_to_trade 属性会保持为 False,直到收集到足够的历史数据(比如 K 线数据达到指定长度)。这个阶段通常需要几秒到几分钟,取决于配置。

策略运行阶段 一旦准备就绪,ready_to_trade 变为 True,策略进入正常运行状态。每个 tick(默认 1 秒)执行一次完整的决策循环:

  1. Script 调用所有 Controller 的 get_executor_actions 方法
  2. Controller 分析最新市场数据,生成买入、卖出或平仓信号
  3. Controller 返回一个或多个 ExecutorAction 对象
  4. Script 通过 ExecutorOrchestrator 执行这些动作
  5. ExecutorOrchestrator 创建新的 Executor 或更新现有 Executor
  6. 每个 Executor 独立管理自己的订单和仓位
  7. Executor 将状态变化报告给 Script,用于状态展示

配置热更新 V2 框架支持动态配置更新。Script 会定期检查配置文件的最后修改时间,如果发现变更,就重新加载配置并更新 Controller 和 Executor 的参数。这意味着可以在不重启策略的情况下调整交易参数,比如修改价差、止损位等。

Controller 与 Script 协同机制

Controller 和 Script 的关系就像"参谋"与"司令"。Controller 负责分析战场形势并提出建议,Script 负责最终决策和资源调度。

职责分离设计

Controller 的职责

  • 接收市场数据,进行技术分析
  • 生成交易信号(买入、卖出、持有)
  • 计算订单价格、数量等参数
  • 管理策略级别的状态(如当前仓位方向)

Script 的职责

  • 管理交易所连接和认证
  • 协调多个 Controller(如果存在)
  • 处理 Executor 的生命周期
  • 提供统一的状态展示接口
  • 管理配置和热更新

这种分离让策略开发更加模块化。一个团队可以专注于开发高性能的 Controller 算法,另一个团队可以专注于 Script 的运维和集成。

协同工作流程

让我们通过一个具体的例子来看两者如何配合。假设我们正在运行一个基于 RSI 的方向性策略:

# 在 Script 的 on_tick 方法中
def on_tick(self):
    # 1. 确保市场数据已准备就绪
    if not self.ready_to_trade:
        return
        
    # 2. 调用 Controller 获取交易动作
    actions = self.controller.get_executor_actions()
    
    # 3. 通过编排器执行动作
    for action in actions:
        self.executor_orchestrator.execute_action(action)
# 在 Controller 的 get_executor_actions 方法中
def get_executor_actions(self) -> List[ExecutorAction]:
    actions = []
    
    # 1. 获取最新数据
    candles_df = self.market_data_provider.get_candles_df(
        self.config.candles_config[0]
    )
    
    # 2. 计算 RSI 指标
    rsi = self.calculate_rsi(candles_df)
    
    # 3. 生成交易信号
    if rsi < self.config.rsi_low_threshold:
        # 超卖,生成买入动作
        action = CreateExecutorAction(
            controller_id=self.config.id,
            executor_config=self.get_long_executor_config()
        )
        actions.append(action)
    elif rsi > self.config.rsi_high_threshold:
        # 超买,生成卖出动作
        action = CreateExecutorAction(
            controller_id=self.config.id,
            executor_config=self.get_short_executor_config()
        )
        actions.append(action)
    
    return actions

这个流程中,Controller 完全不知道 Executor 如何执行订单,也不知道有其他 Controller 存在。它只专注于信号生成。Script 则负责把各个 Controller 的指令汇总,统一调度执行。

多 Controller 管理

V2 框架的一大优势是支持单个 Script 管理多个 Controller。这在多交易对、多策略场景中非常有用。例如,可以同时运行一个 BTC-USDT 的做市策略和一个 ETH-USDT 的趋势跟踪策略:

# Script 配置中可以指定多个 Controller
controllers_config:
  - conf_market_making.pmm_simple_1.yml
  - conf_directional.bollinger_v1_1.yml

在运行时,Script 会为每个 Controller 创建独立的实例,分别管理各自的交易对和策略逻辑。但所有 Controller 共享同一个账户余额,因此需要做好资金分配和风险控制。

Executor 编排器核心功能

ExecutorOrchestrator 是 V2 框架的"执行导演",它管理着舞台上所有 Executor 演员的上场、表演和退场。

Executor 类型体系

V2 框架提供了多种专用 Executor,每种都针对特定的交易场景优化:

PositionExecutor(仓位执行器) 这是功能最全面的 Executor,用于管理方向性交易的完整生命周期。它实现了"三重屏障"风险管理模型:止损、止盈和时间限制。可以配置追踪止损、部分平仓等高级功能。

DCAExecutor(定投执行器) 实现定投策略,在指定价格区间分批建仓或平仓。支持固定价格网格和动态价格调整两种模式。适合大资金进出,减少市场冲击。

GridExecutor(网格执行器) 在价格上下方挂出一系列买卖订单,形成交易网格。当价格震荡时,不断低买高卖赚取网格利润。支持动态网格调整,可以根据波动率自动缩放网格间距。

TWAPExecutor(时间加权执行器) 将大单拆分成多个小单,在指定时间内均匀执行,实现时间加权平均价格。常用于算法交易中的大单执行,隐藏交易意图。

ArbitrageExecutor(套利执行器) 专门处理跨交易所或跨市场的套利交易。监控两个市场的价格差,当价差超过阈值时,同时在两边市场下单,锁定套利利润。

编排器的工作机制

ExecutorOrchestrator 维护一个 Executor 字典,键是 Controller ID,值是 Executor 实例列表。每个 tick,它会执行以下操作:

  1. 处理待执行动作:从 Controller 接收的 ExecutorAction 会先进入队列,编排器按顺序处理。CreateAction 会实例化新的 Executor,StopAction 会终止指定 Executor。

  2. 更新 Executor 状态:调用每个活跃 Executor 的 execute 方法,让它们根据最新市场数据管理订单。Executor 内部会处理订单创建、取消、修改等细节。

  3. 清理已完成 Executor:当 Executor 达到止损、止盈或时间限制时,会自动进入关闭状态。编排器会将其从活跃列表移除,并保存性能数据。

  4. 生成性能报告:汇总所有 Executor 的盈亏、成交量、胜率等指标,供 Script 展示。

Executor 状态机

每个 Executor 都是一个状态机,典型状态转换如下:

NOT_STARTED → RUNNING → COMPLETED
                ↓
              STOPPED
  • NOT_STARTED:Executor 已创建但尚未开始执行
  • RUNNING:正在积极管理订单,可能持有仓位
  • COMPLETED:已完成使命,正常退出
  • STOPPED:被强制终止,可能因配置变更或外部指令

状态转换由 Executor 内部逻辑驱动。例如,PositionExecutor 在仓位达到止盈价时会自动从 RUNNING 转为 COMPLETED。

V2 与 V1 框架全面对比

从 V1 到 V2 的演进,不是简单的功能升级,而是一次架构范式的转变。理解两者的差异,有助于在合适的场景选择合适的框架。

架构设计对比

V1 框架:单体策略 V1 策略继承自 StrategyBaseStrategyPyBase,所有逻辑都集中在一个类中。一个典型的 V1 策略需要处理:

  • 市场数据获取和解析
  • 信号生成和决策
  • 订单创建和管理
  • 仓位监控和风控
  • 状态展示和日志

这种设计简单直接,适合逻辑清晰的经典策略,比如纯做市、跨交易所做市。但当策略复杂度增加时,代码会变得臃肿,难以维护。

V2 框架:组件化策略 V2 将策略拆分成多个独立组件,每个组件有明确职责。这种设计带来了几个关键优势:

  • 模块化:可以独立开发和测试 Controller 和 Executor
  • 可复用:一个 Controller 可以用于多个交易对,一个 Executor 可以被多个 Controller 调用
  • 可组合:可以在一个 Script 中组合多个 Controller 实现复杂策略
  • 可回测:Controller 的逻辑与执行分离,便于历史数据回测

开发体验对比

配置方式 V1 策略使用 config_map 定义参数,通过 create 命令交互式配置。参数类型和验证逻辑分散在代码各处。

V2 策略使用 Pydantic 模型定义配置,类型安全且自文档化。配置变更可以热加载,无需重启策略。例如:

# V2 配置定义示例
class MyControllerConfig(BaseClientModel):
    exchange: str = Field(
        default="binance_perpetual",
        client_data=ClientFieldData(
            prompt_on_new=True,
            prompt="选择交易所:"
        )
    )
    order_amount: Decimal = Field(
        default=Decimal("100"),
        gt=0,
        client_data=ClientFieldData(
            prompt_on_new=True,
            prompt="订单金额:"
        )
    )

订单管理 V1 策略直接调用 buy()sell() 方法下单,需要手动管理订单 ID、状态跟踪、取消逻辑。代码中充斥着大量的订单管理细节。

V2 策略通过 Executor 间接下单,开发者只需配置 Executor 参数,订单管理的复杂性被封装起来。例如,使用 PositionExecutor 时,只需指定止损止盈,Executor 会自动处理订单创建、调整和平仓。

状态展示 V1 策略需要重写 format_status 方法,手动拼接字符串展示状态。当策略复杂时,状态信息难以组织。

V2 策略的 format_status 可以调用 Executor 的 to_format_status 方法,自动获取标准化的性能指标。多个 Controller 的状态可以自然拼接,形成层次化的展示。

性能与资源对比

执行效率 V1 策略的所有逻辑都在主循环中执行,如果某个操作耗时较长(比如复杂指标计算),会阻塞整个策略。

V2 框架的 Market Data Provider 支持异步数据获取,指标计算可以在后台线程进行。Executor 的更新也是批量处理,减少了与交易所的交互次数。

内存占用 V1 策略通常只运行一个交易对,如果需要多交易对,必须启动多个 Hummingbot 实例,每个实例都有独立的内存开销。

V2 策略可以在一个 Script 中管理多个 Controller,共享同一个交易所连接和市场数据缓存。部署 10 个交易对的策略,V2 的内存占用可能只有 V1 的 1/3。

适用场景建议

选择 V1 框架的场景

  • 维护 legacy 策略,代码已经稳定运行
  • 策略逻辑非常简单,比如固定价差的做市
  • 对执行延迟极度敏感,需要绕过 Executor 的抽象层
  • 资源极度受限的环境,无法承受 V2 框架的额外开销

选择 V2 框架的场景

  • 开发新策略,尤其是需要回测的策略
  • 多交易对、多策略组合管理
  • 需要动态调整参数,不能频繁重启
  • 策略逻辑复杂,需要模块化设计
  • 计划使用 Dashboard 进行部署和监控

迁移路径

对于已经在使用 V1 策略的用户,迁移到 V2 不是必须的。V1 框架会继续维护,确保现有策略稳定运行。但如果希望利用 V2 的新特性,可以逐步迁移:

  1. 从简单策略开始,比如将纯做市策略迁移到 PMM Simple Controller
  2. 利用 Dashboard 的回测功能验证 V2 策略的表现
  3. 在 V2 中实现新交易对,V1 保留老交易对
  4. 逐步淘汰 V1 实例,集中管理 V2 策略

V2 框架的设计目标不是取代 V1,而是为复杂场景提供更强大的工具。就像 Python 中既有简单的函数式编程,也有复杂的面向对象编程,两者各有适用场景。

总结

V2 策略框架通过组件化设计,将策略开发从"写代码"变成了"搭积木"。Script、Controller、Executor、Market Data Provider 四大组件各司其职,通过清晰的接口协同工作。这种架构不仅提高了代码的可维护性,也为多策略管理、动态配置、历史回测等高级功能奠定了基础。

与 V1 相比,V2 的学习曲线稍陡,需要理解组件间的交互机制。但一旦掌握,就能以更高的效率开发更复杂的策略。对于新用户,建议直接从 V2 入手;对于老用户,可以根据实际需求选择迁移策略。

在下一章,我们将深入 V2 脚本开发的具体实践,从创建第一个 V2 Script 开始,逐步掌握配置定义、on_tick 实现、状态展示等核心技能。通过动手实践,你会更深刻地理解 V2 框架的设计哲学和强大能力。