发布于VeighNa社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2026-04-28
在上一篇里,我们重点说明了 AlphaLab 的目录结构,以及日频 K 线、指数成分和 contract.json 怎样准备齐全。
到这一步,研究所需的本地数据已经有了稳定来源:行情可以通过 load_bar_df 读成表格,指数成分也可以通过 load_component_filters 约束样本范围。
接下来要解决的问题是:怎样把这些原始行情数据,变成模型可以训练和预测的因子表?
对于已经会写 Python、也接触过一些因子研究的读者来说,这一步通常会关心:
因此,本篇进入 AlphaDataset\:它负责把多标的行情面板整理成因子、标签和样本区间,并在模型训练前完成必要的数据预处理。可以先将它理解为:**AlphaDataset 管“模型要学什么、从哪段数据学、用哪段数据检验”。**
在官方工作流中,通常会先从 AlphaLab 读取指数成分对应的日频行情,并得到一个 Polars DataFrame。这个表至少会包含:
datetime:交易日期vt_symbol:标的编码,例如 600000.SSEopen、high、low、close、volume、turnover、vwap 等行情字段随后将这个表传入 AlphaDataset,并同时指定训练、验证、测试三段时间:
from vnpy.alpha import AlphaDataset
dataset = AlphaDataset(
df=df,
train_period=("2008-01-01", "2018-12-31"),
valid_period=("2019-01-01", "2020-12-31"),
test_period=("2021-01-01", "2023-12-31"),
)
这里的三段时间由 Segment 统一表示。Segment.TRAIN 表示训练集,Segment.VALID 表示验证集,Segment.TEST 表示测试集。后续模型训练、预测和结果检查时,都会围绕这三个区间取数。
需要注意的是,AlphaDataset 并不会在构造函数里立刻把所有因子算完。构造时只是保存原始行情表、三段日期区间,以及后续要用到的因子表达式、标签表达式和预处理器。真正的计算会在后面的 prepare_data 和 process_data 中完成。
AlphaDataset 中最常用的两个入口是 add_feature 和 set_label。
因子可以理解为模型的输入特征。例如过去 5 日收益、均线偏离、成交量变化、截面排名等,都可以作为特征列加入数据集:
dataset.add_feature("roc_5", "ts_delay(close, 5) / close")
dataset.add_feature("ma_20", "ts_mean(close, 20) / close")
这里的字符串表达式会在 vnpy.alpha 提供的时序函数、截面函数和数学函数环境里执行。常见前缀可以按直觉理解:
ts_:时间序列方向的计算,例如滚动均值、延迟、相关系数cs_:同一交易日截面方向的计算,例如截面排名、截面标准化ta_:部分常用技术指标除了字符串表达式,add_feature 也可以接收 Polars 表达式,适合直接使用 Polars 原生写法的场景。
对于已经在外部算好的因子,也可以通过 result= 传入一个包含 datetime、vt_symbol 和因子值的结果表,prepare_data 时会按日期和标的合并。
标签则是模型要学习的目标,通常表示未来一段时间的收益或排序目标。例如内置 Alpha158 中的标签写法是:
dataset.set_label("ts_delay(close, -3) / ts_delay(close, -1) - 1")
它表达的是一个向未来看的收益目标。实际研究时,标签的定义要和后续持仓周期、调仓频率保持一致;如果标签和策略持有逻辑完全脱节,模型训练出来的预测值就很难直接转化为可解释的交易信号。
因子和标签定义好之后,下一步调用 prepare_data:
filters = lab.load_component_filters(index_symbol, start, end)
dataset.prepare_data(filters=filters, max_workers=6)
这一阶段主要完成三件事。
add_feature 中登记的字符串表达式或 Polars 表达式,会被批量计算成新的因子列;如果设置了 label,标签列也会在这一步生成。result= 传入的外部因子,会按 datetime 和 vt_symbol 左连接到结果表中。filters,则只保留某只股票在指数成分存续区间内的样本,避免把非成分期的数据混入研究池。可以把 prepare_data 理解为:把“怎么计算”落实成一张原始因子与标签表。
完成后,AlphaDataset 内部会形成 raw_df。它保留 datetime、vt_symbol,以及新计算出来的因子列和 label。这张表还没有经过完整预处理,因此更适合用于检查因子是否算出来、列名是否正确、缺失值是否集中在预期窗口。
prepare_data 之后,还需要调用 process_data 完成预处理:
dataset.process_data()
这里要理解 AlphaDataset 中的两条数据链:
infer_df:用于预测或推理的数据learn_df:用于训练和验证的数据二者最初都来自 raw_df,但可以挂不同的处理器。通过 add_processor("infer", processor) 添加的是预测链处理器;通过 add_processor("learn", processor) 添加的是训练链处理器。
默认 process_type="append" 时,AlphaDataset 会先按 infer 处理器生成 infer_df,再把它作为 learn_df 的起点,继续执行 learn 处理器。这样做的直觉是:训练和预测都需要一致的特征处理,但训练阶段可能还要额外处理标签或删除无效样本。
例如,一个常见思路是:
infer 阶段对特征做标准化或缺失值填充,保证未来预测时也能使用同样流程learn 阶段删除标签缺失的行,避免模型训练时读到无效目标后续模型会通过 fetch_learn(Segment.TRAIN)、fetch_learn(Segment.VALID) 或 fetch_infer(Segment.TEST) 等方法,按指定区间取出对应数据。
vnpy.alpha 在 dataset.processor 中提供了一些常见预处理函数,入门阶段不需要记住所有参数,但建议先理解它们各自解决什么问题。
process_drop_na:删除指定列存在缺失值的样本,常用于训练前剔除无效因子或标签。process_fill_na:用固定值填充缺失值,适合希望保留样本行、同时让模型能够继续训练或预测的场景。process_cs_norm:按交易日做截面标准化,可选择普通 z-score 或 robust 方法,用于减轻不同因子量纲差异。process_robust_zscore_norm:基于中位数和 MAD 做稳健标准化,并可对极端值截断,适合异常值较多的因子表。process_cs_rank_norm:按交易日做截面排名归一化,适合更关注相对排序、而不是原始数值大小的因子。这些处理器并不是越多越好。实际使用时,应该根据模型类型和因子含义选择。例如线性模型通常更依赖特征尺度稳定,树模型对单调变换相对不敏感,但仍可能受缺失值和异常值影响。
除了手工添加因子,vnpy.alpha 还内置了两个常见因子集:
Alpha158:参考 Qlib 的基础因子集合,包含 K 线形态、收益变化、均线、波动、量价关系等特征。Alpha101:参考 WorldQuant Alpha 101,包含大量时序与截面组合表达式。它们的意义不是让读者逐条背诵公式,而是提供一套可直接运行的基线。第一次跑通流程时,可以先用内置因子集完成从数据集、模型、信号到回测的闭环;等确认流程没有问题后,再逐步替换为自己的因子。
需要注意的是,这些因子集仍然遵守 AlphaDataset 的同一套机制:本质上都是在构造函数里批量调用 add_feature,并设置对应的 label。因此,理解了 AlphaDataset 的基本流程,也就理解了内置因子集如何接入后续模型。
AlphaDataset 还提供了 show_feature_performance(name),用于查看某个因子的表现。它会基于 Alphalens 生成因子分层、前向收益等分析视图。
这里要特别区分两个概念:
也就是说,show_feature_performance 更适合回答“这个因子本身有没有研究价值”;而后续回测更适合回答“把信号变成持仓之后,策略整体表现如何”。二者相关,但不能互相替代。
这一篇的重点,是把 AlphaDataset 在整个投研链路中的位置讲清楚:它接收 AlphaLab 读出的行情面板,通过 add_feature 和 set_label 定义因子与标签,再通过 prepare_data 计算原始结果,通过 process_data 生成训练和预测所需的数据表。
可以先记住这条顺序:
行情面板 -> 因子和标签定义 -> prepare_data 计算合并 -> process_data 预处理 -> 按 Segment 取数
到这里,我们已经有了一张模型可以使用的因子表。下一篇将进入 AlphaModel:模型接口如何设计,fit(dataset) 与 predict(dataset, segment) 分别做什么,Lasso、LightGBM、MLP 三类示例模型各适合怎样的入门场景,以及训练好的模型如何保存和加载。
K线合成器
Q:CTA策略默认传入 1min K 线数据,在哪个函数中传入默认参数 1min?
A:BarGenerator里面的update_tick()用于把tick数据合成1分钟K线数据,update_bar()是用于把1分钟数据合成X分钟数据。可以参考布林带策略示例,那里提供1分钟K线合成为15分钟K线,并且基于15分钟K线来产生买卖信号。
Q:BarGenerator的最大值是不是只能60?
A:合成分钟线的时候,周期最大只能为60(基于60分钟整除来进行N分钟切分);合成小时线的时候,周期可以为任意值(基于多少个小时过去了,进行合成)
K线时间序列管理器
Q:ArrayManager的初始化函数默认size=100,size指的是什么?
A:size是指这个K线时间序列容器缓存的数据量的大小,理论上只要超过了策略中要计算的所有技术指标最长的那个周期,就够用了。比如你要算MA20 RSI14 CCI50,那么最少需要size=50,否则CCI计算的数据量就不够,一般情况下还会在size上加上一定的量,来避免talib中某些指标算法可能需要更长的数据,保证计算的正确性。
Q:策略使用5分钟K线,ArrayManager初始化 self.am = ArrayManager(100)。在初始化size这里输入100的话,请问vn.py会从数据库里面提取100根1分钟的bar还是500根1分钟的bar来初始化指标?
A:缓存到100跟5分钟K线后才会完成初始化状态。
Q:talib安装失败,怎么解决?
A:使用手动安装:
1.进入Unofficial Windows Binaries for Python Extension Packages中找到talib对应的版本(如py3.7,64位)
2.下载对应版本的文件TA_Lib?0.4.17?cp37?cp37m?win_amd64.whl
3.下载好whl文件之后,直接在命令行下安装文件即可,如下。成功后会显示“Sucessful installed TA_Lib?0.4.17”
pip install TA_Lib?0.4.17?cp37?cp37m?win_amd64.whl
Q:如何导出计算的技术指标数值
A:最简单的方法就是直接print了,或者也可以写入到文件里。
回测数据
Q:CTA回测组件提示K线已经下载完成,但是在sqlite数据库中查看不到记录。
A:数据被插入到当前用户目录的database.db中,如C:\Users\HOME\.vntrader\database.db
Q:实盘时vn.py数据是tick级数据推送,回测时数据是分钟级的,它会识别时间戳吗?
A:bar回测的模式,在策略内部将tick合成为bar或者将1分钟bar合成为x分钟的bar
Q:回测加载数据,显示载入数据量为0。
A:没有连接数据库,或者数据库无数据
Q:初始化策略,默认initDays=10,改成初始一定数量的分钟数是不是更加合理?
A:若载入充足的历史数据,就可以立刻交易了。
Q:数据商也提供逐笔数据如何用来回测?
A:用逐笔自行还原出完整的订单簿tick数据,所以用tick模式来回测。
Q:如何获取A股分钟线数据?
A:目前没有免费的下载渠道(交易所禁止),推荐通过米筐的RQData获取。
Q:如何更改数据库存放盘?
A:举个例子:在D盘创建目录D:\test;在D:\test下创建文件夹.vntrader;使用VN Station的VN Trader Pro切换到D:\test目录启动;此时启动的VN Trader运行时目录已经是D:\test(可以在标题栏看到)
参数优化
Q:用capital作为目标,输出结果全是0
A:因为在逐日统计回测中,capital代表的是起始资金,无法作为优化的目标。可以改用endBalance,sharpeRatio,maxDrawdown等目标优化参数试一下。
Q:engine.trades可以输出交易记录,但是记录里只有time,没有具体的date,请问哪里可以输出具体的交易日期和时间?
A:trades记录里的trade对象,有个额外的datetime字段是用来标识该成交的日期时间的。
实盘运行
Q:自定义策略需要放在哪里?
A:在当前运行的脚本目录先创建创建strategies文件夹,然后把策略文件放进去就可以了。 配置文件可以创建.vntrader, 然后把配置文件放进去。
Q:怎样在on_bar里output数据,以此检查策略是不是按照我的想法在运转
A:可以直接在策略初始化的时候打开一个txt文件句柄,然后回测过程中随时往里面写记录
Q:点击CTA策略出现了json错误
A:json读取错误,如把.vntrader下的cta_strategy_data.json删除可解决
Q:CTA模块中是否有自带的变量存储合约的最小变动价位数据?
A:策略发出的买卖指令的价格,会自动根据最小价格变动pricetick进行round取整,无需最小价格变动的数据了
Q:如何在无人值守脚本获得所有合约的信息?
A:调用main_engine.get_all_contracts函数
Q:用CTPtest抓所有合约, 郑交所的合同没有菜粕,苹果。
A:调用get_all_contracts前,sleep等待5秒,接收合约数据推送需要时间;某些测试环境里,是会缺少部分合约的。
Q:为什么使用VnTrader的cta策略组件进行日常实盘交易时,每天交易时段结束之后,一定要把VnTrader关闭,然后在下次开盘前15分钟在重启并初始化策略的参数?
A:关闭交易系统主要是为了清空系统内部(接口层、引擎层)的缓存状态,策略层的缓存状态一般倒是不太容易出问题的(除非你逻辑写错了)
Q:请问backtesting里的cross_limit_order和cross_stop_order什么意思
A:撮合限价单委托,撮合本地停止单(条件单)委托。讲最新的行情K线或者TICK和策略之前下达的所有委托进行检查,如果能够撮合成交,则返回并记录数据
Q:如何实现k线内成交?
A:用的是本地停止单
就比如当前价格是100点,策略发出信号,在下一根K线的120线发出买入:
若价格没突破到120点,继续挂着。
若价格从100涨到130,那么在120点的时候停止单变成市价单追上去保证尽可能成交。
若下一根K线的开盘价大于120,那么以开盘价来立刻成交。
Q:回测1h k线数据, 为什么每一笔订单成交记录都比委托记录晚一个小时 , 请问这个是在哪里设置的?
A:委托时间,是你发出委托请求的时间。成交时间,是你的成交发生的事件。CTA策略回测遵循T时刻发出的委托,永远只在T+1后才能进行撮合的规则(否则会有未来函数)。
实盘中,你的成交时间可能和委托时间非常接近,但是回测中受限于数据没法确定,只能用T+1那根K线的时间戳近似。
Q:如果停止单触发下单之后一段时间没有执行的话,会撤单吗,什么时候撤单,撤单之后还会追单吗?
A:不会,除非策略执行撤单操作。
Q:如果停止单触发下单之后,部分成交,接下来会撤单还是追单?
A:策略会收到这笔委托的回报,用户可以自行处理,不会自动撤单或者追单
Q:如果同时持有多空,pos是单向的,该怎么处理? 例如:如果持有1手多单,sell卖平的委托没有成交,紧接着的short卖开的单子成交了,pos 是多少?
A : 对于CTA策略模块来说,策略的持仓,是基于策略本身发出去的委托成交维护的逻辑持仓(而非账户底层的实际持仓),所以pos会为0
Q:假如策略下了1手多单,手动下了1手多单,pos 是多少 ?
A : 人手工下的单子无影响
Q:假如策略下了2手多单,手动平仓1手,pos 是多少 ?
A:人手工下的单子无影响
Q:两个策略都在跑同一个代码,应该是各自有各自的pos 吧 ?
A:对的,这两个逻辑持仓互相无关
Q:框架对pos值的更新,是在onTrade 和 onOrder推送动作前,还是推送动作后?
A:onTrade推送前更新,保证策略收到onTrade回调的时候,pos已经是最新数据。
Q:vt_setting.json的路径到底在哪?
A:在c:\users\administrator.vntrader目录下,是基于Python的pathlib实现的
Q:positionData中的 yd_volume 是指什么?
A:昨仓,这个主要针对中国市场的股票(只能卖出昨日的股票)和期货(昨仓平仓手续费不同)
Q:当前CTA模块是不是不能在策略里获取当前资金情况?
A:不能获取资金和账户级别的持仓。策略的仓位管理(风险分配)应该由交易员来做,而不是让程序自动做
Q:非交易时间报单,是直接返回拒单,还是返回提交中等开盘了再打出去?
A:在策略层,如on_bar()函数里面第一个逻辑应该是self.cancel_all(),目的是清空为成交的委托(包括本地停止单,限价单),保证当前时间点委托状态是唯一的。
若在非交易时间发单,服务器收到这个委托会推送一个拒单的委托回报。
Q:vn.py如何查询实盘账户的历史资金情况
A:大部分交易系统并未提供历史资金查询功能,一般都是只能查当前时间点的资金,所以需要你自己保存。
其他问题
Q: 请问下,vn.py中有期权回测的例子么?vn.py 关于期权,是不是就只有OptionMatser模块?
A:期权的波动率交易策略一般无需回测,更多依赖建模;可以用其他组件交易期权,比如CTA策略模块赌趋势,或者SpreadTrading模块赌价差,但这些本质都不是期权交易策略。
Q:如何根据资金量进行下单,而不是固定手数下单?
A:vn.py框架下不建议交易程序在实盘中去获取账户可用资金,并调整交易手数,这是很危险的事情。
CTP接口
Q:如何实现行情并发推送?
A:C++ API的回调函数只有一个推送线程;用户可以一次性订阅全市场的合约,某个合约有行情变动的时候才会推送.
Q:CTP能可否获取到指定经纪商的手续费?
A:这个手续费应该是连带期货公司的部分,但是不建议在交易程序中去访问这种数据,相关数据应该每天在启动策略前就获取好配置到策略里。
Q:郑商所的品种都收不到14:59这一根bar
A:郑商所的数据推送,没有3点后的最后一个tick用于标识收盘完成,所以要调用BarGenerator.generate函数来做最终的强制K线生成
Q:国内期货模拟,除了sinnow可以模拟,还有别的账号可以模拟吗,可以去期货公司申请模拟账号模拟吗?
A:SimNow是目前最稳定的仿真环境了,可以找期货公司申请,比如中信、上海中期等。
Q:sinnow连接有时会断开,然后传送的tick数据的时间就会延后,就是收盘时,本来应该平仓平不了,超过三点了还在传tick。
A:SimNow环境因为免费,用的人很多,所以服务器有时会卡。
Q:为什么有时候会不停的断开重连呢?
A:服务器关了或者人满了,或者账号密码错误。
Q:连接进 SimNow后,按”市价“下单被拒绝,按”限价“”FAK“”FOK"下单可以,请问,是否不支持“市价”下单。
A:SimNow不支持市价单,实盘支持。
Q:连接SimNow后,下单提示“提交中“无法成交也无法撤单
A:这种情况,一般是委托请求没有到CTP柜台(网络断了),或者CTP柜台挂了
Q:SimNow上不去,上去了注册又总是提示验证码失败,还有其他模拟的推荐吗?
A:SimNow是目前最推荐的仿真环境,建议换交易时间注册,以及SimNow主要支持移动和联通的手机号
Q:CTP配置无法连接:输入论坛登录名,账号,配置对应的交易服务器和行情服务器,点击连接,无任何反应
A:CTP的测试账号请通过SimNow获取,不是vn.py论坛的
Q:已有行情数据显示。 但是不能发单,如rb1905
A:检查是否漏填交易所或者上委托数量的字段。
Q:下单异常,第一种情况:一点击委托就是直接“已撤销”(委托栏里委托状态),双击撤单的时候,又会显示“交易撤单失败,代码25”。第二种情况:一点击委托就是直接“提交中”(委托栏里委托状态),等到双击撤单的时候,又会显示“交易撤单失败,代码25”
A:第一个情况应该是报单不符合服务端的要求,被拒单撤销了;第二个情况感觉是你的网络断了,报单请求发出但没有到服务器。可以顺着这两个方向检查。
Q:CTPTEST测试交易期货公司采集不到硬盘序列号,CPU序列号,BIOS序列号
A:数据采集是通过API内部的代码自动完成的,其他任何上层程序都无法影响。建议换机器。
Q:登录穿透式仿真账号问题,一直报4097的错
A:不要同时import CTPTEST和CTP这两个接口
Q:使用ctp和ctptest登录都显示不合法登录。错误代码3
A:账户密码错误
Q:CTP能否两个账号同时登录并且同时操作。
A:同一个接口只能登录一次。如果要同时登录两个CTP,需要自己扩展修改CtpGateway,然后加载两个CtpGateway;对于不同的接口,比如股票的XTP和期货的CTP,可以直接同时登录使用,并在策略中同时交易这两个接口的合约
IB接口
Q:如何链接盈透api ?
A:启动TWS;在配置中打开Socket连接功能;在vn.py中加载ibGateway,然后启动就行。
Q:IB接口连接,错误提示显示:couldn't connect to TWS. confirm that "enable activex and socket clients" is enabled aports for new installations of version 954.1 or newer: TWS:7497:IB gateway: 4002。
A:请检查TWS是否打开了socket访问功能。
Q:启动行情记录,则程序假死。
A:IB的行情订阅函数没有异步缓存逻辑,用DataRecorder脚本的话,会在连接TWS之前就进行了订阅请求,导致死掉。IbGatewa已经加上了历史数据查询获取功能,直接从IB查询K线数据进行初始化就行,不建议自己录制了。
富途接口
Q:futu_gateway里面的登陆信息设置,为何只要密码?
A:需要先下载和安装FUTU OPEN API:https://www.futunn.com/openAPI
华宝派
Q:华宝派如何申请试用/实盘呢?
A:请在华宝证券开户,然后联系客户经理申请使用华宝PI
交易复制
Q:当跟单帐户检测到被跟单帐户的仓位变化后,具体操作是什么?
A:TradeCopy的发布者账号,维护一份本地持仓数据表,当有成交推送时立即更新计算最新仓位;发布者每当收到成交推送时,或者每隔一定的时间间隔(默认1秒),会广播一次当前自己的仓位信息;订阅者收到广播推送的发布者仓位后,乘以自身的复制系数,作为目标仓位;订阅者根据目标仓位,和自身实际持仓的偏差,决定具体的下单操作(目标是将实际持仓同步到和目标持仓一致),如果有之前的委托,会先执行撤单。
RQData
Q:import rqdatac 失败,没有找到rqdatac包
A:运行以下命令安装
pip install --extra-index-url https://rquser:ricequant99@py.ricequant.com/simple/ rqdatac==1.0.0a66
Q:如何配置RQDAAT账户?
A:申请试用账号:https://www.ricequant.com/purchase
在VN Trader主界面上,点配置,rqdata.username rqdata.password输入
重启VN Trader就能用了
工作线程
Q:vn.py运行的时候,会启动哪些线程?
A:
1.主线程:带PyQt界面时运行Qt的循环,无界面时可以直接阻塞或者用while循环
2.事件引擎线程:处理事件引擎队列中的事件,并调用注册的处理函数处理,所以如果是CtaStrategy层,所有回调函数你可以认为都是单线程在驱动的(每次只有一个在调用)
3.API层线程:不同的API不一样了。
Q:eventEngine2.__queue很多数据没有处理、队列一直变大?
A:如果queue的大小只增加,不减少,只可能是没有启动EventEngine,导致事件没有处理持续挤压导致的。否则运行过程中即使没有注册处理函数,该事件的数据也会被抛弃掉,不会继续保存着。
行情记录
Q:行情记录后怎么查看和下载历史行情数据?
A:行情记录模块是将tick或者bar直接存入你配置的数据库的,你要单独查看,可以用数据库可视化工具连接本地数据库查看,如果在vn.py里回测使用,不需要下载,是默认从数据库里找相关数据的
Q:datarecoder 和 cta running 的 CTP能不能分开设置?
A:可以,另外新建一个目录,里面创建.vntrader文件夹,在这个目录用run.py或者VN Station启动VN Trader,CTP接口的登录信息就都是独立的了。
其他
Q:网站上下载的vnpy ,和Anaconda site-package里面的vnpy有什么区别?
A:进行运行的是ananconda里面的vnpy。如同在anaconda里面调用numpy一样。
Q:修改vnpy代码后需要更新到anaconda site-package对应的文件里?
A:python里import的vnpy就是site-packages里的,你可以修改下环境变量,把你clone的那个目录加入搜索路径,这样你修改了clone的那个vn.py,用的时候就自动改了
Q:一键安装完2.0.5 后,再另外安装anaconda3, spyder无法使用
A:假设你安装到c:\anaconda3。打开cmd,运行c:\anaconda3\scripts\activate,然后再运行python,就会进入anaconda环境了
Q:请问算法交易怎么用,可以策略生成下单指令,由算法下单吗?
A:目前AlgoTrading模块主要通过GUI和篮子委托文件的方式来实现算法下单
尽管可以通过扩展的方式,实现策略调用算法执行交易,但更建议在CTA策略中自行实现算法交易的逻辑,获得更好的细节控制能力
Q:vn.py中配置界面中email各项设置如何填写,有何用处?
A:设置如下
"email.server": "SMTP邮件服务器地址",
"email.port": SMTP邮件服务器端口号,
"email.username": "邮箱用户名",
"email.password": "邮箱密码",
"email.sender": "发送者邮箱",
"email.receiver": "接收者邮箱",
Q:找回vn.py社区的密码
A:在这个页面可以找回密码:https://www.vnpy.com/auth/reset-password
Q:注册了社区账号,但是登录报错。[WinError 10061] 由于目标计算机积极拒绝,无法连接
A:这个是代理服务器问题吧,换个网络试试。
Q:维恩的派和vn.py社区有什么关系?
A:维恩的派是vn.py社区2015-2018年的老论坛,但由于discuz的各种问题使用体验太差,现在已经停止使用,将在19年底正式下线。
Q:社区怎么上传照片的?
A:直接把图片拖动到编辑框中就能自动上传了
Q:有微信群,QQ群吗?
A:vn.py框架学习群:666359421 ; vn.py框架交流群:262656087
发布于VeighNa社区公众号【vnpy-community】
原文作者:VeighNa小助手 | 发布时间:2026-04-14
【社区活动尊享卡】的受欢迎程度大幅超出我们的预期,为了保证每场社区活动的交流质量,尊享卡已经变更为仅对部分专业交易员用户定向提供。对于参加活动较多的同学强烈推荐!购买请扫描二维码添加小助手咨询:

距离我们上一场围绕 OpenClaw 的社区活动,转眼已经过去两个月。
随着热度逐渐回落,这款一度现象级爆火的 Agent 产品,也开始慢慢褪去光环。新鲜感过去之后,越来越多的用户降低了使用频率,甚至不再继续使用。
不过,由 OpenClaw 带火的「24/7 Agent 助理」这一产品形态,并没有停下迭代的脚步。最近,新挑战者 Hermes Agent 的 Token 使用量已跃升至 OpenRouter 平台热门 Agent 排行榜第二位,仅次于 OpenClaw,而且仍在持续快速增长。
站在巨人的肩膀上,往往能看得更远。受限于早期的开发模式,OpenClaw 在底层系统架构上存在一些先天不足;而作为后来者,Hermes Agent 在吸收 OpenClaw 产品亮点的同时,也凭借后发优势打造了更稳健的技术底座。
那么,Hermes Agent 是否有机会成为量化投研工作中真正提升生产力的 Agent 助理?在本场活动中,我们将围绕这一问题展开深入探索。
本场活动将于4月25日(周六)下午2:00至5:00在上海举办。普通报名仅支持线下参会,尊享卡报名可通过线上直播参与。活动具体地址将在微信群中公布,请在报名成功后扫码加入社区活动群,以便获取相关信息!
时间:4月25日 14:00-17:00
地点:上海(具体地址后续在微信群中通知)
报名费:99元(Elite会员免费参加)
报名方式:扫描下方二维码报名(报名后请扫码加入社区活动微信群获取参会地址)

发布于veighna社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2023-04-19
近几个月,可能许多VeighNa社区用户或多或少都听过了【VeighNa Elite】或者【VeighNa菁英版】的名字(下文简称Elite版)。经过半年多的迭代打磨,Elite版已经于3月底正式发布上线,在此对之前参加内测的用户表示衷心感谢。
作为面向专业交易员推出的量化交易终端产品,Elite版在开源版VeighNa量化平台的基础上针对性能、功能和稳定三个方面做出了许多优化:

具体的优化细节将会在下文中介绍,这里也对VeighNa量化平台现有的三层产品体系做个简单的梳理:

由于Python的GIL全局锁限制,VeighNa平台在单进程的情况下仅能利用CPU单核的算力,所以通常建议开源版中每个VeighNa进程运行CTA策略的数量在20-50的范围内(具体取决于CPU主频和策略计算开销),超过该范围后可能造成Tick-2-Trade延时出现显著的增加。
尽管nogil项目已经被提上了Python 3.12的开发计划,但具体发布时间还有挺大的不确定性。为了在保证Tick-2-Trade延时的情况下,尽可能满足专业交易员在策略并发数量上的需求,Elite版采用了类似Chrome浏览器的多进程架构设计:

通过将开销较大的计算逻辑拆分到独立进程的方式,实现对当今多核心CPU算力的充分利用,并且在系统核心引擎层的算法性能方面也进行了优化:

在使用体验上,Elite版保持了和开源版高度一致的客户端运行模式,双击桌面图标即可直接启动运行,对于老用户来说可以几乎无感上手,而无需担心服务端系统(定制版)中较为繁琐的专业化运维流程。
之前有不少社区用户提交过围绕国内市场专有行情和交易规则的代码修改PR(如非交易时段的Tick过滤、K线合成器对商品期货上午15分钟休息的处理等),但由于前文提到开源版对于各类金融市场的广泛覆盖,这些PR并没有合并到开源仓库中,而是保留在了论坛帖子里供有需要的用户自行学习修改。

作为面向专业交易员的Elite版,覆盖范围上聚焦在三大主流量化市场(期货、期权、证券),因此策略引擎层面也做了相应的适配调整(如基于品种交易时段配置文件的行情Tick过滤等)。

在策略开发方面,Elite版也提供了许多额外的功能:
Elite版中整合了标准化算法交易功能,用户可以直接在主界面快速启动算法执行各种智能化的交易任务:

通过位于数据监控区域的算法监控组件,可以直观跟踪当前算法的执行状态,并支持随时【暂停】或者【停止】:

现有版本中已经提供AlgoTrading模块中的被动类算法,后续也计划接入其他三方算法供应商提供的主动类算法,满足更多专业交易员场景下的执行需求。
对于许多使用VeighNa开源版运行CTA策略交易的用户来说,每当遇到主力合约切换的交易日都要经历一段比较麻烦的操作,整体上要完成的四个步骤包括:
尽管在CTA策略模块中提供了【移仓助手】功能,但每次只能执行一个合约上的移仓操作,且采用的同步下单模式有时会出现移仓瘸腿或者滑点较大的情况。
基于多进程的系统架构,Elite版重新实现了【移仓助手】功能,支持多合约批量并发移仓任务。同时采用价差算法来执行移仓交易,用户可以自行设置每个合约上的移仓数量和委托上限等参数,有效避免移仓中的瘸腿情况并降低整体的滑点成本。

开始移仓任务前,会自动弹出确认执行移仓信息的对话框,方便检查各参数是否设置正确,避免误操作的风险:

对于专业交易员群体中普遍的多账户交易需求,Elite版提供了完善的功能支持。用户可以在登录启动程序时,加载多个VeighNa交易接口并配置对应的交易账号,每个接口可以指定自定义名称,方便在交易过程中的监控识别。

Elite版同时也提供了【多账户批量下单】功能,用户可以根据需求任意创建下单账户组合,并为组合中的每个账户分配独立的下单比例,满足不同资金水平和风险偏好下的多账户交易需求:

在围绕订单流交易(Order-Flow Trading)的领域,【市场深度交易下单】是一种广为使用的交易功能,在不同软件中的名字出入可能比较大:

整体上看,深度交易的核心功能包括:
Elite版当前已经支持单合约的深度交易,后续计划增加对价差交易(SpreadTrading)中的价差组合支持。
目前Elite版仅作为【VeighNa Elite会员服务】的专属权益提供,其他会员权益包括:
Elite会员服务的年费价格为9999元,感兴趣的同学可以直接扫描下方二维码,或者点击公众号菜单栏的【社区交流】->【Elite会员服务】:

发布于VeighNa社区公众号【vnpy-community】
原文作者:用Python的交易员 | 发布时间:2025-12-29
上周我们发布了VeighNa 4.3.0版本。本次更新主要包括:在VeighNa Station中集成全新的AI智能助手VeighNa Assistant,以及对VeighNa Docker镜像进行重构优化。
已安装VeighNa Studio 4.0版本的用户,可使用快速更新功能完成自动升级;尚未安装的用户,建议直接下载VeighNa Studio-4.3.0,体验一键安装的量化交易Python发行版,下载链接:
https://download.vnpy.com/veighna_studio-4.3.0.exe

