"""Simplified dashboard that definitely works""" from flask import Flask, render_template, jsonify, request from flask_socketio import SocketIO, emit import sys from pathlib import Path from datetime import datetime, timedelta import threading import time import numpy as np import os from dotenv import load_dotenv # Load environment variables load_dotenv() # Setup paths project_root = Path(__file__).parent.parent.parent sys.path.insert(0, str(project_root)) # Import Polymarket API clients try: from polymarket.api import GammaClient, ClobClient, DataClient gamma_client = GammaClient() clob_client = ClobClient() data_client = DataClient(api_key=os.getenv('POLYMARKET_API_KEY')) USE_REAL_API = True print("[OK] Connected to real Polymarket API") except Exception as e: print(f"[WARNING] Could not initialize API clients: {e}") print("Falling back to mock data") USE_REAL_API = False gamma_client = None clob_client = None data_client = None app = Flask(__name__, template_folder='templates', static_folder='static') socketio = SocketIO(app, cors_allowed_origins="*") # Mock state is_trading = False strategy_balance = 1000.0 strategy_equity = 1000.0 strategy_positions = 0 strategy_trades = 0 # Backtesting state backtest_running = False backtest_results = None @app.route('/') def index(): """Main dashboard""" return render_template('dashboard.html') @app.route('/api/markets') def get_markets(): """Get markets from real Polymarket API""" if not USE_REAL_API: # Fallback to mock data return jsonify({ 'markets': [ { 'id': '1', 'question': 'Will Bitcoin reach $100k by 2025?', 'event': 'Crypto Markets', 'yes_price': 0.65, 'no_price': 0.35, 'bid': 0.64, 'ask': 0.66, 'spread': 0.02, 'token_id': 'token123' } ] }) try: # Fetch events from Gamma API events_data = gamma_client.get_events(active=True, closed=False, limit=50) # Handle different response formats if isinstance(events_data, dict): events = events_data.get('data', events_data.get('events', [])) else: events = events_data if isinstance(events_data, list) else [] print(f"[DEBUG] Fetched {len(events)} events from API") markets = [] for event in events: event_markets = event.get('markets', []) if not event_markets: # Some events might have markets directly in the event object if 'question' in event or 'clobTokenIds' in event: event_markets = [event] else: continue for market in event_markets: try: # Get clobTokenIds from market (per official docs) # https://docs.polymarket.com/quickstart/fetching-data clob_token_ids = market.get('clobTokenIds', []) if len(clob_token_ids) < 2: continue yes_token = clob_token_ids[0] no_token = clob_token_ids[1] # Parse outcomes and prices from market (per docs format) import json outcomes = json.loads(market.get('outcomes', '["Yes", "No"]')) outcome_prices = json.loads(market.get('outcomePrices', '[0.5, 0.5]')) # Use prices directly from market data first (faster) yes_price = float(outcome_prices[0]) if len(outcome_prices) > 0 else 0.5 no_price = float(outcome_prices[1]) if len(outcome_prices) > 1 else 0.5 # Try to get better prices from orderbook (optional enhancement) try: yes_book = clob_client.get_orderbook(yes_token) yes_bids = yes_book.get('bids', []) yes_asks = yes_book.get('asks', []) if yes_bids and yes_asks: yes_bid = float(yes_bids[0].get('price', yes_price)) yes_ask = float(yes_asks[0].get('price', yes_price)) yes_price = (yes_bid + yes_ask) / 2 except Exception as e: print(f"Warning: Could not get orderbook for YES token: {e}") # Use price from market data as fallback # Calculate spread from orderbook if available spread = abs(yes_price - no_price) try: yes_book = clob_client.get_orderbook(yes_token) yes_bids = yes_book.get('bids', []) yes_asks = yes_book.get('asks', []) if yes_bids and yes_asks: best_bid = float(yes_bids[0].get('price', yes_price)) best_ask = float(yes_asks[0].get('price', yes_price)) spread = best_ask - best_bid except: pass markets.append({ 'id': market.get('id', ''), 'question': market.get('question', 'Unknown Market'), 'event': event.get('title', 'Unknown Event'), 'yes_price': yes_price, 'no_price': no_price, 'bid': yes_price - 0.01 if yes_price > 0.01 else 0.0, 'ask': yes_price + 0.01 if yes_price < 0.99 else 1.0, 'spread': abs(yes_price - no_price), 'token_id': yes_token, 'volume': market.get('volume', 0) }) except Exception as e: print(f"Error processing market: {e}") continue print(f"[DEBUG] Returning {len(markets)} markets to frontend") return jsonify({'markets': markets}) except Exception as e: import traceback print(f"Error fetching markets: {e}") print(traceback.format_exc()) return jsonify({'markets': [], 'error': str(e)}) @app.route('/api/strategy/status') def get_strategy_status(): """Get strategy status from real API""" if not USE_REAL_API: return jsonify({ 'active': is_trading, 'balance': strategy_balance, 'equity': strategy_equity, 'positions': strategy_positions, 'trades': strategy_trades, 'win_rate': 0, 'profit': 0, 'drawdown': 0 }) try: # Get portfolio data (requires user address) # For now, return basic status return jsonify({ 'active': is_trading, 'balance': strategy_balance, 'equity': strategy_equity, 'positions': strategy_positions, 'trades': strategy_trades, 'win_rate': 0, 'profit': 0, 'drawdown': 0 }) except Exception as e: print(f"Error getting strategy status: {e}") return jsonify({ 'active': is_trading, 'balance': 0.0, 'equity': 0.0, 'positions': 0, 'trades': 0, 'win_rate': 0, 'profit': 0, 'drawdown': 0 }) @app.route('/api/strategy/positions') def get_positions(): """Get positions from real API""" if not USE_REAL_API: return jsonify({'positions': []}) try: # Get positions (requires user address - would need to be configured) # For now, return empty return jsonify({'positions': []}) except Exception as e: print(f"Error fetching positions: {e}") return jsonify({'positions': []}) @app.route('/api/strategy/start', methods=['POST']) def start_strategy(): """Start trading strategy""" global is_trading is_trading = True return jsonify({'status': 'started'}) @app.route('/api/strategy/stop', methods=['POST']) def stop_strategy(): """Stop trading strategy""" global is_trading is_trading = False return jsonify({'status': 'stopped'}) @app.route('/api/market/') def get_market_details(market_id): """Get market details from real API""" if not USE_REAL_API: return jsonify({ 'orderbook': {'bids': [], 'asks': []}, 'best_bid_ask': {'bid': 0.5, 'ask': 0.5, 'spread': 0.0}, 'depth': {'bid_depth': 0, 'ask_depth': 0} }) try: # Get market by ID or slug # Try to get orderbook for the token book = clob_client.get_orderbook(market_id) bids = book.get('bids', []) asks = book.get('asks', []) best_bid = float(bids[0].get('price', 0.5)) if bids else 0.5 best_ask = float(asks[0].get('price', 0.5)) if asks else 0.5 bid_depth = sum(float(bid.get('size', 0)) for bid in bids) ask_depth = sum(float(ask.get('size', 0)) for ask in asks) return jsonify({ 'orderbook': { 'bids': bids[:10], # Top 10 bids 'asks': asks[:10] # Top 10 asks }, 'best_bid_ask': { 'bid': best_bid, 'ask': best_ask, 'spread': best_ask - best_bid, 'mid': (best_bid + best_ask) / 2 }, 'depth': { 'bid_depth': bid_depth, 'ask_depth': ask_depth, 'total_depth': bid_depth + ask_depth } }) except Exception as e: print(f"Error fetching market details: {e}") return jsonify({ 'orderbook': {'bids': [], 'asks': []}, 'best_bid_ask': {'bid': 0.5, 'ask': 0.5, 'spread': 0.0}, 'depth': {'bid_depth': 0, 'ask_depth': 0}, 'error': str(e) }) @app.route('/api/backtest/run', methods=['POST']) def run_backtest(): """Run backtest""" global backtest_running, backtest_results if backtest_running: return jsonify({'error': 'Backtest already running'}), 400 try: data = request.json start_date = data.get('start_date') end_date = data.get('end_date') initial_balance = float(data.get('initial_balance', 1000.0)) threshold = float(data.get('threshold', 0.15)) min_confidence = float(data.get('min_confidence', 0.7)) # Parse dates start = datetime.strptime(start_date, '%Y-%m-%d') end = datetime.strptime(end_date, '%Y-%m-%d') # Run backtest in background thread def run_backtest_thread(): global backtest_running, backtest_results backtest_running = True def log_message(msg, msg_type='info'): """Emit log message via WebSocket""" socketio.emit('backtest_log', { 'message': msg, 'type': msg_type, 'timestamp': datetime.now().strftime('%H:%M:%S') }) time.sleep(0.01) # Small delay to prevent flooding try: log_message(f"Starting backtest from {start.date()} to {end.date()}", 'info') log_message(f"Initial Balance: ${initial_balance:.2f}", 'info') log_message(f"Strategy Parameters: threshold={threshold}, confidence={min_confidence}", 'info') # Import backtesting engine from polymarket.backtesting.engine import BacktestEngine from polymarket.strategies.examples import SimpleProbabilityStrategy import traceback # Create strategy strategy = SimpleProbabilityStrategy( initial_balance=initial_balance, threshold=threshold, min_confidence=min_confidence ) log_message("Strategy initialized: SimpleProbabilityStrategy", 'success') # Create engine engine = BacktestEngine(strategy, start, end, initial_balance) # Custom run with logging log_message("Fetching markets...", 'info') markets = engine.fetch_historical_markets() if not markets: log_message("ERROR: No markets found", 'error') raise ValueError("No markets found for backtesting") log_message(f"Found {len(markets)} markets to backtest", 'success') # Run backtest with progress updates current_date = start day_count = 0 total_days = (end - start).days + 1 while current_date <= end: # Process markets for market_snapshot in markets: market = market_snapshot['market'] import json outcomes = json.loads(market.get('outcomes', '["Yes", "No"]')) prices = json.loads(market.get('outcomePrices', '[0.5, 0.5]')) market_data = { 'event': market_snapshot['event'], 'market': market, 'timestamp': current_date, 'prices': { outcome: float(price) for outcome, price in zip(outcomes, prices) } } # Log market data periodically (only once per day, not per market) if day_count % 5 == 0 and len(markets) > 0 and market_snapshot == markets[0]: yes_price = market_data['prices'].get('Yes', 0.5) equity = strategy.calculate_equity() log_message( f"[MARKET] {current_date.date()} | Yes: {yes_price:.2%} | " f"Balance: ${strategy.current_balance:.2f} | Equity: ${equity:.2f} | " f"Positions: {len(strategy.positions)} | Trades: {strategy.total_trades}", 'market' ) # Get strategy signal signal = strategy.analyze_market(market_data) if signal: if signal.confidence >= strategy.min_confidence: result = engine.execute_signal(signal, market_data, current_date) if result: # Calculate PnL for this trade trade_pnl = 0.0 if signal.action == 'SELL': # PnL already calculated in execute_signal # Get from closed positions if strategy.closed_positions: last_closed = strategy.closed_positions[-1] if hasattr(last_closed, 'realized_pnl'): trade_pnl = last_closed.realized_pnl if np.isfinite(last_closed.realized_pnl) else 0.0 equity = strategy.calculate_equity() unrealized_pnl = sum( pos.unrealized_pnl if np.isfinite(pos.unrealized_pnl) else 0.0 for pos in strategy.positions.values() ) log_message( f"[TRADE] {signal.action} | Size: ${result['size']:.2f} | " f"Price: {result['price']:.4f} | Balance: ${strategy.current_balance:.2f} | " f"Positions: {len(strategy.positions)}", 'trade' ) # Emit real-time trade update socketio.emit('backtest_trade', { 'action': signal.action, 'price': float(result['price']), 'size': float(result['size']), 'timestamp': current_date.isoformat(), 'balance': float(strategy.current_balance) if np.isfinite(strategy.current_balance) else 0.0, 'equity': float(equity) if np.isfinite(equity) else 0.0, 'unrealized_pnl': float(unrealized_pnl) if np.isfinite(unrealized_pnl) else 0.0, 'trade_pnl': float(trade_pnl), 'positions': len(strategy.positions), 'total_trades': strategy.total_trades, 'winning_trades': strategy.winning_trades, 'losing_trades': strategy.losing_trades }) # Don't log skipped signals to reduce noise # Update positions for token_id, position in strategy.positions.items(): price_change = np.random.normal(0, 0.02) new_price = max(0.01, min(0.99, position.current_price + price_change)) strategy.update_position(token_id, new_price) # Update equity curve strategy.update_drawdown() equity = strategy.calculate_equity() unrealized_pnl = sum( pos.unrealized_pnl if np.isfinite(pos.unrealized_pnl) else 0.0 for pos in strategy.positions.values() ) equity_point = { 'date': current_date, 'equity': equity if np.isfinite(equity) else strategy.current_balance, 'balance': strategy.current_balance if np.isfinite(strategy.current_balance) else 0.0, 'unrealized_pnl': unrealized_pnl if np.isfinite(unrealized_pnl) else 0.0 } engine.equity_curve.append(equity_point) # Emit real-time equity update (every day) socketio.emit('backtest_equity', { 'date': current_date.isoformat(), 'equity': float(equity_point['equity']), 'balance': float(equity_point['balance']), 'unrealized_pnl': float(equity_point['unrealized_pnl']), 'total_trades': strategy.total_trades, 'positions': len(strategy.positions) }) # Calculate daily return if len(engine.equity_curve) > 1: prev_equity = engine.equity_curve[-2]['equity'] daily_return = (equity - prev_equity) / prev_equity if prev_equity > 0 else 0.0 engine.daily_returns.append(daily_return) # Progress update (less frequent) if day_count % 10 == 0 or day_count == total_days - 1: progress = (day_count / total_days * 100) if total_days > 0 else 0 log_message( f"[PROGRESS] Day {day_count}/{total_days} ({progress:.1f}%) | " f"Equity: ${equity:.2f} | Trades: {strategy.total_trades} | " f"Positions: {len(strategy.positions)} | Win Rate: " f"{(strategy.winning_trades / strategy.total_trades * 100) if strategy.total_trades > 0 else 0:.1f}%", 'info' ) current_date += timedelta(days=1) day_count += 1 # Small delay for visibility time.sleep(0.05) # Close positions log_message("Closing all positions...", 'info') final_equity = strategy.calculate_equity() for token_id, position in list(strategy.positions.items()): if position.size > 0 and position.current_price > 0: exit_value = position.size * position.current_price entry_cost = position.size * position.entry_price pnl = exit_value - entry_cost strategy.current_balance += exit_value strategy.total_trades += 1 if pnl > 0: strategy.winning_trades += 1 strategy.total_profit += pnl else: strategy.losing_trades += 1 strategy.total_loss += abs(pnl) log_message( f"[CLOSE] PnL: ${pnl:.2f} | Entry: {position.entry_price:.4f} | Exit: {position.current_price:.4f}", 'trade' if pnl > 0 else 'warning' ) del strategy.positions[token_id] # Calculate final metrics if engine.initial_balance > 0: total_return = (final_equity - engine.initial_balance) / engine.initial_balance * 100 else: total_return = 0.0 sharpe_ratio = engine._calculate_sharpe_ratio() if strategy.total_trades > 0: win_rate = (strategy.winning_trades / strategy.total_trades * 100) else: win_rate = 0.0 if abs(strategy.total_loss) > 1e-10: profit_factor = abs(strategy.total_profit / strategy.total_loss) else: profit_factor = 0.0 results = { 'strategy': strategy.name, 'start_date': engine.start_date, 'end_date': engine.end_date, 'initial_balance': engine.initial_balance, 'final_balance': strategy.current_balance, 'final_equity': final_equity, 'total_return': total_return if np.isfinite(total_return) else 0.0, 'total_trades': strategy.total_trades, 'winning_trades': strategy.winning_trades, 'losing_trades': strategy.losing_trades, 'win_rate': win_rate if np.isfinite(win_rate) else 0.0, 'total_profit': strategy.total_profit, 'total_loss': strategy.total_loss, 'net_profit': strategy.total_profit + strategy.total_loss, 'profit_factor': profit_factor if np.isfinite(profit_factor) else 0.0, 'max_drawdown': strategy.max_drawdown * 100 if np.isfinite(strategy.max_drawdown) else 0.0, 'sharpe_ratio': sharpe_ratio if np.isfinite(sharpe_ratio) else 0.0, 'trades': engine.trades, 'equity_curve': engine.equity_curve } log_message("=" * 50, 'info') log_message("BACKTEST COMPLETE", 'success') log_message(f"Total Return: {total_return:.2f}%", 'success') log_message(f"Total Trades: {strategy.total_trades}", 'info') log_message(f"Win Rate: {win_rate:.2f}%", 'info') log_message(f"Final Equity: ${final_equity:.2f}", 'success') # Prepare results for frontend equity_curve = results.get('equity_curve', []) if not equity_curve: equity_curve = [ {'date': start, 'equity': initial_balance}, {'date': end, 'equity': results.get('final_equity', initial_balance)} ] def safe_float(value, default=0.0): try: val = float(value) return val if (val == 0 or (val != float('inf') and val != float('-inf') and not (val != val))) else default except (ValueError, TypeError): return default backtest_results = { 'total_return': safe_float(results.get('total_return', 0)), 'total_trades': int(results.get('total_trades', 0)), 'winning_trades': int(results.get('winning_trades', 0)), 'losing_trades': int(results.get('losing_trades', 0)), 'win_rate': safe_float(results.get('win_rate', 0)), 'sharpe_ratio': safe_float(results.get('sharpe_ratio', 0)), 'max_drawdown': safe_float(results.get('max_drawdown', 0)), 'final_equity': safe_float(results.get('final_equity', initial_balance), initial_balance), 'equity_curve': [ { 'date': str(point.get('date', '')), 'equity': safe_float(point.get('equity', initial_balance), initial_balance) } for point in equity_curve ], 'net_profit': safe_float(results.get('net_profit', 0)) } # Emit results via WebSocket socketio.emit('backtest_complete', backtest_results) except Exception as e: import traceback error_msg = f"{str(e)}\n{traceback.format_exc()}" print(f"Backtest error: {error_msg}") socketio.emit('backtest_error', {'error': str(e)}) finally: backtest_running = False thread = threading.Thread(target=run_backtest_thread, daemon=True) thread.start() return jsonify({'status': 'started', 'message': 'Backtest running...'}) except Exception as e: return jsonify({'error': str(e)}), 500 @app.route('/api/backtest/status') def get_backtest_status(): """Get backtest status""" return jsonify({ 'running': backtest_running, 'results': backtest_results }) # WebSocket handlers @socketio.on('connect') def handle_connect(): """Handle WebSocket connection""" emit('status', {'message': 'Connected to Cyberpunk Dashboard'}) @socketio.on('disconnect') def handle_disconnect(): """Handle WebSocket disconnection""" pass if __name__ == '__main__': print("=" * 60) print("CYBERPUNK POLYMARKET DASHBOARD") print("=" * 60) print("Starting server on http://localhost:5000") print("Press Ctrl+C to stop") print("=" * 60) socketio.run(app, host='0.0.0.0', port=5000, debug=True)