Skip to main content

État CLOSED - Fermeture de Position

📊 Statut des Tests

Tests Frontend (Checkly)

checkly-frontend no-tests

Tests Backend (Vitest)

vitest-backend failing

🔗 Avant cette étape

🎯 Vue d’ensemble

L’état CLOSED correspond à une position fermée. Une position peut être fermée pour deux raisons principales : changement de tendance ou Stop Loss touché.

🔄 Transition RUNNING → CLOSED

Conditions de fermeture

  1. Changement de tendance :
    • LONG : candle.wma50 < wma50_htf
    • SHORT : candle.wma50 > wma50_htf
  2. Stop Loss touché :
    • SL de BUY2 touché (BUY2 fermé) → Fermeture complète
    • Note : SL de BUY1 touché (BUY2 non fermé) → Position reste en RUNNING
Note : Le Stop Loss est désormais géré par sharkModeCron.ts qui surveille les bougies 1m et met à jour le SL de manière agressive basé sur le Heikin Ashi. La fermeture par SL est gérée directement via processClose() qui crée l’ordre Close, puis la position passe en CLOSED après l’exécution de l’ordre.

Actions lors de la fermeture

  • Annulation des ordres TP1/TP2 ouverts
  • Création d’un ordre de fermeture (market)
  • Après l’exécution de l’ordre Close : status = CLOSED
  • Calcul des coûts finaux (finalBuyCost, finalSellCost)
  • Calcul des frais finaux (finalBuyFee, finalSellFee)
  • Notification envoyée

Ordre de fermeture (processClose)

Calcul du montant à fermer selon le contexte :
  1. SL touché + BUY2 non fermé :
  2. SL touché + BUY2 fermé :
  3. Fermeture normale (changement de tendance) :
Vérification préalable :
  • Si calculateRelativeAmount(position) <= 0 : Ne pas créer d’ordre Close, passer directement en CLOSED
La valeur relativeAmount stockée en base est conservée pour debug/audit, mais la logique de fermeture doit se baser sur le calcul dynamique.
Note : Pour les fermetures par SL (touché ou sécurité), les frais ne sont pas soustraits du montant à fermer car ils sont gérés séparément lors de l’exécution de l’ordre.

📋 Variantes de Fermeture

Variante 1 : Fermeture par changement de tendance

Séquence :
Comportement attendu :
  • Détection du changement de tendance via le Range Filter : trend bull / bear sur le webhook (plus de condition de clôture basée sur WMA50 LTF vs HTF pour ce déclencheur).
  • Annulation des ordres TP1/TP2 ouverts
  • Création d’un ordre Close (market) pour vendre relativeAmount + reserveAmount - totalBaseAssetFees
  • Après exécution de l’ordre Close : status = CLOSED, closedReason = TREND_CHANGED
  • Calcul des coûts et frais finaux
  • Notification envoyée

Variante 2 : Fermeture par Stop Loss (SL_BUY2)

Séquence :
Comportement attendu :
  • SL de BUY2 touché (BUY2 fermé)
  • slMode = SL_BUY2 défini
  • Création d’un ordre Close pour vendre relativeAmount + reserveAmount
  • Après exécution de l’ordre Close : status = CLOSED, closedReason = REACHED_STOP_LOSS
  • Fermeture complète de la position
  • Calcul des coûts et frais finaux
  • Notification envoyée

Variante 3 : SL de BUY1 touché (position reste RUNNING)

Séquence :
Comportement attendu :
  • SL de BUY1 touché (BUY2 non fermé)
  • slMode = SL_BUY1 défini
  • Création d’un ordre Close pour vendre seulement relativeAmount (pas reserveAmount)
  • Après exécution de l’ordre Close : Position reste en RUNNING (pas CLOSED)
  • relativeAmount mis à jour à 0
  • La position continue d’être traitée pour permettre l’exécution future de BUY2
  • La position ne sera fermée que si :
    • BUY2 est exécuté puis son SL est touché (passage en SL_BUY2)
    • La tendance change (création d’un ordre Close)
Note importante : Cette variante ne ferme PAS la position, elle vend seulement le montant de BUY1. La position reste active pour permettre l’exécution de BUY2.

🔍 Logique de Fermeture Détaillée

Après exécution de l’ordre Close

La fermeture de la position après l’exécution de l’ordre Close est déterminée par slMode et le status, pas par calculatedRelativeAmount.

Cas 1 : SL_BUY2 (SL de BUY2 touché)

  • Condition : slMode === 'SL_BUY2' ET closeOrder.status === 'closed'
  • Comportement :
    • Fermeture complète de la position (l’ordre Close a vendu relativeAmount + reserveAmount)
    • status = CLOSED
    • closedReason = REACHED_STOP_LOSS
    • closedAt = new Date()
    • relativeAmount = 0
    • ✅ Calcul des coûts finaux (finalBuyCost, finalSellCost)
    • ✅ Calcul des frais finaux (finalBuyFee, finalSellFee)
    • ✅ Notification envoyée

Cas 2 : SL_BUY1 (SL de BUY1 touché, BUY2 non fermé)

  • Condition : slMode === 'SL_BUY1' ET buy2IsClosed === false ET closeOrder.status === 'closed'
  • Comportement :
    • La position reste en RUNNING même après l’exécution de l’ordre Close
    • ✅ L’ordre Close a vendu seulement relativeAmount (pas reserveAmount)
    • relativeAmount est mis à jour à 0
    • ✅ La position continue d’être traitée par swingPositions pour permettre l’exécution future de BUY2
    • ✅ La position ne sera fermée que si :
      • BUY2 est exécuté puis son SL est touché (passage en SL_BUY2)
      • La tendance change (création d’un ordre Close)

Cas 3 : Fermeture normale (changement de tendance)

  • Condition : closeOrder.status === 'closed' ET slMode !== 'SL_BUY1' (ou slMode === null)
  • Comportement :
    • status = CLOSED
    • closedReason = TREND_CHANGED
    • closedAt = new Date()
    • ✅ Calcul des coûts finaux (finalBuyCost, finalSellCost)
    • ✅ Calcul des frais finaux (finalBuyFee, finalSellFee)
    • ✅ Notification envoyée

📊 Calculs Finaux

Coûts finaux

Frais finaux

PnL final

🧪 Tests

Tests Frontend

Les tests frontend vérifient l’affichage des positions fermées et l’historique. Fichiers de tests : features/positions/closed-position.spec.ts Configuration Checkly :

Tests Backend

Les tests backend valident la logique de fermeture et les différents scénarios. Fichiers de tests : packages/functions/src/tests/positions/closed-state.test.ts Exécution :

🔗 Navigation