Risk Orchestrator: Composite Score Calculation Explained

The Risk Orchestrator is Whistl's central risk assessment engine. It combines 27 weighted signals into a single composite score (0.0-1.0) that determines when to activate blocking, trigger intervention, and notify partners. This deep dive explains the mathematics behind your protection.

Why a Composite Score?

Individual signals aren't enough—risk emerges from signal combination:

The Limitation of Single Signals

  • Venue proximity alone: Being near a casino doesn't mean you'll gamble
  • Poor sleep alone: Being tired doesn't mean you'll spend impulsively
  • Payday alone: Getting paid doesn't mean you'll blow it

The Power of Combination

  • Venue + poor sleep + payday: Now we're talking—triple risk
  • Synergistic effects: Combined risk > sum of individual risks
  • Context matters: Same signal means different things in different contexts

The Composite Score Formula

The Risk Orchestrator calculates a weighted sum of all 27 signals:

Basic Formula

# Composite risk score calculation
composite_risk = Σ(signal_value × signal_weight)

# Where:
# - signal_value: Current value of each signal (0.0 - 1.0)
# - signal_weight: Importance of each signal (sums to 1.0)
# - composite_risk: Final score (0.0 - 1.0)

# Example calculation with 5 signals (simplified):
signal_1_value = 0.8  # Neural prediction: high
signal_1_weight = 0.127  # 12.7% weight

signal_2_value = 0.6  # Spending velocity: moderate
signal_2_weight = 0.118  # 11.8% weight

signal_3_value = 1.0  # Venue proximity: at venue
signal_3_weight = 0.059  # 5.9% weight

# Partial sum:
partial_risk = (0.8 × 0.127) + (0.6 × 0.118) + (1.0 × 0.059)
             = 0.1016 + 0.0708 + 0.059
             = 0.2314

# Add remaining 24 signals...
# Final composite_risk = 0.73 (HIGH)

Signal Value Normalisation

All signals are normalised to 0.0-1.0 scale:

# Normalisation examples

# HRV: Lower is worse (inverse relationship)
hrv_value = 1.0 - (current_hrv / baseline_hrv)
# If baseline = 50ms, current = 35ms
# hrv_value = 1.0 - (35/50) = 1.0 - 0.7 = 0.3

# Sleep: More is better (up to optimal)
if sleep_hours < 7:
    sleep_value = (7 - sleep_hours) / 7  # Deficit
elif sleep_hours > 9:
    sleep_value = (sleep_hours - 9) / 3  # Excess
else:
    sleep_value = 0  # Optimal range

# Distance to venue: Closer is worse
if distance < 100:  # meters
    venue_value = 1.0  # At venue
elif distance < 500:
    venue_value = 0.8  # Very close
elif distance < 1000:
    venue_value = 0.5  # Close
elif distance < 2000:
    venue_value = 0.2  # Nearby
else:
    venue_value = 0  # Safe distance

Signal Weights by Tier

The 27 signals are organised into tiers based on predictive power:

Tier 1: Primary Predictors (>10% weight each)

SignalWeightWhat It Measures
Neural Impulse Prediction12.7%AI forecast of impulse likelihood
Spending Velocity11.8%Rate of spending vs. average
Neural Relapse Prediction9.8%Likelihood of bypass failure

Tier 1 Total: 34.3% of composite score

Tier 2: Strong Predictors (5-10% weight each)

SignalWeightWhat It Measures
Venue Proximity5.9%Distance to gambling venues
Biometric Vulnerability5.0%HRV, sleep, Oura readiness
Category Spend Ratio4.9%Spending vs. budget by category
Browsing Burst Patterns3.9%Rapid gambling domain queries
Calendar Proximity to Stress3.9%Upcoming stressful events

Tier 2 Total: 23.6% of composite score

Tier 3: Moderate Predictors (2-5% weight each)

SignalsCombined Weight
Sleep Deprivation, Emotional Distress, Crypto Impulse, BNPL Stacking, Merchant Embedding, Time-of-Day, Day-of-Week, Payday Proximity22.0%

Tier 4: Contextual Predictors (1-2% weight each)

SignalsCombined Weight
Weather, Social Context, Financial State, Intervention History, App Engagement20.1%

Risk Thresholds and Actions

The composite score determines protective actions:

Threshold Table

