|
@@ -84,12 +84,54 @@ class PositionTracker:
|
|
# Compare with previous positions (simple exchange state tracking)
|
|
# Compare with previous positions (simple exchange state tracking)
|
|
await self._process_position_changes(previous_positions, self.current_positions)
|
|
await self._process_position_changes(previous_positions, self.current_positions)
|
|
|
|
|
|
|
|
+ # Simple database sync check (once per cycle)
|
|
|
|
+ await self._sync_database_once()
|
|
|
|
+
|
|
# Update database with current market data for open positions
|
|
# Update database with current market data for open positions
|
|
await self._update_database_market_data()
|
|
await self._update_database_market_data()
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
logger.error(f"Error checking position changes: {e}")
|
|
logger.error(f"Error checking position changes: {e}")
|
|
|
|
|
|
|
|
+ async def _sync_database_once(self):
|
|
|
|
+ """Simple one-time check: close database positions that don't exist on exchange"""
|
|
|
|
+ try:
|
|
|
|
+ if self.trading_stats is None:
|
|
|
|
+ from ..stats.trading_stats import TradingStats
|
|
|
|
+ self.trading_stats = TradingStats()
|
|
|
|
+
|
|
|
|
+ open_trades = self.trading_stats.get_open_positions()
|
|
|
|
+
|
|
|
|
+ for trade in open_trades:
|
|
|
|
+ symbol = trade.get('symbol', '')
|
|
|
|
+ if not symbol:
|
|
|
|
+ continue
|
|
|
|
+
|
|
|
|
+ token = symbol.split('/')[0] if '/' in symbol else symbol
|
|
|
|
+
|
|
|
|
+ # If database position doesn't exist on exchange, close it
|
|
|
|
+ if token not in self.current_positions:
|
|
|
|
+ # Create simulated position object from database data
|
|
|
|
+ entry_price = float(trade.get('entry_price', 0))
|
|
|
|
+ amount = float(trade.get('amount', 0))
|
|
|
|
+ side = trade.get('side', '').lower()
|
|
|
|
+
|
|
|
|
+ simulated_position = {
|
|
|
|
+ 'size': amount if side == 'long' else -amount, # Maintain proper sign
|
|
|
|
+ 'entry_px': entry_price,
|
|
|
|
+ 'unrealized_pnl': 0, # Will be calculated
|
|
|
|
+ 'margin_used': 0,
|
|
|
|
+ 'max_leverage': 1,
|
|
|
|
+ 'current_leverage': 1,
|
|
|
|
+ 'return_on_equity': 0
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ # Reuse existing position closed handler - consistent behavior!
|
|
|
|
+ await self._handle_position_closed(token, simulated_position)
|
|
|
|
+
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logger.error(f"Error syncing database: {e}")
|
|
|
|
+
|
|
async def _update_database_market_data(self):
|
|
async def _update_database_market_data(self):
|
|
"""Update database with current market data for open positions"""
|
|
"""Update database with current market data for open positions"""
|
|
try:
|
|
try:
|
|
@@ -287,6 +329,9 @@ class PositionTracker:
|
|
)
|
|
)
|
|
|
|
|
|
if success:
|
|
if success:
|
|
|
|
+ # Migrate to aggregated stats (token_stats table, etc.)
|
|
|
|
+ self.trading_stats.migrate_trade_to_aggregated_stats(lifecycle_id)
|
|
|
|
+
|
|
# Send clean notification
|
|
# Send clean notification
|
|
pnl_emoji = "🟢" if realized_pnl >= 0 else "🔴"
|
|
pnl_emoji = "🟢" if realized_pnl >= 0 else "🔴"
|
|
message = (
|
|
message = (
|