dash closed trades history

This commit is contained in:
Dobromir Popov
2025-05-27 13:47:34 +03:00
parent cc20b6194a
commit 4567912186
12 changed files with 2388 additions and 430 deletions

View File

@ -19,6 +19,7 @@ import pytz
from datetime import datetime, timedelta
from threading import Thread, Lock
from typing import Dict, List, Optional, Any
from collections import deque
import pandas as pd
import numpy as np
import requests
@ -32,16 +33,17 @@ import dash_bootstrap_components as dbc
from core.config import get_config
from core.data_provider import DataProvider, MarketTick
from core.enhanced_orchestrator import EnhancedTradingOrchestrator, TradingAction
from core.trading_executor import TradingExecutor, Position, TradeRecord
logger = logging.getLogger(__name__)
class TradingSession:
"""
Session-based trading with $100 starting balance
Session-based trading with MEXC integration
Tracks P&L for each session but resets between sessions
"""
def __init__(self, session_id: str = None):
def __init__(self, session_id: str = None, trading_executor: TradingExecutor = None):
self.session_id = session_id or str(uuid.uuid4())[:8]
self.start_time = datetime.now()
self.starting_balance = 100.0 # $100 USD starting balance
@ -53,17 +55,36 @@ class TradingSession:
self.positions = {} # symbol -> {'size': float, 'entry_price': float, 'side': str}
self.trade_history = []
self.last_action = None
self.trading_executor = trading_executor
logger.info(f"NEW TRADING SESSION STARTED")
logger.info(f"NEW TRADING SESSION STARTED WITH MEXC INTEGRATION")
logger.info(f"Session ID: {self.session_id}")
logger.info(f"Starting Balance: ${self.starting_balance:.2f}")
logger.info(f"MEXC Trading: {'ENABLED' if trading_executor and trading_executor.trading_enabled else 'DISABLED'}")
logger.info(f"Start Time: {self.start_time.strftime('%Y-%m-%d %H:%M:%S')}")
def execute_trade(self, action: TradingAction, current_price: float):
"""Execute a trading action and update P&L"""
"""Execute a trading action through MEXC and update P&L"""
try:
symbol = action.symbol
# Execute trade through MEXC if available
mexc_success = False
if self.trading_executor and action.action != 'HOLD':
try:
mexc_success = self.trading_executor.execute_signal(
symbol=symbol,
action=action.action,
confidence=action.confidence,
current_price=current_price
)
if mexc_success:
logger.info(f"MEXC: Trade executed successfully: {action.action} {symbol}")
else:
logger.warning(f"MEXC: Trade execution failed: {action.action} {symbol}")
except Exception as e:
logger.error(f"MEXC: Error executing trade: {e}")
# Calculate position size based on confidence and leverage
leverage = 500 # 500x leverage
risk_per_trade = 0.02 # 2% risk per trade
@ -77,7 +98,8 @@ class TradingSession:
'price': current_price,
'size': position_size,
'value': position_value,
'confidence': action.confidence
'confidence': action.confidence,
'mexc_executed': mexc_success
}
if action.action == 'BUY':
@ -121,6 +143,7 @@ class TradingSession:
self.current_balance = self.starting_balance + self.total_pnl
logger.info(f"TRADING: TRADE EXECUTED: {action.action} {symbol} @ ${current_price:.2f}")
logger.info(f"MEXC: {'SUCCESS' if mexc_success else 'SIMULATION'}")
logger.info(f"CHART: Position Size: {position_size:.6f} (${position_value:.2f})")
logger.info(f"MONEY: Session P&L: ${self.total_pnl:+.2f} | Balance: ${self.current_balance:.2f}")
@ -191,11 +214,12 @@ class TradingSession:
class RealTimeScalpingDashboard:
"""Real-time scalping dashboard with WebSocket streaming and ultra-low latency"""
def __init__(self, data_provider: DataProvider = None, orchestrator: EnhancedTradingOrchestrator = None):
"""Initialize the real-time dashboard with WebSocket streaming"""
def __init__(self, data_provider: DataProvider = None, orchestrator: EnhancedTradingOrchestrator = None, trading_executor: TradingExecutor = None):
"""Initialize the real-time dashboard with WebSocket streaming and MEXC integration"""
self.config = get_config()
self.data_provider = data_provider or DataProvider()
self.orchestrator = orchestrator or EnhancedTradingOrchestrator(self.data_provider)
self.trading_executor = trading_executor or TradingExecutor()
# Verify universal data format compliance
logger.info("UNIVERSAL DATA FORMAT VERIFICATION:")
@ -213,8 +237,8 @@ class RealTimeScalpingDashboard:
# Log preload results
for symbol, timeframe_results in preload_results.items():
for timeframe, success in timeframe_results.items():
status = "" if success else ""
logger.info(f" {status} {symbol} {timeframe}")
status = "OK" if success else "FAIL"
logger.info(f" {status} {symbol} {timeframe}")
# Test universal data adapter
try:
@ -230,14 +254,14 @@ class RealTimeScalpingDashboard:
logger.info(f" BTC reference: {len(universal_stream.btc_ticks)} samples")
logger.info(f" Data quality: {universal_stream.metadata['data_quality']['overall_score']:.2f}")
else:
logger.warning(f" Universal data format validation FAILED: {issues}")
logger.warning(f"FAIL: Universal data format validation FAILED: {issues}")
else:
logger.warning(" Failed to get universal data stream")
logger.warning("FAIL: Failed to get universal data stream")
except Exception as e:
logger.error(f" Universal data format test failed: {e}")
logger.error(f"FAIL: Universal data format test failed: {e}")
# Initialize new trading session with $100 starting balance
self.trading_session = TradingSession()
# Initialize new trading session with MEXC integration
self.trading_session = TradingSession(trading_executor=self.trading_executor)
# Timezone setup
self.timezone = pytz.timezone('Europe/Sofia')
@ -549,7 +573,8 @@ class RealTimeScalpingDashboard:
html.Div([
html.H4(id="current-balance", className="text-success"),
html.P("Current Balance", className="text-white")
html.P("Current Balance", className="text-white"),
html.Small(id="account-details", className="text-muted")
], className="col-md-3 text-center"), # Increased from col-md-2
html.Div([
@ -565,40 +590,57 @@ class RealTimeScalpingDashboard:
html.Div([
html.H4("500x", className="text-danger"),
html.P("Leverage", className="text-white")
], className="col-md-3 text-center") # Increased from col-md-2
], className="col-md-2 text-center"),
html.Div([
html.H4(id="mexc-status", className="text-info"),
html.P("MEXC API", className="text-white")
], className="col-md-2 text-center")
], className="row mb-3"),
# Live metrics row
# Live metrics row (split layout)
html.Div([
# Left side - Key metrics (4 columns, 8/12 width)
html.Div([
html.H3(id="live-pnl", className="text-success"),
html.P("Session P&L", className="text-white")
], className="col-md-2 text-center"),
html.Div([
html.H3(id="live-pnl", className="text-success"),
html.P("Session P&L", className="text-white")
], className="col-md-3 text-center"),
html.Div([
html.H3(id="win-rate", className="text-info"),
html.P("Win Rate", className="text-white")
], className="col-md-3 text-center"),
html.Div([
html.H3(id="total-trades", className="text-primary"),
html.P("Total Trades", className="text-white")
], className="col-md-3 text-center"),
html.Div([
html.H3(id="last-action", className="text-warning"),
html.P("Last Action", className="text-white")
], className="col-md-3 text-center")
], className="col-md-4"),
# Middle - Price displays (2 columns, 2/12 width)
html.Div([
html.H3(id="win-rate", className="text-info"),
html.P("Win Rate", className="text-white")
], className="col-md-2 text-center"),
html.Div([
html.H3(id="eth-price", className="text-success"),
html.P("ETH/USDT LIVE", className="text-white")
], className="col-md-6 text-center"),
html.Div([
html.H3(id="btc-price", className="text-success"),
html.P("BTC/USDT LIVE", className="text-white")
], className="col-md-6 text-center")
], className="col-md-2"),
# Right side - Recent Trading Actions (6/12 width)
html.Div([
html.H3(id="total-trades", className="text-primary"),
html.P("Total Trades", className="text-white")
], className="col-md-2 text-center"),
html.Div([
html.H3(id="last-action", className="text-warning"),
html.P("Last Action", className="text-white")
], className="col-md-2 text-center"),
html.Div([
html.H3(id="eth-price", className="text-success"),
html.P("ETH/USDT LIVE", className="text-white")
], className="col-md-2 text-center"),
html.Div([
html.H3(id="btc-price", className="text-success"),
html.P("BTC/USDT LIVE", className="text-white")
], className="col-md-2 text-center")
html.H5("Recent Trading Signals & Executions", className="text-center mb-2 text-warning"),
html.Div(id="actions-log", style={"height": "120px", "overflowY": "auto", "backgroundColor": "rgba(0,0,0,0.3)", "padding": "10px", "borderRadius": "5px"})
], className="col-md-6")
], className="row mb-4")
], className="bg-dark p-3 mb-3"),
@ -651,11 +693,7 @@ class RealTimeScalpingDashboard:
html.Div(id="training-events-log")
], className="mb-4"),
# Live actions log
html.Div([
html.H5("Live Session Trading Actions (Real-Time Stream)", className="text-center mb-3"),
html.Div(id="actions-log")
], className="mb-4"),
# Dynamic interval - adjusts based on system performance
dcc.Interval(
@ -690,6 +728,7 @@ class RealTimeScalpingDashboard:
@self.app.callback(
[
Output('current-balance', 'children'),
Output('account-details', 'children'),
Output('session-duration', 'children'),
Output('open-positions', 'children'),
Output('live-pnl', 'children'),
@ -698,6 +737,7 @@ class RealTimeScalpingDashboard:
Output('last-action', 'children'),
Output('eth-price', 'children'),
Output('btc-price', 'children'),
Output('mexc-status', 'children'),
Output('main-eth-1s-chart', 'figure'),
Output('eth-1m-chart', 'figure'),
Output('eth-1h-chart', 'figure'),
@ -739,6 +779,11 @@ class RealTimeScalpingDashboard:
# Update session metrics
current_balance = f"${dashboard_instance.trading_session.current_balance:.2f}"
# Account details
balance_change = dashboard_instance.trading_session.current_balance - dashboard_instance.trading_session.starting_balance
balance_change_pct = (balance_change / dashboard_instance.trading_session.starting_balance) * 100
account_details = f"Change: ${balance_change:+.2f} ({balance_change_pct:+.1f}%)"
# Create color-coded position display
positions = dashboard_instance.trading_session.positions
if positions:
@ -775,6 +820,14 @@ class RealTimeScalpingDashboard:
eth_price = f"${dashboard_instance.live_prices['ETH/USDT']:.2f}" if dashboard_instance.live_prices['ETH/USDT'] > 0 else "Loading..."
btc_price = f"${dashboard_instance.live_prices['BTC/USDT']:.2f}" if dashboard_instance.live_prices['BTC/USDT'] > 0 else "Loading..."
# MEXC status
if dashboard_instance.trading_executor and dashboard_instance.trading_executor.trading_enabled:
mexc_status = "LIVE"
elif dashboard_instance.trading_executor and dashboard_instance.trading_executor.dry_run:
mexc_status = "DRY RUN"
else:
mexc_status = "OFFLINE"
# Create real-time charts - use WebSocket tick buffer for main chart and BTC
try:
main_eth_chart = dashboard_instance._create_main_tick_chart('ETH/USDT')
@ -840,7 +893,7 @@ class RealTimeScalpingDashboard:
# Store last known state for throttling
result = (
current_balance, duration_str, open_positions, pnl, win_rate, total_trades, last_action, eth_price, btc_price,
current_balance, account_details, duration_str, open_positions, pnl, win_rate, total_trades, last_action, eth_price, btc_price, mexc_status,
main_eth_chart, eth_1m_chart, eth_1h_chart, eth_1d_chart, btc_1s_chart,
model_training_status, orchestrator_status, training_events_log, actions_log, debug_status
)
@ -876,7 +929,7 @@ class RealTimeScalpingDashboard:
])
error_result = (
"$100.00", "00:00:00", "0", "$0.00", "0%", "0", "ERROR", "Loading...", "Loading...",
"$100.00", "Change: $0.00 (0.0%)", "00:00:00", "0", "$0.00", "0%", "0", "ERROR", "Loading...", "Loading...", "OFFLINE",
empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
"Loading model status...", "Loading orchestrator status...", "Loading training events...",
"Loading real-time data...", error_debug
@ -928,7 +981,7 @@ class RealTimeScalpingDashboard:
}
return (
"$100.00", "00:00:00", "0", "$0.00", "0%", "0", "INIT", "Loading...", "Loading...",
"$100.00", "Change: $0.00 (0.0%)", "00:00:00", "0", "$0.00", "0%", "0", "INIT", "Loading...", "Loading...", "OFFLINE",
empty_fig, empty_fig, empty_fig, empty_fig, empty_fig,
"Initializing models...", "Starting orchestrator...", "Loading events...",
"Waiting for data...", html.P("Initializing dashboard...", className="text-info")
@ -1108,11 +1161,11 @@ class RealTimeScalpingDashboard:
# Update live prices
self.live_prices[symbol] = current_price
# Add to tick buffer for charts (simulate tick data from HTTP)
# Add to tick buffer for charts (HTTP polling data)
tick_entry = {
'timestamp': timestamp,
'price': current_price,
'volume': 100.0, # Mock volume for HTTP data
'volume': 0.0, # No volume data from HTTP polling
'open': current_price,
'high': current_price,
'low': current_price,
@ -1248,26 +1301,20 @@ class RealTimeScalpingDashboard:
self.chart_data[symbol][timeframe] = cached_data
logger.info(f"CACHE: Using cached data for {symbol} {timeframe} ({len(cached_data)} candles)")
else:
# Final fallback to mock data
logger.warning(f"MOCK: Generating mock data for {symbol} {timeframe}")
mock_data = self._generate_mock_data(symbol, timeframe, 50)
# No data available - use empty DataFrame
logger.warning(f"NO DATA: No data available for {symbol} {timeframe}")
with self.data_lock:
if symbol not in self.chart_data:
self.chart_data[symbol] = {}
self.chart_data[symbol][timeframe] = mock_data
self.chart_data[symbol][timeframe] = pd.DataFrame()
except Exception as e:
logger.error(f"ERROR: Failed to refresh {symbol} {timeframe}: {e}")
# Generate mock data as final fallback
try:
mock_data = self._generate_mock_data(symbol, timeframe, 50)
with self.data_lock:
if symbol not in self.chart_data:
self.chart_data[symbol] = {}
self.chart_data[symbol][timeframe] = mock_data
logger.warning(f"FALLBACK: Using mock data for {symbol} {timeframe}")
except Exception as mock_error:
logger.error(f"CRITICAL: Failed to generate mock data: {mock_error}")
# Use empty DataFrame as fallback
with self.data_lock:
if symbol not in self.chart_data:
self.chart_data[symbol] = {}
self.chart_data[symbol][timeframe] = pd.DataFrame()
logger.info("REFRESH: LIVE data refresh complete")
@ -1291,83 +1338,7 @@ class RealTimeScalpingDashboard:
logger.error(f"Error fetching fresh candles for {symbol} {timeframe}: {e}")
return pd.DataFrame()
def _generate_mock_data(self, symbol: str, timeframe: str, num_candles: int = 100) -> pd.DataFrame:
"""Generate realistic mock data as fallback when API fails"""
try:
import random
from datetime import datetime, timedelta
# Base prices for different symbols
base_prices = {
'ETH/USDT': 3500.0,
'BTC/USDT': 65000.0
}
base_price = base_prices.get(symbol, 3500.0)
# Timeframe intervals in seconds
intervals = {
'1s': 1,
'1m': 60,
'1h': 3600,
'1d': 86400
}
interval_seconds = intervals.get(timeframe, 60)
# Generate timestamps
end_time = datetime.now()
timestamps = []
for i in range(num_candles):
timestamp = end_time - timedelta(seconds=interval_seconds * (num_candles - i - 1))
timestamps.append(timestamp)
# Generate realistic price data with trend and volatility
data = []
current_price = base_price
for i, timestamp in enumerate(timestamps):
# Add some trend and random walk
trend = 0.0001 * random.uniform(-1, 1) # Small trend
volatility = 0.002 * random.uniform(0.5, 2.0) # Variable volatility
# Price movement
price_change = current_price * (trend + volatility * random.uniform(-1, 1))
current_price += price_change
# Ensure price doesn't go negative
current_price = max(current_price, base_price * 0.5)
# Generate OHLC from current price
high_offset = abs(random.uniform(0, 0.005)) * current_price
low_offset = abs(random.uniform(0, 0.005)) * current_price
open_price = current_price + random.uniform(-0.002, 0.002) * current_price
high_price = max(open_price, current_price) + high_offset
low_price = min(open_price, current_price) - low_offset
close_price = current_price
# Generate volume
base_volume = 1000 if symbol == 'ETH/USDT' else 50
volume = base_volume * random.uniform(0.5, 2.0)
data.append({
'timestamp': timestamp,
'open': round(open_price, 2),
'high': round(high_price, 2),
'low': round(low_price, 2),
'close': round(close_price, 2),
'volume': round(volume, 4)
})
df = pd.DataFrame(data)
logger.info(f"Generated {len(df)} mock candles for {symbol} {timeframe}")
return df
except Exception as e:
logger.error(f"Error generating mock data: {e}")
# Return minimal empty dataframe
return pd.DataFrame(columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
def _create_live_chart(self, symbol: str, timeframe: str, main_chart: bool = False):
"""Create charts with real-time streaming data using proven working method"""
@ -1385,10 +1356,10 @@ class RealTimeScalpingDashboard:
except Exception as e:
logger.warning(f"[ERROR] Error getting cached data: {e}")
# If no cached data, generate mock data immediately
# If no cached data, return empty chart
if data is None or data.empty:
logger.debug(f"[MOCK] Generating mock data for {symbol} {timeframe}")
data = self._generate_mock_data(symbol, timeframe, 50)
logger.debug(f"NO DATA: No data available for {symbol} {timeframe}")
return self._create_empty_chart(f"{symbol} {timeframe} - No Data Available")
# Ensure we have valid data
if data is None or data.empty:
@ -1562,10 +1533,10 @@ class RealTimeScalpingDashboard:
except Exception as e:
logger.warning(f"Error getting cached data: {e}")
# If no cached data, generate mock data
# If no cached data, return empty chart
if data is None or data.empty:
logger.debug(f"Generating mock data for {symbol} {timeframe}")
data = self._generate_mock_data(symbol, timeframe, 50)
logger.debug(f"NO DATA: No data available for {symbol} {timeframe}")
return self._create_empty_chart(f"{symbol} {timeframe} - No Data Available")
# Ensure we have valid data
if data is None or data.empty:
@ -2175,17 +2146,17 @@ class RealTimeScalpingDashboard:
'confidence': confidence_gap,
'color': 'text-info',
'priority': 2
})
})
# Add RL training events based on queue activity
# Add RL training events based on queue activity
if hasattr(self.orchestrator, 'rl_evaluation_queue') and self.orchestrator.rl_evaluation_queue:
queue_size = len(self.orchestrator.rl_evaluation_queue)
current_time = datetime.now()
current_time = datetime.now()
if queue_size > 0:
events.append({
'time': current_time.strftime('%H:%M:%S'),
'type': 'RL',
'type': 'RL',
'event': f'Experience replay active (queue: {queue_size} actions)',
'confidence': min(1.0, queue_size / 10),
'color': 'text-success',
@ -2200,7 +2171,7 @@ class RealTimeScalpingDashboard:
if patterns_detected > 0:
events.append({
'time': datetime.now().strftime('%H:%M:%S'),
'type': 'TICK',
'type': 'TICK',
'event': f'Violent move patterns detected: {patterns_detected}',
'confidence': min(1.0, patterns_detected / 5),
'color': 'text-info',
@ -2529,49 +2500,48 @@ class RealTimeScalpingDashboard:
logger.info("Training data collection thread started")
def _collect_training_ticks(self):
"""Collect tick data for training cache"""
"""Collect real tick data for training cache from data provider"""
try:
# Get current prices and create mock ticks for training
# Get real tick data from data provider subscribers
for symbol in ['ETH/USDT', 'BTC/USDT']:
try:
# Get latest price data
latest_data = self.data_provider.get_historical_data(symbol, '1m', limit=1)
if latest_data is not None and len(latest_data) > 0:
latest_price = latest_data['close'].iloc[-1]
# Create tick data
# Get recent ticks from data provider
recent_ticks = self.data_provider.get_recent_ticks(symbol, count=10)
for tick in recent_ticks:
# Create tick data from real market data
tick_data = {
'symbol': symbol,
'price': latest_price,
'timestamp': datetime.now(),
'volume': latest_data['volume'].iloc[-1] if 'volume' in latest_data.columns else 1000
'symbol': tick.symbol,
'price': tick.price,
'timestamp': tick.timestamp,
'volume': tick.volume
}
# Add to tick cache
self.tick_cache.append(tick_data)
# Create 1s bar data
# Create 1s bar data from real tick
bar_data = {
'symbol': symbol,
'open': latest_price,
'high': latest_price * 1.001,
'low': latest_price * 0.999,
'close': latest_price,
'volume': tick_data['volume'],
'timestamp': datetime.now()
'symbol': tick.symbol,
'open': tick.price,
'high': tick.price,
'low': tick.price,
'close': tick.price,
'volume': tick.volume,
'timestamp': tick.timestamp
}
# Add to 1s bars cache
self.one_second_bars.append(bar_data)
except Exception as e:
logger.error(f"Error collecting tick data for {symbol}: {e}")
logger.error(f"Error collecting real tick data for {symbol}: {e}")
# Set streaming status
# Set streaming status based on real data availability
self.is_streaming = len(self.tick_cache) > 0
except Exception as e:
logger.error(f"Error in tick data collection: {e}")
logger.error(f"Error in real tick data collection: {e}")
def _send_training_data_to_models(self):
"""Send training data to models for actual training"""
@ -2604,9 +2574,9 @@ class RealTimeScalpingDashboard:
except Exception as e:
logger.error(f"Error sending training data to models: {e}")
def create_scalping_dashboard(data_provider=None, orchestrator=None):
"""Create real-time dashboard instance"""
return RealTimeScalpingDashboard(data_provider, orchestrator)
def create_scalping_dashboard(data_provider=None, orchestrator=None, trading_executor=None):
"""Create real-time dashboard instance with MEXC integration"""
return RealTimeScalpingDashboard(data_provider, orchestrator, trading_executor)
# For backward compatibility
ScalpingDashboard = RealTimeScalpingDashboard