Binary Strategies¶
Binary strategies are strategies that reason about singular binary bets. These bets have some payoff if they win, some loss if they lose, and a transaction cost regardless. Bets are not continuous, they either win or lose.
Base Strategy¶
- class keeks.binary_strategies.base.BaseStrategy(payoff: float, loss: float, transaction_cost_rate: float = 0)[source]¶
Bases:
ParameterMixin,ABCAbstract base class for all binary betting strategies.
This class defines the interface that all binary betting strategies must implement. Concrete strategy implementations should inherit from this class and implement the evaluate method.
Beyond
evaluate, the simulators inkeeks.simulatorsresolve two optional hooksgetattr-style once per run and fire them around each settled bet. This base class implements neither, so a strategy may omit them freely: the simulator skips the hook and the strategy stays stateless. A stateful strategy that needs the feedback but omits the hook simply sizes from stale state - there is no error, which is why the contract is documented here.update_bankroll(current_bankroll)- fired at the top of every trial, beforeevaluate, with the bankroll’s current total funds, so an adaptive strategy can size from the bankroll as it stands (keeks.binary_strategies.CPPIStrategyratchets its floor from it). Not fired once the bankroll is depleted - the simulation has already stopped.record_settlement(won, realized_returns)- fired once per settled bet, after the bankroll transfer:wonholds the bet’s realized outcome (one entry per settled option - a singleTrueorFalsefor the binary simulators) andrealized_returnsthe realized simple return per option on the bankroll, positive or negative. Not fired for trials the strategy sits out (a zero stake), and not fired for a settlement a bankroll safeguard refused - a refused settlement leaves the bankroll unchanged and stops the simulation, so there is no result to report (keeks.binary_strategies.DynamicBankrollManagementtracks streaks and volatility through this hook).
A hook should not raise: exceptions other than the simulator’s own
RuinErrorsettlement handling propagate to the caller.- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
This method is only implemented for strategies derived from utility theory (e.g., KellyCriterion, MertonShare). For heuristic strategies without underlying utility functions, this method raises NotImplementedError.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble
probabilities (array-like) – The probability of each outcome (must sum to ≤ 1)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance for binary search. Must be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay for the gamble
- Return type:
- Raises:
NotImplementedError – If the strategy does not have a utility-theoretic basis for calculating indifference prices.
ValueError – Raised by every shipped implementation if the gamble arrays are malformed or if any scalar control falls outside the ranges documented above. Overrides should validate with
keeks.utils._validate_entry_price_scalarsbefore doing work or mutating internal state.
- Warns:
RuntimeWarning – Emitted by the utility-based implementations, which delegate to
keeks.utils.find_indifference_price, when the gamble is still worth buying atcurrent_wealth * max_search_fraction. The returned price is then that bound rather than a solved indifference price. Seefind_indifference_pricefor details.
Notes
This method answers: “What’s the maximum I’d pay to participate in this one-time gamble?” It’s fundamentally different from evaluate(), which answers: “How much should I bet in a repeated betting scenario?”
Only strategies with underlying utility functions can meaningfully answer the entry price question. Heuristic strategies (e.g., FixedFraction, CPPI) don’t have utility functions to derive indifference prices from.
- abstract evaluate(probability: float, current_bankroll: float) float[source]¶
Evaluate the strategy for a given probability.
- Parameters:
- Returns:
The proportion of the bankroll to bet -
0.0for a nonpositive bankroll, where there is nothing left to stake.- Return type:
- Raises:
ValueError – If
probabilityis not finite and between 0 and 1 inclusive, or ifcurrent_bankrollis not finite.
- get_max_safe_bet(current_bankroll: float) float[source]¶
Calculate the maximum safe bet size based on current bankroll.
- Parameters:
current_bankroll (float) – The current bankroll to use for calculations.
- Returns:
The maximum safe bet size as a proportion of bankroll. Zero when there is nothing left to stake (
current_bankroll <= 0).- Return type:
- Raises:
ValueError – If
current_bankrollis not finite.
Notes
The maximum stake that cannot drive the bankroll negative is
current_bankroll / (loss + transaction_cost_rate); expressed as a proportion of the bankroll the bankroll term cancels, so for a positive bankroll this is exactlymin(1.0, 1 / (loss + transaction_cost_rate)). A non-positive bankroll has no safe stake at all, so the answer there is0.0.
Naive Strategy¶
- class keeks.binary_strategies.simple.NaiveStrategy(payoff: float, loss: float, transaction_cost_rate: float)[source]¶
Bases:
BaseStrategyA simple betting strategy that bets based on expected value.
This strategy calculates the expected value of a bet and bets a fixed fraction of the bankroll if the expected value is positive.
- Parameters:
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Naive strategy is risk-neutral and simply pays the expected value.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble
probabilities (array-like) – The probability of each outcome (must sum to ≤ 1)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance (unused, kept for API consistency). Must still be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay (the expected value, bounded above by
current_wealth * max_search_fraction)- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Naive strategy is risk-neutral: willing to pay exactly the expected value. This is the classic “expected value maximizer” that doesn’t account for risk. The expected value can be unbounded (e.g. the St. Petersburg paradox), so the result is capped the same way every other strategy caps it. Pass
max_search_fraction=1.0to allow paying the entire bankroll.
- evaluate(probability: float, current_bankroll: float) float[source]¶
Calculate the bet size based on expected value.
The expected value is calculated as: EV = (probability * payoff) - ((1 - probability) * loss) - transaction_cost_rate
If EV is positive, bet a fixed fraction of the bankroll. If EV is negative, do not bet.
Fixed Fraction Strategy¶
- class keeks.binary_strategies.simple.FixedFractionStrategy(fraction: float, payoff: float, loss: float, transaction_cost_rate: float = 0, min_probability: float = 0.5)[source]¶
Bases:
BaseStrategyA simple strategy that bets a fixed percentage of the bankroll.
This strategy ignores probabilities and odds completely, and simply wagers a fixed fraction of the bankroll. It can be used as a baseline for comparison or as a simple approach for risk management.
- Parameters:
fraction (float) – The fixed fraction of the bankroll to bet (between 0 and 1).
payoff (float) – The amount won per unit bet on a successful outcome.
loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float, default=0) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
min_probability (float, default=0.5) – The minimum probability required to place a bet.
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Fixed fraction strategy simply pays a fixed fraction of current wealth.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble (unused)
probabilities (array-like) – The probability of each outcome (unused)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance (unused, kept for API consistency). Must still be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay (fixed fraction of wealth)
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Fixed fraction strategy doesn’t analyze the gamble - it simply commits a fixed fraction of wealth regardless of the opportunity. This is a mechanical rule-based approach, not optimization-based.
CPPI Strategy¶
- class keeks.binary_strategies.simple.CPPIStrategy(floor_fraction: float, multiplier: float, initial_bankroll: float, payoff: float, loss: float, transaction_cost_rate: float = 0, min_probability: float = 0.5)[source]¶
Bases:
BaseStrategyImplementation of Constant Proportion Portfolio Insurance (CPPI) for binary betting.
This strategy maintains a dynamic floor below which the bankroll should not fall, and invests a multiple of the cushion (amount above floor) in each bet.
- Parameters:
floor_fraction (float) – The fraction of initial bankroll to maintain as a floor.
multiplier (float) – The multiplier to apply to the cushion for determining exposure.
initial_bankroll (float) – The initial bankroll amount.
payoff (float) – The amount won per unit bet on a successful outcome.
loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
min_probability (float, default=0.5) – The minimum probability required to place a bet.
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
CPPI protects a floor value, so entry price is based on the cushion above the floor, scaled by the multiplier.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble (unused in basic calculation)
probabilities (array-like) – The probability of each outcome (unused in basic calculation)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance (unused, kept for API consistency). Must still be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay (based on cushion above floor)
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above. Validation happens before any internal bankroll or floor state is updated.
Notes
CPPI maintains a floor value and bets a multiple of the cushion. For entry price, we apply the same logic: pay multiplier × cushion, but never more than the cushion itself (to maintain floor).
Dynamic Bankroll Management¶
- class keeks.binary_strategies.simple.DynamicBankrollManagement(base_fraction: float, payoff: float, loss: float, transaction_cost_rate: float, window_size: int = 10, max_fraction: float = 0.2, min_fraction: float = 0.05, min_probability: float = 0.5)[source]¶
Bases:
BaseStrategyA dynamic bankroll management strategy that adjusts bet sizes based on performance.
This strategy adjusts the base bet fraction based on: 1. Recent performance (win/loss streak) 2. Volatility of returns 3. Current drawdown level 4. Probability of success
- Parameters:
base_fraction (float) – The base fraction of bankroll to bet.
payoff (float) – The amount won per unit bet on a successful outcome.
loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
window_size (int, default=10) – The number of recent results to consider for adjustments.
max_fraction (float, default=0.2) – The maximum fraction of bankroll that can be bet.
min_fraction (float, default=0.05) – The minimum fraction of bankroll to bet.
min_probability (float, default=0.5) – The minimum probability required to place a bet. Below this the strategy returns 0.0 (no bet).
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Dynamic strategy adjusts based on history, but for a one-time decision with no history, we use the base fraction.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble (unused)
probabilities (array-like) – The probability of each outcome (unused)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance (unused, kept for API consistency). Must still be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay (base fraction of wealth)
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Dynamic strategy normally adjusts based on recent performance history. For a one-time decision with no history, we fall back to the base_fraction. This represents a neutral starting point before dynamic adjustments.
- evaluate(probability: float, current_bankroll: float) float[source]¶
Calculate the bet size based on all adjustment factors.
- get_probability_factor(probability: float) float[source]¶
Calculate the adjustment factor based on probability.
Kelly Criterion¶
- class keeks.binary_strategies.kelly.KellyCriterion(payoff: float, loss: float, transaction_cost_rate: float, min_probability: float | None = None)[source]¶
Bases:
BaseStrategyImplementation of the Kelly Criterion for binary betting.
The Kelly Criterion is a mathematical formula that determines the optimal size of a series of bets to maximize long-term growth rate.
By default the gate is edge-aware: a bet the Kelly formula itself sizes positively is always sized, and a bet with no positive edge returns 0.0. An optional
min_probabilitylongshot gate can additionally refuse low-probability bets - loudly, when the refused bet is one the formula would have sized.- Parameters:
payoff (float) – The amount won per unit bet on a successful outcome: the net win, excluding the stake’s return - decimal odds minus one, so even money is
1.0. Unlike here,BinaryBetsModeland the multi-outcome layer readpayoffas the decimal odds themselves.loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
min_probability (float or None, optional) – An optional longshot gate: when set, bets with a win probability below it return 0.0 even when the Kelly fraction is positive, and a
UserWarningnames the suppressed fraction so the refusal is never silent. The default (None) sizes on edge alone: withpayoff=10,loss=1and probability 0.3, the true Kelly fraction of about 0.23 is placed instead of being zeroed.
Examples
>>> strategy = KellyCriterion(payoff=10, loss=1, transaction_cost_rate=0) >>> # Edge-aware by default: the formula sizes this longshot at 0.23. >>> round(strategy.evaluate(0.3, 1000.0), 4) 0.23 >>> # A bet with no positive edge refuses itself. >>> round(strategy.evaluate(0.05, 1000.0), 4) 0
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Kelly Criterion is derived from log utility maximization (CRRA with γ=1.0), so this uses log utility to find the indifference price.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble
probabilities (array-like) – The probability of each outcome (must sum to ≤ 1)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance for binary search. Must be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay for the gamble
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Kelly Criterion maximizes E[log(wealth)], which corresponds to CRRA utility with risk aversion γ=1.0 (log utility).
- evaluate(probability: float, current_bankroll: float) float[source]¶
Calculate the optimal Kelly bet size.
The Kelly Criterion formula is: f* = p / l - q / a
where: f* = fraction of current bankroll to bet a = payoff after transaction costs l = loss including transaction costs p = probability of winning q = probability of losing (1 - p)
Unlike the classic formula, which assumes the entire stake is lost, this formula explicitly accounts for the loss multiplier used by this library.
The gate is edge-aware: a bet the formula sizes positively is never silently zeroed. When
min_probabilityis set and the probability falls below it, a formula-positive bet is refused with a warning naming the suppressed fraction; a formula-negative bet refuses silently, because the formula itself declines it.
Fractional Kelly Criterion¶
- class keeks.binary_strategies.kelly.FractionalKellyCriterion(payoff: float, loss: float, transaction_cost_rate: float, fraction: float)[source]¶
Bases:
BaseStrategyImplementation of the Fractional Kelly Criterion for binary betting.
The Fractional Kelly Criterion applies a fraction to the full Kelly bet size, which can reduce volatility at the expense of long-term growth rate.
- Parameters:
payoff (float) – The amount won per unit bet on a successful outcome.
loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
fraction (float) – The fraction of the full Kelly bet to use (typically between 0 and 1).
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Fractional Kelly is more conservative than full Kelly, so we scale down the entry price proportionally. This assumes the fraction reflects increased risk aversion beyond log utility.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble
probabilities (array-like) – The probability of each outcome (must sum to ≤ 1)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance for binary search. Must be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay for the gamble
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Fractional Kelly (e.g., Half Kelly) is more conservative than full Kelly. We scale the entry price by the fraction, which is a reasonable approximation though not derived from first principles.
Drawdown-Adjusted Kelly¶
- class keeks.binary_strategies.kelly.DrawdownAdjustedKelly(payoff: float, loss: float, transaction_cost_rate: float, max_transaction_loss: float = 0.2)[source]¶
Bases:
BaseStrategyImplementation of the Drawdown-Adjusted Kelly Criterion for binary betting.
This strategy adjusts the Kelly bet size based on the maximum per-bet transaction loss it tolerates. It provides a more conservative approach by reducing the bet size to minimize the risk of large drawdowns.
- Parameters:
payoff (float) – The amount won per unit bet on a successful outcome.
loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
max_transaction_loss (float) – The maximum transaction loss tolerated per bet, as a fraction of the bankroll (0 to 1, exclusive) - a per-removal cap the sizing scale keys off, not peak-to-trough drawdown monitoring.
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Drawdown-Adjusted Kelly scales down Kelly based on drawdown tolerance. We apply the same scaling factor to the entry price.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble
probabilities (array-like) – The probability of each outcome (must sum to ≤ 1)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance for binary search. Must be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay for the gamble
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Uses the same drawdown adjustment factor as the betting strategy: drawdown_factor = min(1.0, max_transaction_loss / 0.5)
OptimalF¶
- class keeks.binary_strategies.simple.OptimalF(payoff: float, loss: float, transaction_cost_rate: float, win_rate: float, max_risk_fraction: float = 0.2)[source]¶
Bases:
BaseStrategyImplementation of the Optimal f strategy for binary betting.
This strategy calculates the optimal fraction of bankroll to bet based on the win rate and payoff ratio. It’s similar to Kelly but uses a different approach to risk management.
- Parameters:
payoff (float) – The amount won per unit bet on a successful outcome.
loss (float) – The amount lost per unit bet on an unsuccessful outcome.
transaction_cost_rate (float) – The transaction cost as a fraction of each unit staked (per-unit, not a fixed per-transaction amount).
win_rate (float) – The historical or expected win rate (between 0 and 1).
max_risk_fraction (float, default=0.2) – The maximum fraction of bankroll that can be risked on a single bet.
- calculate_max_entry_price(outcomes: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], probabilities: _Buffer | _SupportsArray[dtype[Any]] | _NestedSequence[_SupportsArray[dtype[Any]]] | bool | int | float | complex | str | bytes | _NestedSequence[bool | int | float | complex | str | bytes], current_wealth: float, tolerance: float = 0.01, max_search_fraction: float = 0.5) float[source]¶
Calculate maximum price willing to pay for a one-time gamble.
Optimal F maximizes geometric growth (like Kelly), so we use log utility.
- Parameters:
outcomes (array-like) – The possible payoffs from the gamble
probabilities (array-like) – The probability of each outcome (must sum to ≤ 1)
current_wealth (float) – Current wealth before the gamble. Must be finite and greater than 0.
tolerance (float, default=0.01) – Convergence tolerance for binary search. Must be finite and greater than 0.
max_search_fraction (float, default=0.5) – Maximum fraction of wealth to consider as upper bound. Must be finite and non-negative; values above 1.0 are allowed.
- Returns:
Maximum price willing to pay for the gamble
- Return type:
- Raises:
ValueError – If the gamble arrays are malformed, or if any scalar control falls outside the ranges documented above.
Notes
Optimal F, like Kelly Criterion, aims to maximize geometric growth, which corresponds to log utility (γ=1.0).
- evaluate(probability: float, current_bankroll: float) float[source]¶
Calculate the optimal f bet size based on win rate and payoff ratio.
This implementation follows Ralph Vince’s approach to Optimal f, which is based on maximizing the terminal wealth relative (TWR) for a given risk-to-reward profile.
- Parameters:
- Returns:
The optimal proportion of the bankroll to stake: Ralph Vince’s optimal f converted from a risk fraction to a stake fraction. The two coincide only when
loss + transaction_cost_rateis 1.- Return type: