The short version. The math SDK is an open-source Python toolkit. You describe your game (board, symbols, pays, features, bet modes), simulate tens or hundreds of thousands of rounds per mode, let its optimizer re-weight those rounds to hit your RTP and hit-rate targets, check the results, and upload three kinds of file per game: index.json, a lookup table per mode and a compressed books file per mode.
1. Set up
You need Python 3.12 or newer and pip. Rust (Cargo) is only needed for the optimizer. The repository's make setup creates the environment. The SDK ships sample games covering the common ways to pay: lines, ways, scatter (pay anywhere), cluster, and expanding wilds with prize collection, plus tumbling (cascading) reels.
2. Define the game
Start by copying the template game. A game folder holds:
game_config.py: the game's ID, its win cap (max win), win type, target RTP, reels, pay table, special symbols, free spin triggers and bet modes.gamestate.py,game_executables.py,game_calculations.py,game_events.py,game_override.py: how a round plays out and which events it records.reels/: the reel strips.run.py: how many rounds to simulate per mode, threads, compression, and which steps to run.
The library/ folder, where results land, is generated.
3. Bet modes
Each mode is a BetMode with a name, a cost (a multiple of the base bet), an RTP, a max win and its distributions. A typical game has a base mode at 1× and a bonus buy (the SDK's lines sample prices it at 100×). Flags control whether a mode persists between spins (is_feature), whether it's bought directly (is_buybonus), and whether the round ends automatically. Engine allows at most 20 modes.
4. Distributions and quotas
This is the part people find least obvious. Each mode is split into criteria, outcome classes such as zero wins, base game wins, free game rounds and win-cap rounds. Each criterion gets a quota, its share of the simulations, and conditions such as which reel weights to use or whether to force the feature or the win cap. Before the run, every simulation number is assigned a criterion, and a round is re-run until it meets its criterion.
Why bother? Without it, a million random spins might contain only a handful of max-win rounds, and players would see the same rare round again and again. Quotas make sure every part of the distribution has enough distinct rounds; the optimizer then sets how often each part actually happens.
5. Simulate
Run make run GAME=<id>. Start small and uncompressed while you debug; the docs recommend 100,000 or more simulations per mode for production, and Engine's own guidance for slots is 100,000 to 1,000,000. Every simulated round starts with a weight of 1.
6. Optimize
The optimizer is a Rust genetic algorithm that rewrites the lookup table's weights to hit your targets. You configure it per mode with:
- conditions: per-criterion targets, setting two of RTP, average win and hit rate. Order matters: put the win cap first.
- scaling: bias applied to particular win ranges.
- parameters: how many trials, and minimum and maximum mean-to-median ratios, which is how you steer volatility.
The output is a re-weighted lookup table per mode. If you're new to how RTP, hit rate and volatility pull against each other, read RTP, volatility and hit rate, in plain words first.
7. Analyse and check
The analysis step writes a PAR sheet (an .xlsx of hit rates and RTP contributions), using force files that record which book IDs match which outcomes. A verification script mirrors Engine's checks before upload and writes a summary. Treat it as a first pass: a few of its thresholds differ from Engine's current docs (see the requirements lesson), and the docs win.
8. Export and upload
The files to upload are in library/publish_files/:
| File | What it is |
|---|---|
index.json | Each mode's name, cost, books file and lookup table. Mode names are up to 32 letters, numbers, dashes or underscores, unique ignoring case. |
lookUpTable_<mode>_0.csv | Simulation number, weight, payout per row. Integers only, no header. |
books_<mode>.jsonl.zst | The rounds, compressed. One round at most 1 MiB; one file under 4 GiB (Engine's math tests cap a single books file at 4.2 GB and a mode at 10,000,000 events). |
In Studio, upload the contents of publish_files and publish a new math version. Republishing identical files doesn't create a new version. Payouts are integers in hundredths of the base bet (100 = 1×), and a mode's RTP is its expected payout divided by its cost.
Common mistakes
- Too few simulations, so players see repeated rounds.
- Criteria that are hard to meet, which makes simulation very slow.
- Optimizer conditions in the wrong order (the win cap belongs first).
- No win-cap round in the books. Reviewers ask for a win-cap round ID per mode.
- Lookup-table payouts that don't match the books exactly.
Volt's Math workspace keeps these settings as plain fields and runs the simulation on your Mac; the files it exports are the same ones described here.
Frequently asked questions
What language is the Stake Engine math SDK written in?
Python (3.12 or newer), with an optimizer written in Rust.
How many simulations does a Stake Engine game need?
Engine's guidance for slots is 100,000 to 1,000,000 per mode. The SDK recommends at least 100,000 per mode for production.
What files do I upload for the math?
index.json, one lookup table CSV per mode and one compressed books file (.jsonl.zst) per mode, from the SDK's publish_files folder.
Can I use my own math engine instead of the SDK?
Yes. Engine accepts any math as long as the files match its format: the index, lookup tables and books described above.
How do I hit an exact RTP?
Simulate first, then run the optimizer, which re-weights the lookup table to land on your RTP and hit-rate targets for each mode.
Sources: the Stake Engine math SDK docs (quickstart, game structure, bet modes, distributions, optimization, file info) and Engine's math file format and math verification pages. Checked October 22, 2026.