|
@@ -100,81 +100,128 @@ class CopyTradingMonitor:
|
|
|
|
|
|
self.logger.info(f"Starting copy trading monitor for {self.target_address}")
|
|
|
|
|
|
- # Start state tracking
|
|
|
- self.state_manager.start_copy_trading(self.target_address)
|
|
|
-
|
|
|
- # Get current target positions for initialization
|
|
|
- current_positions = await self.get_target_positions()
|
|
|
- if current_positions:
|
|
|
- # Check if this is a fresh start or resuming
|
|
|
- if not self.state_manager.get_tracked_positions():
|
|
|
- # Fresh start - initialize tracking but don't copy existing positions
|
|
|
- self.logger.info("🆕 Fresh start - initializing with existing positions (won't copy)")
|
|
|
- self.state_manager.initialize_tracked_positions(current_positions)
|
|
|
-
|
|
|
- startup_message = (
|
|
|
- f"🔄 Copy Trading Started (Fresh)\n"
|
|
|
- f"Target: {self.target_address[:10]}...\n"
|
|
|
- f"Portfolio Allocation: {self.portfolio_percentage:.1%}\n"
|
|
|
- f"Mode: {self.copy_mode}\n"
|
|
|
- f"Max Leverage: {self.max_leverage}x\n\n"
|
|
|
- f"📊 Found {len(current_positions)} existing positions\n"
|
|
|
- f"⚠️ Will only copy NEW trades from now on"
|
|
|
+ try:
|
|
|
+ # Start state tracking
|
|
|
+ self.state_manager.start_copy_trading(self.target_address)
|
|
|
+
|
|
|
+ # Get current target positions for initialization (with timeout)
|
|
|
+ try:
|
|
|
+ current_positions = await asyncio.wait_for(
|
|
|
+ self.get_target_positions(),
|
|
|
+ timeout=15.0 # 15 second timeout for initialization
|
|
|
)
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ self.logger.warning("Timeout during initialization - will retry in monitoring loop")
|
|
|
+ current_positions = None
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Error during initialization: {e}")
|
|
|
+ current_positions = None
|
|
|
+
|
|
|
+ if current_positions:
|
|
|
+ # Check if this is a fresh start or resuming
|
|
|
+ if not self.state_manager.get_tracked_positions():
|
|
|
+ # Fresh start - initialize tracking but don't copy existing positions
|
|
|
+ self.logger.info("🆕 Fresh start - initializing with existing positions (won't copy)")
|
|
|
+ self.state_manager.initialize_tracked_positions(current_positions)
|
|
|
+
|
|
|
+ startup_message = (
|
|
|
+ f"🔄 Copy Trading Started (Fresh)\n"
|
|
|
+ f"Target: {self.target_address[:10]}...\n"
|
|
|
+ f"Portfolio Allocation: {self.portfolio_percentage:.1%}\n"
|
|
|
+ f"Mode: {self.copy_mode}\n"
|
|
|
+ f"Max Leverage: {self.max_leverage}x\n\n"
|
|
|
+ f"📊 Found {len(current_positions)} existing positions\n"
|
|
|
+ f"⚠️ Will only copy NEW trades from now on"
|
|
|
+ )
|
|
|
+ else:
|
|
|
+ # Resuming - continue from where we left off
|
|
|
+ tracked_count = len(self.state_manager.get_tracked_positions())
|
|
|
+ self.logger.info(f"▶️ Resuming session - {tracked_count} positions tracked")
|
|
|
+
|
|
|
+ startup_message = (
|
|
|
+ f"▶️ Copy Trading Resumed\n"
|
|
|
+ f"Target: {self.target_address[:10]}...\n"
|
|
|
+ f"Portfolio Allocation: {self.portfolio_percentage:.1%}\n"
|
|
|
+ f"Mode: {self.copy_mode}\n"
|
|
|
+ f"Max Leverage: {self.max_leverage}x\n\n"
|
|
|
+ f"📊 Resuming with {tracked_count} tracked positions"
|
|
|
+ )
|
|
|
else:
|
|
|
- # Resuming - continue from where we left off
|
|
|
- tracked_count = len(self.state_manager.get_tracked_positions())
|
|
|
- self.logger.info(f"▶️ Resuming session - {tracked_count} positions tracked")
|
|
|
-
|
|
|
startup_message = (
|
|
|
- f"▶️ Copy Trading Resumed\n"
|
|
|
+ f"🔄 Copy Trading Started\n"
|
|
|
f"Target: {self.target_address[:10]}...\n"
|
|
|
f"Portfolio Allocation: {self.portfolio_percentage:.1%}\n"
|
|
|
f"Mode: {self.copy_mode}\n"
|
|
|
f"Max Leverage: {self.max_leverage}x\n\n"
|
|
|
- f"📊 Resuming with {tracked_count} tracked positions"
|
|
|
+ f"⚠️ Could not access target trader positions during startup"
|
|
|
)
|
|
|
- else:
|
|
|
- startup_message = (
|
|
|
- f"🔄 Copy Trading Started\n"
|
|
|
- f"Target: {self.target_address[:10]}...\n"
|
|
|
- f"Portfolio Allocation: {self.portfolio_percentage:.1%}\n"
|
|
|
- f"Mode: {self.copy_mode}\n"
|
|
|
- f"Max Leverage: {self.max_leverage}x\n\n"
|
|
|
- f"⚠️ Could not access target trader positions"
|
|
|
- )
|
|
|
-
|
|
|
- # Send startup notification
|
|
|
- if self.notifications_enabled:
|
|
|
- await self.notification_manager.send_message(startup_message)
|
|
|
-
|
|
|
- # Initial sync
|
|
|
- await self.sync_positions()
|
|
|
-
|
|
|
- # Start monitoring loop
|
|
|
- while self.enabled and self.state_manager.is_enabled():
|
|
|
+
|
|
|
+ # Send startup notification
|
|
|
+ if self.notifications_enabled:
|
|
|
+ try:
|
|
|
+ await asyncio.wait_for(
|
|
|
+ self.notification_manager.send_message(startup_message),
|
|
|
+ timeout=5.0
|
|
|
+ )
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Error sending startup notification: {e}")
|
|
|
+
|
|
|
+ # Initial sync (non-blocking)
|
|
|
try:
|
|
|
- await self.monitor_cycle()
|
|
|
- await asyncio.sleep(30) # Check every 30 seconds
|
|
|
+ await asyncio.wait_for(self.sync_positions(), timeout=10.0)
|
|
|
except Exception as e:
|
|
|
- self.logger.error(f"Error in copy trading monitor cycle: {e}")
|
|
|
- await asyncio.sleep(60) # Wait longer on error
|
|
|
+ self.logger.error(f"Error during initial sync: {e}")
|
|
|
+
|
|
|
+ # Start monitoring loop
|
|
|
+ while self.enabled and self.state_manager.is_enabled():
|
|
|
+ try:
|
|
|
+ await self.monitor_cycle()
|
|
|
+ await asyncio.sleep(30) # Check every 30 seconds
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Error in copy trading monitor cycle: {e}")
|
|
|
+ await asyncio.sleep(60) # Wait longer on error
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Fatal error in copy trading monitor: {e}")
|
|
|
+ self.enabled = False
|
|
|
|
|
|
async def monitor_cycle(self):
|
|
|
"""Single monitoring cycle"""
|
|
|
try:
|
|
|
- # Get target trader's current positions
|
|
|
- new_positions = await self.get_target_positions()
|
|
|
-
|
|
|
+ # Get target trader's current positions with timeout
|
|
|
+ try:
|
|
|
+ new_positions = await asyncio.wait_for(
|
|
|
+ self.get_target_positions(),
|
|
|
+ timeout=15.0 # 15 second timeout
|
|
|
+ )
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ self.logger.warning("Timeout getting target positions - skipping this cycle")
|
|
|
+ return
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Error getting target positions: {e}")
|
|
|
+ return
|
|
|
+
|
|
|
if new_positions is None:
|
|
|
return
|
|
|
|
|
|
# Compare with previous positions to detect changes
|
|
|
- position_changes = self.detect_position_changes(new_positions)
|
|
|
+ try:
|
|
|
+ position_changes = self.detect_position_changes(new_positions)
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Error detecting position changes: {e}")
|
|
|
+ return
|
|
|
|
|
|
# Execute any detected trades
|
|
|
for trade in position_changes:
|
|
|
- await self.execute_copy_trade(trade)
|
|
|
+ try:
|
|
|
+ await asyncio.wait_for(
|
|
|
+ self.execute_copy_trade(trade),
|
|
|
+ timeout=30.0 # 30 second timeout per trade
|
|
|
+ )
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ self.logger.error(f"Timeout executing copy trade for {trade.coin}")
|
|
|
+ except Exception as e:
|
|
|
+ self.logger.error(f"Error executing copy trade for {trade.coin}: {e}")
|
|
|
|
|
|
# Update our tracking
|
|
|
self.target_positions = new_positions
|
|
@@ -190,7 +237,9 @@ class CopyTradingMonitor:
|
|
|
"user": self.target_address
|
|
|
}
|
|
|
|
|
|
- async with aiohttp.ClientSession() as session:
|
|
|
+ # Use timeout to prevent blocking
|
|
|
+ timeout = aiohttp.ClientTimeout(total=10.0) # 10 second timeout
|
|
|
+ async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
|
async with session.post(self.info_url, json=payload) as response:
|
|
|
if response.status != 200:
|
|
|
self.logger.error(f"Failed to get target positions: {response.status}")
|
|
@@ -225,6 +274,9 @@ class CopyTradingMonitor:
|
|
|
|
|
|
return positions
|
|
|
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ self.logger.warning("Timeout getting target positions - will retry next cycle")
|
|
|
+ return None
|
|
|
except Exception as e:
|
|
|
self.logger.error(f"Error getting target positions: {e}")
|
|
|
return None
|
|
@@ -439,7 +491,9 @@ class CopyTradingMonitor:
|
|
|
"user": self.target_address
|
|
|
}
|
|
|
|
|
|
- async with aiohttp.ClientSession() as session:
|
|
|
+ # Use timeout to prevent blocking
|
|
|
+ timeout = aiohttp.ClientTimeout(total=10.0) # 10 second timeout
|
|
|
+ async with aiohttp.ClientSession(timeout=timeout) as session:
|
|
|
async with session.post(self.info_url, json=payload) as response:
|
|
|
if response.status == 200:
|
|
|
data = await response.json()
|
|
@@ -447,6 +501,9 @@ class CopyTradingMonitor:
|
|
|
else:
|
|
|
return 0.0
|
|
|
|
|
|
+ except asyncio.TimeoutError:
|
|
|
+ self.logger.warning("Timeout getting target account balance")
|
|
|
+ return 0.0
|
|
|
except Exception as e:
|
|
|
self.logger.error(f"Error getting target account balance: {e}")
|
|
|
return 0.0
|