7.V2控制器设计与实现
在 Hummingbot 的 V2 策略框架中,控制器扮演着策略大脑的角色。如果说执行器是负责具体下单、管理仓位的双手,那么控制器就是决定何时出手、如何出手的决策中枢。它接收市场数据,运行交易逻辑,最终输出一系列动作指令,让执行器去完成实际的交易操作。这种设计把策略逻辑从执行层面彻底解耦,使得同一个控制器可以驱动不同的执行器,而不同的控制器也可以共享同一套执行器组件。
控制器基类与接口规范
控制器的整个体系建立在几个精心设计的基类之上。理解这些基类的继承关系和接口规范,是掌握 V2 控制器设计的第一步。
基类继承体系
控制器的继承链条从 ControllerBase 开始,这个基类定义了所有控制器必须实现的核心接口。在此基础上,框架提供了两个主要的抽象子类:DirectionalTradingControllerBase 和 MarketMakingControllerBase。前者专门处理方向性交易策略,后者则服务于做市策略。这种分层设计让不同类型的策略可以在统一的框架下发展,同时又保留了各自的特性。
# 控制器基类定义了核心接口
class ControllerBase:
def get_actions(self) -> List[ExecutorAction]:
"""生成执行器动作列表"""
pass
def update_processed_data(self) -> Dict:
"""更新并返回处理后的市场数据"""
pass
DirectionalTradingControllerBase 在基类之上增加了信号生成的概念。它要求子类实现 get_signal 方法,这个方法返回一个整数值:1 表示做多信号,-1 表示做空信号,0 表示观望。这种设计把方向性策略的本质——判断市场方向——抽象成了一个简单的接口。
class DirectionalTradingControllerBase(ControllerBase):
def get_signal(self) -> int:
"""生成交易信号:1=做多, -1=做空, 0=观望"""
pass
MarketMakingControllerBase 则走了一条不同的路。它不关注市场方向,而是专注于订单层级的管理。它的核心方法是 get_levels_to_execute,返回一个订单层级的列表,每个层级包含价格、数量、方向等信息。做市策略的本质就是在不同价格水平上提供流动性,这个接口正好抓住了这一核心。
配置参数定义规范
控制器的灵活性很大程度上来自于其配置系统。每个控制器都通过一个配置类来定义自己的参数,这个配置类继承自 BaseClientModel,利用 Python 的类型提示和字段元数据来实现参数验证和用户提示。
class BollingerV1Config(BaseClientModel):
exchange: str = Field(
default="binance_perpetual",
client_data=ClientFieldData(
prompt_on_new=True,
prompt=lambda mi: "Enter the exchange name"
)
)
bb_length: int = Field(
default=100,
client_data=ClientFieldData(
prompt_on_new=True,
prompt=lambda mi: "Enter Bollinger Bands length"
)
)
这种设计有几个好处。首先,参数类型在代码层面就得到了保证,减少了运行时错误。其次,client_data 中的 prompt_on_new 标记决定了哪些参数会在创建配置文件时提示用户,哪些参数是高级选项可以后续手动修改。最后,默认值的存在让新手可以快速上手,而不会被一堆参数吓退。
定向交易控制器类型
定向交易控制器的家族成员都遵循同一个模式:通过技术指标判断市场方向,在合适的时机建立头寸,然后让执行器去管理这个头寸。它们之间的区别主要在于使用什么样的指标,以及如何组合这些指标。
Bollinger V1:基于波动率的突破策略
Bollinger V1 控制器使用布林带百分比指标来生成信号。布林带由三条线组成:中轨是移动平均线,上下轨是中轨加减标准差。布林带百分比计算当前价格在布林带中的相对位置,数值接近 0 表示价格触及下轨,接近 1 表示价格触及上轨。
# 布林带百分比计算逻辑
bbp = (current_price - lower_band) / (upper_band - lower_band)
当 BBP 低于设定的多头阈值时,控制器生成做多信号;当 BBP 高于空头阈值时,生成做空信号。这种策略的本质是均值回归思想:价格偏离均值太远时,有回归的倾向。控制器通过调整布林带的长度和标准差参数,可以控制策略对波动率的敏感度。
MACD-BB V1:趋势与波动率的结合
MACD-BB 控制器把 MACD 指标和布林带结合起来,试图同时捕捉趋势和波动率信息。MACD 用于判断趋势方向和强度,布林带用于确定具体的入场点位。这种组合策略的逻辑是:只在趋势方向明确时入场,在波动率提供的相对高低点执行。
# MACD 信号生成
macd_line = ema_fast - ema_slow
signal_line = ema(macd_line, signal_period)
histogram = macd_line - signal_line
控制器会先检查 MACD 柱状图的符号来确定趋势方向,然后在布林带百分比达到极端值时生成具体信号。比如,当 MACD 显示上升趋势且价格触及布林带下轨时,生成做多信号。这种双重过滤机制减少了假信号,但也可能错过一些机会。
Trend Follower V1:简单的趋势跟踪
Trend Follower 控制器采用了更直接的方法:使用两条简单移动平均线判断趋势,再结合布林带确定入场时机。快速均线上穿慢速均线时认为趋势向上,反之则趋势向下。这种策略不预测市场,而是跟随市场,属于典型的动量策略。
# 趋势判断逻辑
if sma_fast > sma_slow:
trend_direction = 1 # 上升趋势
else:
trend_direction = -1 # 下降趋势
控制器的巧妙之处在于,它不会在趋势反转的瞬间立即行动,而是等待价格回调到布林带边界时才入场。这样既保证了顺势交易,又避免了在价格极端位置追单。
DMan V3:动态调整的定向做市
DMan V3 控制器是一个混血儿,它既有定向交易的特点,又有做市的特征。它使用布林带指标不仅生成方向信号,还动态调整订单的间距和中点价格。当布林带开口扩大时,说明波动率上升,控制器会自动扩大订单间距;当价格持续偏离中轨时,控制器会整体移动订单网格的中心。
# 动态间距计算
spread = bollinger_band_width * spread_ratio
这种动态调整让策略能够适应不同的市场环境,在震荡市中缩小间距增加交易频率,在趋势市中扩大间距避免被扫单。
做市控制器类型
做市控制器的核心任务是在市场两侧同时挂单,从买卖价差中获利。它们不关心市场的长期方向,而是专注于订单的管理和优化。
PMM Simple:基础做市策略
PMM Simple 是最纯粹的做市控制器。它接收一组固定的买卖价差,在当前价格两侧对称地挂出买单和卖单。控制器每隔一段时间检查订单状态,如果订单被成交或过期,就重新挂出新的订单。
# 订单层级生成逻辑
buy_price = mid_price * (1 - buy_spread)
sell_price = mid_price * (1 + sell_spread)
这种简单策略的优劣都很明显。优点是逻辑清晰,参数少,容易理解和调试。缺点是对市场变化没有适应能力,在波动率突然增大时容易被单边扫单,造成库存风险。
PMM Dynamic:响应式做市
PMM Dynamic 控制器在简单版本的基础上增加了动态调整能力。它使用 NATR 指标来衡量市场波动率,根据波动率的大小自动调整订单间距。波动率高时扩大间距,波动率低时缩小间距。这种自适应机制让策略能够在不同市场环境下保持相对稳定的盈利能力。
# 动态价差计算
natr = normalized_average_true_range(high, low, close)
adjusted_spread = base_spread * natr
控制器还可以根据市场趋势调整中点价格。当检测到明显的趋势时,会整体偏移订单网格,让大部分订单落在趋势的反方向,这样可以增加成交概率,同时控制库存风险。
DMan Maker V2:高级做市策略
DMan Maker V2 控制器集成了多种技术指标和风险管理工具。它不仅使用 NATR 调整价差,还引入了 MACD 等指标来判断短期趋势,实现更智能的订单定位。控制器支持不同的订单数量配比,可以在不同价格水平上挂出不同大小的订单。
# 订单数量递增
order_amount_level_2 = order_amount_level_1 * amount_ratio_increase
这种分层设计让策略能够在更有利的价格上挂出更大的订单,提高资金利用效率。同时,控制器内置了止损和止盈机制,当持仓亏损或盈利达到阈值时会自动平仓,防止风险失控。
控制器配置文件创建
配置文件是控制器策略的载体,它把复杂的参数打包成一个 YAML 文件,方便管理、分享和版本控制。理解配置文件的结构和创建流程,是实际使用控制器的关键。
配置文件存储结构
Hummingbot 把配置文件统一放在 conf 目录下,其中控制器配置文件专门存放在 conf/controllers 子目录。这种分离让文件管理更加清晰,也便于备份和迁移。
conf/
├── controllers/ # V2控制器配置文件
├── scripts/ # 脚本配置文件
└── strategies/ # V1策略配置文件
每个配置文件都是一个独立的 YAML 文件,文件名通常包含控制器名称和版本信息,比如 conf_bollinger_v1_1.yml。这种命名方式让文件一目了然,在管理多个配置时不容易混淆。
创建流程详解
创建控制器配置文件使用 create 命令,加上 --controller-config 标志。系统会扫描 controllers 目录下的所有可用控制器,在自动补全列表中显示出来。
create --controller-config market_making.pmm_simple
选择控制器后,系统会根据配置类中定义的字段,逐个提示用户输入参数。每个提示都会显示默认值和帮助信息,让配置过程变得直观。比如设置交易对时,会提示输入格式;设置杠杆时,会说明杠杆的含义。
Enter the name of the exchange to trade on >> binance_perpetual
Enter the trading pair >> BTC-USDT
Enter the total amount in quote asset >> 1000
Enter buy spreads as comma-separated values >> 0.005, 0.01
Enter sell spreads as comma-separated values >> 0.005, 0.01
Enter executor refresh time in seconds >> 60
最后一步是为配置文件命名。系统会建议一个默认名称,但用户可以根据实际情况修改,比如加上日期或版本号,方便后续管理。
配置文件内容解析
生成的 YAML 文件包含了控制器的所有参数,结构清晰,易于手动修改。文件开头通常是元数据部分,包括控制器名称、类型和唯一标识符。
id: 8f3a2b1c-9d4e-5f6a-7b8c-9d0e1f2a3b4c
controller_name: pmm_simple
controller_type: market_making
connector_name: binance_perpetual
trading_pair: BTC-USDT
接下来的部分是策略参数,比如订单金额、价差、刷新时间等。这些参数的值都是用户在创建过程中输入的,可以直接在文件中修改,无需重新运行创建命令。
total_amount_quote: 1000.0
buy_spreads:
- 0.005
- 0.01
sell_spreads:
- 0.005
- 0.01
executor_refresh_time: 60
高级参数通常放在文件末尾,这些参数在创建时不会提示,需要用户手动添加或修改。比如止损止盈设置、尾随止损参数等,这些属于策略的精细调整,适合有经验的用户。
Generic Script集成方法
Generic Script 是 V2 框架的一大创新,它让同一个脚本可以加载不同的控制器配置,实现一脚本多策略的运行模式。这种设计极大地提高了资源利用率,降低了运维复杂度。
加载器脚本原理
v2_generic_with_controllers.py 是一个通用的控制器加载器。它本身不包含任何交易策略逻辑,而是读取配置文件,动态实例化对应的控制器,并把控制器交给执行器编排器管理。
# 加载器核心逻辑
for controller_config in self.controller_configs:
controller = controller_config.create_controller()
self.executor_orchestrator.add_controller(controller)
这种解耦让策略开发和使用分离。量化研究员可以专注于控制器的开发,而运维人员只需要管理配置文件,无需修改代码就能调整策略。
多控制器部署流程
部署多控制器策略需要两个步骤。首先创建各个控制器的配置文件,这些文件定义了不同交易对或不同策略的参数。然后创建一个 Generic Script 的配置文件,列出所有要加载的控制器。
# 创建两个不同的做市配置
create --controller-config market_making.pmm_simple
# 配置1: BTC-USDT, 价差0.5%
# 配置2: ETH-USDT, 价差0.8%
# 创建加载器配置
create --script-config v2_generic_with_controllers
# 输入: conf_pmm_simple_1.yml, conf_pmm_simple_2.yml
启动时,加载器会同时运行所有控制器,每个控制器独立管理自己的执行器。在状态输出中,可以看到每个控制器的运行情况,包括当前持仓、未实现盈亏、成交历史等。
动态配置更新
V2 框架的配置是动态的,这意味着可以在策略运行时修改配置文件,系统会自动加载新配置。这个特性对于需要频繁调整参数的策略特别有用。
# 修改配置文件
nano conf/controllers/conf_pmm_simple_1.yml
# 修改 buy_spreads 从 0.005 到 0.008
# 保存后等待 config_update_interval(默认60秒)
# 策略会自动应用新参数
控制器会定期检查配置文件的最后修改时间,如果发现变化,就重新加载参数。这个过程对交易没有影响,执行器会继续运行,只是新参数会在下一次刷新时生效。这种热更新能力让策略调优变得非常方便,无需重启机器人就能测试不同的参数组合。
控制器的设计体现了 V2 框架的模块化思想。它把策略逻辑封装成独立的组件,通过配置文件驱动,通过执行器实现。这种分层架构不仅提高了代码的复用性,也让策略的管理和部署变得更加灵活。无论是简单的趋势跟踪,还是复杂的多层做市,控制器都提供了清晰的抽象和强大的扩展能力。
下一章将深入探讨执行器的内部机制,看看这些控制器生成的动作指令是如何被具体执行的,以及执行器如何管理订单的生命周期。