程序员量化交易实战 05:量化系统最重要的是数据,不是策略
前四篇已经把边界、工程骨架、核心概念和 A 股交易规则铺好了。现在很多人会想是不是终于可以写策略了还不急。量化系统里最容易被低估的部分不是策略而是数据。脏数据、缺数据、错口径数据会让任何策略看起来像有用也会让任何回测失去意义。这一篇开始进入数据层。策略之前先问数据从哪来一个简单的均线策略看起来只需要收盘价。但真落到系统里问题会立刻变多股票列表从哪里来退市股票怎么处理停牌日有没有 K 线成交量单位是股、手还是供应商自定义单位日线是前复权、后复权还是不复权财报数据是否有发布时间滞后数据源失败时要不要 fallbackfallback 数据能不能进入正式回测这些问题如果不在数据层解决策略层就会到处打补丁。数据源不能写死在策略里策略只应该消费统一后的数据不应该知道供应商细节。比如策略想要一段日线它不应该自己去拼 QVeris、东方财富、Tushare 或同花顺的请求参数。策略应该调用统一接口拿到标准化后的MarketBar。ZiQuant 里用DataSourceConfig管理数据源配置class DataSourceConfig(Base): __tablename__ zi_quant_data_source_configs name mapped_column(String(80), uniqueTrue) adapter mapped_column(String(40), indexTrue) enabled mapped_column(Boolean, defaultTrue) priority mapped_column(Integer, default100) config_json mapped_column(JSONB, defaultdict) secret_ref mapped_column(String(200), nullableTrue) last_status mapped_column(JSONB, defaultdict)几个字段很关键。adapter表示供应商类型比如qveris、eastmoney、tushare。priority表示优先级。secret_ref不直接存密钥而是引用环境变量或密钥系统。last_status保存最近一次调用状态方便排障。这样设计以后策略不需要关心谁提供数据。供应商可以替换策略逻辑不动。行情数据要记录来源行情表不是只有价格。ZiQuant 的MarketBar里有source和payloadclass MarketBar(Base): __tablename__ zi_quant_market_bars symbol mapped_column(String(16), indexTrue) trade_date mapped_column(Date, indexTrue) frequency mapped_column(String(16), default1d, indexTrue) open mapped_column(Float) high mapped_column(Float) low mapped_column(Float) close mapped_column(Float) volume mapped_column(Float, default0) amount mapped_column(Float, default0) source mapped_column(String(40), defaultunknown, indexTrue) payload mapped_column(JSONB, defaultdict)如果一条数据来自东方财富另一条来自 QVeris还有一条来自 fallback系统必须能区分。否则后面发现回测异常时很难判断是策略问题还是数据混了口径。数据来源不是审计洁癖。它是排障入口。财报数据有滞后性价格数据通常按交易日更新财报数据不是。财报有报告期也有披露时间。2025 年年报不等于 2025 年最后一个交易日就可用。如果回测时提前使用未来才披露的财报就会出现未来函数。ZiQuant 当前的FinancialReport先保存基础字段class FinancialReport(Base): __tablename__ zi_quant_financial_reports symbol mapped_column(String(16), indexTrue) report_date mapped_column(Date, indexTrue) report_type mapped_column(String(40), defaultincome) revenue mapped_column(Float, nullableTrue) net_profit mapped_column(Float, nullableTrue) roe mapped_column(Float, nullableTrue) source mapped_column(String(40), defaultunknown, indexTrue) payload mapped_column(JSONB, defaultdict)后续我们会继续补披露时间和口径字段。现在先建立原则财报因子不能直接用“报告期”当作“可交易日可见信息”。fallback 数据只能救急不能糊弄开发早期系统可能用 fallback 数据让页面和接口先跑起来。这没问题但 fallback 必须显式标记。ZiQuant 的 README 里已经写清楚真实行情和财报会标记真实来源fallback 数据会显式标记为simulated_fallback。生产质量检查里也会统计 fallback 比例。这条规则非常重要。fallback 可以用于本地开发、界面联调、测试空链路但不能假装成真实行情进入正式策略结论。数据质量要变成接口数据质量不应该靠人打开数据库看几眼。ZiQuant 已经准备了几个接口GET /api/data/quality GET /api/data/provenance POST /api/data/coverage POST /api/stocks/sync POST /api/data/sync POST /api/financials/sync POST /api/financials/coverage这些接口服务于不同问题quality看整体质量覆盖率、新鲜度、fallback 比例。provenance看数据来源各来源多少行最近同步状态如何。coverage看某批股票有没有足够行情。sync系列负责触发股票、行情和财报同步。后面写策略前我们会先用这些接口确认数据能不能支撑回测。数据源接口先定义清楚第五篇先不实现完整供应商 SDK但要把接口边界想清楚。一个数据源适配器至少应该提供三类能力class MarketDataProvider: async def get_stock_basic(self) - list[dict]: ... async def get_daily_bars(self, symbol: str, start: str, end: str) - list[dict]: ... async def get_financial_reports(self, symbol: str, limit: int 8) - list[dict]: ...注意这里返回的还不是最终 ORM 对象而是供应商数据经过第一层解析后的结构。真正写入数据库前还需要标准化、去重、校验和来源标记。数据层最小验收数据层不是写完一个下载脚本就结束。至少要能回答下面几个问题股票主数据有多少只公共股票池有多少只每只股票有多少根日 K最近一根 K 线是哪天多少数据来自真实供应商fallback 比例是多少财报覆盖多少股票同步失败时最近错误是什么ZiQuant 的/ready、/api/data/quality和/api/data/provenance会逐步承担这些检查。可运行基础校验第五篇强调先做数据质量边界因此基础校验会故意放入一行不自洽的 OHLC 数据。当前统一用这条命令复现第 01-08 篇的基础能力uv run python -m scripts.chapter_examples foundation-check本章对应输出如下raw_rows3、clean_rows2、rejected_rows1说明系统没有静默吞掉异常行情。第一阶段不追求数据源多而是先让坏数据有明确拒绝原因。本篇实战任务这一篇先确认现有数据模型和质量接口入口。运行表结构测试cd ~/projects/zi-quant-platform uv run pytest tests/test_services.py -k schema or data_source or data_provenance如果从 GitHub 拉取这一章对应代码git clone https://github.com/ax2/zi-quant-platform.git cd zi-quant-platform git checkout chapter-05 uv sync --extra dev uv run pytest启动服务后看健康状态uv run uvicorn app.main:app --host 127.0.0.1 --port 8092 curl http://127.0.0.1:8092/health curl http://127.0.0.1:8092/ready如果配置了 API Token后续同步类接口要带X-Zi-Api-Token。这一点后面写真实数据同步时会展开。前五篇阶段 review到这里第一组五篇完成了一个小闭环。第 1 篇回答为什么程序员适合做量化定下“研究、回测、模拟盘不真实下单”的边界。第 2 篇搭 Python 项目骨架让 ZiQuant 可以安装、启动、配置和测试。第 3 篇把核心概念落到模型股票、股票池、K 线、因子、策略、回测和模拟盘。第 4 篇把 A 股交易规则落到代码新增了可测试的trading_rules模块。第 5 篇开始数据层强调数据源抽象、来源记录、质量检查和 fallback 标记。这五篇没有重复写“量化不是玄学”这类开场而是逐步把项目从认知边界推进到工程骨架、领域模型、交易规则和数据层入口。后面第二组文章会继续沿着数据层展开PostgreSQL 表设计、股票基础信息表、500 只 A 股股票池、真实行情接入和数据清洗。本章更新与代码仓库本章更新内容明确数据层优先于策略层。梳理数据源抽象、行情来源、财报滞后、fallback 标记和质量接口。完成前五篇阶段 review确认第一组文章风格、边界和项目进度一致。代码仓库https://github.com/ax2/zi-quant-platform本章代码git clone https://github.com/ax2/zi-quant-platform.git cd zi-quant-platform git checkout chapter-05 uv sync --extra dev uv run pytest本篇小结策略之前先做数据。没有来源记录、质量检查、覆盖率和 fallback 标记策略输出就没有可信基础。程序员做量化真正的起点不是写一个买卖公式而是让数据链路可替换、可检查、可复盘。下一篇我们进入 PostgreSQL开始把股票、行情、财报和股票池这些对象稳定地存下来。