发布于VeighNa社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2026-08-06
VeighNa Fusion 已于 2026 年一季度发布。过去几个月,我们围绕它写了不少 CTA 量化文章,介绍过策略逻辑、连续合约和 AI 辅助投研,却一直欠大家一篇完整的产品介绍。
这篇文章想回答三个最直接的问题:为什么要做 Fusion,Fusion 是什么,以及它能为个人量化投资者减少哪些前期准备。
过去,许多个人投资者会直接使用 VeighNa 开源版连接 CTP 柜台。用户自行配置 Python 环境、安装组件并维护策略,就能搭起一套属于自己的量化交易系统。
随着期货市场程序化交易新规落地,个人使用自建系统时,需要完成的前期准备明显增加。除了编写策略,用户还要准备接入测试、沟通办理流程,并自行解决数据和运行环境问题。对于预算和技术力量有限的个人投资者,这些前期工作可能比写出第一条交易逻辑更难。
过去,接入柜台主要是技术准备;现在,自建系统还要完成接入测试。
新规出台后不久,有期货公司找到我们,希望提供一套由期货公司统一采购、面向客户使用的标准化量化平台。
我们在社区答疑中反复遇到三类问题:系统如何接入,回测数据如何获取,以及如何把想法写成可以检查、回测和迭代的策略代码。
因此,我们基于 VeighNa 已有的 CTA 策略体系开发了 VeighNa Fusion。它保留了大家熟悉的策略开发、历史回测和自动交易功能,同时把运行环境、数据中心与智策投研整合到同一个桌面终端中。
VeighNa Fusion 是韦纳软件面向期货公司推出的标准化期货量化交易平台,由合作期货公司采购后提供给其客户使用。
策略这块,Fusion 还是沿用标准 CtaTemplate 策略体系。已经使用 VeighNa 开源版开发常规 CTA 策略的用户,可以继续沿用原有的代码结构和开发经验。
Fusion 会帮你管好运行环境和版本。装好后可以直接开始写策略,把时间更多花在策略逻辑和风险控制上。

下面依次介绍接入、数据和策略实现这三类前期门槛。
希望使用 Fusion 的客户需要在合作期货公司开通期货账户,并申请相关权限,权限开通后即可使用。
Fusion 平台由期货公司统一提供,客户可以省去单独准备系统接入测试的工作,减少重复的技术准备和沟通。
对客户来说,使用 Fusion 软件本身没有额外收费。期货交易手续费以期货公司业务规则为准。使用智策相关功能时,需自行准备大模型厂商的 API Key,并支付相应的 token 费用。
CTA 策略回测离不开历史行情,而数据的获取、清洗、入库和持续更新,常常是初学者遇到的第二道门槛。
作为期货公司统一采购的标准平台,Fusion 内置了 数据中心 模块,目前提供 2020 年至今的国内期货 1 分钟历史行情。用户可以按交易所和品种选择合约,将分月数据下载到本地数据库。
分月数据在每个交易日收盘后更新,用户可在数据中心执行增量同步。同步分月合约数据后,还需要重新构建连续序列,连续合约才会包含刚同步交易日的数据。
数据中心可以在本地重建三类连续序列:
90:指数连续,按各合约持仓量加权计算;91:前复权主力连续,保留当前真实价格,CTA 策略回测通常优先选用;92:后复权主力连续,保留较早的历史价格,更适合观察长期路径和区间对比。
下载分月数据和重建连续序列是两个步骤。连续合约的构造公式以及 91 和 92 的选择方法,可以看看《CTA 量化通关》里讲连续合约的那一篇。
有了环境和数据,接下来才进入策略开发。对于编程经验有限的用户,从一句交易想法走到可运行代码,中间还隔着规则补全、代码实现、回测配置和参数优化等多步流程。
Fusion 集成的智策Agent会把这段工作组织成四个阶段:
在智策工作台中,这四个阶段还会再拆成八步:生成逻辑、确认逻辑、生成代码、代码检查、执行回测、优化规划、执行优化和选择参数。
智策Agent整理完开平仓、止损和参数规则后会暂停,等待用户检查和修改,确认后才继续生成代码。
在《CTA 量化通关》的策略逻辑示例中,起初的策略想法只写了“1.5 倍 ATR 移动止损”,止损价的更新方向需要在确认逻辑时补充完整,随后才能生成代码。
代码检查覆盖格式、语法、代码规范和类型问题。用户负责检查策略逻辑中的指标计算和条件判断,并评估实盘风险。手续费、滑点等回测假设由用户创建任务时配置,回测结果好不好用,还得看数据、参数和策略本身。
参数优化完成后,智策Agent会比较结果,优先考虑相对稳健的参数区域并给出理由,最终参数会结合策略逻辑和风险要求选择。
智策Agent能帮你省下不少重复劳动。生成的策略仍是标准 CtaTemplate 结构,方便你继续阅读和修改;不过策略逻辑、风险控制和实盘前测试,还是得自己把关。
如果你已经有一定期货交易经验,想尝试 CTA 量化,并希望控制前期接入、数据和开发上的成本与精力,Fusion 可以把原本分散的交易、数据管理和策略投研工具集中起来,编程经验有限的用户也可以快速上手。
对个人投资者来说,VeighNa 现在大致有三条路:
大家可以根据自己的实际情况,选择合适的路线。
如果想使用 VeighNa Fusion,可以扫描下方二维码添加 Fusion 小助手,了解当前的合作期货公司,以及权限开通的具体办理方式,完成这些步骤后,就可以开始使用了。

