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)
| Signal | Weight | What It Measures |
|---|---|---|
| Neural Impulse Prediction | 12.7% | AI forecast of impulse likelihood |
| Spending Velocity | 11.8% | Rate of spending vs. average |
| Neural Relapse Prediction | 9.8% | Likelihood of bypass failure |
Tier 1 Total: 34.3% of composite score
Tier 2: Strong Predictors (5-10% weight each)
| Signal | Weight | What It Measures |
|---|---|---|
| Venue Proximity | 5.9% | Distance to gambling venues |
| Biometric Vulnerability | 5.0% | HRV, sleep, Oura readiness |
| Category Spend Ratio | 4.9% | Spending vs. budget by category |
| Browsing Burst Patterns | 3.9% | Rapid gambling domain queries |
| Calendar Proximity to Stress | 3.9% | Upcoming stressful events |
Tier 2 Total: 23.6% of composite score
Tier 3: Moderate Predictors (2-5% weight each)
| Signals | Combined Weight |
|---|---|
| Sleep Deprivation, Emotional Distress, Crypto Impulse, BNPL Stacking, Merchant Embedding, Time-of-Day, Day-of-Week, Payday Proximity | 22.0% |
Tier 4: Contextual Predictors (1-2% weight each)
| Signals | Combined Weight |
|---|---|
| Weather, Social Context, Financial State, Intervention History, App Engagement | 20.1% |
Risk Thresholds and Actions
The composite score determines protective actions:
Threshold Table
| Score Range | Risk Level | SpendingShield | Actions |
|---|---|---|---|
| 0.00-0.40 | Low (GREEN) | Normal | Passive monitoring |
| 0.40-0.60 | Elevated (YELLOW) | Caution | Proactive check-ins |
| 0.60-0.80 | High (ORANGE) | Protected | Active intervention |
| 0.80-1.00 | Critical (RED) | Locked | Crisis 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
| Signal | Value | Weight | Contribution |
|---|---|---|---|
| Neural Prediction | 0.72 | 12.7% | 0.091 |
| Spending Velocity | 0.45 | 11.8% | 0.053 |
| Relapse Prediction | 0.58 | 9.8% | 0.057 |
| Venue Proximity | 1.0 | 5.9% | 0.059 |
| Biometric | 0.65 | 5.0% | 0.033 |
| Time-of-Day | 0.9 | 2.7% | 0.024 |
| Day-of-Week | 0.85 | 2.5% | 0.021 |
| Payday Proximity | 0.8 | 2.3% | 0.018 |
| Other signals (19) | varies | 47.3% | 0.152 |
| COMPOSITE | 100% | 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:
| Metric | Result |
|---|---|
| Prediction Accuracy (AUC-ROC) | 0.87 |
| High-Risk Detection Rate | 84% |
| False Positive Rate | 13% |
| State Transition Accuracy | 91% |
| 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 FreeRelated: 27 Risk Signals | SpendingShield | Adaptive Weight System