Skip to main content

Keeks

License: MIT

Python bet sizing and bankroll simulation for developers modeling repeated binary outcomes.

Given your estimated win probability, payoff, loss, and normalized transaction cost, Keeks calculates a model-derived fraction of the current bankroll to stake. You can then run the same rule over repeated trials and inspect the bankroll path.

Nine-strategy risk benchmark — what growth, drawdown and early-stop behaviour each shipped strategy actually produces under identical, seeded assumptions, and how that changes with edge, cost, probability-estimate error and the bankroll's loss cap. Regenerate every number with uv run python benchmarks/strategy_benchmark.py.

Install

pip install keeks

Keeks supports Python 3.10 through 3.14.

Get a bet fraction

from keeks.binary_strategies import KellyCriterion

bankroll = 1_000.0
strategy = KellyCriterion(
    payoff=1.0,
    loss=1.0,
    transaction_cost=0.01,
)

fraction = strategy.evaluate(probability=0.55, current_bankroll=bankroll)
amount = bankroll * fraction

print(f"Bankroll fraction: {fraction:.4%}")
print(f"Amount from a $1,000 bankroll: ${amount:.2f}")
Bankroll fraction: 9.0009%
Amount from a $1,000 bankroll: $90.01

evaluate() returns a fraction, not a currency amount. The example multiplies that fraction by the current bankroll only to make the result concrete.

Compare repeated-bet strategies

Keeks includes a headless comparison example that gives every strategy a fresh bankroll under the same repeated-bet inputs:

python -m examples.strategy_comparison

Bankroll paths from the strategy comparison example

The chart is one simulated comparison under the example's assumptions. It does not validate the probability estimate or predict future results.

Choose a strategy

All nine strategies expose evaluate(probability, current_bankroll), but their constructors and sizing rules differ.

Strategy Choose it when you want to model
KellyCriterion Full Kelly sizing from a binary win probability, payoff, loss, and cost.
FractionalKellyCriterion A fixed fraction of the full-Kelly result.
DrawdownAdjustedKelly Kelly sizing scaled by an acceptable-drawdown input.
OptimalF A geometric-growth rule based on a supplied win rate, with a risk-fraction cap.
FixedFractionStrategy A constant fraction above a minimum probability; useful as a baseline.
CPPIStrategy A cushion-based rule relative to a bankroll floor.
DynamicBankrollManagement A fraction adjusted from recent recorded outcomes.
MertonShare A CRRA risk-aversion rule adapted to binary outcomes.
NaiveStrategy A positive-expected-value rule without utility-based sizing.

See the strategy API for constructor parameters and formulas.

Simulate a bankroll

Once you have chosen a strategy, pass it and a fresh BankRoll to a simulator:

from keeks.bankroll import BankRoll
from keeks.binary_strategies import FractionalKellyCriterion
from keeks.simulators.repeated_binary import RepeatedBinarySimulator

bankroll = BankRoll(
    initial_funds=1_000.0,
    percent_bettable=0.8,
    max_draw_down=0.3,
)
strategy = FractionalKellyCriterion(
    payoff=1.0,
    loss=1.0,
    transaction_cost=0.01,
    fraction=0.5,
)
simulator = RepeatedBinarySimulator(
    payoff=1.0,
    loss=1.0,
    transaction_costs=0.01,
    probability=0.55,
    trials=1_000,
)

simulator.evaluate_strategy(strategy, bankroll)

print(f"Final bankroll: ${bankroll.total_funds:.2f}")
bankroll.plot_history(fname="bankroll-history.png")

The test suite executes this exact block as a seeded, down-scaled demo — the simulator clamped to 50 trials with a fixed seed and the plot rendered headlessly — so the example stays runnable; the narrative above describes the full 1,000-trial run.

Simulation mutates the bankroll and records its history. Use matching payoff and Simulation mutates the bankroll and records its history. Use matching payoff and loss assumptions in the strategy and simulator: Keeks enforces the match. Every simulator's evaluate_strategy checks that a BaseStrategy instance's payoff and loss agree with the odds it settles at, and raises ValueError on a mismatch. Duck-typed strategies that carry no odds of their own are not checked — their compatibility stays your responsibility. The cost assumption is a separate matter — see the note below.

Repeated sizing is not one-time pricing

strategy.evaluate(...) answers a repeated-bet question:

Given this binary model, what fraction of the current bankroll does this rule allocate now?

find_indifference_price(...) and supported strategy.calculate_max_entry_price(...) methods answer a different question:

Given possible outcomes and their probabilities, what is the maximum entry price that leaves modeled utility unchanged for a one-time gamble?

Run the shipped decision-theory example with:

python -m examples.st_petersburg_paradox

See examples/st_petersburg_paradox.py and the utilities documentation for the one-time workflow.

Safety semantics and model limits

  • Strategy fractions are floored at zero and capped so the modeled loss plus transaction cost cannot allocate more than the current bankroll.
  • BankRoll can restrict the bettable percentage and stop a simulation when a withdrawal breaches its configured maximum drawdown.
  • These constraints apply to the values in Keeks' binary model. They do not prevent losses, verify your probability estimate, or model spreads, slippage, market impact, venue-specific commissions, correlated positions, or portfolio rebalancing.
  • A strategy's transaction_cost is a per-unit fractional cost that scales with stake size. A simulator's transaction_costs (plural) is a flat, absolute bankroll amount charged once per settled bet. The two are different units — passing the same number to both models two different real-world costs, and Keeks does not convert between them.
  • Kelly-family strategies gate bets behind a default min_probability=0.5: a trial probability below it returns 0.0 regardless of payoff asymmetry. With payoff=10, loss=1, and a 0.3 win probability, the true Kelly fraction is about 0.23, but evaluate returns 0.0 until you pass a lower min_probability. OptimalF hardcodes the same 0.5 gate.

Documentation and examples

References

Disclaimer

Keeks is for educational purposes. It does not provide investment, legal, or tax advice. Models and simulations can be wrong, and financial loss is possible. You are responsible for validating your inputs and deciding whether any real-world use is appropriate.

Contributing

Contributions are welcome. To set up the project and run its checks:

git clone https://github.com/wdm0006/keeks.git
cd keeks
make setup
make install-dev
make test
make lint

Build the documentation with:

make docs

Keeks is available under the MIT License.

Metadata

Release files for keeks 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keeks 0.8.0
File Size Uploaded
keeks-0.8.0.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for keeks 0.8.0
File Interpreter ABI Platform
keeks-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.3 MB

Release files / keeks-0.8.0.tar.gz

Download URL keeks-0.8.0.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
bee34b256cf03f38109758cafd8335e1bfdca72572e274484238340c1c21152a
BLAKE2b-256 checksum
How to use checksums
f0c16c427d1f82e2270d2250334c86249b7ac40f3e6334f8421aa6f21577cdb0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release files / keeks-0.8.0-py3-none-any.whl

Download URL keeks-0.8.0-py3-none-any.whl
Size 54.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
01604f92671e2039128d7590b9fe62eb131676337561d74c2e660dc8df5abe56
BLAKE2b-256 checksum
How to use checksums
2764fda40f5c7c1e6e9fe30e01e5149328824abb6d5377b4a4035e9f164fa38b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.6.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page