程序员量化交易实战 05:量化系统最重要的是数据,不是策略
2026/6/21 6:48:49 网站建设 项目流程

前四篇已经把边界、工程骨架、核心概念和 A 股交易规则铺好了。

现在很多人会想:是不是终于可以写策略了?还不急。量化系统里最容易被低估的部分不是策略,而是数据。脏数据、缺数据、错口径数据,会让任何策略看起来像有用,也会让任何回测失去意义。

这一篇开始进入数据层。

策略之前先问数据从哪来

一个简单的均线策略看起来只需要收盘价。

但真落到系统里,问题会立刻变多:

  • 股票列表从哪里来?
  • 退市股票怎么处理?
  • 停牌日有没有 K 线?
  • 成交量单位是股、手,还是供应商自定义单位?
  • 日线是前复权、后复权,还是不复权?
  • 财报数据是否有发布时间滞后?
  • 数据源失败时要不要 fallback?
  • fallback 数据能不能进入正式回测?

这些问题如果不在数据层解决,策略层就会到处打补丁。

数据源不能写死在策略里

策略只应该消费统一后的数据,不应该知道供应商细节。

比如策略想要一段日线,它不应该自己去拼 QVeris、东方财富、Tushare 或同花顺的请求参数。策略应该调用统一接口,拿到标准化后的MarketBar

ZiQuant 里用DataSourceConfig管理数据源配置:

class DataSourceConfig(Base): __tablename__ = "zi_quant_data_source_configs" name = mapped_column(String(80), unique=True) adapter = mapped_column(String(40), index=True) enabled = mapped_column(Boolean, default=True) priority = mapped_column(Integer, default=100) config_json = mapped_column(JSONB, default=dict) secret_ref = mapped_column(String(200), nullable=True) last_status = mapped_column(JSONB, default=dict)

几个字段很关键。

adapter表示供应商类型,比如qveriseastmoneytusharepriority表示优先级。secret_ref不直接存密钥,而是引用环境变量或密钥系统。last_status保存最近一次调用状态,方便排障。

这样设计以后,策略不需要关心谁提供数据。供应商可以替换,策略逻辑不动。

行情数据要记录来源

行情表不是只有价格。

ZiQuant 的MarketBar里有sourcepayload

class MarketBar(Base): __tablename__ = "zi_quant_market_bars" symbol = mapped_column(String(16), index=True) trade_date = mapped_column(Date, index=True) frequency = mapped_column(String(16), default="1d", index=True) open = mapped_column(Float) high = mapped_column(Float) low = mapped_column(Float) close = mapped_column(Float) volume = mapped_column(Float, default=0) amount = mapped_column(Float, default=0) source = mapped_column(String(40), default="unknown", index=True) payload = mapped_column(JSONB, default=dict)

如果一条数据来自东方财富,另一条来自 QVeris,还有一条来自 fallback,系统必须能区分。否则后面发现回测异常时,很难判断是策略问题,还是数据混了口径。

数据来源不是审计洁癖。它是排障入口。

财报数据有滞后性

价格数据通常按交易日更新,财报数据不是。

财报有报告期,也有披露时间。2025 年年报不等于 2025 年最后一个交易日就可用。如果回测时提前使用未来才披露的财报,就会出现未来函数。

ZiQuant 当前的FinancialReport先保存基础字段:

class FinancialReport(Base): __tablename__ = "zi_quant_financial_reports" symbol = mapped_column(String(16), index=True) report_date = mapped_column(Date, index=True) report_type = mapped_column(String(40), default="income") revenue = mapped_column(Float, nullable=True) net_profit = mapped_column(Float, nullable=True) roe = mapped_column(Float, nullable=True) source = mapped_column(String(40), default="unknown", index=True) payload = mapped_column(JSONB, default=dict)

后续我们会继续补披露时间和口径字段。现在先建立原则:财报因子不能直接用“报告期”当作“可交易日可见信息”。

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_rows=3clean_rows=2rejected_rows=1说明系统没有静默吞掉异常行情。第一阶段不追求数据源多,而是先让坏数据有明确拒绝原因。

本篇实战任务

这一篇先确认现有数据模型和质量接口入口。

运行表结构测试:

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,开始把股票、行情、财报和股票池这些对象稳定地存下来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询