Skip to content

Claude/connect branch 012 dw yg mqz bs g5 vu9 zueq lyz#30

Open
chpeu wants to merge 71 commits into
claude/chaude3-01PqxWibDgf9uzhkmgRSVRzZfrom
claude/connect-branch-012DwYgMQZBsG5VU9ZueqLYZ
Open

Claude/connect branch 012 dw yg mqz bs g5 vu9 zueq lyz#30
chpeu wants to merge 71 commits into
claude/chaude3-01PqxWibDgf9uzhkmgRSVRzZfrom
claude/connect-branch-012DwYgMQZBsG5VU9ZueqLYZ

Conversation

@chpeu

@chpeu chpeu commented Nov 15, 2025

Copy link
Copy Markdown
Owner

PR Type

Enhancement, Bug fix, Tests


Description

  • Implement WebSocket reconnection callback to resubscribe to monitored symbols

  • Add protection against WebSocket updates during active trading positions

  • Fix price formatting inconsistency by changing 'tickSize' to 'tick_size'

  • Add comprehensive test coverage for dashboard routes, MEXC API, and PostgreSQL logger

  • Fix test failures and improve database connection handling


Diagram Walkthrough

flowchart LR
  WS["WebSocket Manager"] -->|reconnect_callback| PR["Price Provider"]
  PR -->|resubscribe_after_reconnect| WS
  PR -->|monitored_symbols| Storage["Symbol Storage"]
  Scanner["Scanner Loop"] -->|check active_position| Guard["Position Guard"]
  Guard -->|prevent update| Scalability["Scalability Refresh"]
  Position["Position Manager"] -->|tick_size| Frontend["Frontend API"]
  Tests["Test Suite"] -->|coverage| Coverage["88%+ Coverage"]
Loading

File Walkthrough

Relevant files
Enhancement
4 files
price_provider.py
Add WebSocket reconnection callback and symbol resubscription
+68/-8   
reliability.py
Add reconnect callback support to WebSocket manager           
+11/-0   
scanner_loop.py
Restart WebSocket for single position symbol monitoring   
+16/-0   
main.py
Refactor WebSocket management for position-specific monitoring
+32/-16 
Bug fix
3 files
scalability_refresh.py
Prevent WebSocket updates during active positions               
+12/-3   
position_manager.py
Change tickSize to tick_size for consistency                         
+1/-1     
postgresql_datalogger.py
Initialize pool to None when psycopg2 unavailable               
+1/-0     
Tests
4 files
test_dashboard_routes.py
Add comprehensive dashboard routes test coverage                 
+320/-0 
test_mexc.py
Add comprehensive MEXC API client test coverage                   
+355/-0 
test_postgresql_datalogger.py
Skip obsolete tests and fix connection handling                   
+9/-7     
test_simple_pg_logger.py
Add comprehensive PostgreSQL logger test coverage               
+518/-0 

chpeu and others added 12 commits November 11, 2025 02:29
- Add tests for core/simple_pg_logger.py (12.03% → 88.61% coverage)
- Add tests for api/mexc.py (43.66% → 88.73% coverage)
- Add tests for api/routes/dashboard.py

These tests significantly improve coverage for critical low-coverage files.
48/49 tests passing for simple_pg_logger and mexc modules.
**Tests fixes:**
- Fix test_dashboard_routes.py (11 tests)
  - Properly mock verify_api_key using dependency_overrides
  - Fix TRADING_CONFIG patch (use config.TRADING_CONFIG)
  - Fix init_instances patch (use main.init_instances)
  - Fix test_get_status_exception with proper unserializable object
  - Fix test_set_socketio_with_ws_manager mock logic

- Fix test_simple_pg_logger.py (1 test)
  - Fix reconnection test to properly simulate connection closing after error

- Fix test_postgresql_datalogger.py (3 tests)
  - Add self.pool = None when PSYCOPG2_AVAILABLE is False
  - Skip obsolete tests (log_scan_error, log_market_context methods no longer exist)

