#!/usr/bin/env python3 """ Feed Hunter Position Monitor Checks current prices for active simulation positions and updates P&L Usage: python3 monitor-positions.py [--update] [--alert-threshold 0.1] """ import argparse import json import os import re import time from datetime import datetime, timezone from urllib.parse import urlparse import urllib.request import urllib.error # Price sources for different asset types PRICE_SOURCES = { 'polymarket': { 'url_pattern': r'polymarket\.com.*?(@[\w]+|markets?[\w/-]+)', 'api_base': 'https://gamma-api.polymarket.com/markets', 'method': 'polymarket_api' }, 'crypto': { 'url_pattern': r'(bitcoin|btc|ethereum|eth|solana|sol|cardano|ada)', 'api_base': 'https://api.coingecko.com/api/v3/simple/price', 'method': 'coingecko_api' }, 'stock': { 'url_pattern': r'(nasdaq|nyse|stock)', 'api_base': 'https://api.example.com/stock', # Placeholder 'method': 'stock_api' } } def detect_asset_type(position): """Detect what type of asset this position represents""" asset = position.get('asset', '').lower() source_url = position.get('source_post', '') strategy = position.get('strategy', '').lower() # Check if it's a prediction market if 'polymarket' in source_url or 'polymarket' in asset or 'polymarket' in strategy: return 'polymarket' elif 'super bowl' in asset or 'win' in asset or 'seahawks' in asset: return 'polymarket' # Sports bets are usually prediction markets elif any(crypto in asset for crypto in ['btc', 'eth', 'sol', 'bitcoin', 'ethereum', 'solana']): return 'crypto' elif any(stock in asset for stock in ['stock', 'nasdaq', 'nyse', 'spy', 'tsla']): return 'stock' else: return 'unknown' def get_polymarket_price(position): """Get current price from Polymarket API""" try: # Extract market info from source URL or asset name source_url = position.get('source_post', '') asset = position.get('asset', '') # For now, return mock price update # In real implementation, would parse URL and call Polymarket API if 'seahawks' in asset.lower() and 'super bowl' in asset.lower(): # Mock price movement for Seahawks Super Bowl bet import random # Simulate some price movement around entry price entry_price = position.get('entry_price', 0.68) price_change = random.uniform(-0.05, 0.05) new_price = max(0.01, min(0.99, entry_price + price_change)) return { 'success': True, 'price': round(new_price, 2), 'timestamp': datetime.now(timezone.utc).isoformat(), 'source': 'polymarket_mock' } except Exception as e: print(f"Polymarket price fetch error: {e}") return {'success': False, 'error': 'Price fetch failed'} def get_crypto_price(position): """Get crypto price from CoinGecko""" try: asset = position.get('asset', '').lower() # Map asset names to CoinGecko IDs coin_map = { 'bitcoin': 'bitcoin', 'btc': 'bitcoin', 'ethereum': 'ethereum', 'eth': 'ethereum', 'solana': 'solana', 'sol': 'solana' } coin_id = None for name, id in coin_map.items(): if name in asset: coin_id = id break if not coin_id: return {'success': False, 'error': 'Unknown crypto asset'} url = f"https://api.coingecko.com/api/v3/simple/price?ids={coin_id}&vs_currencies=usd" with urllib.request.urlopen(url, timeout=10) as response: data = json.loads(response.read().decode()) price = data[coin_id]['usd'] return { 'success': True, 'price': price, 'timestamp': datetime.now(timezone.utc).isoformat(), 'source': 'coingecko' } except Exception as e: print(f"Crypto price fetch error: {e}") return {'success': False, 'error': str(e)} def get_stock_price(position): """Get stock price (placeholder - would need real API)""" # Placeholder for stock price API return {'success': False, 'error': 'Stock price API not implemented'} def get_current_price(position): """Get current price for a position based on asset type""" asset_type = detect_asset_type(position) if asset_type == 'polymarket': return get_polymarket_price(position) elif asset_type == 'crypto': return get_crypto_price(position) elif asset_type == 'stock': return get_stock_price(position) else: return {'success': False, 'error': f'Unknown asset type: {asset_type}'} def calculate_pnl(position, current_price): """Calculate P&L for a position""" entry_price = position.get('entry_price', 0) quantity = position.get('quantity', 0) size = position.get('size', 0) position_type = position.get('type', 'long') if position_type == 'bet': # For prediction market bets (binary outcome) # P&L = quantity * (current_price - entry_price) unrealized_pnl = quantity * (current_price - entry_price) unrealized_pnl_pct = ((current_price / entry_price) - 1) * 100 if entry_price > 0 else 0 else: # For regular long positions # P&L = size * (current_price / entry_price - 1) unrealized_pnl = size * (current_price / entry_price - 1) if entry_price > 0 else 0 unrealized_pnl_pct = ((current_price / entry_price) - 1) * 100 if entry_price > 0 else 0 return { 'unrealized_pnl': round(unrealized_pnl, 2), 'unrealized_pnl_pct': round(unrealized_pnl_pct, 2), 'current_price': current_price } def check_stop_loss_take_profit(position, current_price): """Check if position hits stop loss or take profit""" stop_loss = position.get('stop_loss') take_profit = position.get('take_profit') triggers = [] if stop_loss and current_price <= stop_loss: triggers.append({ 'type': 'stop_loss', 'trigger_price': stop_loss, 'current_price': current_price, 'message': f"STOP LOSS triggered at {current_price} (target: {stop_loss})" }) if take_profit and current_price >= take_profit: triggers.append({ 'type': 'take_profit', 'trigger_price': take_profit, 'current_price': current_price, 'message': f"TAKE PROFIT triggered at {current_price} (target: {take_profit})" }) return triggers def load_active_positions(): """Load active positions from JSON file""" positions_file = 'data/simulations/active.json' if not os.path.exists(positions_file): return None try: with open(positions_file, 'r') as f: return json.load(f) except Exception as e: print(f"Error loading positions: {e}") return None def save_active_positions(data): """Save updated positions to JSON file""" positions_file = 'data/simulations/active.json' try: with open(positions_file, 'w') as f: json.dump(data, f, indent=2) return True except Exception as e: print(f"Error saving positions: {e}") return False def send_alert(position, trigger_info, pnl_info): """Send alert about position trigger (placeholder)""" print(f"🚨 ALERT: {trigger_info['message']}") print(f" Position: {position['asset'][:50]}...") print(f" P&L: ${pnl_info['unrealized_pnl']:.2f} ({pnl_info['unrealized_pnl_pct']:+.1f}%)") print(f" Strategy: {position.get('strategy', 'Unknown')}") print("") def monitor_positions(update=False, alert_threshold=0.1): """Monitor all active positions""" print("šŸ–¤ Feed Hunter - Position Monitor") print(f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") print("") # Load active positions data = load_active_positions() if not data: print("āŒ No active positions file found") return positions = data.get('positions', []) if not positions: print("šŸ“­ No active positions to monitor") return print(f"šŸ“Š Monitoring {len(positions)} active positions...") print("") updated = False alerts_sent = 0 for i, position in enumerate(positions): pos_id = position.get('id', f'pos_{i}') asset = position.get('asset', 'Unknown') print(f"[{i+1}] {asset[:50]}...") print(f" ID: {pos_id}") print(f" Entry: ${position.get('entry_price', 0):.2f}") print(f" Strategy: {position.get('strategy', 'Unknown')}") # Get current price price_result = get_current_price(position) if price_result['success']: current_price = price_result['price'] print(f" Current: ${current_price:.2f} ({price_result['source']})") # Calculate P&L pnl_info = calculate_pnl(position, current_price) pnl_color = "šŸ’š" if pnl_info['unrealized_pnl'] >= 0 else "ā¤ļø" print(f" P&L: {pnl_color} ${pnl_info['unrealized_pnl']:.2f} ({pnl_info['unrealized_pnl_pct']:+.1f}%)") # Check stop loss / take profit triggers = check_stop_loss_take_profit(position, current_price) for trigger in triggers: print(f" 🚨 {trigger['message']}") send_alert(position, trigger, pnl_info) alerts_sent += 1 # Update position if requested if update: position.update(pnl_info) position['last_updated'] = datetime.now(timezone.utc).isoformat() updated = True else: print(f" āŒ Price fetch failed: {price_result.get('error', 'Unknown error')}") print("") # Save updates if any if updated and update: if save_active_positions(data): print("āœ… Positions updated and saved") else: print("āŒ Failed to save position updates") # Summary print("šŸ“ˆ Monitor Summary:") print(f" Positions checked: {len(positions)}") print(f" Alerts sent: {alerts_sent}") if updated: print(f" Updates saved: āœ…") print("") def main(): parser = argparse.ArgumentParser(description="Monitor active simulation positions") parser.add_argument("--update", action="store_true", help="Update positions with current prices") parser.add_argument("--alert-threshold", type=float, default=0.1, help="P&L threshold for alerts (10% default)") parser.add_argument("--interval", type=int, help="Run continuously every N seconds") args = parser.parse_args() try: if args.interval: print(f"šŸ”„ Starting continuous monitoring every {args.interval} seconds") print("Press Ctrl+C to stop") print("") while True: monitor_positions(update=args.update, alert_threshold=args.alert_threshold) print(f"ā±ļø Sleeping for {args.interval} seconds...") time.sleep(args.interval) else: monitor_positions(update=args.update, alert_threshold=args.alert_threshold) except KeyboardInterrupt: print("\nā¹ļø Monitor stopped") except Exception as e: print(f"āŒ Monitor error: {e}") if __name__ == "__main__": main()