Blankly / 三套框架
Model、Strategy、Screener:三套基类该用哪一个
Blankly 的顶层导出里有四个可以直接 import 的框架类:Model、Strategy、Screener,以及期货专用的 FuturesStrategy。但 blankly init 只会问你「strategy 还是 screener」——四类框架、两类模板,这个落差就是选型时最容易踩空的地方。这一页把四者的定位与入口方法摆在一起对照。
blankly/frameworks 目录结构整理)。注意 blankly init 只为 Strategy 与 Screener 提供模板,Model 与 FuturesStrategy 需要自行搭建。四类框架的定位、入口与覆盖情况是什么?
判断该用哪个,只要回答两个问题:你的策略是「盯着一两个标的持续决策」,还是「在一批标的里先筛出候选」;以及你需要的是标准资产还是期货合约。
| 类 | 定位 | 典型入口 | CLI 是否提供模板 |
|---|---|---|---|
Model | 最底层的事件驱动基类,你自己写 main() 循环与 event() 回调 | 继承 blankly.Model,实现 main(self, args);用 self.sleep('1h') 控制节奏 | 否(TEMPLATES 里只有 strategy 与 screener) |
Strategy | 声明式封装,把「价格事件」「自定义事件」「定时事件」注册进去即可 | blankly.Strategy(exchange) + add_price_event(fn, symbol, resolution, init) | 是(模板 none.py、rsi_bot.py) |
Screener | 面向「一批标的」的横截面评估,逐个标的产出筛选结果 | 依赖 ScreenerState 与运行时调度器 | 是(模板 none_screener.py、rsi_screener.py) |
FuturesStrategy | 期货专用策略类,配合 BinanceFutures 等期货接口使用 | 与 Strategy 同族的注册式接口,但账户/持仓语义按合约处理 | 否(init 的交易所列表也不含期货交易所) |
StrategyState / ScreenerState / FuturesStrategyState | 传入回调的状态容器,承载 variables、interface、resolution、base_asset | 由框架构造后作为回调参数传入,不需要自己 new | — |
blankly init 只给两类模板,选 Model 或期货路线的人拿不到脚手架,只能照着仓库 examples 目录里的示例自己搭。按场景选:什么情况下用哪一个
下表按「你要解决的问题」组织,而不是按类名组织。每一行都给出一条能立刻验证的判断依据。
| 你的场景 | 建议基类 | 理由 | 立刻验证的方法 |
|---|---|---|---|
| 单标的、按价格推进做买卖决策 | Strategy | 有官方模板,回调式接口最少样板代码 | 跑一次 blankly init 看生成的 bot.py |
| 需要按固定时间间隔做非价格决策(如每日再平衡) | Strategy 或 Model | Strategy 可注册定时类事件;Model 自己控制 sleep 节奏更灵活 | 看仓库 examples 里的定时相关示例 |
| 需要在多个标的上跑同一套逻辑做横截面排序 | Screener | 它的抽象就是「一批标的 → 筛选结果」 | 跑 blankly init 选 screener,读生成的模板 |
| 需要把外部事件(新闻、情绪、自定义数据)接进决策 | Model 或 Strategy | 两者都支持自定义事件;Model 在示例里示范了 event(type_, data) 写法 | 读仓库 examples 的 custom_model.py |
| 要做期货合约、杠杆与保证金相关逻辑 | FuturesStrategy | 只有它按合约语义处理持仓 | 读仓库 examples 里的 futures 系列示例 |
| 只想做离线回测,不打算上实盘 | Strategy + KeylessExchange | 离线链路最短,零密钥 | 见首个离线回测页 |
| 需要多个策略并行运行 | 注意 BlanklyBot 与多进程示例 | 顶层导出了 BlanklyBot,仓库 examples 里有 multicore_bot.py | 读该示例确认进程模型 |
blankly init 实际会生成什么
脚手架决定了你的起步速度。下面这张表来自 new_cli.py 的模板表与生成逻辑,是当前发行版的真实行为。
| 交互步骤 | 可选值 | 说明 | 注意点 |
|---|---|---|---|
| 选择交易所 | alpaca / binance / coinbase_pro / ftx / oanda / kucoin / Keyless | 来自 deployment/exchange_data.py 的 EXCHANGES 列表 | 比 README 的支持表少很多,详见交易所可用性矩阵页 |
| 选择模型类型 | strategy / screener | 来自 TEMPLATES 字典的两个键 | 没有 model 这个选项 |
| 选择模板 | strategy:none / rsi_bot;screener:none / rsi_screener | 模板文件来自包内的 blankly/data/templates/ | 选 none 就是一个空壳,需要自己填逻辑 |
| 是否添加密钥 | 是 / 否 | 选是会进入交互式密钥录入;Keyless 不涉及密钥 | 可以之后用 blankly key add 补 |
| 生成的文件 | bot.py、backtest.json、requirements.txt、keys.json、blankly.json、settings.json | 共 6 个文件 | README 只列了 5 个(漏了 requirements.txt) |
| 用指定模板初始化 | blankly init <model> | 源码里该分支会直接返回 | 会打印 Starter models are currently disabled. |
bot.py 会把 EXCHANGE_NAME、EXCHANGE_CLASS、SYMBOL_LIST、SYMBOL、QUOTE_ASSET 这几个标记替换成你所选交易所的真实值。也就是说你选 alpaca 时得到的默认标的与选 binance 时不同,这会影响你复制示例代码时的直觉。声明式与自管循环:两种写法你各自会失去什么
Strategy 与 Model 的差别不只是 API 好看不好看,而是「谁控制时间推进」这件事的归属不同。
Strategy:声明式,样板代码少
你只写「当价格更新时做什么」「当某个事件到来时做什么」,时间推进由框架负责。好处是回测与实盘共用同一段回调逻辑,代码量最小。代价是你对「事件之间的相对时序」控制力更弱,复杂的状态机不好表达。
Model:自管循环,控制力强
你实现 main(),用 self.sleep(...) 自己决定节奏,也可以在循环里自由组织多步骤逻辑。好处是复杂流程好写。代价是回测路径与实盘路径的差异要你自己保证一致,框架不再帮你统一。
Screener:换的是问题形态
它回答的不是「现在买还是卖」,而是「这批标的里哪些值得进入下一步」。所以它天然是多标的批处理,产出是候选集合,后续通常还需要第二个环节来决策买卖。
FuturesStrategy:换的是账户语义
期货涉及杠杆、保证金、双向持仓,账户与持仓的读法和现货不同。前三个类处理的是现货式账户,只有它按合约语义处理——这一步不能用「继承后重写几个方法」糊过去。
| 维度 | Strategy | Model | Screener |
|---|---|---|---|
| 时间推进由谁控制 | 框架 | 你的 main() 循环 | 框架调度 |
| 每单位时间处理几个标的 | 通常 1 到少数几个 | 由你在循环里决定 | 一批 |
| 产出形态 | 下单动作 | 下单动作 | 候选集合 |
| 回测与实盘复用度 | 高(同一回调) | 取决于你的循环写法 | 高 |
| 官方脚手架 | 有 | 无(仅 examples 示例) | 有 |
| 上手成本 | 低 | 中 | 中(需理解状态对象) |
选错之后换回来要改什么
如果一开始没判断清楚,切换基类并不是改一行 import 的事。下面按改动范围说明代价,帮你在写代码之前就把这件事决定掉。
| 迁移方向 | 需要改的东西 | 改动量 | 可以复用的部分 |
|---|---|---|---|
| Strategy → Model | 把注册式回调改写成一个 main() 循环;自己处理 sleep 与数据推进 | 大 | 订单调用与指标计算逻辑可以基本照搬 |
| Model → Strategy | 把循环体拆成若干回调并注册;状态从自由变量改为 state.variables | 大 | 同上 |
| Strategy → Screener | 问题形态从「单标的时序决策」改成「多标的横截面评估」,产出从下单改成候选集 | 很大 | 指标计算函数可复用 |
| 现货 → 期货 | 账户读取、下单参数、持仓语义全部改;还要处理杠杆与保证金概念 | 很大 | 指标与信号逻辑可复用 |
| 换交易所(同基类) | 通常只需改交易所构造与标的代码 | 小 | 策略主体理论上不动 |
Strategy 加离线数据把想法跑出形状,再决定是否迁移到 Screener 或期货语义——这样最坏情况只是丢掉回调层的胶水代码。关于框架选型的高频问题
回答基于发行版 1.18.25b0 的顶层导出与 CLI 源码;具体 API 行为以官方仓库实现为准。
为什么 blankly init 没有 Model 选项?
这是当前发行版的实际行为:CLI 的模板表 TEMPLATES 只有 strategy 与 screener 两个键。Model 仍然可以从包顶层导入并使用,只是没有脚手架。需要 Model 写法的话,可以直接读仓库 examples/ 目录里的示例作为起点。
Strategy 和 Model 能混用吗?
它们共享同一套交易所接口层,所以在同一个进程里各自构造是可行的;但两套事件推进机制同时存在会让时序难以推理。实务上更常见的做法是只选一套作为主结构,另一套只用来复用其中的工具函数(如指标计算)。
Screener 跑出来的结果能直接下单吗?
不能直接。Screener 的产出是候选集合,本质是「筛选」而不是「执行」。要真的下单,你需要再写一个消费候选集的环节。这个设计是有意的——筛选与执行是两类不同频率、不同风险特征的任务。
期货部分现在能用于实盘吗?
本站不做这个判断。可核对的官方表述是:仓库的工程文档里写明期货回测引擎「仍在 beta」,并且资金费率的下载与缓存「仍在开发中」。在没有处理资金成本的情况下,期货策略的回测和实盘之间会有系统性差异,请据此评估。
用 Model 写的话,回测和实盘还能复用同一份代码吗?
可以,但复用度取决于你怎么写 main()。Strategy 的复用是由框架保证的(同一份回调同时用于两种模式);Model 的复用由你保证——如果循环里写了对当前时间、当前账户的隐式假设,回测和实盘就可能走出不同路径。这也是为什么新手路径通常从 Strategy 开始。
三套框架的代码分别放在仓库哪里?
都在 blankly/frameworks/ 下:model/model.py、strategy/strategy.py、screener/screener.py,以及 strategy/futures_strategy.py 与对应的状态类文件。想确认某个方法的行为,直接读这几个文件比看文档更准确。