**Price formatting fix:**
- Change 'tickSize' to 'tick_size' in position_manager.py to_dict()
  - Maintains consistency with Python snake_case convention
  - Frontend already handles both formats (tickSize || tick_size)
  - Fixes price decimal precision inconsistency between backend/frontend

**Test results:**
- 56 passed, 2 skipped
- All critical functionality tests passing
- Coverage improvements maintained
…itions

Problem:
- When a position opens, the scalability scan continued running
- WebSocket would restart with multiple symbols instead of staying on the position symbol
- This caused current_price to freeze during active positions

Solution:
1. scalability_refresh.py: Added check in _update_websocket() to prevent
   WebSocket changes when an active position exists
2. scanner_loop.py: After opening position, explicitly restart WebSocket
   with ONLY the position symbol to ensure price updates continue

This ensures that during a position:
- WebSocket stays focused on the position symbol only
- current_price, TP, and SL prices update correctly
- Scalability refresh doesn't interfere with position tracking
…ce freezing

PROBLÈME CRITIQUE résolu:
- Quand une position s'ouvrait, le WebSocket continuait de monitorer tous les symboles précédents (top_pairs)
- subscribe_ticker() AJOUTAIT juste le symbole de la position aux symboles existants
- Résultat: current_price se figeait car le WebSocket ne se concentrait pas sur le bon symbole

SOLUTION dans main.py ligne 898-919:
- ARRÊTER complètement le WebSocket actuel après ouverture de position
- REDÉMARRER le WebSocket avec UNIQUEMENT le symbole de la position
- Ceci garantit que current_price, TP et SL sont mis à jour en temps réel

Logs ajoutés:
- "🔌 WebSocket arrêté avant position"
- "✅ WebSocket redémarré pour position: {symbol} UNIQUEMENT"

Protection double:
1. scalability_refresh_loop_callback (ligne 1716) vérifie déjà position active
2. Le WebSocket est maintenant focalisé uniquement sur le symbole de la position

Sécurité pour live trading:
- Plus de risque de prix figé pendant une position active
- Suivi en temps réel garanti pour TP/SL/current_price
…itions

PROBLÈME CRITIQUE:
- Le scan de scalabilité prend ~20 secondes (batches 1-19)
- Si une position s'ouvre PENDANT le scan (ex: après batch 6)
- Le scan continue jusqu'à la fin (batches 7-19)
- À la fin du scan, le WebSocket redémarre avec 7 symboles au lieu de rester sur le symbole de la position
- Résultat: current_price se fige car WebSocket ne monitore plus le bon symbole

LOGS DU PROBLÈME:
19:15:52 - Position ouverte SHIB/USDT
19:15:53 - Batch 7/19 continue (scan déjà commencé avant position)
...
19:16:09 - Batch 19/19 termine
19:16:10 - Scalability refresh
19:16:11 - ✅ WebSocket démarré pour 7 symboles  ← MAUVAIS!

SOLUTION (ligne 1736-1741):
Ajout d'une vérification DOUBLE dans scalability_refresh_loop_callback():
1. Vérification AVANT scan (existait déjà ligne 1716) - empêche nouveau scan
2. Vérification APRÈS scan (NOUVELLE ligne 1736-1741) - empêche mise à jour WebSocket

Si position active détectée APRÈS scan:
- top_pairs mis à jour quand même (pas de problème)
- WebSocket N'EST PAS touché (protection)
- Log: "⏸️ Mise à jour WebSocket ignorée - Position ouverte pendant le scan"

RÉSULTAT:
- Le scan de scalabilité peut continuer pendant une position (pas grave)
- Mais le WebSocket reste focalisé sur le symbole de la position
- current_price ne se fige plus
- Sécurisé pour live trading
…ection

