A large iGaming operator with a 2 million player base was losing 30% of profitable customers within the first 90 days. Mass bonuses attracted bonus hunters, campaign ROI hovered around 80%—each bonus ruble returned 80 kopecks. After implementing our system, the operator reduced losses: bonus budget cut by $200,000 annually and LTV increased by 20%, generating an additional $1.5 million.
We developed an AI CRM analytics system based on an ensemble of GradientBoosting (Gradient boosting) and KMeans segmentation. Within 6 weeks of deployment, the operator achieved 250% ROI, reduced the bonus budget by 20%, and cut churn rate by 18%. The key change was transitioning from generic RFM to GGR segmentation, which accounts for actual player profitability.
The system includes Airflow pipelines, ClickHouse storage, and Metabase dashboards. For each segment (champion, at_risk_highvalue, loyal_lowvalue, dormant), an individual bonus mechanic with different wagering requirements is selected. Built-in RG checks block bonuses for players with high risk of problematic behavior. The system updates segments and models weekly, adapting to changes in player behavior.
Problems We Solve
- Churn prediction: Standard RFM models ignore trends. We build features: session change over 7/30 days, GGR trend, support complaints. GradientBoosting achieves AUC 0.87—12% better than logistic regression.
- Bonus abuse: Mass bonuses attract hunters. Segmentation identifies 4 groups: champion (5–10% of base, 50–70% of GGR), at_risk_highvalue, loyal_lowvalue, dormant. Each receives a specific bonus type with different wagering.
- RG compliance: Built-in checks block bonuses for high RG-risk players, reducing regulatory complaints.
How to Predict Player Churn in 30 Days
We use an ensemble of GradientBoosting with parameters: n_estimators=200, learning_rate=0.05, max_depth=4. Train on 6 months of data. Features include days_since_last_session, sessions_trend, ggr_trend, bonus_expiry_ignored. The model outputs churn probability and breaks into tiers: low, medium, high, critical. For critical tier, a reactivation campaign with cashback is automatically triggered.
Why GGR Segmentation Outperforms RFM
RFM considers only recency, frequency, and monetary value. GGR = deposits – withdrawals – bonuses. It more accurately reflects true player value. For example, a player with high RFM but frequent withdrawals and bonuses is unprofitable. GGR segmentation identifies 'champion' (high GGR + activity) and 'at_risk_highvalue' (high GGR, low activity)—these are the ones to focus resources on.
How We Do It
Stack: Python, scikit-learn, PyTorch, Hugging Face (for NLP on support chats), ClickHouse (real-time analytics), Airflow (pipelines), Metabase/Tableau (dashboards). Deployed in Docker/Kubernetes.
Example segmentation code:
import pandas as pd
import numpy as np
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.cluster import KMeans
class iGamingPlayerSegmentor:
"""Segmentation by GGR and behavior"""
def compute_player_value(self, player_data: pd.DataFrame) -> pd.DataFrame:
"""
GGR (Gross Gaming Revenue) and LTV calculation.
GGR = deposits - withdrawals - bonuses_paid
"""
df = player_data.copy()
df['ggr'] = df['total_deposits'] - df['total_withdrawals'] - df['bonuses_paid']
df['ggr_per_month'] = df['ggr'] / df['months_active'].clip(1)
df['deposit_frequency'] = df['deposit_count'] / df['months_active'].clip(1)
df['withdrawal_ratio'] = df['total_withdrawals'] / df['total_deposits'].clip(1)
# LTV forecast: simple linear projection
df['predicted_ltv_12m'] = df['ggr_per_month'] * 12 * (
1 - df['churn_probability_30d'].fillna(0.3)
)
return df
def segment_players(self, player_value: pd.DataFrame) -> pd.DataFrame:
"""Segmentation by value and activity"""
df = player_value.copy()
ggr_median = df['ggr'].median()
activity_median = df['sessions_last_30d'].median()
def classify(row):
high_ggr = row['ggr'] > ggr_median
active = row['sessions_last_30d'] > activity_median
if high_ggr and active:
return 'champion'
elif high_ggr and not active:
return 'at_risk_highvalue'
elif not high_ggr and active:
return 'loyal_lowvalue'
else:
return 'dormant'
df['segment'] = df.apply(classify, axis=1)
return df
class ChurnPredictoriGaming:
"""Churn prediction for iGaming"""
def __init__(self):
self.model = GradientBoostingClassifier(
n_estimators=200, learning_rate=0.05, max_depth=4, random_state=42
)
def build_features(self, players: pd.DataFrame) -> pd.DataFrame:
return pd.DataFrame({
'days_since_last_session': players['days_since_last_session'],
'sessions_trend': (players['sessions_last_7d'] - players['sessions_prev_7d']) / (players['sessions_prev_7d'] + 1),
'ggr_trend': (players['ggr_last_30d'] - players['ggr_prev_30d']) / (abs(players['ggr_prev_30d']) + 1),
'deposit_count_7d': players['deposit_count_7d'],
'withdrawal_request': players['has_pending_withdrawal'].astype(int),
'bonus_expiry_ignored': players['bonus_expiry_ignored'].astype(int),
'support_complaint': players['has_support_complaint'].astype(int),
'avg_session_duration_trend': players['avg_session_duration_trend'],
}).fillna(0)
def predict(self, players: pd.DataFrame) -> pd.DataFrame:
X = self.build_features(players)
probs = self.model.predict_proba(X)[:, 1]
result = players[['player_id']].copy() if 'player_id' in players.columns else pd.DataFrame(index=players.index)
result['churn_prob_30d'] = probs
result['churn_tier'] = pd.cut(probs, bins=[0, 0.2, 0.5, 0.75, 1.0],
labels=['low', 'medium', 'high', 'critical'])
return result
class BonusCampaignOptimizer:
"""Bonus campaign optimization"""
def design_retention_campaign(self, player_segment: str,
player_stats: dict,
budget_per_player: float) -> dict:
"""Bonus offer tailored to segment and budget"""
campaigns = {
'champion': {
'bonus_type': 'cashback_vip',
'value': min(player_stats.get('avg_weekly_ggr', 50) * 0.15, budget_per_player),
'wagering_req': 1, # Minimum wagering for VIP
'message': 'Exclusive cashback for our best players'
},
'at_risk_highvalue': {
'bonus_type': 'targeted_reload',
'value': min(player_stats.get('avg_deposit', 100) * 0.25, budget_per_player),
'wagering_req': 3,
'message': 'Special offer — come back to us'
},
'loyal_lowvalue': {
'bonus_type': 'free_spins',
'value': 20, # 20 free spins ~ $5-10 value
'wagering_req': 5,
'message': 'Loyalty bonus'
},
'dormant': {
'bonus_type': 'reactivation_bonus',
'value': min(20, budget_per_player),
'wagering_req': 5,
'message': "We missed you! Here's a bonus for returning"
}
}
campaign = campaigns.get(player_segment, campaigns['loyal_lowvalue'])
# RG check: no bonuses for high RG-risk players
if player_stats.get('rg_risk_level') == 'high':
return {'bonus_type': 'none', 'reason': 'RG restriction'}
return campaign
def calculate_campaign_roi(self, campaign_results: pd.DataFrame) -> dict:
"""ROI of a bonus campaign"""
total_bonus_cost = campaign_results['bonus_value'].sum()
incremental_ggr = (
campaign_results['ggr_post_campaign'] -
campaign_results['ggr_pre_campaign']
).sum()
return {
'total_bonus_cost': round(total_bonus_cost, 2),
'incremental_ggr': round(incremental_ggr, 2),
'roi_pct': round((incremental_ggr - total_bonus_cost) / max(total_bonus_cost, 1) * 100, 1),
'reactivation_rate': (campaign_results['returned_to_active'] > 0).mean(),
'bonus_abuse_rate': (
campaign_results['withdrawal_after_bonus'] > campaign_results['bonus_value'] * 0.9
).mean()
}
Our Process
- Analytics: audit current CRM, collect data (DWH, ClickHouse), identify segments.
- Design: define metrics (GGR, LTV, churn rate), select models.
- Implementation: Airflow pipelines, model training, integration with bonus system.
- Testing: A/B test on 10% traffic, compare ROI.
- Deploy: production rollout, dashboards, documentation.
What’s Included
- Audit of current CRM and data
- DWH setup (ClickHouse, PostgreSQL)
- ML models for segmentation, churn prediction, bonus optimization
- Integration with bonus system (REST API)
- Dashboards in Metabase/Tableau
- Team training (2 workshops)
- 1 month technical support
Estimated Timeline
| Phase | Duration |
|---|---|
| Analytics | 1-2 weeks |
| Design | 1 week |
| Implementation | 3-4 weeks |
| Testing | 1 week |
| Deploy & training | 1-2 weeks |
| Total | 4-8 weeks |
Segmentation Metrics Comparison
| Segment | Share of Base | Share of GGR | Churn Risk | Recommended Bonus |
|---|---|---|---|---|
| Champion | 5–10% | 50–70% | Low | Cashback with wagering 1 |
| At-risk high value | 10–15% | 20–30% | Medium | Reload with wagering 3 |
| Loyal low value | 30–40% | 10–15% | Medium | Free spins with wagering 5 |
| Dormant | 40–50% | 5–10% | High | Reactivation bonus |
GradientBoosting Model Details
Optimal hyperparameters: n_estimators=200, learning_rate=0.05, max_depth=4, subsample=0.8. Feature importance: days_since_last_session (0.25), ggr_trend (0.20), sessions_trend (0.18). AUC on held-out set: 0.87.
Typical Mistakes
- Ignoring RG: without built-in RG checks, bonuses may incentivize problem players, leading to fines.
- Pure RFM: RFM ignores GGR and trends, leading to false priorities.
- Mass bonuses: without segmentation, bonus budget is wasted on hunters, ROI drops below 100%.
- Weak infrastructure: without DWH and pipelines, data is stale and models perform poorly.
We guarantee a minimum 30% increase in bonus campaign ROI based on A/B test results. Contact us for a consultation—we'll assess your project in 2 days. Order the system implementation and see first results in just 4 weeks.







