☰
基于vnPy的多策略量化交易系统:分布式回测与多账户风控实践
2026/10/17 6:35:01 网站建设 项目流程

简介:本资源是一套基于vnPy框架构建的多策略量化交易系统,面向高校人工智能、自动化、电子信息等专业师生及金融IT研发人员,解决多账户协同管理、多策略并行回测与跨市场(期货/股票/期权/数字货币)风险管理等实际问题。压缩包含701个文件,以285个JavaScript前端交互逻辑、116个Z-Bak备份配置、86份Markdown技术文档、73个Less样式文件及56个TypeScript核心模块为主,辅以18个Python后端服务脚本与Docker相关编排文件(如Dockerfile、dockerignore、nginx.conf等),整体仅584KB,轻量但结构完整。已有58人学习下载,资源经导师专项指导并获答辩95分,所有模块均通过实测验证;提供标准化分层架构、分布式回测接口封装、多节点部署配置模板及详尽风险管理参数说明,可直接用于毕业设计、课程实践或二次开发,显著降低量化系统从研究到实盘的落地门槛。 做量化交易这几年,我踩过最大的坑不是策略本身不赚钱,而是整个系统根本没有办法支撑“多策略、多账户、高频迭代”这种玩法。早年间我跑单策略,回测一次几分钟无所谓,后来策略多了、参数组合多了,一次全参数扫描跑一个通宵是常事。等换到多账户实盘,混乱更是加倍:策略A的信号发到账户B,资金分配全靠拍脑袋,风控只能靠人工盯。后来我花了几个月时间,基于vnPy搭了一套多策略量化交易系统,把分布式回测和多账户风险管理一起做了进去。这篇文章就是把这套系统的架构思路、关键实现和踩坑记录完整写出来,希望给正在往这个方向走的朋友一些参考。

这套系统适合谁?我的判断是,至少手里已有两三个能稳定跑的策略、并且开始考虑多账户资金管理的人。如果你还在单策略单账户阶段,这篇文章里的部分设计对你来说可能过度,但分布式回测的思路仍然值得提前了解。我会尽量把每一层讲透,包括为什么这么选、有哪些替代方案、实际操作中会遇到的细节问题。

1. 整体架构设计:为什么我把系统拆成三层

1.1 先回答一个核心问题:为什么要做多策略系统

单策略系统最大的问题是收益曲线太依赖单一逻辑。你做一个趋势跟踪策略,震荡行情里就是难受;做一个套利策略,市场风险偏好突变时可能连开仓机会都等不到。把多个低相关的策略组合在一起,目的从来不是让某一年收益翻倍,而是让资金曲线更平滑,最大回撤更容易接受。

但多策略带来的复杂度是成倍增加的。策略之间如何隔离?同一个标的不同策略同时发信号怎么处理?每个策略的仓位和止损如何独立管理?这些问题在单策略时代根本不存在。如果不从架构上解决,策略越多,系统越容易出事故。

我见过有人用一个全局字典存所有策略的持仓状态,两个策略同时操作同一个合约时互相覆盖数据,最后持仓对不上,亏损了都找不到责任人。这种问题只能靠架构约束,靠“注意一点”是防不住的人性漏洞。

1.2 系统分层:数据层、策略层、执行层、风控层

我最终采用的架构,核心是四层分离。每一层只干自己的事,通过消息和接口通信,不直接互相调用。

先看数据层。数据是量化的地基,vnPy自带的数据服务和回测引擎用的是同一套数据结构,这一点非常关键。我在数据层统一管理历史行情、tick数据、合约信息,把这些数据做成可供回测和实盘共用的格式。回测时数据从本地数据库读,实盘时数据从行情接口接收,但策略层看到的永远是标准的BarData、TickData。

策略层是vnPy的核心领域。每个策略都是独立的策略类实例,拥有自己的参数、持仓状态、信号逻辑。策略层不关心订单最终发到哪个账户,它只负责输出目标仓位和订单请求。

