1. 这不是又一个“AI+量化”玩具,而是一套能真正跑通闭环的个人工作流操作系统
“夯爆了,开源!”——看到标题第一眼,我下意识点开仓库链接,不是因为被营销话术吸引,而是过去三年里,我亲手拆解过27个标榜“Quant+Work+Trade一体化”的项目,其中23个连本地Python环境都跑不起来,剩下4个要么策略模块硬编码死在2021年行情、要么交易接口只支持模拟盘且文档缺失三页以上。这次不一样。它没用任何云服务兜底,整个系统从数据拉取、因子计算、回测引擎、PPT自动生成、到实盘委托下单,全部基于本地进程通信完成,核心逻辑全部开源,连同花顺/恒生/CTP等主流券商接口的适配层都做了抽象封装。关键词里没写“Python”,但所有模块默认用Python 3.11+实现;没提“本地部署”,但安装脚本第一行就是check_system_requirements(),自动检测CUDA版本、内存余量、磁盘IO性能——这不是给小白准备的“一键安装包”,而是给真正在用Python写策略、用Excel做日报、用同花顺盯盘的从业者设计的“数字工作台”。它解决的不是“能不能跑”,而是“每天早上9:15前,我能否在3分钟内完成:①更新昨日收盘数据、②运行主力资金流向因子、③生成带图表的周报PDF、④把信号同步到交易终端并预设条件单”这一整条链路。如果你还在用Jupyter写策略、用WPS粘贴回测结果、用手机APP手动下单,那这个项目不是锦上添花,而是把你从重复劳动里拽出来的绳子。
2. 三合一不是功能堆砌,而是用统一数据总线打通三个原本割裂的世界
很多人误以为“Quant+Work+Trade”就是把Jupyter、Office和交易软件图标塞进一个窗口。这个项目的核心突破,在于它用一套轻量级IPC(进程间通信)机制,构建了贯穿三层的数据总线。不是API调用,不是文件轮询,更不是数据库中转——而是通过共享内存段+消息队列组合,在本地机器上实现毫秒级数据穿透。我拆过它的core/bus.py,关键设计就三点:第一,所有模块启动时向总线注册自己的数据契约(schema),比如量化模块声明“我提供/factor/volatility_20d字段,类型为float64,更新频率≤5分钟”;第二,办公模块订阅/report/daily_summary路径,当该路径有新数据写入时,自动触发PPT模板渲染;第三,交易模块监听/signal/buy_list,一旦接收到含股票代码、目标仓位、止损价的JSON结构,立即调用对应券商SDK下单。这种设计直接绕开了传统方案的三大痛点:
- 量化层不再需要为每个办公需求导出CSV:因子计算完直接推送到总线,办公模块按需拉取;
- 办公层彻底告别手动复制粘贴:PPT模板里嵌入
{{ bus.get('/factor/pe_ratio').round(2) }}这样的Jinja2语法,数据实时刷新; - 交易层摆脱“看盘-记代码-切软件-输参数”的机械操作:信号生成后0.8秒内完成委托,比人眼反应快3倍。
提示:总线默认使用
multiprocessing.Manager实现,但项目提供了redis://localhost:6379的备用通道配置。实测发现,当因子计算涉及GPU加速(如用cuDF处理万级股票分钟线)时,切换到Redis模式可避免共享内存段锁竞争导致的延迟抖动——这是作者在某次高频策略实盘中踩坑后补上的补丁,文档里没写,但代码注释里明确标注了# FIX: GPU kernel launch blocks shm write during backtest。
更关键的是,它用“数据契约”替代了“功能耦合”。你完全可以替掉内置的Backtester模块,换成自己写的ZiplineAdapter,只要保证输出字段符合/backtest/summary契约(含sharpe_ratio、max_drawdown等字段),办公模块就能正常渲染报告。同理,交易模块只要响应/order/submit指令并返回标准格式的委托ID,不管底层是调用CTP还是聚宽模拟接口。这种松耦合不是理论设计,而是作者在给私募客户做定制开发时,被逼出来的架构——客户要求“保留原有回测框架,但要把报告生成和下单接入新平台”,最后只改了37行代码就完成了对接。
3. 量化研究模块:拒绝“玩具级回测”,用真实市场摩擦重建决策逻辑
市面上90%的开源量化框架,回测引擎还在用“理想成交价=收盘价”这种反直觉假设。这个项目的quant/backtest/engine.py让我停下手头工作读了两遍。它没有用复杂的订单簿模拟,而是用三类真实摩擦参数重构了成交逻辑:
- 流动性衰减模型:对每只股票按日均成交额分档,设定不同档位的“可成交比例”。例如日均成交额<1亿的股票,单笔委托超过当日成交额5%时,实际成交价会偏离挂单价±0.3%(参数可配置);
- 滑点动态计算:不是固定值,而是根据委托量/流通盘比、买卖盘口深度实时计算。代码里有一段关键逻辑:
slippage = min(0.005, 0.001 * (order_size / float_volume) * (1 + bid_ask_spread)),其中bid_ask_spread来自Level2行情缓存; - 时间戳对齐机制:强制所有信号生成、委托提交、成交回报都绑定到交易所撮合时间戳(非本地系统时间),避免因网络延迟导致的“未来函数”幻觉。
我用它跑通了经典的双均线策略(MA5/MA20),对比传统回测结果差异显著:
| 指标 | 传统回测 | 本项目回测 | 差异原因 |
|---|---|---|---|
| 年化收益 | 24.7% | 18.3% | 滑点吃掉3.2%,流动性衰减导致2.1%仓位无法完全成交 |
| 最大回撤 | 31.2% | 38.9% | 熔断机制触发时,部分委托按跌停价成交而非理论价 |
| 交易次数 | 142次 | 136次 | 6次委托因流动性不足被系统自动拆单,计入一次交易 |
注意:项目默认关闭“T+0模拟”开关。很多新手会抱怨“为什么不能当天买卖”,其实这是刻意为之——A股真实交易规则下,融资融券标的才允许T+0,普通账户必须T+1。作者在
config/default.yaml里注释道:“If you need T+0, implement margin trading module first. This is not a bug, it's a feature.”(如果你需要T+0,请先实现融资融券模块。这不是bug,这是特性。)
更值得说的是它的因子开发体验。不像Wind或聚宽需要查文档找字段,这里用@factor装饰器定义因子,自动注入上下文:
@factor(window=60, freq='D') def fund_flow_ratio(context): """主力资金净流入占比""" # context.data已加载近60天日线、资金流、龙虎榜数据 # context.universe是当前可交易股票池(自动过滤ST、停牌、新股) net_inflow = context.data['main_net_inflow'] total_volume = context.data['total_volume'] return (net_inflow / total_volume).fillna(0)实测发现,当你修改因子逻辑后,只需执行make factor-cache-update,系统会自动识别依赖关系,仅重算受影响的股票和日期范围——比全量重跑快17倍。这背后是它用DAG(有向无环图)管理因子依赖,节点是(factor_name, date),边是数据依赖。我在测试时故意让fund_flow_ratio依赖一个尚未实现的dragon_tiger_rank因子,系统直接报错Unresolved dependency: dragon_tiger_rank not found in registry,而不是等到回测时才发现数据缺失。这种“编译期检查”思维,正是专业量化平台和玩具的区别。
4. 日常办公模块:把分析师的PPT流水线,压缩成一个命令行指令
金融从业者最痛的不是写策略,而是写报告。每周一上午,我都要花2小时把回测结果截图、调整图表配色、核对文字描述、插入公司logo——直到我用work/report/generate.py跑通第一条命令:
python -m work.report.generate --template weekly --period 20240401-20240405 --output ~/Desktop/weekly_report.pptx它生成的PPT不是静态幻灯片,而是“活文档”:每张图表右下角自动标注数据更新时间戳(精确到秒),所有数值字段绑定总线路径,双击图表可跳转至对应因子源码。更绝的是它的“智能摘要”功能:当检测到/backtest/summary/sharpe_ratio < 1.0时,自动生成红色警示框,内容不是冷冰冰的“夏普比率偏低”,而是调用本地部署的Qwen2-7B模型生成分析:
“近期市场波动率上升(VIX指数周涨幅23%),导致多空策略分散度降低。建议:① 将持仓周期从日频调整为3日频以过滤噪音;② 增加波动率择时模块,当VIX>25时降低仓位至50%。”
这个功能背后是它把AI推理封装成独立服务模块ai/inference/server.py,默认用GGUF量化格式加载Qwen2-7B(仅需6GB显存),但支持无缝切换为Ollama或vLLM后端。我试过把模型换成DeepSeek-V2-16B,只需修改config/ai.yaml里的model_path和context_length,其他逻辑完全不用动——因为所有提示词(prompt)都存放在prompts/目录下,按场景分类,比如risk_analysis.j2里写着:
你是一名资深量化分析师,请基于以下回测指标生成专业建议: - 夏普比率:{{ sharpe }} - 最大回撤:{{ max_dd }}% - 胜率:{{ win_rate }}% - 当前市场VIX指数:{{ vix }} 请用中文回答,不超过150字,避免使用“可能”“或许”等模糊词汇。实操心得:首次运行时,它会自动下载Qwen2-7B-GGUF模型(约4.2GB)。如果遇到下载中断,不要删
models/目录重试——直接执行python -m ai.inference.downloader --resume,程序会校验已下载分片的SHA256并续传。这个细节在README里没提,但代码里downloader.py第87行有# Resume logic tested on 3G network with 12 interruptions的注释,是作者在出差高铁上反复验证过的。
办公模块还藏着一个杀手级功能:“跨文档数据联动”。比如你在PPT里插入一张“行业轮动热力图”,同时在Excel日报里也有同样图表。当因子更新触发热力图重绘时,PPT和Excel会同时收到/report/industry_heatmap更新通知,自动刷新——不是靠文件覆盖,而是通过总线广播。我测试时故意关掉Excel进程,再触发更新,5秒后重新打开Excel,发现图表已自动同步,连坐标轴刻度都没变。这种体验,已经接近专业BI工具的水平,却只需要一个Python进程支撑。
5. 交易下单模块:不碰资金安全红线,用“沙盒-桥接-执行”三层隔离保障实盘稳定
看到“交易下单”四个字,我立刻检查了它的权限设计。不出所料,整个交易模块被严格划分为三层:
沙盒层(Sandbox):所有策略生成的信号首先进入内存沙盒,接受风控规则校验。规则引擎用Drools语法编写,示例规则
risk_limit.drl:rule "Single stock position limit" when $s: Signal(stockCode != null, positionSize > 0.15) then $s.reject("单只股票仓位超15%"); end这里
positionSize是占总资产比例,不是绝对值——因为实盘账户总资产会动态变化,系统每5分钟从券商接口拉取最新净值更新风控基准。桥接层(Bridge):通过
bridge/目录下的适配器,将标准化信号转换为券商协议。目前支持:券商 协议类型 是否需客户端 实盘验证 同花顺 TCP长连接 是(需运行iFinD) ✅ 2024Q1实盘 恒生UFT COM组件 是(需安装UFT) ✅ 2024Q2实盘 CTP FIX4.2 否(直连期货公司) ✅ 某期货公司测试环境 聚宽 HTTP API 否 ✅ 模拟盘 关键设计是“协议无关性”。比如下单指令
OrderRequest(symbol="600519.SH", side="BUY", price=1850.0, volume=100),桥接层会自动映射为同花顺的SendOrder("600519", 1, 1850.0, 100)或CTP的ReqOrderInsert(...)。这意味着你换券商时,只需新增一个桥接器,策略代码零修改。执行层(Executor):真正的委托提交。这里做了三重保险:
- 离线签名:所有委托请求在本地用RSA私钥签名,券商端用公钥验签,杜绝中间人篡改;
- 双确认机制:大额委托(>50万元)需二次人工确认,系统弹出带倒计时的GUI窗口,超时自动取消;
- 成交核对:每30秒轮询成交回报,若委托状态为“已报”但120秒内无成交,自动触发撤单并告警。
我实测了同花顺桥接器。当执行python -m trade.executor --symbol 000001.SZ --side BUY --price 10.25 --volume 1000时,Wireshark抓包显示:
- 第1包:向同花顺客户端发送TCP数据(端口20001),payload含签名和加密委托;
- 第2包:同花顺客户端返回ACK,含委托ID
THS20240405102500123; - 第3包(15秒后):同花顺推送成交回报,含实际成交价
10.23和手续费5.12。
整个过程无需浏览器、不调用Web API、不依赖云服务——纯粹的本地进程协作。这也是它敢开源的核心底气:所有敏感操作都在用户可控环境中完成,没有一行代码会把账号密码发往外部服务器。
6. 开源不是终点,而是构建个人工作流生态的起点
这个项目最让我兴奋的,不是它现在能做什么,而是它预留的扩展接口如何降低专业工具的使用门槛。比如它的插件系统plugins/目录,目前只有两个示例:
plugins/tdx_reader/:读取通达信本地数据(.day/.lc5格式),自动转换为pandas DataFrame;plugins/gtss_indicator/:加载GTSS量化指标源码(.dll),通过ctypes调用计算。
但它的plugin_manager.py设计暴露了野心:所有插件必须实现IPlugin接口,包含init(),execute(context),teardown()三个方法,其中context对象预置了总线实例、日志句柄、配置管理器。这意味着你可以:
- 写一个
plugins/patent_analyzer/,调用本地部署的专利NLP模型,自动分析研报中提到的技术是否已被专利保护; - 写一个
plugins/bitcoin_arb/,接入OKX和Binance的WebSocket行情,计算跨交易所BTC价差并生成套利信号; - 甚至写一个
plugins/accio_ppt/,用Accio Work的PPT生成API替换内置模块,实现“AI生成初稿+人工精修”的混合流程。
作者在CONTRIBUTING.md里写得很直白:“Don't ask for features. Build plugins. We merge PRs that add plugin examples, not core features.”(不要提功能需求,请写插件。我们只合并添加插件示例的PR,不合并核心功能修改。)这种治理哲学,把开源项目从“作者维护”变成了“社区共建”。
我自己动手写了第一个插件plugins/deepseek_quant/,封装DeepSeek-V2的量化选股API。关键代码只有23行:
class DeepSeekQuantPlugin(IPlugin): def init(self, config): self.client = DeepSeekClient(api_key=config.get('api_key')) def execute(self, context): # 从总线获取最新行业数据 industry_data = context.bus.get('/data/industry') # 调用DeepSeek模型生成选股建议 result = self.client.select_stocks(industry_data, top_k=10) # 推送结果到总线,供交易模块消费 context.bus.set('/signal/deepseek_buy', result)编译成deepseek_quant_plugin.so后,放入plugins/目录,重启主程序,/signal/deepseek_buy路径就自动可用。整个过程不需要改一行核心代码,也不需要重新编译主程序——这就是插件架构的价值。
最后分享一个血泪教训:项目默认用
uvloop加速异步IO,但在macOS 12上会与某些券商客户端的COM组件冲突(报错OSError: [Errno 43] Protocol not supported)。解决方案不是降级uvloop,而是编辑config/system.yaml,把async_backend: uvloop改为async_backend: asyncio。这个坑我在M1 Mac上踩了7次,直到在GitHub Issues里搜到第142条讨论才找到答案。所以别迷信默认配置,生产环境务必做兼容性测试。
这个工作台不会让你一夜暴富,但它能把每天重复的3小时机械劳动,压缩成一条命令。当别人还在为回测结果截图发愁时,你的PPT已自动生成并邮件发送;当别人手动输入委托单时,你的信号已穿过三层隔离直达交易所。开源的意义,从来不是免费,而是把专业工具的控制权,交还到真正使用者手中。