后续我们还会继续在《CTA 量化通关》系列里,围绕策略逻辑怎么写清楚、连续合约怎么选、回测结果怎么看、实盘前要注意什么,一件一件往下拆,帮大家把 Fusion 用得更扎实一些。
发布于VeighNa社区公众号【vnpy-community】
原文作者:Fusion小助手 | 发布时间:2026-07-31
上一篇我们把交易想法整理成了策略逻辑说明书。规则清楚之后,下一步是拿历史行情做检验。股票有一条相对完整的价格历史;期货则不同——每一张合约都会到期退市。
要回测某个品种过去三年,通常不能只用某一个交割月。研究者会把不同月份的合约,按规则接成一条更长的序列,行业里常叫它「主力连续」。它方便研究,却容易让人产生错觉:以为屏幕上那根 K 线,就是市场上某一张真实挂牌的合约。
你回测用的那根「主力连续」K 线,在真实市场上从来没有存在过。
本篇只讲清这件事:为什么要连续合约、指数 / 前复权 / 后复权各自适合什么场景、为什么不同软件的数据常对不上。
主力合约,指同一品种里市场参与最集中、通常也更适合作为研究与交易参照的那张合约。持仓量是未平仓合约的数量,常用来衡量资金集中在哪一张合约上。随着交割临近,持仓与成交会迁到更远的月份,研究用的「当前主力」也要跟着换——这一步叫换月。
问题在于:旧主力与新主力在交接前后,价格往往不相等。若把两段行情直接首尾相接,换月处会出现一根「跳空」。这根缺口来自合约切换,不一定来自品种本身的供需冲击。

对趋势类规则来说,这种假跳空特别麻烦。突破跌破、均线穿越,本就对相邻两根 K 线的相对位置敏感;换月缺口一旦被当成真波动,信号可能在「不该下单的日子」被触发。若回测引擎再按跳空幅度记账,权益曲线还会多出一笔现实里对不上的盈亏。
我们在社区答疑里遇到过这类困惑:回测曲线在某个交易日突然多出一块,对着分月合约检查下来,那天并没有对应的真实波动,只是连续序列在换月处没处理好。
所以连续合约要同时回答两件事:月份如何拼出足够长的样本,以及换月缺口如何处理、处理后的价格还能不能用来发信号。
常见的连续序列可以分为三类:指数连续、前复权主力连续和后复权主力连续。
初学时不必急着记住这些名称。三类序列其实回答两个不同的问题:指数连续回答「怎样概括整个品种的价格走势」;前复权和后复权回答「换月产生价差后,保留今天的价格,还是保留过去的价格」。
指数连续不是依次拼接某一张主力合约,而是把同一品种的多个可交易合约,通常按照持仓量加权,合成一条价格序列。
它反映的是整个品种的大致价格水位,通常比较平滑,适合观察长期趋势和进行宏观研究。但这个价格是计算出来的,可能不等于任何一张实际合约的价格,因此不能直接把它当成可以挂单成交的价格。
可以把它理解为:
指数连续回答「这个品种整体在涨还是在跌」,但不回答「今天应该按什么价格下单」。
前复权主力连续始终跟踪主力合约。换月时,它保留新主力当前的真实价格,同时平移换月前的历史价格,把合约切换产生的假跳空消除(这种用价差做整体平移的做法,常称 Panama 加法复权)。
例如,旧主力换月前为 3500 点,新主力为 3600 点,两者相差 100 点。直接拼接会出现一个 100 点的跳空。前复权会把换月前的历史价格统一加上 100 点,使换月前后的走势衔接起来,而今天的 3600 点仍然对应真实可交易的主力合约。
因此,前复权适合需要让历史信号与当前盘面价格保持一致的场景。CTA 策略回测多数会优先使用这一类序列。
后复权主力连续同样跟踪主力合约,但选择保留较早的历史价格,把换月后的较新价格向历史价格平移。
仍以上面的换月为例,旧主力为 3500 点,新主力为 3600 点。后复权会保留旧主力的 3500 点,并把换月后的价格减去 100 点,使两段走势连续。
这种序列更适合观察长期路径、比较长区间的净值变化。但它最新端的价格经过了调整,未必等于当前主力合约的真实盘面价格,因此不适合直接拿来判断今天应该在哪个价位下单。
三者的区别可以归纳为:

如果需要进一步理解它们的数学关系,记 (t) 为当前观察日,(t_k) 为第 (k) 次换月的生效日,换月价差定义为:

新主力价格旧主力价格
各家采用开盘价还是收盘价计算,规则可能不同。
前复权:

后复权:

也就是说,前复权把观察日之后才发生的换月价差加到历史价格上;后复权则从较新价格中减去观察日当时已经发生的换月价差。
公式并不是重点。实际使用时,只需先记住:
前复权还有两个需要注意的特点。
第一,加法复权会让历次换月价差在远端历史中不断累积。如果价差长期同向,早期价格会被持续抬高或压低;当累计调整为负时,极端情况下可能逼近零,甚至变成负数。使用依赖绝对价格水平的指标时,需要特别留意。
第二,前复权历史并不是永久固定的。如果重新构建数据时,换月日期或换月价差发生变化,整段历史价格也会随之改变。因此,上周导出的前复权序列与本周重新构建的序列,同一天的收盘价可能不同。这是复权规则带来的正常结果,并不意味着数据库出了问题。
做 CTA 策略回测时,多数情况下会选择前复权主力连续,因为它回答的是:
按照今天的价格口径,历史上的策略信号是否应该触发?
选错序列,后面所有回测结论都会建立在错误的价格口径上。
「谁算主力」本身就是一条规则。Fusion 数据中心采用双轨口径(与常见行业实践一致):
把倍数、生效时点、是否要求成交量同步最大写进规则之后,同一天「谁是主力」才会有唯一答案。规则改一点,换月日就可能错开,连续序列上的缺口位置与复权幅度也会跟着变。
因此有一条比「哪家数据更准」更管用的判断:
主力判定、换月时点和缺口处理规则不同,不同软件的主力连续自然可能对不上——看到不一致,先查两边的构造规则。
不能只凭价格不同就判断哪一方的数据有误。比较连续序列之前,首先要确认两边是否属于同一类型;如果都是主力连续,再核对主力判定、换月时点和复权规则。
前面把规则和序列类型讲清楚之后,下一步就是在数据中心里操作:先下载分月合约入库,再重建成本地连续序列。VeighNa Fusion 用固定后缀区分上述三类序列——这是平台内约定,不是交易所官方代码:

连续序列的代码也是平台内部约定。以螺纹钢为例,Fusion 的指数连续使用 rb90.SHFE,米筐 RQData 使用 RB99;迅投研则将同类序列称为「加权合约」,代码为 rbJQ00.SF,本文将其归入指数连续一类。
三者都通过多个可交易合约反映品种整体价格,但纳入的合约范围、持仓量权重及计算时点可能不同,因此价格不一定完全一致。
本系列后文回测演示,会与螺纹钢同一品种对齐,策略侧常用前复权序列 rb91.SHFE。
第一步,在数据中心选择交易所、品种和日期范围,点击「下载入库」。任务完成后,分月合约数据会写入本地数据库。

下载螺纹钢分月合约
第二步,分月数据入库后,点击「重建连续」。系统会识别主力换月日期,并按照对应规则生成三类连续序列。

重建螺纹钢连续序列
第三步,重建完成后,在左侧合约列表中检查是否已经生成 rb90、rb91 和 rb92。

检查生成的 rb90、rb91 和 rb92
有一条容易漏的操作纪律:追加下载了更新的分月数据之后,需要手动触发重建,连续序列才会按最新合约与换月结果重算。只下载、不重建,回测仍可能跑在旧的连续结果上——分月库已经是新的,91 却还是上一次拼接的产物。
下载与重建拆开,是为了让你看清「原始分月」和「研究用连续」是两层数据。连续序列出问题,优先回到分月合约和换月记录排查,别急着先改策略参数。
90 / 91 / 92 区分三类序列,更新分月数据后记得重建。
数据就绪之后,可以把上一篇的策略逻辑说明书交给 AI 生成代码。实现门槛变低了,读懂代码、核对翻译是否忠实,反而成了更关键的一步。
下一篇:不会写代码,也要会读策略。
VeighNa Fusion 是韦纳软件面向期货公司推出的标准化期货量化交易平台,本系列文中演示均基于 Fusion 内置功能完成,如希望了解详情,请扫描下方二维码添加小助手交流:
作为初学者,面对 vnpy 无所不包、博大精深的丰富内容,试图用图形对 vnpy 的运行流程做一个归纳。
不到之处,还请各位多多指正

发布于VeighNa社区公众号【vnpy-community】
原文作者:Fusion小助手 | 发布时间:2026-07-29
上一篇我们说到,做 CTA 量化的第一道坎,是把交易想法翻译成机器能执行的规则。不少人其实已经有一条口头纪律,听起来也够具体——「均线金叉就做多」。
主观交易时,这句话背后往往还藏着一串默认前提:用哪根周期的均线、金叉当下进还是等收盘、进几手、什么条件离场。盘中靠经验,这些空白会被随手补上。程序不会补。把同样一句话交给机器,它未必当场报错,却可能用你从未同意过的默认假设继续往下跑。
AI 时代交易员的核心工作不是写代码,而是确认逻辑。
本篇只做一件事:给出一套五要素框架,并用一个完整案例,演示如何把大白话整理成一份可执行、可核对的策略逻辑说明书。代码怎么写、这份策略能不能赚钱、布林带和 Aroon 的公式细节,分别留给后面的文章。
上一篇留下的那串问题——周期、确认时机、手数、离场、加仓、方向不明——归纳成五类。缺任何一类,机器仍然无法稳定执行。