PROBLÈME CRITIQUE RÉSOLU:
Le prix se figeait indéfiniment pendant une position car:
1. Position ouverte → WebSocket redémarre avec symbole position ✅
2. WebSocket se déconnecte (timeout, erreur réseau)
3. WebSocket se reconnecte automatiquement ✅
4. MAIS ne se réabonne à AUCUN symbole ❌
5. Résultat: plus de mise à jour de prix → current_price fige indéfiniment

CAUSE RACINE:
- price_provider.py start_websocket() recevait les symboles en paramètre
- MAIS ne les stockait NULLE PART
- reliability.py _reconnect_loop() reconnectait le WebSocket
- MAIS n'avait aucun moyen de savoir quels symboles réabonner
- Résultat: WebSocket connecté mais n'écoute RIEN

SOLUTION COMPLÈTE:

1. api/price_provider.py (ligne 44):
   - Ajout self.monitored_symbols: list = []
   - Stocke les symboles actuellement monitorés

2. api/price_provider.py start_websocket() (ligne 150):
   - self.monitored_symbols = symbols
   - Stocke les symboles pour réabonnement futur
   - self.ws_manager.reconnect_callback = self._resubscribe_after_reconnect
   - Configure le callback appelé après reconnexion

3. api/price_provider.py _resubscribe_after_reconnect() (ligne 200-248):
   - Nouvelle méthode appelée automatiquement après reconnexion
   - Vérifie si position active via app_state et position_manager
   - Si position active: réabonne UNIQUEMENT au symbole de la position
   - Sinon: réabonne à tous les symboles dans monitored_symbols
   - Logs: "🔄 Réabonnement WebSocket (position active): SYMBOL UNIQUEMENT"

4. api/reliability.py __init__() (ligne 212):
   - Ajout self.reconnect_callback: Optional[Callable] = None
   - Permet de configurer un callback après reconnexion

5. api/reliability.py _reconnect_loop() (ligne 385-390):
   - Après reconnexion réussie, appelle reconnect_callback si configuré
   - Permet au PriceProvider de réabonner aux symboles

LOGS ATTENDUS:
Quand WebSocket se déconnecte puis reconnecte pendant une position:
- 🐕 Watchdog arrêté
- 🔄 WebSocket: Tentative reconnexion...
- ✅ WebSocket reconnecté
- 🔄 Réabonnement WebSocket (position active): SHIB/USDT UNIQUEMENT
- ✅ WebSocket réabonné à 1 symbole(s)

RÉSULTAT:
- WebSocket se reconnecte ET réabonne aux symboles automatiquement
- Si position active: focus UNIQUEMENT sur symbole position
- current_price ne se fige plus jamais
- Sécurisé pour live trading
@qodo-code-review

qodo-code-review Bot commented Nov 15, 2025

Copy link
Copy Markdown

You are nearing your monthly Qodo Merge usage quota. For more information, please visit here.

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit: New critical actions like restarting WebSocket on position open are not explicitly logged
with user/context metadata, making audit reconstruction unclear.

Referred Code
# Ceci garantit que current_price sera mis à jour correctement pendant la position
if _price_provider:
    try:
        # Arrêter WebSocket actuel
        if hasattr(_price_provider, 'stop_websocket'):
            await _price_provider.stop_websocket()
            logger.debug("🔌 WebSocket arrêté pour position")

        # Redémarrer WebSocket uniquement sur le symbole de la position
        if hasattr(_price_provider, 'start_websocket'):
            await _price_provider.start_websocket([symbol])
            logger.info(f"✅ WebSocket redémarré pour position: {symbol} uniquement")
    except Exception as e:
        logger.error(f"❌ Erreur redémarrage WebSocket pour position: {e}")

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Broad except: Broad exception handlers without contextual details (e.g., in
_resubscribe_after_reconnect) may hide actionable diagnostics and skip specific edge
cases.

Referred Code
except Exception as e:
    logger.error(f"❌ Erreur réabonnement WebSocket: {e}")

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated input: The new resubscription flow trusts symbols from state without explicit validation or
sanitization before subscribing to WebSocket channels.

