wip-dash-broken

This commit is contained in:
Dobromir Popov 2025-05-24 12:21:03 +03:00
parent d7f8d1af69
commit 01f0a2608f

View File

@ -1,26 +1,31 @@
""" """
Ultra-Fast Scalping Dashboard (500x Leverage) - Real Market Data Ultra-Fast Real-Time Scalping Dashboard (500x Leverage) - Live Data Streaming
Dashboard using ONLY real market data from APIs with: Real-time WebSocket streaming dashboard with:
- Main 1s ETH/USDT chart (full width) - Main 1s ETH/USDT chart (full width) with live updates
- 4 small charts: 1m ETH, 1h ETH, 1d ETH, 1s BTC - 4 small charts: 1m ETH, 1h ETH, 1d ETH, 1s BTC
- 500 candles preloaded at startup - WebSocket price streaming for instant updates
- Real-time updates from data provider - Europe/Sofia timezone support
- Ultra-low latency UI updates (100ms)
- NO CACHED DATA - 100% live streaming
""" """
import asyncio import asyncio
import json import json
import logging import logging
import time import time
import websockets
import pytz
from datetime import datetime, timedelta from datetime import datetime, timedelta
from threading import Thread from threading import Thread, Lock
from typing import Dict, List, Optional, Any from typing import Dict, List, Optional, Any
import pandas as pd
import numpy as np
import requests
import dash import dash
from dash import dcc, html, Input, Output from dash import dcc, html, Input, Output
import plotly.graph_objects as go import plotly.graph_objects as go
import pandas as pd
import numpy as np
from core.config import get_config from core.config import get_config
from core.data_provider import DataProvider from core.data_provider import DataProvider
@ -28,185 +33,156 @@ from core.enhanced_orchestrator import EnhancedTradingOrchestrator, TradingActio
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ScalpingDashboard: class RealTimeScalpingDashboard:
"""Real market data scalping dashboard for 500x leverage trading""" """Real-time scalping dashboard with WebSocket streaming and ultra-low latency"""
def __init__(self, data_provider: DataProvider = None, orchestrator: EnhancedTradingOrchestrator = None): def __init__(self, data_provider: DataProvider = None, orchestrator: EnhancedTradingOrchestrator = None):
"""Initialize the dashboard with real market data""" """Initialize the real-time dashboard with WebSocket streaming"""
self.config = get_config() self.config = get_config()
self.data_provider = data_provider or DataProvider() self.data_provider = data_provider or DataProvider()
self.orchestrator = orchestrator or EnhancedTradingOrchestrator(self.data_provider) self.orchestrator = orchestrator or EnhancedTradingOrchestrator(self.data_provider)
# Timezone setup
self.timezone = pytz.timezone('Europe/Sofia')
# Dashboard state # Dashboard state
self.recent_decisions = [] self.recent_decisions = []
self.scalping_metrics = { self.scalping_metrics = {
'total_trades': 0, 'total_trades': 0,
'win_rate': 0.78, 'win_rate': 0.78,
'total_pnl': 0.0, # Will be updated by runner 'total_pnl': 0.0,
'avg_trade_time': 3.2, # seconds 'avg_trade_time': 3.2,
'leverage': '500x', 'leverage': '500x',
'last_action': None 'last_action': None
} }
# Real market data cache - preload 500 candles for each chart # Real-time price streaming data
self.market_data = { self.live_prices = {
'ETH/USDT': 0.0,
'BTC/USDT': 0.0
}
# Real-time chart data (no caching - always fresh)
self.chart_data = {
'ETH/USDT': { 'ETH/USDT': {
'1s': None, # Main chart '1s': pd.DataFrame(),
'1m': None, # Small chart '1m': pd.DataFrame(),
'1h': None, # Small chart '1h': pd.DataFrame(),
'1d': None # Small chart '1d': pd.DataFrame()
}, },
'BTC/USDT': { 'BTC/USDT': {
'1s': None # Small chart '1s': pd.DataFrame()
} }
} }
# Initialize real market data # WebSocket streaming control
self._preload_market_data() self.streaming = False
self.websocket_threads = []
self.data_lock = Lock()
# Create Dash app # Create Dash app with real-time updates
self.app = dash.Dash(__name__) self.app = dash.Dash(__name__,
external_stylesheets=['https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css'])
# Setup layout and callbacks # Setup layout and callbacks
self._setup_layout() self._setup_layout()
self._setup_callbacks() self._setup_callbacks()
self._start_real_time_streaming()
logger.info("Ultra-Fast Scalping Dashboard initialized with REAL MARKET DATA") logger.info("🚀 Real-Time Scalping Dashboard initialized with LIVE STREAMING")
logger.info("📡 WebSocket price streaming enabled")
def _preload_market_data(self): logger.info(f"🌍 Timezone: {self.timezone}")
"""Preload 500 candles for each chart from real market APIs"""
logger.info("🔄 Preloading 500 candles of real market data...")
try:
# Load ETH/USDT data for main chart and small charts
self.market_data['ETH/USDT']['1s'] = self.data_provider.get_historical_data(
'ETH/USDT', '1s', limit=500
)
self.market_data['ETH/USDT']['1m'] = self.data_provider.get_historical_data(
'ETH/USDT', '1m', limit=500
)
self.market_data['ETH/USDT']['1h'] = self.data_provider.get_historical_data(
'ETH/USDT', '1h', limit=500
)
self.market_data['ETH/USDT']['1d'] = self.data_provider.get_historical_data(
'ETH/USDT', '1d', limit=500
)
# Load BTC/USDT 1s data for small chart
self.market_data['BTC/USDT']['1s'] = self.data_provider.get_historical_data(
'BTC/USDT', '1s', limit=500
)
# Log successful data loading
for symbol in self.market_data:
for timeframe, data in self.market_data[symbol].items():
if data is not None and not data.empty:
logger.info(f"✅ Loaded {len(data)} candles for {symbol} {timeframe}")
logger.info(f" Price range: ${data['close'].min():.2f} - ${data['close'].max():.2f}")
else:
logger.warning(f"⚠️ No data loaded for {symbol} {timeframe}")
logger.info("✅ Market data preload complete - ready for real-time updates")
except Exception as e:
logger.error(f"❌ Error preloading market data: {e}")
# Initialize empty DataFrames as fallback
for symbol in self.market_data:
for timeframe in self.market_data[symbol]:
self.market_data[symbol][timeframe] = pd.DataFrame()
def _setup_layout(self): def _setup_layout(self):
"""Setup the 5-chart dashboard layout""" """Setup the ultra-fast real-time dashboard layout"""
self.app.layout = html.Div([ self.app.layout = html.Div([
# Header with real-time metrics # Header with live metrics
html.Div([ html.Div([
html.H1("ULTRA-FAST SCALPING DASHBOARD - 500x LEVERAGE - REAL MARKET DATA", html.H1("🚀 REAL-TIME SCALPING DASHBOARD - 500x LEVERAGE - LIVE STREAMING",
className="text-center mb-4"), className="text-center mb-4 text-white"),
html.P(f"🌍 Sofia Time Zone | 📡 Live WebSocket Streaming | ⚡ 100ms Updates",
className="text-center text-info"),
# Real-time metrics row # Live metrics row
html.Div([ html.Div([
html.Div([ html.Div([
html.H3(id="live-pnl", className="text-success"), html.H3(id="live-pnl", className="text-success"),
html.P("Total P&L") html.P("Total P&L", className="text-white")
], className="col-md-2 text-center"), ], className="col-md-2 text-center"),
html.Div([ html.Div([
html.H3(id="win-rate", className="text-info"), html.H3(id="win-rate", className="text-info"),
html.P("Win Rate") html.P("Win Rate", className="text-white")
], className="col-md-2 text-center"), ], className="col-md-2 text-center"),
html.Div([ html.Div([
html.H3(id="total-trades", className="text-primary"), html.H3(id="total-trades", className="text-primary"),
html.P("Total Trades") html.P("Total Trades", className="text-white")
], className="col-md-2 text-center"), ], className="col-md-2 text-center"),
html.Div([ html.Div([
html.H3(id="last-action", className="text-warning"), html.H3(id="last-action", className="text-warning"),
html.P("Last Action") html.P("Last Action", className="text-white")
], className="col-md-2 text-center"), ], className="col-md-2 text-center"),
html.Div([ html.Div([
html.H3(id="eth-price", className="text-white"), html.H3(id="eth-price", className="text-success"),
html.P("ETH/USDT Live") html.P("ETH/USDT LIVE", className="text-white")
], className="col-md-2 text-center"), ], className="col-md-2 text-center"),
html.Div([ html.Div([
html.H3(id="btc-price", className="text-white"), html.H3(id="btc-price", className="text-success"),
html.P("BTC/USDT Live") html.P("BTC/USDT LIVE", className="text-white")
], className="col-md-2 text-center") ], className="col-md-2 text-center")
], className="row mb-4") ], className="row mb-4")
], className="bg-dark p-3 mb-3"), ], className="bg-dark p-3 mb-3"),
# Main 1s ETH/USDT chart (full width) # Main 1s ETH/USDT chart (full width) - REAL-TIME
html.Div([ html.Div([
html.H4("ETH/USDT 1s Real-Time Chart (Main Trading Signal)", html.H4("📈 ETH/USDT 1s Real-Time Chart (Live WebSocket Feed)",
className="text-center mb-3"), className="text-center mb-3"),
dcc.Graph(id="main-eth-1s-chart", style={"height": "500px"}) dcc.Graph(id="main-eth-1s-chart", style={"height": "600px"})
], className="mb-4"), ], className="mb-4"),
# Row of 4 small charts # Row of 4 small charts - ALL REAL-TIME
html.Div([ html.Div([
# ETH/USDT 1m
html.Div([ html.Div([
html.H6("ETH/USDT 1m", className="text-center"), html.H6("ETH/USDT 1m LIVE", className="text-center"),
dcc.Graph(id="eth-1m-chart", style={"height": "250px"}) dcc.Graph(id="eth-1m-chart", style={"height": "300px"})
], className="col-md-3"), ], className="col-md-3"),
# ETH/USDT 1h
html.Div([ html.Div([
html.H6("ETH/USDT 1h", className="text-center"), html.H6("ETH/USDT 1h LIVE", className="text-center"),
dcc.Graph(id="eth-1h-chart", style={"height": "250px"}) dcc.Graph(id="eth-1h-chart", style={"height": "300px"})
], className="col-md-3"), ], className="col-md-3"),
# ETH/USDT 1d
html.Div([ html.Div([
html.H6("ETH/USDT 1d", className="text-center"), html.H6("ETH/USDT 1d LIVE", className="text-center"),
dcc.Graph(id="eth-1d-chart", style={"height": "250px"}) dcc.Graph(id="eth-1d-chart", style={"height": "300px"})
], className="col-md-3"), ], className="col-md-3"),
# BTC/USDT 1s
html.Div([ html.Div([
html.H6("BTC/USDT 1s", className="text-center"), html.H6("BTC/USDT 1s LIVE", className="text-center"),
dcc.Graph(id="btc-1s-chart", style={"height": "250px"}) dcc.Graph(id="btc-1s-chart", style={"height": "300px"})
], className="col-md-3") ], className="col-md-3")
], className="row mb-4"), ], className="row mb-4"),
# Recent actions log # Live actions log
html.Div([ html.Div([
html.H5("Live Trading Actions (Real Market Data)", className="text-center mb-3"), html.H5("🔥 Live Trading Actions (Real-Time Stream)", className="text-center mb-3"),
html.Div(id="actions-log") html.Div(id="actions-log")
], className="mb-4"), ], className="mb-4"),
# Auto-refresh for real-time updates # Ultra-fast refresh for real-time updates (100ms)
dcc.Interval( dcc.Interval(
id='market-data-interval', id='ultra-fast-interval',
interval=1000, # Update every 1 second for real-time feel interval=100, # 100ms for ultra-low latency
n_intervals=0 n_intervals=0
) )
], className="container-fluid") ], className="container-fluid bg-dark")
def _setup_callbacks(self): def _setup_callbacks(self):
"""Setup dashboard callbacks with real market data""" """Setup ultra-fast callbacks with real-time streaming data"""
@self.app.callback( @self.app.callback(
[ [
@ -223,34 +199,35 @@ class ScalpingDashboard:
Output('btc-1s-chart', 'figure'), Output('btc-1s-chart', 'figure'),
Output('actions-log', 'children') Output('actions-log', 'children')
], ],
[Input('market-data-interval', 'n_intervals')] [Input('ultra-fast-interval', 'n_intervals')]
) )
def update_dashboard_with_real_data(n_intervals): def update_real_time_dashboard(n_intervals):
"""Update all dashboard components with real market data""" """Update all components with real-time streaming data"""
try: try:
with self.data_lock:
# Update metrics # Update metrics
pnl = f"${self.scalping_metrics['total_pnl']:+.2f}" pnl = f"${self.scalping_metrics['total_pnl']:+.2f}"
win_rate = f"{self.scalping_metrics['win_rate']*100:.1f}%" win_rate = f"{self.scalping_metrics['win_rate']*100:.1f}%"
total_trades = str(self.scalping_metrics['total_trades']) total_trades = str(self.scalping_metrics['total_trades'])
last_action = self.scalping_metrics['last_action'] or "WAITING" last_action = self.scalping_metrics['last_action'] or "WAITING"
# Get current prices from real market data # Live prices from WebSocket stream
eth_price = self._get_current_price('ETH/USDT') eth_price = f"${self.live_prices['ETH/USDT']:.2f}" if self.live_prices['ETH/USDT'] > 0 else "🔄 Loading..."
btc_price = self._get_current_price('BTC/USDT') btc_price = f"${self.live_prices['BTC/USDT']:.2f}" if self.live_prices['BTC/USDT'] > 0 else "🔄 Loading..."
# Refresh market data periodically (every 10 updates) # Refresh chart data every 10 intervals (1 second)
if n_intervals % 10 == 0: if n_intervals % 10 == 0:
self._refresh_market_data() self._refresh_live_data()
# Create charts with real market data # Create real-time charts
main_eth_chart = self._create_real_chart('ETH/USDT', '1s', main_chart=True) main_eth_chart = self._create_live_chart('ETH/USDT', '1s', main_chart=True)
eth_1m_chart = self._create_real_chart('ETH/USDT', '1m') eth_1m_chart = self._create_live_chart('ETH/USDT', '1m')
eth_1h_chart = self._create_real_chart('ETH/USDT', '1h') eth_1h_chart = self._create_live_chart('ETH/USDT', '1h')
eth_1d_chart = self._create_real_chart('ETH/USDT', '1d') eth_1d_chart = self._create_live_chart('ETH/USDT', '1d')
btc_1s_chart = self._create_real_chart('BTC/USDT', '1s') btc_1s_chart = self._create_live_chart('BTC/USDT', '1s')
# Create actions log # Live actions log
actions_log = self._create_actions_log() actions_log = self._create_live_actions_log()
return ( return (
pnl, win_rate, total_trades, last_action, eth_price, btc_price, pnl, win_rate, total_trades, last_action, eth_price, btc_price,
@ -259,71 +236,161 @@ class ScalpingDashboard:
) )
except Exception as e: except Exception as e:
logger.error(f"Error updating dashboard: {e}") logger.error(f"Error in real-time update: {e}")
# Return safe defaults
return ( return (
"$0.00", "0%", "0", "ERROR", "$0", "$0", "$0.00", "0%", "0", "ERROR", "🔄 Loading...", "🔄 Loading...",
{}, {}, {}, {}, {}, "Loading real market data..." {}, {}, {}, {}, {}, "🔄 Loading real-time data..."
) )
def _refresh_market_data(self): def _start_real_time_streaming(self):
"""Refresh market data from APIs""" """Start WebSocket streaming for real-time price updates"""
logger.info("🚀 Starting real-time WebSocket price streaming...")
self.streaming = True
# Start WebSocket streams for each symbol
for symbol in ['ETHUSDT', 'BTCUSDT']:
thread = Thread(target=self._websocket_price_stream, args=(symbol,), daemon=True)
thread.start()
self.websocket_threads.append(thread)
logger.info("📡 WebSocket streams started for ETH/USDT and BTC/USDT")
def _websocket_price_stream(self, symbol: str):
"""WebSocket stream for real-time price updates"""
url = f"wss://stream.binance.com:9443/ws/{symbol.lower()}@ticker"
while self.streaming:
try: try:
# Get latest data for each chart (last 100 candles for efficiency) async def stream_prices():
for symbol in self.market_data: async with websockets.connect(url) as websocket:
for timeframe in self.market_data[symbol]: logger.info(f"📡 WebSocket connected for {symbol}")
latest_data = self.data_provider.get_latest_candles(symbol, timeframe, limit=100) async for message in websocket:
if latest_data is not None and not latest_data.empty: if not self.streaming:
# Update our cache with latest data break
if self.market_data[symbol][timeframe] is not None:
# Append new data and keep last 500 candles try:
combined = pd.concat([self.market_data[symbol][timeframe], latest_data]) data = json.loads(message)
combined = combined.drop_duplicates(subset=['timestamp'], keep='last') price = float(data.get('c', 0))
self.market_data[symbol][timeframe] = combined.tail(500)
else: # Update live prices
self.market_data[symbol][timeframe] = latest_data.tail(500) with self.data_lock:
formatted_symbol = f"{symbol[:3]}/{symbol[3:]}"
self.live_prices[formatted_symbol] = price
logger.debug(f"💰 {formatted_symbol}: ${price:.2f}")
except Exception as e: except Exception as e:
logger.warning(f"Error refreshing market data: {e}") logger.warning(f"Error processing WebSocket data for {symbol}: {e}")
# Run the async stream
asyncio.new_event_loop().run_until_complete(stream_prices())
def _get_current_price(self, symbol: str) -> str:
"""Get current price from real market data"""
try:
data = self.market_data[symbol]['1s']
if data is not None and not data.empty:
current_price = data['close'].iloc[-1]
return f"${current_price:.2f}"
return "$0.00"
except Exception as e: except Exception as e:
logger.warning(f"Error getting current price for {symbol}: {e}") logger.error(f"WebSocket error for {symbol}: {e}")
return "$0.00" if self.streaming:
logger.info(f"🔄 Reconnecting WebSocket for {symbol} in 5 seconds...")
time.sleep(5)
def _create_real_chart(self, symbol: str, timeframe: str, main_chart: bool = False): def _refresh_live_data(self):
"""Create chart using real market data""" """Refresh chart data with fresh API calls (NO CACHING)"""
try: try:
data = self.market_data[symbol][timeframe] logger.info("🔄 Fetching fresh market data (NO CACHE)...")
if data is None or data.empty: # Force fresh API calls for all timeframes
# Return empty chart with message for symbol, timeframes in self.chart_data.items():
for timeframe in timeframes:
try:
# FORCE fresh data - explicitly set refresh=True
fresh_data = self._fetch_fresh_candles(symbol, timeframe, limit=200)
if fresh_data is not None and not fresh_data.empty:
with self.data_lock:
self.chart_data[symbol][timeframe] = fresh_data
logger.debug(f"✅ Fresh data loaded: {symbol} {timeframe} - {len(fresh_data)} candles")
except Exception as e:
logger.warning(f"Error fetching fresh data for {symbol} {timeframe}: {e}")
except Exception as e:
logger.error(f"Error in live data refresh: {e}")
def _fetch_fresh_candles(self, symbol: str, timeframe: str, limit: int = 200) -> pd.DataFrame:
"""Fetch fresh candles directly from Binance API (bypass all caching)"""
try:
# Convert symbol format
binance_symbol = symbol.replace('/', '').upper()
# Convert timeframe
timeframe_map = {
'1s': '1s', '1m': '1m', '1h': '1h', '1d': '1d'
}
binance_timeframe = timeframe_map.get(timeframe, '1m')
# Direct API call to Binance
url = "https://api.binance.com/api/v3/klines"
params = {
'symbol': binance_symbol,
'interval': binance_timeframe,
'limit': limit
}
response = requests.get(url, params=params, timeout=5)
response.raise_for_status()
data = response.json()
# Convert to DataFrame
df = pd.DataFrame(data, columns=[
'timestamp', 'open', 'high', 'low', 'close', 'volume',
'close_time', 'quote_volume', 'trades', 'taker_buy_base',
'taker_buy_quote', 'ignore'
])
# Process columns with Sofia timezone
df['timestamp'] = pd.to_datetime(df['timestamp'], unit='ms').dt.tz_localize('UTC').dt.tz_convert(self.timezone)
for col in ['open', 'high', 'low', 'close', 'volume']:
df[col] = df[col].astype(float)
# Keep only OHLCV columns
df = df[['timestamp', 'open', 'high', 'low', 'close', 'volume']]
df = df.sort_values('timestamp').reset_index(drop=True)
logger.debug(f"📊 Fresh API data: {symbol} {timeframe} - {len(df)} candles")
return df
except Exception as e:
logger.error(f"Error fetching fresh candles from API: {e}")
return pd.DataFrame()
def _create_live_chart(self, symbol: str, timeframe: str, main_chart: bool = False):
"""Create charts with real-time streaming data"""
try:
with self.data_lock:
data = self.chart_data[symbol][timeframe]
if data.empty:
# Return loading chart
fig = go.Figure() fig = go.Figure()
fig.add_annotation( fig.add_annotation(
text=f"Loading real market data for {symbol} {timeframe}...", text=f"🔄 Loading real-time data for {symbol} {timeframe}...",
xref="paper", yref="paper", xref="paper", yref="paper",
x=0.5, y=0.5, showarrow=False, x=0.5, y=0.5, showarrow=False,
font=dict(size=16, color="red") font=dict(size=16, color="#00ff88")
) )
fig.update_layout( fig.update_layout(
title=f"{symbol} {timeframe} - Real Market Data", title=f"📡 {symbol} {timeframe} - Live Stream",
template="plotly_dark", template="plotly_dark",
height=500 if main_chart else 250 height=600 if main_chart else 300,
paper_bgcolor='#1e1e1e',
plot_bgcolor='#1e1e1e'
) )
return fig return fig
# Create candlestick chart from real data # Create real-time chart
fig = go.Figure() fig = go.Figure()
if main_chart: if main_chart:
# Main chart with candlesticks and volume # Main chart with candlesticks, volume, and live price
fig.add_trace(go.Candlestick( fig.add_trace(go.Candlestick(
x=data['timestamp'], x=data['timestamp'],
open=data['open'], open=data['open'],
@ -335,127 +402,157 @@ class ScalpingDashboard:
decreasing_line_color='#ff4444' decreasing_line_color='#ff4444'
)) ))
# Add volume as secondary plot # Volume subplot
fig.add_trace(go.Bar( fig.add_trace(go.Bar(
x=data['timestamp'], x=data['timestamp'],
y=data['volume'], y=data['volume'],
name="Volume", name="Volume",
yaxis='y2', yaxis='y2',
opacity=0.3, opacity=0.4,
marker_color='lightblue' marker_color='#4CAF50'
)) ))
# Main chart layout # Current live price line
current_price = self.live_prices.get(symbol, data['close'].iloc[-1] if not data.empty else 0)
if current_price > 0:
fig.add_hline(
y=current_price,
line_dash="dot",
line_color="#FFD700",
line_width=2,
annotation_text=f"LIVE: ${current_price:.2f}",
annotation_position="right"
)
# Sofia time in title
sofia_time = datetime.now(self.timezone).strftime("%H:%M:%S %Z")
fig.update_layout( fig.update_layout(
title=f"{symbol} {timeframe} Real-Time Market Data - Latest: ${data['close'].iloc[-1]:.2f}", title=f"📈 {symbol} {timeframe} LIVE | Sofia Time: {sofia_time} | Current: ${current_price:.2f}",
yaxis_title="Price (USDT)", yaxis_title="Price (USDT)",
yaxis2=dict(title="Volume", overlaying='y', side='right'), yaxis2=dict(title="Volume", overlaying='y', side='right'),
template="plotly_dark", template="plotly_dark",
showlegend=False, showlegend=False,
height=500 height=600,
) paper_bgcolor='#1e1e1e',
plot_bgcolor='#1e1e1e'
# Add current price line
current_price = data['close'].iloc[-1]
fig.add_hline(
y=current_price,
line_dash="dash",
line_color="yellow",
annotation_text=f"${current_price:.2f}",
annotation_position="right"
) )
else: else:
# Small chart - simple line chart # Small chart - line chart with live updates
price_change = ((data['close'].iloc[-1] - data['close'].iloc[0]) / data['close'].iloc[0]) * 100
line_color = '#00ff88' if price_change >= 0 else '#ff4444'
fig.add_trace(go.Scatter( fig.add_trace(go.Scatter(
x=data['timestamp'], x=data['timestamp'],
y=data['close'], y=data['close'],
mode='lines', mode='lines',
name=f"{symbol} {timeframe}", name=f"{symbol} {timeframe}",
line=dict(color=line_color, width=2) line=dict(color='#00ff88', width=2)
))
# Live price point
current_price = self.live_prices.get(symbol, data['close'].iloc[-1] if not data.empty else 0)
if current_price > 0:
fig.add_trace(go.Scatter(
x=[data['timestamp'].iloc[-1]] if not data.empty else [datetime.now(self.timezone)],
y=[current_price],
mode='markers',
marker=dict(color='#FFD700', size=8),
name="Live Price"
)) ))
# Small chart layout
fig.update_layout( fig.update_layout(
template="plotly_dark", template="plotly_dark",
showlegend=False, showlegend=False,
margin=dict(l=10, r=10, t=30, b=10), margin=dict(l=10, r=10, t=40, b=10),
height=250, height=300,
title=f"{symbol} {timeframe}: ${data['close'].iloc[-1]:.2f}" title=f"📊 {symbol} {timeframe} | ${current_price:.2f}",
) paper_bgcolor='#1e1e1e',
plot_bgcolor='#1e1e1e'
# Add price change annotation
change_color = "green" if price_change >= 0 else "red"
fig.add_annotation(
text=f"{price_change:+.2f}%",
xref="paper", yref="paper",
x=0.95, y=0.95,
showarrow=False,
font=dict(color=change_color, size=12, weight="bold"),
bgcolor="rgba(0,0,0,0.7)"
) )
return fig return fig
except Exception as e: except Exception as e:
logger.error(f"Error creating chart for {symbol} {timeframe}: {e}") logger.error(f"Error creating live chart for {symbol} {timeframe}: {e}")
# Return error chart # Return error chart
fig = go.Figure() fig = go.Figure()
fig.add_annotation( fig.add_annotation(
text=f"Error loading {symbol} {timeframe}", text=f"Error loading {symbol} {timeframe}",
xref="paper", yref="paper", xref="paper", yref="paper",
x=0.5, y=0.5, showarrow=False, x=0.5, y=0.5, showarrow=False,
font=dict(size=14, color="red") font=dict(size=14, color="#ff4444")
) )
fig.update_layout( fig.update_layout(
template="plotly_dark", template="plotly_dark",
height=500 if main_chart else 250 height=600 if main_chart else 300,
paper_bgcolor='#1e1e1e',
plot_bgcolor='#1e1e1e'
) )
return fig return fig
def _create_actions_log(self): def _create_live_actions_log(self):
"""Create trading actions log""" """Create live trading actions log"""
if not self.recent_decisions: if not self.recent_decisions:
return html.P("Waiting for trading signals from real market data...", className="text-muted text-center") return html.P("⏳ Waiting for live trading signals from real-time stream...",
className="text-muted text-center")
log_items = [] log_items = []
for action in self.recent_decisions[-5:]: # Show last 5 actions for action in self.recent_decisions[-5:]:
sofia_time = action.timestamp.astimezone(self.timezone).strftime("%H:%M:%S")
log_items.append( log_items.append(
html.P( html.P(
f"🔥 {action.action} {action.symbol} @ ${action.price:.2f} " f"🔥 {sofia_time} | {action.action} {action.symbol} @ ${action.price:.2f} "
f"(Confidence: {action.confidence:.1%}) - Real Market Data", f"(Confidence: {action.confidence:.1%}) | 📡 LIVE STREAM",
className="text-center mb-1" className="text-center mb-1 text-light"
) )
) )
return html.Div(log_items) return html.Div(log_items)
def add_trading_decision(self, decision: TradingAction): def add_trading_decision(self, decision: TradingAction):
"""Add a new trading decision based on real market data""" """Add trading decision with Sofia timezone"""
decision.timestamp = decision.timestamp.astimezone(self.timezone)
self.recent_decisions.append(decision) self.recent_decisions.append(decision)
if len(self.recent_decisions) > 50: if len(self.recent_decisions) > 50:
self.recent_decisions.pop(0) self.recent_decisions.pop(0)
self.scalping_metrics['total_trades'] += 1 self.scalping_metrics['total_trades'] += 1
self.scalping_metrics['last_action'] = f"{decision.action} {decision.symbol}" self.scalping_metrics['last_action'] = f"{decision.action} {decision.symbol}"
logger.info(f"📊 Added real market trading decision: {decision.action} {decision.symbol} @ ${decision.price:.2f}") sofia_time = decision.timestamp.strftime("%H:%M:%S %Z")
logger.info(f"🔥 {sofia_time} | Live trading decision: {decision.action} {decision.symbol} @ ${decision.price:.2f}")
def run(self, host: str = '127.0.0.1', port: int = 8050, debug: bool = False): def stop_streaming(self):
"""Run the real market data dashboard""" """Stop all WebSocket streams"""
logger.info(f"🚀 Starting Real Market Data Dashboard at http://{host}:{port}") logger.info("🛑 Stopping real-time WebSocket streams...")
logger.info("📊 Dashboard Features:") self.streaming = False
logger.info(" • Main 1s ETH/USDT chart with real market data")
logger.info(" • 4 small charts: 1m/1h/1d ETH + 1s BTC") for thread in self.websocket_threads:
logger.info(" • 500 candles preloaded from Binance API") if thread.is_alive():
logger.info(" • Real-time updates every second") thread.join(timeout=2)
logger.info(" • NO GENERATED DATA - 100% real market feeds")
logger.info("📡 WebSocket streams stopped")
def run(self, host: str = '127.0.0.1', port: int = 8051, debug: bool = False):
"""Run the real-time dashboard"""
try:
logger.info(f"🚀 Starting Real-Time Scalping Dashboard at http://{host}:{port}")
logger.info("📡 Features:")
logger.info(" • WebSocket price streaming (100ms updates)")
logger.info(" • NO CACHED DATA - Always fresh API calls")
logger.info(f" • Sofia timezone: {self.timezone}")
logger.info(" • Ultra-low latency real-time charts")
logger.info(" • Live P&L and trading metrics")
self.app.run(host=host, port=port, debug=debug) self.app.run(host=host, port=port, debug=debug)
except KeyboardInterrupt:
logger.info("👋 Shutting down real-time dashboard...")
finally:
self.stop_streaming()
def create_scalping_dashboard(data_provider=None, orchestrator=None): def create_scalping_dashboard(data_provider=None, orchestrator=None):
"""Create dashboard instance with real market data""" """Create real-time dashboard instance"""
return ScalpingDashboard(data_provider, orchestrator) return RealTimeScalpingDashboard(data_provider, orchestrator)
# For backward compatibility
ScalpingDashboard = RealTimeScalpingDashboard