added leverage, better training

This commit is contained in:
Dobromir Popov
2025-06-26 13:46:36 +03:00
parent 3a5a1056c4
commit b7ccd0f97b
8 changed files with 481 additions and 87 deletions

View File

@ -133,6 +133,11 @@ class CleanTradingDashboard:
self.total_fees = 0.0
self.current_position = None
# Leverage management - adjustable x1 to x100
self.current_leverage = 50 # Default x50 leverage
self.min_leverage = 1
self.max_leverage = 100
# WebSocket streaming
self.ws_price_cache = {}
self.is_streaming = False
@ -188,7 +193,6 @@ class CleanTradingDashboard:
# Start Universal Data Stream
if self.unified_stream:
import threading
threading.Thread(target=self._start_unified_stream, daemon=True).start()
logger.info("Universal Data Stream starting...")
@ -198,8 +202,20 @@ class CleanTradingDashboard:
# Start signal generation loop to ensure continuous trading signals
self._start_signal_generation_loop()
# Start training sessions if models are showing FRESH status
threading.Thread(target=self._delayed_training_check, daemon=True).start()
logger.info("Clean Trading Dashboard initialized with HIGH-FREQUENCY COB integration and signal generation")
def _delayed_training_check(self):
"""Check and start training after a delay to allow initialization"""
try:
time.sleep(10) # Wait 10 seconds for initialization
logger.info("Checking if models need training activation...")
self._start_actual_training_if_needed()
except Exception as e:
logger.error(f"Error in delayed training check: {e}")
def load_model_dynamically(self, model_name: str, model_type: str, model_path: Optional[str] = None) -> bool:
"""Dynamically load a model at runtime - Not implemented in orchestrator"""
logger.warning("Dynamic model loading not implemented in orchestrator")
@ -246,9 +262,9 @@ class CleanTradingDashboard:
[Output('current-price', 'children'),
Output('session-pnl', 'children'),
Output('current-position', 'children'),
Output('portfolio-value', 'children'),
Output('total-fees', 'children'),
# Output('leverage-info', 'children'),
Output('trade-count', 'children'),
Output('portfolio-value', 'children'),
Output('mexc-status', 'children')],
[Input('interval-component', 'n_intervals')]
)
@ -266,34 +282,34 @@ class CleanTradingDashboard:
# Calculate session P&L including unrealized P&L from current position
total_session_pnl = self.session_pnl # Start with realized P&L
# Add unrealized P&L from current position (x50 leverage)
# Add unrealized P&L from current position (adjustable leverage)
if self.current_position and current_price:
side = self.current_position.get('side', 'UNKNOWN')
size = self.current_position.get('size', 0)
entry_price = self.current_position.get('price', 0)
if entry_price and size > 0:
# Calculate unrealized P&L with x50 leverage
# Calculate unrealized P&L with current leverage
if side.upper() == 'LONG' or side.upper() == 'BUY':
raw_pnl_per_unit = current_price - entry_price
else: # SHORT or SELL
raw_pnl_per_unit = entry_price - current_price
# Apply x50 leverage to unrealized P&L
leveraged_unrealized_pnl = raw_pnl_per_unit * size * 50
# Apply current leverage to unrealized P&L
leveraged_unrealized_pnl = raw_pnl_per_unit * size * self.current_leverage
total_session_pnl += leveraged_unrealized_pnl
session_pnl_str = f"${total_session_pnl:.2f}"
session_pnl_class = "text-success" if total_session_pnl >= 0 else "text-danger"
# Current position with unrealized P&L (x50 leverage)
# Current position with unrealized P&L (adjustable leverage)
position_str = "No Position"
if self.current_position:
side = self.current_position.get('side', 'UNKNOWN')
size = self.current_position.get('size', 0)
entry_price = self.current_position.get('price', 0)
# Calculate unrealized P&L with x50 leverage
# Calculate unrealized P&L with current leverage
unrealized_pnl = 0.0
pnl_str = ""
pnl_class = ""
@ -305,9 +321,9 @@ class CleanTradingDashboard:
else: # SHORT or SELL
raw_pnl_per_unit = entry_price - current_price
# Apply x50 leverage to P&L calculation
# Apply current leverage to P&L calculation
# With leverage, P&L is amplified by the leverage factor
leveraged_pnl_per_unit = raw_pnl_per_unit * 50
leveraged_pnl_per_unit = raw_pnl_per_unit * self.current_leverage
unrealized_pnl = leveraged_pnl_per_unit * size
# Format P&L string with color
@ -318,20 +334,19 @@ class CleanTradingDashboard:
pnl_str = f" (${unrealized_pnl:.2f})"
pnl_class = "text-danger"
position_str = f"{side.upper()} {size:.3f} @ ${entry_price:.2f}{pnl_str}"
# Portfolio value
initial_balance = self._get_initial_balance()
portfolio_value = initial_balance + self.session_pnl
portfolio_str = f"${portfolio_value:.2f}"
# Total fees
fees_str = f"${self.total_fees:.3f}"
# Show position size in USD value instead of crypto amount
position_usd = size * entry_price
position_str = f"{side.upper()} ${position_usd:.2f} @ ${entry_price:.2f}{pnl_str} (x{self.current_leverage})"
# Trade count
trade_count = len(self.closed_trades)
trade_str = f"{trade_count} Trades"
# Portfolio value
initial_balance = self._get_initial_balance()
portfolio_value = initial_balance + total_session_pnl # Use total P&L including unrealized
portfolio_str = f"${portfolio_value:.2f}"
# MEXC status
mexc_status = "SIM"
if self.trading_executor:
@ -339,11 +354,11 @@ class CleanTradingDashboard:
if hasattr(self.trading_executor, 'simulation_mode') and not self.trading_executor.simulation_mode:
mexc_status = "LIVE"
return price_str, session_pnl_str, position_str, portfolio_str, fees_str, trade_str, mexc_status
return price_str, session_pnl_str, position_str, trade_str, portfolio_str, mexc_status
except Exception as e:
logger.error(f"Error updating metrics: {e}")
return "Error", "$0.00", "Error", "$100.00", "$0.00", "0", "ERROR"
return "Error", "$0.00", "Error", "0", "$100.00", "ERROR"
@self.app.callback(
Output('recent-decisions', 'children'),
@ -457,6 +472,18 @@ class CleanTradingDashboard:
self._execute_manual_trade('SELL')
return [html.I(className="fas fa-arrow-down me-1"), "SELL"]
# Leverage slider callback
@self.app.callback(
Output('leverage-display', 'children'),
[Input('leverage-slider', 'value')]
)
def update_leverage_display(leverage_value):
"""Update leverage display and internal leverage setting"""
if leverage_value:
self.current_leverage = leverage_value
return f"x{leverage_value}"
return "x50"
# Clear session button
@self.app.callback(
Output('clear-session-btn', 'children'),
@ -1179,9 +1206,10 @@ class CleanTradingDashboard:
except (TypeError, ZeroDivisionError):
return default_improvement
# 1. DQN Model Status - using orchestrator SSOT
# 1. DQN Model Status - using orchestrator SSOT with real training detection
dqn_state = model_states.get('dqn', {})
dqn_active = True
dqn_training_status = self._is_model_actually_training('dqn')
dqn_active = dqn_training_status['is_training']
dqn_prediction_count = len(self.recent_decisions) if signal_generation_active else 0
if signal_generation_active and len(self.recent_decisions) > 0:
@ -1189,7 +1217,7 @@ class CleanTradingDashboard:
last_action = self._get_signal_attribute(recent_signal, 'action', 'SIGNAL_GEN')
last_confidence = self._get_signal_attribute(recent_signal, 'confidence', 0.72)
else:
last_action = 'TRAINING'
last_action = dqn_training_status['status']
last_confidence = 0.68
dqn_model_info = {
@ -1200,19 +1228,21 @@ class CleanTradingDashboard:
'action': last_action,
'confidence': last_confidence
},
'loss_5ma': dqn_state.get('current_loss', 0.0145),
'loss_5ma': dqn_state.get('current_loss', dqn_state.get('initial_loss', 0.2850)),
'initial_loss': dqn_state.get('initial_loss', 0.2850),
'best_loss': dqn_state.get('best_loss', 0.0098),
'best_loss': dqn_state.get('best_loss', dqn_state.get('initial_loss', 0.2850)),
'improvement': safe_improvement_calc(
dqn_state.get('initial_loss', 0.2850),
dqn_state.get('current_loss', 0.0145),
94.9 # Default improvement percentage
dqn_state.get('current_loss', dqn_state.get('initial_loss', 0.2850)),
0.0 if not dqn_active else 94.9 # No improvement if not training
),
'checkpoint_loaded': dqn_state.get('checkpoint_loaded', False),
'model_type': 'DQN',
'description': 'Deep Q-Network Agent (Data Bus Input)',
'prediction_count': dqn_prediction_count,
'epsilon': 1.0
'epsilon': 1.0,
'training_evidence': dqn_training_status['evidence'],
'training_steps': dqn_training_status['training_steps']
}
loaded_models['dqn'] = dqn_model_info
@ -1353,6 +1383,71 @@ class CleanTradingDashboard:
logger.debug(f"Error checking signal generation status: {e}")
return False
def _is_model_actually_training(self, model_name: str) -> Dict[str, Any]:
"""Check if a model is actually training vs showing placeholder values"""
try:
training_status = {
'is_training': False,
'evidence': [],
'status': 'FRESH',
'last_update': None,
'training_steps': 0
}
if model_name == 'dqn' and self.orchestrator and hasattr(self.orchestrator, 'rl_agent'):
agent = self.orchestrator.rl_agent
if agent:
# Check for actual training evidence
if hasattr(agent, 'losses') and len(agent.losses) > 0:
training_status['is_training'] = True
training_status['evidence'].append(f"{len(agent.losses)} training losses recorded")
training_status['training_steps'] = len(agent.losses)
training_status['status'] = 'TRAINING'
if hasattr(agent, 'episode_count') and agent.episode_count > 0:
training_status['evidence'].append(f"Episode {agent.episode_count}")
if hasattr(agent, 'memory') and len(agent.memory) > 0:
training_status['evidence'].append(f"{len(agent.memory)} experiences in memory")
if hasattr(agent, 'epsilon') and agent.epsilon < 1.0:
training_status['evidence'].append(f"Epsilon decayed to {agent.epsilon:.3f}")
elif model_name == 'cnn' and self.orchestrator and hasattr(self.orchestrator, 'cnn_model'):
model = self.orchestrator.cnn_model
if model:
if hasattr(model, 'losses') and len(model.losses) > 0:
training_status['is_training'] = True
training_status['evidence'].append(f"{len(model.losses)} training losses")
training_status['training_steps'] = len(model.losses)
training_status['status'] = 'TRAINING'
elif model_name == 'extrema_trainer' and self.orchestrator and hasattr(self.orchestrator, 'extrema_trainer'):
trainer = self.orchestrator.extrema_trainer
if trainer:
if hasattr(trainer, 'training_losses') and len(trainer.training_losses) > 0:
training_status['is_training'] = True
training_status['evidence'].append(f"{len(trainer.training_losses)} training losses")
training_status['training_steps'] = len(trainer.training_losses)
training_status['status'] = 'TRAINING'
# If no evidence of training, mark as fresh/not training
if not training_status['evidence']:
training_status['status'] = 'FRESH'
training_status['evidence'].append("No training activity detected")
return training_status
except Exception as e:
logger.debug(f"Error checking training status for {model_name}: {e}")
return {
'is_training': False,
'evidence': [f"Error checking: {str(e)}"],
'status': 'ERROR',
'last_update': None,
'training_steps': 0
}
def _sync_position_from_executor(self, symbol: str):
"""Sync current position from trading executor"""
try:
@ -1366,7 +1461,7 @@ class CleanTradingDashboard:
'price': executor_position.get('price', 0),
'symbol': executor_position.get('symbol', symbol),
'entry_time': executor_position.get('entry_time', datetime.now()),
'leverage': 50,
'leverage': self.current_leverage, # Store current leverage with position
'unrealized_pnl': executor_position.get('unrealized_pnl', 0)
}
logger.debug(f"Synced position from executor: {self.current_position['side']} {self.current_position['size']:.3f}")
@ -1613,14 +1708,14 @@ class CleanTradingDashboard:
entry_price = self.current_position.get('price', 0)
if entry_price and size > 0:
# Calculate unrealized P&L with x50 leverage
# Calculate unrealized P&L with current leverage
if side.upper() == 'LONG':
raw_pnl_per_unit = current_price - entry_price
else: # SHORT
raw_pnl_per_unit = entry_price - current_price
# Apply x50 leverage to P&L calculation
leveraged_unrealized_pnl = raw_pnl_per_unit * size * 50
# Apply current leverage to P&L calculation
leveraged_unrealized_pnl = raw_pnl_per_unit * size * self.current_leverage
# Calculate profit incentive - bigger profits create stronger incentive to close
if leveraged_unrealized_pnl > 0:
@ -3258,6 +3353,175 @@ class CleanTradingDashboard:
logger.debug(f"Error getting BTC reference: {e}")
return None
def _start_actual_training_if_needed(self):
"""Start actual model training if models are showing FRESH status"""
try:
if not self.orchestrator:
logger.warning("No orchestrator available for training")
return
# Check if DQN needs training
dqn_status = self._is_model_actually_training('dqn')
if not dqn_status['is_training'] and hasattr(self.orchestrator, 'rl_agent') and self.orchestrator.rl_agent:
logger.info("DQN showing FRESH status - starting training session")
self._start_dqn_training_session()
# Check if CNN needs training
cnn_status = self._is_model_actually_training('cnn')
if not cnn_status['is_training'] and hasattr(self.orchestrator, 'cnn_model') and self.orchestrator.cnn_model:
logger.info("CNN showing FRESH status - starting training session")
self._start_cnn_training_session()
# Check if extrema trainer needs training
extrema_status = self._is_model_actually_training('extrema_trainer')
if not extrema_status['is_training'] and hasattr(self.orchestrator, 'extrema_trainer') and self.orchestrator.extrema_trainer:
logger.info("Extrema trainer showing FRESH status - starting training session")
self._start_extrema_training_session()
except Exception as e:
logger.error(f"Error starting training sessions: {e}")
def _start_dqn_training_session(self):
"""Start a DQN training session with real experiences"""
try:
if not self.orchestrator or not hasattr(self.orchestrator, 'rl_agent') or not self.orchestrator.rl_agent:
return
agent = self.orchestrator.rl_agent
# Add some initial experiences from recent trading if available
if len(self.closed_trades) > 0:
logger.info("Adding real trading experiences to DQN memory")
for trade in self.closed_trades[-10:]: # Last 10 trades
try:
# Create state representation from trade data
state = self._create_state_from_trade(trade)
action = 0 if trade.get('side') == 'BUY' else 1 # 0=BUY, 1=SELL
reward = trade.get('pnl', 0) * self.current_leverage # Scale by leverage
next_state = state # Simplified - same state
done = True # Trade completed
agent.remember(state, action, reward, next_state, done)
except Exception as e:
logger.debug(f"Error adding trade to DQN memory: {e}")
# Start training loop in background
def training_worker():
try:
logger.info("Starting DQN training worker")
for episode in range(50): # 50 training episodes
if len(agent.memory) >= agent.batch_size:
loss = agent.replay()
if loss is not None:
logger.debug(f"DQN training episode {episode}: loss={loss:.6f}")
time.sleep(0.1) # Small delay between episodes
logger.info("DQN training session completed")
except Exception as e:
logger.error(f"Error in DQN training worker: {e}")
import threading
training_thread = threading.Thread(target=training_worker, daemon=True)
training_thread.start()
except Exception as e:
logger.error(f"Error starting DQN training session: {e}")
def _start_cnn_training_session(self):
"""Start a CNN training session"""
try:
if not self.orchestrator or not hasattr(self.orchestrator, 'cnn_model') or not self.orchestrator.cnn_model:
return
# Start a simple CNN training session
def cnn_training_worker():
try:
logger.info("Starting CNN training worker")
model = self.orchestrator.cnn_model
# Simulate some training steps
if hasattr(model, 'train') and callable(model.train):
for step in range(20): # 20 training steps
try:
loss = model.train()
if loss is not None:
logger.debug(f"CNN training step {step}: loss={loss:.6f}")
except Exception as e:
logger.debug(f"CNN training step {step} failed: {e}")
time.sleep(0.2) # Small delay
logger.info("CNN training session completed")
except Exception as e:
logger.error(f"Error in CNN training worker: {e}")
import threading
training_thread = threading.Thread(target=cnn_training_worker, daemon=True)
training_thread.start()
except Exception as e:
logger.error(f"Error starting CNN training session: {e}")
def _start_extrema_training_session(self):
"""Start an extrema trainer training session"""
try:
if not self.orchestrator or not hasattr(self.orchestrator, 'extrema_trainer') or not self.orchestrator.extrema_trainer:
return
# Start extrema training session
def extrema_training_worker():
try:
logger.info("Starting extrema trainer worker")
trainer = self.orchestrator.extrema_trainer
# Run training if method available
if hasattr(trainer, 'train') and callable(trainer.train):
for step in range(15): # 15 training steps
try:
loss = trainer.train()
if loss is not None:
logger.debug(f"Extrema training step {step}: loss={loss:.6f}")
except Exception as e:
logger.debug(f"Extrema training step {step} failed: {e}")
time.sleep(0.3) # Small delay
logger.info("Extrema training session completed")
except Exception as e:
logger.error(f"Error in extrema training worker: {e}")
import threading
training_thread = threading.Thread(target=extrema_training_worker, daemon=True)
training_thread.start()
except Exception as e:
logger.error(f"Error starting extrema training session: {e}")
def _create_state_from_trade(self, trade) -> np.ndarray:
"""Create a state representation from trade data"""
try:
# Simple state representation (can be enhanced)
state = np.array([
trade.get('entry_price', 0) / 10000, # Normalized price
trade.get('exit_price', 0) / 10000, # Normalized price
trade.get('confidence', 0), # Confidence
trade.get('pnl', 0) / 10, # Normalized P&L
1.0 if trade.get('side') == 'BUY' else 0.0, # Side encoding
self.current_leverage / 100, # Normalized leverage
])
# Pad to expected state size if needed
if hasattr(self.orchestrator, 'rl_agent') and hasattr(self.orchestrator.rl_agent, 'state_dim'):
expected_size = self.orchestrator.rl_agent.state_dim
if isinstance(expected_size, int) and expected_size > len(state):
# Pad with zeros
padded_state = np.zeros(expected_size)
padded_state[:len(state)] = state
return padded_state
return state
except Exception as e:
logger.debug(f"Error creating state from trade: {e}")
return np.array([0.0] * 100) # Fallback state
def create_clean_dashboard(data_provider: Optional[DataProvider] = None, orchestrator: Optional[TradingOrchestrator] = None, trading_executor: Optional[TradingExecutor] = None):
"""Factory function to create a CleanTradingDashboard instance"""