信号周期回答的是:用什么级别的 K 线做判断。交易所公开推送的行情里,1 分钟 K 线往往是研究者能稳定拿到的更细行情;策略若按 15 分钟决策,常见做法是用 1 分钟合成 15 分钟,再在合成后的序列上算指标、出信号。
这里容易混的一点是:合成周期描述的是「决策频率」,跟数据有多细不是一回事。同一套进出场逻辑,放在 5 分钟和 15 分钟上,交易次数和持仓时间会差不少。先定多久做一次判断,再选合成方式,比先随口定一个周期再倒推要清楚。
入场条件最好拆成两层:一层管过滤,一层管触发。过滤回答「只做多、只做空,还是都可以」;触发回答「什么价格动作才实际下单」。把两层混成一句「看起来该买了」,回测和实盘都很难核对。趋势过滤器加突破触发,是 CTA 里很常见的组合——过滤器负责少做逆势单,触发条件负责给出具体下单的时点。
出场条件至少要有一条离场路径:固定止损、移动止损、止盈、反向信号平仓,都可以,但不能没有。只写进场、不写出场,规则只完成了一半。若同时有多条离场路径,还要写清谁优先,避免程序里两条规则抢着平仓。
仓位规则在入门阶段建议先固定手数。固定 1 手看起来笨,却能把「逻辑对不对」和「仓位大不大」分开。变量少了,后面回测和核对才有参照。等逻辑稳定了,再讨论按波动或资金比例加减仓也不迟。
边界情况专门写「没有信号时怎么办」。方向不明、条件未触发、已有持仓又看到重复信号——这些在主观交易里常被略过,程序却必须有答案。「不交易」「不加仓」本身就是规则,写进说明书,才算完整。
我们在社区答疑里见过一种情况:口头规则只有进场,出场全靠「到时候看」。丢给 AI 也能生成能跑的代码,但离场逻辑往往是生成器自己补的默认值,和交易者心里想的并不是一回事。五要素的作用,就是在生成代码之前,先把这些空白落到纸面上。
下面用本系列的固定案例走一遍。后文讲读代码、审查、回测、参数时,都会回到同一份说明书,中途不更换。
先看交易者口头版:
用 1 分钟 K 线合成 15 分钟;布林带周期 20、宽度 1.7;Aroon 周期 20 判断多空——上升趋势强就等突破布林上轨做多,下降趋势强就等跌破下轨做空,方向不明就不做;固定 1 手;持仓后用 1.5 倍 ATR(周期 14)移动止损。
三个指标在本案例里的分工可以记成一句话:布林带用价格相对均值的偏离画通道,突破上下轨作为触发;Aroon 比较近期最高价、最低价出现的先后,用来判断趋势方向;ATR 衡量近期波动幅度,用来设定止损距离。
我们第一次把这段话按五要素逐项填空时,卡在「趋势强」三个字上——到底强到什么程度?说明书里不能停在语感,必须写成可以比较的条件。下面按五要素翻译成一版完整说明书。
【信号周期】
【入场条件】
【出场条件】
【仓位规则】
【边界情况】
对照口头版可以看出,大白话里没写、说明书里必须写清楚的,至少有这几处:在 15 分钟收盘后判断、Aroon 用 Up/Down 相对高低定义「趋势强」、突破看收盘价、移动止损只朝有利方向更新、持仓期间不加仓也不对锁。
你也可以拿自己的策略试一次同样的动作:先原样写下口头版,再按五要素逐项填空。填不满的格子,往往就是盘中靠经验临场发挥的地方;那些地方不补上,很难真正程序化。
这份说明书有两个用途。对机器,它是执行规格;对人,它是后面读代码、做审查、看回测时的核对底稿。第 4 篇起我们会拿着它逐项问:代码里的翻译是否忠实。
把上面的口头想法交给 AI,通常也能得到一份结构化逻辑草稿。能够生成只是起点;生成之后有没有人把关,才决定这份草稿能不能用。
在 VeighNa Fusion 的智策工作台里,打开智策投研,可以用同一段大白话作为任务输入。系统会先走「生成逻辑」——产出的结构,应能和上一节五要素说明书互相对照。

智策投研输入大白话想法

生成逻辑产物与说明书对照
更关键的是下一步:流程会停在等待你确认,而不会自动写代码、自动回测。

等待你确认

这一停,对应的正是本篇的钩子:整理和起草可以借助工具,交易规则有没有漏项、和你想的是否一致,确认权在人。若生成稿里「趋势强」仍是模糊说法,或出场只写了止损却没写更新方式,应在确认环节改成可以执行的表述,再进入后续步骤。
规则写清楚以后,下一步是拿历史行情做检验。但期货合约会到期退市,回测常用的「主力连续」并不是市场上真实交易过的那张合约——构造方式不同,结论就可能不同。
下一篇:连续合约没搞对,回测结论靠不住。
VeighNa Fusion 是韦纳软件面向期货公司推出的标准化期货量化交易平台,本系列文中演示均基于 Fusion 内置功能完成,如希望了解详情,请扫描下方二维码添加小助手交流:
发布于VeighNa社区公众号【vnpy-community】
原文作者:Fusion小助手 | 发布时间:2026-07-28
夜盘进行到一半,你起身离开了几分钟。回来时,原本等着的突破已经走完。也有反过来的时候:止损条件明明已经触发,你却对着屏幕想“再等等”。一边是错过该做的,一边是拖延该走的——难点都在:执行依赖人,也容易被人改掉。
这两种经历都会引出同一个念头:能不能把交易条件提前写清楚,让程序去盯盘和执行?
带着这个念头往下走,很多人会接触到CTA量化。CTA量化策略在国内通常指这样一类策略:主要研究对象是各个交易品种自身的价格时间序列,趋势跟踪是其中非常典型的一种。随着研究方法演进,如今的CTA量化早已不止简单的规则组合,也会用到各类时序因子和机器学习算法。但对刚上手的人来说,先把输入、判断和执行条件讲清楚仍然是非常合适的入口,所以本系列从规则化方法讲起。
不过,把规则写清楚只是第一步。从“有个想法”走到“程序在实盘里跑起来”,中间还隔着几道具体的坎。我们在社区答疑中见到的问题五花八门,归拢下来却大多落在同样五道坎上:规则、数据、代码、验证和实盘。
本篇只负责把这张地图画清楚——每道坎是什么,难在哪里。连续合约怎么合成、代码怎么审查、回测指标怎么读、实盘流程怎么走,留到后面的文章逐项展开。
做量化的第一道坎不是编程,而是把“我感觉能赚钱”翻译成机器能执行的规则。

