Compare commits
5 Commits
97f7f54c30
...
1d09b3778e
Author | SHA1 | Date | |
---|---|---|---|
1d09b3778e | |||
06fbbeb81e | |||
36d4c543c3 | |||
8a51ef8b8c | |||
165b3be21a |
File diff suppressed because it is too large
Load Diff
@ -23,7 +23,11 @@ import asyncio
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
import websockets
|
try:
|
||||||
|
import websockets
|
||||||
|
except ImportError:
|
||||||
|
# Fallback for environments where websockets is not available
|
||||||
|
websockets = None
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@ -106,7 +110,7 @@ class MultiExchangeCOBProvider:
|
|||||||
to create a consolidated view of market liquidity and pricing.
|
to create a consolidated view of market liquidity and pricing.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, symbols: List[str] = None, bucket_size_bps: float = 1.0):
|
def __init__(self, symbols: Optional[List[str]] = None, bucket_size_bps: float = 1.0):
|
||||||
"""
|
"""
|
||||||
Initialize Multi-Exchange COB Provider
|
Initialize Multi-Exchange COB Provider
|
||||||
|
|
||||||
@ -461,6 +465,8 @@ class MultiExchangeCOBProvider:
|
|||||||
ws_url = f"{config.websocket_url}{config.symbols_mapping[symbol].lower()}@depth20@100ms"
|
ws_url = f"{config.websocket_url}{config.symbols_mapping[symbol].lower()}@depth20@100ms"
|
||||||
logger.info(f"Connecting to Binance WebSocket: {ws_url}")
|
logger.info(f"Connecting to Binance WebSocket: {ws_url}")
|
||||||
|
|
||||||
|
if websockets is None:
|
||||||
|
raise ImportError("websockets module not available")
|
||||||
async with websockets.connect(ws_url) as websocket:
|
async with websockets.connect(ws_url) as websocket:
|
||||||
self.exchange_order_books[symbol]['binance']['connected'] = True
|
self.exchange_order_books[symbol]['binance']['connected'] = True
|
||||||
logger.info(f"Connected to Binance order book stream for {symbol}")
|
logger.info(f"Connected to Binance order book stream for {symbol}")
|
||||||
@ -537,7 +543,7 @@ class MultiExchangeCOBProvider:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error adding trade to SVP: {e}")
|
logger.error(f"Error adding trade to SVP: {e}")
|
||||||
|
|
||||||
def get_session_volume_profile(self, symbol: str, bucket_size: float = None) -> Dict:
|
def get_session_volume_profile(self, symbol: str, bucket_size: Optional[float] = None) -> Dict:
|
||||||
"""Get session volume profile for a symbol"""
|
"""Get session volume profile for a symbol"""
|
||||||
try:
|
try:
|
||||||
if bucket_size is None:
|
if bucket_size is None:
|
||||||
@ -690,6 +696,8 @@ class MultiExchangeCOBProvider:
|
|||||||
ws_url = f"{config.websocket_url}{config.symbols_mapping[symbol].lower()}@trade"
|
ws_url = f"{config.websocket_url}{config.symbols_mapping[symbol].lower()}@trade"
|
||||||
logger.info(f"Connecting to Binance trade stream: {ws_url}")
|
logger.info(f"Connecting to Binance trade stream: {ws_url}")
|
||||||
|
|
||||||
|
if websockets is None:
|
||||||
|
raise ImportError("websockets module not available")
|
||||||
async with websockets.connect(ws_url) as websocket:
|
async with websockets.connect(ws_url) as websocket:
|
||||||
logger.info(f"Connected to Binance trade stream for {symbol}")
|
logger.info(f"Connected to Binance trade stream for {symbol}")
|
||||||
|
|
||||||
|
@ -301,6 +301,13 @@ class RealtimeRLCOBTrader:
|
|||||||
'last_inference_time': None
|
'last_inference_time': None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# PnL tracking for loss cutting optimization
|
||||||
|
self.pnl_history: Dict[str, deque] = {
|
||||||
|
symbol: deque(maxlen=1000) for symbol in self.symbols
|
||||||
|
}
|
||||||
|
self.position_peak_pnl: Dict[str, float] = {symbol: 0.0 for symbol in self.symbols}
|
||||||
|
self.trade_history: Dict[str, List] = {symbol: [] for symbol in self.symbols}
|
||||||
|
|
||||||
# Threading
|
# Threading
|
||||||
self.running = False
|
self.running = False
|
||||||
self.inference_lock = Lock()
|
self.inference_lock = Lock()
|
||||||
@ -961,8 +968,10 @@ class RealtimeRLCOBTrader:
|
|||||||
actual_direction: int,
|
actual_direction: int,
|
||||||
confidence: float,
|
confidence: float,
|
||||||
predicted_change: float,
|
predicted_change: float,
|
||||||
actual_change: float) -> float:
|
actual_change: float,
|
||||||
"""Calculate reward for a prediction"""
|
current_pnl: float = 0.0,
|
||||||
|
position_duration: float = 0.0) -> float:
|
||||||
|
"""Calculate reward for a prediction with PnL-aware loss cutting optimization"""
|
||||||
try:
|
try:
|
||||||
# Base reward for correct direction
|
# Base reward for correct direction
|
||||||
if predicted_direction == actual_direction:
|
if predicted_direction == actual_direction:
|
||||||
@ -983,7 +992,42 @@ class RealtimeRLCOBTrader:
|
|||||||
if base_reward < 0 and confidence > 0.8:
|
if base_reward < 0 and confidence > 0.8:
|
||||||
confidence_scaled_reward *= 1.5 # Increase penalty
|
confidence_scaled_reward *= 1.5 # Increase penalty
|
||||||
|
|
||||||
return float(confidence_scaled_reward)
|
# === PnL-AWARE LOSS CUTTING REWARDS ===
|
||||||
|
|
||||||
|
pnl_reward = 0.0
|
||||||
|
|
||||||
|
# Reward cutting losses early (SIDEWAYS when losing)
|
||||||
|
if current_pnl < -10.0: # In significant loss
|
||||||
|
if predicted_direction == 1: # SIDEWAYS (exit signal)
|
||||||
|
# Reward cutting losses before they get worse
|
||||||
|
loss_cutting_bonus = min(1.0, abs(current_pnl) / 100.0) * confidence
|
||||||
|
pnl_reward += loss_cutting_bonus
|
||||||
|
elif predicted_direction != 1: # Continuing to trade while in loss
|
||||||
|
# Penalty for not cutting losses
|
||||||
|
pnl_reward -= 0.5 * confidence
|
||||||
|
|
||||||
|
# Reward protecting profits (SIDEWAYS when in profit and market turning)
|
||||||
|
elif current_pnl > 10.0: # In profit
|
||||||
|
if predicted_direction == 1 and base_reward > 0: # Correct SIDEWAYS prediction
|
||||||
|
# Reward protecting profits from reversal
|
||||||
|
profit_protection_bonus = min(0.5, current_pnl / 200.0) * confidence
|
||||||
|
pnl_reward += profit_protection_bonus
|
||||||
|
|
||||||
|
# Duration penalty for holding losing positions
|
||||||
|
if current_pnl < 0 and position_duration > 3600: # Losing for > 1 hour
|
||||||
|
duration_penalty = min(1.0, position_duration / 7200.0) * 0.3 # Up to 30% penalty
|
||||||
|
confidence_scaled_reward -= duration_penalty
|
||||||
|
|
||||||
|
# Severe penalty for letting small losses become big losses
|
||||||
|
if current_pnl < -50.0: # Large loss
|
||||||
|
drawdown_penalty = min(2.0, abs(current_pnl) / 100.0) * confidence
|
||||||
|
confidence_scaled_reward -= drawdown_penalty
|
||||||
|
|
||||||
|
# Total reward
|
||||||
|
total_reward = confidence_scaled_reward + pnl_reward
|
||||||
|
|
||||||
|
# Clamp final reward
|
||||||
|
return max(-5.0, min(5.0, float(total_reward)))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error calculating reward: {e}")
|
logger.error(f"Error calculating reward: {e}")
|
||||||
|
@ -304,7 +304,7 @@ class RealTimeTickProcessor:
|
|||||||
|
|
||||||
if len(self.processing_times) % 100 == 0:
|
if len(self.processing_times) % 100 == 0:
|
||||||
avg_time = np.mean(list(self.processing_times))
|
avg_time = np.mean(list(self.processing_times))
|
||||||
logger.info(f"Average processing time: {avg_time:.2f}ms")
|
logger.debug(f"RTP: Average processing time: {avg_time:.2f}ms")
|
||||||
|
|
||||||
# Small sleep to prevent CPU overload
|
# Small sleep to prevent CPU overload
|
||||||
time.sleep(0.001) # 1ms sleep for ultra-low latency
|
time.sleep(0.001) # 1ms sleep for ultra-low latency
|
||||||
|
@ -121,7 +121,7 @@ class COBDashboardServer:
|
|||||||
|
|
||||||
# Initialize COB integration
|
# Initialize COB integration
|
||||||
self.cob_integration = COBIntegration(symbols=self.symbols)
|
self.cob_integration = COBIntegration(symbols=self.symbols)
|
||||||
self.cob_integration.add_dashboard_callback(self._on_cob_update)
|
self.cob_integration.add_dashboard_callback(self._sync_cob_update_wrapper)
|
||||||
|
|
||||||
# Start COB data streaming as background task
|
# Start COB data streaming as background task
|
||||||
asyncio.create_task(self.cob_integration.start())
|
asyncio.create_task(self.cob_integration.start())
|
||||||
@ -319,6 +319,14 @@ class COBDashboardServer:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error handling WebSocket message: {e}")
|
logger.error(f"Error handling WebSocket message: {e}")
|
||||||
|
|
||||||
|
def _sync_cob_update_wrapper(self, symbol: str, data: Dict):
|
||||||
|
"""Sync wrapper for async COB update handler"""
|
||||||
|
try:
|
||||||
|
# Create async task to handle the update
|
||||||
|
asyncio.create_task(self._on_cob_update(symbol, data))
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error in COB update wrapper for {symbol}: {e}")
|
||||||
|
|
||||||
async def _on_cob_update(self, symbol: str, data: Dict):
|
async def _on_cob_update(self, symbol: str, data: Dict):
|
||||||
"""Handle COB updates from integration"""
|
"""Handle COB updates from integration"""
|
||||||
try:
|
try:
|
||||||
@ -504,7 +512,7 @@ class COBDashboardServer:
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.websocket_connections.discard(ws)
|
self.websocket_connections.discard(ws)
|
||||||
|
|
||||||
await asyncio.sleep(5) # Update every 5 seconds
|
await asyncio.sleep(1) # Update every 1 second for real-time responsiveness
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error in periodic stats update: {e}")
|
logger.error(f"Error in periodic stats update: {e}")
|
||||||
|
405
web/dashboard.py
405
web/dashboard.py
@ -778,10 +778,10 @@ class TradingDashboard:
|
|||||||
className="text-light mb-0 opacity-75 small")
|
className="text-light mb-0 opacity-75 small")
|
||||||
], className="bg-dark p-2 mb-2"),
|
], className="bg-dark p-2 mb-2"),
|
||||||
|
|
||||||
# Auto-refresh component - optimized for efficient updates
|
# Auto-refresh component - ultra-fast updates for real-time trading
|
||||||
dcc.Interval(
|
dcc.Interval(
|
||||||
id='interval-component',
|
id='interval-component',
|
||||||
interval=10000, # Update every 10 seconds for efficiency
|
interval=1000, # Update every 1 second for maximum responsiveness
|
||||||
n_intervals=0
|
n_intervals=0
|
||||||
),
|
),
|
||||||
|
|
||||||
@ -841,21 +841,35 @@ class TradingDashboard:
|
|||||||
], className="card bg-light", style={"height": "60px"}),
|
], className="card bg-light", style={"height": "60px"}),
|
||||||
], style={"display": "grid", "gridTemplateColumns": "repeat(4, 1fr)", "gap": "8px", "width": "60%"}),
|
], style={"display": "grid", "gridTemplateColumns": "repeat(4, 1fr)", "gap": "8px", "width": "60%"}),
|
||||||
|
|
||||||
# Right side - Recent Signals & Executions
|
# Right side - Merged: Recent Signals & Model Training - 2 columns
|
||||||
html.Div([
|
html.Div([
|
||||||
|
# Recent Trading Signals Column (50%)
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H6([
|
html.Div([
|
||||||
html.I(className="fas fa-robot me-2"),
|
html.H6([
|
||||||
"Recent Trading Signals & Executions"
|
html.I(className="fas fa-robot me-2"),
|
||||||
], className="card-title mb-2"),
|
"Recent Trading Signals"
|
||||||
html.Div(id="recent-decisions", style={"height": "160px", "overflowY": "auto"})
|
], className="card-title mb-2"),
|
||||||
], className="card-body p-2")
|
html.Div(id="recent-decisions", style={"height": "160px", "overflowY": "auto"})
|
||||||
], className="card", style={"width": "48%", "marginLeft": "2%"})
|
], className="card-body p-2")
|
||||||
|
], className="card", style={"width": "48%"}),
|
||||||
|
|
||||||
|
# Model Training + COB Buckets Column (50%)
|
||||||
|
html.Div([
|
||||||
|
html.Div([
|
||||||
|
html.H6([
|
||||||
|
html.I(className="fas fa-brain me-2"),
|
||||||
|
"Training Progress & COB $1 Buckets"
|
||||||
|
], className="card-title mb-2"),
|
||||||
|
html.Div(id="training-metrics", style={"height": "160px", "overflowY": "auto"})
|
||||||
|
], className="card-body p-2")
|
||||||
|
], className="card", style={"width": "48%", "marginLeft": "4%"}),
|
||||||
|
], style={"width": "48%", "marginLeft": "2%", "display": "flex"})
|
||||||
], className="d-flex mb-3"),
|
], className="d-flex mb-3"),
|
||||||
|
|
||||||
# Charts row - More compact
|
# Charts row - Now full width since training moved up
|
||||||
html.Div([
|
html.Div([
|
||||||
# Price chart - 70% width
|
# Price chart - Full width
|
||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H6([
|
html.H6([
|
||||||
@ -864,45 +878,55 @@ class TradingDashboard:
|
|||||||
], className="card-title mb-2"),
|
], className="card-title mb-2"),
|
||||||
dcc.Graph(id="price-chart", style={"height": "400px"})
|
dcc.Graph(id="price-chart", style={"height": "400px"})
|
||||||
], className="card-body p-2")
|
], className="card-body p-2")
|
||||||
], className="card", style={"width": "70%"}),
|
], className="card", style={"width": "100%"}),
|
||||||
|
|
||||||
# Model Training Metrics - 30% width
|
|
||||||
html.Div([
|
|
||||||
html.Div([
|
|
||||||
html.H6([
|
|
||||||
html.I(className="fas fa-brain me-2"),
|
|
||||||
"Model Training Progress"
|
|
||||||
], className="card-title mb-2"),
|
|
||||||
html.Div(id="training-metrics", style={"height": "400px", "overflowY": "auto"})
|
|
||||||
], className="card-body p-2")
|
|
||||||
], className="card", style={"width": "28%", "marginLeft": "2%"}),
|
|
||||||
], className="row g-2 mb-3"),
|
], className="row g-2 mb-3"),
|
||||||
|
|
||||||
# CNN Model Monitoring Section
|
# CNN Model Monitoring & COB Integration - MERGED into 1 row with 4 columns
|
||||||
html.Div([
|
html.Div([
|
||||||
|
# CNN Status Column
|
||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H6([
|
html.H6([
|
||||||
html.I(className="fas fa-brain me-2"),
|
html.I(className="fas fa-brain me-2"),
|
||||||
"CNN Model Analysis & Predictions"
|
"CNN Model Analysis"
|
||||||
], className="card-title mb-2"),
|
], className="card-title mb-2"),
|
||||||
html.Div(id="cnn-monitoring-content", style={"height": "350px", "overflowY": "auto"})
|
html.Div(id="cnn-monitoring-content", style={"height": "280px", "overflowY": "auto"})
|
||||||
], className="card-body p-2")
|
], className="card-body p-2")
|
||||||
], className="card")
|
], className="card", style={"width": "23%"}),
|
||||||
], className="mb-3"),
|
|
||||||
|
# COB Status Column
|
||||||
# COB (Consolidated Order Book) Visualization Section
|
|
||||||
html.Div([
|
|
||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.H6([
|
html.H6([
|
||||||
html.I(className="fas fa-layer-group me-2"),
|
html.I(className="fas fa-layer-group me-2"),
|
||||||
"Consolidated Order Book (COB) - Real-time Market Microstructure"
|
"COB → Training Pipeline"
|
||||||
], className="card-title mb-2"),
|
], className="card-title mb-2"),
|
||||||
html.Div(id="cob-visualization-content", style={"height": "400px", "overflowY": "auto"})
|
html.Div(id="cob-status-content", style={"height": "280px", "overflowY": "auto"})
|
||||||
], className="card-body p-2")
|
], className="card-body p-2")
|
||||||
], className="card")
|
], className="card", style={"width": "23%", "marginLeft": "2%"}),
|
||||||
], className="mb-3"),
|
|
||||||
|
# ETH/USDT COB Details Column
|
||||||
|
html.Div([
|
||||||
|
html.Div([
|
||||||
|
html.H6([
|
||||||
|
html.I(className="fas fa-ethereum me-2", style={"color": "#627EEA"}),
|
||||||
|
"ETH/USDT - COB"
|
||||||
|
], className="card-title mb-2"),
|
||||||
|
html.Div(id="eth-cob-content", style={"height": "280px", "overflowY": "auto"})
|
||||||
|
], className="card-body p-2")
|
||||||
|
], className="card", style={"width": "23%", "marginLeft": "2%"}),
|
||||||
|
|
||||||
|
# BTC/USDT COB Details Column
|
||||||
|
html.Div([
|
||||||
|
html.Div([
|
||||||
|
html.H6([
|
||||||
|
html.I(className="fas fa-bitcoin me-2", style={"color": "#F7931A"}),
|
||||||
|
"BTC/USDT - COB"
|
||||||
|
], className="card-title mb-2"),
|
||||||
|
html.Div(id="btc-cob-content", style={"height": "280px", "overflowY": "auto"})
|
||||||
|
], className="card-body p-2")
|
||||||
|
], className="card", style={"width": "23%", "marginLeft": "2%"}),
|
||||||
|
], className="d-flex mb-3"),
|
||||||
|
|
||||||
# Bottom row - Session performance and system status
|
# Bottom row - Session performance and system status
|
||||||
html.Div([
|
html.Div([
|
||||||
@ -1026,7 +1050,9 @@ class TradingDashboard:
|
|||||||
Output('current-leverage', 'children'),
|
Output('current-leverage', 'children'),
|
||||||
Output('leverage-risk', 'children'),
|
Output('leverage-risk', 'children'),
|
||||||
Output('cnn-monitoring-content', 'children'),
|
Output('cnn-monitoring-content', 'children'),
|
||||||
Output('cob-visualization-content', 'children')
|
Output('cob-status-content', 'children'),
|
||||||
|
Output('eth-cob-content', 'children'),
|
||||||
|
Output('btc-cob-content', 'children')
|
||||||
],
|
],
|
||||||
[Input('interval-component', 'n_intervals')]
|
[Input('interval-component', 'n_intervals')]
|
||||||
)
|
)
|
||||||
@ -1035,9 +1061,9 @@ class TradingDashboard:
|
|||||||
update_start = time.time()
|
update_start = time.time()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Smart update scheduling - different frequencies for different components
|
# Smart update scheduling - optimized for 1s responsiveness
|
||||||
is_price_update = True # Price updates every interval (1s)
|
is_price_update = True # Price updates every interval (1s)
|
||||||
is_chart_update = n_intervals % 5 == 0 # Chart updates every 5s
|
is_chart_update = True # Chart updates every 1s for real-time feel
|
||||||
is_heavy_update = n_intervals % 10 == 0 # Heavy operations every 10s
|
is_heavy_update = n_intervals % 10 == 0 # Heavy operations every 10s
|
||||||
is_cleanup_update = n_intervals % 60 == 0 # Cleanup every 60s
|
is_cleanup_update = n_intervals % 60 == 0 # Cleanup every 60s
|
||||||
|
|
||||||
@ -1125,7 +1151,7 @@ class TradingDashboard:
|
|||||||
trade_count_text = f"{len(self.session_trades)}"
|
trade_count_text = f"{len(self.session_trades)}"
|
||||||
portfolio_text = f"${portfolio_value:,.2f}"
|
portfolio_text = f"${portfolio_value:,.2f}"
|
||||||
|
|
||||||
# OPTIMIZED POSITION INFO
|
# OPTIMIZED POSITION INFO with separate colors for position and P&L
|
||||||
if self.current_position:
|
if self.current_position:
|
||||||
pos_side = self.current_position['side']
|
pos_side = self.current_position['side']
|
||||||
pos_size = self.current_position['size']
|
pos_size = self.current_position['size']
|
||||||
@ -1133,9 +1159,18 @@ class TradingDashboard:
|
|||||||
|
|
||||||
side_icon = "[LONG]" if pos_side == 'LONG' else "[SHORT]"
|
side_icon = "[LONG]" if pos_side == 'LONG' else "[SHORT]"
|
||||||
side_color = "success" if pos_side == 'LONG' else "danger"
|
side_color = "success" if pos_side == 'LONG' else "danger"
|
||||||
|
pnl_color = "success" if unrealized_pnl > 0 else "danger"
|
||||||
pnl_sign = "+" if unrealized_pnl > 0 else ""
|
pnl_sign = "+" if unrealized_pnl > 0 else ""
|
||||||
position_text = f"{side_icon} {pos_size} @ ${pos_price:.2f} | P&L: {pnl_sign}${unrealized_pnl:.2f}"
|
|
||||||
position_class = f"text-{side_color} fw-bold mb-0 small"
|
# Create position text with separate colors for position type and P&L
|
||||||
|
from dash import html
|
||||||
|
position_text = [
|
||||||
|
html.Span(f"{side_icon} {pos_size} @ ${pos_price:.2f} | P&L: ",
|
||||||
|
className=f"text-{side_color}"),
|
||||||
|
html.Span(f"{pnl_sign}${unrealized_pnl:.2f}",
|
||||||
|
className=f"text-{pnl_color}")
|
||||||
|
]
|
||||||
|
position_class = "fw-bold mb-0 small"
|
||||||
else:
|
else:
|
||||||
position_text = "No Position"
|
position_text = "No Position"
|
||||||
position_class = "text-muted mb-0 small"
|
position_class = "text-muted mb-0 small"
|
||||||
@ -1143,12 +1178,12 @@ class TradingDashboard:
|
|||||||
# MEXC status (simple)
|
# MEXC status (simple)
|
||||||
mexc_status = "LIVE" if (self.trading_executor and self.trading_executor.trading_enabled and not self.trading_executor.simulation_mode) else "SIM"
|
mexc_status = "LIVE" if (self.trading_executor and self.trading_executor.trading_enabled and not self.trading_executor.simulation_mode) else "SIM"
|
||||||
|
|
||||||
# CHART OPTIMIZATION - Only update charts every 5 seconds
|
# CHART OPTIMIZATION - Real-time chart updates every 1 second
|
||||||
if is_chart_update:
|
if is_chart_update:
|
||||||
try:
|
try:
|
||||||
if hasattr(self, '_cached_chart_data_time'):
|
if hasattr(self, '_cached_chart_data_time'):
|
||||||
cache_time = self._cached_chart_data_time
|
cache_time = self._cached_chart_data_time
|
||||||
if time.time() - cache_time < 20: # Use cached chart if < 20s old
|
if time.time() - cache_time < 5: # Use cached chart if < 5s old for faster updates
|
||||||
price_chart = getattr(self, '_cached_price_chart', None)
|
price_chart = getattr(self, '_cached_price_chart', None)
|
||||||
else:
|
else:
|
||||||
price_chart = self._create_price_chart_optimized(symbol, current_price)
|
price_chart = self._create_price_chart_optimized(symbol, current_price)
|
||||||
@ -1163,7 +1198,7 @@ class TradingDashboard:
|
|||||||
price_chart = getattr(self, '_cached_price_chart',
|
price_chart = getattr(self, '_cached_price_chart',
|
||||||
self._create_empty_chart("Chart Error", "Chart temporarily unavailable"))
|
self._create_empty_chart("Chart Error", "Chart temporarily unavailable"))
|
||||||
else:
|
else:
|
||||||
# Use cached chart
|
# Use cached chart (should not happen since is_chart_update is always True now)
|
||||||
price_chart = getattr(self, '_cached_price_chart',
|
price_chart = getattr(self, '_cached_price_chart',
|
||||||
self._create_empty_chart("Loading", "Chart loading..."))
|
self._create_empty_chart("Loading", "Chart loading..."))
|
||||||
|
|
||||||
@ -1234,12 +1269,16 @@ class TradingDashboard:
|
|||||||
else:
|
else:
|
||||||
risk_level = "Extreme Risk"
|
risk_level = "Extreme Risk"
|
||||||
|
|
||||||
# Generate COB visualization content
|
# Generate COB 4-column content
|
||||||
try:
|
try:
|
||||||
cob_content = self._create_cob_visualization_content()
|
cob_status_content = self._create_cob_status_content()
|
||||||
|
eth_cob_content = self._create_symbol_cob_content('ETH/USDT')
|
||||||
|
btc_cob_content = self._create_symbol_cob_content('BTC/USDT')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"COB visualization error: {e}")
|
logger.warning(f"COB content error: {e}")
|
||||||
cob_content = [html.P("COB data loading...", className="text-muted")]
|
cob_status_content = [html.P("COB data loading...", className="text-muted")]
|
||||||
|
eth_cob_content = [html.P("ETH COB loading...", className="text-muted")]
|
||||||
|
btc_cob_content = [html.P("BTC COB loading...", className="text-muted")]
|
||||||
|
|
||||||
# BUILD FINAL RESULT
|
# BUILD FINAL RESULT
|
||||||
result = (
|
result = (
|
||||||
@ -1247,7 +1286,7 @@ class TradingDashboard:
|
|||||||
trade_count_text, portfolio_text, mexc_status, price_chart, training_metrics,
|
trade_count_text, portfolio_text, mexc_status, price_chart, training_metrics,
|
||||||
decisions_list, session_perf, closed_trades_table, system_status['icon_class'],
|
decisions_list, session_perf, closed_trades_table, system_status['icon_class'],
|
||||||
system_status['title'], system_status['details'], leverage_text, risk_level,
|
system_status['title'], system_status['details'], leverage_text, risk_level,
|
||||||
cnn_monitoring_content, cob_content
|
cnn_monitoring_content, cob_status_content, eth_cob_content, btc_cob_content
|
||||||
)
|
)
|
||||||
|
|
||||||
# Cache the result for emergencies
|
# Cache the result for emergencies
|
||||||
@ -6034,17 +6073,108 @@ class TradingDashboard:
|
|||||||
return self._create_empty_chart("Chart Error", "Chart temporarily unavailable")
|
return self._create_empty_chart("Chart Error", "Chart temporarily unavailable")
|
||||||
|
|
||||||
def _create_training_metrics_cached(self):
|
def _create_training_metrics_cached(self):
|
||||||
"""Cached training metrics with reduced computation"""
|
"""Enhanced training metrics with COB $1 buckets"""
|
||||||
try:
|
try:
|
||||||
return [
|
content = []
|
||||||
html.H6("Training Status", className="text-success"),
|
|
||||||
html.P(f"Models Active: {len(getattr(self.model_registry, 'models', {})) if self.model_registry else 0}",
|
# Training Status Section
|
||||||
className="text-muted small"),
|
content.append(html.H6("Training Status", className="text-success mb-2"))
|
||||||
html.P(f"Last Update: {datetime.now().strftime('%H:%M:%S')}",
|
content.append(html.P(f"Models Active: {len(getattr(self.model_registry, 'models', {})) if self.model_registry else 0}",
|
||||||
className="text-muted small")
|
className="text-muted small"))
|
||||||
]
|
content.append(html.P(f"Last Update: {datetime.now().strftime('%H:%M:%S')}",
|
||||||
except:
|
className="text-muted small"))
|
||||||
|
|
||||||
|
# COB $1 Buckets Section
|
||||||
|
content.append(html.Hr())
|
||||||
|
content.append(html.H6("COB $1 Buckets", className="text-info mb-2"))
|
||||||
|
|
||||||
|
# Get COB bucket data if available
|
||||||
|
try:
|
||||||
|
if hasattr(self.orchestrator, 'cob_integration') and self.orchestrator.cob_integration:
|
||||||
|
cob_buckets = self._get_cob_dollar_buckets()
|
||||||
|
if cob_buckets:
|
||||||
|
# Show top 5 buckets by volume
|
||||||
|
for i, bucket in enumerate(cob_buckets[:5]):
|
||||||
|
price_range = f"${bucket['price']:.0f}-${bucket['price']+1:.0f}"
|
||||||
|
volume = bucket['total_volume']
|
||||||
|
bid_pct = (bucket['bid_volume'] / volume * 100) if volume > 0 else 0
|
||||||
|
ask_pct = (bucket['ask_volume'] / volume * 100) if volume > 0 else 0
|
||||||
|
|
||||||
|
content.append(html.P([
|
||||||
|
html.Span(price_range, className="text-warning small fw-bold"),
|
||||||
|
html.Br(),
|
||||||
|
html.Span(f"Vol: ${volume:,.0f} ", className="text-muted small"),
|
||||||
|
html.Span(f"B:{bid_pct:.0f}% ", className="text-success small"),
|
||||||
|
html.Span(f"A:{ask_pct:.0f}%", className="text-danger small")
|
||||||
|
], className="mb-1"))
|
||||||
|
else:
|
||||||
|
content.append(html.P("COB buckets loading...", className="text-muted small"))
|
||||||
|
else:
|
||||||
|
content.append(html.P("COB integration inactive", className="text-warning small"))
|
||||||
|
except Exception as e:
|
||||||
|
content.append(html.P(f"COB error: {str(e)[:30]}...", className="text-danger small"))
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
return [html.P("Training metrics unavailable", className="text-muted")]
|
return [html.P("Training metrics unavailable", className="text-muted")]
|
||||||
|
|
||||||
|
def _get_cob_dollar_buckets(self) -> List[Dict]:
|
||||||
|
"""Get COB data grouped into $1 buckets"""
|
||||||
|
try:
|
||||||
|
buckets = []
|
||||||
|
|
||||||
|
# Get COB data for primary symbols
|
||||||
|
for symbol in ['ETH/USDT', 'BTC/USDT']:
|
||||||
|
if hasattr(self.orchestrator, 'cob_integration') and self.orchestrator.cob_integration:
|
||||||
|
try:
|
||||||
|
cob_snapshot = self.orchestrator.cob_integration.get_cob_snapshot(symbol)
|
||||||
|
if cob_snapshot:
|
||||||
|
mid_price = cob_snapshot.volume_weighted_mid
|
||||||
|
|
||||||
|
# Create $1 buckets around mid price (±$50 range)
|
||||||
|
price_buckets = {}
|
||||||
|
for i in range(-50, 51):
|
||||||
|
bucket_price = int(mid_price) + i
|
||||||
|
price_buckets[bucket_price] = {
|
||||||
|
'price': bucket_price,
|
||||||
|
'bid_volume': 0,
|
||||||
|
'ask_volume': 0,
|
||||||
|
'total_volume': 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Aggregate bid data into buckets
|
||||||
|
for level in cob_snapshot.consolidated_bids:
|
||||||
|
bucket_price = int(level.price)
|
||||||
|
if bucket_price in price_buckets:
|
||||||
|
price_buckets[bucket_price]['bid_volume'] += level.total_volume_usd
|
||||||
|
price_buckets[bucket_price]['total_volume'] += level.total_volume_usd
|
||||||
|
|
||||||
|
# Aggregate ask data into buckets
|
||||||
|
for level in cob_snapshot.consolidated_asks:
|
||||||
|
bucket_price = int(level.price)
|
||||||
|
if bucket_price in price_buckets:
|
||||||
|
price_buckets[bucket_price]['ask_volume'] += level.total_volume_usd
|
||||||
|
price_buckets[bucket_price]['total_volume'] += level.total_volume_usd
|
||||||
|
|
||||||
|
# Convert to list and sort by volume
|
||||||
|
symbol_buckets = [bucket for bucket in price_buckets.values() if bucket['total_volume'] > 0]
|
||||||
|
symbol_buckets.sort(key=lambda x: x['total_volume'], reverse=True)
|
||||||
|
|
||||||
|
# Add symbol info and take top buckets
|
||||||
|
for bucket in symbol_buckets[:10]:
|
||||||
|
bucket['symbol'] = symbol
|
||||||
|
buckets.append(bucket)
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Error getting COB buckets for {symbol}: {e}")
|
||||||
|
|
||||||
|
# Sort all buckets by total volume and return top 10
|
||||||
|
buckets.sort(key=lambda x: x['total_volume'], reverse=True)
|
||||||
|
return buckets[:10]
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating COB dollar buckets: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
def _create_decisions_list_cached(self):
|
def _create_decisions_list_cached(self):
|
||||||
"""Cached decisions list with limited entries"""
|
"""Cached decisions list with limited entries"""
|
||||||
@ -6140,6 +6270,163 @@ class TradingDashboard:
|
|||||||
except:
|
except:
|
||||||
return [html.P("CNN monitoring unavailable", className="text-muted")]
|
return [html.P("CNN monitoring unavailable", className="text-muted")]
|
||||||
|
|
||||||
|
def _create_cob_status_content(self) -> List:
|
||||||
|
"""Create COB status and training pipeline content"""
|
||||||
|
try:
|
||||||
|
content = []
|
||||||
|
|
||||||
|
# Check if we have enhanced orchestrator with COB integration
|
||||||
|
if not hasattr(self.orchestrator, 'latest_cob_features') or not hasattr(self.orchestrator, 'cob_integration'):
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-exclamation-triangle text-warning me-2"),
|
||||||
|
"COB integration not available"
|
||||||
|
], className="small"))
|
||||||
|
content.append(html.P("Using basic orchestrator", className="text-muted small"))
|
||||||
|
return content
|
||||||
|
|
||||||
|
# COB Integration Status
|
||||||
|
if self.orchestrator.cob_integration:
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-check-circle text-success me-2"),
|
||||||
|
"COB integration active"
|
||||||
|
], className="small"))
|
||||||
|
else:
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-exclamation-triangle text-warning me-2"),
|
||||||
|
"COB integration inactive"
|
||||||
|
], className="small"))
|
||||||
|
|
||||||
|
# Training Pipeline Status
|
||||||
|
if hasattr(self.orchestrator, 'enhanced_rl_training') and self.orchestrator.enhanced_rl_training:
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-brain text-info me-2"),
|
||||||
|
"COB → RL pipeline enabled"
|
||||||
|
], className="small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-arrow-right text-secondary me-1"),
|
||||||
|
"Real-time market microstructure"
|
||||||
|
], className="small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-arrow-right text-secondary me-1"),
|
||||||
|
"CNN features generation"
|
||||||
|
], className="small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-arrow-right text-secondary me-1"),
|
||||||
|
"RL state building"
|
||||||
|
], className="small"))
|
||||||
|
else:
|
||||||
|
content.append(html.P([
|
||||||
|
html.I(className="fas fa-times-circle text-danger me-2"),
|
||||||
|
"Training pipeline inactive"
|
||||||
|
], className="small"))
|
||||||
|
|
||||||
|
# Performance metrics
|
||||||
|
cob_update_count = 0
|
||||||
|
for symbol in ['ETH/USDT', 'BTC/USDT']:
|
||||||
|
if symbol in getattr(self.orchestrator, 'latest_cob_features', {}):
|
||||||
|
cob_update_count += 1
|
||||||
|
|
||||||
|
content.append(html.Hr())
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Active Symbols: "),
|
||||||
|
f"{cob_update_count}/2"
|
||||||
|
], className="text-info small"))
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating COB status content: {e}")
|
||||||
|
return [html.P(f"COB status error: {str(e)}", className="text-danger")]
|
||||||
|
|
||||||
|
def _create_symbol_cob_content(self, symbol: str) -> List:
|
||||||
|
"""Create COB content for a specific symbol"""
|
||||||
|
try:
|
||||||
|
content = []
|
||||||
|
|
||||||
|
# Check if we have enhanced orchestrator with COB integration
|
||||||
|
if not hasattr(self.orchestrator, 'latest_cob_features') or not hasattr(self.orchestrator, 'cob_integration'):
|
||||||
|
content.append(html.P("COB integration not available", className="text-warning small"))
|
||||||
|
return content
|
||||||
|
|
||||||
|
# Get COB features and state
|
||||||
|
cob_features = getattr(self.orchestrator, 'latest_cob_features', {}).get(symbol)
|
||||||
|
cob_state = getattr(self.orchestrator, 'latest_cob_state', {}).get(symbol)
|
||||||
|
|
||||||
|
# CNN Features Status
|
||||||
|
if cob_features is not None:
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("CNN Features: "),
|
||||||
|
html.Span("Available", className="text-success")
|
||||||
|
], className="small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Span(f"Shape: {cob_features.shape}", className="text-muted")
|
||||||
|
], className="small"))
|
||||||
|
else:
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("CNN Features: "),
|
||||||
|
html.Span("Not available", className="text-warning")
|
||||||
|
], className="small"))
|
||||||
|
|
||||||
|
# RL State Status
|
||||||
|
if cob_state is not None:
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("RL State: "),
|
||||||
|
html.Span("Available", className="text-success")
|
||||||
|
], className="small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Span(f"Shape: {cob_state.shape}", className="text-muted")
|
||||||
|
], className="small"))
|
||||||
|
else:
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("RL State: "),
|
||||||
|
html.Span("Not available", className="text-warning")
|
||||||
|
], className="small"))
|
||||||
|
|
||||||
|
# Get COB snapshot if integration is active
|
||||||
|
cob_snapshot = None
|
||||||
|
if hasattr(self.orchestrator, 'cob_integration') and self.orchestrator.cob_integration:
|
||||||
|
try:
|
||||||
|
cob_snapshot = self.orchestrator.cob_integration.get_cob_snapshot(symbol)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# COB Snapshot Details
|
||||||
|
if cob_snapshot:
|
||||||
|
content.append(html.Hr())
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Mid Price: "),
|
||||||
|
f"${cob_snapshot.volume_weighted_mid:.2f}"
|
||||||
|
], className="text-info small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Spread: "),
|
||||||
|
f"{cob_snapshot.spread_bps:.1f} bps"
|
||||||
|
], className="text-info small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Bid Liquidity: "),
|
||||||
|
f"${cob_snapshot.total_bid_liquidity:,.0f}"
|
||||||
|
], className="text-success small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Ask Liquidity: "),
|
||||||
|
f"${cob_snapshot.total_ask_liquidity:,.0f}"
|
||||||
|
], className="text-success small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Exchanges: "),
|
||||||
|
", ".join(cob_snapshot.exchanges_active)
|
||||||
|
], className="text-secondary small"))
|
||||||
|
content.append(html.P([
|
||||||
|
html.Strong("Levels: "),
|
||||||
|
f"{len(cob_snapshot.consolidated_bids)} bids, {len(cob_snapshot.consolidated_asks)} asks"
|
||||||
|
], className="text-secondary small"))
|
||||||
|
else:
|
||||||
|
content.append(html.Hr())
|
||||||
|
content.append(html.P("COB snapshot not available", className="text-muted small"))
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error creating COB content for {symbol}: {e}")
|
||||||
|
return [html.P(f"COB error: {str(e)}", className="text-danger")]
|
||||||
|
|
||||||
def _create_cob_visualization_content(self) -> List:
|
def _create_cob_visualization_content(self) -> List:
|
||||||
"""Create COB (Consolidated Order Book) visualization content"""
|
"""Create COB (Consolidated Order Book) visualization content"""
|
||||||
try:
|
try:
|
||||||
|
Reference in New Issue
Block a user