Score RangeRisk LevelSpendingShieldActions
0.00-0.40Low (GREEN)NormalPassive monitoring
0.40-0.60Elevated (YELLOW)CautionProactive check-ins
0.60-0.80High (ORANGE)ProtectedActive intervention
0.80-1.00Critical (RED)LockedCrisis response

Hysteresis for Stability

To prevent rapid state flipping, thresholds have hysteresis:

# Hysteresis implementation
def should_change_state(current_state, new_score):
    if current_state == "GREEN":
        # Must exceed 0.45 to leave GREEN (not just 0.40)
        return new_score > 0.45
    elif current_state == "YELLOW":
        # Must drop below 0.35 to go to GREEN
        # Must exceed 0.65 to go to ORANGE
        if new_score < 0.35:
            return "GREEN"
        elif new_score > 0.65:
            return "ORANGE"
        return "YELLOW"
    elif current_state == "ORANGE":
        # Must drop below 0.55 to go to YELLOW
        # Must exceed 0.85 to go to RED
        if new_score < 0.55:
            return "YELLOW"
        elif new_score > 0.85:
            return "RED"
        return "ORANGE"
    elif current_state == "RED":
        # Must drop below 0.75 to leave RED
        return new_score < 0.75

Real-World Calculation Example

Marcus, Friday 8:30pm, near Crown Casino:

Signal Values and Contributions

SignalValueWeightContribution
Neural Prediction0.7212.7%0.091
Spending Velocity0.4511.8%0.053
Relapse Prediction0.589.8%0.057
Venue Proximity1.05.9%0.059
Biometric0.655.0%0.033
Time-of-Day0.92.7%0.024
Day-of-Week0.852.5%0.021
Payday Proximity0.82.3%0.018
Other signals (19)varies47.3%0.152
COMPOSITE100%0.508

State Determination

Composite score of 0.508 = YELLOW state (Elevated risk)

Actions: Proactive check-in, increased monitoring, partner notification optional

Weight Adaptation Over Time

Signal weights aren't static—they adapt based on predictive accuracy:

Exponential Moving Average Updates

# Weight adaptation algorithm
def update_signal_weight(signal_name, current_weight, prediction, outcome):
    learning_rate = 0.05  # How quickly weights adapt
    
    # Calculate prediction error
    error = abs(outcome - prediction)
    
    # If signal predicted correctly, increase weight
    if error < 0.1:
        # Reinforce: move weight toward maximum (0.20)
        adjustment = learning_rate * (1.0 - current_weight)
        new_weight = current_weight + adjustment
    # If signal predicted incorrectly, decrease weight
    elif error > 0.3:
        # Reduce: move weight toward minimum (0.01)
        adjustment = learning_rate * current_weight
        new_weight = current_weight - adjustment
    else:
        # Small error: minor adjustment
        new_weight = current_weight
    
    # Ensure weights stay in valid range
    new_weight = max(0.01, min(0.20, new_weight))
    
    return new_weight

Personal Calibration

After 30 days, your weights are uniquely calibrated:

  • If venue proximity predicts YOUR impulses well → Weight increases
  • If weather doesn't predict YOUR impulses → Weight decreases
  • Your model differs from other users based on your patterns

Effectiveness Data

Risk Orchestrator performance metrics:

MetricResult
Prediction Accuracy (AUC-ROC)0.87
High-Risk Detection Rate84%
False Positive Rate13%
State Transition Accuracy91%
Weight Calibration Stability±3% after 30 days

User Testimonials

"I don't understand all the math, but I know when Whistl says my risk is high, it's right. The composite score thing works." — Marcus, 28

"Love seeing the breakdown of what's driving my risk. Helps me understand my triggers." — Sarah, 34

"The fact that it learns my patterns over time is wild. It knows me better than I know myself." — Jake, 31

Conclusion

The Risk Orchestrator transforms 27 disparate signals into a single, actionable risk score. Through weighted combination, adaptive learning, and intelligent thresholds, it creates a comprehensive picture of your impulse vulnerability.

This isn't just math—it's your personal risk radar, constantly scanning, constantly learning, constantly protecting.

Experience Intelligent Risk Assessment

Whistl's Risk Orchestrator combines 27 signals to protect you. Download free and see your risk in real-time.

Download Whistl Free

Related: 27 Risk Signals | SpendingShield | Adaptive Weight System