ui state, models toggle

This commit is contained in:
Dobromir Popov
2025-07-28 23:49:47 +03:00
parent a341fade80
commit 548c0d5e0f
3 changed files with 411 additions and 231 deletions

View File

@ -50,7 +50,7 @@ exchanges:
bybit:
enabled: true
test_mode: false # Use mainnet (your credentials are for live trading)
trading_mode: "live" # simulation, testnet, live - SWITCHED TO SIMULATION FOR TRAINING
trading_mode: "live" # simulation, testnet, live
supported_symbols: ["BTCUSDT", "ETHUSDT"] # Bybit perpetual format
base_position_percent: 5.0
max_position_percent: 20.0
@ -163,7 +163,7 @@ orchestrator:
# Decision Fusion Configuration
decision_fusion:
enabled: true # Use neural network decision fusion instead of programmatic
mode: "programmatic" # "neural" or "programmatic"
mode: "neural" # "neural" or "programmatic"
input_size: 128 # Size of input features for decision fusion network
hidden_size: 256 # Hidden layer size
history_length: 20 # Number of recent decisions to include
@ -327,7 +327,7 @@ logs_dir: "logs"
# GPU/Performance
gpu:
enabled: true
enabled: true
memory_fraction: 0.8 # Use 80% of GPU memory
allow_growth: true # Allow dynamic memory allocation

View File

