Workflow et Diagramme des Positions Swing
🎯 Vue d’ensemble
Ce document décrit le workflow complet des positions swing avec toutes les conditions et transitions d’état. Il inclut un diagramme Mermaid pour visualiser le flux complet.📊 Diagramme de Workflow Complet
🔄 Transitions d’État Détaillées
NEW → RUNNING
Conditions requises :buy1Order.status === 'closed'buy1Order.averageOUbuy1Order.priceexistenetAmount > 0(buy1Amount - reserveAmount)
- Calcul de
buy1Amount,buy1Price,avgEntryPrice - Calcul de
relativeAmount = buy1Amount - reserveAmount - Calcul des prix TP1/TP2 basés sur
avgMaxPnlForPairavec valeur par défaut de 2.0% si non disponible - Calcul des montants TP1/TP2 (70% / 30% de
relativeAmount) - Création des ordres TP1 et TP2
- Mise à jour
status = RUNNING
RUNNING → CLOSING
Conditions de fermeture :- Changement de tendance :
- LONG :
candle.wma50 < wma50_htf - SHORT :
candle.wma50 > wma50_htf
- LONG :
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 dans processStopLossOrder() qui passe la position en CLOSED.
Actions :
- Annulation des ordres TP1/TP2 ouverts
- Création d’un ordre de fermeture (market)
status = CLOSING
🦈 Gestion du Shark Mode
Le Shark Mode est un mode de gestion agressive du Stop Loss activé automatiquement lorsque le prix évolue favorablement d’au moins 0.5% par rapport au prix d’entrée.Activation du Shark Mode
Conditions :- Position en statut
RUNNINGouNEW - Évolution du prix ≥ 0.5% par rapport au prix d’entrée (
avgEntryPriceourelativeEntryPrice) - Calcul basé sur les bougies Heikin Ashi 1m
sharkModeEnabled = truesharkModeEnabledAt = Date.now()- Ajout d’une activité
SHARK_MODE_ACTIVATEDavec le pourcentage d’évolution
Mise à jour du Stop Loss en Shark Mode
Fréquence : Chaque minute (cron job) Calcul du SL agressif :- LONG : SL =
haLow(low de la dernière bougie Heikin Ashi) - SHORT : SL =
haHigh(high de la dernière bougie Heikin Ashi) - Le SL ne peut que se rapprocher du prix (monter pour LONG, descendre pour SHORT)
sharkModePrice :
- Si
buy2Order.status === 'closed':sharkModePrice = avgEntryPrice - Le
sharkModePricereprésente le prix d’entrée de référence pour le Shark Mode après BUY2
Gestion du SL Touché en Shark Mode
Cas 1 : BUY2 non fermé + SL touché
Comportement :- ✅ Le status reste RUNNING (ne pas passer en CLOSING)
- ✅ Créer un ordre Close pour vendre
relativeAmount(si > 0) - ✅ Ajouter une activité
SL_TOUCHED - ✅ Utiliser
processClose()pour créer l’ordre Close (position reste en RUNNING)
relativeAmount restant (montant de BUY1 moins TP1/TP2 déjà vendus). La position reste active car BUY2 peut encore être exécuté.
Cas 2 : BUY2 fermé + SL touché
Comportement :- ✅ Passer en CLOSING avec
closedReason = REACHED_STOP_LOSS - ✅ Créer un ordre Close pour vendre
relativeAmount + reserveAmount(si relativeAmount > 0) - ✅ Ajouter une activité
SL_TOUCHED - ✅ Utiliser
processClose()pour gérer la fermeture (annulation TP1/TP2, création ordre market)
SL de Sécurité basé sur lastCandle
En plus du SL agressif basé sur le Heikin Ashi, un SL de sécurité est surveillé basé surlastCandle.low (LONG) ou lastCandle.high (SHORT).
Conditions de déclenchement :
- LONG : Prix 1m (
haClose) ≤lastCandle.low - SHORT : Prix 1m (
haClose) ≥lastCandle.high
Cas 1 : BUY2 non fermé + SL de sécurité touché
Comportement :- ✅ Le status reste RUNNING (ne pas passer en CLOSING)
- ✅ Créer un ordre Close pour vendre
relativeAmount(si > 0) - ✅ Ajouter une activité
SAFETY_SL_TOUCHED - ✅ Utiliser
processClose()pour créer l’ordre Close (position reste en RUNNING)
Cas 2 : BUY2 fermé + SL de sécurité touché
Comportement :- ✅ Passer en CLOSING avec
closedReason = REACHED_STOP_LOSS - ✅ Créer un ordre Close pour vendre
relativeAmount + reserveAmount(si relativeAmount > 0) - ✅ Ajouter une activité
SAFETY_SL_TOUCHED - ✅ Utiliser
processClose()pour gérer la fermeture (annulation TP1/TP2, création ordre market)
Ordre de fermeture (processClose)
Calcul du montant à fermer selon le contexte :-
SL touché + BUY2 non fermé :
-
SL touché + BUY2 fermé :
-
Fermeture normale (changement de tendance) :
- Si
relativeAmount <= 0: Ne pas créer d’ordre Close, passer directement en CLOSED
CLOSING → CLOSED
Conditions :closeOrder.status === 'closed'
- Calcul des coûts finaux (
finalBuyCost,finalSellCost) - Calcul des frais finaux (
finalBuyFee,finalSellFee) status = CLOSEDclosedReason = TREND_CHANGED | REACHED_STOP_LOSSclosedAt = new Date()- Notification envoyée
📋 Conditions de Création de Position
Condition LONG
Condition SHORT
Vérifications de Budget
totalBudget <= availableBudget(balance libre - budget actif)minNotionalrespecté pour TP2 après BUY1reserveAmountcalculé pour garantir minNotional sur TP2
🔍 Gestion des Ordres en RUNNING
BUY2 (processBuy2)
Conditions de création/remplacement :buy2Order.status !== 'closed'buy2Ordern’existe pas OU prix différent OU annulé
- Mise à jour de
buy2Order,buy2Amount,buy2Price - Recalcul de
relativeEntryPrice(moyenne pondérée de BUY1 et BUY2) - Mise à jour de
relativeAmountviacalculateRelativeAmount()
buy2Amount après fermeture de TP1/TP2 sont gérés dans processTp1() et processTp2(), pas dans processBuy2(). Les recalculs et archivages des TP sont également gérés dans leurs fonctions respectives.
TP1 (processTp1)
Conditions de création/remplacement :tp1Order.status !== 'closed'tp1Ordern’existe pas OU montant différent OU annulé
- Archive de TP1 dans
archivedTp1Orders(si nécessaire) - Calcul de
relativeAmountviacalculateRelativeAmount()qui prend en compte tous les ordres fermés - Si BUY2 pas encore fermé : ajustement de
buy2Amountpour inclure le montant TP1 vendu
TP2 (processTp2)
Conditions de création/remplacement :tp2Order.status !== 'closed'tp2Ordern’existe pas OU montant différent OU annulé
- Archive de TP2 dans
archivedTp2Orders(si nécessaire) - Calcul de
relativeAmountviacalculateRelativeAmount()qui prend en compte tous les ordres fermés - Si BUY2 pas encore fermé : ajustement de
buy2Amountpour inclure le montant TP2 vendu
processTp1() qui doit détecter et signaler les anomalies si TP2 est fermé avant TP1.
⚠️ Incohérences Détectées et Solutions
1. Incohérences de Budget
Problème :buy1Budget ou buy2Budget ≠ amount × price
Cause possible :
- Calculs de budget effectués avant l’exécution réelle
- Arrondis lors de la création d’ordre
- Frais non pris en compte dans le budget initial
- Utiliser
amount × pricecomme source de vérité - Recalculer le budget réel après exécution
- Stocker
actualBuy1CostetactualBuy2Cost
2. Incohérences des Montants TP
Problème :tp1Amount + tp2Amount ≠ buy1Amount + buy2Amount
Cause possible :
- Ajustements après fermeture de TP avant BUY2
- Calculs de
relativeAmountincorrects - Archives non prises en compte
- Toujours utiliser
calculateRelativeAmount()pour calculerrelativeAmount - Vérifier que
tp1Amount + tp2Amount + relativeAmount = buy1Amount + buy2Amount - Prendre en compte les TP archivés dans les calculs
3. Incohérences de relativeAmount
Problème :relativeAmount ≠ totalBuy - totalTP
Cause possible :
- Calculs non synchronisés avec les ordres fermés
- TP archivés non pris en compte
- Ajustements de BUY2 non reflétés
- Utiliser
calculateRelativeAmount()qui prend en compte tous les ordres - Vérifier la cohérence après chaque transition d’état
- Ajouter
verifyPositionIntegrity()après chaque modification
4. Erreur de Prix TP pour SHORT
Problème :tp1Price >= buy1Price pour une position SHORT
Cause possible :
- Calcul des prix TP incorrect pour SHORT
- Formule inversée
- Vérifier la formule :
tp1Price = relativeEntryPrice * (1 - (0.5 * avgMaxPnl / 100)) - Pour SHORT : TP1 doit être < entryPrice, TP2 < TP1
- Ajouter une validation avant création d’ordre TP
5. Ordre Chronologique des Activités
Problème : Timestamps d’activités non chronologiques Cause possible :- Activités ajoutées en parallèle
- Timestamps non synchronisés
- Retards dans l’enregistrement
- Utiliser
new Date()au moment de l’ajout - Trier les activités par timestamp avant affichage
- Ajouter un index de séquence si nécessaire
🔧 Fonctions de Vérification
verifyPositionIntegrity()
Vérifie la cohérence de la position après chaque modification :calculateRelativeAmount()
Calcule le montant relatif en prenant en compte tous les ordres :- Prend en compte les ordres TP archivés (cycles multiples)
- Gère les ordres partiellement exécutés puis annulés
- Ne retourne jamais un montant négatif
- Doit être utilisé après chaque modification d’ordre
📝 Checklist de Validation
Avant chaque transition d’état, vérifier :- Budgets cohérents avec les coûts réels
- Montants TP cohérents avec les achats
-
relativeAmountcalculé correctement - Prix TP cohérents avec
orderSide - Ordres archivés correctement
- Activités enregistrées avec timestamps corrects
- Status cohérent avec l’état des ordres
- Pas de double comptage des montants
🔗 Références
- Spécifications des Scénarios : Scénarios détaillés de workflow
- Service SwingPositionService : Implémentation complète
- Schema Prisma : Modèle de données