如何用SQL或python从股票日数据中抽取出季度数据

企业微信

为什么需要从日数据抽季度数据

做股票或期货策略时,高开低收的日数据太细碎,季度数据却能帮你看见大势。比如你研究一家公司连续20个季度的营收与股价关系,或者测试一个季度调仓的期货趋势策略,都需要把每天的价格浓缩成每季一根K线。手工整理不现实,用SQL或Python自动抽取才是交易员的日常。

季度数据的定义与常见规则

季度划分有两种主流方式:自然季度(1-3月、4-6月、7-9月、10-12月)和按财报季(部分期货品种按合约季)。股票通常用自然季度,每季最后一天的收盘价作为该季收盘价,第一天的开盘价作为季度开盘价,期间最高价与最低价分别取最大值和最小值。成交量则是整个季度累加。

如何用SQL或python从股票日数据中抽取出季度数据

有些策略会使用季度末最后一个交易日的价格,有些用季度内均价。规则要先定清楚,否则抽取出来的数据无法回测。

SQL方案 利用窗口函数与分组

假设有一张日线表 daily_price,字段为 code(股票代码)、trade_date(日期)、open、high、low、close、volume。要抽取每只股票每季度的OHLCV。

第一步,给每行打上季度标签。不同数据库语法略有差异,核心是用年份和季度组合。MySQL可以用 CONCAT(YEAR(trade_date), 'Q', QUARTER(trade_date)),PostgreSQL用 EXTRACT(YEAR FROM trade_date) || 'Q' || EXTRACT(QUARTER FROM trade_date)。

第二步,用窗口函数找出每季第一天和最后一天。以MySQL 8.0为例:


WITH qtr_label AS (

  SELECT *,

         CONCAT(YEAR(trade_date), 'Q', QUARTER(trade_date)) AS qtr,

         ROW_NUMBER() OVER (PARTITION BY code, CONCAT(YEAR(trade_date), 'Q', QUARTER(trade_date)) ORDER BY trade_date) AS rn_first,

         ROW_NUMBER() OVER (PARTITION BY code, CONCAT(YEAR(trade_date), 'Q', QUARTER(trade_date)) ORDER BY trade_date DESC) AS rn_last

  FROM daily_price

)

SELECT 

  code,

  qtr,

  MAX(CASE WHEN rn_first = 1 THEN open END) AS q_open,

  MAX(high) AS q_high,

  MIN(low) AS q_low,

  MAX(CASE WHEN rn_last = 1 THEN close END) AS q_close,

  SUM(volume) AS q_volume

FROM qtr_label

GROUP BY code, qtr

ORDER BY code, qtr;

这里用 MAX(CASE WHEN ...) 提取首日开盘和末日收盘,是因为分组后每季只有一行满足条件。若数据库支持 FIRST_VALUE 和 LAST_VALUE 窗口函数,也可直接取值,但要注意 LAST_VALUE 默认窗口帧问题,需指定 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING。

对于期货,如果主力合约换月,需要先处理连续合约再抽取季度数据,否则季度开盘价可能来自不同合约。

Python方案 用pandas重采样更灵活

拿到日线DataFrame后,把日期设为索引,用 resample('Q') 聚合。pandas的Q代表季度末,Q-DEC为自然季。代码示例如下:


import pandas as pd

df = pd.read_csv('daily_price.csv', parse_dates=['trade_date'])

df = df.set_index('trade_date')

df = df.sort_index()

quarterly = df.resample('Q').agg({

    'open': 'first',

    'high': 'max',

    'low': 'min',

    'close': 'last',

    'volume': 'sum'

})

quarterly.index = quarterly.index.to_period('Q')

如果有多只股票,需要先按code分组再重采样:


def to_quarterly(group):

    return group.resample('Q').agg({'open':'first','high':'max','low':'min','close':'last','volume':'sum'})

quarterly = df.groupby('code').apply(to_quarterly).reset_index()

pandas也能处理非自然季度,比如按期货合约季(3月、6月、9月、12月),只需自定义偏移量或先标记季度再分组。

关键细节与坑

停牌或缺失数据会破坏首日开盘的准确性。若某季第一天没交易,pandas的first会取该季第一个有数据的交易日,SQL的ROW_NUMBER也取第一条记录。多数场景下这符合逻辑,但严格回测时需要标记出真实缺失。

交易日期格式要统一,字符串日期会导致排序错误,必须先转成日期类型。SQL中用STR_TO_DATE或TO_DATE,Python用pd.to_datetime。

期货的夜盘归属问题:国内期货夜盘属于下一交易日,抽取季度数据时要确保日期已经按交易日历调整,否则季度最后一天的收盘价可能落在夜盘时段,造成偏差。

季度数据的频率低,样本量小,回测时容易过拟合。用季度数据做策略,最好结合基本面因子,不要单纯依赖技术指标。

实战中的选择建议

SQL适合在数据库里直接跑,数据量大时效率高,结果可以存入新表供后续查询。Python适合探索性分析,能快速画图、计算收益率、合并财务数据。两者也可以结合:用SQL抽取季度数据存入宽表,再用Python读取做回测。

无论哪种工具,核心逻辑就一条:按季度分组,每组的首开、高、低、末收、量总和。把这五个值抓准,季度数据就抽好了。

转载请注明出处:https://www.lianghuajiaoyi.top/wenzhang/jidu-shuju-gupiao-ri-shuju-1022.html