“均线金叉就做多”听起来已经很像一条规则,但机器仍然无法直接执行。
均线用几分钟的K线计算?金叉出现时立即进场,还是等收盘确认?一次进多少手?满足什么条件离场?信号连续出现时要不要加仓?方向不明确时又该怎么办?
主观交易者会在盘中用经验补全这些空白,程序不会。每一个分支都要提前给出明确答案,连“没有信号就不交易”也要写进规则。
所以策略规则化的关键,是找出交易想法里隐藏的前提,而不只是把一句话换成几行代码。本系列第2篇文章会给出一套五要素框架,把一段大白话整理成可以执行、也可以核对的“策略逻辑说明书”。
规则写好以后,需要放到历史行情中检验。而期货和股票不同:合约会到期退市,要回测一个品种过去三年的表现,通常不能只用某一个交割月份的合约。
研究者需要按照一定规则,把不同月份的合约拼接成连续序列。主力合约什么时候切换、新旧合约之间的价差怎么处理,这些选择都会影响最终得到的K线。
直接拼接还可能在换月处留下虚假跳空。程序如果把这段跳空当作真实行情,就可能生成现实中并不存在的交易信号和盈亏。
这也解释了为什么不同软件的“主力连续”数据有时对不上:差异往往来自构造规则的不同,不能只凭价格不一样就断定哪一方的数据有误。本系列第3篇文章会讲清指数、前复权和后复权连续序列各自适合什么场景。
过去,交易者往往要先学习Python、配置开发环境,再熟悉量化框架,才能写出第一份策略。现在,AI辅助策略投研开发已经能根据逻辑说明生成代码初稿,这道坎正在显著变矮。
但实现变得容易,不等于确认的责任也一起交了出去。代码能够运行,只说明它通过了基本的语法和接口要求,并不能证明它忠实实现了原始想法。
例如,用户要求用1.5倍ATR移动止损,AI在代码里却可能写成固定止损。两种写法都能跑完回测,交易行为却已经不是一回事。
想要把这类偏差找出来,不一定必须先学会从零写策略。阅读一份结构固定的CTA策略代码,通常比从零编写要容易得多。本系列第4篇文章会介绍参数、K线合成、指标计算、信号判断和持仓管理这五个固定部件,第5篇继续讨论AI生成代码的审查方法。
策略跑得通、回测曲线向上,仍不足以说明它可以投入实盘。
回测用的是历史数据和简化的成交模型。有三种情况会让报告显得过于乐观:代码引用了决策时还拿不到的信息(未来函数);手续费和滑点设得过低(成本低估);参数被反复调整到恰好贴合某段历史(过度拟合)。
以其中的滑点为例:它指下单时的预期价格与实际成交价格之间的偏差。对交易频率较高的策略来说,即使每次只差一个最小变动价位,累积起来也可能改变回测结论。
在我们看来,验证是整条路线中极为需要耐心的一段。明显的程序错误通常会直接让策略跑不起来,而验证方法有缺陷却可能留下一份完整报告,让人带着错误的信心继续往前走。
因此,本系列第5至第7篇文章会依次讨论怎么审查代码、怎么读回测报告,以及怎么识别参数优化中的过拟合。
策略验证通过后,离真实账户交易仍有一段路:程序化交易权限要单独申请;用外部软件接入时,往往还要先完成系统合规测试,流程更长。接入方式不同,准备工作也不同——回测跑通,不等于可以立刻实盘。
本系列第8篇文章会依据当前期货行业的监管规定,把这条路上的常见步骤与路径差异讲清楚。
地图有了,还要选择用什么工具。常见做法可以归纳为两类:自行安装开源框架并维护环境,或者使用期货公司提供的标准平台。

这两条路径面向的是不同类型用户的需求:VeighNa 开源版适合希望自行配置环境、扩展功能的开发者;VeighNa Fusion适合希望把精力放在量化投研与交易本身的交易者。
两个版本都采用标准CtaTemplate策略模板,策略结构和开发经验可以延续,区别主要在于环境、数据和运行依赖由谁维护。
本系列后续会用Fusion展示实际操作,但文章中的规则化、数据处理和验证方法与具体工具无关。即使选择其他量化平台,也可以沿着同一张地图开展工作。
下面两张图分别是Fusion主界面,以及内置CTA Agent的工作流全景——后文演示会沿着这条工作流展开。


五道坎的顺序,也是本系列计划的连载顺序:

下一篇,我们从第一道坎动手:用一个具体案例,演示怎么把一段交易想法整理成机器能执行、人能逐项核对的策略逻辑说明书。
VeighNa Fusion 是韦纳软件面向期货公司推出的标准化期货量化交易平台,本系列文中演示均基于 Fusion 内置功能完成,如希望了解详情,请扫描下方二维码添加小助手交流:
安装流程:





打开terminal-注意环境切换成 py37_vnpy,执行以下命令,安装需要的插件
pip install -r requirements.txt -i http://pypi.douban.com/simple --trusted-host pypi.douban.com

遇到安装失败的可以单独安装:
pip install PyQt5 -i http://pypi.douban.com/simple --trusted-host pypi.douban.com



备注:
如果需要在cmd 下使用 py37_vnpy 环境。
打开 CMD
运行: conda activate py37_vnpy
会切换到py37_vnpy环境下

经过好几天的反复,终于完成了。所谓的复盘,就是盘后把行情从新播放一遍,如果使用tick数据,就和真实的盘面一模一样,我这里使用的是1分钟数据复盘,所以简化了很多。
代码如下:
import multiprocessing
import time
from datetime import datetime
from vnpy.trader.constant import Exchange, Interval
from vnpy.trader.database import database_manager
from vnpy.chart import ChartWidget, VolumeItem, CandleItem
from vnpy.trader.ui import create_qapp, QtCore
from vnpy.trader.object import BarData
import os
bar: BarData
def putbardata(q_1m,q_5m,q_30m,q_4h,su):
#从数据库中读取1分钟数据,你的数据库必须有下载好的数据。
bars = database_manager.load_bar_data(
symbol="APEUSDT",
exchange=Exchange.BINANCE,
interval=Interval.MINUTE,
start=datetime(2022, 5, 4),
end=datetime(2025, 1, 1)
)
sudu = 0.055
i = 0
for bar in bars:
q_1m.put(bar)
q_5m.put(bar)
q_30m.put(bar)
q_4h.put(bar)
if i > 1200: #先快速播放一定数量的一分钟bar
if not su.empty():
sudu = int(su.get(True))
print("速度已经设定为:", sudu)
if i % 10 == 1 :
os.system("pause") #正常播放以后,每10个一分钟bar暂停一下,按任意键继续,不需要这个功能的可以删掉。
time.sleep(sudu)
i = i + 1
def MINUTE_5m(q):
app = create_qapp()
widget = ChartWidget()
widget.add_plot("candle", hide_x_axis=True)
widget.add_plot("volume", maximum_height=180)
widget.add_item(CandleItem, "candle", "candle")
widget.add_item(VolumeItem, "volume", "volume")
widget.add_cursor()
history : BarData
history = []
global i_5
i_5 = 0
global bar_
def update_bar():
global i_5
global bar_
if not q.empty():
bar = q.get(True)
if i_5 == 0 :
bar_ = bar
i_5 = 1
history.append(bar_)
if i_5 == 5 :
bar_ = bar
i_5 = 1
history.append(bar_)
else :
bar_.close_price = bar.close_price
if bar.high_price > bar_.high_price:
bar_.high_price = bar.high_price
if bar.low_price < bar_.low_price:
bar_.low_price = bar.low_price
bar_.volume = bar_.volume + bar.volume
i_5 = i_5 + 1
history[-1] = bar_ #这一段是把一分钟数据形成5分钟数据
widget.clear_all()
widget.update_history(history) #刷新图形数据
timer = QtCore.QTimer()
timer.timeout.connect(update_bar)
timer.start(50)
widget.setWindowTitle("五分钟") #设定五分钟窗口的标题和窗口大小以及位置
widget.setGeometry(0, 0, 900, 550)
widget.show()
app.exec_()
def MINUTE_30m(q): #30分钟和5分钟类似
app = create_qapp()
widget = ChartWidget()
widget.add_plot("candle", hide_x_axis=True)
widget.add_plot("volume", maximum_height=180)
widget.add_item(CandleItem, "candle", "candle")
widget.add_item(VolumeItem, "volume", "volume")
widget.add_cursor()
history: BarData
history = []
global i_30
i_30 = 0
global bar_
def update_bar():
global i_30
global bar_
if not q.empty():
bar = q.get(True)
if i_30 == 0 :
bar_ = bar
i_30 = 1
history.append(bar_)
if i_30 == 30 :
bar_ = bar
i_30 = 1
history.append(bar_)
else :
bar_.close_price = bar.close_price
if bar.high_price > bar_.high_price:
bar_.high_price = bar.high_price
if bar.low_price < bar_.low_price:
bar_.low_price = bar.low_price
bar_.volume = bar_.volume + bar.volume
i_30 = i_30 + 1
history[-1] = bar_
widget.clear_all()
widget.update_history(history)
timer = QtCore.QTimer()
timer.timeout.connect(update_bar)
timer.start(50)
widget.setWindowTitle("三十分钟")
widget.setGeometry(0, 560, 900, 550)
widget.show()
app.exec_()
def MINUTE_4h(q):
app = create_qapp()
widget = ChartWidget()
widget.add_plot("candle", hide_x_axis=True)
widget.add_plot("volume", maximum_height=180)
widget.add_item(CandleItem, "candle", "candle")
widget.add_item(VolumeItem, "volume", "volume")
widget.add_cursor()
history: BarData
history = []
global i_4h
i_4h = 0
global bar_
def update_bar():
global i_4h
global bar_
if not q.empty():
bar = q.get(True)
if i_4h == 0 :
bar_ = bar
i_4h = 1
history.append(bar_)
if i_4h == 240 :
bar_ = bar
i_4h = 1
history.append(bar_)
else :
bar_.close_price = bar.close_price
if bar.high_price > bar_.high_price:
bar_.high_price = bar.high_price
if bar.low_price < bar_.low_price:
bar_.low_price = bar.low_price
bar_.volume = bar_.volume + bar.volume
i_4h = i_4h + 1
history[-1] = bar_
widget.clear_all()
widget.update_history(history)
timer = QtCore.QTimer()
timer.timeout.connect(update_bar)
timer.start(50)
widget.setWindowTitle("四小时")
widget.setGeometry(860, 560, 1050, 530)
widget.show()
app.exec_()
def MINUTE(q): #一分钟的是最简单的,直接使用就好。
app = create_qapp()
widget = ChartWidget()
widget.add_plot("candle", hide_x_axis=True)
widget.add_plot("volume", maximum_height=180)
widget.add_item(CandleItem, "candle", "candle")
widget.add_item(VolumeItem, "volume", "volume")
widget.add_cursor()
def update_bar():
if not q.empty():
bar = q.get(True)
widget.update_bar(bar)
timer = QtCore.QTimer()
timer.timeout.connect(update_bar)
timer.start(50)
widget.setWindowTitle("一分钟")
widget.setGeometry(860, 15, 1050, 550)
widget.show()
app.exec_()
if __name__ == '__main__':
manager = multiprocessing.Manager()
q_1m = manager.Queue()
q_5m = manager.Queue()
q_30m = manager.Queue()
q_4h = manager.Queue()
su = manager.Queue()
pw = multiprocessing.Process(target=putbardata, args=(q_1m,q_5m,q_30m,q_4h,su))
pr_1m = multiprocessing.Process(target=MINUTE, args=(q_1m,))
pr_5m = multiprocessing.Process(target=MINUTE_5m, args=(q_5m,))
pr_30m = multiprocessing.Process(target=MINUTE_30m, args=(q_30m,))
pr_4h = multiprocessing.Process(target=MINUTE_4h, args=(q_4h,))
pw.start()
pr_1m.start()
pr_5m.start()
pr_30m.start()
pr_4h.start()
sudu = input("请输入速度:")
su.put(sudu)
time.sleep(1000000)
print('任务完成')
大概说一下原理,程序设定了5个进程,通过通道交换数据,其中一个进程发送数据,另外4个进程接受数据,接受数据的四个进程就是4个周期的窗口,把接受的一分钟数据变化成3分钟30分钟等,并用图形展示出来。
只要控制发送数据的节奏,就可以动态的把行情从新演示一遍了。
这是盘后复盘用的,可以回忆一下当天到底发生了什么。
国内期货有一个盘立方软件是可以完美复盘的,数字货币没有这个东西,tradingview有这个功能,但是每月要收费90元,而且tradingview也只能使用1分钟数据复盘。
身为程序员,当然不愿意掏钱,因为自己可以写一个。
感谢vnpy提供的ChartWidget,真的很好用。
发布于VeighNa社区公众号【vnpy-community】
原文作者:VeighNa小助手 | 发布时间:2026-06-15
【社区活动尊享卡】的受欢迎程度大幅超出我们的预期,为了保证每场社区活动的交流质量,尊享卡已经变更为仅对部分专业交易员用户定向提供。对于参加活动较多的同学强烈推荐!购买请扫描二维码添加小助手咨询:

过去一年里,VeighNa社区办过多场AI智能体相关的活动。已经有一些同学把这些工具用到了自己的量化投研工作里,比如辅助写策略、整理资料、跑一些重复性的流程,确实省下了不少时间。
不过也有很多同学反馈,自己虽然已经开始尝试大模型和Agent,但真正用起来还是不太顺。有时候是工具没选对,有时候是不清楚大模型到底是怎么和外部工具配合工作的,也有时候是不知道该从哪里开始,把它接到自己的量化流程里。
所以我们准备做一个【量化AI智能体学习】系列社区活动,尽量从基础讲起,一步一步带大家把这些东西真正用起来。
这个系列会尽量坚持几个原则:
目前计划先做5场:
第一场活动将于6月27日(周六)下午2:00至5:00在上海举办。普通报名仅支持线下参会,尊享卡报名可通过线上直播参与。活动具体地址将在微信群中公布,请在报名成功后扫码加入社区活动群,以便获取相关信息!
**时间:**6月27日 14:00-17:00
地点:上海(具体地址后续在微信群中通知)
**报名费:**99元(Elite会员免费参加)
**报名方式:**扫描下方二维码报名(报名后请扫码加入社区活动微信群获取参会地址)

最近在我工作小组内部做了一次关于风险和投资组合的基础知识分享, 主要使用了Jupyter Notebook。
把PPT和 Jupyter Notebook都传到我的Github 分享,有兴趣可以看看
https://github.com/BillyZhangGuoping/MarketDataAnaylzerbyDataFrame/tree/master/Risk%20and%20Portfolio%20Knowledge%20Sharing
主要内容标题:
这些内容在Jupyter Notebook里面都有反应。
经过一段时间学习和实践,感觉对于期货择时交易,跟随趋势基本上可以被认为是不二途径。那么对于CTA交易策略来说,及早发现趋势是非常重要。寻找趋势的方法很多,经典的比如均线快慢线交叉,布林带还有其他各种时序动量指标,这里推荐看看公众号刀疤连的一边大作‘和趋势做纯纯的朋友’,有非常全面分析。
在实践中,发现经常陷入一个两难局面,一方面是希望及时发现趋势,这样不可避免就是的判定阈值变得敏感;往往就是敏感的指标被市场白噪声和某些蓄意大宝剑图形(针对趋势交易者的迅速拉升然后砸下来图形)触发,被来回打脸,连续亏损。另一个方面为了避免被来回打脸,调高触发指标,又会导致错失趋势。
之前一直尝试用流水线质检的方法去寻找趋势策略, 和分析时序序列有点相似。质检时候,要及时发现产出是否有生产线问题,如果晚了,大量残次品出来;如果太敏感,把个体随机问题认为是生产问题,又会因为生产线停机检修造成损失。之前用的CUSUM就是这个方法。
最近有一个思路,尝试用T 检验这个方法去发现趋势。关于T 检验现在也广泛用在质量检测,药效测试。这里面还有个典故,T检验是戈斯特为了观测酿酒质量而发明的。戈斯特在位于都柏林的健力士酿酒厂担任统计学家。戈斯特于1908年在Biometrika上公布T检验,但因其老板认为其为商业机密而被迫使用笔名(学生),所以有时候也叫学生检验。
这里不准备讲解具体检验数学逻辑,具体可以搜索之,这里只是说说应用;因为也是边学边用,有错漏这出。
T检验有三种主要类型:
1.独立样本t检验:比较两组独立样本平均值的方法,这里要求两组样本 方差相等,即具有方差齐性。
2.配对样本t检验:比较同一组中不同时间(例如,相隔一年)平均值的方法。
3.单一样本t检验:检验单个组的平均值对照一个已知的平均值。
python的库stats已经提供T检验,就是stats.ttest_ind/ stats.ttest_rel/ stats.test_1samp 方法, 示例代码如下:
import numpy as np
import numpy.matlib
a1 = np.random.normal(0,0.1,30) #均值为0,标准差为0.1的30个队列
b1 = np.random.normal(1,0.2,30) #均值为1,标准差为0.2的30个队列
t2, p2= stats.ttest_rel(a1, b1) #配对T检验,a1比b1,如果t值为负,p值小于0.05,说明均值a1小于b1,a1和b1同一个队列假设不成立
print("t2 = " + str(t2))
print("p2 = " + str(p2))
t4, p4= stats.ttest_1samp(a1, 0) #样本T检验,a1对比0,如果t只在1,-1之间,说明a1均值为0,假设成立
print("t4 = " + str(t4))
print("p4 = " + str(p4))
t5, p5= stats.ttest_1samp(b1, 0) #样本T检验,b1对比0,如果t值不在1,-1之间,说明b1均值为0,假设不成立
print("t5 = " + str(t5))
print("p5 = " + str(p5))
这里生成两个正态分布队列,方法很简单,返回T值,和P值;T值越接近0,则可认为假设可成立,如果T值离0值越远,比如[1,-1]之外,则假设不成立,正均值想上,小于0均值向下。
这里还有个前提,就是样本是来自正态分布总体的随机样本。而我们日常所用的期货价格分布并不是正态分布,就像CUSUM里面用的,这里使用时段(5分钟)的对数收益率,显示为近似正态分布,均值近似为0。可以考虑是否去极值,就是计算出标准差,然后将其中大于 u+3σ 的置换为u+3σ,将小于u-3σ 的置换为u-3σ,消除跳开的冲击。
下图为示意图,从close转为对数收益率,和对数收益率分布。

