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.100 | 09:31 |
09:31:00.000 | 09:31 |
09:31:02.000 | 09:32 |
11:29:59.000 | 11:30 |
11:30:00.000 | 11:30 |
11:30:02.800 | 11:30 |
11:30:05.000 | 排除,超过容差 |
12:00:00.000 | 排除,处于午休 |
13:00:01.000 | 13:01 |
13:01:00.000 | 13:01 |
14:59:59.000 | 15:00 |
15:00:00.000 | 15:00 |
15:00:02.000 | 15: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参数,应先核对所用版本的函数说明。
备注:内容仅供参考。
