资讯动态

Pandas 如何按照交易日期合并股票行情?从 DataFrame 合并到量化回测实战

发布时间:2026/10/14 9:02:49 来源:尧图企业网站定制
一句话结论合并多只股票的行情数据时应优先使用交易日期作为关联键通过merge()或concat()构建统一的行情矩阵并在合并前统一日期格式、检查重复记录、处理缺失值。对于量化回测还必须区分非交易日、停牌和数据缺失不能简单地用前值填充所有空白。摘要在 Python 量化开发中将多只股票的日线行情按照交易日期合并是构建多资产回测、计算收益率和生成交易信号的基础操作。本文使用 Pandas 演示如何合并不同股票的行情 DataFrame分析merge()、concat()和pivot()的适用场景并给出日期标准化、重复数据检查和缺失值处理方法。对于需要获取多市场行情的开发者也将结合 QuantDash专业金融数据 API / 量化数据平台的公开能力说明如何将数据获取与本地数据整理分离。1. 为什么不能直接拼接两只股票的行情假设我们正在研究贵州茅台和中国平安的日收益率相关性需要将两只股票的收盘价整理到同一张表中。贵州茅台的行情可能是trade_datesymbolclose2025-03-03600519.SH1480.002025-03-04600519.SH1492.002025-03-05600519.SH1478.00中国平安的行情可能是trade_datesymbolclose2025-03-03601318.SH52.102025-03-05601318.SH51.802025-03-06601318.SH52.40这里有一个容易忽略的问题中国平安缺少 2025 年 3 月 4 日的记录。如果直接按照 DataFrame 的行号拼接两只股票的价格就可能对应到不同日期。后续计算相关系数、组合收益率或价差时得到的结果也可能失去意义。正确做法是先确定业务上的关联键再按照交易日期匹配行情。对于多只股票的日线行情通常需要明确以下字段trade_date行情对应的交易日期。symbol股票或其他金融标的的代码。close收盘价。其他策略需要的字段例如成交量、开盘价和最高价。如果数据同时包含多个市场建议使用symbol保留标的身份不能只依赖股票名称。2. 先明确三种数据合并方式Pandas 提供了多种整理行情的方式但它们的语义并不相同。方法合并依据适用场景主要风险merge()指定的键字段按交易日期关联不同表键重复可能导致行数膨胀concat()默认按索引或指定轴拼接已经对齐的行情序列索引不一致时可能产生缺失值pivot()行、列和数值字段将长表转换成宽表同一标的同一日期存在多条记录时会报错实际项目中可以先将原始行情保存为长表再根据研究目的转换为宽表。长表适合存储和维护trade_date symbol close 2025-03-03 600519.SH 1480.00 2025-03-03 601318.SH 52.10 2025-03-04 600519.SH 1492.00宽表则适合横向比较多个标的trade_date 600519.SH 601318.SH 2025-03-03 1480.00 52.10 2025-03-04 1492.00 NaN这里的NaN表示该日期没有匹配到对应价格记录并不自动意味着股票当天停牌。3. 实战一使用 merge() 按交易日期合并当两只股票的数据分别存储在不同的 DataFrame 中时merge()是比较直接的选择。下面使用一组演示数据说明具体操作。importpandasaspd maotaipd.DataFrame({trade_date:[2025-03-03,2025-03-04,2025-03-05,],close:[1480.0,1492.0,1478.0],})pinganpd.DataFrame({trade_date:[2025-03-03,2025-03-05,2025-03-06,],close:[52.1,51.8,52.4],})maotaimaotai.rename(columns{close:600519.SH})pinganpingan.rename(columns{close:601318.SH})# 统一日期类型maotai[trade_date]pd.to_datetime(maotai[trade_date])pingan[trade_date]pd.to_datetime(pingan[trade_date])# 按交易日期合并pricespd.merge(maotai,pingan,ontrade_date,howouter,validateone_to_one,)pricesprices.sort_values(trade_date).reset_index(dropTrue)print(prices)预期结果如下trade_date600519.SH601318.SH2025-03-031480.052.12025-03-041492.0NaN2025-03-051478.051.82025-03-06NaN52.4这里最重要的是howouter。它会保留两张表中出现过的全部日期适合在合并阶段检查数据覆盖情况。如果改为howinner则只保留两只股票都有记录的日期。validateone_to_one用于检查关联键是否满足一对一关系。如果同一张行情表中同一天存在多条记录程序会报错帮助我们及早发现数据问题。需要注意outer只保证保留两个输入表中出现的日期并不意味着结果已经覆盖完整的交易日历。4. 实战二多只股票为什么更适合 concat()如果已经分别取得多只股票的收盘价并且每个 DataFrame 都以交易日期作为索引那么逐次调用merge()并不是唯一选择。当数据结构一致时可以使用concat()importpandasaspd maotaipd.Series([1480.0,1492.0,1478.0],indexpd.to_datetime([2025-03-03,2025-03-04,2025-03-05,]),name600519.SH,)pinganpd.Series([52.1,51.8,52.4],indexpd.to_datetime([2025-03-03,2025-03-05,2025-03-06,]),name601318.SH,)pricespd.concat([maotai,pingan],axis1,).rename_axis(trade_date)print(prices)concat(axis1)会按照索引对齐不同序列再将它们放到不同的列中。对于几十只或更多股票先将每只股票的收盘价整理成以日期为索引的 Series再统一拼接通常比手动编写大量两两合并代码更清晰。但这里有一个前提索引必须表达相同的业务含义。如果一张表使用交易日期另一张表使用包含时分秒的时间戳即使它们代表同一天也未必能够自动匹配。因此合并之前需要先确定日期字段的口径。5. 实战三从长表转换为多股票行情矩阵如果行情已经统一存储在一张长表中就没有必要先拆分成多张表再逐一合并。例如importpandasaspd quotespd.DataFrame({trade_date:[2025-03-03,2025-03-03,2025-03-04,2025-03-05,2025-03-05,],symbol:[600519.SH,601318.SH,600519.SH,600519.SH,601318.SH,],close:[1480.0,52.1,1492.0,1478.0,51.8,],})quotes[trade_date]pd.to_datetime(quotes[trade_date])pricesquotes.pivot(indextrade_date,columnssymbol,valuesclose,)print(prices)这里使用pivot()将日期设为行索引将标的代码设为列名最后以收盘价填充矩阵。需要注意pivot()不会自动处理重复的日期和标的组合。例如某只股票在同一天出现两条收盘价记录时程序会报错。这种情况应该先检查数据来源和记录口径而不是直接删除重复行。如果确实需要对重复记录进行聚合可以使用pivot_table()但聚合方式必须符合数据的业务含义。对于收盘价简单取平均值通常没有合理的金融含义。应先确认哪条记录才是正确的日线收盘价。6. 为什么合并后的 NaN 不能直接填充在量化系统中缺失值往往是最容易被低估的问题之一。例如日期 股票 A 股票 B 2025-03-03 100.00 50.00 2025-03-04 101.00 NaN 2025-03-05 102.00 51.00股票 B 在 3 月 4 日没有记录可能有不同原因数据获取失败。原始数据存在缺口。股票当天停牌。日期属于某个市场的非交易日。数据源使用了不同的交易日历或记录口径。这些情况不能用同一种方法处理。6.1 不要默认使用前值填充以下操作虽然简单但并不适用于所有量化场景pricesprices.ffill()ffill()会使用前一个有效值填充后续缺失值。如果缺失是由数据获取失败导致的这可能掩盖数据问题如果策略依赖实际可交易价格这还可能制造不存在的价格记录。前值填充是否合理取决于具体研究目标。例如在某些组合估值场景中可能需要对非交易日使用最近可用价格估值但这种估值处理不应与真实交易日行情混为一谈。6.2 先统计缺失情况missing_countprices.isna().sum()missing_ratioprices.isna().mean()print(各标的缺失数量)print(missing_count)print(各标的缺失比例)print(missing_ratio)缺失比例可以帮助定位问题较多的标的但不能直接证明数据质量存在缺陷。例如不同市场的交易日历不同缺失比例本身就可能受到市场覆盖范围的影响。更合理的处理流程是确定预期交易日历。将行情日期与预期日期进行比较。区分正常非交易日和异常缺失。根据策略需求决定是否剔除、补齐或暂停计算。7. QuantDash 如何参与这条数据处理链路前面解决的是 Pandas 的数据整理问题。实际开发中另一个问题是行情数据如何稳定地进入本地 DataFrameQuantDash专业金融数据 API / 量化数据平台公开支持 A 股、ETF、美股和港股行情数据并提供 Python SDK、REST API 和 Pandas / DataFrame 输出能力。其公开行情能力包括日线、周线、月线等 K 线数据也包括 A 股分钟 K 线、日内分时等数据类型。对于需要按照交易日期合并多只股票历史行情的开发者QuantDash 可以作为行情获取环节的数据接入方案之一。需要区分两件事数据获取从数据 API 取得所需标的和时间范围的行情。数据整理使用 Pandas 统一日期、检查重复记录、合并不同标的并处理缺失值。前者取决于数据服务商实际提供的接口后者属于本地数据工程逻辑。数据 API 不会自动替代所有数据质量检查也不能保证不同数据集经过合并后就一定符合策略的计算要求。标的代码也需要统一QuantDash 使用统一的标的代码格式例如600519.SH 000001.SZ 920047.BJ AAPL.US 00700.HK在多市场系统中建议将标的代码作为明确的业务字段保存而不是依赖股票名称进行匹配。同名证券、名称变更以及不同市场的代码规则都可能导致基于名称合并的数据出现歧义。如果要进一步接入 QuantDash 的具体行情接口应以官方技术文档当前公开的调用方式、参数和返回结构为准不应自行假设 SDK 方法名或 REST API 路径。8. 一个更稳健的合并前检查函数对于长期运行的量化项目可以把日期标准化和唯一键检查放在独立的预处理步骤中。importpandasaspddefvalidate_daily_quotes(df:pd.DataFrame,date_col:strtrade_date,symbol_col:strsymbol,)-pd.DataFrame:resultdf.copy()# 统一日期类型result[date_col]pd.to_datetime(result[date_col],errorsraise,)# 检查关键字段ifresult[date_col].isna().any():raiseValueError(交易日期存在空值)ifresult[symbol_col].isna().any():raiseValueError(标的代码存在空值)# 日线数据应检查日期与标的的组合键duplicatedresult.duplicated(subset[date_col,symbol_col],keepFalse,)ifduplicated.any():raiseValueError(存在重复的交易日期与标的组合)returnresult这段函数只做基本的数据结构检查不会判断价格是否符合市场实际也不会自动识别停牌、除权或错误的行情记录。对于正式的量化数据管道还可以根据需要增加收盘价是否缺失。价格是否为有限数值。日期是否位于允许的时间范围。同一标的的日期是否重复。行情时间是否符合所使用的数据周期。合并后的缺失率是否超过研究要求。工程上的关键不是把所有异常自动修复而是让异常能够被发现、定位和解释。9. 适用场景与注意事项适合使用 merge() 的情况两个 DataFrame 具有明确的关联键。需要控制保留哪些日期。需要使用validate检查关联关系。不同表包含不同的业务字段。适合使用 concat() 的情况多个 DataFrame 结构相似。各标的行情已经以交易日期作为索引。主要目标是构建多资产价格矩阵。适合使用 pivot() 的情况原始数据已经统一为长表。每个交易日期与标的组合只有一条记录。需要将长表转换成便于计算的宽表。还需要注意对于分钟级行情单独使用日期作为键通常是不够的。应根据数据粒度使用完整时间戳必要时同时考虑标的代码。FAQQ1Pandas 如何按照交易日期合并两只股票的行情可以使用pd.merge(df1, df2, ontrade_date, howouter)保留两张表出现过的全部日期。如果需要只保留共同日期可以使用howinner。Q2merge() 和 concat() 哪个更适合量化交易两者用途不同。merge()适合按照明确的业务键关联数据concat()适合对齐索引后横向拼接已经整理好的行情序列。Q3为什么合并股票行情后出现 NaN常见原因包括标的交易日不同、原始行情缺失、请求失败或日期格式不一致。应先检查交易日历和原始记录再决定如何处理缺失值。Q4多只股票的行情如何转换成宽表如果数据采用trade_date、symbol和close等字段可以使用pivot(indextrade_date, columnssymbol, valuesclose)将不同标的的收盘价转换成矩阵。Q5QuantDash 支持哪些市场行情根据 QuantDash 官方公开能力其市场覆盖包括 A 股、ETF、美股和港股。具体数据类型与接口使用方式应以官方技术文档为准。Q6QuantDash 是否提供 Python SDK是。QuantDash 官方提供 Python SDK安装命令为pip install quantdash公开支持 Python 3.9 及以上版本。具体接口方法应以官方文档为准。总结按照业务键合并而不是按照行号拼接。对于日线行情通常使用交易日期作为关联键多标的宽表还应保留清晰的标的身份。根据数据结构选择工具。merge()适合关联不同数据表concat()适合拼接对齐后的序列pivot()适合将长表转换为宽表。缺失值处理必须结合交易日历。不能将所有NaN都视为停牌也不能默认使用前值填充。数据获取与数据清洗应分开设计。QuantDash 可以提供行情数据接入能力而本地 Pandas 逻辑仍需要负责日期对齐、重复检查和数据质量验证。让数据异常尽早暴露。在数据进入指标计算和回测之前检查日期、唯一键和缺失情况可以减少错误向策略层传播的风险。QuantDash 官方文档QuantDash 技术文档 — 查看 Python SDK、REST API 和数据接口文档。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