Compare commits
2 Commits
ec420c2a5f
...
0c4dc8269c
Author | SHA1 | Date | |
---|---|---|---|
0c4dc8269c | |||
77a96030ba |
@ -1,9 +1,7 @@
|
||||
"""
|
||||
Central Configuration Management
|
||||
|
||||
This module handles all configuration for the trading system.
|
||||
It loads settings from config.yaml and provides easy access to all components.
|
||||
"""
|
||||
|
||||
import os
|
||||
import yaml
|
||||
@ -236,6 +234,15 @@ def get_config(config_path: str = "config.yaml") -> Config:
|
||||
_config_instance = Config(config_path)
|
||||
return _config_instance
|
||||
|
||||
def load_config(config_path: str = "config.yaml") -> Dict[str, Any]:
|
||||
"""Load configuration from YAML file"""
|
||||
try:
|
||||
config = get_config(config_path)
|
||||
return config._config
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading configuration: {e}")
|
||||
return {}
|
||||
|
||||
def setup_logging(config: Optional[Config] = None):
|
||||
"""Setup logging based on configuration"""
|
||||
if config is None:
|
||||
@ -257,4 +264,13 @@ def setup_logging(config: Optional[Config] = None):
|
||||
]
|
||||
)
|
||||
|
||||
logger.info("Logging configured successfully")
|
||||
logger.info("Logging configured successfully")
|
||||
|
||||
def load_config(config_path: str = "config.yaml") -> Dict[str, Any]:
|
||||
"""Load configuration from YAML file"""
|
||||
try:
|
||||
config = get_config(config_path)
|
||||
return config._config
|
||||
except Exception as e:
|
||||
logger.error(f"Error loading configuration: {e}")
|
||||
return {}
|
||||
|
513
run_integrated_rl_cob_dashboard.py
Normal file
513
run_integrated_rl_cob_dashboard.py
Normal file
@ -0,0 +1,513 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Integrated Real-time RL COB Trading System with Dashboard
|
||||
|
||||
This script starts both:
|
||||
1. RealtimeRLCOBTrader - 1B parameter RL model with real-time training
|
||||
2. COB Dashboard - Real-time visualization with RL predictions
|
||||
|
||||
The RL predictions are integrated into the dashboard for live visualization.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import signal
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import Dict, Any
|
||||
from aiohttp import web
|
||||
|
||||
# Local imports
|
||||
from core.realtime_rl_cob_trader import RealtimeRLCOBTrader, PredictionResult
|
||||
from core.trading_executor import TradingExecutor
|
||||
from core.config import load_config
|
||||
from web.cob_realtime_dashboard import COBDashboardServer
|
||||
|
||||
# Configure logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.FileHandler('logs/integrated_rl_cob_system.log'),
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class IntegratedRLCOBSystem:
|
||||
"""
|
||||
Integrated Real-time RL COB Trading System with Dashboard
|
||||
"""
|
||||
|
||||
def __init__(self, config_path: str = "config.yaml"):
|
||||
"""Initialize integrated system with configuration"""
|
||||
self.config = load_config(config_path)
|
||||
self.trader = None
|
||||
self.dashboard = None
|
||||
self.trading_executor = None
|
||||
self.running = False
|
||||
|
||||
# RL prediction storage for dashboard
|
||||
self.rl_predictions: Dict[str, list] = {}
|
||||
self.prediction_history: Dict[str, list] = {}
|
||||
|
||||
# Setup signal handlers for graceful shutdown
|
||||
signal.signal(signal.SIGINT, self._signal_handler)
|
||||
signal.signal(signal.SIGTERM, self._signal_handler)
|
||||
|
||||
logger.info("IntegratedRLCOBSystem initialized")
|
||||
|
||||
def _signal_handler(self, signum, frame):
|
||||
"""Handle shutdown signals"""
|
||||
logger.info(f"Received signal {signum}, initiating graceful shutdown...")
|
||||
self.running = False
|
||||
|
||||
async def start(self):
|
||||
"""Start the integrated RL COB trading system with dashboard"""
|
||||
try:
|
||||
logger.info("=" * 60)
|
||||
logger.info("INTEGRATED RL COB SYSTEM STARTING")
|
||||
logger.info("🔥 Real-time RL Trading + Dashboard")
|
||||
logger.info("=" * 60)
|
||||
|
||||
# Initialize trading executor
|
||||
await self._initialize_trading_executor()
|
||||
|
||||
# Initialize RL trader with prediction callback
|
||||
await self._initialize_rl_trader()
|
||||
|
||||
# Initialize dashboard with RL integration
|
||||
await self._initialize_dashboard()
|
||||
|
||||
# Start the integrated system
|
||||
await self._start_integrated_system()
|
||||
|
||||
# Run main loop
|
||||
await self._run_main_loop()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Critical error in integrated system: {e}")
|
||||
raise
|
||||
finally:
|
||||
await self.stop()
|
||||
|
||||
async def _initialize_trading_executor(self):
|
||||
"""Initialize the trading executor"""
|
||||
logger.info("Initializing Trading Executor...")
|
||||
|
||||
# Get trading configuration
|
||||
trading_config = self.config.get('trading', {})
|
||||
mexc_config = self.config.get('mexc', {})
|
||||
|
||||
# Determine if we should run in simulation mode
|
||||
simulation_mode = mexc_config.get('simulation_mode', True)
|
||||
|
||||
if simulation_mode:
|
||||
logger.info("Running in SIMULATION mode - no real trades will be executed")
|
||||
else:
|
||||
logger.warning("Running in LIVE TRADING mode - real money at risk!")
|
||||
|
||||
# Add safety confirmation for live trading
|
||||
confirmation = input("Type 'CONFIRM_LIVE_TRADING' to proceed with live trading: ")
|
||||
if confirmation != 'CONFIRM_LIVE_TRADING':
|
||||
logger.info("Live trading not confirmed, switching to simulation mode")
|
||||
simulation_mode = True
|
||||
|
||||
# Initialize trading executor
|
||||
self.trading_executor = TradingExecutor(
|
||||
simulation_mode=simulation_mode,
|
||||
mexc_config=mexc_config
|
||||
)
|
||||
|
||||
logger.info(f"Trading Executor initialized in {'SIMULATION' if simulation_mode else 'LIVE'} mode")
|
||||
|
||||
async def _initialize_rl_trader(self):
|
||||
"""Initialize the RL trader with prediction callbacks"""
|
||||
logger.info("Initializing Real-time RL COB Trader...")
|
||||
|
||||
# Get RL configuration
|
||||
rl_config = self.config.get('realtime_rl', {})
|
||||
|
||||
# Trading symbols
|
||||
symbols = rl_config.get('symbols', ['BTC/USDT', 'ETH/USDT'])
|
||||
|
||||
# Initialize prediction storage
|
||||
for symbol in symbols:
|
||||
self.rl_predictions[symbol] = []
|
||||
self.prediction_history[symbol] = []
|
||||
|
||||
# RL parameters
|
||||
inference_interval_ms = rl_config.get('inference_interval_ms', 200)
|
||||
min_confidence_threshold = rl_config.get('min_confidence_threshold', 0.7)
|
||||
required_confident_predictions = rl_config.get('required_confident_predictions', 3)
|
||||
model_checkpoint_dir = rl_config.get('model_checkpoint_dir', 'models/realtime_rl_cob')
|
||||
|
||||
# Initialize RL trader
|
||||
self.trader = RealtimeRLCOBTrader(
|
||||
symbols=symbols,
|
||||
trading_executor=self.trading_executor,
|
||||
model_checkpoint_dir=model_checkpoint_dir,
|
||||
inference_interval_ms=inference_interval_ms,
|
||||
min_confidence_threshold=min_confidence_threshold,
|
||||
required_confident_predictions=required_confident_predictions
|
||||
)
|
||||
|
||||
# Monkey-patch the trader to capture predictions
|
||||
original_add_signal = self.trader._add_signal
|
||||
def enhanced_add_signal(symbol: str, prediction: PredictionResult):
|
||||
# Call original method
|
||||
original_add_signal(symbol, prediction)
|
||||
# Capture prediction for dashboard
|
||||
self._on_rl_prediction(symbol, prediction)
|
||||
|
||||
self.trader._add_signal = enhanced_add_signal
|
||||
|
||||
logger.info(f"RL Trader initialized for symbols: {symbols}")
|
||||
logger.info(f"Inference interval: {inference_interval_ms}ms")
|
||||
logger.info(f"Confidence threshold: {min_confidence_threshold}")
|
||||
logger.info(f"Required predictions: {required_confident_predictions}")
|
||||
|
||||
def _on_rl_prediction(self, symbol: str, prediction: PredictionResult):
|
||||
"""Handle RL predictions for dashboard integration"""
|
||||
try:
|
||||
# Convert prediction to dashboard format
|
||||
prediction_data = {
|
||||
'timestamp': prediction.timestamp.isoformat(),
|
||||
'direction': prediction.predicted_direction, # 0=DOWN, 1=SIDEWAYS, 2=UP
|
||||
'confidence': prediction.confidence,
|
||||
'predicted_change': prediction.predicted_change,
|
||||
'direction_text': ['DOWN', 'SIDEWAYS', 'UP'][prediction.predicted_direction],
|
||||
'color': ['red', 'gray', 'green'][prediction.predicted_direction]
|
||||
}
|
||||
|
||||
# Add to current predictions (for live display)
|
||||
self.rl_predictions[symbol].append(prediction_data)
|
||||
if len(self.rl_predictions[symbol]) > 100: # Keep last 100
|
||||
self.rl_predictions[symbol] = self.rl_predictions[symbol][-100:]
|
||||
|
||||
# Add to history (for chart overlay)
|
||||
self.prediction_history[symbol].append(prediction_data)
|
||||
if len(self.prediction_history[symbol]) > 1000: # Keep last 1000
|
||||
self.prediction_history[symbol] = self.prediction_history[symbol][-1000:]
|
||||
|
||||
logger.debug(f"Captured RL prediction for {symbol}: {prediction.predicted_direction} "
|
||||
f"(confidence: {prediction.confidence:.3f})")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error capturing RL prediction: {e}")
|
||||
|
||||
async def _initialize_dashboard(self):
|
||||
"""Initialize the COB dashboard with RL integration"""
|
||||
logger.info("Initializing COB Dashboard with RL Integration...")
|
||||
|
||||
# Get dashboard configuration
|
||||
dashboard_config = self.config.get('dashboard', {})
|
||||
host = dashboard_config.get('host', 'localhost')
|
||||
port = dashboard_config.get('port', 8053)
|
||||
|
||||
# Create enhanced dashboard server
|
||||
self.dashboard = EnhancedCOBDashboardServer(
|
||||
host=host,
|
||||
port=port,
|
||||
rl_system=self # Pass reference to get predictions
|
||||
)
|
||||
|
||||
logger.info(f"COB Dashboard initialized at http://{host}:{port}")
|
||||
|
||||
async def _start_integrated_system(self):
|
||||
"""Start the complete integrated system"""
|
||||
logger.info("Starting Integrated RL COB System...")
|
||||
|
||||
# Start RL trader first (this initializes COB integration)
|
||||
await self.trader.start()
|
||||
logger.info("✅ RL Trader started")
|
||||
|
||||
# Start dashboard (uses same COB integration)
|
||||
await self.dashboard.start()
|
||||
logger.info("✅ COB Dashboard started")
|
||||
|
||||
self.running = True
|
||||
|
||||
logger.info("🎉 INTEGRATED SYSTEM FULLY OPERATIONAL!")
|
||||
logger.info("🔥 1B parameter RL model: ACTIVE")
|
||||
logger.info("📊 Real-time COB data: STREAMING")
|
||||
logger.info("🎯 Signal accumulation: ACTIVE")
|
||||
logger.info("💹 Live predictions: VISIBLE IN DASHBOARD")
|
||||
logger.info("⚡ Continuous training: ACTIVE")
|
||||
logger.info(f"🌐 Dashboard URL: http://{self.dashboard.host}:{self.dashboard.port}")
|
||||
|
||||
async def _run_main_loop(self):
|
||||
"""Main monitoring and statistics loop"""
|
||||
logger.info("Starting integrated system monitoring...")
|
||||
|
||||
last_stats_time = datetime.now()
|
||||
stats_interval = 60 # Print stats every 60 seconds
|
||||
|
||||
while self.running:
|
||||
try:
|
||||
# Sleep for a bit
|
||||
await asyncio.sleep(10)
|
||||
|
||||
# Print periodic statistics
|
||||
current_time = datetime.now()
|
||||
if (current_time - last_stats_time).total_seconds() >= stats_interval:
|
||||
await self._print_integrated_stats()
|
||||
last_stats_time = current_time
|
||||
|
||||
except asyncio.CancelledError:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"Error in main loop: {e}")
|
||||
await asyncio.sleep(5)
|
||||
|
||||
logger.info("Integrated system monitoring stopped")
|
||||
|
||||
async def _print_integrated_stats(self):
|
||||
"""Print comprehensive integrated system statistics"""
|
||||
try:
|
||||
logger.info("=" * 80)
|
||||
logger.info("🔥 INTEGRATED RL COB SYSTEM STATISTICS")
|
||||
logger.info("=" * 80)
|
||||
|
||||
# RL Trader Statistics
|
||||
if self.trader:
|
||||
rl_stats = self.trader.get_performance_stats()
|
||||
logger.info("\n🤖 RL TRADER PERFORMANCE:")
|
||||
|
||||
for symbol in self.trader.symbols:
|
||||
training_stats = rl_stats.get('training_stats', {}).get(symbol, {})
|
||||
inference_stats = rl_stats.get('inference_stats', {}).get(symbol, {})
|
||||
signal_stats = rl_stats.get('signal_stats', {}).get(symbol, {})
|
||||
|
||||
logger.info(f"\n 📈 {symbol}:")
|
||||
logger.info(f" Predictions: {training_stats.get('total_predictions', 0)}")
|
||||
logger.info(f" Success Rate: {signal_stats.get('success_rate', 0):.1%}")
|
||||
logger.info(f" Avg Inference: {inference_stats.get('average_inference_time_ms', 0):.1f}ms")
|
||||
logger.info(f" Current Signals: {signal_stats.get('current_signals', 0)}")
|
||||
|
||||
# RL prediction stats for dashboard
|
||||
recent_predictions = len(self.rl_predictions.get(symbol, []))
|
||||
total_predictions = len(self.prediction_history.get(symbol, []))
|
||||
logger.info(f" Dashboard Predictions: {recent_predictions} recent, {total_predictions} total")
|
||||
|
||||
# Dashboard Statistics
|
||||
if self.dashboard:
|
||||
logger.info(f"\n🌐 DASHBOARD STATISTICS:")
|
||||
logger.info(f" Active Connections: {len(self.dashboard.websocket_connections)}")
|
||||
logger.info(f" Server Status: {'RUNNING' if self.dashboard.site else 'STOPPED'}")
|
||||
logger.info(f" URL: http://{self.dashboard.host}:{self.dashboard.port}")
|
||||
|
||||
# Trading Executor Statistics
|
||||
if self.trading_executor:
|
||||
positions = self.trading_executor.get_positions()
|
||||
trade_history = self.trading_executor.get_trade_history()
|
||||
|
||||
logger.info(f"\n💰 TRADING STATISTICS:")
|
||||
logger.info(f" Active Positions: {len(positions)}")
|
||||
logger.info(f" Total Trades: {len(trade_history)}")
|
||||
|
||||
if trade_history:
|
||||
total_pnl = sum(trade.pnl for trade in trade_history)
|
||||
profitable_trades = sum(1 for trade in trade_history if trade.pnl > 0)
|
||||
win_rate = (profitable_trades / len(trade_history)) * 100
|
||||
|
||||
logger.info(f" Total P&L: ${total_pnl:.2f}")
|
||||
logger.info(f" Win Rate: {win_rate:.1f}%")
|
||||
|
||||
logger.info("=" * 80)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error printing integrated stats: {e}")
|
||||
|
||||
async def stop(self):
|
||||
"""Stop the integrated system gracefully"""
|
||||
if not self.running:
|
||||
return
|
||||
|
||||
logger.info("Stopping Integrated RL COB System...")
|
||||
|
||||
self.running = False
|
||||
|
||||
# Stop dashboard
|
||||
if self.dashboard:
|
||||
await self.dashboard.stop()
|
||||
logger.info("✅ Dashboard stopped")
|
||||
|
||||
# Stop RL trader
|
||||
if self.trader:
|
||||
await self.trader.stop()
|
||||
logger.info("✅ RL Trader stopped")
|
||||
|
||||
logger.info("🏁 Integrated system stopped successfully")
|
||||
|
||||
def get_rl_predictions(self, symbol: str) -> Dict[str, Any]:
|
||||
"""Get RL predictions for dashboard display"""
|
||||
return {
|
||||
'recent_predictions': self.rl_predictions.get(symbol, []),
|
||||
'prediction_history': self.prediction_history.get(symbol, []),
|
||||
'total_predictions': len(self.prediction_history.get(symbol, [])),
|
||||
'recent_count': len(self.rl_predictions.get(symbol, []))
|
||||
}
|
||||
|
||||
class EnhancedCOBDashboardServer(COBDashboardServer):
|
||||
"""Enhanced COB Dashboard with RL prediction integration"""
|
||||
|
||||
def __init__(self, host: str = 'localhost', port: int = 8053, rl_system: IntegratedRLCOBSystem = None):
|
||||
super().__init__(host, port)
|
||||
self.rl_system = rl_system
|
||||
|
||||
# Add RL prediction routes
|
||||
self._setup_rl_routes()
|
||||
|
||||
logger.info("Enhanced COB Dashboard with RL predictions initialized")
|
||||
|
||||
async def serve_dashboard(self, request):
|
||||
"""Serve the enhanced dashboard HTML with RL predictions"""
|
||||
try:
|
||||
# Read the enhanced dashboard HTML
|
||||
dashboard_path = os.path.join(os.path.dirname(__file__), 'enhanced_cob_dashboard.html')
|
||||
|
||||
if os.path.exists(dashboard_path):
|
||||
with open(dashboard_path, 'r', encoding='utf-8') as f:
|
||||
html_content = f.read()
|
||||
return web.Response(text=html_content, content_type='text/html')
|
||||
else:
|
||||
# Fallback to basic dashboard
|
||||
logger.warning("Enhanced dashboard HTML not found, using basic dashboard")
|
||||
return await super().serve_dashboard(request)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error serving enhanced dashboard: {e}")
|
||||
return web.Response(text="Dashboard error", status=500)
|
||||
|
||||
def _setup_rl_routes(self):
|
||||
"""Setup additional routes for RL predictions"""
|
||||
self.app.router.add_get('/api/rl-predictions/{symbol}', self.get_rl_predictions)
|
||||
self.app.router.add_get('/api/rl-status', self.get_rl_status)
|
||||
|
||||
async def get_rl_predictions(self, request):
|
||||
"""Get RL predictions for a symbol"""
|
||||
try:
|
||||
symbol = request.match_info['symbol']
|
||||
symbol = symbol.replace('%2F', '/')
|
||||
|
||||
if symbol not in self.symbols:
|
||||
return web.json_response({
|
||||
'error': f'Symbol {symbol} not supported'
|
||||
}, status=400)
|
||||
|
||||
if not self.rl_system:
|
||||
return web.json_response({
|
||||
'error': 'RL system not available'
|
||||
}, status=503)
|
||||
|
||||
predictions = self.rl_system.get_rl_predictions(symbol)
|
||||
|
||||
return web.json_response({
|
||||
'symbol': symbol,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'predictions': predictions
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting RL predictions: {e}")
|
||||
return web.json_response({
|
||||
'error': str(e)
|
||||
}, status=500)
|
||||
|
||||
async def get_rl_status(self, request):
|
||||
"""Get RL system status"""
|
||||
try:
|
||||
if not self.rl_system or not self.rl_system.trader:
|
||||
return web.json_response({
|
||||
'status': 'inactive',
|
||||
'error': 'RL system not available'
|
||||
})
|
||||
|
||||
rl_stats = self.rl_system.trader.get_performance_stats()
|
||||
|
||||
status = {
|
||||
'status': 'active',
|
||||
'symbols': self.rl_system.trader.symbols,
|
||||
'model_info': rl_stats.get('model_info', {}),
|
||||
'inference_interval_ms': self.rl_system.trader.inference_interval_ms,
|
||||
'confidence_threshold': self.rl_system.trader.min_confidence_threshold,
|
||||
'required_predictions': self.rl_system.trader.required_confident_predictions,
|
||||
'device': str(self.rl_system.trader.device),
|
||||
'running': self.rl_system.trader.running
|
||||
}
|
||||
|
||||
return web.json_response(status)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting RL status: {e}")
|
||||
return web.json_response({
|
||||
'status': 'error',
|
||||
'error': str(e)
|
||||
}, status=500)
|
||||
|
||||
async def _broadcast_cob_update(self, symbol: str, data: Dict):
|
||||
"""Enhanced COB update broadcast with RL predictions"""
|
||||
try:
|
||||
# Get RL predictions if available
|
||||
rl_data = {}
|
||||
if self.rl_system:
|
||||
rl_predictions = self.rl_system.get_rl_predictions(symbol)
|
||||
rl_data = {
|
||||
'rl_predictions': rl_predictions.get('recent_predictions', [])[-10:], # Last 10
|
||||
'prediction_count': rl_predictions.get('total_predictions', 0)
|
||||
}
|
||||
|
||||
# Enhanced data with RL predictions
|
||||
enhanced_data = {
|
||||
**data,
|
||||
'rl_data': rl_data
|
||||
}
|
||||
|
||||
# Broadcast to all WebSocket connections
|
||||
message = json.dumps({
|
||||
'type': 'cob_update',
|
||||
'symbol': symbol,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'data': enhanced_data
|
||||
}, default=str)
|
||||
|
||||
# Send to all connected clients
|
||||
disconnected = []
|
||||
for ws in self.websocket_connections:
|
||||
try:
|
||||
await ws.send_str(message)
|
||||
except Exception as e:
|
||||
logger.debug(f"WebSocket send failed: {e}")
|
||||
disconnected.append(ws)
|
||||
|
||||
# Remove disconnected clients
|
||||
for ws in disconnected:
|
||||
self.websocket_connections.discard(ws)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error broadcasting enhanced COB update: {e}")
|
||||
|
||||
async def main():
|
||||
"""Main entry point for integrated RL COB system"""
|
||||
# Create logs directory
|
||||
os.makedirs('logs', exist_ok=True)
|
||||
|
||||
# Initialize and start integrated system
|
||||
system = IntegratedRLCOBSystem()
|
||||
|
||||
try:
|
||||
await system.start()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("Received keyboard interrupt")
|
||||
except Exception as e:
|
||||
logger.error(f"System error: {e}")
|
||||
raise
|
||||
finally:
|
||||
await system.stop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
418
web/enhanced_cob_dashboard.html
Normal file
418
web/enhanced_cob_dashboard.html
Normal file
@ -0,0 +1,418 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Enhanced RL COB Trading Dashboard</title>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns/dist/chartjs-adapter-date-fns.bundle.min.js"></script>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, #0c0c0c 0%, #1a1a1a 100%);
|
||||
color: #e0e0e0;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
.header {
|
||||
background: rgba(20, 20, 20, 0.9);
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
.title {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(45deg, #00d4ff, #00ff88);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.status-indicators {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.status-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #ff4444;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
.status-dot.active {
|
||||
background: #00ff88;
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
.main-container {
|
||||
display: grid;
|
||||
grid-template-columns: 2fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
.chart-section, .sidebar {
|
||||
background: rgba(20, 20, 20, 0.8);
|
||||
border-radius: 12px;
|
||||
padding: 20px;
|
||||
border: 1px solid #333;
|
||||
}
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
position: relative;
|
||||
}
|
||||
.predictions-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.prediction-item {
|
||||
background: rgba(40, 40, 40, 0.6);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
margin-bottom: 8px;
|
||||
border-left: 4px solid #666;
|
||||
}
|
||||
.prediction-item.up { border-left-color: #00ff88; }
|
||||
.prediction-item.down { border-left-color: #ff4444; }
|
||||
.prediction-item.sideways { border-left-color: #ffaa00; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="title">🔥 Enhanced RL COB Trading Dashboard</div>
|
||||
<div class="status-indicators">
|
||||
<div>
|
||||
<span class="status-dot" id="rl-status"></span>
|
||||
<span>RL Model</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="status-dot" id="cob-status"></span>
|
||||
<span>COB Data</span>
|
||||
</div>
|
||||
<div>
|
||||
<span class="status-dot" id="ws-status"></span>
|
||||
<span>WebSocket</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main-container">
|
||||
<div class="chart-section">
|
||||
<h3>📈 Price Chart with RL Predictions</h3>
|
||||
<div class="chart-container">
|
||||
<canvas id="priceChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar">
|
||||
<h3>🤖 RL Predictions</h3>
|
||||
<div class="predictions-list" id="predictions-list">
|
||||
<div>Loading predictions...</div>
|
||||
</div>
|
||||
|
||||
<h3 style="margin-top: 30px;">📊 COB Data</h3>
|
||||
<div id="cob-data">
|
||||
<div>Loading COB data...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let priceChart = null;
|
||||
let websocket = null;
|
||||
let currentSymbol = 'BTC/USDT';
|
||||
|
||||
// Initialize dashboard
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initializeChart();
|
||||
initializeWebSocket();
|
||||
loadInitialData();
|
||||
|
||||
// Update data every 2 seconds
|
||||
setInterval(loadData, 2000);
|
||||
});
|
||||
|
||||
function initializeChart() {
|
||||
const ctx = document.getElementById('priceChart').getContext('2d');
|
||||
|
||||
priceChart = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
label: 'Price',
|
||||
data: [],
|
||||
borderColor: '#00d4ff',
|
||||
backgroundColor: 'rgba(0, 212, 255, 0.1)',
|
||||
borderWidth: 2,
|
||||
fill: false,
|
||||
pointRadius: 0
|
||||
},
|
||||
{
|
||||
label: 'RL Predictions (UP)',
|
||||
data: [],
|
||||
borderColor: '#00ff88',
|
||||
backgroundColor: '#00ff88',
|
||||
pointRadius: 6,
|
||||
pointStyle: 'triangle',
|
||||
showLine: false
|
||||
},
|
||||
{
|
||||
label: 'RL Predictions (DOWN)',
|
||||
data: [],
|
||||
borderColor: '#ff4444',
|
||||
backgroundColor: '#ff4444',
|
||||
pointRadius: 6,
|
||||
pointStyle: 'triangle',
|
||||
rotation: 180,
|
||||
showLine: false
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
scales: {
|
||||
x: {
|
||||
type: 'time',
|
||||
time: {
|
||||
unit: 'minute'
|
||||
},
|
||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||
ticks: { color: '#aaa' }
|
||||
},
|
||||
y: {
|
||||
grid: { color: 'rgba(255, 255, 255, 0.1)' },
|
||||
ticks: { color: '#aaa' }
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
labels: { color: '#e0e0e0' }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeWebSocket() {
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${protocol}//${window.location.host}/ws`;
|
||||
|
||||
websocket = new WebSocket(wsUrl);
|
||||
|
||||
websocket.onopen = function() {
|
||||
updateStatusDot('ws-status', true);
|
||||
websocket.send(JSON.stringify({
|
||||
type: 'subscribe',
|
||||
symbol: currentSymbol
|
||||
}));
|
||||
};
|
||||
|
||||
websocket.onmessage = function(event) {
|
||||
const data = JSON.parse(event.data);
|
||||
if (data.type === 'cob_update') {
|
||||
handleCOBUpdate(data);
|
||||
}
|
||||
};
|
||||
|
||||
websocket.onerror = function() {
|
||||
updateStatusDot('ws-status', false);
|
||||
};
|
||||
|
||||
websocket.onclose = function() {
|
||||
updateStatusDot('ws-status', false);
|
||||
setTimeout(initializeWebSocket, 5000);
|
||||
};
|
||||
}
|
||||
|
||||
async function loadInitialData() {
|
||||
await loadRLStatus();
|
||||
await loadData();
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
await loadCOBData();
|
||||
await loadRLPredictions();
|
||||
}
|
||||
|
||||
async function loadRLStatus() {
|
||||
try {
|
||||
const response = await fetch('/api/rl-status');
|
||||
const data = await response.json();
|
||||
updateStatusDot('rl-status', data.status === 'active');
|
||||
} catch (error) {
|
||||
updateStatusDot('rl-status', false);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadCOBData() {
|
||||
try {
|
||||
const response = await fetch(`/api/cob-data/${encodeURIComponent(currentSymbol)}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.data) {
|
||||
updateCOBDisplay(data.data);
|
||||
updateStatusDot('cob-status', true);
|
||||
|
||||
// Add price point to chart
|
||||
if (data.data.stats && data.data.stats.mid_price) {
|
||||
addPricePoint(data.data.stats.mid_price);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
updateStatusDot('cob-status', false);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRLPredictions() {
|
||||
try {
|
||||
const response = await fetch(`/api/rl-predictions/${encodeURIComponent(currentSymbol)}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.predictions) {
|
||||
updatePredictionsDisplay(data.predictions);
|
||||
updatePredictionsChart(data.predictions);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading RL predictions:', error);
|
||||
}
|
||||
}
|
||||
|
||||
function handleCOBUpdate(data) {
|
||||
if (data.symbol === currentSymbol) {
|
||||
updateCOBDisplay(data.data);
|
||||
|
||||
if (data.data.stats && data.data.stats.mid_price) {
|
||||
addPricePoint(data.data.stats.mid_price);
|
||||
}
|
||||
|
||||
if (data.data.rl_data && data.data.rl_data.rl_predictions) {
|
||||
updatePredictionsFromWS(data.data.rl_data.rl_predictions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addPricePoint(price) {
|
||||
const now = new Date();
|
||||
priceChart.data.datasets[0].data.push({
|
||||
x: now,
|
||||
y: price
|
||||
});
|
||||
|
||||
// Keep last 300 points (5 minutes)
|
||||
if (priceChart.data.datasets[0].data.length > 300) {
|
||||
priceChart.data.datasets[0].data.shift();
|
||||
}
|
||||
|
||||
priceChart.update('none');
|
||||
}
|
||||
|
||||
function updateCOBDisplay(cobData) {
|
||||
const container = document.getElementById('cob-data');
|
||||
|
||||
if (!cobData.bids || !cobData.asks) {
|
||||
container.innerHTML = '<div>No COB data available</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '<div style="font-size: 0.9rem;">';
|
||||
|
||||
// Show top 3 asks
|
||||
cobData.asks.slice(0, 3).reverse().forEach(ask => {
|
||||
html += `<div style="color: #ff4444; margin: 2px 0;">Ask: $${ask.price.toFixed(2)} (${ask.size.toFixed(4)})</div>`;
|
||||
});
|
||||
|
||||
// Show spread
|
||||
if (cobData.stats && cobData.stats.mid_price) {
|
||||
html += `<div style="background: rgba(255,255,255,0.1); padding: 4px; margin: 4px 0; text-align: center;">Mid: $${cobData.stats.mid_price.toFixed(2)}</div>`;
|
||||
}
|
||||
|
||||
// Show top 3 bids
|
||||
cobData.bids.slice(0, 3).forEach(bid => {
|
||||
html += `<div style="color: #00ff88; margin: 2px 0;">Bid: $${bid.price.toFixed(2)} (${bid.size.toFixed(4)})</div>`;
|
||||
});
|
||||
|
||||
html += '</div>';
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function updatePredictionsDisplay(predictions) {
|
||||
const container = document.getElementById('predictions-list');
|
||||
|
||||
if (!predictions.recent_predictions || predictions.recent_predictions.length === 0) {
|
||||
container.innerHTML = '<div>No recent predictions</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
const recent = predictions.recent_predictions.slice(-10).reverse();
|
||||
|
||||
recent.forEach(pred => {
|
||||
const directionClass = pred.direction === 2 ? 'up' : (pred.direction === 0 ? 'down' : 'sideways');
|
||||
const directionText = ['DOWN', 'SIDEWAYS', 'UP'][pred.direction];
|
||||
const confidence = (pred.confidence * 100).toFixed(1);
|
||||
const time = new Date(pred.timestamp).toLocaleTimeString();
|
||||
|
||||
html += `
|
||||
<div class="prediction-item ${directionClass}">
|
||||
<div style="font-weight: bold;">${directionText}</div>
|
||||
<div style="font-size: 0.8rem; color: #aaa;">${time} - ${confidence}% confidence</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function updatePredictionsChart(predictions) {
|
||||
if (!predictions.prediction_history) return;
|
||||
|
||||
const upPredictions = [];
|
||||
const downPredictions = [];
|
||||
|
||||
predictions.prediction_history.slice(-50).forEach(pred => {
|
||||
const point = {
|
||||
x: new Date(pred.timestamp),
|
||||
y: pred.price || 0
|
||||
};
|
||||
|
||||
if (pred.direction === 2) upPredictions.push(point);
|
||||
else if (pred.direction === 0) downPredictions.push(point);
|
||||
});
|
||||
|
||||
priceChart.data.datasets[1].data = upPredictions;
|
||||
priceChart.data.datasets[2].data = downPredictions;
|
||||
priceChart.update('none');
|
||||
}
|
||||
|
||||
function updatePredictionsFromWS(predictions) {
|
||||
predictions.forEach(pred => {
|
||||
const point = {
|
||||
x: new Date(pred.timestamp),
|
||||
y: pred.price || priceChart.data.datasets[0].data[priceChart.data.datasets[0].data.length - 1]?.y || 0
|
||||
};
|
||||
|
||||
if (pred.direction === 2) {
|
||||
priceChart.data.datasets[1].data.push(point);
|
||||
} else if (pred.direction === 0) {
|
||||
priceChart.data.datasets[2].data.push(point);
|
||||
}
|
||||
});
|
||||
|
||||
priceChart.update('none');
|
||||
}
|
||||
|
||||
function updateStatusDot(elementId, isActive) {
|
||||
const dot = document.getElementById(elementId);
|
||||
if (dot) {
|
||||
dot.classList.toggle('active', isActive);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user