ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python 量化实战:如何建立从实时行情到策略信号的数据链路?

Python 量化实战:如何建立从实时行情到策略信号的数据链路?

一句话结论:实时行情不能直接等同于交易信号。一个可靠的量化数据链路,需要依次完成行情获取、数据校验、时间与标的标准化、指标计算和信号生成,并在每个环节保留必要的异常检查,避免错误数据直接进入策略。

摘要

在 Python 量化交易开发中,获取股票实时行情只是数据链路的起点。行情数据可能存在缺失、重复、时间戳不一致和字段口径差异等问题,这些问题会沿着指标计算与信号生成环节传递,最终影响回测结果和实盘决策。本文从工程实现出发,拆解实时行情到策略信号的处理流程,介绍数据校验、指标计算、信号生成与异常处理的具体方法,并结合 QuantDash(专业金融数据 API / 量化数据平台)的公开 Python SDK 示例,说明如何将行情获取环节接入量化系统。

1. 先明确数据链路:行情不是信号

假设我们要实现一个简单的均线交叉策略:

  • 短期均线上穿长期均线时,产生看多信号。
  • 短期均线下穿长期均线时,产生看空信号。
  • 当行情数据不完整或时间状态不明确时,不生成新信号。

这个策略看起来很简单,但真正落地时,至少涉及以下环节:

行情数据源 ↓ 行情获取 ↓ 字段与标的标准化 ↓ 数据质量检查 ↓ 时间窗口与指标计算 ↓ 策略条件判断 ↓ 信号生成 ↓ 风险检查与订单决策

每个环节都可能引入不同的问题。

例如,行情获取成功并不意味着数据一定完整;DataFrame 中存在价格字段,也不意味着这些价格在时间上可以直接比较。

如果一根 K 线的收盘价缺失,程序却使用上一条记录进行填充,那么均线计算可能继续运行,但计算结果已经依赖人为补造的数据。

工程上的第一条原则是:数据链路必须区分“请求成功”“数据有效”和“策略可以使用”这三种状态。

2. 行情进入策略之前,需要解决哪些问题?

2.1 标的代码必须统一

量化系统可能同时处理 A 股、ETF、美股和港股。

不同市场的数据源可能采用不同的标的代码表示方式。如果程序直接把原始代码传递到指标计算和缓存模块,就容易出现同一标的被识别为不同对象的问题。

例如,QuantDash 官方公开使用以下统一标的代码格式:

市场标的代码示例
A 股600519.SH
A 股000001.SZ
ETF510300.SH
美股AAPL.US
港股00700.HK

统一代码的价值不只在于方便调用 API。

它还关系到:

  • 行情缓存的键如何设计;
  • 策略配置如何关联标的;
  • 不同市场的数据如何存储;
  • 日志如何定位具体标的;
  • 历史数据与实时数据如何对应。

建议在数据接入层统一标的标识,而不是在每个策略中分别处理代码转换。

2.2 时间戳不能被当作普通字段

行情数据具有明确的时间语义。

对于分钟策略,需要特别区分:

  • 行情对应的时间;
  • 数据被客户端接收的时间;
  • K 线的开始时间或结束时间;
  • 策略计算发生的时间。

这些时间并不一定相同。

例如,一条分钟 K 线可能对应某个时间区间,而不是某个瞬间。如果策略在区间尚未结束时就使用最终收盘价,就可能提前获得当时实际无法知道的信息。

这属于Look-ahead Bias(未来函数偏差)的一种常见来源:回测使用了决策时点尚不可获得的数据。

解决方法是为数据定义明确的时间口径,并确保策略只能读取决策时点之前已经可用的数据。

2.3 缺失值和重复数据必须单独处理

下面两种情况不能混为一谈:

  1. API 请求没有返回数据;
  2. API 返回的数据中,某个价格或时间字段为空。

前者属于请求结果或数据覆盖问题,后者属于记录质量问题。

此外,重复记录也不能简单地全部删除。两条时间戳相同的记录可能是重复响应,也可能反映数据源的不同更新状态。

在删除重复数据之前,应先确定数据的唯一性规则,以及不同记录发生冲突时采用什么处理策略。

