@@ -444,7 +444,7 @@ def execute_buy_order(self, symbol: str, usdc_amount: float) -> Dict:
444444 return {'success' : False , 'error' : str (e )}
445445
446446 def execute_sell_order_with_stop_loss (self , symbol : str , bought_quantity : float , buy_price : float , profit_target : float , buy_transaction_id : int = None ) -> Dict :
447- """Exécute un ordre OCO avec profit + stop-loss + INSERTION EN BASE"""
447+ """Exécute un ordre OCO avec profit + stop-loss + INSERTION EN BASE BULLETPROOF """
448448 try :
449449 # Configuration des ordres
450450 future_transfer_enabled = self .advanced_config .get ('future_transfer_enabled' , True )
@@ -610,7 +610,7 @@ def execute_sell_order_with_stop_loss(self, symbol: str, bought_quantity: float,
610610
611611 self .logger .info (f"✅ ORDRE OCO PLACÉ { symbol } " )
612612
613- # 🔥 EXTRACTION AMÉLIORÉE DES IDs D'ORDRES
613+ # 🔥 EXTRACTION IDS AVEC LOGIQUE DIAGNOSTIQUE CORRECTE
614614 profit_order_id = None
615615 stop_order_id = None
616616 oco_order_list_id = oco_order .get ('orderListId' , '' )
@@ -620,13 +620,15 @@ def execute_sell_order_with_stop_loss(self, symbol: str, bought_quantity: float,
620620 orders = oco_order .get ('orders' , [])
621621 self .logger .debug (f"🔍 Orders in OCO: { len (orders )} " )
622622
623+ # 🎯 LOGIQUE BASÉE SUR VOTRE DIAGNOSTIC RÉUSSI
623624 for i , order in enumerate (orders ):
624625 order_id = order .get ('orderId' )
625626 order_type = order .get ('type' )
626627 order_side = order .get ('side' , '' )
627628
628629 self .logger .debug (f" Order { i + 1 } : ID={ order_id } , Type={ order_type } , Side={ order_side } " )
629630
631+ # ✅ LOGIQUE EXACTE IDENTIFIÉE PAR VOTRE DIAGNOSTIC
630632 if order_type == 'LIMIT_MAKER' :
631633 profit_order_id = order_id
632634 self .logger .info (f" 📈 Limite profit: { profit_order_id } " )
@@ -642,7 +644,7 @@ def execute_sell_order_with_stop_loss(self, symbol: str, bought_quantity: float,
642644 if not stop_order_id :
643645 self .logger .warning (f"⚠️ STOP_ORDER_ID non trouvé dans la réponse OCO !" )
644646
645- # 🔥 INSERTION EN BASE (PARTIE CRUCIALE!)
647+ # 🔥 INSERTION EN BASE BULLETPROOF
646648 try :
647649 oco_db_id = self .database .insert_oco_order (
648650 symbol = symbol ,
@@ -658,9 +660,19 @@ def execute_sell_order_with_stop_loss(self, symbol: str, bought_quantity: float,
658660
659661 self .logger .info (f"💾 Ordre OCO enregistré en base (DB ID: { oco_db_id } )" )
660662
663+ # Log de vérification insertion
664+ if profit_order_id and stop_order_id :
665+ self .logger .info (f"✅ INSERTION COMPLÈTE avec les 2 IDs" )
666+ elif profit_order_id or stop_order_id :
667+ self .logger .warning (f"⚠️ INSERTION PARTIELLE (1 ID manquant)" )
668+ else :
669+ self .logger .error (f"❌ INSERTION SANS IDs (problème critique)" )
670+
661671 except Exception as db_error :
662672 # L'ordre est placé sur Binance mais pas en base - log l'erreur
663673 self .logger .error (f"❌ Erreur insertion OCO en base: { db_error } " )
674+ import traceback
675+ self .logger .debug (traceback .format_exc ())
664676 oco_db_id = None
665677
666678 return {
@@ -727,7 +739,7 @@ def monitor_oco_orders(self):
727739
728740 for oco_order in active_oco_orders :
729741 try :
730- # Vérification DOUBLE pour plus de fiabilité
742+ # Vérification avec méthode robuste
731743 was_updated = self ._check_oco_status_enhanced (oco_order )
732744 if was_updated :
733745 updated_count += 1
@@ -742,15 +754,15 @@ def monitor_oco_orders(self):
742754 self .logger .error (f"❌ Erreur surveillance OCO: { e } " )
743755
744756 def _check_oco_status_enhanced (self , oco_order : Dict ) -> bool :
745- """Version améliorée de vérification OCO avec double vérification """
757+ """Version robuste de vérification OCO avec détection d'exécution """
746758 try :
747759 symbol = oco_order ['symbol' ]
748760 profit_order_id = oco_order .get ('profit_order_id' )
749761 stop_order_id = oco_order .get ('stop_order_id' )
750762
751763 # VÉRIFICATION DIRECTE des ordres individuels (plus fiable)
752764 for order_id , order_type in [(profit_order_id , 'PROFIT' ), (stop_order_id , 'STOP' )]:
753- if not order_id :
765+ if not order_id or order_id == '' :
754766 continue
755767
756768 try :
@@ -775,7 +787,7 @@ def _check_oco_status_enhanced(self, oco_order: Dict) -> bool:
775787 return False
776788
777789 def _handle_oco_execution_direct (self , oco_order : Dict , executed_order : Dict , execution_type : str ):
778- """🔥 VERSION CORRIGÉE - Traite l'exécution OCO avec création transaction BULLETPROOF"""
790+ """🔥 Traite l'exécution OCO avec création transaction BULLETPROOF"""
779791 try :
780792 symbol = oco_order ['symbol' ]
781793 oco_order_id = oco_order ['oco_order_id' ]
@@ -808,7 +820,7 @@ def _handle_oco_execution_direct(self, oco_order: Dict, executed_order: Dict, ex
808820 execution_type
809821 )
810822
811- # 2. 🔥 CRÉER LA TRANSACTION DE VENTE (MÉTHODE BULLETPROOF)
823+ # 2. 🔥 CRÉER LA TRANSACTION DE VENTE (BULLETPROOF)
812824 try :
813825 # Vérifier si la transaction existe déjà - MÉTHODE SQL DIRECTE
814826 cursor = self .database .conn .execute (
@@ -844,267 +856,3 @@ def _handle_oco_execution_direct(self, oco_order: Dict, executed_order: Dict, ex
844856 self .logger .error (f"❌ Erreur traitement exécution directe: { e } " )
845857 import traceback
846858 self .logger .debug (traceback .format_exc ())
847-
848- def _check_oco_status (self , oco_order : Dict ):
849- """Vérifie le statut d'un ordre OCO sur Binance avec méthode robuste - VERSION LEGACY"""
850- try :
851- symbol = oco_order ['symbol' ]
852- oco_order_id = oco_order ['oco_order_id' ]
853- profit_order_id = oco_order .get ('profit_order_id' )
854- stop_order_id = oco_order .get ('stop_order_id' )
855-
856- # 🔥 MÉTHODE 1: Vérifier via les ordres ouverts (plus fiable)
857- try :
858- open_orders = self .binance_client ._make_request_with_retry (
859- self .binance_client .client .get_open_orders ,
860- symbol = symbol
861- )
862-
863- # Chercher nos ordres dans la liste
864- profit_found = False
865- stop_found = False
866- oco_found = False
867-
868- for order in open_orders :
869- order_list_id = str (order .get ('orderListId' , - 1 ))
870- order_id = str (order .get ('orderId' ))
871-
872- # Vérifier si c'est notre OCO
873- if order_list_id == str (oco_order_id ):
874- oco_found = True
875-
876- # Vérifier les ordres individuels aussi (backup)
877- if profit_order_id and order_id == str (profit_order_id ):
878- profit_found = True
879- if stop_order_id and order_id == str (stop_order_id ):
880- stop_found = True
881-
882- if oco_found or profit_found or stop_found :
883- # OCO encore actif
884- self .logger .debug (f"📊 OCO { symbol } toujours actif (oco:{ oco_found } , profit:{ profit_found } , stop:{ stop_found } )" )
885- return
886-
887- # OCO plus dans les ordres ouverts = exécuté !
888- self .logger .info (f"🎯 OCO { symbol } n'est plus actif → Recherche dans l'historique" )
889-
890- except Exception as open_orders_error :
891- self .logger .warning (f"⚠️ Erreur vérification ordres ouverts: { open_orders_error } " )
892-
893- # 🔥 MÉTHODE 2: Vérifier dans l'historique récent
894- try :
895- # Historique des dernières 24h
896- yesterday = datetime .now () - timedelta (hours = 24 )
897- start_time = int (yesterday .timestamp () * 1000 )
898-
899- all_orders = self .binance_client ._make_request_with_retry (
900- self .binance_client .client .get_all_orders ,
901- symbol = symbol ,
902- startTime = start_time ,
903- limit = 100
904- )
905-
906- # Chercher les ordres de notre OCO
907- executed_orders = []
908- for order in all_orders :
909- order_list_id = str (order .get ('orderListId' , - 1 ))
910-
911- if order_list_id == str (oco_order_id ):
912- executed_orders .append (order )
913-
914- if executed_orders :
915- self .logger .info (f"📜 Trouvé { len (executed_orders )} ordres dans l'historique pour OCO { oco_order_id } " )
916-
917- # Analyser les ordres exécutés
918- for order in executed_orders :
919- status = order .get ('status' )
920- order_type = order .get ('type' )
921-
922- self .logger .info (f" - Status: { status } , Type: { order_type } " )
923-
924- if status == 'FILLED' :
925- # Ordre exécuté ! Traiter l'exécution
926- self ._handle_oco_execution_from_history (oco_order , executed_orders )
927- return
928-
929- # Si aucun FILLED mais ordres trouvés = probablement annulés
930- self .logger .warning (f"⚠️ OCO { oco_order_id } trouvé dans l'historique mais aucun ordre FILLED" )
931- self .database .update_oco_execution (oco_order_id , 'EXPIRED_OR_CANCELED' , 0 , 0 , 'UNKNOWN' )
932-
933- else :
934- self .logger .warning (f"❓ OCO { oco_order_id } non trouvé dans l'historique récent" )
935-
936- except Exception as history_error :
937- self .logger .error (f"❌ Erreur vérification historique: { history_error } " )
938-
939- except Exception as e :
940- self .logger .error (f"❌ Erreur vérification OCO { oco_order .get ('symbol' , 'UNKNOWN' )} : { e } " )
941-
942- def _handle_oco_execution (self , oco_order : Dict , binance_status : Dict ):
943- """Traite l'exécution d'un ordre OCO avec commissions réelles - VERSION LEGACY"""
944- try :
945- symbol = oco_order ['symbol' ]
946- oco_order_id = oco_order ['oco_order_id' ]
947-
948- # Déterminer quel ordre s'est exécuté
949- executed_order = None
950- execution_type = None
951-
952- for order in binance_status .get ('orders' , []):
953- if order ['status' ] == 'FILLED' :
954- executed_order = order
955- if str (order ['orderId' ]) == str (oco_order .get ('profit_order_id' , '' )):
956- execution_type = 'PROFIT'
957- elif str (order ['orderId' ]) == str (oco_order .get ('stop_order_id' , '' )):
958- execution_type = 'STOP_LOSS'
959- break
960-
961- if executed_order and execution_type :
962- # Prix et quantité d'exécution
963- exec_price = float (executed_order ['price' ])
964- exec_qty = float (executed_order ['executedQty' ])
965-
966- # Commission réelle (si disponible)
967- commission = float (executed_order .get ('commission' , 0.0 ))
968- commission_asset = executed_order .get ('commissionAsset' , 'USDC' )
969-
970- # Log de l'événement
971- if execution_type == 'PROFIT' :
972- self .logger .info (f"🎯 PROFIT RÉALISÉ { symbol } !" )
973- self .logger .info (f" 📈 Prix: { exec_price :.6f} USDC" )
974- self .logger .info (f" 📦 Quantité: { exec_qty :.8f} " )
975- self .logger .info (f" 💎 Crypto gardée: { oco_order .get ('kept_quantity' , 0 ):.8f} { symbol .replace ('USDC' , '' )} " )
976- if commission > 0 :
977- self .logger .info (f" 💰 Commission: { commission :.8f} { commission_asset } " )
978- new_status = 'PROFIT_FILLED'
979-
980- elif execution_type == 'STOP_LOSS' :
981- self .logger .warning (f"🛡️ STOP-LOSS DÉCLENCHÉ { symbol } " )
982- self .logger .warning (f" 📉 Prix: { exec_price :.6f} USDC" )
983- self .logger .warning (f" 📦 Quantité: { exec_qty :.8f} " )
984- self .logger .warning (f" 💎 Crypto gardée: { oco_order .get ('kept_quantity' , 0 ):.8f} { symbol .replace ('USDC' , '' )} " )
985- if commission > 0 :
986- self .logger .warning (f" 💰 Commission: { commission :.8f} { commission_asset } " )
987- new_status = 'STOP_FILLED'
988-
989- # Mettre à jour la DB
990- self .database .update_oco_execution (
991- oco_order_id ,
992- new_status ,
993- exec_price ,
994- exec_qty ,
995- execution_type
996- )
997-
998- # 🔥 AUSSI CRÉER LA TRANSACTION DE VENTE (AJOUT CRUCIAL)
999- try :
1000- # Vérifier si elle existe déjà
1001- cursor = self .database .conn .execute (
1002- "SELECT id FROM transactions WHERE order_id = ? AND order_side = 'SELL'" ,
1003- (str (executed_order ['orderId' ]),)
1004- )
1005- existing_tx = cursor .fetchone ()
1006-
1007- if not existing_tx :
1008- # Enregistrer la transaction de vente avec commission réelle
1009- self .database .insert_transaction (
1010- symbol = symbol ,
1011- order_id = str (executed_order ['orderId' ]),
1012- transact_time = str (executed_order .get ('time' , int (time .time () * 1000 ))),
1013- order_type = executed_order .get ('type' , 'LIMIT' ),
1014- order_side = 'SELL' , # 🎯 VENTE
1015- price = exec_price ,
1016- qty = exec_qty ,
1017- commission = commission , # ✅ Commission réelle
1018- commission_asset = commission_asset # ✅ Asset de commission réel
1019- )
1020- self .logger .info (f" 📝 Transaction VENTE créée" )
1021-
1022- except Exception as tx_error :
1023- self .logger .warning (f"⚠️ Erreur création transaction: { tx_error } " )
1024-
1025- self .logger .info (f"💾 Exécution OCO { execution_type } enregistrée en base" )
1026-
1027- else :
1028- self .logger .warning (f"⚠️ OCO { oco_order_id } terminé mais aucun ordre FILLED trouvé" )
1029- # Marquer comme terminé quand même
1030- self .database .update_oco_execution (oco_order_id , 'COMPLETED_UNKNOWN' , 0 , 0 , 'UNKNOWN' )
1031-
1032- except Exception as e :
1033- self .logger .error (f"❌ Erreur traitement exécution OCO: { e } " )
1034- import traceback
1035- self .logger .debug (traceback .format_exc ())
1036-
1037- def _handle_oco_execution_from_history (self , oco_order : Dict , executed_orders : List [Dict ]):
1038- """🔥 VERSION CORRIGÉE - Traite l'exécution OCO depuis l'historique avec transaction"""
1039- try :
1040- symbol = oco_order ['symbol' ]
1041- oco_order_id = oco_order ['oco_order_id' ]
1042-
1043- # Trouver l'ordre FILLED
1044- filled_order = None
1045- execution_type = None
1046-
1047- for order in executed_orders :
1048- if order ['status' ] == 'FILLED' :
1049- filled_order = order
1050-
1051- # Déterminer le type d'exécution
1052- order_type = order .get ('type' )
1053- if order_type == 'LIMIT_MAKER' :
1054- execution_type = 'PROFIT'
1055- elif order_type in ['STOP_LOSS_LIMIT' , 'STOP_LOSS' ]:
1056- execution_type = 'STOP_LOSS'
1057- break
1058-
1059- if filled_order and execution_type :
1060- exec_price = float (filled_order ['price' ])
1061- exec_qty = float (filled_order ['executedQty' ])
1062- order_id = str (filled_order ['orderId' ])
1063-
1064- if execution_type == 'PROFIT' :
1065- self .logger .info (f"🎯 PROFIT HISTORIQUE DÉTECTÉ { symbol } !" )
1066- new_status = 'PROFIT_FILLED'
1067- else :
1068- self .logger .warning (f"🛡️ STOP-LOSS HISTORIQUE DÉTECTÉ { symbol } " )
1069- new_status = 'STOP_FILLED'
1070-
1071- # Mettre à jour la DB
1072- self .database .update_oco_execution (
1073- oco_order_id ,
1074- new_status ,
1075- exec_price ,
1076- exec_qty ,
1077- execution_type
1078- )
1079-
1080- # 🔥 CRÉER LA TRANSACTION DE VENTE (AJOUT CRUCIAL)
1081- try :
1082- # Vérifier si elle existe déjà
1083- cursor = self .database .conn .execute (
1084- "SELECT id FROM transactions WHERE order_id = ? AND order_side = 'SELL'" ,
1085- (order_id ,)
1086- )
1087- existing_tx = cursor .fetchone ()
1088-
1089- if not existing_tx :
1090- # Enregistrer la transaction de vente
1091- self .database .insert_transaction (
1092- symbol = symbol ,
1093- order_id = order_id ,
1094- transact_time = str (filled_order .get ('updateTime' , int (time .time () * 1000 ))),
1095- order_type = filled_order .get ('type' , 'LIMIT' ),
1096- order_side = 'SELL' , # 🎯 VENTE
1097- price = exec_price ,
1098- qty = exec_qty ,
1099- commission = float (filled_order .get ('commission' , 0.0 )),
1100- commission_asset = filled_order .get ('commissionAsset' , 'USDC' )
1101- )
1102- self .logger .info (f" 📝 Transaction VENTE historique créée" )
1103-
1104- except Exception as tx_error :
1105- self .logger .warning (f"⚠️ Erreur création transaction historique: { tx_error } " )
1106-
1107- self .logger .info (f"💾 Exécution OCO historique enregistrée" )
1108-
1109- except Exception as e :
1110- self .logger .error (f"❌ Erreur traitement exécution historique: { e } " )
0 commit comments