第 1 章 · 02 核心包与外部插件的架构边界 本节摘要:本节是全教程最重要的"认知澄清"。当你 clone 下 仓库,翻遍目录会发现——没有一行 CTP 连接代码,没有一个完整的 CTA 策略,没有数据库实现,没有数据下载逻辑。这不是项目残缺,而是 VeighNa 在 3.0 版本刻意做的架构拆分:核心仓库只保留"骨架",所有网关/应用/数据库/数据服务都拆成独立的 PyPI 包。这种"核心 + 插件"设计让框架活了十年仍能持续扩展,是整个 VeighNa 最值得学的工程决策。本节讲清拆分的来龙去脉、核心包里到底有什么没有什么、以及怎么按需装配外部插件。 内容来源:原项目源码 目录结构、 、 、 ,精读并套用体系化模板。
本节摘要:本节是全教程最重要的"认知澄清"。当你 clone 下
vnpy仓库,翻遍目录会发现——没有一行 CTP 连接代码,没有一个完整的 CTA 策略,没有数据库实现,没有数据下载逻辑。这不是项目残缺,而是 VeighNa 在 3.0 版本刻意做的架构拆分:核心仓库只保留"骨架",所有网关/应用/数据库/数据服务都拆成独立的 PyPI 包。这种"核心 + 插件"设计让框架活了十年仍能持续扩展,是整个 VeighNa 最值得学的工程决策。本节讲清拆分的来龙去脉、核心包里到底有什么没有什么、以及怎么按需装配外部插件。
内容来源:原项目源码
vnpy/目录结构、vnpy/trader/database.py、vnpy/trader/datafeed.py、README.md,精读并套用体系化模板。
阅读完本节,你应当能够:
import_module(f"vnpy_{name}") 这个动态加载魔法。假设你刚 clone 了 vnpy 仓库,想学"VeighNa 是怎么连 CTP 的"。你打开 vnpy/ 目录,翻遍所有 .py 文件,大概率会困惑:
ctp 相关?——没有。connect(连接登录)的真正实现?——只有抽象方法签名,没有实现。CtaTemplate。sqlite 实现。这是因为:
⚠️ 架构边界(全教程最重要的认知):本仓库
vnpy只是核心包(skeleton)。所有对接具体交易所/数据库/数据服务的代码,都拆成了独立的 PyPI 包,需要单独pip install再import。
我们把 vnpy/ 目录摊开,看核心包实际包含的五大组件:
| 组件 | 路径 | 行数 | 作用 |
|---|---|---|---|
| 事件引擎 | vnpy/event/engine.py |
145 | Queue+Thread 发布订阅总线,框架心脏 |
| 主框架 | vnpy/trader/ |
~8000 | MainEngine 装配、object 数据建模、gateway/app 抽象、utility 工具、converter 开平转换、optimize 优化、ui 界面 |
| K 线图表 | vnpy/chart/ |
~1100 | 基于 pyqtgraph 的 K 线可视化 |
| RPC 分布式 | vnpy/rpc/ |
~320 | 基于 ZeroMQ 的跨进程通讯 |
| AI 量化 | vnpy/alpha/ |
~3700 | 多因子 + 机器学习 + 组合回测(4.0 新增) |
合计约 12,840 行 / 67 个 Python 文件。注意:
三层职责清晰:
3.0 之前,VeighNa 是一个"巨石仓库"——所有网关、应用都在一个 repo 里。这带来几个痛点:
拆成独立包后:
vnpy_ctp 单独发版,CTP 接口变了只升 vnpy_ctp,核心 vnpy 不动。pip install 自己用的几个插件,依赖精简。💡 核心心法:这是经典的**"开闭原则"**落地——核心对扩展开放(随时加新网关新应用),对修改关闭(加新插件不用改核心一行代码)。十年里 VeighNa 从纯 CTP 扩展到全球几十个市场,核心包的主干结构几乎没变,靠的就是这套设计。
核心包怎么做到"零硬依赖插件"却又"能用插件"?答案是 Python 标准库的 importlib.import_module。看 vnpy/trader/database.py:139-159 的工厂函数:
def get_database() -> BaseDatabase: global database if database: return database # 单例缓存 database_name = SETTINGS["database.name"] # 如 "sqlite"/"mysql" module_name = f"vnpy_{database_name}" try: module = import_module(module_name) # 动态导入 vnpy_sqlite except ModuleNotFoundError: print(_("找不到数据库驱动{},使用默认的SQLite数据库").format(module_name)) module = import_module("vnpy_sqlite") # 兜底用 vnpy_sqlite database = module.Database() # 约定:模块暴露 Database 类 return database
逐行解读:
SETTINGS["database.name"] 读你想要的数据库,比如 "mysql"。vnpy_mysql。import_module("vnpy_mysql") 运行时导入——如果没装这个包会抛 ModuleNotFoundError。Database 类,实例化即得到具体实现。datafeed.py:39-68 的 get_datafeed() 是完全同构的设计(暴露 Datafeed 类)。vnpy/trader/datafeed.py:39-68:
def get_datafeed() -> BaseDatafeed: global datafeed if datafeed: return datafeed datafeed_name = SETTINGS["datafeed.name"] # 如 "rqdata" if not datafeed_name: datafeed = BaseDatafeed() # 没配置就用空实现 print(_("没有配置要使用的数据服务...")) else: module_name = f"vnpy_{datafeed_name}" try: module = import_module(module_name) datafeed = module.Datafeed() except ModuleNotFoundError: datafeed = BaseDatafeed() print(_("无法加载数据服务模块,请运行 pip install {} 尝试安装").format(module_name)) return datafeed
💡 核心心法:
import_module是这套架构的"粘合剂"。核心包写死了字符串约定(vnpy_<name>包 +Database/Datafeed类名),但不 import 任何具体包。运行时才按用户配置"现用现导"。这是 Python 实现"插件架构"最轻量的方式——不需要插件注册中心,不需要 entry_points,纯靠命名约定 + 动态导入。
数据库/数据服务用 import_module 自动加载(读配置即可),但网关和应用需要用户在 run.py 里显式装配——因为同一个核心包可能要同时装多个网关(CTP + IB),无法靠单一配置项表达。
装配方式是 MainEngine.add_gateway(GatewayClass) / add_app(AppClass),传入已 import 的类。例如:
from vnpy_ctp import CtpGateway # 你 pip install vnpy_ctp 后才能 import from vnpy_ctastrategy import CtaStrategyApp main_engine.add_gateway(CtpGateway) # 装配网关 main_engine.add_app(CtaStrategyApp) # 装配应用
注意:这里没有 import_module——用户自己 import 外部包,把类对象传给 add_gateway。核心包仍然不 import 任何外部网关包,是用户的 run.py 把两边"接"上的。
⚠️ 澄清一个常见误解:有人说"VeighNa 用 importlib 自动发现所有网关",这是错的。数据库/数据服务是自动发现的(读 SETTINGS 配置 import_module),网关/应用是用户手动装配的(run.py 里 import + add_*)。两者的"插件化"实现方式不同,第 4、5 章会详讲。
实际使用时,你按业务需求 pip install 对应插件即可:
# 做国内期货 CTA pip install vnpy vnpy_ctp vnpy_ctastrategy vnpy_sqlite # 加美股期权 pip install vnpy_ib vnpy_optionmaster # 用 RQData 下载数据 pip install vnpy_rqdata vnpy_datamanager # 用 AI 量化(alpha 已内置,无需额外装) pip install vnpy # vnpy.alpha 在核心包里
核心包 vnpy 是必装的底座,其他全按需。这就是"核心 + 插件"在用户侧的体验。
database.py/datafeed.py 用 import_module(f"vnpy_{name}") 按配置现用现导,核心零硬依赖。import + add_gateway/add_app,不是自动发现。vnpy.alpha 是内置在核心包的 AI 量化模块,无需额外 pip install。下一节,我们用
examples/veighna_trader/run.py这份"装配清单",实操演示怎么从零搭起一个完整的 VeighNa 交易程序——七步装配,一步不少。