执行层往下对接vnPy的网关,统一处理报单、撤单、成交回报。这一层也需要处理多账户路由:根据策略所属的账户标签,把订单发到对应的交易接口。

风控层则完全独立于策略逻辑。所有订单在真正发出去之前,必须先经过风控校验;所有持仓变化都要经过风控监控。风控层的权限高于策略层,一旦触发熔断,可以直接禁止开仓或者强平持仓。

这套分层听着简单,实际操作中很容易被破坏。最典型的错误是把风控逻辑写在策略里,导致风控跟着策略参数一起被回测优化,到了实盘等于没有风控。我的原则是:风控代码和策略代码物理隔离,放在不同目录、不同模块,绝不允许策略直接调用风控内部函数。

2. 基于vnPy的多策略引擎实现

2.1 vnPy框架的核心机制,以及我为什么没重新造轮子

vnPy本身是一个事件驱动的开源量化交易框架,内置了CTA策略模板、回测引擎、实盘交易网关、数据管理等功能。我选择基于它而不是自己从头写,原因很现实:稳定性和生态。

vnPy的事件引擎底层是消息队列模式,行情、成交、订单状态都是不同的事件类型,策略通过订阅事件来接收数据。这种机制决定了它天然适合做多策略——每个策略都是独立的订阅者,互不干扰。

我用的是vnPy里的CtaTemplate基类来实现策略。这个模板定义了on_init、on_start、on_bar、on_tick、on_order等回调方法。做多策略扩展时,核心问题是如何管理多个策略对象。vnPy官方的策略引擎可以直接加载多个策略,但对多账户、多风控的支持不够。我在它的基础上做了一层封装,增加了策略实例的账户归属和状态管理。

# 策略管理器的核心结构(基于vnPy官方引擎改造) # 完整代码很长,这里只保留关键设计 class StrategyManager: def __init__(self, event_engine, risk_manager): self.event_engine = event_engine self.risk_manager = risk_manager self.strategies = {} # strategy_id -> strategy_instance self.strategy_account_map = {} # strategy_id -> account_tag def add_strategy(self, strategy_class, strategy_id, account_tag, settings): # 每个策略实例绑定一个账户标签 strategy = strategy_class(self.event_engine, strategy_id, settings) self.strategies[strategy_id] = strategy self.strategy_account_map[strategy_id] = account_tag return strategy def send_order(self, strategy_id, contract, direction, offset, price, volume): # 所有订单先经过风控层检查 account_tag = self.strategy_account_map[strategy_id] if not self.risk_manager.check_order(strategy_id, account_tag, contract, direction, volume): self.event_engine.put(LogEvent("风控拒绝订单", strategy_id)) return None # 然后按账户路由到对应网关 gateway = self.get_gateway_by_account(account_tag) return gateway.send_order(contract, direction, offset, price, volume)

这里面有几个关键点。一是策略与账户的映射必须在创建时就固定,不能中途更改,否则运营时很容易混乱。二是所有报单都走统一入口,策略本身不直接操作任何gateway对象,方便风控拦截和日志审计。

2.2 多策略调度的核心:实例隔离与共享机制

多策略调度最核心的问题是隔离与共享的边界。我的做法是:策略状态完全隔离,策略数据有条件共享。

状态隔离是硬性的。每个策略实例都有自己的持仓字典、参数缓存、信号状态,策略之间禁止互相读取。要查看另一个策略的状态,只能通过管理器对外暴露的只读接口。这样做的原因是状态一旦交叉,问题极难排查。例如两个策略都做了螺纹钢多单,一个策略止盈平仓时,错误地触发了另一个策略的持仓更新,就会导致账户实际持仓与策略内部持仓不一致。

数据共享则是性能优化的关键。同一份行情数据如果每个策略都复制一份,内存和加载时间都浪费。我的做法是:行情数据通过事件引擎广播,所有策略共享同一份数据引用,但策略内部处理时不允许修改原始数据。

