如何根据研究的起止时间截取分钟级行情?Python 实战与 QuantDash 数据处理指南

做分钟级量化研究时,我们经常需要分析一个明确的时间窗口:例如开盘后前 30 分钟、收盘前最后一小时,或者某个交易日中一段特定的行情。

这类任务看起来只是对时间进行筛选,实际上有三个需要解决的问题:

  • 如何获取覆盖研究时间段的分钟 K 线?
  • 如何准确地按照起止时间保留数据?
  • 如何确保截取后的数据没有遗漏、重复或意外包含研究区间之外的行情?

如果处理不当,即使后续的指标计算和策略逻辑完全正确,研究结果仍可能出现偏差。

本文使用 Python、Pandas 和 QuantDash Python SDK,介绍一套从行情获取到时间窗口截取的实用流程。

一、先明确:研究区间不等于数据查询数量

假设我们需要研究贵州茅台在某个交易日 14:00 至 14:30 的分钟级行情。

如果直接获取最近 30 根一分钟 K 线,并不一定能得到这段时间的数据。

原因很简单:count=30 表示按接口定义获取指定数量的 K 线,而不是指定开始和结束时间。返回的数据可能来自其他时段,甚至跨越不同交易日。

因此,应当区分两个概念:

  • 数据获取范围:从数据服务中请求哪些行情。
  • 研究时间范围:最终允许哪些行情进入分析。

前者决定能拿到什么数据,后者决定哪些数据参与研究。

对于有明确起止时间的任务,推荐先获取覆盖研究窗口的数据,再使用 Pandas 执行精确筛选。

二、使用 QuantDash 获取分钟级行情

安装 SDK:

bash 复制代码
pip install quantdash pandas

在使用之前,需要准备有效的 QuantDash API Key,并通过环境变量配置:

bash 复制代码
# macOS / Linux
export QUANTDASH_API_KEY="your_api_key"

Windows PowerShell:

powershell 复制代码
$env:QUANTDASH_API_KEY = "your_api_key"

下面先获取一个交易日的分钟行情:

python 复制代码
import pandas as pd
from quantdash import QuantDash

qd = QuantDash()

symbol = "600519.SH"

# 获取当日分钟级行情
df = qd.klines.intraday(
    symbol,
    period="1m",
    to_dataframe=True,
)

if df.empty:
    raise ValueError("未获取到分钟行情,请检查标的代码、API Key 和接口权限")

print(df.head())
print(df.columns.tolist())

这里使用的是 QuantDash 的日内行情接口。

需要注意,intraday() 面向当日分钟级行情。如果研究对象是过去某一天,不能简单地认为该接口会自动返回指定历史日期的数据。

对于历史研究,应根据官方 K 线接口的参数说明,使用支持相应时间范围的查询方式获取数据。

另外,分钟行情的返回字段应以实际接口响应为准。后续示例使用 trade_time 作为时间列,因此在执行前需要确认返回数据中存在该字段。

三、用 Pandas 精确截取研究时间

假设已经获得覆盖研究窗口的分钟行情,接下来就可以使用 Pandas 进行筛选。

例如,研究区间为:

  • 开始时间:2026 年 6 月 22 日 14:00:00
  • 结束时间:2026 年 6 月 22 日 14:30:00

代码如下:

python 复制代码
import pandas as pd

# df 为已经获取的分钟 K 线 DataFrame

if "trade_time" not in df.columns:
    raise ValueError("行情数据缺少 trade_time 时间字段")

df = df.copy()

# 统一时间字段类型
df["trade_time"] = pd.to_datetime(
    df["trade_time"],
    errors="raise",
)

# 排序,确保后续分析按照时间顺序进行
df = df.sort_values("trade_time").reset_index(drop=True)

start_time = pd.Timestamp("2026-06-22 14:00:00")
end_time = pd.Timestamp("2026-06-22 14:30:00")

if start_time >= end_time:
    raise ValueError("开始时间必须早于结束时间")

# 使用左闭右开区间:[start_time, end_time)
mask = (
    (df["trade_time"] >= start_time)
    & (df["trade_time"] < end_time)
)

research_df = df.loc[mask].copy()

if research_df.empty:
    raise ValueError("指定研究区间内没有行情数据")

print("截取数据条数:", len(research_df))
print("实际开始时间:", research_df["trade_time"].min())
print("实际结束时间:", research_df["trade_time"].max())

这里最重要的不是筛选语法,而是时间区间的定义。

示例使用的是左闭右开区间:

t \\in \[t_{\\text{start}}, t_{\\text{end}})

也就是包含开始时间,但不包含结束时间。

这种方式特别适合相邻时间窗口的拼接。例如:

  • 第一段:14:00 至 14:30。
  • 第二段:14:30 至 15:00。

第一段不包含 14:30,第二段则从 14:30 开始,因此不会因为边界定义不一致而重复纳入同一个时间戳。

如果研究规则明确要求包含结束时间,可以改为:

python 复制代码
mask = (
    (df["trade_time"] >= start_time)
    & (df["trade_time"] <= end_time)
)

但应当注意,时间戳恰好等于结束时间的记录是否存在,以及它代表哪一段行情,都需要结合数据源的 K 线时间定义判断。

不能只因为结束时间写成 14:30,就认定一定存在一根 14:30 的完整分钟 K 线。

四、起止时间需要转换成时间戳吗?

在 Pandas 中筛选时间时,通常直接使用 Timestamp 就可以。

但如果数据接口要求使用 Unix 时间戳,就需要进行转换。

例如,某个接口要求以毫秒表示的时间戳:

python 复制代码
from datetime import datetime, timezone

start_dt = datetime(
    2026, 6, 22, 14, 0,
    tzinfo=timezone.utc,
)

end_dt = datetime(
    2026, 6, 22, 14, 30,
    tzinfo=timezone.utc,
)

start_ms = int(start_dt.timestamp() * 1000)
end_ms = int(end_dt.timestamp() * 1000)

print(start_ms)
print(end_ms)

这段代码演示的是时间戳转换,并不代表所有行情接口都要求毫秒时间戳,也不代表所有市场都应使用 UTC 作为研究时间。

对于中国 A 股,研究者通常会以交易所本地时间定义交易窗口。若接口要求 UTC 时间戳,应先明确时间所属时区,再进行转换。

例如,2026 年 6 月 22 日 14:00 的北京时间,对应的是 UTC 06:00,而不是 UTC 14:00。

因此,不能直接将一个没有时区信息的本地时间转换成时间戳,然后假设得到的结果一定符合服务端的要求。

建议:先确认接口的时间参数单位、时区和边界规则,再构造请求参数。

QuantDash 的具体 K 线查询参数应以官方技术文档为准,不要仅凭其他数据源的调用习惯推断。

五、截取完成后,还要检查什么?

成功返回 DataFrame 并不意味着数据一定适合研究。

至少可以检查以下几个方面。

1. 检查实际时间边界

python 复制代码
print("研究窗口开始:", start_time)
print("研究窗口结束:", end_time)

print("数据实际开始:", research_df["trade_time"].min())
print("数据实际结束:", research_df["trade_time"].max())

assert research_df["trade_time"].min() >= start_time
assert research_df["trade_time"].max() < end_time

如果断言失败,说明筛选结果不符合预期,应检查时间字段类型、时区或筛选条件。

2. 检查重复时间戳

python 复制代码
duplicate_count = research_df["trade_time"].duplicated().sum()

print("重复时间戳数量:", duplicate_count)

如果同一标的在同一周期下出现重复时间戳,需要进一步确认是数据重复、合并操作造成的重复,还是接口返回结构中存在其他维度。

不要在没有查明原因之前直接删除重复记录。

3. 检查 OHLC 数据

python 复制代码
price_columns = ["open", "high", "low", "close"]

missing_columns = [
    column
    for column in price_columns
    if column not in research_df.columns
]

if missing_columns:
    raise ValueError(f"缺少价格字段:{missing_columns}")

missing_price_count = (
    research_df[price_columns]
    .isna()
    .any(axis=1)
    .sum()
)

print("价格字段存在缺失的记录数:", missing_price_count)

这段代码只检查字段和空值,并不能证明所有行情记录都符合金融数据规则。

如果后续研究涉及成交量、成交额或其他指标,还应根据实际返回字段增加对应检查。

六、几个容易忽略的边界问题

问题 可能的影响 建议
只用 count 获取数据 返回的数据不一定覆盖目标时间 先确认查询方式能覆盖研究区间
直接使用字符串比较时间 字段格式不一致时可能出错 统一转换为时间类型
混用本地时间与 UTC 截取到错误的时段 明确时区后再比较
忽略结束时间边界 相邻窗口可能出现重复数据 统一采用左闭右开或其他明确规则
把交易时段之外的缺口当作数据缺失 错误地判断行情不完整 根据交易所交易日历判断
直接删除重复记录 可能掩盖数据处理问题 先查明重复原因

还有一个值得强调的问题:分钟 K 线不一定严格按照每分钟连续生成。

午间休市、周末、法定节假日、停牌以及交易所交易规则,都可能影响时间序列的连续性。

例如,A 股上午交易结束后,时间序列不会因为中午休市而自然产生一整段有效成交分钟线。

因此,检查分钟数据完整性时,不应简单地认为相邻记录的时间差必须等于一分钟。

结语

根据研究起止时间截取分钟行情,关键在于把数据获取与研究窗口定义分开处理。

首先获取覆盖目标区间的数据,再统一时间字段、明确时区和边界规则,最后使用 Pandas 精确筛选并检查结果。

如果研究的是历史某一天,应优先确认历史 K 线接口的时间查询能力;如果研究的是当前交易日,则可以根据需要使用日内行情接口。

这样做的价值不只是让代码正确运行,更是确保后续的收益率、技术指标和策略回测建立在符合研究定义的数据之上。

QuantDash 官方文档

相关推荐
晨曦中的暮雨3 小时前
限流算法——固定窗口、滑动窗口、漏桶、令牌桶
java·开发语言·算法
喵了几个咪4 小时前
GoWind CMS 拆解:在 Go 里给 TipTap 搭一套 Yjs 协同后端
开发语言·后端·golang
程序员清风4 小时前
数据处理脚本工程化:日志、配置、命令行与测试
服务器·开发语言·数据库
淡海水5 小时前
07-02-源码-.NET Core GC分配路径
算法·unity·c#·.netcore·gc·1024程序员节
在世修行6 小时前
智学文枢:页面切换与跨页数据预填
python·桌面应用·signal·1024程序员节·pyside·跨页跳转·状态预填
2601_962885726 小时前
如何用 Python 把 K 线重采样成任意周期?
开发语言·python
打工仔折腾 AI6 小时前
在 Ubuntu ARM64 部署 Q1 V3.0 MuJoCo 模型:加载、单步推理与执行器响应解析
linux·运维·人工智能·python·深度学习·ubuntu·性能优化
yyuuuzz6 小时前
跨境站点日志时区混乱的排查记录
运维·服务器·网络·云计算·aws·1024程序员节
西安同步电子6 小时前
GNSS信号模拟器在车规导航ISO26262证中的测试方案
1024程序员节