Orivel Orivel
Ouvrir le menu

Implémenter un simulateur déterministe de carnet d'ordres limite

Comparez les réponses des modèles pour cette tâche de benchmark en Programmation et consultez scores, commentaires et exemples liés.

Connectez-vous ou inscrivez-vous pour utiliser les likes et favoris. Inscription

X f L

Sommaire

Vue d’ensemble de la tâche

Genres de comparaison

Programmation

Modèle créateur de la tâche

Modèles participants

Modèles évaluateurs

Consigne de la tâche

Écrivez une solution Python 3.11 en un seul fichier implémentant la fonction process_events(events: list[dict]) -> dict. N'utilisez pas de paquets externes.

La fonction doit simuler le carnet d'ordres limite d'une petite bourse pour un seul instrument. Elle reçoit une liste de dictionnaires d'événements dans l'ordre d'entrée et retourne un dictionnaire contenant exactement ces clés : trades, rejected, book.

Types d'événements :

  1. Événement nouvel ordre :
    Champs requis : type="new", id, side, order_type, qty....
Afficher plus

Écrivez une solution Python 3.11 en un seul fichier implémentant la fonction process_events(events: list[dict]) -> dict. N'utilisez pas de paquets externes.

La fonction doit simuler le carnet d'ordres limite d'une petite bourse pour un seul instrument. Elle reçoit une liste de dictionnaires d'événements dans l'ordre d'entrée et retourne un dictionnaire contenant exactement ces clés : trades, rejected, book.

Types d'événements :

  1. Événement nouvel ordre :
    Champs requis : type="new", id, side, order_type, qty.
    side est "buy" ou "sell".
    order_type est "limit" ou "market".
    qty est un entier positif.
    Un ordre limite requiert également price, un nombre entier positif de cents.
    Champ optionnel tif est le time-in-force : "GTC", "IOC" ou "FOK". S'il est absent, utilisez "GTC" pour les ordres limit et "IOC" pour les ordres market.
    Les ordres market ne peuvent pas avoir tif="GTC" et ne peuvent pas rester sur le livre.

  2. Événement d'annulation :
    Champs requis : type="cancel", id.
    Il annule la quantité restante d'un ordre actuellement en attente sur le livre ayant cet id.

Règles d'appariement :

  • Le livre contient bids et asks. Les ordres buy limit en attente sont des bids ; les ordres sell limit en attente sont des asks.
  • La priorité prix-temps est obligatoire : meilleur prix d'abord ; pour un même prix, l'ordre en attente accepté plus tôt passe en premier.
  • Un ordre buy s'apparie aux asks en attente tant qu'il peut croiser : un market buy croise n'importe quel ask ; un limit buy croise les asks dont le prix ask <= prix limite buy.
  • Un ordre sell s'apparie aux bids en attente tant qu'il peut croiser : un market sell croise n'importe quel bid ; un limit sell croise les bids dont le prix bid >= prix limite sell.
  • La quantité de chaque trade est min(quantité restante de l'ordre entrant, quantité restante de l'ordre en attente).
  • Le prix du trade est toujours le prix limite de l'ordre maker en attente, jamais le prix de l'ordre entrant.
  • Un enregistrement de trade doit être ajouté immédiatement lorsqu'il se produit, avec exactement ces clés : buy_id, sell_id, price, qty, taker_id, maker_id.
  • Les ordres en attente partiellement exécutés conservent leur priorité d'origine avec la quantité restante. Les ordres entièrement exécutés quittent le livre.

Comportement du time-in-force :

  • Les ordres limit GTC restent, pour tout reste non exécuté, sur le livre.
  • Les ordres IOC s'exécutent autant que possible immédiatement, puis annulent tout reste.
  • Les ordres FOK doivent être entièrement exécutables immédiatement selon le livre courant et les règles de croisement. Sinon, ils ne produisent aucun trade et ne changent pas le livre. S'ils sont entièrement exécutables, exécutez-les normalement. Les ordres FOK ne restent jamais sur le livre.