Referred Code
for symbol in symbols_to_subscribe:
    await self.ws_manager.subscribe_ticker(symbol)
    await asyncio.sleep(0.05)  # Petit délai

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review

qodo-code-review Bot commented Nov 15, 2025

Copy link
Copy Markdown

You are nearing your monthly Qodo Merge usage quota. For more information, please visit here.

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Refactor duplicated code into a helper

Refactor the duplicated WebSocket restart logic from
core/callbacks/scanner_loop.py and main.py into a single reusable helper
function to improve maintainability.

core/callbacks/scanner_loop.py [417-431]

 # 🔥 FIX: Redémarrer WebSocket UNIQUEMENT sur le symbole de la position
 # Ceci garantit que current_price sera mis à jour correctement pendant la position
+# NOTE: This logic should be moved to a shared utility function to avoid duplication.
+# Example: await restart_websocket_for_position(symbol, _price_provider)
 if _price_provider:
     try:
         # Arrêter WebSocket actuel
         if hasattr(_price_provider, 'stop_websocket'):
             await _price_provider.stop_websocket()
             logger.debug("🔌 WebSocket arrêté pour position")
 
         # Redémarrer WebSocket uniquement sur le symbole de la position
         if hasattr(_price_provider, 'start_websocket'):
             await _price_provider.start_websocket([symbol])
             logger.info(f"✅ WebSocket redémarré pour position: {symbol} uniquement")
     except Exception as e:
         logger.error(f"❌ Erreur redémarrage WebSocket pour position: {e}")
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies duplicated logic for restarting the WebSocket in core/callbacks/scanner_loop.py and main.py and proposes a valid refactoring to improve maintainability.

Low
  • Update

claude and others added 15 commits November 15, 2025 19:11
…g zeros

PROBLÈME:
Lors de l'affichage d'une position active dans le frontend, les prix
(current_price, TP, SL) affichaient un nombre de décimales incohérent
et gardaient des zéros superflus à la fin.

Exemple:
- Backend Entry: 1.132200
- Frontend affichait: "1.132200" (avec zéros de fin)
- Souhaité: "1.1322" (4 décimales significatives, sans zéros)

SOLUTION:

1. frontend/src/lib/utils/format.js:
   - Ajout getSignificantDecimals(value): calcule le nombre de décimales
     significatives d'un nombre (sans compter les zéros finaux)
     Exemple: 1.132200 → 4 (car 1.1322 a 4 décimales significatives)

   - Ajout formatWithoutTrailingZeros(value, decimals): formate un nombre
     avec un nombre précis de décimales puis supprime les zéros de fin
     Exemple: formatWithoutTrailingZeros(1.128000, 4) → "1.128"

2. frontend/src/lib/components/PositionCard.svelte:
   - Import des nouvelles fonctions
   - Calcul réactif entryDecimals depuis entry price
   - Remplacement de formatPriceWithPrecision():
     * Utilise entryDecimals comme nombre de décimales de référence
     * Applique formatWithoutTrailingZeros() pour supprimer les zéros
     * Tous les prix (current, TP, SL) utilisent le même formatage

   - Logique:
     entry=1.132200 → entryDecimals=4
     current=1.128000 → affiché "1.128" (4 décimales max, sans zéros)
     tp=1.138993 → affiché "1.139" (4 décimales max, arrondi)
     sl=1.129369 → affiché "1.1294" (4 décimales max)

RÉSULTAT:
- Tous les prix affichés ont le même nombre de décimales (basé sur entry)
- Les zéros superflus à la fin sont supprimés
- Affichage cohérent et lisible
- Minimum 2 décimales, maximum 8 (pour lisibilité)

Exemple avec ASTER/USDT:
- Entry backend: 1.132200 → frontend: "1.1322"
- Current: 1.128000 → frontend: "1.128"
- TP: 1.138993 → frontend: "1.139"
- SL: 1.129369 → frontend: "1.1294"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants