أَغْنِنَا بِحَلَالِكَ عَنْ حَرَامِكَ

Fonctionnalités du moteur de conformité — keel

Cette page ne décrit que ce que le dépôt du moteur peut montrer. Chaque section renvoie à la source qui l'atteste : si une affirmation s'écartait un jour du code, c'est par ce lien que vous nous prendriez en défaut.

Filtrage des actifs par attestation — blocage par défaut

L'admission dans la liste blanche est découpée selon ce qu'il est possible de savoir. Les faits de marché se calculent. Les classifications Shariah — l'activité principale du token relève-t-elle d'un secteur interdit (§28.4), s'agit-il d'un 'ayn adossé à un actif ou d'une créance dayn (§65.5/§67.2), le token verse-t-il un rendement assimilable au riba — sont attestées, jamais déduites, au moyen de keel assets attest. Une attestation manquante vaut refus, pas acceptation par défaut.

Vérifier dans le dépôt : compliance/screen.py

Les garde-fous (rails) — vingt-et-un contrôles qu'aucun ordre ne contourne

Des contrôles déterministes que rien ne peut désactiver, pas même le mode autonome. Ils sont vingt-et-un, numérotés de 1 à 14 et de 16 à 22 — le garde-fou 15 a été retiré et son numéro jamais réattribué :

  • La liste blanche halal, les plafonds de dépense par ordre et par jour, les plafonds d'exposition et de concentration
  • Un dimensionnement qui tient compte des corrélations, un seuil de mouvement minimal, l'interdiction de la martingale et de l'élargissement des stops
  • Des disjoncteurs de perte maximale (drawdown) totale et hebdomadaire, un disjoncteur de pertes consécutives et d'érosion de l'avantage statistique (edge)
  • Des contrôles de fraîcheur des flux de données et de cohérence des cotations
  • Garde-fou 14 — attestations d'abonnement et de retrait sur la plateforme : tout achat réel est refusé tant que l'opérateur n'a pas attesté
  • Garde-fou 17 — qabd §65.4 : la capacité de retrait est attestée puis appliquée, au motif qu'un actif impossible à retirer n'a peut-être jamais été valablement possédé
  • Un plafond d'écart (spread) à l'entrée, qui refuse tout achat réel à partir de 50 points de base et bloque d'office si le carnet est illisible
  • Garde-fou 20 — périmètre de négociation : une clé qui lit correctement ne prouve pas qu'elle peut négocier ; toute entrée réelle est refusée tant que la plateforme elle-même n'a pas confirmé que cette clé peut en placer une
  • Garde-fou 21 — solde de l'actif : une vente est refusée lorsque la plateforme signale explicitement l'absence de solde, et délibérément laissée passer lorsque le solde est simplement inconnu, car bloquer une sortie enferme une position qui voulait sortir
  • Tout veto d'un garde-fou se nomme et indique la commande qui le lève

Vérifier dans le dépôt : execution/guards.py

Périmètre de négociation — c'est la plateforme qui peut démentir l'attestation de l'opérateur

Une clé qui lit correctement ne prouve pas qu'elle peut négocier : une clé bien formée, dont toutes les lectures aboutissaient, s'est vu refuser son premier ordre réel — « vous n'avez pas la permission d'effectuer cette action ». L'affirmation de l'opérateur selon laquelle la clé d'une plateforme peut placer des entrées réelles est donc devenue un enregistrement que la plateforme elle-même peut renverser : keel scope attest écrit la moitié de l'opérateur, un refus de la plateforme l'écrase, et l'enregistrement est lié à l'empreinte de la clé qui l'a obtenu — changer de clé n'hérite donc pas de l'autorisation de l'ancienne. Le garde-fou 20 lit cet enregistrement et refuse les entrées réelles tant qu'il dit non. Les sorties restent délibérément intactes, car une sortie doit toujours pouvoir partir.

Vérifier dans le dépôt : commands/scope.py

Verrous de stratégie — candidate → papier → réel

Une règle doit franchir trois étapes avant de toucher de l'argent réel. La promotion passe par un verrou en deux volets : des seuils de performance et un contrôle de surapprentissage (PBO/CSCV). Le seuil de 100 transactions peut être atteint par le backtest de la règle, ou en mutualisant le même jeu de paramètres sur d'autres produits en papier — à condition qu'au moins cinq produits y contribuent pour dix transactions chacun, car un ensemble d'échantillons corrélés surestime sa propre puissance.

Vérifier dans le dépôt : agent.py — RULE_REGISTRY

Mesure honnête, face au DCA

