From 0fe8286787b52a290bfa73e82d811d6476db9e5f Mon Sep 17 00:00:00 2001 From: Dobromir Popov Date: Sat, 24 May 2025 09:58:36 +0300 Subject: [PATCH] misc --- .vscode/launch.json | 43 ++ TENSORBOARD_MONITORING.md | 332 +++++++++++++++ TRAINING_STATUS.md | 1 + main_clean.py | 86 ++-- monitor_training.py | 83 ++++ readme.md | 35 +- run_tensorboard.py | 115 ++--- test_cnn_only.py | 67 +-- train_realtime_with_tensorboard.py | 420 ++++++++++++++++++ training/cnn_trainer.py | 658 ++++++++++++++--------------- training/rl_trainer.py | 39 ++ 11 files changed, 1396 insertions(+), 483 deletions(-) create mode 100644 TENSORBOARD_MONITORING.md create mode 100644 TRAINING_STATUS.md create mode 100644 monitor_training.py create mode 100644 train_realtime_with_tensorboard.py diff --git a/.vscode/launch.json b/.vscode/launch.json index 2cec79b..f332d19 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -204,6 +204,49 @@ ], "console": "integratedTerminal", "justMyCode": true + }, + { + "name": "Realtime RL Training + TensorBoard + Web UI", + "type": "python", + "request": "launch", + "program": "train_realtime_with_tensorboard.py", + "args": [ + "--episodes", + "50", + "--symbol", + "ETH/USDT", + "--balance", + "1000.0", + "--web-port", + "8051" + ], + "console": "integratedTerminal", + "justMyCode": true, + "env": { + "PYTHONUNBUFFERED": "1", + "ENABLE_REAL_DATA_ONLY": "1" + }, + "preLaunchTask": "Kill Stale Processes" + }, + { + "name": "Quick CNN Test (Real Data + TensorBoard)", + "type": "python", + "request": "launch", + "program": "test_cnn_only.py", + "console": "integratedTerminal", + "justMyCode": true, + "env": { + "PYTHONUNBUFFERED": "1" + }, + "preLaunchTask": "Kill Stale Processes" + }, + { + "name": "TensorBoard Monitor (All Runs)", + "type": "python", + "request": "launch", + "program": "run_tensorboard.py", + "console": "integratedTerminal", + "justMyCode": true } ] } diff --git a/TENSORBOARD_MONITORING.md b/TENSORBOARD_MONITORING.md new file mode 100644 index 0000000..72ebc05 --- /dev/null +++ b/TENSORBOARD_MONITORING.md @@ -0,0 +1,332 @@ +# TensorBoard Monitoring Guide + +## Overview + +The trading system now uses **TensorBoard** for real-time training monitoring instead of static charts. This provides dynamic, interactive visualizations that update during training. + +## 🚨 CRITICAL: Real Market Data Only + +All TensorBoard metrics are derived from **REAL market data training**. No synthetic or generated data is used. + +## Quick Start + +### 1. Start Training with TensorBoard +```bash +# CNN Training with TensorBoard +python main_clean.py --mode cnn --symbol ETH/USDT + +# RL Training with TensorBoard +python train_rl_with_realtime.py --episodes 10 + +# Quick CNN Test +python test_cnn_only.py +``` + +### 2. Launch TensorBoard +```bash +# Option 1: Direct command +tensorboard --logdir=runs + +# Option 2: Convenience script +python run_tensorboard.py +``` + +### 3. Access TensorBoard +Open your browser to: **http://localhost:6006** + +## Available Metrics + +### CNN Training Metrics + +#### **Training Progress** +- `Training/EpochLoss` - Training loss per epoch +- `Training/EpochAccuracy` - Training accuracy per epoch +- `Training/BatchLoss` - Batch-level loss +- `Training/BatchAccuracy` - Batch-level accuracy +- `Training/BatchConfidence` - Model confidence scores +- `Training/LearningRate` - Learning rate schedule +- `Training/EpochTime` - Time per epoch + +#### **Validation Metrics** +- `Validation/Loss` - Validation loss +- `Validation/Accuracy` - Validation accuracy +- `Validation/AvgConfidence` - Average confidence on validation set +- `Validation/Class_0_Accuracy` - BUY class accuracy +- `Validation/Class_1_Accuracy` - SELL class accuracy +- `Validation/Class_2_Accuracy` - HOLD class accuracy + +#### **Best Model Tracking** +- `Best/ValidationLoss` - Best validation loss achieved +- `Best/ValidationAccuracy` - Best validation accuracy achieved + +#### **Data Statistics** +- `Data/TotalSamples` - Number of training samples from real data +- `Data/Features` - Number of features (detected from real data) +- `Data/Timeframes` - Number of timeframes used +- `Data/WindowSize` - Window size for temporal patterns +- `Data/Class_X_Count` - Sample count per class +- `Data/Feature_X_Mean/Std` - Feature statistics + +#### **Model Architecture** +- `Model/TotalParameters` - Total model parameters +- `Model/TrainableParameters` - Trainable parameters + +#### **Training Configuration** +- `Config/LearningRate` - Learning rate used +- `Config/BatchSize` - Batch size +- `Config/MaxEpochs` - Maximum epochs + +### RL Training Metrics + +#### **Episode Performance** +- `Episode/TotalReward` - Total reward per episode +- `Episode/FinalBalance` - Final balance after episode +- `Episode/TotalReturn` - Return percentage +- `Episode/Steps` - Steps taken in episode + +#### **Trading Performance** +- `Trading/TotalTrades` - Number of trades executed +- `Trading/WinRate` - Percentage of profitable trades +- `Trading/ProfitFactor` - Gross profit / gross loss ratio +- `Trading/MaxDrawdown` - Maximum drawdown percentage + +#### **Agent Learning** +- `Agent/Epsilon` - Exploration rate (epsilon) +- `Agent/LearningRate` - Agent learning rate +- `Agent/MemorySize` - Experience replay buffer size +- `Agent/Loss` - Training loss from experience replay + +#### **Moving Averages** +- `Moving_Average/Reward_50ep` - 50-episode average reward +- `Moving_Average/Return_50ep` - 50-episode average return + +#### **Best Performance** +- `Best/Return` - Best return percentage achieved + +## Directory Structure + +``` +runs/ +ā”œā”€ā”€ cnn_training_1748043814/ # CNN training session +│ ā”œā”€ā”€ events.out.tfevents.* # TensorBoard event files +│ └── ... +ā”œā”€ā”€ rl_training_1748043920/ # RL training session +│ ā”œā”€ā”€ events.out.tfevents.* +│ └── ... +└── ... # Other training sessions +``` + +## TensorBoard Features + +### **Scalars Tab** +- Real-time line charts of all metrics +- Smoothing controls for noisy metrics +- Multiple run comparisons +- Download data as CSV + +### **Images Tab** +- Model architecture visualizations +- Training progression images + +### **Graphs Tab** +- Computational graph of models +- Network architecture visualization + +### **Histograms Tab** +- Weight and gradient distributions +- Activation patterns over time + +### **Projector Tab** +- High-dimensional data visualization +- Feature embeddings + +## Usage Examples + +### 1. Monitor CNN Training +```bash +# Start CNN training (generates TensorBoard logs) +python main_clean.py --mode cnn --symbol ETH/USDT + +# In another terminal, start TensorBoard +tensorboard --logdir=runs + +# Open browser to http://localhost:6006 +# Navigate to Scalars tab to see: +# - Training/EpochLoss declining over time +# - Validation/Accuracy improving +# - Training/LearningRate schedule +``` + +### 2. Compare Multiple Training Runs +```bash +# Run multiple training sessions +python test_cnn_only.py # Creates cnn_training_X +python test_cnn_only.py # Creates cnn_training_Y + +# TensorBoard automatically shows both runs +# Compare performance across runs in the same charts +``` + +### 3. Monitor RL Agent Training +```bash +# Start RL training with TensorBoard logging +python main_clean.py --mode rl --symbol ETH/USDT + +# View in TensorBoard: +# - Episode/TotalReward trending up +# - Trading/WinRate improving +# - Agent/Epsilon decreasing (less exploration) +``` + +## Real-Time Monitoring + +### Key Indicators to Watch + +#### **CNN Training Health** +- āœ… `Training/EpochLoss` should decrease over time +- āœ… `Validation/Accuracy` should increase +- āš ļø Watch for overfitting (val loss increases while train loss decreases) +- āœ… `Training/LearningRate` should follow schedule + +#### **RL Training Health** +- āœ… `Episode/TotalReward` trending upward +- āœ… `Trading/WinRate` above 50% +- āœ… `Moving_Average/Return_50ep` positive and stable +- āš ļø `Agent/Epsilon` should decay over time + +### Warning Signs +- **Loss not decreasing**: Check learning rate, data quality +- **Accuracy plateauing**: May need more data or different architecture +- **RL rewards oscillating**: Unstable learning, adjust hyperparameters +- **Win rate dropping**: Strategy not working, need different approach + +## Configuration + +### Custom TensorBoard Setup +```python +from torch.utils.tensorboard import SummaryWriter + +# Custom log directory +writer = SummaryWriter(log_dir='runs/my_experiment') + +# Log custom metrics +writer.add_scalar('Custom/Metric', value, step) +writer.add_histogram('Custom/Weights', weights, step) +``` + +### Advanced Features +```bash +# Start TensorBoard with custom port +tensorboard --logdir=runs --port=6007 + +# Enable debugging +tensorboard --logdir=runs --debugger_port=6064 + +# Profile performance +tensorboard --logdir=runs --load_fast=false +``` + +## Integration with Training + +### CNN Trainer Integration +- Automatically logs all training metrics +- Model architecture visualization +- Real data statistics tracking +- Best model checkpointing based on TensorBoard metrics + +### RL Trainer Integration +- Episode-by-episode performance tracking +- Trading strategy effectiveness monitoring +- Agent learning progress visualization +- Hyperparameter optimization guidance + +## Benefits Over Static Charts + +### āœ… **Real-Time Updates** +- See training progress as it happens +- No need to wait for training completion +- Immediate feedback on hyperparameter changes + +### āœ… **Interactive Exploration** +- Zoom, pan, and explore metrics +- Smooth noisy data with built-in controls +- Compare multiple training runs side-by-side + +### āœ… **Rich Visualizations** +- Scalars, histograms, images, and graphs +- Model architecture visualization +- High-dimensional data projections + +### āœ… **Data Export** +- Download metrics as CSV +- Programmatic access to training data +- Integration with external analysis tools + +## Troubleshooting + +### TensorBoard Not Starting +```bash +# Check if TensorBoard is installed +pip install tensorboard + +# Verify runs directory exists +dir runs # Windows +ls runs # Linux/Mac + +# Kill existing TensorBoard processes +taskkill /F /IM tensorboard.exe # Windows +pkill -f tensorboard # Linux/Mac +``` + +### No Data Showing +- Ensure training is generating logs in `runs/` directory +- Check browser console for errors +- Try refreshing the page +- Verify correct port (default 6006) + +### Performance Issues +- Use `--load_fast=true` for faster loading +- Clear old log directories +- Reduce logging frequency in training code + +## Best Practices + +### šŸŽÆ **Regular Monitoring** +- Check TensorBoard every 10-20 epochs during CNN training +- Monitor RL agents every 50-100 episodes +- Look for concerning trends early + +### šŸ“Š **Metric Organization** +- Use clear naming conventions (Training/, Validation/, etc.) +- Group related metrics together +- Log at appropriate frequencies (not every step) + +### šŸ’¾ **Data Management** +- Archive old training runs periodically +- Keep successful run logs for reference +- Document experiment parameters in run names + +### šŸ” **Hyperparameter Tuning** +- Compare multiple runs with different hyperparameters +- Use TensorBoard data to guide optimization +- Track which settings produce best results + +--- + +## Summary + +TensorBoard integration provides **real-time, interactive monitoring** of training progress using **only real market data**. This replaces static plots with dynamic visualizations that help optimize model performance and catch issues early. + +**Key Commands:** +```bash +# Train with TensorBoard logging +python main_clean.py --mode cnn --symbol ETH/USDT + +# Start TensorBoard +python run_tensorboard.py + +# Access dashboard +http://localhost:6006 +``` + +All metrics are derived from **real cryptocurrency market data** to ensure authentic trading model development. \ No newline at end of file diff --git a/TRAINING_STATUS.md b/TRAINING_STATUS.md new file mode 100644 index 0000000..0519ecb --- /dev/null +++ b/TRAINING_STATUS.md @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/main_clean.py b/main_clean.py index 74fd121..6094dc8 100644 --- a/main_clean.py +++ b/main_clean.py @@ -82,72 +82,50 @@ def run_data_test(): logger.error(traceback.format_exc()) raise -def run_cnn_training(): - """Train CNN models only with comprehensive pipeline""" +def run_cnn_training(config: Config, symbol: str): + """Run CNN training mode with TensorBoard monitoring""" + logger.info("Starting CNN Training Mode...") + + # Initialize data provider and trainer + data_provider = DataProvider(config) + trainer = CNNTrainer(config) + + # Use configured symbols or provided symbol + symbols = config.symbols if symbol == "ETH/USDT" else [symbol] + config.symbols + save_path = f"models/cnn/scalping_cnn_trained.pt" + + logger.info(f"Training CNN for symbols: {symbols}") + logger.info(f"Will save to: {save_path}") + logger.info(f"šŸ”— Monitor training: tensorboard --logdir=runs") + try: - logger.info("Starting CNN Training Mode...") + # Train model with TensorBoard logging + results = trainer.train(symbols, save_path=save_path) - # Initialize components - data_provider = DataProvider( - symbols=['ETH/USDT', 'BTC/USDT'], - timeframes=['1s', '1m', '5m', '1h', '4h'] - ) - - # Import and create CNN trainer - from training.cnn_trainer import CNNTrainer - trainer = CNNTrainer(data_provider) - - # Configure training - trainer.num_samples = 20000 # Training samples - trainer.batch_size = 64 - trainer.num_epochs = 100 - trainer.patience = 15 - - # Train the model - symbols = ['ETH/USDT', 'BTC/USDT'] - save_path = 'models/cnn/scalping_cnn_trained.pt' - - logger.info(f"Training CNN for symbols: {symbols}") - logger.info(f"Will save to: {save_path}") - - results = trainer.train(symbols, save_path) - - # Log results logger.info("CNN Training Results:") logger.info(f" Best validation accuracy: {results['best_val_accuracy']:.4f}") logger.info(f" Best validation loss: {results['best_val_loss']:.4f}") logger.info(f" Total epochs: {results['total_epochs']}") - logger.info(f" Training time: {results['total_time']:.2f} seconds") + logger.info(f" Training time: {results['training_time']:.2f} seconds") + logger.info(f" TensorBoard logs: {results['tensorboard_dir']}") - # Plot training history - try: - plot_path = 'models/cnn/training_history.png' - trainer.plot_training_history(plot_path) - logger.info(f"Training plots saved to: {plot_path}") - except Exception as e: - logger.warning(f"Could not save training plots: {e}") + logger.info(f"šŸ“Š View training progress: tensorboard --logdir=runs") + logger.info("Evaluating CNN on test data...") - # Evaluate on test data - try: - logger.info("Evaluating CNN on test data...") - test_symbols = ['ETH/USDT'] # Use subset for testing - eval_results = trainer.evaluate_model(test_symbols) - - logger.info("CNN Evaluation Results:") - logger.info(f" Test accuracy: {eval_results['test_accuracy']:.4f}") - logger.info(f" Test loss: {eval_results['test_loss']:.4f}") - logger.info(f" Average confidence: {eval_results['avg_confidence']:.4f}") - - except Exception as e: - logger.warning(f"Could not run evaluation: {e}") + # Quick evaluation on same symbols + test_results = trainer.evaluate(symbols[:1]) # Use first symbol for quick test + logger.info("CNN Evaluation Results:") + logger.info(f" Test accuracy: {test_results['test_accuracy']:.4f}") + logger.info(f" Test loss: {test_results['test_loss']:.4f}") + logger.info(f" Average confidence: {test_results['avg_confidence']:.4f}") logger.info("CNN training completed successfully!") except Exception as e: - logger.error(f"Error in CNN training: {e}") - import traceback - logger.error(traceback.format_exc()) + logger.error(f"CNN training failed: {e}") raise + finally: + trainer.close_tensorboard() def run_rl_training(): """Train RL agents only with comprehensive pipeline""" @@ -404,7 +382,7 @@ async def main(): if args.mode == 'test': run_data_test() elif args.mode == 'cnn': - run_cnn_training() + run_cnn_training(get_config(), args.symbol) elif args.mode == 'rl': run_rl_training() elif args.mode == 'train': diff --git a/monitor_training.py b/monitor_training.py new file mode 100644 index 0000000..28afb39 --- /dev/null +++ b/monitor_training.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +""" +Training Monitor Script + +Quick script to check the status of realtime training and show key metrics. +""" + +import os +import time +from pathlib import Path +from datetime import datetime +import glob + +def check_training_status(): + """Check status of training processes and logs""" + print("=" * 60) + print("REALTIME RL TRAINING STATUS CHECK") + print("=" * 60) + + # Check TensorBoard logs + runs_dir = Path("runs") + if runs_dir.exists(): + log_dirs = list(runs_dir.glob("rl_training_*")) + recent_logs = sorted(log_dirs, key=lambda x: x.name)[-3:] # Last 3 sessions + + print("\nšŸ“Š RECENT TENSORBOARD LOGS:") + for log_dir in recent_logs: + # Get creation time + stat = log_dir.stat() + created = datetime.fromtimestamp(stat.st_ctime) + + # Check for event files + event_files = list(log_dir.glob("*.tfevents.*")) + + print(f" šŸ“ {log_dir.name}") + print(f" Created: {created.strftime('%Y-%m-%d %H:%M:%S')}") + print(f" Event files: {len(event_files)}") + + if event_files: + latest_event = max(event_files, key=lambda x: x.stat().st_mtime) + modified = datetime.fromtimestamp(latest_event.stat().st_mtime) + print(f" Last update: {modified.strftime('%Y-%m-%d %H:%M:%S')}") + print() + + # Check running processes + print("šŸ” PROCESS STATUS:") + try: + import subprocess + result = subprocess.run(['tasklist'], capture_output=True, text=True, shell=True) + python_processes = [line for line in result.stdout.split('\n') if 'python.exe' in line] + print(f" Python processes running: {len(python_processes)}") + for i, proc in enumerate(python_processes[:5]): # Show first 5 + print(f" {i+1}. {proc.strip()}") + except Exception as e: + print(f" Error checking processes: {e}") + + # Check web services + print("\n🌐 WEB SERVICES:") + print(" TensorBoard: http://localhost:6006") + print(" Web Dashboard: http://localhost:8051") + + # Check model saves + models_dir = Path("models/rl") + if models_dir.exists(): + model_files = list(models_dir.glob("realtime_agent_*.pt")) + print(f"\nšŸ’¾ SAVED MODELS: {len(model_files)}") + for model_file in sorted(model_files, key=lambda x: x.stat().st_mtime)[-3:]: + modified = datetime.fromtimestamp(model_file.stat().st_mtime) + print(f" šŸ“„ {model_file.name} - {modified.strftime('%Y-%m-%d %H:%M:%S')}") + + print("\n" + "=" * 60) + print("āœ… MONITORING URLs:") + print("šŸ“Š TensorBoard: http://localhost:6006") + print("🌐 Dashboard: http://localhost:8051") + print("=" * 60) + +if __name__ == "__main__": + try: + check_training_status() + except KeyboardInterrupt: + print("\nMonitoring stopped.") + except Exception as e: + print(f"Error: {e}") \ No newline at end of file diff --git a/readme.md b/readme.md index e0b8fc9..64de986 100644 --- a/readme.md +++ b/readme.md @@ -80,24 +80,53 @@ gogo2/ ## Training Modes -### CNN Training +### CNN Training with TensorBoard ```bash -# Train on real ETH/USDT data +# Train on real ETH/USDT data with TensorBoard monitoring python main_clean.py --mode cnn --symbol ETH/USDT +# Monitor training in real-time +tensorboard --logdir=runs + +# Or use the convenience script +python run_tensorboard.py + # Quick test with real data python test_cnn_only.py ``` -### RL Training +### RL Training with TensorBoard ```bash # Train RL agent with real data python main_clean.py --mode rl --symbol ETH/USDT # Real-time RL training python train_rl_with_realtime.py --episodes 10 + +# Monitor RL training metrics +tensorboard --logdir=runs ``` +## TensorBoard Monitoring + +All training sessions are logged to TensorBoard for real-time monitoring: + +```bash +# Start TensorBoard server +tensorboard --logdir=runs + +# Or use the convenience script +python run_tensorboard.py +``` + +**Metrics Available:** +- **CNN Training**: Loss, accuracy, confidence scores, feature statistics +- **RL Training**: Rewards, returns, win rates, epsilon values, trading metrics +- **Model Architecture**: Parameter counts, memory usage +- **Real-time Updates**: Batch-level and epoch-level metrics + +Access TensorBoard at: http://localhost:6006 + ## Performance - **Memory Usage**: <2GB per model diff --git a/run_tensorboard.py b/run_tensorboard.py index 5cfb5f1..d4aa993 100644 --- a/run_tensorboard.py +++ b/run_tensorboard.py @@ -1,69 +1,74 @@ -import os -import sys -import subprocess -import webbrowser -import time -import argparse +#!/usr/bin/env python3 +""" +TensorBoard Launch Script -def run_tensorboard(): - """Run TensorBoard server and open browser""" - parser = argparse.ArgumentParser(description='TensorBoard Launcher') - parser.add_argument('--port', type=int, default=6006, help='Port for TensorBoard server') - parser.add_argument('--logdir', type=str, default='runs', help='Log directory for TensorBoard') - parser.add_argument('--no-browser', action='store_true', help='Do not open browser automatically') - args = parser.parse_args() +Starts TensorBoard server for monitoring training progress. +""" + +import subprocess +import sys +import os +import time +import webbrowser +from pathlib import Path + +def main(): + """Launch TensorBoard""" - # Create log directory if it doesn't exist - os.makedirs(args.logdir, exist_ok=True) + # Check if runs directory exists + runs_dir = Path("runs") + if not runs_dir.exists(): + print("āŒ No 'runs' directory found.") + print(" Start training first to generate TensorBoard logs.") + return - # Print banner - print("\n" + "="*60) - print("šŸ“Š TRADING BOT - TENSORBOARD MONITORING šŸ“Š") - print("="*60) - print(f"Starting TensorBoard server on port {args.port}") - print(f"Log directory: {args.logdir}") - print("Press Ctrl+C to stop the server") - print("="*60 + "\n") + # Check if there are any log directories + log_dirs = list(runs_dir.glob("*")) + if not log_dirs: + print("āŒ No training logs found in 'runs' directory.") + print(" Start training first to generate TensorBoard logs.") + return - # Start TensorBoard server - cmd = ["tensorboard", "--logdir", args.logdir, "--port", str(args.port)] + print("šŸš€ Starting TensorBoard...") + print(f"šŸ“ Log directory: {runs_dir.absolute()}") + print(f"šŸ“Š Found {len(log_dirs)} training sessions") + # List available sessions + print("\nAvailable training sessions:") + for i, log_dir in enumerate(sorted(log_dirs), 1): + print(f" {i}. {log_dir.name}") + + # Start TensorBoard try: + port = 6006 + print(f"\n🌐 Starting TensorBoard on port {port}...") + print(f"šŸ”— Access at: http://localhost:{port}") + + # Try to open browser automatically + try: + webbrowser.open(f"http://localhost:{port}") + print("šŸŒ Browser opened automatically") + except: + pass + # Start TensorBoard process - process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True - ) + cmd = [sys.executable, "-m", "tensorboard.main", "--logdir", str(runs_dir), "--port", str(port)] - # Wait for TensorBoard to start - time.sleep(3) + print("\n" + "="*50) + print("šŸ”„ TensorBoard is running!") + print(f"šŸ“ˆ View training metrics at: http://localhost:{port}") + print("ā¹ļø Press Ctrl+C to stop TensorBoard") + print("="*50 + "\n") - # Open browser - if not args.no_browser: - url = f"http://localhost:{args.port}" - print(f"Opening browser to {url}") - webbrowser.open(url) + # Run TensorBoard + subprocess.run(cmd) - # Print TensorBoard output - while True: - output = process.stdout.readline() - if output == '' and process.poll() is not None: - break - if output: - print(output.strip()) - - return process.poll() - except KeyboardInterrupt: - print("\nStopping TensorBoard server...") - process.terminate() - return 0 + print("\nšŸ›‘ TensorBoard stopped") + except FileNotFoundError: + print("āŒ TensorBoard not found. Install with: pip install tensorboard") except Exception as e: - print(f"Error running TensorBoard: {str(e)}") - return 1 + print(f"āŒ Error starting TensorBoard: {e}") if __name__ == "__main__": - exit_code = run_tensorboard() - sys.exit(exit_code) \ No newline at end of file + main() \ No newline at end of file diff --git a/test_cnn_only.py b/test_cnn_only.py index 63ea21d..fd01b33 100644 --- a/test_cnn_only.py +++ b/test_cnn_only.py @@ -1,54 +1,65 @@ #!/usr/bin/env python3 """ -Quick CNN training test +Quick CNN Training Test - Real Market Data Only + +This script tests CNN training with a small dataset for quick validation. +All training metrics are logged to TensorBoard for real-time monitoring. """ -import sys -from pathlib import Path -sys.path.insert(0, str(Path(__file__).parent)) - -from core.config import setup_logging +import logging +from core.config import setup_logging, get_config from core.data_provider import DataProvider from training.cnn_trainer import CNNTrainer def main(): + """Test CNN training with real market data""" setup_logging() print("Setting up CNN training test...") + print("šŸ“Š Monitor training: tensorboard --logdir=runs") - # Setup - data_provider = DataProvider(['ETH/USDT'], ['1m', '5m', '1h']) - trainer = CNNTrainer(data_provider) + # Configure test parameters + config = get_config() - # Configure for quick test - trainer.num_samples = 500 # Very small dataset - trainer.num_epochs = 2 # Just 2 epochs - trainer.batch_size = 16 - trainer.timeframes = ['1m', '5m', '1h'] # Skip 1s for now - trainer.n_timeframes = 3 + # Test configuration + symbols = ['ETH/USDT'] + timeframes = ['1m', '5m', '1h'] + num_samples = 500 + epochs = 2 + batch_size = 16 - print(f"Configuration:") - print(f" Samples: {trainer.num_samples}") - print(f" Epochs: {trainer.num_epochs}") - print(f" Batch size: {trainer.batch_size}") - print(f" Timeframes: {trainer.timeframes}") + # Override config for quick test + config._config['timeframes'] = timeframes # Direct config access + + trainer = CNNTrainer(config) + trainer.batch_size = batch_size + trainer.epochs = epochs + + print("Configuration:") + print(f" Symbols: {symbols}") + print(f" Timeframes: {timeframes}") + print(f" Samples: {num_samples}") + print(f" Epochs: {epochs}") + print(f" Batch size: {batch_size}") + print(" Data source: REAL market data from exchange APIs") - # Train try: - results = trainer.train(['ETH/USDT'], save_path='test_models/quick_cnn.pt') + # Train model with TensorBoard logging + results = trainer.train(symbols, save_path='test_models/quick_cnn.pt', num_samples=num_samples) print(f"\nāœ… CNN Training completed!") print(f" Best accuracy: {results['best_val_accuracy']:.4f}") print(f" Total epochs: {results['total_epochs']}") - print(f" Training time: {results['total_time']:.2f}s") + print(f" Training time: {results['training_time']:.2f}s") + print(f" TensorBoard logs: {results['tensorboard_dir']}") + print(f"\nšŸ“Š View training progress: tensorboard --logdir=runs") except Exception as e: - print(f"\nāŒ Training failed: {e}") + print(f"āŒ Training failed: {e}") import traceback traceback.print_exc() - return 1 - - return 0 + finally: + trainer.close_tensorboard() if __name__ == "__main__": - sys.exit(main()) \ No newline at end of file + main() \ No newline at end of file diff --git a/train_realtime_with_tensorboard.py b/train_realtime_with_tensorboard.py new file mode 100644 index 0000000..5901f96 --- /dev/null +++ b/train_realtime_with_tensorboard.py @@ -0,0 +1,420 @@ +#!/usr/bin/env python3 +""" +Realtime RL Training with TensorBoard and Web UI Monitoring + +This script runs RL training with: +- TensorBoard monitoring for training metrics +- Web UI for real-time trading visualization +- Real market data integration +- PnL tracking and performance analysis +""" + +import asyncio +import threading +import time +import logging +import argparse +from datetime import datetime +import os +import sys +from pathlib import Path + +# Add project path +project_root = Path(__file__).parent +sys.path.insert(0, str(project_root)) + +from core.config import setup_logging, get_config +from core.data_provider import DataProvider +from training.rl_trainer import RLTrainer +from torch.utils.tensorboard import SummaryWriter + +logger = logging.getLogger(__name__) + +class RealtimeRLTrainer: + """Realtime RL Trainer with TensorBoard and Web UI""" + + def __init__(self, symbol="ETH/USDT", initial_balance=1000.0): + self.symbol = symbol + self.initial_balance = initial_balance + + # Initialize data provider + self.data_provider = DataProvider( + symbols=[symbol], + timeframes=['1s', '1m', '5m', '15m', '1h'] + ) + + # Initialize RL trainer with TensorBoard + self.rl_trainer = RLTrainer(self.data_provider) + + # Training state + self.current_episode = 0 + self.session_trades = [] + self.session_balance = initial_balance + self.session_pnl = 0.0 + self.training_active = False + + # Web dashboard + self.dashboard = None + self.dashboard_thread = None + + logger.info(f"RealtimeRLTrainer initialized for {symbol}") + logger.info(f"TensorBoard logs: {self.rl_trainer.tensorboard_dir}") + + def setup_web_dashboard(self, port=8051): + """Setup web dashboard for monitoring""" + try: + import dash + from dash import dcc, html, Input, Output + import plotly.graph_objects as go + import plotly.express as px + + # Create Dash app + app = dash.Dash(__name__) + + # Layout + app.layout = html.Div([ + html.H1(f"RL Training Monitor - {self.symbol}", + style={'textAlign': 'center', 'color': '#2c3e50'}), + + # Refresh interval + dcc.Interval( + id='interval-component', + interval=2000, # Update every 2 seconds + n_intervals=0 + ), + + # Status row + html.Div([ + html.Div([ + html.H3("Training Status", style={'color': '#34495e'}), + html.P(id='training-status', style={'fontSize': 18}) + ], className='three columns'), + + html.Div([ + html.H3("Current Episode", style={'color': '#34495e'}), + html.P(id='current-episode', style={'fontSize': 18}) + ], className='three columns'), + + html.Div([ + html.H3("Session Balance", style={'color': '#27ae60'}), + html.P(id='session-balance', style={'fontSize': 18}) + ], className='three columns'), + + html.Div([ + html.H3("Session PnL", style={'color': '#e74c3c'}), + html.P(id='session-pnl', style={'fontSize': 18}) + ], className='three columns'), + ], className='row', style={'margin': '20px'}), + + # Charts row + html.Div([ + html.Div([ + dcc.Graph(id='rewards-chart') + ], className='six columns'), + + html.Div([ + dcc.Graph(id='balance-chart') + ], className='six columns'), + ], className='row'), + + html.Div([ + html.Div([ + dcc.Graph(id='trades-chart') + ], className='six columns'), + + html.Div([ + dcc.Graph(id='win-rate-chart') + ], className='six columns'), + ], className='row'), + + # TensorBoard link + html.Div([ + html.H3("TensorBoard Monitoring"), + html.A("Open TensorBoard", + href="http://localhost:6006", + target="_blank", + style={'fontSize': 16, 'color': '#3498db'}) + ], style={'textAlign': 'center', 'margin': '20px'}) + ]) + + # Callbacks + @app.callback( + [Output('training-status', 'children'), + Output('current-episode', 'children'), + Output('session-balance', 'children'), + Output('session-pnl', 'children'), + Output('rewards-chart', 'figure'), + Output('balance-chart', 'figure'), + Output('trades-chart', 'figure'), + Output('win-rate-chart', 'figure')], + [Input('interval-component', 'n_intervals')] + ) + def update_dashboard(n): + # Status updates + status = "TRAINING" if self.training_active else "IDLE" + episode = f"{self.current_episode}" + balance = f"${self.session_balance:.2f}" + pnl = f"${self.session_pnl:.2f}" + + # Create charts + rewards_fig = self._create_rewards_chart() + balance_fig = self._create_balance_chart() + trades_fig = self._create_trades_chart() + win_rate_fig = self._create_win_rate_chart() + + return status, episode, balance, pnl, rewards_fig, balance_fig, trades_fig, win_rate_fig + + self.dashboard = app + logger.info(f"Web dashboard created for port {port}") + + except Exception as e: + logger.error(f"Error setting up web dashboard: {e}") + self.dashboard = None + + def _create_rewards_chart(self): + """Create rewards chart""" + import plotly.graph_objects as go + + if not self.rl_trainer.episode_rewards: + fig = go.Figure() + fig.add_annotation(text="No data yet", x=0.5, y=0.5, xref="paper", yref="paper") + else: + fig = go.Figure() + fig.add_trace(go.Scatter( + y=self.rl_trainer.episode_rewards, + mode='lines', + name='Episode Rewards', + line=dict(color='#3498db') + )) + + # Add moving average if enough data + if len(self.rl_trainer.avg_rewards) > 0: + fig.add_trace(go.Scatter( + y=self.rl_trainer.avg_rewards, + mode='lines', + name='Moving Average', + line=dict(color='#e74c3c', width=2) + )) + + fig.update_layout(title="Episode Rewards", xaxis_title="Episode", yaxis_title="Reward") + return fig + + def _create_balance_chart(self): + """Create balance chart""" + import plotly.graph_objects as go + + if not self.rl_trainer.episode_balances: + fig = go.Figure() + fig.add_annotation(text="No data yet", x=0.5, y=0.5, xref="paper", yref="paper") + else: + fig = go.Figure() + fig.add_trace(go.Scatter( + y=self.rl_trainer.episode_balances, + mode='lines', + name='Balance', + line=dict(color='#27ae60') + )) + + # Add initial balance line + fig.add_hline(y=self.initial_balance, line_dash="dash", + annotation_text="Initial Balance") + + fig.update_layout(title="Portfolio Balance", xaxis_title="Episode", yaxis_title="Balance ($)") + return fig + + def _create_trades_chart(self): + """Create trades per episode chart""" + import plotly.graph_objects as go + + if not self.rl_trainer.episode_trades: + fig = go.Figure() + fig.add_annotation(text="No data yet", x=0.5, y=0.5, xref="paper", yref="paper") + else: + fig = go.Figure() + fig.add_trace(go.Bar( + y=self.rl_trainer.episode_trades, + name='Trades per Episode', + marker_color='#f39c12' + )) + + fig.update_layout(title="Trades per Episode", xaxis_title="Episode", yaxis_title="Number of Trades") + return fig + + def _create_win_rate_chart(self): + """Create win rate chart""" + import plotly.graph_objects as go + + if not self.rl_trainer.win_rates: + fig = go.Figure() + fig.add_annotation(text="No data yet", x=0.5, y=0.5, xref="paper", yref="paper") + else: + fig = go.Figure() + fig.add_trace(go.Scatter( + y=self.rl_trainer.win_rates, + mode='lines+markers', + name='Win Rate', + line=dict(color='#9b59b6') + )) + + # Add 50% line + fig.add_hline(y=0.5, line_dash="dash", + annotation_text="Break Even") + + fig.update_layout(title="Win Rate", xaxis_title="Evaluation", yaxis_title="Win Rate") + return fig + + def start_web_dashboard(self, port=8051): + """Start web dashboard in background thread""" + if self.dashboard is None: + self.setup_web_dashboard(port) + + if self.dashboard is not None: + def run_dashboard(): + try: + # Use run instead of run_server for newer Dash versions + self.dashboard.run(port=port, debug=False, use_reloader=False) + except Exception as e: + logger.error(f"Error running dashboard: {e}") + + self.dashboard_thread = threading.Thread(target=run_dashboard, daemon=True) + self.dashboard_thread.start() + logger.info(f"Web dashboard started on http://localhost:{port}") + else: + logger.warning("Dashboard not available") + + async def train_realtime(self, episodes=100, evaluation_interval=10): + """Run realtime training with monitoring""" + logger.info(f"Starting realtime RL training for {episodes} episodes") + logger.info(f"TensorBoard: http://localhost:6006") + logger.info(f"Web UI: http://localhost:8051") + + self.training_active = True + + # Setup environment and agent + environment, agent = self.rl_trainer.setup_environment_and_agent() + + # Training loop + for episode in range(episodes): + self.current_episode = episode + + # Run episode + episode_start = time.time() + results = self.rl_trainer.run_episode(episode, training=True) + episode_time = time.time() - episode_start + + # Update session tracking + self.session_balance = results.get('balance', self.initial_balance) + self.session_pnl = self.session_balance - self.initial_balance + + # Log episode metrics to TensorBoard + self.rl_trainer.log_episode_metrics(episode, { + 'total_reward': results['reward'], + 'final_balance': results['balance'], + 'total_return': results['pnl_percentage'], + 'steps': results['steps'], + 'total_trades': results['trades'], + 'win_rate': 1.0 if results['pnl'] > 0 else 0.0, + 'epsilon': agent.epsilon, + 'memory_size': len(agent.memory) if hasattr(agent, 'memory') else 0 + }) + + # Log progress + if episode % 10 == 0: + logger.info( + f"Episode {episode}/{episodes} - " + f"Reward: {results['reward']:.4f}, " + f"Balance: ${results['balance']:.2f}, " + f"PnL: {results['pnl_percentage']:.2f}%, " + f"Trades: {results['trades']}, " + f"Time: {episode_time:.2f}s" + ) + + # Evaluation + if episode % evaluation_interval == 0 and episode > 0: + eval_results = self.rl_trainer.evaluate_agent(num_episodes=3) + logger.info( + f"Evaluation - Avg Reward: {eval_results['avg_reward']:.4f}, " + f"Win Rate: {eval_results['win_rate']:.2%}" + ) + + # Small delay to allow UI updates + await asyncio.sleep(0.1) + + self.training_active = False + logger.info("Training completed!") + + # Save final model + save_path = f"models/rl/realtime_agent_{int(time.time())}.pt" + agent.save(save_path) + logger.info(f"Model saved: {save_path}") + + return { + 'episodes': episodes, + 'final_balance': self.session_balance, + 'final_pnl': self.session_pnl, + 'model_path': save_path + } + +async def main(): + """Main function""" + parser = argparse.ArgumentParser(description='Realtime RL Training with Monitoring') + parser.add_argument('--symbol', type=str, default='ETH/USDT', help='Trading symbol') + parser.add_argument('--episodes', type=int, default=50, help='Number of episodes') + parser.add_argument('--balance', type=float, default=1000.0, help='Initial balance') + parser.add_argument('--web-port', type=int, default=8051, help='Web dashboard port') + + args = parser.parse_args() + + # Setup logging + setup_logging() + + logger.info("=" * 60) + logger.info("REALTIME RL TRAINING WITH MONITORING") + logger.info(f"Symbol: {args.symbol}") + logger.info(f"Episodes: {args.episodes}") + logger.info(f"Initial Balance: ${args.balance:.2f}") + logger.info("=" * 60) + + try: + # Create trainer + trainer = RealtimeRLTrainer( + symbol=args.symbol, + initial_balance=args.balance + ) + + # Start web dashboard + trainer.start_web_dashboard(port=args.web_port) + + # Wait for dashboard to start + await asyncio.sleep(2) + + logger.info("MONITORING READY!") + logger.info(f"TensorBoard: http://localhost:6006") + logger.info(f"Web Dashboard: http://localhost:{args.web_port}") + logger.info("=" * 60) + + # Run training + results = await trainer.train_realtime( + episodes=args.episodes, + evaluation_interval=10 + ) + + logger.info("Training Results:") + logger.info(f" Final Balance: ${results['final_balance']:.2f}") + logger.info(f" Final PnL: ${results['final_pnl']:.2f}") + logger.info(f" Model Saved: {results['model_path']}") + + # Keep running for monitoring + logger.info("Training complete. Press Ctrl+C to exit monitoring.") + while True: + await asyncio.sleep(1) + + except KeyboardInterrupt: + logger.info("Training stopped by user") + except Exception as e: + logger.error(f"Error in training: {e}") + import traceback + logger.error(traceback.format_exc()) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/training/cnn_trainer.py b/training/cnn_trainer.py index 84697f9..c067fcc 100644 --- a/training/cnn_trainer.py +++ b/training/cnn_trainer.py @@ -1,31 +1,23 @@ """ -CNN Training Pipeline - Scalping Pattern Recognition +CNN Training Pipeline -Comprehensive training pipeline for multi-timeframe CNN models: -- Automated data generation and preprocessing -- Training with validation and early stopping -- Memory-efficient batch processing -- Model evaluation and metrics +This module handles training of the CNN model using ONLY real market data. +All training metrics are logged to TensorBoard for real-time monitoring. """ import torch import torch.nn as nn import torch.optim as optim -from torch.utils.data import DataLoader, Dataset +from torch.utils.data import Dataset, DataLoader, random_split +from torch.utils.tensorboard import SummaryWriter import numpy as np import pandas as pd import logging from typing import Dict, List, Tuple, Optional -import time from pathlib import Path +import time from sklearn.metrics import classification_report, confusion_matrix -from sklearn.model_selection import train_test_split -import matplotlib.pyplot as plt - -# Add project imports -import sys -import os -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +import json from core.config import get_config from core.data_provider import DataProvider @@ -33,13 +25,12 @@ from models.cnn.scalping_cnn import MultiTimeframeCNN, ScalpingDataGenerator logger = logging.getLogger(__name__) -class TradingDataset(Dataset): - """PyTorch dataset for trading data""" +class CNNDataset(Dataset): + """Dataset for CNN training with real market data""" - def __init__(self, features: np.ndarray, labels: np.ndarray, metadata: Optional[Dict] = None): + def __init__(self, features: np.ndarray, labels: np.ndarray): self.features = torch.FloatTensor(features) - self.labels = torch.FloatTensor(labels) - self.metadata = metadata or {} + self.labels = torch.LongTensor(np.argmax(labels, axis=1)) # Convert one-hot to class indices def __len__(self): return len(self.features) @@ -48,431 +39,437 @@ class TradingDataset(Dataset): return self.features[idx], self.labels[idx] class CNNTrainer: - """ - CNN Training Pipeline for Scalping - """ + """CNN Trainer using ONLY real market data with TensorBoard monitoring""" - def __init__(self, data_provider: DataProvider, config: Optional[Dict] = None): - self.data_provider = data_provider + def __init__(self, config: Optional[Dict] = None): + """Initialize CNN trainer""" self.config = config or get_config() - - # Training parameters - self.learning_rate = 1e-4 - self.batch_size = 64 - self.num_epochs = 100 - self.patience = 15 - self.validation_split = 0.2 - - # Data parameters - self.timeframes = ['1s', '1m', '5m', '1h'] - self.window_size = 20 - self.num_samples = 20000 - - # Model parameters - self.n_timeframes = len(self.timeframes) - self.n_features = 26 # Number of technical indicators - self.n_classes = 3 # BUY, SELL, HOLD - - # Device self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') - # Initialize data generator - self.data_generator = ScalpingDataGenerator(data_provider, self.window_size) + # Training parameters + self.learning_rate = self.config.training.get('learning_rate', 0.001) + self.batch_size = self.config.training.get('batch_size', 32) + self.epochs = self.config.training.get('epochs', 100) + self.validation_split = self.config.training.get('validation_split', 0.2) + self.early_stopping_patience = self.config.training.get('early_stopping_patience', 10) - # Training state + # Model parameters - will be updated based on real data + self.n_timeframes = len(self.config.timeframes) + self.window_size = self.config.cnn.get('window_size', 20) + self.n_features = self.config.cnn.get('features', 26) # Will be dynamically updated + self.n_classes = 3 # BUY, SELL, HOLD + + # Initialize components + self.data_provider = DataProvider(self.config) + self.data_generator = ScalpingDataGenerator(self.data_provider, self.window_size) self.model = None - self.train_losses = [] - self.val_losses = [] - self.train_accuracies = [] - self.val_accuracies = [] + + # TensorBoard setup + self.setup_tensorboard() logger.info(f"CNNTrainer initialized with {self.n_timeframes} timeframes, {self.n_features} features") + logger.info("Will use ONLY real market data for training") - def prepare_data(self, symbols: List[str]) -> Tuple[DataLoader, DataLoader, Dict]: - """Prepare training and validation data""" - logger.info("Preparing training data...") + def setup_tensorboard(self): + """Setup TensorBoard logging""" + # Create tensorboard logs directory + log_dir = Path("runs") / f"cnn_training_{int(time.time())}" + log_dir.mkdir(parents=True, exist_ok=True) - all_features = [] - all_labels = [] - all_metadata = {'symbols': []} + self.writer = SummaryWriter(log_dir=str(log_dir)) + self.tensorboard_dir = log_dir - # Generate data for each symbol - for symbol in symbols: - logger.info(f"Generating data for {symbol}...") - - features, labels, metadata = self.data_generator.generate_training_cases( - symbol, self.timeframes, self.num_samples // len(symbols) - ) - - if features is not None and labels is not None: - all_features.append(features) - all_labels.append(labels) - all_metadata['symbols'].extend([symbol] * len(features)) - - logger.info(f"Generated {len(features)} samples for {symbol}") - - # Update feature count based on actual data - if len(all_features) == 1: - actual_features = features.shape[-1] - if actual_features != self.n_features: - logger.info(f"Updating feature count from {self.n_features} to {actual_features}") - self.n_features = actual_features - else: - logger.warning(f"No data generated for {symbol}") - - if not all_features: - raise ValueError("No training data generated") - - # Combine all data - combined_features = np.concatenate(all_features, axis=0) - combined_labels = np.concatenate(all_labels, axis=0) - - logger.info(f"Total dataset: {len(combined_features)} samples") - logger.info(f"Features shape: {combined_features.shape}") - logger.info(f"Labels shape: {combined_labels.shape}") - - # Split into train/validation - X_train, X_val, y_train, y_val = train_test_split( - combined_features, combined_labels, - test_size=self.validation_split, - stratify=np.argmax(combined_labels, axis=1), - random_state=42 - ) - - # Create datasets - train_dataset = TradingDataset(X_train, y_train) - val_dataset = TradingDataset(X_val, y_val) - - # Create data loaders - train_loader = DataLoader( - train_dataset, - batch_size=self.batch_size, - shuffle=True, - num_workers=0, # Set to 0 to avoid multiprocessing issues - pin_memory=True if torch.cuda.is_available() else False - ) - - val_loader = DataLoader( - val_dataset, - batch_size=self.batch_size, - shuffle=False, - num_workers=0, - pin_memory=True if torch.cuda.is_available() else False - ) - - # Prepare metadata for return - dataset_info = { - 'train_size': len(train_dataset), - 'val_size': len(val_dataset), - 'feature_shape': combined_features.shape[1:], - 'label_distribution': { - 'train': np.bincount(np.argmax(y_train, axis=1)), - 'val': np.bincount(np.argmax(y_val, axis=1)) - } - } - - logger.info(f"Train samples: {dataset_info['train_size']}") - logger.info(f"Validation samples: {dataset_info['val_size']}") - logger.info(f"Train label distribution: {dataset_info['label_distribution']['train']}") - logger.info(f"Val label distribution: {dataset_info['label_distribution']['val']}") - - return train_loader, val_loader, dataset_info + logger.info(f"TensorBoard logging to: {log_dir}") + logger.info(f"Run: tensorboard --logdir=runs") + def log_model_architecture(self): + """Log model architecture to TensorBoard""" + if self.model is not None: + # Log model graph (requires a dummy input) + dummy_input = torch.randn(1, self.n_timeframes, self.window_size, self.n_features).to(self.device) + try: + self.writer.add_graph(self.model, dummy_input) + logger.info("Model architecture logged to TensorBoard") + except Exception as e: + logger.warning(f"Could not log model graph: {e}") + + # Log model parameters count + total_params = sum(p.numel() for p in self.model.parameters()) + trainable_params = sum(p.numel() for p in self.model.parameters() if p.requires_grad) + + self.writer.add_scalar('Model/TotalParameters', total_params, 0) + self.writer.add_scalar('Model/TrainableParameters', trainable_params, 0) + def create_model(self) -> MultiTimeframeCNN: - """Create and initialize the CNN model""" + """Create CNN model""" model = MultiTimeframeCNN( n_timeframes=self.n_timeframes, window_size=self.window_size, n_features=self.n_features, - n_classes=self.n_classes + n_classes=self.n_classes, + dropout_rate=self.config.cnn.get('dropout', 0.2) ) - model.to(self.device) + model = model.to(self.device) # Log model info total_params = sum(p.numel() for p in model.parameters()) trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad) + memory_usage = model.get_memory_usage() logger.info(f"Model created with {total_params:,} total parameters") logger.info(f"Trainable parameters: {trainable_params:,}") - logger.info(f"Estimated memory usage: {model.get_memory_usage()}MB") + logger.info(f"Estimated memory usage: {memory_usage}MB") return model + def prepare_data(self, symbols: List[str], num_samples: int = 10000) -> Tuple[np.ndarray, np.ndarray, Dict]: + """Prepare training data from REAL market data""" + logger.info("Preparing training data...") + logger.info("Data source: REAL market data from exchange APIs") + + all_features = [] + all_labels = [] + all_metadata = [] + + for symbol in symbols: + logger.info(f"Generating data for {symbol}...") + + features, labels, metadata = self.data_generator.generate_training_cases( + symbol=symbol, + timeframes=self.config.timeframes, + num_samples=num_samples + ) + + if features is not None: + all_features.append(features) + all_labels.append(labels) + all_metadata.append(metadata) + + logger.info(f"Generated {len(features)} samples for {symbol}") + + # Update feature count if needed + actual_features = features.shape[-1] + if actual_features != self.n_features: + logger.info(f"Updating feature count from {self.n_features} to {actual_features}") + self.n_features = actual_features + + if not all_features: + raise ValueError("No training data generated from real market data") + + # Combine all data + features = np.concatenate(all_features, axis=0) + labels = np.concatenate(all_labels, axis=0) + + # Log data statistics to TensorBoard + self.log_data_statistics(features, labels) + + return features, labels, all_metadata + + def log_data_statistics(self, features: np.ndarray, labels: np.ndarray): + """Log data statistics to TensorBoard""" + # Dataset size + self.writer.add_scalar('Data/TotalSamples', len(features), 0) + self.writer.add_scalar('Data/Features', features.shape[-1], 0) + self.writer.add_scalar('Data/Timeframes', features.shape[1], 0) + self.writer.add_scalar('Data/WindowSize', features.shape[2], 0) + + # Class distribution + class_counts = np.bincount(np.argmax(labels, axis=1)) + for i, count in enumerate(class_counts): + self.writer.add_scalar(f'Data/Class_{i}_Count', count, 0) + + # Feature statistics + feature_means = features.mean(axis=(0, 1, 2)) + feature_stds = features.std(axis=(0, 1, 2)) + + for i in range(min(10, len(feature_means))): # Log first 10 features + self.writer.add_scalar(f'Data/Feature_{i}_Mean', feature_means[i], 0) + self.writer.add_scalar(f'Data/Feature_{i}_Std', feature_stds[i], 0) + def train_epoch(self, model: nn.Module, train_loader: DataLoader, - optimizer: optim.Optimizer, criterion: nn.Module) -> Tuple[float, float]: - """Train for one epoch""" + optimizer: torch.optim.Optimizer, criterion: nn.Module, epoch: int) -> Tuple[float, float]: + """Train for one epoch with TensorBoard logging""" model.train() total_loss = 0.0 - correct_predictions = 0 - total_predictions = 0 + correct = 0 + total = 0 for batch_idx, (features, labels) in enumerate(train_loader): - features = features.to(self.device) - labels = labels.to(self.device) + features, labels = features.to(self.device), labels.to(self.device) - # Zero gradients optimizer.zero_grad() - - # Forward pass predictions = model(features) - - # Calculate loss (multi-task loss) - action_loss = criterion(predictions['action'], labels) - - # Additional losses for auxiliary tasks - confidence_loss = torch.mean(torch.abs(predictions['confidence'] - 0.5)) # Encourage diversity - - # Total loss - total_loss_batch = action_loss + 0.1 * confidence_loss - - # Backward pass - total_loss_batch.backward() - - # Gradient clipping - torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) - - # Update weights + loss = criterion(predictions['action'], labels) + loss.backward() optimizer.step() - # Track metrics - total_loss += total_loss_batch.item() + total_loss += loss.item() + _, predicted = torch.max(predictions['action'].data, 1) + total += labels.size(0) + correct += (predicted == labels).sum().item() - # Calculate accuracy - pred_classes = torch.argmax(predictions['action'], dim=1) - true_classes = torch.argmax(labels, dim=1) - correct_predictions += (pred_classes == true_classes).sum().item() - total_predictions += labels.size(0) + # Log batch metrics + step = epoch * len(train_loader) + batch_idx + self.writer.add_scalar('Training/BatchLoss', loss.item(), step) - # Log progress - if batch_idx % 100 == 0: - logger.debug(f"Batch {batch_idx}/{len(train_loader)}, Loss: {total_loss_batch.item():.4f}") + if batch_idx % 50 == 0: # Log every 50 batches + batch_acc = 100. * (predicted == labels).sum().item() / labels.size(0) + self.writer.add_scalar('Training/BatchAccuracy', batch_acc, step) + + # Log confidence scores + avg_confidence = predictions['confidence'].mean().item() + self.writer.add_scalar('Training/BatchConfidence', avg_confidence, step) - avg_loss = total_loss / len(train_loader) - accuracy = correct_predictions / total_predictions + epoch_loss = total_loss / len(train_loader) + epoch_accuracy = correct / total - return avg_loss, accuracy + return epoch_loss, epoch_accuracy def validate_epoch(self, model: nn.Module, val_loader: DataLoader, - criterion: nn.Module) -> Tuple[float, float, Dict]: - """Validate for one epoch""" + criterion: nn.Module, epoch: int) -> Tuple[float, float, Dict]: + """Validate for one epoch with TensorBoard logging""" model.eval() total_loss = 0.0 - correct_predictions = 0 - total_predictions = 0 - + correct = 0 + total = 0 all_predictions = [] all_labels = [] all_confidences = [] with torch.no_grad(): for features, labels in val_loader: - features = features.to(self.device) - labels = labels.to(self.device) + features, labels = features.to(self.device), labels.to(self.device) - # Forward pass predictions = model(features) - - # Calculate loss loss = criterion(predictions['action'], labels) + total_loss += loss.item() + _, predicted = torch.max(predictions['action'].data, 1) + total += labels.size(0) + correct += (predicted == labels).sum().item() - # Track predictions - pred_classes = torch.argmax(predictions['action'], dim=1) - true_classes = torch.argmax(labels, dim=1) - - correct_predictions += (pred_classes == true_classes).sum().item() - total_predictions += labels.size(0) - - # Store for detailed analysis - all_predictions.extend(pred_classes.cpu().numpy()) - all_labels.extend(true_classes.cpu().numpy()) + all_predictions.extend(predicted.cpu().numpy()) + all_labels.extend(labels.cpu().numpy()) all_confidences.extend(predictions['confidence'].cpu().numpy()) - avg_loss = total_loss / len(val_loader) - accuracy = correct_predictions / total_predictions + epoch_loss = total_loss / len(val_loader) + epoch_accuracy = correct / total - # Additional metrics - metrics = { - 'predictions': np.array(all_predictions), - 'labels': np.array(all_labels), - 'confidences': np.array(all_confidences), - 'accuracy_by_class': {}, - 'avg_confidence': np.mean(all_confidences) - } + # Calculate detailed metrics + metrics = self.calculate_detailed_metrics(all_predictions, all_labels, all_confidences) - # Calculate per-class accuracy - for class_idx in range(self.n_classes): - class_mask = metrics['labels'] == class_idx - if np.sum(class_mask) > 0: - class_accuracy = np.mean(metrics['predictions'][class_mask] == metrics['labels'][class_mask]) - metrics['accuracy_by_class'][class_idx] = class_accuracy + # Log validation metrics to TensorBoard + self.writer.add_scalar('Validation/Loss', epoch_loss, epoch) + self.writer.add_scalar('Validation/Accuracy', epoch_accuracy, epoch) + self.writer.add_scalar('Validation/AvgConfidence', metrics['avg_confidence'], epoch) - return avg_loss, accuracy, metrics + for class_idx, acc in metrics['class_accuracies'].items(): + self.writer.add_scalar(f'Validation/Class_{class_idx}_Accuracy', acc, epoch) + + return epoch_loss, epoch_accuracy, metrics - def train(self, symbols: List[str], save_path: Optional[str] = None) -> Dict: - """Train the CNN model""" + def calculate_detailed_metrics(self, predictions: List, labels: List, confidences: List) -> Dict: + """Calculate detailed training metrics""" + predictions = np.array(predictions) + labels = np.array(labels) + confidences = np.array(confidences) + + # Class-wise accuracies + class_accuracies = {} + for class_idx in range(self.n_classes): + class_mask = labels == class_idx + if class_mask.sum() > 0: + class_acc = (predictions[class_mask] == labels[class_mask]).mean() + class_accuracies[class_idx] = class_acc + + return { + 'class_accuracies': class_accuracies, + 'avg_confidence': confidences.mean(), + 'confusion_matrix': confusion_matrix(labels, predictions) + } + + def train(self, symbols: List[str], save_path: str = 'models/cnn/scalping_cnn_trained.pt', + num_samples: int = 10000) -> Dict: + """Train CNN model with TensorBoard monitoring""" logger.info("Starting CNN training...") + logger.info("Using ONLY real market data from exchange APIs") - # Prepare data first to get actual feature count - train_loader, val_loader, dataset_info = self.prepare_data(symbols) + # Prepare data + features, labels, metadata = self.prepare_data(symbols, num_samples) - # Create model with correct feature count + # Log training configuration + self.writer.add_text('Config/Symbols', str(symbols), 0) + self.writer.add_text('Config/Timeframes', str(self.config.timeframes), 0) + self.writer.add_scalar('Config/LearningRate', self.learning_rate, 0) + self.writer.add_scalar('Config/BatchSize', self.batch_size, 0) + self.writer.add_scalar('Config/MaxEpochs', self.epochs, 0) + + # Create datasets + dataset = CNNDataset(features, labels) + + # Split data + val_size = int(len(dataset) * self.validation_split) + train_size = len(dataset) - val_size + train_dataset, val_dataset = random_split(dataset, [train_size, val_size]) + + # Create data loaders + train_loader = DataLoader(train_dataset, batch_size=self.batch_size, shuffle=True) + val_loader = DataLoader(val_dataset, batch_size=self.batch_size, shuffle=False) + + logger.info(f"Total dataset: {len(dataset)} samples") + logger.info(f"Features shape: {features.shape}") + logger.info(f"Labels shape: {labels.shape}") + logger.info(f"Train samples: {train_size}") + logger.info(f"Validation samples: {val_size}") + + # Log class distributions + train_labels = [dataset[i][1].item() for i in train_dataset.indices] + val_labels = [dataset[i][1].item() for i in val_dataset.indices] + + logger.info(f"Train label distribution: {np.bincount(train_labels)}") + logger.info(f"Val label distribution: {np.bincount(val_labels)}") + + # Create model self.model = self.create_model() + self.log_model_architecture() # Setup training criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(self.model.parameters(), lr=self.learning_rate) - scheduler = optim.lr_scheduler.ReduceLROnPlateau( - optimizer, mode='min', factor=0.5, patience=5, verbose=True - ) + scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=5, verbose=True) - # Training state + # Training loop best_val_loss = float('inf') best_val_accuracy = 0.0 patience_counter = 0 start_time = time.time() - # Training loop - for epoch in range(self.num_epochs): - epoch_start_time = time.time() + for epoch in range(self.epochs): + epoch_start = time.time() # Train - train_loss, train_accuracy = self.train_epoch( - self.model, train_loader, optimizer, criterion - ) + train_loss, train_accuracy = self.train_epoch(self.model, train_loader, optimizer, criterion, epoch) # Validate - val_loss, val_accuracy, val_metrics = self.validate_epoch( - self.model, val_loader, criterion - ) + val_loss, val_accuracy, val_metrics = self.validate_epoch(self.model, val_loader, criterion, epoch) # Update learning rate scheduler.step(val_loss) + current_lr = optimizer.param_groups[0]['lr'] - # Track metrics - self.train_losses.append(train_loss) - self.val_losses.append(val_loss) - self.train_accuracies.append(train_accuracy) - self.val_accuracies.append(val_accuracy) + # Log epoch metrics + self.writer.add_scalar('Training/EpochLoss', train_loss, epoch) + self.writer.add_scalar('Training/EpochAccuracy', train_accuracy, epoch) + self.writer.add_scalar('Training/LearningRate', current_lr, epoch) - # Check for improvement + epoch_time = time.time() - epoch_start + self.writer.add_scalar('Training/EpochTime', epoch_time, epoch) + + # Save best model if val_loss < best_val_loss: best_val_loss = val_loss best_val_accuracy = val_accuracy patience_counter = 0 # Save best model - if save_path: - best_path = save_path.replace('.pt', '_best.pt') - self.model.save(best_path) - logger.info(f"New best model saved: {best_path}") + best_path = save_path.replace('.pt', '_best.pt') + self.model.save(best_path) + logger.info(f"New best model saved: {best_path}") + + # Log best metrics + self.writer.add_scalar('Best/ValidationLoss', best_val_loss, epoch) + self.writer.add_scalar('Best/ValidationAccuracy', best_val_accuracy, epoch) else: patience_counter += 1 - # Log progress - epoch_time = time.time() - epoch_start_time - logger.info( - f"Epoch {epoch+1}/{self.num_epochs} - " - f"Train Loss: {train_loss:.4f}, Train Acc: {train_accuracy:.4f} - " - f"Val Loss: {val_loss:.4f}, Val Acc: {val_accuracy:.4f} - " - f"Time: {epoch_time:.2f}s" - ) + logger.info(f"Epoch {epoch+1}/{self.epochs} - " + f"Train Loss: {train_loss:.4f}, Train Acc: {train_accuracy:.4f} - " + f"Val Loss: {val_loss:.4f}, Val Acc: {val_accuracy:.4f} - " + f"Time: {epoch_time:.2f}s") - # Detailed validation metrics every 10 epochs + # Log detailed metrics every 10 epochs if (epoch + 1) % 10 == 0: - logger.info(f"Class accuracies: {val_metrics['accuracy_by_class']}") + logger.info(f"Class accuracies: {val_metrics['class_accuracies']}") logger.info(f"Average confidence: {val_metrics['avg_confidence']:.4f}") # Early stopping - if patience_counter >= self.patience: + if patience_counter >= self.early_stopping_patience: logger.info(f"Early stopping triggered after {epoch+1} epochs") break - # Training complete + # Training completed total_time = time.time() - start_time logger.info(f"Training completed in {total_time:.2f} seconds") logger.info(f"Best validation loss: {best_val_loss:.4f}") logger.info(f"Best validation accuracy: {best_val_accuracy:.4f}") - # Save final model - if save_path: - self.model.save(save_path) - logger.info(f"Final model saved: {save_path}") + # Log final metrics + self.writer.add_scalar('Final/TotalTrainingTime', total_time, 0) + self.writer.add_scalar('Final/TotalEpochs', epoch + 1, 0) - # Prepare training results - results = { + # Save final model + self.model.save(save_path) + logger.info(f"Final model saved: {save_path}") + + # Log training summary + self.writer.add_text('Training/Summary', + f"Completed training with {len(features)} real market samples. " + f"Best validation accuracy: {best_val_accuracy:.4f}", 0) + + return { 'best_val_loss': best_val_loss, 'best_val_accuracy': best_val_accuracy, 'total_epochs': epoch + 1, - 'total_time': total_time, - 'train_losses': self.train_losses, - 'val_losses': self.val_losses, - 'train_accuracies': self.train_accuracies, - 'val_accuracies': self.val_accuracies, - 'dataset_info': dataset_info, - 'final_metrics': val_metrics + 'training_time': total_time, + 'tensorboard_dir': str(self.tensorboard_dir) } - - return results - def evaluate_model(self, test_symbols: List[str]) -> Dict: + def evaluate(self, symbols: List[str], num_samples: int = 5000) -> Dict: """Evaluate trained model on test data""" if self.model is None: raise ValueError("Model not trained yet") logger.info("Evaluating model...") - # Generate test data - test_features = [] - test_labels = [] + # Generate test data from real market data + features, labels, metadata = self.prepare_data(symbols, num_samples) - for symbol in test_symbols: - features, labels, _ = self.data_generator.generate_training_cases( - symbol, self.timeframes, 5000 - ) - if features is not None: - test_features.append(features) - test_labels.append(labels) - - if not test_features: - raise ValueError("No test data generated") - - test_features = np.concatenate(test_features, axis=0) - test_labels = np.concatenate(test_labels, axis=0) - - # Create test loader - test_dataset = TradingDataset(test_features, test_labels) + # Create test dataset and loader + test_dataset = CNNDataset(features, labels) test_loader = DataLoader(test_dataset, batch_size=self.batch_size, shuffle=False) # Evaluate criterion = nn.CrossEntropyLoss() test_loss, test_accuracy, test_metrics = self.validate_epoch( - self.model, test_loader, criterion + self.model, test_loader, criterion, epoch=0 ) - # Generate classification report + # Generate detailed classification report + from sklearn.metrics import classification_report class_names = ['BUY', 'SELL', 'HOLD'] - classification_rep = classification_report( - test_metrics['labels'], - test_metrics['predictions'], - target_names=class_names, - output_dict=True - ) + all_predictions = [] + all_labels = [] - # Confusion matrix - conf_matrix = confusion_matrix( - test_metrics['labels'], - test_metrics['predictions'] + with torch.no_grad(): + for features_batch, labels_batch in test_loader: + features_batch = features_batch.to(self.device) + predictions = self.model(features_batch) + _, predicted = torch.max(predictions['action'].data, 1) + all_predictions.extend(predicted.cpu().numpy()) + all_labels.extend(labels_batch.numpy()) + + classification_rep = classification_report( + all_labels, all_predictions, target_names=class_names, output_dict=True ) evaluation_results = { 'test_loss': test_loss, 'test_accuracy': test_accuracy, 'classification_report': classification_rep, - 'confusion_matrix': conf_matrix, - 'class_accuracies': test_metrics['accuracy_by_class'], - 'avg_confidence': test_metrics['avg_confidence'] + 'class_accuracies': test_metrics['class_accuracies'], + 'avg_confidence': test_metrics['avg_confidence'], + 'confusion_matrix': test_metrics['confusion_matrix'] } logger.info(f"Test accuracy: {test_accuracy:.4f}") @@ -480,40 +477,15 @@ class CNNTrainer: return evaluation_results - def plot_training_history(self, save_path: Optional[str] = None): - """Plot training history""" - if not self.train_losses: - logger.warning("No training history to plot") - return - - fig, ((ax1, ax2)) = plt.subplots(1, 2, figsize=(12, 4)) - - # Loss plot - epochs = range(1, len(self.train_losses) + 1) - ax1.plot(epochs, self.train_losses, 'b-', label='Training Loss') - ax1.plot(epochs, self.val_losses, 'r-', label='Validation Loss') - ax1.set_title('Training and Validation Loss') - ax1.set_xlabel('Epoch') - ax1.set_ylabel('Loss') - ax1.legend() - ax1.grid(True) - - # Accuracy plot - ax2.plot(epochs, self.train_accuracies, 'b-', label='Training Accuracy') - ax2.plot(epochs, self.val_accuracies, 'r-', label='Validation Accuracy') - ax2.set_title('Training and Validation Accuracy') - ax2.set_xlabel('Epoch') - ax2.set_ylabel('Accuracy') - ax2.legend() - ax2.grid(True) - - plt.tight_layout() - - if save_path: - plt.savefig(save_path, dpi=300, bbox_inches='tight') - logger.info(f"Training history plot saved: {save_path}") - - plt.show() + def close_tensorboard(self): + """Close TensorBoard writer""" + if hasattr(self, 'writer'): + self.writer.close() + logger.info("TensorBoard writer closed") + + def __del__(self): + """Cleanup""" + self.close_tensorboard() # Export -__all__ = ['CNNTrainer', 'TradingDataset'] \ No newline at end of file +__all__ = ['CNNTrainer', 'CNNDataset'] \ No newline at end of file diff --git a/training/rl_trainer.py b/training/rl_trainer.py index 2e083bd..d46c817 100644 --- a/training/rl_trainer.py +++ b/training/rl_trainer.py @@ -18,6 +18,7 @@ from pathlib import Path import matplotlib.pyplot as plt from collections import deque import random +from torch.utils.tensorboard import SummaryWriter # Add project imports import sys @@ -75,8 +76,23 @@ class RLTrainer: self.win_rates = [] self.avg_rewards = [] + # TensorBoard setup + self.setup_tensorboard() + logger.info(f"RLTrainer initialized for symbols: {self.symbols}") + def setup_tensorboard(self): + """Setup TensorBoard logging""" + # Create tensorboard logs directory + log_dir = Path("runs") / f"rl_training_{int(time.time())}" + log_dir.mkdir(parents=True, exist_ok=True) + + self.writer = SummaryWriter(log_dir=str(log_dir)) + self.tensorboard_dir = log_dir + + logger.info(f"TensorBoard logging to: {log_dir}") + logger.info(f"Run: tensorboard --logdir=runs") + def setup_environment_and_agent(self) -> Tuple[ScalpingEnvironment, ScalpingRLAgent]: """Setup trading environment and RL agent""" logger.info("Setting up environment and agent...") @@ -443,6 +459,29 @@ class RLTrainer: plt.show() + def log_episode_metrics(self, episode: int, metrics: Dict): + """Log episode metrics to TensorBoard""" + # Main performance metrics + self.writer.add_scalar('Episode/TotalReward', metrics['total_reward'], episode) + self.writer.add_scalar('Episode/FinalBalance', metrics['final_balance'], episode) + self.writer.add_scalar('Episode/TotalReturn', metrics['total_return'], episode) + self.writer.add_scalar('Episode/Steps', metrics['steps'], episode) + + # Trading metrics + self.writer.add_scalar('Trading/TotalTrades', metrics['total_trades'], episode) + self.writer.add_scalar('Trading/WinRate', metrics['win_rate'], episode) + self.writer.add_scalar('Trading/ProfitFactor', metrics.get('profit_factor', 0), episode) + self.writer.add_scalar('Trading/MaxDrawdown', metrics.get('max_drawdown', 0), episode) + + # Agent metrics + self.writer.add_scalar('Agent/Epsilon', metrics['epsilon'], episode) + self.writer.add_scalar('Agent/LearningRate', metrics.get('learning_rate', self.learning_rate), episode) + self.writer.add_scalar('Agent/MemorySize', metrics.get('memory_size', 0), episode) + + # Loss metrics (if available) + if 'loss' in metrics: + self.writer.add_scalar('Agent/Loss', metrics['loss'], episode) + class HybridTrainer: """ Hybrid training pipeline combining CNN and RL