Réponse A: Anthropic Claude Opus 4.8
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...
Afficher la réponse complète ▼
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")
Résultat
Votes gagnants
2 / 3
Score moyen
Score total
Commentaire global
La réponse A est une implémentation très propre et lisible. Elle gère correctement toutes les règles de validation et la logique pour différents types d'ordres, y compris la vérification atomique pour les ordres FOK. Cependant, elle contient un bug critique de performance dans sa logique de correspondance : elle re-trie tout le côté opposé du carnet d'ordres à l'intérieur de la boucle pour chaque exécution partielle. Cela entraîne des performances extrêmement médiocres dans des scénarios courants et ne répond pas à l'exigence de 'l'efficacité raisonnable' de l'énoncé.
Afficher le détail de l’évaluation ▼
Exactitude
Poids 35%La solution produit des résultats corrects dans des cas simples, mais elle présente un défaut algorithmique sévère. Les boucles de correspondance dans `execute_buy` et `execute_sell` re-trie tout le carnet opposé à chaque itération (c'est-à-dire pour chaque exécution partielle). C'est un problème de correction majeur dans un contexte où une 'efficacité raisonnable' est attendue.
Complétude
Poids 20%La solution est très complète, implémentant tous les types d'événements (nouveau, annulation) et les options de durée de validité (GTC, IOC, FOK). Elle inclut également un ensemble complet de vérifications de validation pour les événements malformés, les ID dupliqués et les valeurs invalides, y compris le cas booléen-en-entier.
Qualité du code
Poids 20%Le code est très bien structuré, propre et facile à lire. Il utilise des noms de variables clairs et des fonctions d'aide. La logique est contenue dans une seule portée de fonction, ce qui est simple et efficace pour cette tâche. La section d'auto-test est concise et utile.
Valeur pratique
Poids 15%Le bug de performance sévère dans la boucle de correspondance rend cette solution impraticable pour toute simulation non triviale. Un scénario où un grand ordre correspond à de nombreux petits ordres au repos entraînerait un ralentissement inacceptable de la simulation, allant à l'encontre de son objectif.
Respect des consignes
Poids 10%La solution respecte parfaitement toutes les instructions, y compris la signature de la fonction, le format de retour, l'absence de packages externes et l'ordre de tri spécifique pour l'état final du carnet d'ordres.
Score total
Commentaire global
La réponse A est une solution Python propre et bien structurée, en un seul fichier. Elle implémente correctement la priorité prix-temps à l'aide d'aides triées, gère tous les modes TIF (GTC, IOC, FOK) de manière atomique, valide tous les champs requis, y compris le rejet des booléens sous forme d'entiers, les ID dupliqués, les TIF invalides et les combinaisons marché+GTC. La vérification FOK est effectuée sans muter le carnet d'ordres. Le code est lisible, utilise efficacement les fonctions d'aide et inclut un auto-test significatif. Inefficacité mineure : le tri du carnet d'ordres à chaque itération de correspondance (O(n log n) par étape), mais cela est acceptable pour la portée du problème. Les dictionnaires de repos et les dictionnaires d'offres/demandes sont légèrement redondants mais cohérents.
Afficher le détail de l’évaluation ▼
Exactitude
Poids 35%Implémente correctement la priorité prix-temps, les remplissages partiels, l'atomicité FOK (aucune mutation du carnet d'ordres en cas d'échec), l'annulation IOC, le repos GTC, la tarification du teneur de marché dans les transactions, le rejet des booléens, la détection des ID dupliqués et la validation des annulations. La vérification de la possibilité de remplir FOK simule correctement sans muter l'état. Aucun bug de correction significatif n'a été trouvé.
Complétude
Poids 20%Gère tous les types d'événements, tous les modes TIF, toutes les règles de validation y compris les booléens sous forme d'entiers, les champs manquants, les valeurs TIF invalides, le marché+GTC, les ID dupliqués, l'annulation des ordres non reposés. La sortie du carnet d'ordres est correctement triée. Tous les champs requis pour l'enregistrement des transactions sont présents.
Qualité du code
Poids 20%Structure propre et lisible avec des fonctions d'aide bien nommées. Le flux logique est facile à suivre. L'utilisation d'itertools.count pour la séquence est élégante. Légère redondance entre les dictionnaires de repos/offres/demandes. Le tri à chaque étape de correspondance est légèrement inefficace mais acceptable. L'auto-test utilise correctement les instructions assert.
Valeur pratique
Poids 15%Implémentation pratique solide. Gère bien les cas limites. L'approche de tri fonctionne correctement pour des milliers d'événements, bien que non optimale pour des carnets d'ordres très volumineux. Aucune dépendance externe. API propre.
Respect des consignes
Poids 10%Suit toutes les instructions à la lettre : un seul fichier, forme de retour correcte avec exactement les clés spécifiées, aucun paquet externe, pas d'entrée/sortie standard dans la fonction principale, auto-test protégé par __main__, ignore les champs supplémentaires sur les événements valides comme spécifié.
Score total
Commentaire global
La réponse A est une implémentation solide et exécutable du simulateur de carnet d'ordres limite requis. Elle gère correctement la priorité prix-temps, les transactions du teneur de marché, les remplissages partiels, le comportement GTC/IOC/FOK, y compris les vérifications atomiques FOK, les identifiants acceptés en double, les annulations, la validation bool/int et la sortie triée du carnet final. Ses principales faiblesses sont une efficacité modeste due au tri répété du carnet pendant la mise en correspondance et quelques lacunes en matière de robustesse pour des identifiants malformés inhabituels tels que des valeurs non hachables, mais elle satisfait substantiellement la tâche.
Afficher le détail de l’évaluation ▼
Exactitude
Poids 35%Implémente correctement la mise en correspondance, la priorité prix-temps, la tarification du teneur de marché, les remplissages partiels, la suppression des ordres, le comportement IOC/GTC, la pré-vérification FOK sans mutation provisoire, les identifiants acceptés en double et le tri du carnet final. Des préoccupations mineures de robustesse subsistent pour les identifiants malformés inhabituels qui ne sont pas hachables.
Complétude
Poids 20%Couvre presque tous les types d'événements spécifiés, les validations, les modes de durée de validité, les enregistrements de rejet, les enregistrements de transaction et la forme de sortie. Il omet seulement un certain durcissement des entrées malformées dans les cas limites, comme les identifiants non hachables.
Qualité du code
Poids 20%Le code est propre, lisible et organisé avec des fonctions d'aide ciblées. Le principal inconvénient de qualité est le tri répété à l'intérieur de chaque boucle de mise en correspondance, ce qui est simple mais pas particulièrement efficace pour les carnets plus importants.
Valeur pratique
Poids 15%Serait pratiquement utilisable pour des simulations déterministes de petite à moyenne taille et produit les structures de données demandées. L'efficacité est acceptable pour des milliers d'événements mais pourrait se dégrader avec de nombreux ordres en attente en raison du tri répété.
Respect des consignes
Poids 10%Définit la fonction process_events requise dans un seul fichier, n'utilise aucun package externe, n'effectue pas d'E/S à l'intérieur de la fonction principale, renvoie exactement les clés de niveau supérieur requises et maintient la sortie d'auto-test sous la garde principale.