Claude Code Plugins

Community-maintained marketplace

Feedback

prediction-outcome-recording

@smith6jt-cop/Skills_Registry
0
0

Record prediction outcomes when positions close. Trigger when: (1) predictions logged but outcomes missing, (2) calculating prediction accuracy, (3) tracking model performance.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name prediction-outcome-recording
description Record prediction outcomes when positions close. Trigger when: (1) predictions logged but outcomes missing, (2) calculating prediction accuracy, (3) tracking model performance.
author Claude Code
date Sun Dec 29 2024 00:00:00 GMT+0000 (Coordinated Universal Time)

Prediction Outcome Recording Pattern

Experiment Overview

Item Details
Date 2024-12-29
Goal Complete the prediction lifecycle by recording outcomes when positions close
Environment live_trader.py, prediction_metrics.py, db_manager.py
Status Success

Context

The system was logging predictions when positions opened but never recording outcomes when positions closed. This broke:

  • Accuracy calculations (no outcomes to compare against)
  • Model evaluation (can't measure prediction quality)
  • Retraining decisions (no performance data)

Root cause: Predictions were being made and logged, but no code existed to update them with actual results.

Verified Workflow

1. Track prediction_id in Position State

# In PositionState dataclass or equivalent
@dataclass
class PositionState:
    symbol: str
    entry_price: float
    entry_time: datetime
    prediction_id: Optional[int] = None  # Track the prediction
    # ... other fields

2. Store prediction_id When Opening Position

# When logging prediction and opening position
prediction_id = prediction_tracker.log_prediction(
    symbol=symbol,
    timeframe=timeframe,
    predicted_direction=signal,
    predicted_magnitude=magnitude,
    confidence=confidence,
    model_version=model_version,
    regime=regime,
    entry_price=entry_price,
)

# Store in position state
position_state.prediction_id = prediction_id

3. Record Outcome at ALL Exit Points

There are typically 3 exit paths that ALL need outcome recording:

Exit Point 1: Stop-Loss / Take-Profit

# After stop-loss or take-profit triggers
if state.prediction_id is not None:
    actual_magnitude = (exit_price - state.entry_price) / state.entry_price
    actual_direction = 1 if actual_magnitude > 0.001 else (-1 if actual_magnitude < -0.001 else 0)

    prediction_tracker.db.update_prediction_outcome(
        prediction_id=state.prediction_id,
        actual_direction=actual_direction,
        actual_magnitude=actual_magnitude,
        final_price=exit_price
    )

Exit Point 2: Signal-Based Flatten

# When signal goes to 0/HOLD and position is flattened
if state.prediction_id is not None:
    actual_magnitude = (exit_price - state.entry_price) / state.entry_price
    actual_direction = 1 if actual_magnitude > 0.001 else (-1 if actual_magnitude < -0.001 else 0)

    prediction_tracker.db.update_prediction_outcome(
        prediction_id=state.prediction_id,
        actual_direction=actual_direction,
        actual_magnitude=actual_magnitude,
        final_price=exit_price
    )

Exit Point 3: Reverse Signal Close

# When signal reverses (long -> short or vice versa)
if state.prediction_id is not None:
    actual_magnitude = (exit_price - state.entry_price) / state.entry_price
    actual_direction = 1 if actual_magnitude > 0.001 else (-1 if actual_magnitude < -0.001 else 0)

    prediction_tracker.db.update_prediction_outcome(
        prediction_id=state.prediction_id,
        actual_direction=actual_direction,
        actual_magnitude=actual_magnitude,
        final_price=exit_price
    )

4. Pass prediction_tracker to Functions

def decide_and_trade_optimized(
    symbol: str,
    df: pd.DataFrame,
    state: PositionState,
    prediction_tracker: Optional[PredictionTracker] = None,  # ADD THIS
    # ... other params
) -> Tuple[int, float, str]:
    ...

5. Direction Calculation

# Threshold for direction: must be > 0.1% to count as up/down
def calculate_direction(actual_magnitude: float) -> int:
    if actual_magnitude > 0.001:    # > 0.1% up
        return 1
    elif actual_magnitude < -0.001: # < -0.1% down
        return -1
    else:
        return 0  # Neutral

Failed Attempts (Critical)

Attempt Why it Failed Lesson Learned
Recording outcome only at one exit point Missed 2 of 3 exit paths ALL exit paths need outcome recording
Using raw price difference for magnitude Values inconsistent Use percentage: (exit - entry) / entry
Direction threshold of 0 Too many neutral classifications Use 0.1% threshold for meaningful moves
Not checking if prediction_id exists AttributeError on None Always check if state.prediction_id is not None
Global prediction_tracker variable Threading issues Pass as function parameter

Final Parameters

# Direction thresholds
up_threshold: 0.001    # > 0.1% = direction 1
down_threshold: -0.001 # < -0.1% = direction -1
neutral: 0             # otherwise

# Outcome fields
actual_direction: int    # -1, 0, 1
actual_magnitude: float  # % change (e.g., 0.025 = 2.5%)
final_price: float       # Exit price
directional_correct: int # 1 if predicted == actual, else 0
magnitude_error: float   # abs(predicted_mag - actual_mag)

Key Insights

  • Track prediction_id in position state: Essential for correlating entries to exits
  • Cover ALL exit paths: Stop-loss, take-profit, signal flatten, signal reverse
  • Guard against None: Always check prediction_id before recording
  • Use percentages: Magnitude as % change, not absolute price difference
  • 0.1% threshold: Avoid classifying tiny moves as directional

Verification Query

-- Check if outcomes are being recorded
SELECT
    DATE(p.timestamp, 'unixepoch') as date,
    COUNT(p.id) as predictions,
    COUNT(o.prediction_id) as outcomes,
    ROUND(100.0 * COUNT(o.prediction_id) / COUNT(p.id), 1) as outcome_pct
FROM predictions p
LEFT JOIN prediction_outcomes o ON p.id = o.prediction_id
WHERE p.timestamp > strftime('%s', 'now') - 7*86400
GROUP BY DATE(p.timestamp, 'unixepoch')
ORDER BY date DESC;

References

  • scripts/live_trader.py: Lines 1395-1404, 1469-1478, 1546-1555 (exit points)
  • alpaca_trading/evaluation/prediction_metrics.py: PredictionTracker class
  • alpaca_trading/data/db_manager.py: update_prediction_outcome()