@ -284,7 +284,19 @@ class TradingOrchestrator:
self.enhanced_rl_training = enhanced_rl_training
# Determine the device to use (GPU if available, else CPU)
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# Initialize device - force CPU mode to avoid CUDA errors
if torch.cuda.is_available():
try:
# Test CUDA availability
test_tensor = torch.tensor([1.0]).cuda()
self.device = torch.device("cuda")
logger.info("CUDA device initialized successfully")
except Exception as e:
logger.warning(f"CUDA initialization failed: {e}, falling back to CPU")
self.device = torch.device("cpu")
else:
self.device = torch.device("cpu")
logger.info(f"Using device: {self.device}")
# Configuration - AGGRESSIVE for more training data
@ -389,7 +401,20 @@ class TradingOrchestrator:
self.fusion_training_history: List[Any] = []
self.last_fusion_inputs: Dict[str, Any] = (
{}
) # Fix: Explicitly initialize as dictionary
)
# Model toggle states - control which models contribute to decisions
self.model_toggle_states = {
"dqn": {"inference_enabled": True, "training_enabled": True},
"cnn": {"inference_enabled": True, "training_enabled": True},
"cob_rl": {"inference_enabled": True, "training_enabled": True},
"decision_fusion": {"inference_enabled": True, "training_enabled": True},
"transformer": {"inference_enabled": True, "training_enabled": True},
}
# UI state persistence
self.ui_state_file = "data/ui_state.json"
self._load_ui_state() # Fix: Explicitly initialize as dictionary
self.fusion_checkpoint_frequency: int = 50 # Save every 50 decisions
self.fusion_decisions_count: int = 0
self.fusion_training_data: List[Any] = (
@ -1309,6 +1334,57 @@ class TradingOrchestrator:
else:
logger.info("No saved orchestrator state found. Starting fresh.")
def _load_ui_state(self):
"""Load UI state from file"""
try:
if os.path.exists(self.ui_state_file):
with open(self.ui_state_file, "r") as f:
ui_state = json.load(f)
if "model_toggle_states" in ui_state:
self.model_toggle_states.update(ui_state["model_toggle_states"])
logger.info(f"UI state loaded from {self.ui_state_file}")
except Exception as e:
logger.error(f"Error loading UI state: {e}")
def _save_ui_state(self):
"""Save UI state to file"""
try:
os.makedirs(os.path.dirname(self.ui_state_file), exist_ok=True)
ui_state = {
"model_toggle_states": self.model_toggle_states,
"timestamp": datetime.now().isoformat()
}
with open(self.ui_state_file, "w") as f:
json.dump(ui_state, f, indent=4)
logger.debug(f"UI state saved to {self.ui_state_file}")
except Exception as e:
logger.error(f"Error saving UI state: {e}")
def get_model_toggle_state(self, model_name: str) -> Dict[str, bool]:
"""Get toggle state for a model"""
return self.model_toggle_states.get(model_name, {"inference_enabled": True, "training_enabled": True})
def set_model_toggle_state(self, model_name: str, inference_enabled: bool = None, training_enabled: bool = None):
"""Set toggle state for a model"""
if model_name not in self.model_toggle_states:
self.model_toggle_states[model_name] = {"inference_enabled": True, "training_enabled": True}
if inference_enabled is not None:
self.model_toggle_states[model_name]["inference_enabled"] = inference_enabled
if training_enabled is not None:
self.model_toggle_states[model_name]["training_enabled"] = training_enabled
self._save_ui_state()
logger.info(f"Model {model_name} toggle state updated: inference={self.model_toggle_states[model_name]['inference_enabled']}, training={self.model_toggle_states[model_name]['training_enabled']}")
def is_model_inference_enabled(self, model_name: str) -> bool:
"""Check if model inference is enabled"""
return self.model_toggle_states.get(model_name, {}).get("inference_enabled", True)
def is_model_training_enabled(self, model_name: str) -> bool:
"""Check if model training is enabled"""
return self.model_toggle_states.get(model_name, {}).get("training_enabled", True)
async def start_continuous_trading(self, symbols: Optional[List[str]] = None):
"""Start the continuous trading loop, using a decision model and trading executor"""
if symbols is None:
@ -1846,11 +1922,14 @@ class TradingOrchestrator:
logger.debug(f"No fallback prediction available for {symbol}")
return None
# Choose decision method based on configuration
# Choose decision method based on configuration and toggle state
decision_fusion_inference_enabled = self.is_model_inference_enabled("decision_fusion")
if (
self.decision_fusion_enabled
and self.decision_fusion_mode == "neural"
and self.decision_fusion_network is not None
and decision_fusion_inference_enabled
):
# Use neural decision fusion
decision = self._make_decision_fusion_decision(
@ -1861,6 +1940,11 @@ class TradingOrchestrator:
)
else:
# Use programmatic decision combination
if not decision_fusion_inference_enabled:
logger.info(f"Decision fusion model disabled, using programmatic mode for {symbol}")
else:
logger.debug(f"Using programmatic decision combination for {symbol}")
decision = self._combine_predictions(
symbol=symbol,
price=current_price,
@ -4490,8 +4574,13 @@ class TradingOrchestrator:
action_scores = {"BUY": 0.0, "SELL": 0.0, "HOLD": 0.0}
total_weight = 0.0
# Process all predictions
# Process all predictions (filter out disabled models)
for pred in predictions:
# Check if model inference is enabled
if not self.is_model_inference_enabled(pred.model_name):
logger.debug(f"Skipping disabled model {pred.model_name} in decision making")
continue
# Get model weight
model_weight = self.model_weights.get(pred.model_name, 0.1)
@ -4914,16 +5003,17 @@ class TradingOrchestrator:
self.fc4 = nn.Linear(hidden_size // 2, 3) # BUY, SELL, HOLD
self.dropout = nn.Dropout(0.3)
self.batch_norm1 = nn.BatchNorm1d(hidden_size)
self.batch_norm2 = nn.BatchNorm1d(hidden_size)
self.batch_norm3 = nn.BatchNorm1d(hidden_size // 2)
# Use LayerNorm instead of BatchNorm1d for single-sample training compatibility
self.layer_norm1 = nn.LayerNorm(hidden_size)
self.layer_norm2 = nn.LayerNorm(hidden_size)
self.layer_norm3 = nn.LayerNorm(hidden_size // 2)
def forward(self, x):
x = torch.relu(self.batch_norm1(self.fc1(x)))
x = torch.relu(self.layer_norm1(self.fc1(x)))
x = self.dropout(x)
x = torch.relu(self.batch_norm2(self.fc2(x)))
x = torch.relu(self.layer_norm2(self.fc2(x)))
x = self.dropout(x)
x = torch.relu(self.batch_norm3(self.fc3(x)))
x = torch.relu(self.layer_norm3(self.fc3(x)))
x = self.dropout(x)
return torch.softmax(self.fc4(x), dim=1)
@ -4980,18 +5070,30 @@ class TradingOrchestrator:
try:
from utils.checkpoint_manager import load_best_checkpoint
result = load_best_checkpoint("decision", "decision")
if result:
file_path, metadata = result
self.decision_fusion_network.load(file_path)
self.model_states["decision"]["checkpoint_loaded"] = True
self.model_states["decision"][
"checkpoint_filename"
] = metadata.checkpoint_id
logger.info(
f"Decision fusion network loaded from checkpoint: {metadata.checkpoint_id}"
)
else:
# Try multiple checkpoint names for decision fusion
checkpoint_names = ["decision_fusion", "decision", "fusion"]
checkpoint_loaded = False
for checkpoint_name in checkpoint_names:
try:
result = load_best_checkpoint(checkpoint_name, checkpoint_name)
if result:
file_path, metadata = result
self.decision_fusion_network.load(file_path)
self.model_states["decision"]["checkpoint_loaded"] = True
self.model_states["decision"][
"checkpoint_filename"
] = metadata.checkpoint_id
logger.info(
f"Decision fusion network loaded from checkpoint: {metadata.checkpoint_id}"
)
checkpoint_loaded = True
break
except Exception as e:
logger.debug(f"Failed to load checkpoint '{checkpoint_name}': {e}")
continue
if not checkpoint_loaded:
logger.info(
"No existing decision fusion checkpoint found, starting fresh"
)
@ -5113,6 +5215,15 @@ class TradingOrchestrator:
elif len(features) > expected_size:
features = features[:expected_size]
# Log input feature statistics for debugging
if len(features) > 0:
feature_array = np.array(features)
logger.debug(f"Decision fusion input features: size={len(features)}, "
f"mean={np.mean(feature_array):.4f}, "
f"std={np.std(feature_array):.4f}, "
f"min={np.min(feature_array):.4f}, "
f"max={np.max(feature_array):.4f}")
return torch.tensor(
features, dtype=torch.float32, device=self.device
).unsqueeze(0)
@ -5399,6 +5510,15 @@ class TradingOrchestrator:
if input_features is None:
logger.warning("No input features found for decision fusion training")
return
# Validate input features
if not isinstance(input_features, torch.Tensor):
logger.warning(f"Invalid input features type: {type(input_features)}")
return
if input_features.dim() != 2 or input_features.size(0) != 1:
logger.warning(f"Invalid input features shape: {input_features.shape}")
return
# Create target based on outcome
predicted_action = record.get("action", "HOLD")
@ -5433,24 +5553,19 @@ class TradingOrchestrator:
optimizer.zero_grad()
# Temporarily disable batch normalization for single sample training
for module in self.decision_fusion_network.modules():
if isinstance(module, nn.BatchNorm1d):
module.eval() # Use running statistics instead of batch statistics
# Forward pass - handle single sample properly
# Forward pass - LayerNorm works with single samples
output = self.decision_fusion_network(input_features)
loss = criterion(output, target.unsqueeze(0))
# Log training details for debugging
logger.debug(f"Decision fusion training: input_shape={input_features.shape}, "
f"output_shape={output.shape}, target_shape={target.unsqueeze(0).shape}, "
f"loss={loss.item():.4f}")
# Backward pass
loss.backward()
optimizer.step()
# Re-enable batch normalization for future training
for module in self.decision_fusion_network.modules():
if isinstance(module, nn.BatchNorm1d):
module.train()
# Set back to eval mode for inference
self.decision_fusion_network.eval()
@ -6241,8 +6356,8 @@ class TradingOrchestrator:
# Track if any model was trained for checkpoint saving
models_trained = []
# Train DQN agent if available
if self.rl_agent and hasattr(self.rl_agent, "add_experience"):
# Train DQN agent if available and enabled
if self.rl_agent and hasattr(self.rl_agent, "add_experience") and self.is_model_training_enabled("dqn"):
try:
# Create state representation
state = self._create_state_for_training(symbol, market_data)
@ -6271,8 +6386,8 @@ class TradingOrchestrator:
except Exception as e:
logger.debug(f"Error training DQN on decision: {e}")
# Train CNN model if available
if self.cnn_model and hasattr(self.cnn_model, "add_training_sample"):
# Train CNN model if available and enabled
if self.cnn_model and hasattr(self.cnn_model, "add_training_sample") and self.is_model_training_enabled("cnn"):
try:
# Create CNN input features
cnn_features = self._create_cnn_features_for_training(
@ -6298,8 +6413,8 @@ class TradingOrchestrator:
except Exception as e:
logger.debug(f"Error training CNN on decision: {e}")
# Train COB RL model if available and we have COB data
if self.cob_rl_agent and symbol in self.latest_cob_data:
# Train COB RL model if available, enabled, and we have COB data
if self.cob_rl_agent and symbol in self.latest_cob_data and self.is_model_training_enabled("cob_rl"):
try:
cob_data = self.latest_cob_data[symbol]
if hasattr(self.cob_rl_agent, "add_experience"):
@ -6322,6 +6437,33 @@ class TradingOrchestrator:
except Exception as e:
logger.debug(f"Error training COB RL on decision: {e}")
# Train decision fusion model if available and enabled
if self.decision_fusion_network and self.is_model_training_enabled("decision_fusion"):
try:
# Create decision fusion input
fusion_input = self._create_decision_fusion_training_input(
symbol, market_data
)
# Create target based on action
target_mapping = {
"BUY": [1, 0, 0],
"SELL": [0, 1, 0],
"HOLD": [0, 0, 1],
}
target = target_mapping.get(action, [0, 0, 1])
# Add training sample
self.decision_fusion_network.add_training_sample(
fusion_input, target, weight=confidence
)
models_trained.append("decision_fusion")
logger.debug(f"🤝 Added decision fusion training sample: {action} {symbol}")
except Exception as e:
logger.debug(f"Error training decision fusion on decision: {e}")
# CRITICAL FIX: Save checkpoints after training
if models_trained:
self._save_training_checkpoints(models_trained, confidence)
@ -6363,6 +6505,10 @@ class TradingOrchestrator:
model_obj = self.cob_rl_agent
current_loss = 1.0 - performance_score
elif model_name == "decision_fusion" and self.decision_fusion_network:
model_obj = self.decision_fusion_network
current_loss = 1.0 - performance_score
if model_obj and current_loss is not None:
# Check if this is the best performance so far
model_state = self.model_states.get(model_name, {})
@ -6372,11 +6518,11 @@ class TradingOrchestrator:
model_state["current_loss"] = current_loss
model_state["last_training"] = datetime.now()
# Save checkpoint if performance improved or periodic save
# Save checkpoint if performance improved or every 3rd training
should_save = (
current_loss < best_loss # Performance improved
or self.training_iterations % 100
== 0 # Periodic save every 100 iterations
or self.training_iterations % 3
== 0 # Save every 3rd training iteration
)
if should_save:
@ -6548,6 +6694,38 @@ class TradingOrchestrator:
logger.debug(f"Error creating COB state for training: {e}")
return np.zeros(2000)
def _create_decision_fusion_training_input(self, symbol: str, market_data: Dict) -> np.ndarray:
"""Create decision fusion training input from market data"""
try:
# Extract features from market data
ohlcv_data = market_data.get("ohlcv", [])
if not ohlcv_data:
return np.zeros(100) # Default state size
# Extract features from recent candles
features = []
for candle in ohlcv_data[-20:]: # Last 20 candles
features.extend(
[
candle.get("open", 0),
candle.get("high", 0),
candle.get("low", 0),
candle.get("close", 0),
candle.get("volume", 0),
]
)
# Pad or truncate to expected size
state = np.array(features[:100])
if len(state) < 100:
state = np.pad(state, (0, 100 - len(state)), "constant")
return state
except Exception as e:
logger.debug(f"Error creating decision fusion input: {e}")
return np.zeros(100)
def _check_signal_confirmation(
self, symbol: str, signal_data: Dict
) -> Optional[str]:
@ -6684,175 +6862,6 @@ class TradingOrchestrator:
except Exception as e:
logger.error(f"Database cleanup failed: {e}")
def _save_training_checkpoints(
self, models_trained: List[str], performance_score: float
):
"""Save checkpoints for trained models if performance improved
This is CRITICAL for preserving training progress across restarts.
"""
try:
if not self.checkpoint_manager:
return
# Increment training counter
self.training_iterations += 1
# Save checkpoints for each trained model
for model_name in models_trained:
try:
model_obj = None
current_loss = None
model_type = model_name
# Get model object and calculate current performance
if model_name == "dqn" and self.rl_agent:
model_obj = self.rl_agent
# Use current loss from model state or estimate from performance
current_loss = self.model_states["dqn"].get("current_loss")
if current_loss is None:
# Estimate loss from performance score (inverse relationship)
current_loss = max(0.001, 1.0 - performance_score)
# Update model state tracking
self.model_states["dqn"]["current_loss"] = current_loss
# If this is the first loss value, set it as initial and best
if self.model_states["dqn"]["initial_loss"] is None:
self.model_states["dqn"]["initial_loss"] = current_loss
if (
self.model_states["dqn"]["best_loss"] is None
or current_loss < self.model_states["dqn"]["best_loss"]
):
self.model_states["dqn"]["best_loss"] = current_loss
elif model_name == "cnn" and self.cnn_model:
model_obj = self.cnn_model
# Use current loss from model state or estimate from performance
current_loss = self.model_states["cnn"].get("current_loss")
if current_loss is None:
# Estimate loss from performance score (inverse relationship)
current_loss = max(0.001, 1.0 - performance_score)
# Update model state tracking
self.model_states["cnn"]["current_loss"] = current_loss
# If this is the first loss value, set it as initial and best
if self.model_states["cnn"]["initial_loss"] is None:
self.model_states["cnn"]["initial_loss"] = current_loss
if (
self.model_states["cnn"]["best_loss"] is None
or current_loss < self.model_states["cnn"]["best_loss"]
):
self.model_states["cnn"]["best_loss"] = current_loss
elif model_name == "cob_rl" and self.cob_rl_agent:
model_obj = self.cob_rl_agent
# Use current loss from model state or estimate from performance
current_loss = self.model_states["cob_rl"].get("current_loss")
if current_loss is None:
# Estimate loss from performance score (inverse relationship)
current_loss = max(0.001, 1.0 - performance_score)
# Update model state tracking
self.model_states["cob_rl"]["current_loss"] = current_loss
# If this is the first loss value, set it as initial and best
if self.model_states["cob_rl"]["initial_loss"] is None:
self.model_states["cob_rl"]["initial_loss"] = current_loss
if (
self.model_states["cob_rl"]["best_loss"] is None
or current_loss < self.model_states["cob_rl"]["best_loss"]
):
self.model_states["cob_rl"]["best_loss"] = current_loss
elif (
model_name == "extrema"
and hasattr(self, "extrema_trainer")
and self.extrema_trainer
):
model_obj = self.extrema_trainer
# Use current loss from model state or estimate from performance
current_loss = self.model_states["extrema"].get("current_loss")
if current_loss is None:
# Estimate loss from performance score (inverse relationship)
current_loss = max(0.001, 1.0 - performance_score)
# Update model state tracking
self.model_states["extrema"]["current_loss"] = current_loss
# If this is the first loss value, set it as initial and best
if self.model_states["extrema"]["initial_loss"] is None:
self.model_states["extrema"]["initial_loss"] = current_loss
if (
self.model_states["extrema"]["best_loss"] is None
or current_loss < self.model_states["extrema"]["best_loss"]
):
self.model_states["extrema"]["best_loss"] = current_loss
# Skip if we couldn't get a model object
if model_obj is None:
continue
# Prepare performance metrics for checkpoint
performance_metrics = {
"loss": current_loss,
"accuracy": performance_score, # Use confidence as a proxy for accuracy
}
# Prepare training metadata
training_metadata = {
"training_iteration": self.training_iterations,
"timestamp": datetime.now().isoformat(),
}
# Save checkpoint using checkpoint manager
from utils.checkpoint_manager import save_checkpoint
checkpoint_metadata = save_checkpoint(
model=model_obj,
model_name=model_name,
model_type=model_type,
performance_metrics=performance_metrics,
training_metadata=training_metadata,
)
if checkpoint_metadata:
logger.info(
f"Saved checkpoint for {model_name}: {checkpoint_metadata.checkpoint_id} (loss={current_loss:.4f})"
)
# Also save periodically based on training iterations
if self.training_iterations % 100 == 0:
# Force save every 100 training iterations regardless of performance
checkpoint_metadata = save_checkpoint(
model=model_obj,
model_name=model_name,
model_type=model_type,
performance_metrics=performance_metrics,
training_metadata=training_metadata,
force_save=True,
)
if checkpoint_metadata:
logger.info(
f"Periodic checkpoint saved for {model_name}: {checkpoint_metadata.checkpoint_id}"
)
except Exception as e:
logger.error(f"Error saving checkpoint for {model_name}: {e}")
except Exception as e:
logger.error(f"Error in _save_training_checkpoints: {e}")
def _schedule_database_cleanup(self):
"""Schedule periodic database cleanup"""
try:
# Clean up old inference records (keep 30 days)
self.inference_logger.cleanup_old_logs(days_to_keep=30)
logger.info("Database cleanup completed")
except Exception as e:
logger.error(f"Database cleanup failed: {e}")
def log_model_inference(
self,
model_name: str,

View File

@ -1137,6 +1137,19 @@ class CleanTradingDashboard:
[Input('slow-interval-component', 'n_intervals')] # OPTIMIZED: Move to 10s interval
)
def update_training_metrics(n):
# Get toggle states from orchestrator
toggle_states = {}
if self.orchestrator:
for model_name in ["dqn", "cnn", "cob_rl", "decision_fusion"]:
toggle_states[model_name] = self.orchestrator.get_model_toggle_state(model_name)
else:
# Fallback to dashboard state
toggle_states = {
"dqn": {"inference_enabled": self.dqn_inference_enabled, "training_enabled": self.dqn_training_enabled},
"cnn": {"inference_enabled": self.cnn_inference_enabled, "training_enabled": self.cnn_training_enabled},
"cob_rl": {"inference_enabled": True, "training_enabled": True},
"decision_fusion": {"inference_enabled": True, "training_enabled": True}
}
"""Update training metrics"""
try:
# Now using slow-interval-component (10s) - no batching needed
@ -1295,6 +1308,93 @@ class CleanTradingDashboard:
[Input('cold-start-switch', 'value')]
)
def update_cold_start(switch_value):
if switch_value:
self.cold_start_enabled = True
return "Cold Start: ON", "badge bg-success"
else:
self.cold_start_enabled = False
return "Cold Start: OFF", "badge bg-secondary"
# Model toggle callbacks
@self.app.callback(
Output('dqn-inference-toggle', 'value'),
[Input('dqn-inference-toggle', 'value')],
prevent_initial_call=True
)
def update_dqn_inference_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("dqn", inference_enabled=value)
return value
@self.app.callback(
Output('dqn-training-toggle', 'value'),
[Input('dqn-training-toggle', 'value')],
prevent_initial_call=True
)
def update_dqn_training_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("dqn", training_enabled=value)
return value
@self.app.callback(
Output('cnn-inference-toggle', 'value'),
[Input('cnn-inference-toggle', 'value')],
prevent_initial_call=True
)
def update_cnn_inference_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("cnn", inference_enabled=value)
return value
@self.app.callback(
Output('cnn-training-toggle', 'value'),
[Input('cnn-training-toggle', 'value')],
prevent_initial_call=True
)
def update_cnn_training_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("cnn", training_enabled=value)
return value
@self.app.callback(
Output('cob-rl-inference-toggle', 'value'),
[Input('cob-rl-inference-toggle', 'value')],
prevent_initial_call=True
)
def update_cob_rl_inference_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("cob_rl", inference_enabled=value)
return value
@self.app.callback(
Output('cob-rl-training-toggle', 'value'),
[Input('cob-rl-training-toggle', 'value')],
prevent_initial_call=True
)
def update_cob_rl_training_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("cob_rl", training_enabled=value)
return value
@self.app.callback(
Output('decision-fusion-inference-toggle', 'value'),
[Input('decision-fusion-inference-toggle', 'value')],
prevent_initial_call=True
)
def update_decision_fusion_inference_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("decision_fusion", inference_enabled=value)
return value
@self.app.callback(
Output('decision-fusion-training-toggle', 'value'),
[Input('decision-fusion-training-toggle', 'value')],
prevent_initial_call=True
)
def update_decision_fusion_training_toggle(value):
if self.orchestrator:
self.orchestrator.set_model_toggle_state("decision_fusion", training_enabled=value)
return value
"""Update cold start training mode"""
logger.debug(f"Cold start callback triggered with value: {switch_value}")
try:
@ -3285,8 +3385,9 @@ class CleanTradingDashboard:
dqn_timing = get_model_timing_info('DQN')
# SEPARATE TOGGLES: Inference and Training can be controlled independently
dqn_inference_enabled = getattr(self, 'dqn_inference_enabled', True) # Default: enabled
dqn_training_enabled = getattr(self, 'dqn_training_enabled', True) # Default: enabled
dqn_toggle_state = toggle_states.get('dqn', {"inference_enabled": True, "training_enabled": True})
dqn_inference_enabled = dqn_toggle_state.get("inference_enabled", True)
dqn_training_enabled = dqn_toggle_state.get("training_enabled", True)
dqn_checkpoint_loaded = dqn_state.get('checkpoint_loaded', False)
# DQN is active if checkpoint is loaded AND inference is enabled AND orchestrator has the model
@ -3366,7 +3467,16 @@ class CleanTradingDashboard:
'average_training_time_ms': f"{dqn_timing.get('average_training_time_ms', 0):.1f}"
},
# NEW: Performance metrics for split-second decisions
'performance': self.get_model_performance_metrics().get('dqn', {})
'performance': self.get_model_performance_metrics().get('dqn', {}),
# ENHANCED: Add toggle state information
'inference_enabled': dqn_inference_enabled,
'training_enabled': dqn_training_enabled,
'status_details': {
'checkpoint_loaded': dqn_checkpoint_loaded,
'inference_enabled': dqn_inference_enabled,
'training_enabled': dqn_training_enabled,
'is_training': dqn_training_status['is_training']
}
}
loaded_models['dqn'] = dqn_model_info
@ -3392,6 +3502,11 @@ class CleanTradingDashboard:
except:
cnn_predicted_price = 0
# Get CNN toggle states
cnn_toggle_state = toggle_states.get('cnn', {"inference_enabled": True, "training_enabled": True})
cnn_inference_enabled = cnn_toggle_state.get("inference_enabled", True)
cnn_training_enabled = cnn_toggle_state.get("training_enabled", True)
cnn_model_info = {
'active': cnn_active,
'parameters': 50000000, # ~50M params
@ -3443,7 +3558,16 @@ class CleanTradingDashboard:
'average_training_time_ms': f"{cnn_timing.get('average_training_time_ms', 0):.1f}"
},
# NEW: Performance metrics for split-second decisions
'performance': self.get_model_performance_metrics().get('cnn', {})
'performance': self.get_model_performance_metrics().get('cnn', {}),
# ENHANCED: Add toggle state information
'inference_enabled': cnn_inference_enabled,
'training_enabled': cnn_training_enabled,
'status_details': {
'checkpoint_loaded': cnn_state.get('checkpoint_loaded', False),
'inference_enabled': cnn_inference_enabled,
'training_enabled': cnn_training_enabled,
'is_training': cnn_panel_data.get('is_training', False)
}
}
loaded_models['cnn'] = cnn_model_info
@ -3566,6 +3690,11 @@ class CleanTradingDashboard:
cob_active = True
cob_predictions_count = len(self.recent_decisions) * 2
# Get COB RL toggle states
cob_toggle_state = toggle_states.get('cob_rl', {"inference_enabled": True, "training_enabled": True})
cob_inference_enabled = cob_toggle_state.get("inference_enabled", True)
cob_training_enabled = cob_toggle_state.get("training_enabled", True)
cob_model_info = {
'active': cob_active,
'parameters': 400000000, # 400M optimized
@ -3594,7 +3723,16 @@ class CleanTradingDashboard:
'predictions_24h': cob_timing['prediction_count_24h']
},
# NEW: Performance metrics for split-second decisions
'performance': self.get_model_performance_metrics().get('cob_rl', {})
'performance': self.get_model_performance_metrics().get('cob_rl', {}),
# ENHANCED: Add toggle state information
'inference_enabled': cob_inference_enabled,
'training_enabled': cob_training_enabled,
'status_details': {
'checkpoint_loaded': cob_state.get('checkpoint_loaded', False),
'inference_enabled': cob_inference_enabled,
'training_enabled': cob_training_enabled,
'is_training': False # COB RL training status
}
}
loaded_models['cob_rl'] = cob_model_info
@ -3603,21 +3741,42 @@ class CleanTradingDashboard:
decision_timing = get_model_timing_info('DECISION')
decision_active = signal_generation_active
# Get Decision Fusion toggle states
decision_toggle_state = toggle_states.get('decision_fusion', {"inference_enabled": True, "training_enabled": True})
decision_inference_enabled = decision_toggle_state.get("inference_enabled", True)
decision_training_enabled = decision_toggle_state.get("training_enabled", True)
# Get real decision fusion statistics from orchestrator
decision_stats = None
if self.orchestrator and hasattr(self.orchestrator, 'model_statistics'):
decision_stats = self.orchestrator.model_statistics.get('decision_fusion')
# Get real last prediction
last_prediction = 'HOLD'
last_confidence = 0.0
last_timestamp = datetime.now().strftime('%H:%M:%S')
if decision_stats and decision_stats.last_prediction:
last_prediction = decision_stats.last_prediction
last_confidence = decision_stats.last_confidence or 0.0
if decision_stats.last_inference_time:
last_timestamp = decision_stats.last_inference_time.strftime('%H:%M:%S')
decision_model_info = {
'active': decision_active,
'parameters': 10000000, # ~10M params for decision model
'last_prediction': {
'timestamp': datetime.now().strftime('%H:%M:%S'),
'action': 'DECISION_MAKING',
'confidence': 0.78
'timestamp': last_timestamp,
'action': last_prediction,
'confidence': last_confidence
},
'loss_5ma': decision_state.get('current_loss', 0.0089),
'initial_loss': decision_state.get('initial_loss'),
'best_loss': decision_state.get('best_loss', 0.0065),
'loss_5ma': decision_stats.current_loss if decision_stats else 0.0,
'initial_loss': decision_stats.best_loss if decision_stats else 0.0,
'best_loss': decision_stats.best_loss if decision_stats else 0.0,
'improvement': safe_improvement_calc(
decision_state.get('initial_loss'),
decision_state.get('current_loss', 0.0089),
97.0 # Default improvement percentage
decision_stats.best_loss if decision_stats else 0.0,
decision_stats.current_loss if decision_stats else 0.0,
0.0 # Calculate real improvement
),
'checkpoint_loaded': decision_state.get('checkpoint_loaded', False),
'model_type': 'DECISION',
@ -3637,7 +3796,16 @@ class CleanTradingDashboard:
'predictions_24h': decision_timing['prediction_count_24h']
},
# NEW: Performance metrics for split-second decisions
'performance': self.get_model_performance_metrics().get('decision', {})
'performance': self.get_model_performance_metrics().get('decision', {}),
# ENHANCED: Add toggle state information
'inference_enabled': decision_inference_enabled,
'training_enabled': decision_training_enabled,
'status_details': {
'checkpoint_loaded': decision_state.get('checkpoint_loaded', False),
'inference_enabled': decision_inference_enabled,
'training_enabled': decision_training_enabled,
'is_training': False # Decision fusion training status
}
}
loaded_models['decision'] = decision_model_info
@ -7972,9 +8140,12 @@ class CleanTradingDashboard:
else:
direction = 'HOLD'
agressiveness_threshold = self.agressiveness_enter if direction == 'ENTER' else self.agressiveness_exit
# Calculate aggressiveness threshold: higher aggressiveness = lower threshold (more trades)
aggressiveness = self.orchestrator.entry_aggressiveness if direction == 'ENTER' else self.orchestrator.exit_aggressiveness
# Map aggressiveness (0.0-1.0) to threshold (0.8-0.2): more aggressive = lower threshold
aggressiveness_threshold = 0.8 - (0.6 * aggressiveness)
# compare confidence with current aggressiveness
if self.trading_executor and confidence > agressiveness_threshold:
if self.trading_executor and confidence > aggressiveness_threshold:
try:
logger.info(f"[ORCHESTRATOR EXECUTION] Attempting to execute {action} for {symbol} via trading executor...")
success = self.trading_executor.execute_signal(