from itertools import count
def _is_pos_int(v):
return isinstance(v, int) and not isinstance(v, bool) and v > 0
def process_events(events):
trades = []
rejected = []
# Resting book stored as dicts keyed by id; we keep a global sequence
# for time priority.
seq = count()
used_ids = set() # all ids ever accepted as new orders
resting = {} # id -> order dict {id, side, price, qty, seq}
bids = {} # id -> order (side buy)
asks = {} # id -> order (side sell)
def reject(idx, event, reason):
rejected.append({"input_index": idx, "event": event, "reason": reaso...
全文を表示 ▼
from itertools import count
def _is_pos_int(v):
return isinstance(v, int) and not isinstance(v, bool) and v > 0
def process_events(events):
trades = []
rejected = []
# Resting book stored as dicts keyed by id; we keep a global sequence
# for time priority.
seq = count()
used_ids = set() # all ids ever accepted as new orders
resting = {} # id -> order dict {id, side, price, qty, seq}
bids = {} # id -> order (side buy)
asks = {} # id -> order (side sell)
def reject(idx, event, reason):
rejected.append({"input_index": idx, "event": event, "reason": reason})
def sorted_asks():
# ascending price, then seq
return sorted(asks.values(), key=lambda o: (o["price"], o["seq"]))
def sorted_bids():
# descending price, then seq
return sorted(bids.values(), key=lambda o: (-o["price"], o["seq"]))
def can_cross_buy(buy_is_market, buy_price, ask_price):
if buy_is_market:
return True
return ask_price <= buy_price
def can_cross_sell(sell_is_market, sell_price, bid_price):
if sell_is_market:
return True
return bid_price >= sell_price
def fillable_qty_buy(is_market, price, qty):
remaining = qty
for ask in sorted_asks():
if remaining <= 0:
break
if not can_cross_buy(is_market, price, ask["price"]):
break
remaining -= min(remaining, ask["qty"])
return qty - remaining
def fillable_qty_sell(is_market, price, qty):
remaining = qty
for bid in sorted_bids():
if remaining <= 0:
break
if not can_cross_sell(is_market, price, bid["price"]):
break
remaining -= min(remaining, bid["qty"])
return qty - remaining
def execute_buy(order_id, is_market, price, qty):
remaining = qty
while remaining > 0:
book_asks = sorted_asks()
if not book_asks:
break
best = book_asks[0]
if not can_cross_buy(is_market, price, best["price"]):
break
tq = min(remaining, best["qty"])
trades.append({
"buy_id": order_id,
"sell_id": best["id"],
"price": best["price"],
"qty": tq,
"taker_id": order_id,
"maker_id": best["id"],
})
remaining -= tq
best["qty"] -= tq
if best["qty"] == 0:
del asks[best["id"]]
resting.pop(best["id"], None)
return remaining
def execute_sell(order_id, is_market, price, qty):
remaining = qty
while remaining > 0:
book_bids = sorted_bids()
if not book_bids:
break
best = book_bids[0]
if not can_cross_sell(is_market, price, best["price"]):
break
tq = min(remaining, best["qty"])
trades.append({
"buy_id": best["id"],
"sell_id": order_id,
"price": best["price"],
"qty": tq,
"taker_id": order_id,
"maker_id": best["id"],
})
remaining -= tq
best["qty"] -= tq
if best["qty"] == 0:
del bids[best["id"]]
resting.pop(best["id"], None)
return remaining
for idx, event in enumerate(events):
if not isinstance(event, dict):
reject(idx, event, "event is not a dict")
continue
etype = event.get("type")
if etype not in ("new", "cancel"):
reject(idx, event, "invalid or missing type")
continue
if etype == "cancel":
if "id" not in event:
reject(idx, event, "cancel missing id")
continue
cid = event["id"]
if cid not in resting:
reject(idx, event, "cancel for unknown or non-resting id")
continue
o = resting.pop(cid)
if o["side"] == "buy":
bids.pop(cid, None)
else:
asks.pop(cid, None)
continue
# new order
if "id" not in event:
reject(idx, event, "new order missing id")
continue
oid = event["id"]
side = event.get("side")
order_type = event.get("order_type")
qty = event.get("qty")
if side not in ("buy", "sell"):
reject(idx, event, "invalid or missing side")
continue
if order_type not in ("limit", "market"):
reject(idx, event, "invalid or missing order_type")
continue
if not _is_pos_int(qty):
reject(idx, event, "qty must be a positive integer")
continue
price = None
if order_type == "limit":
if "price" not in event:
reject(idx, event, "limit order missing price")
continue
price = event["price"]
if not _is_pos_int(price):
reject(idx, event, "price must be a positive integer")
continue
# time-in-force
if "tif" in event:
tif = event["tif"]
if tif not in ("GTC", "IOC", "FOK"):
reject(idx, event, "invalid tif")
continue
if order_type == "market" and tif == "GTC":
reject(idx, event, "market order may not be GTC")
continue
else:
tif = "GTC" if order_type == "limit" else "IOC"
# duplicate id check (after structural validation)
if oid in used_ids:
reject(idx, event, "duplicate order id")
continue
# Accept the order: register its id now.
used_ids.add(oid)
is_market = order_type == "market"
if tif == "FOK":
if side == "buy":
fillable = fillable_qty_buy(is_market, price, qty)
else:
fillable = fillable_qty_sell(is_market, price, qty)
if fillable < qty:
# not completely fillable; no trades, no book change
continue
# completely fillable, execute normally
if side == "buy":
execute_buy(oid, is_market, price, qty)
else:
execute_sell(oid, is_market, price, qty)
continue
# IOC and GTC
if side == "buy":
remaining = execute_buy(oid, is_market, price, qty)
else:
remaining = execute_sell(oid, is_market, price, qty)
if remaining > 0:
if tif == "GTC" and not is_market:
order = {
"id": oid,
"side": side,
"price": price,
"qty": remaining,
"seq": next(seq),
}
resting[oid] = order
if side == "buy":
bids[oid] = order
else:
asks[oid] = order
# IOC: cancel remainder (do nothing)
book = {
"bids": [
{"id": o["id"], "price": o["price"], "qty": o["qty"]}
for o in sorted_bids()
],
"asks": [
{"id": o["id"], "price": o["price"], "qty": o["qty"]}
for o in sorted_asks()
],
}
return {"trades": trades, "rejected": rejected, "book": book}
if name == "main":
evs = [
{"type": "new", "id": "a", "side": "sell", "order_type": "limit", "qty": 10, "price": 100},
{"type": "new", "id": "b", "side": "sell", "order_type": "limit", "qty": 5, "price": 101},
{"type": "new", "id": "c", "side": "buy", "order_type": "limit", "qty": 12, "price": 100},
{"type": "new", "id": "d", "side": "buy", "order_type": "market", "qty": 3},
{"type": "new", "id": "a", "side": "buy", "order_type": "limit", "qty": 1, "price": 50},
{"type": "cancel", "id": "b"},
{"type": "new", "id": "e", "side": "buy", "order_type": "limit", "qty": 0, "price": 100},
{"type": "new", "id": "f", "side": "buy", "order_type": "limit", "qty": True, "price": 100},
{"type": "new", "id": "g", "side": "buy", "order_type": "limit", "qty": 100, "price": 90, "tif": "FOK"},
]
result = process_events(evs)
assert result["trades"][0] == {"buy_id": "c", "sell_id": "a", "price": 100, "qty": 10, "taker_id": "c", "maker_id": "a"}
assert any(r["reason"] == "duplicate order id" for r in result["rejected"])
assert any("qty must be a positive integer" in r["reason"] for r in result["rejected"])
print("self-tests passed")
判定
勝利票
2 / 3
平均スコア
総合点
総評
回答Aは非常にクリーンで読みやすい実装です。すべての検証ルールと、FOK注文の原子的なチェックを含むさまざまな注文タイプのロジックを正しく処理しています。しかし、マッチングロジックに重大なパフォーマンスバグがあります。各部分的な約定のループ内で、反対側のブック全体を再ソートしています。これにより、一般的なシナリオでパフォーマンスが著しく低下し、「妥当な効率」というプロンプトの要件を満たせなくなります。
採点詳細を表示 ▼
正確さ
重み 35%ソリューションは単純なケースでは正しい結果を生成しますが、深刻なアルゴリズム上の欠陥があります。`execute_buy`および`execute_sell`のマッチングループは、各イテレーション(つまり、各部分的な約定)で反対側のブック全体を再ソートします。「妥当な効率」が期待されるコンテキストでは、これは重大な正しさの問題です。
完全性
重み 20%ソリューションは非常に完全で、すべてのイベントタイプ(新規、キャンセル)とタイムインフォースオプション(GTC、IOC、FOK)を実装しています。また、不正なイベント、重複ID、無効な値(bool-as-intケースを含む)に対する包括的な検証チェックも含まれています。
コード品質
重み 20%コードは非常によく構造化されており、クリーンで読みやすいです。明確な変数名とヘルパー関数を使用しています。ロジックは単一の関数スコープ内に含まれており、このタスクではシンプルで効果的です。自己テストセクションは簡潔で役立ちます。
実用性
重み 15%マッチングループの深刻なパフォーマンスバグにより、このソリューションは、些細でないシミュレーションには実用的ではありません。1つの大きな注文が多くの小さな待機中の注文と約定するシナリオでは、シミュレーションが許容できないほど遅くなり、その目的が損なわれます。
指示遵守
重み 10%ソリューションは、関数シグネチャ、返却形式、外部パッケージの不使用、最終的なブック状態の特定のソート順序など、すべての指示に完全に準拠しています。
総合点
総評
回答Aは、クリーンで構造化された単一ファイルのPythonソリューションです。ソートされたヘルパーを使用して価格時間優先を正しく実装し、すべてのTIFモード(GTC、IOC、FOK)をアトミックに処理し、bool-as-intの拒否、重複ID、無効なTIF、マーケット+GTCの組み合わせを含むすべての必須フィールドを検証します。FOKチェックは、ブックを変更せずに実行されます。コードは読みやすく、ヘルパー関数を効果的に使用しており、意味のある自己テストが含まれています。マイナーな非効率性:すべてのマッチイテレーションでブックを再ソートすること(ステップごとにO(n log n))ですが、問題の範囲では許容範囲です。レスト辞書とビッド/アスク辞書はわずかに冗長ですが、一貫性があります。
採点詳細を表示 ▼
正確さ
重み 35%価格時間優先、部分約定、FOKアトミシティ(失敗時のブック変更なし)、IOCキャンセル、GTCレスト、取引におけるメーカー価格設定、bool拒否、重複ID検出、キャンセル検証を正しく実装しています。FOK約定可能チェックは、状態を変更せずに正しくシミュレートします。重大な正確性バグは見つかりませんでした。
完全性
重み 20%すべてのイベントタイプ、すべてのTIFモード、bool-as-int、欠落フィールド、無効なTIF値、マーケット+GTC、重複ID、未レスト注文のキャンセルを含むすべての検証ルールを処理します。ブックの出力は正しくソートされています。すべての必須取引レコードフィールドが存在します。
コード品質
重み 20%クリーンで読みやすい構造で、ヘルパー関数には適切な名前が付いています。論理フローは追跡しやすいです。シーケンスにitertools.countを使用しているのはエレガントです。レスト/ビッド/アスク辞書間にはわずかな冗長性があります。各マッチステップでの再ソートはわずかに非効率的ですが、許容範囲です。自己テストではアサート文が適切に使用されています。
実用性
重み 15%堅実な実践的実装。エッジケースをうまく処理します。再ソートアプローチは、数千のイベントに対しては正しく機能しますが、非常に大きなブックには最適ではありません。外部依存関係はありません。クリーンなAPIです。
指示遵守
重み 10%すべての指示に正確に従っています:単一ファイル、指定されたキーのみを持つ正しい返却形状、外部パッケージなし、コア関数での標準入力/標準出力なし、__main__によって保護された自己テスト、指定されたとおりに有効なイベントの追加フィールドを無視します。
総合点
総評
回答Aは、要求された指値注文板シミュレータの強力で実行可能な実装です。価格・時間優先度、メイカー価格取引、部分約定、GTC/IOC/FOKの動作(アトミックなFOKチェック、重複した受け入れID、キャンセル、bool/int検証、ソートされた最終的な板の出力を含む)を正しく処理します。主な弱点は、マッチング中に板を繰り返しソートすることによる効率の低下と、ハッシュ不可能な値のような異常な不正形式のIDに対する堅牢性のギャップがいくつかあることですが、タスクを実質的に満たしています。
採点詳細を表示 ▼
正確さ
重み 35%マッチング、価格・時間優先度、メイカー価格設定、部分約定、注文削除、IOC/GTC動作、テンポラリな変更なしのFOK事前チェック、重複した受け入れID、および最終的な板のソートを正しく実装しています。ハッシュ不可能な異常な不正形式のIDに対する軽微な堅牢性の懸念が残っています。
完全性
重み 20%指定されたほとんどすべてのイベントタイプ、検証、タイムインフォースモード、拒否レコード、取引レコード、および出力形状をカバーしています。ハッシュ不可能なIDのようなエッジケースの不正入力に対する堅牢性強化は一部省略されています。
コード品質
重み 20%コードはクリーンで読みやすく、焦点を絞ったヘルパー関数で整理されています。主な品質上の欠点は、各マッチングループ内で繰り返しソートが行われることであり、これは単純ですが、大きな板に対しては特に効率的ではありません。
実用性
重み 15%小規模から中規模の決定論的シミュレーションには実用的に使用でき、要求されたデータ構造を生成します。効率は数千のイベントには許容範囲ですが、繰り返しソートのために多数の待機注文があると低下する可能性があります。
指示遵守
重み 10%単一ファイルに必要なprocess_events関数を定義し、外部パッケージを使用せず、コア関数内でI/Oを実行せず、要求されたトップレベルのキーを正確に返し、メインガードの下でセルフテスト出力を保持しています。