← 返回编程题库
coding-fill-attribution-by-parent-id简单免费版2000ms未尝试

按父单 ID 归集成交:子单回填到父单

Fill Attribution by Parent Order ID

开始编码

真实的算法执行系统几乎不会把一个大单直接打到交易所。OMS 会把一个父单(例如"在 16:00 之前买 50,000 股 AAPL")切成许多子单,路由到不同场所,被分批成交后以一连串 fill 的形式回流。要看清父单的真实进度——平均成交价、已经做了多少、什么时候开始的、最后一笔在何时——就需要遍历这条 fill 流,把子单的成交按父单聚合回去。

实现 solution(fills) -> dict[str, dict]fills 是一个 dict 列表,每条包含 parent_idqtypricets 四个 key。按 parent_id 分组,返回一个 dict,把每个 parent_id 映射到一个汇总字典,必须包含下列 key:

  • total_filled_qty (float):该父单下所有 fill 的 qty 之和。
  • vwap (float):sum(qty * price) / sum(qty)。当 total_filled_qty == 0 时返回 0.0
  • n_fills (int):该父单下的 fill 数量(qty == 0 也计数)。
  • first_fill_ts (int):该父单下所有 fill 的 ts 最小值。
  • last_fill_ts (int):该父单下所有 fill 的 ts 最大值。

返回字典的 key 顺序必须与每个 parent_id 在输入中首次出现的顺序一致。

例子

fills = [
    {"parent_id": "P1", "qty": 100, "price": 50.0, "ts": 1000},
    {"parent_id": "P1", "qty": 200, "price": 51.0, "ts": 1100},
    {"parent_id": "P2", "qty": 50,  "price": 99.0, "ts": 1050},
]
solution(fills)
# -> {
#     "P1": {"total_filled_qty": 300.0, "vwap": 50.666666666666664, "n_fills": 2,
#            "first_fill_ts": 1000, "last_fill_ts": 1100},
#     "P2": {"total_filled_qty": 50.0,  "vwap": 99.0,                "n_fills": 1,
#            "first_fill_ts": 1050, "last_fill_ts": 1050},
# }
solution([])
# -> {}
fills = [
    {"parent_id": "P9", "qty": 0, "price": 10.0, "ts": 5},
    {"parent_id": "P9", "qty": 0, "price": 12.0, "ts": 7},
]
solution(fills)
# -> {"P9": {"total_filled_qty": 0.0, "vwap": 0.0, "n_fills": 2,
#            "first_fill_ts": 5, "last_fill_ts": 7}}

注意第三个例子:两条 qty == 0 的 fill 仍然算作 fill(n_fills = 2),仍然会刷新 first_fill_ts / last_fill_ts——只是 VWAP 因为没有可加权的数量而退化为 0.0

实践背景

父单/子单的拆单路由是算法执行里的常态:交易员面前的一个父单("按 VWAP 算法,在 16:00 之前买 50,000 股 AAPL")会被算法切成成百个子单,分散打到不同交易所、暗池和时点上。交易所磁带只看得到子单成交,但所有 TCA 报告、盘后对账、实时进度监控都需要把这些 fill 折回父单视角:进度多少、平均价多少、何时开始成交、最后一笔何时打出。这正是 OMS 内部以及每个执行研究 notebook 在做 slippage 和 impact 分析之前必经的一步簿记。

约束条件

  • 0 ≤ len(fills) ≤ 10000。
  • 每条 fill 是一个 dict,包含 `parent_id`(非空字符串)、`qty`(int 或 float,≥ 0)、`price`(float,> 0)、`ts`(int)。
  • 允许 `qty == 0` 的成交——它们会让 `n_fills` 加 1、刷新 `first_fill_ts` / `last_fill_ts`,但对 `total_filled_qty` 和 VWAP 没有贡献。
  • VWAP 定义为 `sum(qty * price) / sum(qty)`;当父单总量为 0 时,返回 `vwap = 0.0`,不要除以零。
  • 返回字典的 key 顺序必须等于每个 `parent_id` 在 `fills` 中首次出现的顺序——不要排序。

样例

Case 1 · statement-example: two parents, one with two fills

输入: [[{"parent_id":"P1","qty":100,"price":50,"ts":1000},{"parent_id":"P1","qty":200,"price":51,"ts":1100},{"parent_id":"P2","qty":50,"price":99,"ts":1050}]]

期望: {"P1":{"total_filled_qty":300,"vwap":50.666666666666664,"n_fills":2,"first_fill_ts":1000,"last_fill_ts":1100},"P2":{"total_filled_qty":50,"vwap":99,"n_fills":1,"first_fill_ts":1050,"last_fill_ts":1050}}

按 parent_id 把 fill 累加;P1 的 VWAP = (100*50 + 200*51)/300 ≈ 50.6667,first_fill_ts=1000,last_fill_ts=1100。

Case 2 · empty: no fills returns empty dict

输入: [[]]

期望: {}

空输入:直接返回空字典 {}。

Case 3 · single fill: trivial single-parent single-fill

输入: [[{"parent_id":"PX","qty":10,"price":100,"ts":42}]]

期望: {"PX":{"total_filled_qty":10,"vwap":100,"n_fills":1,"first_fill_ts":42,"last_fill_ts":42}}

只有一笔 fill,VWAP 等于该 fill 的价格,first_fill_ts == last_fill_ts。

最近提交

还没有提交记录。

编码区

实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

Case 1 · statement-example: two parents, one with two fills

输入: [[{"parent_id":"P1","qty":100,"price":50,"ts":1000},{"parent_id":"P1","qty":200,"price":51,"ts":1100},{"parent_id":"P2","qty":50,"price":99,"ts":1050}]]

期望: {"P1":{"total_filled_qty":300,"vwap":50.666666666666664,"n_fills":2,"first_fill_ts":1000,"last_fill_ts":1100},"P2":{"total_filled_qty":50,"vwap":99,"n_fills":1,"first_fill_ts":1050,"last_fill_ts":1050}}

按 parent_id 把 fill 累加;P1 的 VWAP = (100*50 + 200*51)/300 ≈ 50.6667,first_fill_ts=1000,last_fill_ts=1100。

Case 2 · empty: no fills returns empty dict

输入: [[]]

期望: {}

空输入:直接返回空字典 {}。

Case 3 · single fill: trivial single-parent single-fill

输入: [[{"parent_id":"PX","qty":10,"price":100,"ts":42}]]

期望: {"PX":{"total_filled_qty":10,"vwap":100,"n_fills":1,"first_fill_ts":42,"last_fill_ts":42}}

只有一笔 fill,VWAP 等于该 fill 的价格,first_fill_ts == last_fill_ts。