ghost T predictions plotting on chart live chart updates

This commit is contained in:
Dobromir Popov
2025-11-22 01:14:32 +02:00
parent f967f0a142
commit d349a1bac0
4 changed files with 272 additions and 30 deletions

View File

@@ -16,12 +16,13 @@ import uuid
import time
import threading
import os
from typing import Dict, List, Optional, Any
from typing import Dict, List, Optional, Any, Tuple
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
from pathlib import Path
import torch
import numpy as np
try:
import pytz
@@ -2488,7 +2489,7 @@ class RealTrainingAdapter:
trainer = getattr(self.orchestrator, 'primary_transformer_trainer', None)
if trainer and trainer.model:
# Get recent market data
market_data = self._get_realtime_market_data(symbol, data_provider)
market_data, norm_params = self._get_realtime_market_data(symbol, data_provider)
if not market_data:
return None
@@ -2507,41 +2508,162 @@ class RealTrainingAdapter:
actions = ['BUY', 'SELL', 'HOLD']
action = actions[action_idx] if action_idx < len(actions) else 'HOLD'
# Handle predicted candles - DENORMALIZE them
predicted_candles_raw = {}
if 'next_candles' in outputs:
for tf, tensor in outputs['next_candles'].items():
predicted_candles_raw[tf] = tensor.detach().cpu().numpy().tolist()
# Denormalize if we have params
predicted_candles_denorm = {}
if predicted_candles_raw and norm_params:
for tf, raw_candle in predicted_candles_raw.items():
# raw_candle is [1, 5] list
if tf in norm_params:
params = norm_params[tf]
price_min = params['price_min']
price_max = params['price_max']
vol_min = params['volume_min']
vol_max = params['volume_max']
# Denormalize [Open, High, Low, Close, Volume]
# Note: raw_candle[0] is the list of 5 values
candle_values = raw_candle[0]
denorm_candle = [
candle_values[0] * (price_max - price_min) + price_min, # Open
candle_values[1] * (price_max - price_min) + price_min, # High
candle_values[2] * (price_max - price_min) + price_min, # Low
candle_values[3] * (price_max - price_min) + price_min, # Close
candle_values[4] * (vol_max - vol_min) + vol_min # Volume
]
predicted_candles_denorm[tf] = denorm_candle
# Calculate predicted price from candle close
predicted_price = None
if '1m' in predicted_candles_denorm:
predicted_price = predicted_candles_denorm['1m'][3] # Close price
elif '1s' in predicted_candles_denorm:
predicted_price = predicted_candles_denorm['1s'][3]
elif outputs.get('price_prediction') is not None:
# Fallback to price_prediction head if available (normalized)
# This would need separate denormalization based on reference price
pass
return {
'action': action,
'confidence': confidence
'confidence': confidence,
'predicted_price': predicted_price,
'predicted_candle': predicted_candles_denorm
}
return None
except Exception as e:
logger.debug(f"Error making realtime prediction: {e}")
import traceback
logger.debug(traceback.format_exc())
return None
def _get_realtime_market_data(self, symbol: str, data_provider) -> Dict:
"""Get current market data for prediction"""
def _get_realtime_market_data(self, symbol: str, data_provider) -> Tuple[Dict, Dict]:
"""
Get current market data for prediction AND normalization parameters
Returns:
Tuple of (model_inputs_dict, normalization_params_dict)
"""
try:
# Get recent candles for all timeframes
data = {}
norm_params = {}
for tf in ['1s', '1m', '1h', '1d']:
df = data_provider.get_historical_data(symbol, tf, limit=200)
# Get historical data (raw)
# Force refresh for 1s/1m to ensure we have the very latest candle for prediction
refresh = tf in ['1s', '1m']
df = data_provider.get_historical_data(symbol, tf, limit=600, refresh=refresh)
if df is not None and not df.empty:
# Convert to tensor format (simplified)
import torch
import numpy as np
# Extract raw arrays
opens = df['open'].values.astype(np.float32)
highs = df['high'].values.astype(np.float32)
lows = df['low'].values.astype(np.float32)
closes = df['close'].values.astype(np.float32)
volumes = df['volume'].values.astype(np.float32)
candles = df[['open', 'high', 'low', 'close', 'volume']].values
candles_tensor = torch.tensor(candles, dtype=torch.float32).unsqueeze(0)
# Need at least 1 candle
if len(closes) == 0:
continue
# Prepare OHLCV for normalization logic
# Padding if needed (though limit=600 usually suffices)
if len(closes) < 600:
pad_len = 600 - len(closes)
# Pad with first value
opens = np.pad(opens, (pad_len, 0), mode='edge')
highs = np.pad(highs, (pad_len, 0), mode='edge')
lows = np.pad(lows, (pad_len, 0), mode='edge')
closes = np.pad(closes, (pad_len, 0), mode='edge')
volumes = np.pad(volumes, (pad_len, 0), mode='edge')
else:
# Take last 600
opens = opens[-600:]
highs = highs[-600:]
lows = lows[-600:]
closes = closes[-600:]
volumes = volumes[-600:]
# Stack OHLCV [seq_len, 5]
ohlcv = np.stack([opens, highs, lows, closes, volumes], axis=-1)
# Calculate min/max for normalization
price_min = np.min(ohlcv[:, :4])
price_max = np.max(ohlcv[:, :4])
volume_min = np.min(ohlcv[:, 4])
volume_max = np.max(ohlcv[:, 4])
# Avoid division by zero
if price_max == price_min: price_max += 1.0
if volume_max == volume_min: volume_max += 1.0
# Store params for denormalization later
norm_params[tf] = {
'price_min': float(price_min),
'price_max': float(price_max),
'volume_min': float(volume_min),
'volume_max': float(volume_max)
}
# Normalize in-place
ohlcv[:, :4] = (ohlcv[:, :4] - price_min) / (price_max - price_min)
ohlcv[:, 4] = (ohlcv[:, 4] - volume_min) / (volume_max - volume_min)
# Convert to tensor [1, seq_len, 5]
import torch
candles_tensor = torch.tensor(ohlcv, dtype=torch.float32).unsqueeze(0)
data[f'price_data_{tf}'] = candles_tensor
# Add placeholder data for other inputs
import torch
data['tech_data'] = torch.zeros(1, 40, dtype=torch.float32)
data['market_data'] = torch.zeros(1, 30, dtype=torch.float32)
# Add placeholder data for other inputs if we have at least one timeframe
if data:
import torch
# Correct shapes based on model expectation
# tech_data: [1, 40]
# market_data: [1, 30]
# cob_data: [1, 600, 100]
data['tech_data'] = torch.zeros(1, 40, dtype=torch.float32)
data['market_data'] = torch.zeros(1, 30, dtype=torch.float32)
data['cob_data'] = torch.zeros(1, 600, 100, dtype=torch.float32)
# Move to device if available
if hasattr(self.orchestrator, 'device'):
device = self.orchestrator.device
for k, v in data.items():
data[k] = v.to(device)
return data if data else None
return data, norm_params if data else (None, None)
except Exception as e:
logger.debug(f"Error getting realtime market data: {e}")
return None
import traceback
logger.debug(traceback.format_exc())
return None, None
def _train_on_new_candle(self, session: Dict, symbol: str, timeframe: str, data_provider):
"""Train model on new candle when it closes"""
@@ -2734,7 +2856,9 @@ class RealTrainingAdapter:
'model': model_name,
'action': prediction['action'],
'confidence': prediction['confidence'],
'price': current_price
'price': current_price,
'predicted_price': prediction.get('predicted_price'),
'predicted_candle': prediction.get('predicted_candle')
}
session['signals'].append(signal)