多策略的启动和停止也需要注意。vnPy的策略加载一般是逐个初始化,如果十几个策略依次初始化,每个策略都要读取持仓和加载历史数据,整个过程可能需要几秒到几十秒。我写了一个预加载机制,在策略真正启动前先把所有策略需要的数据请求集中起来,从数据库批量读取后分发到策略缓存里,这样初始化的耗时会大幅下降。

还有一个容易被忽略的细节:策略的启动顺序。如果有两个策略都处于运行状态,而且它们会在同一根K线上发出相反方向的订单,谁先谁后会影响成交结果。我通过给策略增加priority字段来管理启动优先级,同时在同一根K线结束时统一触发event_engine的bar_end事件,避免在同一根bar内频繁并发调报单接口。

3. 分布式回测:从单机串行到多进程并行

3.1 回测为什么会成为多策略迭代的瓶颈

先说一个真实案例。我有个策略组合包含六个子策略,每个策略有几十个参数组合。单策略串行回测一次需要大约40秒,六策略全组合就是6乘几十等于几百次回测,总时长轻松超过五个小时。而且这不是只跑一次,实盘上线后每周都要重新滚动优化,五个小时根本卡不住节奏。

回测本身慢主要是因为三件事。一是历史数据量大,尤其使用分钟级数据时,一个品种一年的分钟K线就有约10万根,多品种数据量更大。二是策略逻辑里大量的循环和指标计算,每次都要重复算一遍。三是参数组合之间的重复计算没有被利用,相邻参数组合回测结果本来应该有连续性,但串行方式下完全无法共享。

这三个瓶颈决定了单靠“换一台更快的电脑”是解决不了根本问题的。必须把计算任务拆分到多个进程甚至多台机器上并行处理,也就是分布式回测。

3.2 分布式回测的方案选型:我为什么选了multiprocessing加Redis

市面上可选的分布式回测方案不少,我简单对比过:

  • Python内置的multiprocessing:零依赖,适合单机多核并行,但任务调度和结果回收需要自己写。
  • Celery:成熟的任务队列框架,支持多机部署,但本身比较重,需要额外维护消息中间件,回测这种密集计算任务反而不太适合它的worker模型。还有一个问题:Celery的任务结果序列化效率一般,大批量元组数据存Redis再取回会有额外开销。
  • Ray:分布式计算框架,抽象做得很好,支持大规模并行,但引入的依赖较重,环境部署复杂,对习惯了传统Python生态的量化团队来说门槛偏高。
  • Dask:擅长大数据量下的并行计算,但回测场景的数据量其实没那么大,主要瓶颈是策略计算本身,Dask的优势用不上。

最终我选择了multiprocessing加Redis的方案。Redis在这里不负责计算,只做任务分发和结果汇总。每个worker是一个独立的Python进程,运行vnPy的回测引擎,从Redis队列中领取回测任务,跑完后把结果写入另一个Redis队列。主进程负责任务拆分、下发和结果回收。

为什么选这个组合?因为简单、可控、易排查。multiprocessing是Python标准库,worker崩溃后不会影响主进程,配合Redis可以实现比较粗糙但有效的任务队列。对于单机多核或者几台机器组成的小集群,这套方案足够用。如果后续规模再扩大,我可以把Redis队列换成消息中间件,但workder的接口不需要大改。

# 分布式回测任务调度器核心逻辑(简化版) import multiprocessing as mp import redis class BacktestScheduler: def __init__(self, redis_host='localhost', task_queue='bt_tasks', result_queue='bt_results'): self.r = redis.Redis(host=redis_host) self.task_queue = task_queue self.result_queue = result_queue self.workers = [] def generate_tasks(self, strategy_configs, param_grids): # 把"策略 x 参数组合"拆成独立任务,每个任务包含完整配置 tasks = [] for strategy_id, config in strategy_configs.items(): for param_set in param_grids[strategy_id]: tasks.append({ 'strategy_id': strategy_id, 'config': config, 'params': param_set }) return tasks def submit_tasks(self, tasks): for task in tasks: self.r.rpush(self.task_queue, json.dumps(task)) def start_workers(self, worker_class, num_workers=mp.cpu_count() - 2): # 每个worker是一个独立进程 for _ in range(num_workers): p = mp.Process(target=worker_class.run, args=(self.task_queue, self.result_queue)) p.start() self.workers.append(p) def collect_results(self, task_count): results = [] for _ in range(task_count): # 轮询结果队列,看实际情况加超时 result_json = self.r.blpop(self.result_queue, timeout=60)[1] results.append(json.loads(result_json)) return results

worker进程内部的关键是初始化回测环境。vnPy的回测引擎需要在每个worker中单独初始化,包括数据引擎、回测引擎和策略模板。为了避免每个任务都重新加载数据,我在worker启动时先把数据预加载到内存里,后续回测直接从内存取。

# worker进程的简化实现 class BacktestWorker: @classmethod def run(cls, task_queue, result_queue): r = redis.Redis() # 预加载共享历史数据(比如主力连续合约的日线/分钟线) data_cache = cls.load_data_cache() while True: task_json = r.blpop(task_queue, timeout=5) if not task_json: continue task = json.loads(task_json[1]) # 用vnPy回测引擎执行 bt_engine = BacktestEngine() bt_engine.set_parameters(**task['config']) bt_engine.add_strategy(eval(task['strategy_id']), task['params']) bt_engine.load_data(data_cache[task['strategy_id']]) bt_engine.run_backtest() statistics = bt_engine.calculate_statistics() r.rpush(result_queue, json.dumps({ 'task': task, 'statistics': statistics }))

有一个细节必须提醒:每个任务必须固定随机种子,否则同一个参数组合跑出来的结果可能因为随机数不同而产生差异,回测优化就失去意义了。

3.3 回测任务切分原则:按参数组合切,而不是按时间片切

一开始我不太懂,尝试过把一张五年的历史数据按年份切成五段,让五个worker并行跑,再把结果拼接起来。结果一跑就发现问题:策略有持仓期,持仓跨年段的时候,前一年结束的持仓在第二年段里状态不连续,导致回测结果和完整跑出来的结果对不上。

后来我明白了一个原则:回测任务应该按策略和参数组合切,而不是按时间片切。因为vnPy回测引擎本身就是完整的,它需要完整的历史数据来维护策略状态。每个任务相当于完整跑一遍回测,只是参数不同。分布式带来的加速来自“多个参数组合同时跑”,而不是“一个回测被拆成多段”。

当然,如果你的策略是日频、持仓周期很短,且不会跨天持仓,按时间片切也不是绝对不行。但大多数CTA策略和统计套利策略都有跨周期持仓,我还是建议按参数组合切,安全省心。

3.4 分布式回测的结果一致性:一个容易被忽略的坑

回测结果一致性是分布式系统里的经典问题。同一份代码,在单机串行时结果确定,包装成多进程后结果可能变了。这个大概率不是代码逻辑问题,而是数据读取的时序问题。

我遇到过的一个具体问题:多个worker同时从本地数据库读取Redis里缓存的历史数据,数据库连接数被占满,部分worker读取超时,拿到的是空数据,回测结果自然不对。解决方法是worker启动时一次性把数据加载到进程内存,回测过程中不访问数据库。如果数据量太大,也可以先把数据序列化成文件格式,worker从本地文件加载。

另外,vnPy回测引擎里的某些统计指标,比如最大回撤,计算时需要完整资金曲线,分布式并行如果只返回统计指标,不返回资金曲线,后期调试可能不够用。我的做法是任务结果里除了统计指标,还压缩保存一个精简版资金曲线和交易记录,方便后续分析和画图。结果数据的序列化格式用msgpack比json更紧凑,速度也更快。

4. 多账户风险管理:资金分配与风险隔离

4.1 多账户场景到底复杂在哪

