GUIDE TECHNIQUE — INFRASTRUCTURE TRADING

Multi-Broker & Ledger Interne : Gérer N Comptes comme un Seul Système

Comment unifier Alpaca, IBKR, Binance et Saxo derrière un ledger interne unique. Routage d'ordres, réconciliation quotidienne, monitoring temps réel, multi-devises, et scaling de 1 à N modes × M brokers — avec du code, des schémas, et des cas réels.

Multi-Broker
22 min
Architecture
Réconciliation

Pourquoi un compte ne suffit plus

Vous avez un compte Alpaca pour le paper trading US, un IBKR pour le live, un Trading212 pour les ETF européens, un Binance pour le crypto, et un Saxo pour le forex. Chaque broker a sa propre API, ses propres formats, ses propres limites. Comment connaître votre P&L global à l'instant T ?

La question semble triviale. Elle ne l'est pas. Chaque broker rapporte les positions dans un format différent. IBKR utilise des contrats et affiche les P&L en devise locale. Alpaca parle en shares avec un unrealized_pnl en USD. Binance fonctionne en base/quote pairs avec des précisions décimales variables. Et Trading212 ajoute sa propre couche d'abstraction avec des "pies" et des fractions de shares.

Quand vous exécutez 6+ stratégies (Dynamic, Balanced, TKL, Orbit, Alpha, Fortress — plus crypto, métaux et forex en expansion) à travers 4+ brokers, vous perdez la source unique de vérité. Le P&L de votre mode Dynamic est réparti entre Alpaca (paper) et IBKR (live). Votre exposition globale est la somme de N dashboards que personne ne regarde simultanément. Et quand un broker tombe, vous ne savez même pas quelles positions sont affectées.

DailyTickers a résolu ce problème en construisant un ledger interne — un journal comptable unique qui enregistre chaque transaction, indépendamment du broker qui l'a exécutée. Le broker devient un simple canal d'exécution. La vérité est dans le ledger. Et pour s'assurer que la vérité est correcte, un broker-simulator tourne en parallèle, miroir fidèle des trades, pour valider avant de basculer la source de vérité.

6+
Modes de trading actifs, chacun avec ses propres paramètres de risque, régime filters, et position sizing
4+
Brokers potentiels : Alpaca (paper US), IBKR (live US/EU), Binance (crypto), Saxo (forex/EU)
T+0 à T+2
Délais de règlement différents : crypto instantané, US T+1, Europe T+2 — impact direct sur le pouvoir d'achat
1
Un seul ledger comme source de vérité, indépendant de tout broker, reconstructible à partir des transactions

Source : IBKR — Multi-Currency Trading

Architecture Multi-Broker

L'architecture repose sur un principe simple : séparer l'exécution de la comptabilité. Les brokers exécutent les ordres. Le ledger enregistre les faits. Le monitoring surveille les écarts. Et le portfolio API expose une vue unifiée à tous les consommateurs.

Alpaca Paper US, REST API, WebSocket fills, T+1
IBKR Live US/EU, TWS API, complex order types, multi-devise
Binance Crypto spot/futures, REST + WS, T+0, base/quote pairs
Saxo Forex/EU, OpenAPI, T+2, multi-leg
▼ Adapters (normalisation vers format commun)
Unified Order Router Reçoit les signaux des modes, sélectionne le broker optimal (asset class, coûts, disponibilité), route l'ordre, track le lifecycle (PENDING → FILLED/REJECTED)
Broker Simulator Mirror-run parallèle, exécute les mêmes trades indépendamment pour valider le ledger
▼ Fill events
Internal Ledger (Source de Vérité) Append-only. Chaque transaction : ticker, qty, price, broker, mode, fees, timestamp, FX rate. Positions dérivées. Balances recalculées. Jamais de mutation, jamais de suppression.
▼ Events + Snapshots
Réconciliation Engine Compare ledger vs positions broker. Classe les écarts : missing, phantom, price drift. Alerte si divergence > seuil.
Monitoring P&L par mode, drawdown, exposure par broker, fill rate, cash utilization. Alertes Discord.
Portfolio API portfolio/v1/ — 6 modes × endpoints JSON standardisés. Status, positions, performance, trades.

Le pattern Adapter

Chaque broker a son propre langage. L'adapter traduit vers un format commun avant que les données n'entrent dans le système. C'est le même principe que les pilotes d'imprimante : votre application envoie un document, le pilote traduit pour le matériel spécifique.