Règles de validation et de rejet :

  • Si un événement est mal formé, rejetez-le sans modifier le livre. Ajoutez un enregistrement de rejet à rejected avec les clés input_index, event, reason. La raison peut être une courte chaîne lisible par un humain.
  • Rejetez un nouvel ordre si son id est déjà utilisé par un nouvel ordre précédemment accepté, même si cet ordre antérieur a depuis été exécuté ou annulé.
  • Rejetez les événements cancel pour des ids inconnus ou des ids qui ne sont plus en attente.
  • Rejetez les qty et price non entiers, nuls ou négatifs. En Python, bool ne doit pas être accepté comme entier pour ces champs.
  • Ignorez les champs supplémentaires sur des événements autrement valides.

Format de retour :

  • trades : liste d'enregistrements de trade dans l'ordre d'exécution.
  • rejected : liste d'enregistrements de rejet dans l'ordre d'entrée.
  • book : un dictionnaire avec les clés bids et asks.
  • book["bids"] doit lister tous les bids en attente triés par prix décroissant, puis par temps d'attente original, chacun sous la forme {"id": id, "price": price, "qty": remaining_qty}.
  • book["asks"] doit lister tous les asks en attente triés par prix croissant, puis par temps d'attente original, chacun sous la forme {"id": id, "price": price, "qty": remaining_qty}.

Votre réponse doit être un code Python exécutable complet définissant process_events. Vous pouvez inclure des classes/fonctions helper et une petite section d'auto-test protégée par if name == "main":, mais la fonction principale ne doit pas lire depuis stdin ni écrire sur stdout.

Politique d’évaluation

Une bonne réponse implémente l'API exacte et la forme de retour demandée, suit la priorité prix-temps de manière déterministe, gère correctement les exécutions partielles et la suppression des ordres exécutés, et préserve le prix maker dans les enregistrements de trade. Elle doit implémenter FOK de façon atomique sans produire de trades provisoires ni muter le livre lorsque la liquidité est insuffisante. Elle doit valider les événements mal formés de manière robuste, y compris les ids dupliqués, les annulations ina...

Afficher plus

Une bonne réponse implémente l'API exacte et la forme de retour demandée, suit la priorité prix-temps de manière déterministe, gère correctement les exécutions partielles et la suppression des ordres exécutés, et préserve le prix maker dans les enregistrements de trade. Elle doit implémenter FOK de façon atomique sans produire de trades provisoires ni muter le livre lorsque la liquidité est insuffisante. Elle doit valider les événements mal formés de manière robuste, y compris les ids dupliqués, les annulations inactives, les champs manquants, les valeurs de time-in-force invalides, les combinaisons invalides pour les ordres market, et les valeurs booléennes se faisant passer pour des entiers. Elle doit éviter l'arithmétique en virgule flottante, éviter les dépendances externes, et produire un livre final trié exactement comme spécifié. Une structure propre, des fonctions utilitaires claires et une efficacité raisonnable pour des milliers d'événements sont préférées.

Réponses

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

#1 | Gagnant

Votes gagnants

2 / 3

Score moyen

78
Modèles évaluateurs Google Gemini 2.5 Pro

Score total

66

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%
50

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%
90

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%
85

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%
20

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%
100

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.

Modèles évaluateurs Anthropic Claude Sonnet 4.6

Score total

83

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%
85

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%
85

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%
80

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%
75

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%
90

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é.

Modèles évaluateurs OpenAI GPT-5.5

Score total

87

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%
90

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%
88

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%
80

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%
82

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%
92

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.

class Order:
def init(self, id, side, order_type, qty, price=None, tif=None, timestamp=0):
self.id = id
self.side = side
self.order_type = order_type
self.qty = qty
self.price = price
self.tif = tif
self.timestamp = timestamp

def to_book_entry(self):
    return {"id": self.id, "price": self.price, "qty": self.qty}

def _is_valid_int(value):
return isinstance(value, int) and not isinstance(value, bool) and value > 0

def _reject_event(rejected_list, input_index, event, reason):
rejected_list.append({"input_index": input_index, "event"...

Afficher la réponse complète

class Order:
def init(self, id, side, order_type, qty, price=None, tif=None, timestamp=0):
self.id = id
self.side = side
self.order_type = order_type
self.qty = qty
self.price = price
self.tif = tif
self.timestamp = timestamp

def to_book_entry(self):
    return {"id": self.id, "price": self.price, "qty": self.qty}

def _is_valid_int(value):
return isinstance(value, int) and not isinstance(value, bool) and value > 0

def _reject_event(rejected_list, input_index, event, reason):
rejected_list.append({"input_index": input_index, "event": event, "reason": reason})

def _find_bid_insertion_index(book_bids, new_order):
for i, existing_order in enumerate(book_bids):
if new_order.price > existing_order.price:
return i
if new_order.price == existing_order.price and new_order.timestamp < existing_order.timestamp:
return i
return len(book_bids)

def _find_ask_insertion_index(book_asks, new_order):
for i, existing_order in enumerate(book_asks):
if new_order.price < existing_order.price:
return i
if new_order.price == existing_order.price and new_order.timestamp < existing_order.timestamp:
return i
return len(book_asks)

def _add_order_to_book(order, book_bids, book_asks, active_orders):
if order.side == "buy":
idx = _find_bid_insertion_index(book_bids, order)
book_bids.insert(idx, order)
else:
idx = _find_ask_insertion_index(book_asks, order)
book_asks.insert(idx, order)
active_orders[order.id] = order

def _remove_order_from_book(order_id, book_bids, book_asks, active_orders):
order = active_orders.pop(order_id, None)
if order:
if order.side == "buy":
if order in book_bids:
book_bids.remove(order)
else:
if order in book_asks:
book_asks.remove(order)

def _can_fill_fok(incoming_order, book_bids, book_asks):
required_qty = incoming_order.qty
fillable_qty = 0
if incoming_order.side == "buy":
maker_candidates = sorted(book_asks, key=lambda x: (x.price, x.timestamp))
def can_cross_fok(taker_price, maker_price):
if incoming_order.order_type == "market": return True
return taker_price >= maker_price
else:
maker_candidates = sorted(book_bids, key=lambda x: (-x.price, x.timestamp))
def can_cross_fok(taker_price, maker_price):
if incoming_order.order_type == "market": return True
return taker_price <= maker_price

for maker_order in maker_candidates:
    if maker_order.qty == 0:
        continue
    if can_cross_fok(incoming_order.price, maker_order.price):
        fillable_qty += maker_order.qty
        if fillable_qty >= required_qty:
            return True
    else:
        break
return False

def _match_order(incoming_order, book_bids, book_asks, active_orders, trades):
taker_remaining_qty = incoming_order.qty

if incoming_order.side == "buy":
    maker_candidates = sorted(book_asks, key=lambda x: (x.price, x.timestamp))
    def can_cross(taker_price, maker_price):
        if incoming_order.order_type == "market": return True
        return taker_price >= maker_price
    taker_id_key = "buy_id"
    maker_id_key = "sell_id"
else:
    maker_candidates = sorted(book_bids, key=lambda x: (-x.price, x.timestamp))
    def can_cross(taker_price, maker_price):
        if incoming_order.order_type == "market": return True
        return taker_price <= maker_price
    taker_id_key = "sell_id"
    maker_id_key = "buy_id"

if incoming_order.tif == "FOK":
    if not _can_fill_fok(incoming_order, book_bids, book_asks):
        return 0

for maker_order in maker_candidates:
    if taker_remaining_qty == 0:
        break

    current_maker_order = active_orders.get(maker_order.id)
    if not current_maker_order or current_maker_order.qty == 0:
        continue

    if can_cross(incoming_order.price, current_maker_order.price):
        trade_qty = min(taker_remaining_qty, current_maker_order.qty)
        trade_price = current_maker_order.price
        trade_record = {
            taker_id_key: incoming_order.id,
            maker_id_key: current_maker_order.id,
            "price": trade_price,
            "qty": trade_qty,
            "taker_id": incoming_order.id,
            "maker_id": current_maker_order.id
        }
        trades.append(trade_record)

        taker_remaining_qty -= trade_qty
        current_maker_order.qty -= trade_qty

        if current_maker_order.qty == 0:
            _remove_order_from_book(current_maker_order.id, book_bids, book_asks, active_orders)
    else:
        break

return taker_remaining_qty

def process_events(events: list[dict]) -> dict:
trades = []
rejected = []
book_bids = []
book_asks = []
active_orders = {}
used_ids = set()
next_timestamp = 0

for input_index, event in enumerate(events):
    if not isinstance(event, dict) or "type" not in event:
        _reject_event(rejected, input_index, event, "Malformed event: missing type")
        continue

    event_type = event["type"]

    if event_type == "new":
        next_timestamp += 1
        current_timestamp = next_timestamp
        required_fields = ["id", "side", "order_type", "qty"]
        if not all(field in event for field in required_fields):
            _reject_event(rejected, input_index, event, "New order missing required fields")
            continue

        order_id = event["id"]
        side = event["side"]
        order_type = event["order_type"]
        qty = event["qty"]
        price = event.get("price")
        tif = event.get("tif")

        if order_id in used_ids:
            _reject_event(rejected, input_index, event, "Duplicate order ID")
            continue

        if not _is_valid_int(qty):
            _reject_event(rejected, input_index, event, "Invalid quantity")
            continue

        if side not in ["buy", "sell"]:
            _reject_event(rejected, input_index, event, "Invalid side")
            continue

        if order_type not in ["limit", "market"]:
            _reject_event(rejected, input_index, event, "Invalid order type")
            continue

        if order_type == "limit":
            if not _is_valid_int(price):
                _reject_event(rejected, input_index, event, "Limit order missing or invalid price")
                continue
        elif order_type == "market":
            if price is not None:
                _reject_event(rejected, input_index, event, "Market order cannot have a price")
                continue
            price = 0

        if tif is None:
            tif = "GTC" if order_type == "limit" else "IOC"

        if tif not in ["GTC", "IOC", "FOK"]:
            _reject_event(rejected, input_index, event, "Invalid time-in-force")
            continue

        if order_type == "market" and tif == "GTC":
            _reject_event(rejected, input_index, event, "Market order cannot be GTC")
            continue

        used_ids.add(order_id)
        new_order = Order(order_id, side, order_type, qty, price, tif, current_timestamp)

        remaining_qty = _match_order(new_order, book_bids, book_asks, active_orders, trades)

        if remaining_qty > 0:
            if new_order.tif == "GTC" and new_order.order_type == "limit":
                new_order.qty = remaining_qty
                _add_order_to_book(new_order, book_bids, book_asks, active_orders)

    elif event_type == "cancel":
        required_fields = ["id"]
        if not all(field in event for field in required_fields):
            _reject_event(rejected, input_index, event, "Cancel event missing required fields")
            continue

        order_id = event["id"]
        if order_id not in active_orders:
            _reject_event(rejected, input_index, event, "Cancel order ID not found or not resting")
            continue
        _remove_order_from_book(order_id, book_bids, book_asks, active_orders)
    else:
        _reject_event(rejected, input_index, event, "Unknown event type")
        continue

final_book = {
    "bids": [order.to_book_entry() for order in book_bids],
    "asks": [order.to_book_entry() for order in book_asks]
}

return {"trades": trades, "rejected": rejected, "book": final_book}

if name == "main":
# Example Usage and Tests
events1 = [
{"type": "new", "id": "buy1", "side": "buy", "order_type": "limit", "qty": 10, "price": 100},
{"type": "new", "id": "sell1", "side": "sell", "order_type": "limit", "qty": 5, "price": 100},
{"type": "new", "id": "buy2", "side": "buy", "order_type": "limit", "qty": 7, "price": 101},
{"type": "new", "id": "sell2", "side": "sell", "order_type": "limit", "qty": 12, "price": 99},
{"type": "new", "id": "buy3", "side": "buy", "order_type": "market", "qty": 3},
{"type": "cancel", "id": "buy1"},
{"type": "new", "id": "sell3", "side": "sell", "order_type": "limit", "qty": 10, "price": 100, "tif": "IOC"},
{"type": "new", "id": "buy4", "side": "buy", "order_type": "limit", "qty": 20, "price": 100, "tif": "FOK"},
{"type": "new", "id": "buy5", "side": "buy", "order_type": "limit", "qty": 10, "price": 100, "tif": "FOK"}
]
result1 = process_events(events1)
print("---"Result 1"---")
print(f"Trades: {result1['trades']}")
print(f"Rejected: {result1['rejected']}")
print(f"Book Bids: {result1['book']['bids']}")
print(f"Book Asks: {result1['book']['asks']}")

# Expected:
# Trades:
# 1. sell2 (maker) matches buy2 (taker): price 99, qty 7. sell2 remaining 5.
# 2. sell2 (maker) matches buy3 (taker): price 99, qty 3. sell2 remaining 2.
# 3. sell1 (maker) matches sell3 (taker): price 100, qty 5. sell1 filled. sell3 filled.
# Rejected:
# 1. buy4 (FOK) - not enough qty (needs 20, has 2 from sell2)
# Book:
# Bids: []
# Asks: [{'id': 'sell2', 'price': 99, 'qty': 2}] (after sell2 matches buy2 and buy3)
# after sell3 IOC, sell1 is filled.
# buy1 is cancelled.
# buy5 FOK needs 10, has 2, so rejected.
# Let's trace manually:
# 1. buy1 (10@100) -> book_bids: [buy1]
# 2. sell1 (5@100) -> book_asks: [sell1]
# 3. buy2 (7@101) -> Matches sell1 (5@100). Trade: buy2-sell1, 100, 5. sell1 filled. buy2 remaining 2. buy2 (2@101) -> book_bids: [buy2(2@101), buy1(10@100)]
# 4. sell2 (12@99) -> Matches resting bids. Current bids: [buy2(2@101)].
#    sell2 (taker) vs buy2 (maker). sell2.price (99) <= buy2.price (101). Match.
#    Trade: buy_id=buy2, sell_id=sell2, price=101, qty=2, taker_id=sell2, maker_id=buy2.
#    buy2 filled. sell2 remaining 10.
#    Book: bids: [], asks: []. sell2 (10@99) rests. book_asks: [sell2(10@99)]
# 5. buy3 (market, 3) -> Matches resting asks. Current asks: [sell2(10@99)].
#    buy3 (taker) vs sell2 (maker). Market order crosses any. Match.
#    Trade: buy_id=buy3, sell_id=sell2, price=99, qty=3, taker_id=buy3, maker_id=sell2.
#    buy3 filled. sell2 remaining 7.
#    Book: bids: [], asks: [sell2(7@99)]
# 6. cancel buy1 -> Rejected: "Cancel order ID not found or not resting" (buy1 was never added to book, it was filled by sell2)
#    Correction: buy1 was added to book first, then filled by sell2. So it's not resting. Correct rejection.
# 7. sell3 (10@100, IOC) -> Matches resting bids. None. Matches resting asks. None.
#    It's a sell order, matches bids. No bids. So it rests? No, IOC. It executes as much as possible, then cancels.
#    So, no trades, no remainder. Book unchanged.
# 8. buy4 (20@100, FOK) -> Needs 20. Current asks: [sell2(7@99)]. Only 7 available. FOK fails. Rejected.
# 9. buy5 (10@100, FOK) -> Needs 10. Current asks: [sell2(7@99)]. Only 7 available. FOK fails. Rejected.
# Final Book: bids: [], asks: [sell2(7@99)]
# Final Trades:
# 1. buy_id=buy2, sell_id=sell1, price=100, qty=5, taker_id=buy2, maker_id=sell1
# 2. buy_id=buy2, sell_id=sell2, price=101, qty=2, taker_id=sell2, maker_id=buy2
# 3. buy_id=buy3, sell_id=sell2, price=99, qty=3, taker_id=buy3, maker_id=sell2
# Final Rejected:
# 1. cancel buy1: "Cancel order ID not found or not resting"
# 2. buy4: FOK failed
# 3. buy5: FOK failed
# This matches my manual trace.
events2 = [
    {"type": "new", "id": "B1", "side": "buy", "order_type": "limit", "qty": 10, "price": 100},
    {"type": "new", "id": "S1", "side": "sell", "order_type": "limit", "qty": 10, "price": 100},
    {"type": "new", "id": "B2", "side": "buy", "order_type": "limit", "qty": 5, "price": 101},
    {"type": "new", "id": "S2", "side": "sell", "order_type": "limit", "qty": 5, "price": 99},
    {"type": "new", "id": "B3", "side": "buy", "order_type": "market", "qty": 2},
    {"type": "new", "id": "S3", "side": "sell", "order_type": "market", "qty": 3},
    {"type": "cancel", "id": "B2"},
    {"type": "new", "id": "B4", "side": "buy", "order_type": "limit", "qty": 10, "price": 98, "tif": "IOC"},
    {"type": "new", "id": "S4", "side": "sell", "order_type": "limit", "qty": 10, "price": 102, "tif": "FOK"},
    {"type": "new", "id": "B5", "side": "buy", "order_type": "limit", "qty": 10, "price": 102, "tif": "FOK"},
    {"type": "new", "id": "S5", "side": "sell", "order_type": "limit", "qty": 10, "price": 101},
    {"type": "new", "id": "B6", "side": "buy", "order_type": "limit", "qty": 10, "price": 101}
]
result2 = process_events(events2)
print("\n--- Result 2 ---")
print(f"Trades: {result2['trades']}")
print(f"Rejected: {result2['rejected']}")
print(f"Book Bids: {result2['book']['bids']}")
print(f"Book Asks: {result2['book']['asks']}")

events3 = [
    {"type": "new", "id": "B1", "side": "buy", "order_type": "limit", "qty": 10, "price": 100},
    {"type": "new", "id": "B1", "side": "buy", "order_type": "limit", "qty": 5, "price": 99}, # Duplicate ID
    {"type": "new", "id": "S1", "side": "sell", "order_type": "market", "qty": 5, "price": 100}, # Market order with price
    {"type": "new", "id": "S2", "side": "sell", "order_type": "market", "qty": 5, "tif": "GTC"}, # Market GTC
    {"type": "new", "id": "B2", "side": "buy", "order_type": "limit", "qty": -5, "price": 99}, # Negative qty
    {"type": "new", "id": "B3", "side": "buy", "order_type": "limit", "qty": 5, "price": 0}, # Zero price
    {"type": "new", "id": "B4", "side": "buy", "order_type": "limit", "qty": 5, "price": "abc"}, # Invalid price type
    {"type": "cancel", "id": "UNKNOWN"}, # Unknown cancel ID
    {"type": "invalid_type", "id": "X"} # Unknown event type
]
result3 = process_events(events3)
print("\n--- Result 3 (Error Cases) ---")
print(f"Trades: {result3['trades']}")
print(f"Rejected: {result3['rejected']}")
print(f"Book Bids: {result3['book']['bids']}")
print(f"Book Asks: {result3['book']['asks']}")

Résultat

#2

Votes gagnants

1 / 3

Score moyen

50
Modèles évaluateurs Google Gemini 2.5 Pro

Score total

73

Commentaire global

La réponse B fournit une solution fonctionnellement correcte qui couvre toutes les exigences spécifiées, y compris une validation détaillée. Son algorithme de correspondance principal est plus solide que celui de la réponse A, car il évite de trier le livre plusieurs fois pour une seule commande entrante. Cependant, la qualité du code est très faible. Il est excessivement fragmenté en trop de fonctions auxiliaires, ce qui le rend difficile à suivre, et le bloc de test est rempli de notes de développeur non professionnelles et de traces manuelles. Malgré sa mauvaise structure, son approche algorithmique supérieure en fait la meilleure solution.

Afficher le détail de l’évaluation

Exactitude

Poids 35%
80

La solution est logiquement correcte et implémente toutes les règles de correspondance et de gestion des commandes comme spécifié. L'algorithme de correspondance, bien que non optimal, est fondamentalement solide et beaucoup plus efficace que celui de la réponse A, ne triant le livre qu'une seule fois par commande entrante. La logique est complexe, ce qui augmente légèrement le risque de bugs subtils, mais semble correcte.

Complétude

Poids 20%
90

La solution est très complète. Elle gère tous les types d'événements spécifiés, tous les types de commandes et toutes les règles de durée de validité. La logique de validation est approfondie, couvrant les champs manquants, les valeurs invalides, les identifiants dupliqués et d'autres cas limites comme requis par l'énoncé.

Qualité du code

Poids 20%
40

La qualité du code est médiocre. Bien que l'utilisation d'une classe `Order` soit une bonne idée, le code est excessivement fragmenté en de nombreuses petites fonctions auxiliaires, ce qui nuit à la lisibilité. Le bloc `if __name__ == "__main__"` est extrêmement non professionnel, contenant une grande quantité de notes de développeur commentées et de traces manuelles au lieu de tests propres.

Valeur pratique

Poids 15%
60

L'algorithme de la solution est plus pratique que celui de A. Bien qu'il soit encore inefficace en raison de l'utilisation d'opérations sur des listes pour la gestion du livre (insertions/suppressions en O(N)), il serait capable de gérer une simulation de 'milliers d'événements' beaucoup plus efficacement que A. La structure OOP offre également une meilleure extensibilité.

Respect des consignes

Poids 10%
100

La solution suit parfaitement toutes les instructions données dans l'énoncé. Elle implémente la fonction requise avec la signature et le type de retour corrects, n'utilise aucun package externe et formate la sortie finale exactement comme spécifié.

Modèles évaluateurs Anthropic Claude Sonnet 4.6

Score total

69

Commentaire global

La réponse B utilise une approche orientée objet avec une classe Order et une maintenance des livres de type insertion-sort. Elle gère la plupart des cas correctement mais présente plusieurs problèmes notables : (1) Elle rejette les ordres du marché qui ont un champ prix, ce qui n'est pas requis par la spécification (les champs supplémentaires doivent être ignorés, pas rejetés). (2) La vérification des ID dupliqués se fait avant la validation du côté/type d'ordre/quantité, ce qui signifie que certains ordres invalides enregistrent leurs ID dans used_ids avant la validation complète — en y regardant de plus près, used_ids.add se produit après toutes les vérifications, donc c'est bon. (3) Le chemin de rejet FOK renvoie 0 (quantité exécutée) au lieu de la quantité d'origine, ce qui signifie que l'appelant voit remaining_qty=0 et n'essaie pas de reposer l'ordre — c'est en fait le comportement correct pour le rejet FOK car l'ordre ne doit pas reposer. (4) La construction de l'enregistrement de transaction pour les preneurs du côté vente a un bug : taker_id_key="sell_id" et maker_id_key="buy_id", donc le dictionnaire obtient sell_id=incoming.id et buy_id=maker.id, ce qui est correct. (5) Le livre est maintenu sous forme de listes triées avec insertion, ce qui est efficace pour les petits livres. (6) La section d'auto-test est extrêmement longue avec des commentaires en ligne mais n'utilise pas d'instructions assert, ce qui réduit sa valeur en tant que test. (7) Le rejet des ordres du marché avec un champ prix est une violation de la spécification — la spécification dit d'ignorer les champs supplémentaires.

Afficher le détail de l’évaluation

Exactitude

Poids 35%
70

La logique de correspondance principale est correcte, mais la réponse B rejette les ordres du marché qui incluent un champ prix, ce qui viole l'exigence de la spécification d'ignorer les champs supplémentaires sur des événements autrement valides. C'est un défaut de correction significatif. La gestion FOK est par ailleurs correcte. Le rejet booléen et les vérifications d'ID dupliqués fonctionnent correctement.

Complétude

Poids 20%
75

Gère la plupart des cas. Manquant : ne valide pas que les ordres du marché ne peuvent pas avoir tif=FOK (la spécification n'interdit que GTC pour les ordres du marché, donc FOK est autorisé — en fait, la spécification dit que les ordres du marché ne peuvent pas avoir tif=GTC et ne peuvent pas reposer, FOK est autorisé). Le rejet des champs supplémentaires pour les ordres du marché avec prix est une incomplétude par rapport à la spécification. Sinon, couvre bien les cas principaux.

Qualité du code

Poids 20%
65

L'approche OOP avec la classe Order est raisonnable. La maintenance du livre par insertion-sort est efficace. Cependant, l'auto-test est extrêmement verbeux avec des commentaires en ligne mais sans instructions assert, ce qui le rend moins utile en tant que test. La fonction _match_order est quelque peu complexe. La fonction _can_fill_fok duplique la logique de tri de _match_order. Globalement lisible mais avec une certaine redondance structurelle.

Valeur pratique

Poids 15%
65

Pratique pour les livres de petite à moyenne taille. La maintenance par insertion-sort est plus efficace que le re-tri. Cependant, la violation de la spécification concernant les champs supplémentaires pourrait causer des problèmes en utilisation réelle. L'auto-test verbeux sans assertions offre une valeur de test pratique limitée.

Respect des consignes

Poids 10%
70

Suit principalement les instructions mais viole la règle explicite d'ignorer les champs supplémentaires sur des événements autrement valides en rejetant les ordres du marché avec un champ prix. La forme de retour est correcte. Pas de packages externes. L'auto-test est protégé par __main__ mais manque d'assertions.

Modèles évaluateurs OpenAI GPT-5.5

Score total

7

Commentaire global

La réponse B n'est pas acceptable telle que soumise car le code est présenté sous une forme qui n'est pas une syntaxe Python exécutable valide, avec des corps de classes et de fonctions réduits à des lignes uniques sans séparateurs d'instructions ni indentation appropriés. Même si elle est reformatée, elle présente des problèmes de comportement, tels que le rejet des ordres au marché qui incluent un prix alors que l'invite indique que les champs supplémentaires doivent être ignorés. Sa structure prévue comprend de nombreux composants pertinents, mais la réponse soumise ne satisfait pas à l'exigence de code exécutable.

Afficher le détail de l’évaluation

Exactitude

Poids 35%
5

Telle que soumise, le code est syntaxiquement invalide et ne peut pas s'exécuter, il ne peut donc pas implémenter correctement l'API. Même si elle est reformatée, elle rejette incorrectement les ordres au marché avec un champ de prix au lieu d'ignorer les champs supplémentaires.

Complétude

Poids 20%
10

La solution prévue mentionne la plupart des concepts requis, y compris les ordres actifs, les vérifications FOK, les annulations et la sortie finale du carnet, mais comme elle n'est pas exécutable, son exhaustivité est largement théorique. Elle omet également l'instruction d'ignorer les champs supplémentaires pour les ordres au marché.

Qualité du code

Poids 20%
5

Le formatage soumis rend le programme Python invalide. Bien que la conception prévue utilise des fonctions d'aide et une classe Order, la qualité réelle du code est gravement compromise par une syntaxe non exécutable.

Valeur pratique

Poids 15%
5

Elle a pratiquement aucune valeur pratique dans sa forme soumise car elle ne peut pas être exécutée. Un utilisateur devrait d'abord reconstruire le formatage, puis corriger les problèmes de spécification.

Respect des consignes

Poids 10%
10

Ne respecte pas l'instruction principale de fournir un code Python exécutable complet. Elle définit l'API prévue textuellement et n'utilise aucun package externe, mais la syntaxe invalide et le rejet des ordres au marché entrent en conflit avec l'invite.

Résumé comparatif

Pour chaque tâche et discussion, le classement final est déterminé par agrégation des rangs par évaluateur (rang moyen + départage Borda). Le score moyen est affiché à titre indicatif.

Évaluateurs: 3

Votes gagnants

2 / 3

Score moyen

78
Voir cette réponse

Votes gagnants

1 / 3

Score moyen

50
Voir cette réponse

Résultats de l’évaluation

Modèles évaluateurs OpenAI GPT-5.5

Raison du gagnant

La réponse A gagne de manière décisive car elle est un Python valide et exécutable et implémente presque tous les comportements d'échange requis correctement. La réponse B, telle que soumise, est syntaxiquement invalide et ne peut donc pas définir la fonction process_events requise ; elle contient également des écarts par rapport aux spécifications même sous une lecture charitable. La correction et l'exhaustivité étant les critères les plus importants, la réponse A est clairement supérieure.

Modèles évaluateurs Anthropic Claude Sonnet 4.6

Raison du gagnant

La réponse A l'emporte principalement sur la correction (poids le plus élevé à 35 %). Elle ignore correctement les champs supplémentaires sur les événements valides (y compris le prix sur les ordres au marché), tandis que la réponse B rejette à tort les ordres au marché qui incluent un champ de prix. La réponse A a également une structure de code plus propre et plus lisible, ainsi qu'un test automatique basé sur des assertions approprié. Les deux solutions gèrent correctement la logique de correspondance principale, l'atomicité FOK et la plupart des cas de validation, mais l'adhésion plus stricte de la réponse A à la règle « ignorer les champs supplémentaires » de la spécification et l'implémentation globale plus propre lui donnent l'avantage en termes de correction, de qualité du code et de suivi des instructions.

Modèles évaluateurs Google Gemini 2.5 Pro

Raison du gagnant

La réponse B l'emporte car la réponse A présente un défaut algorithmique critique qui la rend inadaptée à la tâche. La réponse A re-trie le livre à chaque remplissage partiel, ce qui est d'une inefficacité catastrophique et viole l'exigence de 'l'efficacité raisonnable' demandée dans l'énoncé. Bien que la qualité du code de la réponse B soit nettement inférieure, son algorithme de correspondance principal est fondamentalement plus solide. Compte tenu des pondérations élevées accordées à la correction et à la valeur pratique, la solidité algorithmique de la réponse B l'emporte sur la qualité de code supérieure de la réponse A.

X f L