keel simulate rejoue les vraies règles sur l'historique récupéré, les compare à la référence DCA et rédige un rapport GO-LIVE / TRAIN-MORE qui nomme chaque verrou et ses chiffres. Le backtesteur applique à chaque produit un glissement calibré sur sa propre liquidité — de 5 points de base à l'extrémité la plus liquide jusqu'à 183,8, le taux que paie le nom le plus étroit de l'univers mesuré — afin qu'aucun résultat ne puisse être flatté par un carnet d'ordres peu liquide. Sur cet univers, pas un seul des 24 actifs n'atteint le plancher de 5 points de base. Le rapport indique quelle passe emploie quel taux, car le tableau d'avantage est tarifé par produit tandis que la passe de compte et la référence DCA restent à taux fixe. Sur les règles par défaut, il vous répondra très probablement TRAIN-MORE : c'est le moteur qui fonctionne, pas une panne.

Vérifier dans le dépôt : le registre des expériences

Le registre des ordres consigne le carnet sur lequel keel a réellement été exécuté

La table des ordres était écrite depuis le début sans qu'aucune surface ne la lise : « qu'a acheté et vendu keel, et à quel prix » n'avait de réponse qu'en ouvrant SQLite à la main. C'est un service désormais : keel orders affiche le rapport, et la console web locale projette ce même rapport au lieu de le recalculer, de sorte que les deux interfaces ne peuvent pas diverger. Chaque ordre porte le haut du carnet de la plateforme au moment de la soumission, conservé comme la paire achat/vente et non comme un unique chiffre d'écart — un écart calculé puis stocké perd les deux nombres dont il provient. Aux tailles d'ordre de ce déploiement, l'écart constitue presque tout le coût : il est donc consigné comme preuve plutôt que reconstruit après coup.

Vérifier dans le dépôt : commands/orders.py

keel research — une porte d'entrée unique sur treize modules de preuve

La boîte à outils comptait treize modules et aucun endroit ne le disait ; six d'entre eux n'étaient atteignables qu'en lisant le code. keel research index les nomme désormais tous les treize — ce à quoi chacun répond, ce à quoi il ne peut pas répondre, et la commande qui l'exécute. Il n'ajoute aucune statistique propre : les cinq qui avaient déjà leur place sous keel trials y sont enregistrés une seconde fois comme les mêmes objets de commande, jamais des copies, car une porte d'entrée qui réimplémente diverge dès que l'une des copies reçoit un correctif que l'autre n'a pas. La colonne « ne peut pas répondre » est à lire avec autant d'attention que l'autre.

Vérifier dans le dépôt : commands/research.py

Trois profils de déploiement qui ne partagent rien

Papier quotidien, réel, et un profil horaire de collecte de preuves (paper-hourly) — chacun avec sa propre base de données et sa propre configuration. Le profil horaire existe parce que l'horloge quotidienne ne mesure que 2,15 signaux par actif et par an : à ce rythme, il faudrait de 31 à 84 ans pour réunir les 100 transactions d'une revue. Les mêmes règles sur des bougies ONE_HOUR en déclenchent 49,4 — environ 940 signaux d'entrée par an une fois mutualisés — ce qui ramène cette revue à quelques semaines au lieu de quelques décennies. Lui aussi est mesuré perdant : il existe pour collecter des preuves recevables, pas du profit.

Vérifier dans le dépôt : le runbook opérateur

Un port courtier, pas un enfermement propriétaire

Les adaptateurs mettent en œuvre un seul contrat — le port keel-broker-api — et se déclarent sous le point d'entrée keel.brokers. Coinbase Advanced Trade est l'adaptateur de référence ; Robinhood est livré comme plateforme optionnelle, délibérément non raccordée ; un adaptateur Alpaca s'y est ajouté en v0.10.0 ; et un adaptateur Kraken est présent à l'état d'ébauche complète du port, dont chaque méthode de données et de marché lève une exception — il atteste la forme du port, non que keel puisse y négocier. Une plateforme factice, volontairement divergente, maintient le port honnête : la suite de conformité s'exécute sur les deux, au sein d'une base d'environ 6 650 tests.

Vérifier dans le dépôt : packages/

Un bracket est un seul type d'ordre, parce que deux jambes font la course

Le port qu'implémentent les adaptateurs connaît désormais le bracket comme un type d'ordre unique — BracketGTC, un bracket de sortie qui clôture une position détenue — et non deux ordres à placer puis à apparier côté client. Deux jambes posées séparément font la course : si l'une se remplit et que la survivante n'est jamais annulée, un ordre vivant reste capable de vendre une position déjà close, et deux jambes portant chacune la quantité totale engagent la position deux fois. Un ordre natif unique supprime l'un et l'autre par construction. Ceci est l'étape un, du vocabulaire de port : chaque adaptateur le mappe, la suite de conformité l'épingle, et le chemin live de keel place encore directement le bracket natif de la plateforme — déplacer cet appel vers le port est l'étape suivante. C'est un travail de justesse : rien ici ne prétend que le moteur trade mieux, seulement qu'il y a moins de façons d'avoir tort.

Vérifier dans le dépôt : orders.py — BracketGTC

Taille d'ordre quantifiée à la précision de la plateforme — une sortie n'est jamais bloquée

Un ordre dimensionné au risque tombe rarement sur un chiffre rond, et une plateforme rejette une taille qui porte plus de décimales que le pas du produit — le premier ordre live dimensionné au risque a été refusé exactement pour cela : une erreur de formatage, pas un garde-fou. Les tailles sont désormais quantifiées à la baisse au pas que la plateforme déclare pour le produit, des deux côtés de l'ordre. Quand le pas est inconnu, les deux côtés échouent dans des directions opposées, délibérément : un achat est refusé — une entrée refusée ne coûte rien — tandis qu'une vente part telle quelle et est journalisée, parce qu'une sortie qui ne peut pas partir est un échec plus grave qu'une entrée qui ne peut pas commencer. Une taille qui se quantifie à zéro est refusée net.

Vérifier dans le dépôt : execution/executor.py

Les exécutions partielles sont un état à part ; une sortie ne libère que ce qui s'est vendu

Un ordre au marché qui ne se remplit que partiellement n'est plus forcé de paraître soit en attente soit exécuté : 0 < rempli < demandé est un état non terminal à part, avec la quantité observée chez la plateforme enregistrée à côté de la quantité demandée, et la moyenne courante de la plateforme comme prix d'exécution. Tout ce qui est en aval calcule sur ce qui s'est réellement exécuté — la base de calcul de la moyenne pondérée compte un achat partiel à sa quantité remplie, jamais à une taille qui n'a rien acheté. Une entrée partiellement remplie alerte fort, car son bracket de sortie a été dimensionné pour la quantité demandée et peut dépasser ce qui est réellement détenu ; le redimensionner est délibérément une action d'opérateur, pas automatique. Une sortie partiellement remplie comptabilise le résultat pour la quantité réellement vendue et retient la libération du reliquat encore détenu — une tranche ne se clôture que lorsque son remplissage la couvre. Le côté auquel l'avertissement appartient est épinglé par un test.

Vérifier dans le dépôt : execution/reconcile.py

Le registre de crash s'écrit avant l'annulation, pour qu'un roll mort soit bruyant

Déplacer un stop, c'est annuler le bracket au repos puis placer son remplaçant — et un processus mourant entre les deux laissait la position nue ET silencieuse, parce que la réconciliation reste volontairement discrète sur les lignes sans intention enregistrée, pour ne pas dresser l'opérateur à ignorer l'alerte. L'ordre des opérations est désormais inversé : l'enregistrement d'intention « unbracketed » — le registre de crash qui nomme la position ayant perdu sa protection — s'écrit avant que l'annulation ne parte. Un roll qui meurt en vol est bruyant dès le cycle de réconciliation suivant, avec les chiffres nécessaires pour replacer le bracket.

Vérifier dans le dépôt : execution/executor.py — _roll_stop

Robinhood : les règles de la plateforme vérifiées avant l'envoi d'un ordre

L'adaptateur Robinhood — toujours optionnel, toujours délibérément non raccordé — lit les règles par paire que la plateforme publie (une taille minimale libellée dans la monnaie de cotation, le pas de taille, un maximum) et vérifie chaque ordre contre elles avant soumission : un ordre sous le minimum ou hors pas est refusé localement au lieu d'être découvert chez la plateforme. Et son point de terminaison best_bid_ask n'est pas un instantané de carnet — les deux jambes y sont échantillonnées indépendamment et se croisent sur les paires les plus liquides — donc l'adaptateur ne rend une cotation que lorsque les chiffres de la plateforme s'ordonnent de façon cohérente, et refuse d'en fabriquer une autrement.

Vérifier dans le dépôt : keel-broker-robinhood/adapter.py

Confirmation par défaut ; l'autonomie change l'interlocuteur, pas la règle

keel prévisualise chaque ordre et demande confirmation dans le terminal ; sans opérateur, il refuse. keel autonomy on change qui l'on interroge, jamais ce qui est permis. Pour tout arrêter : keel kill — le coupe-circuit se ferme en cas de défaillance.

Vérifier dans le dépôt : le README, « How keel works »

keel est livré inerte

Rien ne se négocie tant que vous n'avez pas promu une règle, attesté l'abonnement à la plateforme, approvisionné le compte et — en mode confirmation — tapé y. Uniquement du comptant à l'achat : pas de levier, pas de vente à découvert, pas de produits dérivés, et le dimensionnement se fait sur du cash réel, donc sans riba.

Les obligations au niveau du compte qu'aucun garde-fou ne peut voir — désactiver la rémunération USDC des soldes dormants, principalement — restent à la charge de l'opérateur ; le runbook opérateur les recense.

Et c'est dans le profil papier que toute règle commence : gratuit, exécutions simulées, rien en jeu, sans compte approvisionné ni identifiants de trading (une clé de données de marché gratuite, en lecture seule, suffit). Ce n'est pas une démonstration : les transactions papier d'un même jeu de paramètres comptent dans le minimum exigé par le verrou de promotion — la preuve papier est la première étape du parcours.

Traduit d'après la révision anglaise 2026-09-16.1.