// Interface commune — chaque broker implémente ces méthodes
class BrokerAdapter {
  // Normalise une position broker → format interne
  normalizePosition(rawPosition) {
    return {
      ticker:        this.mapTicker(rawPosition),     // AAPL, BTC-USD, ASML.AS → symbole unifié
      qty:           this.parseQuantity(rawPosition),  // gère les fractions (T212)
      avgCost:       this.parsePrice(rawPosition),     // en devise de trading
      avgCostBase:   this.toBaseCurrency(rawPosition), // converti en USD
      unrealizedPnl: this.parsePnl(rawPosition),
      broker:        this.name,                        // 'alpaca' | 'ibkr' | 'binance' | 'saxo'
      assetClass:    this.detectAssetClass(rawPosition),
      currency:      this.parseCurrency(rawPosition),
      lastUpdate:    new Date().toISOString()
    };
  }

  // Normalise un fill event → transaction ledger
  normalizeOrder(rawOrder) {
    return {
      id:           this.generateTxId(),
      brokerOrderId: rawOrder.id || rawOrder.orderId,
      side:         this.parseSide(rawOrder),        // 'BUY' | 'SELL'
      ticker:       this.mapTicker(rawOrder),
      qty:          this.parseQuantity(rawOrder),
      price:        this.parseFillPrice(rawOrder),
      fees:         this.parseFees(rawOrder),
      broker:       this.name,
      timestamp:    this.parseTimestamp(rawOrder),
      settlementDate: this.computeSettlement(rawOrder),
      fxRate:       this.getCurrentFxRate(rawOrder),
    };
  }
}

Pourquoi plusieurs brokers ?

Raison Détail Exemple
Couverture d'actifs Aucun broker ne couvre toutes les classes d'actifs IBKR pour actions + options, Binance pour crypto, Saxo pour forex
Régulation Certains brokers ne sont pas disponibles dans certaines juridictions Alpaca = US-only paper, Trading212 = EU résident
Redondance Si un broker tombe (maintenance, incident), le routage bascule IBKR down le 15/03 → ordres routés vers Alpaca temporairement
Optimisation des frais Les structures de commission varient fortement Alpaca = $0 US equities, IBKR = $0.005/share, Binance = 0.1% spot
Paper vs Live Tester en paper avant de passer live sur un broker différent Alpaca paper → IBKR live (status: deploying → live)

Source : Alpaca — Trading API Documentation

Le Ledger Interne : Design et Invariants

Un ledger interne est un journal comptable append-only qui enregistre chaque événement financier indépendamment de tout broker. Le broker dit "vous avez 100 AAPL" — c'est son opinion. Le ledger dit "vous avez acheté 50 AAPL le 3 mars et 50 le 15 mars" — ce sont des faits.

Les 4 entités du ledger

Entité Nature Stockage Exemple
Transaction Fait atomique — immuable une fois écrit Append-only (.jsonl) BUY 100 AAPL @ $150.00, IBKR, fee $1.00, mode=dynamic, 2026-03-15T14:30:00Z
Position Vue agrégée — dérivée des transactions Calculé (snapshot quotidien) AAPL: qty=100, avgCost=$150.00, unrealizedPnl=+$320
Balance Cash + valeur positions par broker et par mode Calculé IBKR: cash=$45,200, positions=$82,400, total=$127,600
Transfer Mouvement entre brokers ou modes — pas un trade Append-only MOVE $10,000 from Alpaca/dynamic to IBKR/dynamic (paper → live cutover)

Les 4 invariants

Le ledger repose sur quatre invariants qui doivent toujours être vrais. Si un invariant est violé, c'est un bug — pas une approximation acceptable.

// Invariant 1 — Reconstructibilité
// La somme de toutes les transactions = les positions courantes
assert(
  sumTransactions(ledger.transactions, 'AAPL').qty
  === ledger.positions['AAPL'].qty
);

// Invariant 2 — Réconciliation cash
assert(
  cash_start + deposits - withdrawals + realized_pnl - total_fees
  === cash_current
);

// Invariant 3 — Cross-check mode/broker
// La somme des positions par mode = la somme des positions par broker
assert(
  sumByMode(positions).totalValue
  === sumByBroker(positions).totalValue
);

