#!/usr/bin/env python3 """ Test script for the simplified position tracker. This script validates that the simplified position tracker: 1. Detects position changes correctly 2. Sends appropriate notifications 3. Handles pending stop losses properly """ import asyncio import sys import os import logging # Add project root to path sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) from src.monitoring.simple_position_tracker import SimplePositionTracker from src.trading.trading_engine import TradingEngine from src.notifications.notification_manager import NotificationManager # Set up logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) logger = logging.getLogger(__name__) class MockNotificationManager: """Mock notification manager for testing.""" async def send_generic_notification(self, message: str): """Print notification instead of sending.""" print(f"\n๐Ÿ“จ NOTIFICATION:\n{message}\n") async def test_simplified_position_tracker(): """Test the simplified position tracker.""" try: logger.info("๐Ÿงช Starting simplified position tracker test") # Initialize components trading_engine = TradingEngine() notification_manager = MockNotificationManager() # Create simplified position tracker tracker = SimplePositionTracker(trading_engine, notification_manager) logger.info("โœ… Components initialized successfully") # Test position change detection logger.info("๐Ÿ” Testing position change detection...") await tracker.check_all_position_changes() logger.info("โœ… Position change detection completed") # Get current positions for reference exchange_positions = trading_engine.get_positions() or [] db_positions = trading_engine.get_stats().get_open_positions() if trading_engine.get_stats() else [] logger.info(f"๐Ÿ“Š Current state:") logger.info(f" Exchange positions: {len(exchange_positions)}") logger.info(f" DB positions: {len(db_positions)}") for pos in exchange_positions: symbol = pos.get('symbol') contracts = pos.get('contracts', 0) logger.info(f" Exchange: {symbol} = {contracts}") for pos in db_positions: symbol = pos.get('symbol') size = pos.get('current_position_size', 0) status = pos.get('status') logger.info(f" DB: {symbol} = {size} ({status})") logger.info("๐ŸŽ‰ Test completed successfully!") except Exception as e: logger.error(f"โŒ Test failed: {e}") raise async def test_pending_stop_losses(): """Test pending stop loss handling.""" try: logger.info("๐Ÿงช Testing pending stop loss handling") trading_engine = TradingEngine() notification_manager = MockNotificationManager() tracker = SimplePositionTracker(trading_engine, notification_manager) # Check pending SLs stats = trading_engine.get_stats() if stats: pending_sls = stats.get_pending_stop_loss_activations() logger.info(f"๐Ÿ“‹ Found {len(pending_sls)} pending stop losses") for sl in pending_sls: symbol = sl.get('symbol') stop_price = sl.get('stop_loss_price') logger.info(f" Pending SL: {symbol} @ {stop_price}") # Test SL handling await tracker._handle_pending_stop_losses(stats) logger.info("โœ… Pending stop loss test completed") except Exception as e: logger.error(f"โŒ Pending SL test failed: {e}") raise async def test_orphaned_pending_trades(): """Test orphaned pending trades cleanup.""" try: logger.info("๐Ÿงช Testing orphaned pending trades handling") trading_engine = TradingEngine() notification_manager = MockNotificationManager() tracker = SimplePositionTracker(trading_engine, notification_manager) # Check pending trades stats = trading_engine.get_stats() if stats: pending_trades = stats.get_trades_by_status('pending') logger.info(f"๐Ÿ“‹ Found {len(pending_trades)} pending trades") for trade in pending_trades: symbol = trade.get('symbol') lifecycle_id = trade.get('trade_lifecycle_id') entry_order_id = trade.get('entry_order_id') logger.info(f" Pending trade: {symbol} (Lifecycle: {lifecycle_id[:8]}, Order: {entry_order_id})") # Test orphaned trade handling await tracker._handle_orphaned_pending_trades(stats) logger.info("โœ… Orphaned pending trades test completed") except Exception as e: logger.error(f"โŒ Orphaned pending trades test failed: {e}") raise def print_status_comparison(): """Print comparison between simplified and complex approaches.""" print(""" ๐ŸŽฏ SIMPLIFIED POSITION TRACKER vs COMPLEX EXTERNAL EVENT MONITOR โœ… SIMPLIFIED APPROACH: โ€ข Single method: check_all_position_changes() โ€ข Clear logic: Exchange positions โ†” DB positions โ€ข Always sends notifications โ€ข Simple pending SL handling โ€ข ~280 lines of code โ€ข Uses existing trades table and managers โŒ COMPLEX APPROACH: โ€ข Multiple interacting methods โ€ข Complex fill analysis and state tracking โ€ข Missed notifications (auto-sync gap) โ€ข Complicated order tracking โ€ข ~750+ lines of code โ€ข Over-engineered state management ๐ŸŽ‰ BENEFITS OF SIMPLIFICATION: 1. No more missed notifications 2. Easier to understand and maintain 3. Reuses existing infrastructure 4. Clear separation of concerns 5. Reliable position state tracking """) if __name__ == "__main__": async def main(): print_status_comparison() # Run tests await test_simplified_position_tracker() await test_pending_stop_losses() await test_orphaned_pending_trades() print("\n๐ŸŽ‰ All tests completed successfully!") print("\n๐Ÿ’ก The simplified position tracker is ready to replace the complex external event monitor!") asyncio.run(main())