Docs menu
Recipe

Same answer twice

Store a result in a way that lets you get exactly the same answer again later.

Why results vary

Optimize measures risk by simulating the slate 100,000 times. Two identical requests use different random draws, so the figures differ slightly, and once in a while the allocation’s share of Kelly moves a step. Neither answer is wrong; they are two samples.

Every response returns the seed it used. Send that seed back with the same request and the result is identical, to the cent. You can also choose your own seed on the first call.

Needs KELLYIQ_API_KEY. Uses two optimizes each time it runs.

The script

reproducible-results
import json
import os
import requests

API = os.environ.get("KELLYIQ_API_BASE", "https://api.kellyiq.com")
KEY = os.environ["KELLYIQ_API_KEY"]

slate = {
    "bankroll": 1000,
    # Settings in the request, not from your app settings, which you might change later.
    "maxTotalExposure": 0.5,
    "perBetCap": 0.2,
    "useAccountDefaults": False,
    "picks": [
        {"id": "lad", "sport": "baseball_mlb", "team": "Dodgers", "opponent": "Padres",
         "odds": -145, "opponentOdds": 125, "confidence": "STRONG_EDGE"},
        {"id": "chc", "sport": "baseball_mlb", "team": "Cubs", "opponent": "Cardinals",
         "odds": 120, "opponentOdds": -140, "confidence": "MODERATE_EDGE"},
    ],
}


def optimize(body):
    resp = requests.post(f"{API}/v1/optimize", headers={"Authorization": f"Bearer {KEY}"},
                         json=body, timeout=60)
    if not resp.ok:
        raise SystemExit(f"HTTP {resp.status_code}: {resp.text[:500]}")
    return resp.json()


# First run: no seed, so the server picks one and returns it.
first = optimize(slate)

# Store the request and what produced the result, together.
record = {
    "request": slate,
    "seed": first["seed"],
    "engineVersion": first["engineVersion"],
    "stakes": first["allocation"]["stakes"],
}
with open("result.json", "w") as f:
    json.dump(record, f, indent=2)

# Later: resend the same request with the stored seed.
with open("result.json") as f:
    saved = json.load(f)
again = optimize({**saved["request"], "seed": saved["seed"]})

if again["engineVersion"] != saved["engineVersion"]:
    print("The engine has changed since this result was stored, so numbers may differ.")
same = again["allocation"]["stakes"] == saved["stakes"]
print("Identical allocation:", same)
if not same:
    raise SystemExit(1)

When the engine changes

A seed reproduces a result on the same engine. When we improve the optimizer, engineVersion changes and the same seed can give different numbers. That is why the script stores engineVersion with the seed: if they differ, you know why the numbers do.