// Invariant 4 — Unicité et traçabilité
// Chaque transaction a un ID unique, un broker_order_id, et un mode
for (const tx of ledger.transactions) {
  assert(tx.id);            // UUID interne
  assert(tx.brokerOrderId); // ID côté broker
  assert(tx.mode);           // 'dynamic' | 'balanced' | 'tkl' | ...
}

La règle append-only

On ne modifie jamais une entrée du ledger. Jamais. Si une transaction est erronée, on écrit une correction : une entrée de reversal (qui annule l'originale) suivie d'une entrée correcte. C'est exactement comme la comptabilité en partie double : on ne gomme pas, on passe une écriture inverse.

// Transaction originale (erreur : prix 150 au lieu de 155)
{ "id": "tx-001", "side": "BUY", "ticker": "AAPL", "qty": 100, "price": 150.00 }

// Correction : reversal + nouvelle entrée
{ "id": "tx-002", "type": "REVERSAL", "refTxId": "tx-001", "side": "SELL", "qty": 100, "price": 150.00 }
{ "id": "tx-003", "type": "CORRECTION", "refTxId": "tx-001", "side": "BUY", "qty": 100, "price": 155.00 }

Le stockage chez DailyTickers utilise des fichiers JSON (pit-state.json pour les positions courantes, modes-config-history.json pour l'audit trail des configurations). Pour des volumes plus importants, SQLite ou PostgreSQL avec des tables append-only et des vues matérialisées pour les positions.

Le ledger n'est pas une base de données

C'est un journal comptable. Chaque ligne est un fait historique immuable. Les positions et les balances sont des vues dérivées que l'on peut toujours recalculer à partir du journal. Si vous perdez les snapshots de positions, vous pouvez les reconstruire. Si vous perdez le journal, vous avez tout perdu.

Source : Martin Fowler — Event Sourcing

Routage des Ordres : Du Signal au Fill

Le parcours d'un ordre suit une chaîne précise : le scanner génère un signal, le mode l'assigne, le routeur sélectionne le broker, le broker exécute, le fill event remonte, et le ledger enregistre. Chaque maillon est indépendant et peut échouer sans corrompre les autres.

1. Signal Scanner détecte un setup : AAPL, score 92, entry $188.50, stop $182.00, TP1 $198.00
2. Mode Assignment Le signal correspond aux filtres du mode Dynamic (score ≥ 85, mcap > $10B, RSI < 70)
▼
3. Broker Selection Routeur consulte la matrice : US equity + mode live = IBKR. Vérifie : IBKR up ? Cash suffisant ? Ordre pas déjà soumis (idempotency) ? Si oui → route. Si non → fallback ou queue.
▼
4. Execution IBKR reçoit : LIMIT BUY 50 AAPL @ $188.50 GTC. Status: SUBMITTED.
5. Fill → Ledger Fill event reçu : 50 AAPL @ $188.42, fee $0.25. Le ledger enregistre la transaction.

Logique de sélection du broker

Critère Règle Exemples
Classe d'actif Crypto → Binance, US equities → Alpaca/IBKR, EU equities → Saxo, Forex → Saxo BTC-USD → Binance, ASML → Saxo, AAPL → IBKR
Mode Paper modes → Alpaca paper, live modes → IBKR Alpha (deploying) → Alpaca paper, Dynamic (live) → IBKR
Fallback Si broker primaire down → secondaire, avec retry exponentiel IBKR timeout → tentative Alpaca live (si disponible)
Coûts À volume équivalent, router vers le broker le moins cher 1000 shares AAPL : Alpaca $0 vs IBKR $5 → Alpaca si disponible

Lifecycle d'un ordre

PENDING → SUBMITTED → PARTIAL_FILL → FILLED
Ou : SUBMITTED → REJECTED | CANCELLED

Idempotency et race conditions

Trois scénarios critiques que le routeur doit gérer :

Scénario Risque Solution
Le même signal se déclenche deux fois Double exécution → position 2× trop grande Order ID idempotent : mode-ticker-date-signal_id. Le broker rejette le doublon.
Un fill arrive après un cancel Position non souhaitée dans le portefeuille Le fill gagne toujours (il a déjà eu lieu sur l'exchange). Le ledger l'enregistre, le monitoring alerte.
Deux modes veulent le même ticker Confusion sur l'attribution de la position Chaque mode a sa propre position. AAPL/dynamic et AAPL/balanced sont deux entrées distinctes dans le ledger.

Un ordre envoyé est un fait

Même si le code crashe après l'envoi, le fill existe sur le broker. Le ledger DOIT enregistrer tout ce qui est envoyé, pas seulement ce qui est confirmé. Au redémarrage, le premier réflexe est de réconcilier les ordres en vol avec le broker pour rattraper les fills manqués.

Source : IBKR — TWS API Documentation

Réconciliation : Broker vs Ledger

Le ledger dit que vous avez 100 AAPL. IBKR dit 95. Qui a raison ? La réponse n'est jamais évidente, et les deux peuvent avoir tort. La réconciliation est le processus qui détecte ces écarts, les classe, et génère des actions correctives.

Les 3 types de divergence

Type Symptôme Cause fréquente Action
Missing transaction Broker montre une position que le ledger ne connaît pas Fill reçu pendant un crash, WebSocket déconnecté, latence réseau Interroger l'API fills du broker, recréer la transaction dans le ledger
Phantom position Le ledger montre une position que le broker ne connaît pas Ordre annulé mais enregistré comme filled, corporate action (merger) Vérifier l'historique des ordres, passer une écriture de reversal
Price divergence Quantité identique mais prix moyen différent Split/reverse-split non traité, conversion FX à un moment différent, fill partiel à prix différent Ajuster le prix moyen avec une écriture de correction, appliquer le split

L'algorithme de réconciliation

async function reconcile(broker, ledger) {
  const brokerPositions = await broker.getPositions();
  const ledgerPositions = ledger.getPositionsByBroker(broker.name);

  const discrepancies = [];

  // 1. Positions chez le broker mais pas dans le ledger → Missing
  for (const bp of brokerPositions) {
    const lp = ledgerPositions.find(p => p.ticker === bp.ticker);
    if (!lp) {
      discrepancies.push({ type: 'MISSING', ticker: bp.ticker, brokerQty: bp.qty, ledgerQty: 0 });
      continue;
    }
    // Quantité différente → Price divergence ou partial missing
    if (Math.abs(bp.qty - lp.qty) > 0.001) {
      discrepancies.push({ type: 'QTY_MISMATCH', ticker: bp.ticker, brokerQty: bp.qty, ledgerQty: lp.qty });
    }
    // Prix moyen diverge > 1%
    if (Math.abs(bp.avgCost - lp.avgCost) / lp.avgCost > 0.01) {
      discrepancies.push({ type: 'PRICE_DRIFT', ticker: bp.ticker, brokerPrice: bp.avgCost, ledgerPrice: lp.avgCost });
    }
  }

  // 2. Positions dans le ledger mais pas chez le broker → Phantom
  for (const lp of ledgerPositions) {
    if (!brokerPositions.find(p => p.ticker === lp.ticker)) {
      discrepancies.push({ type: 'PHANTOM', ticker: lp.ticker, brokerQty: 0, ledgerQty: lp.qty });
    }
  }

  // 3. Alerter si nécessaire
  if (discrepancies.length > 0) {
    await alertDiscord(`⚠️ ${discrepancies.length} écarts détectés sur ${broker.name}`);
    for (const d of discrepancies) {
      if (valueOfDiscrepancy(d) > 500) {
        await alertDiscord(`🚨 CRITIQUE: ${d.ticker} ${d.type} — broker:${d.brokerQty} vs ledger:${d.ledgerQty}`);
      }
    }
  }
  return discrepancies;
}

Le parallel-run DailyTickers

Plutôt que de faire confiance aveuglément au ledger, DailyTickers fait tourner un broker-simulator en parallèle. Ce simulateur reçoit les mêmes signaux, exécute les mêmes trades de façon indépendante, et maintient son propre état. Chaque nuit, le reconcile compare les deux :

Pit-Engine (Ledger) Source de vérité actuelle. pit-state.json. Positions, P&L, drawdown.
Broker-Simulator (Mirror) Juge indépendant. Même trades, exécution propre. État parallèle isolé.
▼ Réconciliation nocturne
Décision de cutover Si les deux concordent pendant N jours consécutifs → le simulateur devient la nouvelle source de vérité. Avec fallback dur sur pit-state.json si le simulateur diverge.

Réconcilier TOUS LES JOURS

Pas toutes les semaines. Pas quand quelque chose semble bizarre. Tous les jours. Les erreurs de 24h se corrigent en 5 minutes. Les erreurs de 30 jours se corrigent en 3 heures — si vous arrivez à retrouver ce qui s'est passé. L'expérience montre que les divergences non détectées pendant plus de 5 jours deviennent exponentiellement plus coûteuses à résoudre.

Source : Martin Fowler — Parallel Change

Monitoring : Alertes et Tableau de Bord

Le monitoring n'est pas un nice-to-have — c'est le système nerveux du multi-broker. Sans lui, vous découvrez les problèmes quand il est trop tard : un drawdown qui a dépassé le seuil il y a 3 jours, un broker déconnecté depuis ce matin, un fill manqué qui a créé une exposition non couverte.

Les 6 métriques essentielles

Métrique Source Fréquence Seuil d'alerte
P&L par mode Ledger (transactions + MtM) Chaque fill + EOD Perte journalière > 2% du capital mode
Drawdown par mode Ledger (peak-to-trough) Temps réel > 5% = warning, > 8% = HARD STOP → pausing
Exposure par broker Ledger + broker API Horaire > 80% du capital chez un seul broker
Fill rate Order router logs Par batch d'ordres Rejection rate > 10% = investigation
Réconciliation Reconciliation engine Quotidienne Écart > $500 ou > 1% d'une position
Cash utilization Ledger (cash / total capital) EOD < 10% = sous-investi, > 95% = pas de marge d'erreur

Le seuil de drawdown et la state machine

Quand le drawdown d'un mode dépasse le seuil, le monitoring déclenche une transition de statut via la mode status state machine — 8 états qui protègent contre les décisions précipitées :

draft → test → deploying → live → pausing → paused
Urgence : live → liquidated (toutes positions fermées au marché)

pausing est l'état critique : pas de nouvelles entrées, pas de rotation, mais les stops/TP/horizon/trailing continuent sur les positions ouvertes. C'est une sortie organique, pas un panic sell. Le mode bascule vers paused quand toutes les positions sont fermées naturellement.

liquidated est l'urgence absolue : toutes les positions sont fermées au marché à la prochaine séance, sans regarder SL/TP/horizon. Compliance, panic, black swan.

Le portfolio API

Le monitoring alimente le portfolio/v1/ — une API JSON publique qui expose l'état de chaque mode dans un format standardisé. Chaque mode a ses propres endpoints : positions, performance, trades récents, configuration, et statut. Le dashboard (gen-status-page.js) consomme cette API pour afficher la vue unifiée.

// portfolio/v1/status.json — vue agrégée de tous les modes
{
  "generated": "2026-06-21T22:00:00Z",
  "modes": {
    "dynamic":  { "status": "live",      "positions": 8,  "dd": "-2.1%", "weekReturn": "+1.4%" },
    "balanced": { "status": "live",      "positions": 12, "dd": "-3.2%", "weekReturn": "+0.8%" },
    "tkl":      { "status": "stopped",   "positions": 0,  "dd": "0.0%",  "weekReturn": "0.0%"  },
    "orbit":    { "status": "deploying", "positions": 4,  "dd": "-0.8%", "weekReturn": "+0.5%" },
    "alpha":    { "status": "stopped",   "positions": 0,  "dd": "0.0%",  "weekReturn": "0.0%"  },
    "fortress": { "status": "pausing",   "positions": 3,  "dd": "-0.5%", "weekReturn": "+0.1%" }
  }
}

Source : Alpaca — Account Activities API

Multi-Devises et Settlement

Vous achetez ASML sur Euronext en euros. Votre base currency est le dollar. Le broker vous montre la position en euros. Votre ledger doit la convertir en dollars — mais à quel taux ? Celui du moment de l'achat ? Celui de la clôture ? Celui du broker, qui peut différer de l'ECB de 15 bps ?

Les 4 couches de devise

Couche Définition Exemple
Trade currency La devise de l'instrument — déterminée par l'exchange EUR pour ASML (Euronext), USD pour AAPL (NYSE), BTC pour BTC-USD
Settlement currency La devise dans laquelle le broker règle — peut différer de la trade currency IBKR peut régler ASML en EUR ou en USD (conversion automatique)
Base currency Votre devise de reporting — celle dans laquelle vous mesurez la performance USD pour DailyTickers (tous les P&L sont exprimés en USD)
FX rate source Le taux de change utilisé pour la conversion Broker rate (IBKR), ECB mid-rate, Yahoo Finance spot

Gestion dans le ledger

La règle fondamentale : stocker les deux montants. Le montant en devise de trading ET l'équivalent en base currency, plus le taux FX utilisé. Cela permet de calculer a posteriori les gains/pertes de change réalisés.

// Transaction multi-devise dans le ledger
{
  "id": "tx-4201",
  "side": "BUY",
  "ticker": "ASML",
  "qty": 10,
  "price": 920.50,           // en EUR (trade currency)
  "tradeCurrency": "EUR",
  "fxRate": 1.0845,           // EUR/USD au moment du fill
  "priceBase": 998.28,        // 920.50 × 1.0845 = USD
  "totalBase": 9982.80,       // 10 × 998.28
  "fees": 4.00,              // EUR
  "feesBase": 4.34,           // USD
  "broker": "ibkr",
  "mode": "balanced",
  "timestamp": "2026-06-15T09:15:00Z"
}

// Plus tard, à la vente — le FX a bougé
{
  "id": "tx-4315",
  "side": "SELL",
  "ticker": "ASML",
  "qty": 10,
  "price": 940.00,           // +2.1% en EUR
  "fxRate": 1.0720,           // EUR/USD a baissé de 1.15%
  "priceBase": 1007.68,       // 940.00 × 1.0720
  // P&L en EUR : +195 (gain)
  // P&L en USD : +94 (le gain EUR est mangé par le FX adverse)
  // FX P&L seul : -101 (perte de change)
}

Settlement timing

Marché Settlement Exemple Impact buying power
US Equities T+1 Achat lundi → cash débité mardi Le cash est bloqué mais la position existe immédiatement
EU Equities T+2 Achat lundi → cash débité mercredi 2 jours de float — attention au double-spending
Crypto (spot) T+0 Achat instantané → cash débité immédiatement Pas de float, pas de risque de settlement
Forex (spot) T+2 Trade lundi → settlement mercredi Le P&L est comptabilisé immédiatement mais le cash bouge en T+2
Futures Daily MtM Margin ajustée chaque jour à la clôture Cash impacté quotidiennement, pas au settlement final

Le ledger doit tracker les deux dates : la trade date (quand vous décidez) et la settlement date (quand le cash bouge). La trade date détermine le P&L. La settlement date détermine le buying power. Confondre les deux mène à des ordres rejetés pour insuffisance de fonds.

Le FX n'est pas un détail

Sur un portefeuille global, les gains et pertes de change peuvent représenter 5 à 15% du P&L total — dans un sens ou dans l'autre. Un trade gagnant de +2% en euros peut devenir un trade perdant en dollars si l'EUR/USD chute de 3%. Ne jamais ignorer le FX dans le reporting.

Source : ECB — Euro Reference Exchange Rates

Scaling : De 1 à N Modes × M Brokers

DailyTickers n'est pas né avec 6 modes et 4 brokers. Le système a évolué en 5 phases, chacune forçant une refonte architecturale. Voici la chronologie — et surtout, ce qui a cassé à chaque étape.

Les 5 phases de scaling

Phase Configuration Ce qui a cassé Ce qui a été construit
Phase 1
Début 2026
1 mode (dynamic), 1 broker (Alpaca paper) Rien — mais le P&L était dans un seul fichier JSON sans structure pit-state.json — premier ledger, format plat
Phase 2
Mars 2026
3 modes (dynamic, balanced, secured), Alpaca Les 3 modes écrivaient dans le même pit-state.json → conflits, positions mélangées modes-config.json — séparation par mode, config per-mode
Phase 3
Avril 2026
3 modes + IBKR pour le live Les formats Alpaca et IBKR sont incompatibles. Le routeur ne savait pas vers quel broker envoyer. Adapter pattern + Unified Order Router + Reconciliation engine
Phase 4
Mai-Juin 2026
6 modes (+ TKL, Orbit/Alpha, Fortress) + crypto/forex en test Problème de status management (quand pauser un mode ? comment relancer ?), FX non géré Mode Status State Machine (8 états), multi-currency support, Portfolio API v1
Phase 5
Planifié
+ metals/mines, forex dédié, geographic routing (À venir) Volume de transactions, latence de réconciliation, regulatory per-broker Daily snapshots, archive cold storage, routing géographique

Configuration management

Tout le système est piloté par un fichier unique : modes-config.json. Il contient, pour chaque mode :

// Extrait de modes-config.json — configuration du mode Dynamic
{
  "dynamic": {
    "label": "Dynamic",
    "status": "live",
    "statusSince": "2026-03-01",
    "statusReason": "Initial launch",
    "statusNextReviewAt": "2026-07-01",
    "broker": "ibkr",
    "brokerFallback": "alpaca",
    "portfolioSize": 50000,
    "maxPositions": 10,
    "maxDD": 0.08,            // 8% → triggers pausing
    "atrMultiplier": 2.5,
    "trailingActivation": 1.5, // R-multiples
    "regimeFilters": {
      "riskOff": { "maxNewEntries": 0 },
      "earlyRiskOff": { "maxNewEntries": 2, "sizingMultiplier": 0.5 }
    },
    "mcapTiers": {
      "reject": "< 2B",
      "halfSize": "2B-10B",
      "reducedSize": "10B-50B"
    }
  }
}

Chaque modification de configuration est loguée dans modes-config-history.json — un fichier append-only qui sert d'audit trail. On peut reconstruire l'état exact de n'importe quel mode à n'importe quelle date.

Performance à l'échelle

Quand le nombre de transactions dépasse 10 000, les requêtes sur le ledger brut deviennent lentes. Trois optimisations :

Optimisation Mécanisme Gain
Daily snapshots Chaque soir, matérialiser les positions courantes dans un fichier séparé. Pour le P&L du jour, lire le snapshot d'hier + les transactions d'aujourd'hui. O(transactions_du_jour) au lieu de O(toutes_transactions)
Archive cold storage Transactions > 1 an → compressées et archivées. Le ledger actif ne contient que l'année en cours. Taille du fichier réduite de 90%, parsing 10× plus rapide
Index par mode + ticker Un index secondaire qui mappe (mode, ticker) → liste de transaction IDs pour des lookups rapides sans scanner le ledger complet. Lookup d'une position spécifique en O(1) au lieu de O(n)

Source : SQLite — When to Use SQLite

Checklist Multi-Broker

Gérer plusieurs brokers semble complexe. En réalité, le système est plus transparent qu'un mono-broker — et c'est un avantage. Chaque décision est traçable, chaque flux est vérifiable, chaque erreur est détectable. Voici la checklist pour construire le vôtre.

Les 8 principes du multi-broker

  • Un ledger, une vérité — Append-only, immuable, reconstructible à partir des transactions brutes. Le broker est un canal d'exécution, pas la source de vérité.
  • Adapter par broker — Normaliser les formats (positions, ordres, fills, devises) AVANT qu'ils n'entrent dans le système. Le reste du code ne voit qu'un format unifié.
  • Réconcilier quotidiennement — Comparer ledger vs broker chaque jour. Classer les écarts (missing, phantom, price drift). Alerter au-dessus du seuil.
  • Router intelligemment — Asset class → broker primaire, avec fallback. Optimiser les coûts. Vérifier la disponibilité avant de router.
  • Monitorer en temps réel — P&L, drawdown, exposure, fill rate, réconciliation, cash utilization. Seuils d'alerte connectés à Discord.
  • Gérer les devises explicitement — Stocker montant trade + montant base + taux FX. Le P&L inclut les gains/pertes de change. Tracker trade date ET settlement date.
  • Statuer chaque mode — La state machine 8 états (draft → live → paused) protège contre les transitions dangereuses. Un mode en pausing ne prend plus de nouvelles positions mais laisse les existantes se fermer naturellement.
  • Versionner la config — modes-config-history.json = audit trail complet. On peut reconstruire l'état de n'importe quel mode à n'importe quelle date. Les war rooms utilisent cet historique pour analyser les décisions passées.

Le coût de cette transparence est un investissement initial en architecture — les adapters, le ledger, la réconciliation. Mais une fois en place, le système est plus robuste, plus auditable, et plus extensible qu'un mono-broker ne le sera jamais.

Pour aller plus loin

Sujet Article
Valider les données avant publication Harnesses IA en Finance
Le pipeline scanner de bout en bout Inside the Scanner Pipeline
Tester honnêtement les stratégies Backtesting Honnête
Automatiser la publication Claude Code Trading Bot

Source : Investopedia — Reconciliation in Accounting

Intro Architecture Ledger Routage Réconciliation Monitoring Devises Scaling Checklist