多账户风险管理是一个比回测更接近业务本质的问题。它有好几种场景:同一个策略在多个资金账户里运行,资金量不同;不同的策略分别跑在不同账户里;还有母子账户结构,母账户做统一风控,子账户执行交易。

我刚做多账户时犯过一个低级错误:用两个账户跑同一个趋势策略,账户A和账户B的资金量分别是100万和50万。我在策略里写的是固定手数,结果两个账户都按10手下单,资金小的账户仓位明显偏重,回撤控制完全失效。

后来我才意识到,多账户管理的本质是风险预算的分配,而不是简单的资金分配。你必须先定义每个账户能承受多少风险,再根据这个风险预算倒推仓位。

4.2 风险预算模型:怎么分配资金才科学

常用的风险预算模型有三种:固定比例法、波动率目标法、等风险贡献法。

固定比例法最简单:比如总资金1000万,策略A分30%,策略B分30%,策略C分40%。这种方法的优点是直观,缺点是风险不均衡。如果策略A的波动率特别大,策略C虽然资金占比高但实际承担的风险可能远小于A。所以固定比例法更适合策略之间波动率相近的场景。

波动率目标法稍微复杂一点:先设定账户目标年化波动率,比如10%,然后根据每个策略当前的预期波动率计算仓位。公式很简单:目标波动率除以策略年化波动率,再乘以一个调节系数。这个方法的优势是风险可预期,但缺点是需要估计策略未来的波动率,估计不准照样出问题。

等风险贡献法是最精细的模型:让每个策略对组合总风险的贡献度相等。它需要先算出组合协方差矩阵,然后用优化算法求解权重。对于只有几个策略的情况,手工Excel也能算,但需要每天都做,比较麻烦。

我实际用的是波动率目标法,因为它最容易落地,也最容易解释给不懂量化的人。每个策略在回测阶段就计算过去60日的滚动年化波动率,实盘每周末更新一次目标仓位。回测时我就把波动率目标参数包含在回测配置里,这样回测和实盘的仓位逻辑是一致的。

4.3 交易前、交易中、交易后的三层风控

多账户风险管理需要覆盖订单产生的全生命周期。我的系统里风控分三层,每一层盯不同的问题。

第一层是交易前风控:任何订单在发往网关之前,必须通过检查。检查内容包括账户资金是否充足、当前持仓是否接近持仓上限、订单价格是否超出涨跌停价、单笔下单量是否超过大单限额等。这一层的作用是把明显的错误拦截在交易系统内部,避免错误单发到真实市场。

第二层是交易中风控:订单发出去之后,持续监控成交状态和持仓变化。比如限价单超过一定时间没有成交需要自动撤单重发;多个策略同时买入同一合约时,所有账户累计持仓不能超过合约总持仓限额;盘中一旦某个账户日内亏损达到阈值,暂停该账户所有开仓操作。这些规则都写在独立的RiskMonitor模块里,和策略逻辑完全解耦。

第三层是交易后风控:每天盘后对每笔交易和每个账户做归因分析,检查策略实际收益和理论预期之间是否有显著偏差,账户资金与持仓的报表是否一致。这一层看似简单,但往往能发现很多隐藏问题。

一个我印象很深的例子:账户里有一笔手动交易,被误记在策略A名下,导致策略A的收益曲线和偏差分析完全失真。如果没有交易后归因检查,这个错误可能要在结算时才能暴露。

4.4 风控模块代码示例:一个可落地的检查器

下面是我风控模块里一个简化的检查函数,展示如何在订单发出前做多账户风控。