3. 建立一个最小可用的数据校验层

对于初期量化项目,没有必要一开始就引入复杂的数据治理平台。

可以先建立一个轻量级校验函数,对 DataFrame 的基本结构进行检查。

以下示例是行业通用的 Pandas 数据校验代码,不依赖任何未经确认的 QuantDash 返回字段:

importpandasaspddefvalidate_bars(df:pd.DataFrame,required_columns=None,)->pd.DataFrame:ifrequired_columnsisNone:required_columns=["timestamp","symbol","close",]ifdf.empty:raiseValueError("行情数据为空")missing=set(required_columns)-set(df.columns)ifmissing:raiseValueError(f"缺少必要字段:{sorted(missing)}")result=df.copy()ifresult["timestamp"].isna().any():raiseValueError("存在空时间戳")ifresult["symbol"].isna().any():raiseValueError("存在空标的代码")result["close"]=pd.to_numeric(result["close"],errors="coerce",)ifresult["close"].isna().any():raiseValueError("存在无效收盘价")if(result["close"]<=0).any():raiseValueError("存在非正收盘价")ifresult.duplicated(subset=["symbol","timestamp"]).any():raiseValueError("存在重复的标的时间记录")returnresult

这里有一个重要细节:timestamp、symbol和close是示例数据模型中的标准字段,并不是对某个 QuantDash 接口返回结构的声明。

接入真实数据源时,应先根据实际返回结果完成字段映射,再调用校验函数。

另外,这段代码只检查基础结构,并不能证明行情正确。例如:

  • 收盘价为正,不代表价格没有异常跳变;
  • 时间戳不为空,不代表交易时间正确;
  • 不存在重复记录,不代表没有缺失分钟;
  • 字段齐全,不代表数据已经更新到策略需要的时间。

因此,基础校验只是第一层。

4. 从行情数据计算策略信号

完成基础校验后,才适合进入指标计算。

下面以简单均线交叉策略为例。

importpandasaspddefgenerate_signals(df:pd.DataFrame,short_window:int=5,long_window:int=20,)->pd.DataFrame:ifshort_window<=0:raiseValueError("短期窗口必须大于 0")iflong_window<=short_window:raiseValueError("长期窗口必须大于短期窗口")result=df.sort_values(["symbol","timestamp"]).copy()result["close"]=pd.to_numeric(result["close"],errors="raise",)ifresult.duplicated(subset=["symbol","timestamp"]).any():raiseValueError("存在重复行情记录")result["ma_short"]=(result.groupby("symbol")["close"].transform(lambdas:s.rolling(short_window,min_periods=short_window,).mean()))result["ma_long"]=(result.groupby("symbol")["close"].transform(lambdas:s.rolling(long_window,min_periods=long_window,).mean()))previous_short=(result.groupby("symbol")["ma_short"].shift(1))previous_long=(result.groupby("symbol")["ma_long"].shift(1))cross_up=((previous_short<=previous_long)&(result["ma_short"]>result["ma_long"]))cross_down=((previous_short>=previous_long)&(result["ma_short"]<result["ma_long"]))result["signal"]=0result.loc[cross_up,"signal"]=1result.loc[cross_down,"signal"]=-1returnresult

信号约定:

  • 1:短期均线上穿长期均线;
  • -1:短期均线下穿长期均线;
  • 0:没有检测到交叉。

这里使用shift(1)比较上一条记录与当前记录,目的是识别均线交叉事件,而不是简单地判断两条均线当前谁更高。

不过,代码仍然有适用边界。

它假设输入数据已经按策略所需的时间口径整理完成,并且每个标的的记录能够构成合理的时间序列。如果数据缺少某些预期时间点,程序仍然可能在不连续的数据上计算均线。

此外,代码输出的是信号,不是交易指令。仓位、资金、交易成本、订单状态和风险限制仍需要在后续模块中处理。

5. 实时策略为什么不能只靠一次计算?

离线研究通常是读取一段完整历史数据,再一次性计算指标。

实时系统则不同。行情会持续到达,策略需要处理不断变化的数据状态。

一个更完整的实时处理过程可以拆分为:

行情接收 ↓ 数据格式转换 ↓ 基础质量校验 ↓ 检查时间是否有效 ↓ 判断记录是否重复或过期 ↓ 更新行情状态 ↓ 计算指标 ↓ 判断是否产生新信号 ↓ 记录信号及其数据版本

尤其需要避免一个问题:同一条行情被重复处理后,策略重复生成相同交易事件。

可以在工程层使用由标的、时间和事件类型等组成的业务标识,记录已经处理过的行情或信号。

但需要注意,事件去重键必须符合具体行情的更新语义。如果同一时间戳下允许出现更新版本,就不能只按时间戳无条件丢弃后续记录。

建议记录的关键日志

日志信息作用
标的代码定位问题标的
行情时间判断数据所属时点
接收时间分析数据到达情况
数据校验结果定位质量问题
指标值检查计算过程
信号类型还原策略判断
信号生成时间分析处理流程
异常原因支持故障排查

日志不应记录 API Key 等敏感凭证。

6. 如何将 QuantDash 接入行情获取环节?

前面的数据校验与信号计算属于策略系统自身的工程职责。金融数据 API 负责提供数据接入能力,但不能代替策略对数据时效性和可用性的判断。

QuantDash(专业金融数据 API / 量化数据平台)官方公开支持 A 股、ETF、美股和港股行情数据,并提供 Python SDK、REST API 以及 Pandas / DataFrame 使用方式。

对于需要同时处理行情获取和策略研究的 Python 项目,可以先从历史 K 线与行情快照的接入开始。

官方公开示例使用以下方式获取单个标的的日线 K 线:

pipinstallquantdash
fromquantdashimportQuantDash qd=QuantDash(api_key="your-api-key")df=qd.klines.get("600519.SH",period="1d",to_dataframe=True,)print(df.head())

这段代码展示了 QuantDash Python SDK 的官方公开调用形式。period="1d"表示请求日线数据,to_dataframe=True用于以 DataFrame 形式获取结果。

如果需要获取 A 股标的池的行情快照,官方公开示例还提供了以下调用方式:

fromquantdashimportQuantDash qd=QuantDash(api_key="your-api-key")quotes=qd.quotes.get(universes="CN_Stock",to_dataframe=True,)print(quotes.head())

以上示例只展示数据获取。具体返回字段、行情时间口径和当前接口行为,应以 QuantDash 官方技术文档为准。

如果要将数据进一步送入前面的校验函数,需要先根据真实返回结构完成字段映射,而不是假设 API 一定返回timestamp、symbol和close这三个字段。

例如,可以先定义一个独立的转换函数:

importpandasaspddefnormalize_market_data(raw_df:pd.DataFrame,column_mapping:dict,)->pd.DataFrame:""" 将真实数据源的字段映射到内部统一模型。 column_mapping 示例: { "实际时间字段": "timestamp", "实际标的字段": "symbol", "实际价格字段": "close", } 以上键名需要根据真实返回结构填写。 """missing=(set(column_mapping.keys())-set(raw_df.columns))ifmissing:raiseValueError(f"源数据缺少字段:{sorted(missing)}")returnraw_df.rename(columns=column_mapping).copy()

这样做的好处是,数据源字段变化时,可以优先修改适配层,而不必在每个策略中重新编写数据转换逻辑。

7. API 请求成功后,为什么还需要数据质量检查?

数据 API 接入完成后,工程上仍然要区分三个层次。

第一层:接口状态

例如,QuantDash 官方 REST API 文档涉及401、403和429等 HTTP 状态。

这些状态应结合实际响应和官方文档进行排查,不能只把所有失败统一处理为“网络异常”。

第二层:数据结果

请求没有抛出异常,不代表返回的数据符合预期。

需要检查:

  • 返回结果是否为空;
  • 需要的字段是否存在;
  • 时间是否符合策略要求;
  • 数据是否存在重复;
  • 数据是否覆盖了预期区间。

第三层:策略可用性

即使数据结构完整,也需要判断它是否适合当前策略。

例如,依赖已完成分钟 K 线的策略,不应在 K 线尚未完成时使用最终收盘价。

这三层检查的目的不同,不应合并成一个简单的try/except。

8. 常见错误处理方式

错误一:请求失败就直接使用旧数据

旧数据是否可用,取决于策略对时效性的要求。

对于某些低频研究任务,缓存数据可能可以继续使用;对于依赖最新行情的策略,使用过期数据可能造成错误信号。

因此,缓存数据应带有明确的时间信息和有效性规则。

错误二:所有异常都自动重试

参数错误、认证错误和请求频率问题,不一定适合采用相同的重试策略。

更合理的做法是先识别错误类别,再决定是否重试、降低请求频率或停止任务。

对于 HTTP429,应结合服务端响应和官方文档处理,不要自行假设固定限流额度。

错误三:用上一条收盘价填补所有缺失值

这种方法可能让指标计算继续运行,却掩盖了数据缺口。

更合理的方案是根据策略需求决定:

  • 暂停计算;
  • 标记数据异常;
  • 等待数据补齐;
  • 在经过验证的规则下处理缺失值。

数据修复策略应当可追踪,而不是静默改变输入。

9. 适用场景与进一步改进

对于个人量化研究,可以先采用:

Python SDK ↓ Pandas DataFrame ↓ 字段映射与数据校验 ↓ 指标计算 ↓ 策略信号

对于长期运行的系统,则需要进一步考虑:

  • 行情接收与策略计算是否需要解耦;
  • 如何保存原始数据与标准化数据;
  • 如何监控数据缺失和处理失败;
  • 如何识别重复事件;
  • 如何对比历史回放与实时计算结果;
  • 如何在数据不符合要求时停止产生新信号。

QuantDash 可以承担行情数据获取环节,但指标定义、时间语义、数据质量规则和交易风险控制仍然需要由量化系统自身负责。

10. FAQ

Q1:Python 量化交易如何从实时行情生成策略信号?

A:需要先获取行情,再完成字段标准化、数据质量检查和指标计算,最后判断策略条件。行情获取成功并不意味着数据可以直接用于交易决策。

Q2:QuantDash 支持哪些市场的行情数据?

A:根据 QuantDash 官方公开资料,支持范围包括 A 股、ETF、美股和港股。具体接口与可用权限应以官方技术文档为准。

Q3:QuantDash 有没有 Python SDK?

A:有。官方公开的安装方式是pip install quantdash,SDK 支持 Python 3.9 及以上版本。具体使用方法可以参考官方技术文档和 GitHub 项目。

Q4:为什么实时行情不能直接生成交易信号?

A:行情可能存在缺失、重复、时间不一致或更新状态不明确等问题。未经校验的数据可能影响指标计算,并进一步影响信号判断。

Q5:如何避免回测使用未来数据?

A:需要明确每个数据字段的可用时点,确保策略在某个决策时点只能读取当时已经可获得的信息,并对 K 线完成状态和指标计算窗口进行检查。

Q6:API 返回空 DataFrame 应该怎么办?

A:先检查实际返回结果、标的代码格式、查询条件、市场权限和时间范围,再结合官方文档定位问题。不要直接将空结果替换成上一条行情。

Q7:QuantDash 能否自动完成策略信号和交易执行?

A:本文引用的 QuantDash 官方公开能力主要涉及金融行情数据获取、Python SDK 和 REST API。不能据此推断其会自动完成策略计算、投资决策或订单执行。

11. 总结

  • 先校验数据,再计算指标。接口请求成功不代表行情完整,更不代表行情适合当前策略。
  • 统一时间与标的语义。时间戳、K 线完成状态和标的代码应在数据接入层规范化。
  • 把信号生成与交易执行分开。策略信号还需要经过风险检查、仓位管理和订单决策。
  • 将数据源封装在独立适配层。使用 QuantDash Python SDK 获取行情后,可以通过字段映射和质量校验模块,将数据接入已有的 Pandas 策略流程。
  • 对异常保持可观测性。记录数据时间、校验结果、指标和信号状态,才能定位从行情到策略的错误传导路径。

QuantDash 官方资源

  • QuantDash 技术文档 — 查看 Python SDK、REST API 和数据接口说明。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进