在CTA趋势发现时候, 这里使用主要是 配对样本t检验和单一样本t检验。
配对样本t检验:是按照时段进行对比,这里可以有相互部分覆盖的时段序列对比,比如 [t-30, t-10]和[t-20,t]对比,中间可以有10个时段相互覆盖。如果前后两个时间队列的T检验发现T值为正且大于1则,相对过往时段有趋势向上;反正向下。
单一样本t检验,用一段时间段和均值0对比,如果值在[-1.1]之间,可以认为其实没有趋势,如果大于1或者小于-1,则可以认为有向上或向下趋势。
下面简单代码示例, 是用5分钟对数收益率进行配对和样本T检测,如果连续3个配对样本的T值都大于1,或都小于-1,则去向上或向下。
dfrb5min['close']是一个close array,需要自己提前定义。
from scipy import stats
from scipy.stats import norm,t
import matplotlib.pyplot as plt
logdata = pd.DataFrame()
logdata['close'] = np.log(dfrb5min['close']) # 对数化传入5分钟close队列dfrb5min['close']
logdata['closeIndexDiff_1'] = logdata['close'].diff() # 1阶差分处理,求出对数收益率
t = 40
for i in range(t,len(logdata['close'])):
#求出配对T检测t4
t4, p4= stats.ttest_rel(logdata['closeIndexDiff_1'][i-20+1:i+1],logdata['closeIndexDiff_1'][i-30+1:i-10+1])
#求出样本T检测p0
t0, p0 = stats.ttest_1samp(logdata['closeIndexDiff_1'][i-15+1:i+1],0)
logdata.loc[i,'t'] = t4
logdata.loc[i,'t1samp'] = t0
#如果绝对值大于0,则使用
if abs(t0) > 1.0:
logdata.loc[i,'t0Index'] = t0
else:
logdata.loc[i,'t0Index'] = 0
if abs(t4) > 1.0:
logdata.loc[i,'tIndex'] = t4
else:
logdata.loc[i,'tIndex'] = 0
#csv输出
logdata.to_csv("C:\\Project\\MA8888va1min5v3.csv", index=True, header=True)
closeArray = np.array(dfrb5min['close'])
listup,listdown = [],[]
for i in range(1,len(logdata['closeO'])):
if logdata.loc[i-1,'tIndex'] >1 and logdata.loc[i,'tIndex']>1 and logdata.loc[i-1,'t0Index'] >1 and logdata.loc[i,'t0Index']>1 and logdata.loc[i-2,'tIndex']>1 and logdata.loc[i-2,'t0Index'] >1 :
listup.append(i)
elif (logdata.loc[i,'tIndex'] < -1 and logdata.loc[i-1,'tIndex'] <-1) and (logdata.loc[i,'t0Index'] < -1 and logdata.loc[i-1,'t0Index'] <-1) and logdata.loc[i-2,'tIndex']<-1 and logdata.loc[i-2,'t0Index'] <-1 :
listdown.append(i)
fig=plt.figure(figsize=(18,6))
plt.plot(closeArray, color='y', lw=2.)
plt.plot(closeArray, '^', markersize=5, color='r', label='UP signal', markevery=listup)
plt.plot(closeArray, 'v', markersize=5, color='g', label='DOWN signal', markevery=listdown)
plt.legend()
plt.show()
从示例图来看,有一定趋势发现能力,但是还是陷入短趋势困境,这个是时序队列长度选取,t值都有关系,还是要进一步优化。
您确定执行该操作?该操作不可撤销.
*Emphasized text*
**Bold Text**
*** (Horizontal rule)
> Blockquote
[Google](http://www.google.com)
[Google](http://www.google.com "Google")

[](http://google.com)
print "Hello World"
For a more detailed info visit the official Markdown website