# 多账户风控检查器(简化版) class MultiAccountRiskManager: def __init__(self, account_configs): self.accounts = account_configs self.positions = {} # (account_tag, vt_symbol) -> volume self.daily_pnl = {} # account_tag -> today_pnl def check_order(self, strategy_id, account_tag, contract, direction, volume): # 1. 检查账户是否存在 if account_tag not in self.accounts: return False # 2. 检查单笔数量 max_order_volume = self.accounts[account_tag].get('max_order_volume', 100) if volume > max_order_volume: return False # 3. 检查账户总仓位限制 # 这里用当前所有持仓的总市值占比做限制 total_position_value = sum( self.positions[(account_tag, symbol)] * price for (account_tag, symbol), price in self.positions.items() if account_tag == account_tag ) account_equity = self.accounts[account_tag]['equity'] if total_position_value / account_equity > self.accounts[account_tag].get('max_position_ratio', 0.8): return False # 4. 检查日内亏损限额 daily_loss_limit = self.accounts[account_tag].get('daily_loss_limit', 0.05) if self.daily_pnl[account_tag] < -account_equity * daily_loss_limit: return False # 5. 检查该策略今天是否已经触发熔断 if self.is_strategy_blocked(strategy_id, account_tag): return False return True

这个检查器里的每个数字都需要根据实际账户调整。我的建议是风控参数不要直接写在代码里,而是放在配置文件里,不同账户可以独立配置。这样运营人员可以按账户灵活调整,而不是每次改参数都要改代码重新发布。

5. 实盘接入的坑与排查记录

5.1 我遇到的常见问题速查表

下面是这套系统在实盘接入和运行过程中,我记录的一些常见问题和排查思路,整理成表供参考:

问题现象可能原因排查路径
策略实盘信号与回测不一致数据源不同、复权方式不同、撮合逻辑差异对比同一时间段bar数据;检查回测是否用后复权;实盘是否用实时价格
订单已发出但状态一直未变网关断线或接口限频检查vnPy日志、交易接口状态、网络连接;确认是否触发风控拦截
多账户下订单发错账户账户映射配置错误检查strategy_account_map配置;开启订单审计日志,记录每个订单的来源策略和账户
盘中策略卡死不响应事件循环阻塞、数据库查询耗时过长在事件循环里做耗时操作排查;把数据库查询改为异步或缓存
回测任务偶发失败worker崩溃、数据加载不全查看worker日志;在worker中增加任务重试机制
资金占用计算错误未考虑保证金和手续费用vnPy的AccountData同步账户真实资金,避免用策略内部估算资金

这些问题的共性是:大部分不是单点逻辑失误,而是系统各部分之间交互方式不清晰。所以我在项目里强制要求所有关键路径打审计日志,日志字段至少包含时间、策略ID、账户标签、合约代码、方向、价格、数量、风控结果。

5.2 几个让我印象深刻的真实事故

第一个事故和浮点精度有关。某策略计算仓位时用1.618作为参数,Python浮点数累加过程中出现微小偏差,导致实盘下单手数偶尔差1手。回测时因为数据量小没有暴露,实盘交易多了问题就出来了。后来我把所有仓位计算的输入参数统一做了quantization处理,即先乘10000取整再计算,最后再转回浮点。

第二个事故是关于时区的。我用的是vnPy默认的本地时区,但数据服务返回的时间戳是UTC,两者在切换夏令时时出现了一个小时的偏差。策略在下午三点五十八发出平仓信号,由于时间戳偏移,某些数据被归到了错误的一根K线里,导致开盘就平仓,损失了一整段的趋势利润。排查过程非常痛苦,最后是通过对比行情时间戳和服务器本地时间差异才发现的。从那以后,我统一所有时间戳为UTC,只在UI展示层做时区转换。

第三个事故出在配置管理上。我当时把账户资金阈值写在代码里,部署到生产环境后不小心修改了一个账户的配置,结果风控参数被刷新成默认值,临时把问题掩盖了。后续排查才发现账户的实际风控参数没有按计划更新。这个教训让我彻底转向了独立的配置文件管理,并把配置文件纳入版本控制。

5.3 值得养成的工程习惯

做这套系统之前,我对“能跑就行”很有执念,现在没这个想法了。以下几个工程习惯是我实盘运行之后越来越觉得重要的。

