price vector predictions

This commit is contained in:
Dobromir Popov
2025-07-30 00:31:51 +03:00
parent 29382ac0db
commit 8335ad8e64
4 changed files with 789 additions and 2 deletions

View File

@ -3809,6 +3809,69 @@ class TradingOrchestrator:
)
return (1.0 if simple_correct else -0.5, simple_correct)
def _calculate_price_vector_loss(
self,
predicted_vector: dict,
actual_price_change_pct: float,
time_diff_minutes: float
) -> float:
"""
Calculate training loss for price vector predictions to improve accuracy
Args:
predicted_vector: Dict with 'direction' (-1 to 1) and 'confidence' (0 to 1)
actual_price_change_pct: Actual price change percentage
time_diff_minutes: Time elapsed since prediction
Returns:
Loss value for training the price vector prediction head
"""
try:
if not predicted_vector or not isinstance(predicted_vector, dict):
return 0.0
predicted_direction = predicted_vector.get('direction', 0.0)
predicted_confidence = predicted_vector.get('confidence', 0.0)
# Skip very weak predictions
if abs(predicted_direction) < 0.05 or predicted_confidence < 0.1:
return 0.0
# Calculate actual direction and magnitude
actual_direction = 1.0 if actual_price_change_pct > 0.05 else -1.0 if actual_price_change_pct < -0.05 else 0.0
actual_magnitude = min(abs(actual_price_change_pct) / 2.0, 1.0) # Normalize to 0-1, cap at 2%
# DIRECTION LOSS: penalize wrong direction predictions
if actual_direction != 0.0:
# Expected direction should match actual
direction_error = abs(predicted_direction - actual_direction)
else:
# If no significant movement, direction should be close to 0
direction_error = abs(predicted_direction) * 0.5 # Reduced penalty for neutral
# MAGNITUDE LOSS: penalize inaccurate magnitude predictions
# Convert predicted direction+confidence to expected magnitude
predicted_magnitude = abs(predicted_direction) * predicted_confidence
magnitude_error = abs(predicted_magnitude - actual_magnitude)
# TIME DECAY: predictions should be accurate quickly
time_decay = max(0.1, 1.0 - (time_diff_minutes / 30.0)) # 30min decay window
# COMBINED LOSS
direction_loss = direction_error * 2.0 # Direction is very important
magnitude_loss = magnitude_error * 1.0 # Magnitude is important
total_loss = (direction_loss + magnitude_loss) * time_decay
logger.debug(f"PRICE VECTOR LOSS: pred_dir={predicted_direction:.3f}, actual_dir={actual_direction:.3f}, "
f"pred_mag={predicted_magnitude:.3f}, actual_mag={actual_magnitude:.3f}, "
f"dir_loss={direction_loss:.3f}, mag_loss={magnitude_loss:.3f}, total={total_loss:.3f}")
return min(total_loss, 5.0) # Cap loss to prevent exploding gradients
except Exception as e:
logger.error(f"Error calculating price vector loss: {e}")
return 0.0
def _calculate_price_vector_bonus(
self,
predicted_vector: dict,
@ -3881,6 +3944,91 @@ class TradingOrchestrator:
logger.error(f"Error calculating price vector bonus: {e}")
return 0.0
def _should_execute_action(
self,
action: str,
confidence: float,
predicted_vector: dict = None,
current_price: float = None,
symbol: str = None
) -> tuple[bool, str]:
"""
Intelligent action filtering based on predicted price movement and confidence
Args:
action: Predicted action (BUY/SELL/HOLD)
confidence: Model confidence (0 to 1)
predicted_vector: Dict with 'direction' and 'confidence'
current_price: Current market price
symbol: Trading symbol
Returns:
(should_execute, reason)
"""
try:
# Basic confidence threshold
min_action_confidence = 0.6 # Require 60% confidence for any action
if confidence < min_action_confidence:
return False, f"Low action confidence ({confidence:.1%} < {min_action_confidence:.1%})"
# HOLD actions always allowed
if action == "HOLD":
return True, "HOLD action approved"
# Check if we have price vector predictions
if not predicted_vector or not isinstance(predicted_vector, dict):
# No vector available - use basic confidence only
high_confidence_threshold = 0.8
if confidence >= high_confidence_threshold:
return True, f"High confidence action without vector ({confidence:.1%})"
else:
return False, f"No price vector available, requires high confidence ({confidence:.1%} < {high_confidence_threshold:.1%})"
predicted_direction = predicted_vector.get('direction', 0.0)
vector_confidence = predicted_vector.get('confidence', 0.0)
# VECTOR-BASED FILTERING
min_vector_confidence = 0.5 # Require 50% vector confidence
min_direction_strength = 0.3 # Require 30% direction strength
if vector_confidence < min_vector_confidence:
return False, f"Low vector confidence ({vector_confidence:.1%} < {min_vector_confidence:.1%})"
if abs(predicted_direction) < min_direction_strength:
return False, f"Weak direction prediction ({abs(predicted_direction):.1%} < {min_direction_strength:.1%})"
# DIRECTION ALIGNMENT CHECK
if action == "BUY" and predicted_direction <= 0:
return False, f"BUY action misaligned with predicted direction ({predicted_direction:.3f})"
if action == "SELL" and predicted_direction >= 0:
return False, f"SELL action misaligned with predicted direction ({predicted_direction:.3f})"
# STEEPNESS/MAGNITUDE CHECK (fee-aware)
fee_cost = 0.12 # 0.12% round trip fee cost
predicted_magnitude = abs(predicted_direction) * vector_confidence * 2.0 # Scale to ~2% max
if predicted_magnitude < fee_cost * 2.0: # Require 2x fee coverage
return False, f"Predicted magnitude too small ({predicted_magnitude:.2f}% < {fee_cost * 2.0:.2f}% minimum)"
# COMBINED CONFIDENCE CHECK
combined_confidence = (confidence + vector_confidence) / 2.0
min_combined_confidence = 0.7 # Require 70% combined confidence
if combined_confidence < min_combined_confidence:
return False, f"Low combined confidence ({combined_confidence:.1%} < {min_combined_confidence:.1%})"
# ALL CHECKS PASSED
logger.info(f"ACTION APPROVED: {action} with {confidence:.1%} confidence, "
f"vector: {predicted_direction:+.3f} ({vector_confidence:.1%}), "
f"predicted magnitude: {predicted_magnitude:.2f}%")
return True, f"Action approved: strong prediction with adequate magnitude"
except Exception as e:
logger.error(f"Error in action filtering: {e}")
return False, f"Action filtering error: {e}"
async def _train_model_on_outcome(
self,
record: Dict,
@ -3913,6 +4061,18 @@ class TradingOrchestrator:
current_position_pnl=current_pnl,
predicted_price_vector=predicted_price_vector,
)
# Calculate price vector training loss if we have vector predictions
if predicted_price_vector:
vector_loss = self._calculate_price_vector_loss(
predicted_price_vector,
price_change_pct,
record.get("time_diff_minutes", 1.0)
)
# Store the vector loss for training
record["price_vector_loss"] = vector_loss
if vector_loss > 0:
logger.debug(f"PRICE VECTOR TRAINING: {model_name} vector loss = {vector_loss:.3f}")
# Train decision fusion model if it's the model being evaluated
if model_name == "decision_fusion":