Executive Summary
MonetizeSlot Engine computes real-time pricing and bet configuration decisions for iGaming slot games, adapting to demand signals while enforcing explainable policies and regulatory guardrails.
The system routes requests through a Python "brain" that selects between capacity-based rules and Thompson Sampling bandits, optionally delegating capacity logic to a Go microservice under high load.
Operators configure policies in PostgreSQL, persist every decision for audit, stream events to Kafka, and run daily fairness audits using Gini coefficients—all while maintaining a simple integration contract via FastAPI endpoints.
The Challenge
iGaming operators face variable demand across games and hours, requiring dynamic pricing that maximizes ARPU without introducing excessive volatility or fairness risk.
Static pricing under-monetizes peaks, while uncontrolled optimization can violate regulatory constraints or create unequal player experiences.
MonetizeSlot solves this with a layered architecture: forecast → policy selection → optimization (capacity or bandit) → governance → persistence and events.
Architecture Overview
MonetizeSlot separates concerns across explicit layers, with policy configuration stored in pricing_policies and loaded dynamically at runtime.
The core supports per-game demand disaggregation via game_share_dow_hour and policy resolution via experiment scopes or game mappings.
Every decision returns a unified structure: final_price, reason, bandit metadata (action_idx, multiplier, actions_hash), experiment metadata, and bet configuration (min_bet, max_bet, default_bet, bet_steps, rtp_profile_id).
Runtime Decision Modes
Operators select mode per request: capacity (rule-based) or bandit (online learning).
Capacity Mode (Layer 2)
- Uses a
PriceConfig(base/min/max price, soft/hard capacity) to compute recommended price from predicted players. - Supports game-specific baselines via
game_capacity_baseline. - Optionally delegates to Go microservice
/runtime/pricefor low-latency execution when enabled. - Example logic: raise price above soft capacity, cap above hard capacity, lower below utilization threshold.
Bandit Mode (Layer 3)
- Selects a multiplier from discrete actions (default
[0.8, 1.0, 1.2, 1.5]) via Thompson Sampling, where each action maintains a Normal posterior (mu,precision). - Bandits are per-game + demand-bucketed (
p_high/p_mid/p_loword_high/d_mid/d_low). - Bandits are keyed by
(game_id:bucket:actions_hash)to prevent action remapping errors. - State persists via
BanditStatewith load-from-DB bootstrap.
Bet Configuration Generation
Every decision builds a bet ladder from final_price using policy-provided bet_config factors (default: min 0.5x, max 1.5x, 5 steps) or simple heuristics.
Operators can specify rtp_profile_id per policy, enabling joint optimization of price and RTP.
This ensures consistent game configuration across capacity and bandit decisions.
Policy Resolution & Experimentation
Runtime resolves policies via resolve_policy_for_request: experiments first (via experiment_scopes), then game mappings (game_pricing_policies), filtered by type/time/segment/default.
Separate experiment bucketing uses stable MD5 hash (experiment_id:player_id) % 100 to assign deterministic variants from traffic_allocation.
Decisions record resolved_policy_id, experiment_id, and variant for full traceability.
Governance and Fairness (Layer 4)
Daily run_daily_fairness_audit computes the Gini coefficient over final_price distribution in price_decisions for the audit date, flagging if above a threshold (default 0.3).
Results upsert into fairness_audit with avg_price, gini_coefficient, alert_flag, and action_taken.
Gini measures inequality on a 0–1 scale where 0 indicates perfect equality and 1 indicates maximal inequality.
Persistence and Observability
Every runtime decision inserts into price_decisions with demand inputs, policy id, bandit metadata, bet config, and decision_mode.
Experiment assignments log to experiment_assignments; Kafka publishes pricing_decision or bandit_decision events keyed by decision_id for downstream analytics.
Structured logging captures full decision context via logger.info with rich extra payloads.
Offline Simulation
run_simulation_worker executes A/B comparisons between control/variant policies over horizon_days × n_players, persisting decisions to simulation_price_decisions and summarizing uplift in simulation_runs.
It supports capacity, bandit, and static policies with realistic revenue simulation and bet configuration generation.
This is designed for policy iteration and configuration validation, not production deployment.
Integration API (FastAPI)
Public endpoints use Pydantic models and structured responses.
/runtime/price (Primary)
POST /runtime/price
{
"game_id": "slots_gonzo_quest",
"player_id": 12345,
"base_price": 2.0,
"mode": "bandit",
"context": {"predicted_players": 150}
}{
"price": 3.0,
"policy_type": "bandit_ts",
"reason": "thompson_sampling_action=1.5_idx=3",
"action_id": 3,
"multiplier": 1.5,
"min_bet": 1.5,
"max_bet": 4.5,
"bet_steps": [1.5, 2.25, 3.0, 3.75, 4.5],
"rtp_profile_id": "rtp_96",
"experiment_id": 1,
"variant": "treatment_2"
}Other runtime endpoints
/price/decide-and-store— persist + event; derives forecast andp_gameinternally and supports capacity fallback./price/bandit-decide— bandit-only decision + persistence + event./price/recommendation— non-persisting capacity recommendation and demand breakdown.
Production Considerations
- Multi-tenancy: Operator scoping via
operator_id(currently hardcoded 1; planned tenant resolution). - Latency: Optional Go delegation for capacity; bandit selection is O(actions) with small action sets.
- Persistence: In-memory bandits bootstrap from
BanditState; decisions persist synchronously. - Scaling: Stateless runtime + Kafka decouples decisioning from analytics; Go runtime scales independently.
Appendix: Code-to-Feature Mapping
| Feature | Implementation |
|---|---|
| Runtime orchestration | src/engine/pricing_core.py::compute_runtime_price |
| Policy resolution | src/engine/policy_resolution.py |
| Thompson Sampling | src/layer3_rl/thompson_sampling.py |
| Contextual bandits | src/layer3_rl/contextual_bandits.py |
| Fairness audit | src/layer4_execution/fairness_audit.py |
| Runtime API | src/api/routers/runtime.py |
| Schemas | src/api/schemas.py |
| Simulation | src/engine/simulation.py |
| Batch capacity | src/layer4_execution/price_generator.py |
MonetizeSlot Engine v0.1
Production-ready pricing for iGaming operators. Deploy, simulate, roll out, measure.
References
- Internal MonetizeSlot Engine implementation (pricing core, runtime router, RL bandits, fairness audit, simulation).
- Open Graph protocol and usage guidance for social sharing metadata (og:type article recommended for articles). ogp.me
- Gini coefficient definition (0 = perfect equality, 1 = maximal inequality). Our World in Data
- Kafka event sourcing overview and practical patterns. Tinybird
- FastAPI response_model behavior and schema generation. FastAPI docs
- Open Graph best practices (title/description/type guidance). Ahrefs