Skip to main content

Horizon Kelly Criterion

Horizon includes a complete suite of Kelly criterion functions for optimal position sizing in prediction markets, equities, options, and crypto. All functions are implemented in Rust for maximum performance and exposed to Python via PyO3.
The Kelly criterion tells you the theoretically optimal fraction of your bankroll to wager given your edge. In practice, fractional Kelly (typically 0.25x to 0.5x) is preferred to reduce variance and account for estimation error.
Kelly works for any binary outcome with a known probability and price — prediction market contracts, stock directional bets, option payoffs, or crypto positions. The math is the same: estimate the true probability, compare to the market-implied price, and size accordingly.

Overview

Edge Calculation

hz.edge() computes the expected value of a bet given your probability estimate and the market price.

Full Kelly

hz.kelly() and hz.kelly_no() compute the optimal fraction for Yes and No sides.

Fractional Kelly

hz.fractional_kelly() scales the Kelly fraction to reduce variance and risk of ruin.

Position Sizing

hz.kelly_size() converts the fraction into a concrete position size in contract units.

Core Functions

hz.edge

Compute the expected edge (expected value) of a Yes bet.
The edge is fair_prob - market_price. A positive edge on Yes means the market underprices the event.

hz.kelly

Full Kelly fraction for the Yes side.
Formula: (fair_prob * (1 - market_price) - (1 - fair_prob) * market_price) / (1 - market_price) If the result is negative, there is no edge on the Yes side.

hz.kelly_no

Full Kelly fraction for the No side.
Use this when you believe the event is less likely than the market implies and want to bet No.

hz.fractional_kelly

Scale the Kelly fraction by a conservative multiplier.
Half-Kelly (fraction=0.5) is the most common choice in practice. It achieves 75% of the growth rate of full Kelly while cutting variance in half. Quarter-Kelly (fraction=0.25) is even more conservative and suitable when your probability estimates are noisy.

hz.kelly_size

Convert a Kelly fraction into a concrete position size in contract units.
Returns the number of contracts to buy. A return value of 0.0 or negative means no bet.

hz.multi_kelly

Optimal sizing across multiple simultaneous positions.
multi_kelly returns Kelly fractions (not contract sizes). The sum of fractions will not exceed max_total. This is preferred over calling kelly independently for each position, which could over-allocate capital.

hz.liquidity_adjusted_kelly

Adjusts the Kelly size for market impact based on available liquidity.
The function computes a raw Kelly size then scales it down using a square-root dampening factor based on the ratio of available liquidity to the raw size. The result is capped at both max_size and available_liquidity.

Pipeline Helpers

Horizon provides two helper functions designed for use inside hz.run() pipeline functions.

hz.kelly_sizer

Factory function that returns a pipeline-compatible sizing function for use with hz.run().
Returns a callable (Context, float) -> float that extracts the market price from ctx.feeds and calls hz.kelly_size() internally.

hz.kelly_sizer_with_liquidity

Like kelly_sizer but adjusts for available liquidity using hz.liquidity_adjusted_kelly().
Returns a callable (Context, float) -> float that extracts both market price and available liquidity from ctx and calls hz.liquidity_adjusted_kelly() internally.

Examples

Basic Kelly Sizing

Multi-Position Portfolio

Liquidity-Aware Sizing

Kelly in a Pipeline

The most common usage is inside an hz.run() pipeline where Kelly determines order size dynamically.

Comparing Kelly Fractions


Mathematical Background

For a binary outcome (Yes/No) with market price p and your estimated probability q:Yes Kelly fraction = (q * (1-p) - (1-q) * p) / (1-p)No Kelly fraction = ((1-q) * p - q * (1-p)) / pThis maximizes the expected logarithm of wealth (geometric growth rate).
Full Kelly is optimal only if:
  1. Your probability estimates are perfectly calibrated
  2. You have infinite time horizon
  3. You can tolerate extreme drawdowns
In practice, none of these hold. Fractional Kelly (f < 1.0) provides:
  • Lower variance (proportional to f^2)
  • Lower drawdowns
  • Robustness to estimation error
  • Only marginally lower expected growth (at half-Kelly, growth is 75% of full Kelly)
When holding multiple positions, independent Kelly calculations can over-allocate capital. hz.multi_kelly() solves the joint optimization problem, ensuring total allocation respects the bankroll constraint.
Kelly sizing assumes your probability estimates are accurate. If your model is poorly calibrated, Kelly will aggressively over-size positions. Always validate calibration using hz.backtest() with the outcomes parameter before deploying Kelly sizing in production.