公司动态
给自己搭一个量化研究工作台:QuantDash 管数据,Codex 管代码
很多人学量化时会把注意力放在“策略”上。这当然重要。但如果你真的想长期做研究只靠零散脚本是不够的。今天一个 test.py明天一个 ma_final_v3.py后天一个 真的最终版.ipynb时间久了你会发现自己根本不知道哪个结果可信。一个更好的做法是从一开始就给自己搭一个轻量的量化研究工作台。它不需要很复杂也不需要上来就做成专业系统。你只需要把数据获取、策略代码、回测结果、图表、配置文件放到固定位置让每一次研究都能复现。这篇文章将详细拆解一个适合个人使用的高效工作台结构QuantDash负责提供和缓存多市场行情数据Codex或 Cursor/DeepSeek 等 AI 编程助手帮你写策略、调试、重构和补全测试代码。1. 为什么需要工作台零散脚本的最大问题不是丑而是不可复现。你可能会遇到以下典型痛点【痛点 Before】策略回测结果不错但隔了几天忘了当时用的具体参数。净值图表保存在本地但不知道对应哪一个版本的策略代码。临时换了一组股票池旧的研究结果和新的回测数据直接混在一起。某天修改了均线计算逻辑或数据复权方式历史回测绩效全部失效。AI 编程助手帮你重构了代码但你没有记录任何改动的缘由。【优雅实现 After】量化研究要长期迭代必须让每次实验留下确定性的痕迹。一个标准的个人轻量工作台可以按以下结构组织quant-workbench/ ├── data/ │ ├── raw/ # 存放原始下载的 CSV 缓存 │ └── processed/ # 存放清洗后的因子特征数据 ├── strategies/ │ ├── ma_cross.py # 双均线信号生成 │ └── rsi_reversal.py # RSI 逆势信号 ├── reports/ │ ├── charts/ │ └── runs/ # 每次实验运行的配置与指标快照 ├── configs/ │ ├── ma_cross.json │ └── rsi_reversal.json ├── notebooks/ # 仅用于临时灵感探索不放核心业务代码 ├── data_loader.py # 数据加载与缓存 ├── backtest.py # 通用回测计算模块 ├── main.py # 主运行入口 └── requirements.txt这不是唯一的标准但它能帮你把“数据、信号、回测、配置、报告”这五个层面划分得清清楚楚。2. 数据层统一从 QuantDash 取数先写一个高内聚的数据模块比如 data_loader.py。在量化研究中我们最忌讳每次跑回测都去重复请求远端 API不仅耗时还容易在批量回测时被限流。利用 QuantDash 的高稳定 API 结合本地 CSV 缓存可以完美解决这一问题from pathlib import Path import pandas as pd from quantdash import QuantDash class QuantDashDataLoader: def __init__(self, cache_dir: str data/raw): # 初始化客户端会自动读取环境变量 QUANTDASH_API_KEY self.qd QuantDash() self.cache_dir Path(cache_dir) self.cache_dir.mkdir(parentsTrue, exist_okTrue) def load_daily_bars( self, symbol: str, count: int 800, adjust: str forward, use_cache: bool True, ) - pd.DataFrame: 加载日线K线优先读取本地缓存 cache_file self.cache_dir / f{symbol}_{count}_{adjust}.csv if use_cache and cache_file.exists(): return pd.read_csv(cache_file) # 统一的 K 线获取接口前复权 df self.qd.klines.get( symbolsymbol, period1d, countcount, adjustadjust, to_dataframeTrue, ) # 标准化排序并重置索引 df df.sort_values(trade_date).reset_index(dropTrue) # 写入本地缓存避免重复请求 df.to_csv(cache_file, indexFalse) return df def load_quotes(self, symbols: list[str]) - pd.DataFrame: 获取实时行情 return self.qd.quotes.get(symbolssymbols, to_dataframeTrue)这样做有几个核心好处策略代码不直接关心底层 API 细节。缓存文件极大减少了重复网络请求回测调参快如闪电。复权方式adjust、数据长度都写在函数参数中防呆防错。以后如果要扩展或更换数据源只需要修改这个 data_loader.py 即可其他策略无需改动任何一行代码。3. 策略层每个策略写成可复用函数比如经典的双均线策略 strategies/ma_cross.pyimport pandas as pd def ma_cross_signal( df: pd.DataFrame, short_window: int 20, long_window: int 60, ) - pd.DataFrame: 生成双均线交叉交易信号 df df.copy() df[ma_short] df[close].rolling(short_window).mean() df[ma_long] df[close].rolling(long_window).mean() # 均线多头排列时信号为 1否则为 0 df[signal] (df[ma_short] df[ma_long]).astype(int) return df注意这里策略函数只负责生成 signal信号绝对不参与计算收益。把“信号生成”和“回测计算”彻底解耦是一个至关重要的专业习惯。否则你每个策略文件里都会充斥着大量的收益率计算、滑点扣除、绩效画图代码越写越臃肿极难维护。4. 回测层统一处理收益、成本和指标在 backtest.py 中写一个通用的回测引擎import numpy as np import pandas as pd def calc_metrics(equity: pd.Series, returns: pd.Series) - dict: 计算核心量化绩效指标 total_return equity.iloc[-1] / equity.iloc[0] - 1 annual_return (1 total_return) ** (252 / len(equity)) - 1 drawdown equity / equity.cummax() - 1 sharpe 0 if returns.std() 0 else returns.mean() / returns.std() * np.sqrt(252) return { total_return: total_return, annual_return: annual_return, max_drawdown: drawdown.min(), sharpe: sharpe, } def backtest_single_asset( df: pd.DataFrame, fee: float 0.0003, slippage: float 0.0002, ) - tuple[pd.DataFrame, dict]: 对单只标的运行标准回测含手续费与滑点 df df.copy().dropna(subset[signal]) # 【避坑红线】信号后移一天保证收盘后次日开盘买入规避未来函数 df[position] df[signal].shift(1).fillna(0) df[ret] df[close].pct_change().fillna(0) df[strategy_ret] df[position] * df[ret] # 计算交易摩擦 df[turnover] df[position].diff().abs().fillna(0) df[cost] df[turnover] * (fee slippage) df[strategy_ret] df[strategy_ret] - df[cost] # 净值曲线 df[equity] (1 df[strategy_ret]).cumprod() metrics calc_metrics(df[equity], df[strategy_ret]) return df, metrics有了这个模块以后你写任何单标的策略如突破、MACD、RSI 等只要在策略函数里生成标准的 signal 序列就能直接扔进这个回测引擎里。这就是工作台的意义把重复劳动变少把研究动作变标准。5. 配置层每次实验都保存参数不要把任何选股参数、均线窗口死写在 Python 代码里。我们统一使用外部 JSON 文件例如 configs/ma_cross.json来进行实验配置{ name: ma_cross_600519, symbol: 600519.SH, count: 800, adjust: forward, short_window: 20, long_window: 60, fee: 0.0003, slippage: 0.0002 }主运行程序 main.py 只需要读取这个配置并依次调用我们的工作台各层组件import json from data_loader import QuantDashDataLoader from strategies.ma_cross import ma_cross_signal from backtest import backtest_single_asset # 1. 加载本次实验的配置文件 with open(configs/ma_cross.json, r, encodingutf-8) as f: config json.load(f) # 2. 从数据层取数自动命中本地缓存 loader QuantDashDataLoader() df loader.load_daily_bars( config[symbol], countconfig[count], adjustconfig[adjust], ) # 3. 策略信号生成 df ma_cross_signal( df, short_windowconfig[short_window], long_windowconfig[long_window], ) # 4. 执行标准化回测 result, metrics backtest_single_asset( df, feeconfig[fee], slippageconfig[slippage], ) print( 回测绩效 ) print(metrics)这样你每次实验的输入都被清晰、固化地记录了下来。当你以后回看结果时再也不会面对一堆不知道用什么参数跑出来的“孤儿”图表而抓狂。6. 报告层保存结果而不是只打印每次回测运行结束后将这三样东西保存到同一个实验文件夹中配置快照config.json回测指标metrics.json收益曲线及明细result.csv equity.pngfrom pathlib import Path import json import matplotlib.pyplot as plt # 根据实验名建立专属报告夹 run_dir Path(reports/runs/ma_cross_600519) run_dir.mkdir(parentsTrue, exist_okTrue) with open(run_dir / config.json, w, encodingutf-8) as f: json.dump(config, f, ensure_asciiFalse, indent2) with open(run_dir / metrics.json, w, encodingutf-8) as f: json.dump(metrics, f, ensure_asciiFalse, indent2) result.to_csv(run_dir / result.csv, indexFalse) # 绘制净值图并保存 result.set_index(trade_date)[equity].plot(figsize(10, 5)) plt.tight_layout() plt.savefig(run_dir / equity.png, dpi160) plt.close()运行后一个确定性的实验目录就诞生了reports/runs/ma_cross_600519/ ├── config.json ├── metrics.json ├── result.csv └── equity.png这对于长期维护和复盘非常舒服。你可以随时翻阅、随时对比也可以直接打包发给 AI 帮助你做更深一步的绩效归因和漏洞分析。7. AI 在工作台里能做什么当你的项目结构目录树、文件调用职责、参数标准稳定下来之后大语言模型如 Codex 等的作用会被无限放大。你可以非常轻松地对 AI 发出以下指令【新增策略】请在 strategies/ 下新增一个 rsi_reversal.py。要求生成 signal 字段RSI 小于 30 时买入信号设为 1大于 50 时卖出信号设为 0。不要在策略函数里计算收益保持和 ma_cross.py 完全一致的函数和入参风格。【扩展回测引擎特征】请给 backtest.py 中的 backtest_single_asset 函数增加一个可选的 benchmark 参数。如果传入基准价格序列请自动计算超额收益Alpha和信息比率Information Ratio并并入 metrics 返回。【全面防作弊自查】请检查我整个 quant-workbench/ 目录下的回测代码重点帮我检查信号signal、持仓position和收益率ret的对齐关系看看有没有任何未来函数或偷看收盘价的可能。如果有请立刻指出并给出修改方案。最关键的是你不再让 AI 去面对一团无章可循的散乱脚本而是在一个具有严谨标准的高内聚项目里工作。这样 AI 能够以极高的成功率理解你的开发意图并产生极高质量的代码改动。8. 一个适合发给 AI 的项目提示词当你新开一个 AI 会话时建议把这段作为工作台的全局说明发给它这是我的个人量化研究工作台。 数据源使用 QuantDash - QuantDash 官网https://quantdash.net/ - Python SDK 文档https://docs.quantdash.net/ 常用接口 - 日K线qd.klines.get(symbol, period1d, count800, adjustforward, to_dataframeTrue) - 批量 K 线qd.klines.batch(symbols, period1d, count800, adjustforward, to_dataframeTrue) - 实时行情qd.quotes.get(symbolssymbols, to_dataframeTrue) - 全量 A 股qd.quotes.get(universesCN_Stock, to_dataframeTrue) - 全量 ETFqd.quotes.get(universesCN_ETF, to_dataframeTrue) - 五档盘口qd.depth.get(symbol) 项目约定 1. data_loader.py 只负责从 QuantDash 取数和本地 CSV 缓存。 2. strategies/ 里的各个策略函数只负责生成 signal 字段绝对不计算收益。 3. backtest.py 统一负责处理 position、returns、cost、equity、metrics 的核心回测逻辑。 4. 所有的 signal 都必须 shift(1) 后才能转化为下一天的持仓 position以彻底避免未来函数。 5. 每次运行完都要在 reports/runs/[实验名]/ 下保存 config.json、metrics.json、result.csv、equity.png。 6. 不要把 API key 暴露在明文代码里使用 QUANTDASH_API_KEY 环境变量自动读取。有了这段上下文你后续让 AI 新增策略、写多标的回测、甚至参数扫描时它都会表现得像一个深谙你习惯的熟手助理。9. 工作台的下一步从脚本到小应用当你这个命令行的工作台稳定运行、积累了几十次策略回测之后你可以顺理成章地借助 Streamlit 做一个简单的本地可视化面板参数面板用 Streamlit 拖拽滑动条选择均线窗口、止损比例自动重写 config.json。图表展示用 Streamlit 直接绘制动态交互的净值曲线、回撤曲线、持仓占比图。实验历史列表读取 reports/runs做一个历史回测夏普比率排行榜。盘中行情监控接入 QuantDash 的实时行情计算当前自选股的多周期指标自动生成预警提示。量化研究是一门长期的工程实践。先从一个最简单的、数据与逻辑解耦的轻量级工作台开始。不要一开始就追求庞大完美追求**“让每次实验都有据可查让每次回测都真实可信”**你的研发和迭代效率就会获得质的飞跃。结语与“三步走”落地指引第一步获取完整源码。请访问官方开源托管仓库获取本文完整 Demo 代码架构及进阶配置https://github.com/quantdash-net/QuantDash请认准官方 quantdash-net 组织欢迎 Star 支持。第二步申请专属密钥。注册获取您的个人免费/生产级 API Keyhttps://quantdash.net/。第三步查阅开发细节。更多高频行情、多市场 Tick 及实时深度五档盘口接口参数请参考https://docs.quantdash.net/。