在本次4.3.0版本中,VeighNa Station集成了VeighNa Assistant智能助手(基于VNAG框架开发),主要功能包括:
要使用VeighNa Assistant,首先需要准备一个大模型服务的API Key(可理解为访问凭证)。对于初次接触的用户,推荐使用阿里云百炼的AI服务(目前提供较为充足的免费额度),具体开通流程请参考阿里云官方的详细步骤说明。
准备好API Key后,双击桌面快捷方式启动VeighNa Station,在顶部菜单栏中找到【功能 -> AI服务配置】选项:

点击后将打开AI服务配置对话框。在顶部的下拉框中选择【Bailian】,然后在配置参数区域填入之前准备好的API Key,API地址保持默认即可:

点击【保存】按钮后,系统会弹出提示框,告知需要重启VeighNa Station以使配置生效:

完成重启后,点击顶部菜单栏的【功能 -> 模型浏览器】,选择想要使用的大模型:

在模型浏览器中,可以通过上图红框中的箭头按钮来添加或移除大模型,同时也可以调整已选模型的优先级顺序。对于百炼AI服务,推荐选择Agentic能力较强的qwen3-max-preview或kimi-k2-thinking模型。完成设置后,点击右下角的【保存】按钮,系统会弹出确认提示框:

点击【OK】返回主界面后,即可在聊天区域与智能体进行交互。整体使用方式与ChatGPT等主流AI聊天工具高度相似,有相关使用经验的用户可以快速上手:

如果在使用过程中遇到任何问题或有改进建议,欢迎在社区论坛的【VeighNa Assistant】专区发帖交流。
基于社区用户的反馈,我们在4.3.0版本中 对VeighNa Docker镜像进行了全方位的重构与优化。
核心改进:
快速开始:
用户可以直接访问VeighNa Docker Hub查看详情,或使用以下命令直接拉取并启动:
# 拉取 4.3.0 版本镜像
docker pull veighna/veighna:4.3.0
# 启动容器(示例:挂载本地目录并启动图形界面)
docker run -it \
-e DISPLAY=$DISPLAY \
-v /tmp/.X11-unix:/tmp/.X11-unix \
-v $(pwd)/home:/home \
-p 8888:8888 \
veighna/veighna:4.3.0 python3 -m veighna_station
新增
调整
修复
[知乎专栏经验发布] (https://www.zhihu.com/column/c_1760768090802171904)
距离上一篇”完结”过去了快一年,本来以为这个系列就这样了。
然后 AI 来了。
不是那种”帮你补全几行代码”的 AI,而是真的能从零开始、一个文件一个文件帮你写出来的 AI。看着 Claude 一步步理解我的需求,读懂 VNPY 的源码,处理 PySide6 的信号槽、QSS 样式、Pydantic 数据模型……说实话,有点震撼。
于是趁着春节前的这段时间,我做了一个实验:用 AI 辅助,从 VNPY 3.9 重构到 4.3,看看能走多远。
结果是:大约一周时间,从项目搭建到功能完善,我没有手写一行代码。所有的代码都是在和 AI 的对话中产生的——我负责提需求、审代码、做决策,AI 负责实现。
为什么要重构
VNPY 从 3.9 到 4.3 变化很大,核心模块拆分成了独立包(vnpy-ctastrategy、vnpy-ctp 等),架构上差异太多,简单迁移行不通,不如重新来过。
正好,V1 积累了不少想法但一直没动手改的地方,这次借 AI 之手一并实现了。
V2 主要变化
变化 说明
基础框架 VNPY 3.9 → 4.3
数据库 SQLite/MySQL → ArcticDB (LMDB)
策略架构 三层数据模型 Params/State/Vars
双模交易 同一策略支持全自动 + 辅助半自动
多账户 实盘/模拟/7×24 环境并存
主力合约 自动识别 + 换月持仓保护
AI 助手 实验性接入,目标是策略信号辅助
界面 PySide6 + QFluentWidgets 全面重写
详细内容就不在这里展开了,感兴趣的朋友请移步 GitHub 仓库查看 README。
开源
项目已开源,MIT 协议,可自由使用和修改:
GitHub:https://github.com/48645970/guanlan
欢迎 Star,欢迎提 Issue,欢迎交流。
感谢
感谢 VNPY 团队提供了优秀的量化交易框架,让这一切有了基础。
感谢 Claude —— 这个项目的”联合开发者”。一周时间里,从框架搭建到细节打磨,每一行代码都经过了我们之间的对话。AI 不会疲倦,不会敷衍,能记住上下文,能读懂源码,能理解”这里不太对”是什么意思。这种体验,一年前完全无法想象。
我们正站在一个很有意思的时代节点上。
最后
祝大家新年快乐,交易顺利。
主窗口

多账户管理,自动登录

合约管理,收藏,手续费设置(策略中算手续费),定期从外部获取后计算最大持仓定为主力

交易持仓

绩效查看,按账户统计

数据管理,一键批量导入通达信数据(日线、分钟线)

策略执行
使用数据模拟定义参数和状态显示,配合限制输入框,对输入内容格式、最大最小值做限制
界面参数、状态值,使用模型指定的中文标题显示
Params、State 显示在窗口
Variable 仅做为中间变量存储,但不显示
扩展 on_ready,在在 on_start 后触发,不同的是,交易状态 self.trading == True
扩展 on_reset,在画面点击重置时触发



安装流程:





打开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环境下

作为初学者,面对 vnpy 无所不包、博大精深的丰富内容,试图用图形对 vnpy 的运行流程做一个归纳。
不到之处,还请各位多多指正

我原本想用WSL2在windows机器上启动一个ubuntu机器来运行vnpy,因为期货公司不让用云服务器或者虚拟机进行测试,所以我同时安装了windows,也把Windows和Ubuntu两种环境的坑都踩了。。。
在windows/mac/linux环境中,windows不许需要环境准备,直接下载安装包就行。我使用ubuntu属于linux。
其他回答普遍反映install.sh有问题,我试了一下,确实有问题。于是直接手动安装。说是手动安装其实也快。
# 下载vnpy到ubuntu本地
git clone git@github.com:vnpy/vnpy.git
下面开始配置环境,我使用的pyenv+venv轻量化的环境配置方案。
pyenv功能:为不同项目提供不同的python版本。比如我A项目使用3.7.4, 而vnpy推荐使用3.13版本。使用了pyenv我就可以实现在A项目用3.7.4,在vnpy使用3.13,而不是两个系统使用同一个版本的python。
venv功能:为不同项目提供不同的依赖。A项目使用了2.27.1版本的requests包,而vnpy使用3.0.01版本的requests包(其实没有),直接使用pip install只能同时安装一个包,使用venv就可以把A项目和vnpy项目依赖的包隔离开了。
其他教程里面的conda,minicoda其实都是实现pyenv+venv的功能,选择使用conda还是pyenv+venv区别不大。
# 安装pyenv
curl https://pyenv.run | bash
# 查看可安装的 Python 版本
pyenv install --list
# 安装指定版本
pyenv install 3.13.2
# 查看已安装的版本
pyenv versions
# * system (当前使用的版本)
# 3.8.10
# 3.11.0
# 2. 本地(当前目录及子目录)
cd vnpy
pyenv local 3.13.2 # 该项目使用 3.13.2
# 会自动创建 .python-version 文件,此时在vnpy项目下运行python --version就会提示版本信息为3.13.2
# 创建venv虚拟环境
python3 -m venv .venv
# 激活虚拟环境
source .venv/bin/activate
# 安装vnpy的依赖包
pip install .
# 安装build-essential
sudo apt-get install build-essential
# 安装ta-lib
wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz
tar -zxvf ta-lib-0.4.0-src.tar.gz
cd ta-lib/
./configure --prefix=/usr
make
sudo make install
pip install ta-lib
# 我并没有确认下面两个是否必须,直接按照其他教程安装了
pip install quickfix
pip install psycopg2-binary
在examples/veighna_trader/run.py有个run.py文件,将它复制到vnpy的根目录下。在vnpy目录,运行python run.py

这时候可能会报“'CtaStrategyApp' from 'vnpy_ctastrategy' (unknown location)”,很简单,只要安装这个模块就行,比如这个vnpy_ctastrategy,使用pip install vnpy_ctastrategy命令即可。
然后运行python run.py,就可以打开vnpy界面。如果没有打开,很有可能是没有GUI,如果是没有GUI的话,可以看下面。

如果是使用没有界面的ubuntu,比如我使用WSL,是不能装xubuntu-desktop太重的界面的。如果是win11可以使用WSLg,我的是win10,用的X410。AI给的完整UI配置流程我也放下面了。

去SimNow仿真交易【官方网站】官网注册一个账号,注册账号后,找到官网通知公告内的BrokeID,Trade Front,Market Front
SimNow仿真交易-产品与服务。左侧“产品和服务”打不开的话,先打开官网,然后点击顶部导航栏的“产品与服务”,下拉到中间就有simnow的链接配置信息。
在vnpy界面中,点击系统->连接CPT,然后按照下图所示的配置填写。其中

登录成功后,可以点击帮助->查询合约->查询,来看合约信息是否有,有就成功了。
后面的穿透测试流程以及window,linux环境下decode err的解决方法我都写在这里了。https://www.vnpy.com/forum/topic/34604
经过好几天的反复,终于完成了。所谓的复盘,就是盘后把行情从新播放一遍,如果使用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,真的很好用。