第一是日志规范。量化系统的bug往往需要事后复盘才能发现,日志是唯一的线索。我给每条日志都加了结构化字段,方便后续用脚本分析。盘后我会定时跑一个日志分析脚本,自动统计异常订单、风控拒绝、网关断线等事件。

第二是配置与代码分离。所有账户参数、策略参数、风控阈值、数据库地址都放到配置文件中。代码仓库里只保留配置模板,实际配置由部署流程生成。这样即使不同账户有不同配置,代码也只维护一份。

第三是自动化测试。一开始我觉得回测系统不需要测试,后来发现分布式回测的调度逻辑经常被改坏。我写了一些简单的单元测试,覆盖任务拆分、结果汇总、风控检查这几个核心函数。每次改动代码,先跑测试,再上线,这个习惯帮我省下了很多加班时间。

第四是版本控制。策略的参数组合本质上是一个高维空间,我建议用Git管理策略代码版本,用数据库或者文件记录每次回测参数和结果的对应关系。这样任何一次策略优化都能追溯到确切代码版本,免得几周后回看结果时不知道当时用的是哪套代码。

6. 这套系统的后续扩展方向

6.1 从单机分布式到跨机集群的迁移路径

当前这套分布式回测跑在单机多核模式下,已经能满足我的日常需求。如果后续策略数量继续增加,我可以考虑把worker部署到多台机器上。现有架构下迁移的成本并不高,因为worker和调度器之间通过Redis解耦,我只需要让worker的启动程序在多台机器上同时运行,并让它们连接同一个Redis即可。

这里面有一个必须重新设计的点:数据分发。单机时worker可以共享本机数据,跨机器后每台机器都需要完整的数据副本,否则不同机器上的worker拿到不同数据,回测结果没有可比性。我计划在扩展时给每台机器配一个本地数据同步脚本,把基础历史数据以文件方式分发,再用版本号和校验和保证数据一致性。

6.2 风控层的可扩展性设计

现在的风控规则都是基于简单的阈值判断,已经能覆盖大部分场景。但如果以后管理资金规模变大,可能需要引入实时风险价值计算、期权希腊字母风险监控等更精细的模型。我风控模块的设计保留了规则引擎的接口,后续可以新增算法类型,而不需要修改上层调用逻辑。

另一个方向是组合层风控。目前的风控以账户维度为主,虽然有累计持仓检查,但还没有形成组合层的最优风险配置。如果资金量再上一个台阶,我会在风控层加入组合优化模块,定期输出每个账户的策略权重建议和风险预算调整方案。

6.3 机器学习策略的接入问题

现在市面上很多量化框架都在往机器学习方向靠,vnPy本身不是为机器学习策略设计的,但可以通过数据接口和策略模板兼容。我计划后续把信号生成部分改造成独立模块,让机器学习模型跑在独立的推理服务里,模型输出信号后再转成vnPy可以理解的订单请求。

这样一来需要注意时延和异步问题。机器学习推理通常需要GPU资源,和回测/交易进程放在一起不现实,我的思路是在策略层设置一个异步post-signal回调,把策略状态发给推理服务,拿到预测结果后异步更新信号,再触发报单逻辑。这个设计还处于规划阶段,等真正落地之后我再写一篇单独的文章来分享。

对于已经熟悉vnPy的朋友,我希望这篇文章能帮你们避开一些我踩过的坑,尤其是多账户管理和分布式回测这两个方向。在动手之前,先想清楚自己的瓶颈到底是什么,不要因为“别人都在用分布式”就盲目上马。回测慢,先评估是数据量问题还是参数组合数量问题,很多时候优化策略代码本身比上分布式更快。多账户也是同理,先把账户映射和风控规则理清楚,再上系统,这套架构才能稳定运行。

最后再说一个我在实际使用中的体会:量化系统的复杂度不会因为策略逻辑好而自动降低,反而策略越好的系统越需要架构支撑。花时间把底层做扎实,让每个账户、每个策略、每笔订单都可审计、可追溯、可回滚,这才是长期跑下去的基础。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询