很多人学量化时,会把注意力放在“策略”上。这当然重要。但如果你真的想长期做研究,只靠零散脚本是不够的。今天一个 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(parents=True, exist_ok=True) 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( symbol=symbol, period="1d", count=count, adjust=adjust, to_dataframe=True, ) # 标准化排序并重置索引 df = df.sort_values("trade_date").reset_index(drop=True) # 写入本地缓存,避免重复请求 df.to_csv(cache_file, index=False) return df def load_quotes(self, symbols: list[str]) -> pd.DataFrame: """获取实时行情""" return self.qd.quotes.get(symbols=symbols, to_dataframe=True)这样做有几个核心好处:
策略代码不直接关心底层 API 细节。
缓存文件极大减少了重复网络请求,回测调参快如闪电。
复权方式(adjust)、数据长度都写在函数参数中,防呆防错。
以后如果要扩展或更换数据源,只需要修改这个 data_loader.py 即可,其他策略无需改动任何一行代码。
3. 策略层:每个策略写成可复用函数
比如经典的双均线策略 strategies/ma_cross.py:
import 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", encoding="utf-8") as f: config = json.load(f) # 2. 从数据层取数(自动命中本地缓存) loader = QuantDashDataLoader() df = loader.load_daily_bars( config["symbol"], count=config["count"], adjust=config["adjust"], ) # 3. 策略信号生成 df = ma_cross_signal( df, short_window=config["short_window"], long_window=config["long_window"], ) # 4. 执行标准化回测 result, metrics = backtest_single_asset( df, fee=config["fee"], slippage=config["slippage"], ) print("=== 回测绩效 ===") print(metrics)这样你每次实验的输入都被清晰、固化地记录了下来。当你以后回看结果时,再也不会面对一堆不知道用什么参数跑出来的“孤儿”图表而抓狂。
6. 报告层:保存结果,而不是只打印
每次回测运行结束后,将这三样东西保存到同一个实验文件夹中:
配置快照(config.json)
回测指标(metrics.json)
收益曲线及明细(result.csv + equity.png)
from pathlib import Path import json import matplotlib.pyplot as plt # 根据实验名建立专属报告夹 run_dir = Path("reports/runs/ma_cross_600519") run_dir.mkdir(parents=True, exist_ok=True) with open(run_dir / "config.json", "w", encoding="utf-8") as f: json.dump(config, f, ensure_ascii=False, indent=2) with open(run_dir / "metrics.json", "w", encoding="utf-8") as f: json.dump(metrics, f, ensure_ascii=False, indent=2) result.to_csv(run_dir / "result.csv", index=False) # 绘制净值图并保存 result.set_index("trade_date")["equity"].plot(figsize=(10, 5)) plt.tight_layout() plt.savefig(run_dir / "equity.png", dpi=160) 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, period="1d", count=800, adjust="forward", to_dataframe=True) - 批量 K 线:qd.klines.batch(symbols, period="1d", count=800, adjust="forward", to_dataframe=True) - 实时行情:qd.quotes.get(symbols=symbols, to_dataframe=True) - 全量 A 股:qd.quotes.get(universes="CN_Stock", to_dataframe=True) - 全量 ETF:qd.quotes.get(universes="CN_ETF", to_dataframe=True) - 五档盘口: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 Key:https://quantdash.net/。
第三步:查阅开发细节。更多高频行情、多市场 Tick 及实时深度五档盘口接口参数请参考:https://docs.quantdash.net/。