PAYATHON 2026

DolphinDB 如何按交易时段和收盘容差聚合非均匀快照为分钟线?

数据阿明

先过滤允许的交易时间,再将容差范围内收盘后的时间截到对应收盘时刻。然后用 bar(..., 1m, closed="right") 生成分钟标签,按证券和标签计算 stdp(LastPrice)。如果要求每个证券每天固定输出 240 行,还需要补齐没有快照的分钟。

为什么不能直接使用 minute(DateTime) + 1m?

无条件增加一分钟会把整分钟记录推到下一根 bar。例如,09:31:00.000 应属于 (09:30, 09:31],标签仍应为 09:31。

DolphinDB 的 bar 支持 closed="right":整分钟时间保留当前边界,其他时间取所在窗口的右端点。这样可以直接表达左开右闭的分钟窗口,收盘容差则需要另行处理。参见 bar 官方文档。

先过滤,再截断并聚合

原问题没有指定容差数值。下面取 tolMs = 3000,允许收盘后最多 3 秒,包含恰好到达容差上限的记录。这个示例值可以保留 11:30:02.800 和 15:00:02.000,同时排除 11:30:05.000。

代码假设 DateTime 已使用与题目交易时段一致的市场本地时间,输入表为已创建的 mock。

// 收盘容差,单位:毫秒
// 示例值,实际应由业务规则确定
tolMs = 3000

src = select DateTime,
             date(DateTime) as TradeDate,
             time(DateTime) as TickTime,
             SecurityID,
             LastPrice
      from mock

// 先筛选两个允许区间,并标记所属时段的收盘时间
valid = select *,
               iif(TickTime < 13:00:00.000,
                   11:30:00.000,
                   15:00:00.000) as SessionEnd
        from src
        where (TickTime > 09:30:00.000
               and TickTime <= (11:30:00.000 + tolMs))
           or (TickTime > 13:00:00.000
               and TickTime <= (15:00:00.000 + tolMs))

// 仅为生成标签截断时间,保留原始 DateTime
labeled = select DateTime,
                 SecurityID,
                 LastPrice,
                 concatDateTime(
                     TradeDate,
                     bar(
                         iif(TickTime > SessionEnd,
                             SessionEnd,
                             TickTime),
                         1m,
                         closed="right"
                     )
                 ) as BarTime
          from valid

bars = select stdp(LastPrice) as LastPriceStdp
       from labeled
       group by SecurityID, BarTime
       order by SecurityID, BarTime

这里的过滤区间是 (09:30:00.000, 11:30:03.000] 和 (13:00:00.000, 15:00:03.000]。午休数据在 valid 这一步就被排除,只有上午收盘后的容差区间作为例外保留。

过滤必须在截断之前完成。如果先把所有晚于收盘的时间都截到收盘时刻,12:00:00.000 等无效记录也可能被误收进最后一根 bar。

TickTime 和 SessionEnd 都使用毫秒精度的 TIME。在这个类型上增加整数 3000 表示增加 3000 毫秒。concatDateTime 将日期与 TIME 合并后返回 TIMESTAMP,因此分组标签包含完整日期,不会把不同交易日的同一分钟合并。参见 时间运算文档和 concatDateTime 文档。

示例数据应该如何归属?

按上述 3 秒容差规则,各时间的预期归属如下。表中仅展示时间部分,实际 BarTime 包含日期。

原始时间bar 标签或处理结果
09:30:03.10009:31
09:31:00.00009:31
09:31:02.00009:32
11:29:59.00011:30
11:30:00.00011:30
11:30:02.80011:30
11:30:05.000排除,超过容差
12:00:00.000排除,处于午休
13:00:01.00013:01
13:01:00.00013:01
14:59:59.00015:00
15:00:00.00015:00
15:00:02.00015:00
15:01:30.000排除,超过容差

可以查看中间表,核对原始时间和标签的对应关系:

select DateTime, SecurityID, BarTime
from labeled
order by DateTime, SecurityID

检查边界时,还应覆盖 09:30:00.000、13:00:00.000、容差上限以及上限之后 1 毫秒。按当前规则,两个开盘整点都被排除,容差上限被保留。

如何固定输出每天 240 根 bar?

group by 只会产生实际存在记录的分组。题目中的稀疏模拟数据不会自动得到每个证券 240 行,需要先生成完整分钟网格,再把聚合结果左连接上去。

下面接续前面的代码,为 src 中出现过的每个“日期、证券”组合补齐 240 个分钟标签。空分钟保留 NULL。

// 取输入中出现过的日期、证券组合
pairs = select distinct TradeDate, SecurityID from src

// 上午 120 个标签,下午 120 个标签
// 转为 TIME,使后续 BarTime 与聚合结果同为 TIMESTAMP
minuteGrid = table(
    time(join(09:31m..11:30m, 13:01m..15:00m)) as BarClock
)

grid = select SecurityID,
              concatDateTime(TradeDate, BarClock) as BarTime
       from cj(pairs, minuteGrid)

fullBars = select *
           from lj(grid, bars, `SecurityID`BarTime)
           order by SecurityID, BarTime

cj 用于生成日期、证券与分钟标签的笛卡尔积,参见 cross join 文档。如果某个证券某天完全没有输入记录,pairs 就无法推导出这个组合,需要从业务维护的交易日和证券范围生成网格。

使用时还需要注意什么?

  • 不均匀采样不妨碍时间分组。每条快照按自身时间归属窗口即可,无须先补成严格的 3 秒序列。
  • stdp(LastPrice) 按逐条快照等权计算总体标准差,NULL 不参与计算,也不会根据相邻快照的时间间隔加权。参见 stdp 官方文档。不要为了凑齐分钟而先补造价格记录,这会改变统计结果。
  • 补齐网格后的 NULL 表示没有对应聚合结果,需要与零波动区分。在确定业务规则前,不应直接填成 0。
  • 这里处理的是时间字段的收盘超界。如果记录实际到达较晚,但 DateTime 仍处于正常交易时段,批量查询会按该字段正常归组。实时流中的等待、触发和历史结果更新需要另行设计。
  • 以上写法依据所引官方函数文档,未在你的 DolphinDB 实例中执行。如果实例不接受 closed 参数,应先核对所用版本的函数说明。

备注:内容仅供参考。