Skip to main content
Pro Feature. Requires a Pro or Ultra subscription. Get started at api.mathematicalcompany.com
What is this? Microstructure invariance gives you a model-free way to estimate ‘normal’ trade sizes and price impact for any market. The key insight: when you adjust for volatility and volume, the distribution of bet sizes is the same across all liquid markets. Use it to detect unusually large (potentially informed) trades and estimate your own market impact.

Microstructure Invariance

Horizon implements the Kyle-Obizhaeva (2016) microstructure invariance framework. The invariance hypothesis states that the distribution of risk transferred per bet, measured in units of business time, is constant across markets and time periods. This provides a model-free approach to estimating natural bet sizes, market impact, and detecting informed trading. All computation runs in Rust via PyO3.

Trading Activity

Estimate the rate of bet arrivals from volume, volatility, and price data.

Invariant Bet Size

Compute the natural bet size implied by the invariance hypothesis.

Market Impact

Kyle-Obizhaeva square-root impact model calibrated from trading activity.

Informed Trade Detection

Flag trades whose size exceeds the invariant bet size threshold.

Why Invariance?

Traditional market impact models require calibration to specific venues. The invariance hypothesis provides a universal scaling law: markets that differ in volume, volatility, and tick size all share the same distribution of bets when measured in the right units. This means you can estimate impact and detect informed trading without venue-specific calibration. The key scaling relationships are:
  • Trading activity: gamma = volume * volatility / price (dollar-risk per unit time)
  • Invariant bet size: V* = volume / (gamma^(2/3)) (natural trade size)
  • Impact coefficient: lambda ~ gamma^(1/3) (Kyle’s lambda from invariance)
Invariance is especially useful in prediction markets where historical calibration data is sparse. The framework transfers impact estimates from liquid markets to illiquid ones through the universal scaling law.

API

hz.trading_activity

Compute the Kyle-Obizhaeva trading activity measure (gamma) from market observables.
import horizon as hz

gamma = hz.trading_activity(
    volume=50000.0,       # daily volume in contracts
    volatility=0.02,      # daily return volatility
    price=0.55,           # current market price
)
print(f"Trading activity: {gamma:.4f}")
ParameterTypeDescription
volumefloatTrading volume over the period (positive)
volatilityfloatReturn volatility over the same period (positive)
pricefloatCurrent market price (positive)
Returns float: the trading activity measure gamma.

hz.invariant_bet_size

Compute the natural bet size implied by the invariance hypothesis.
import horizon as hz

v_star = hz.invariant_bet_size(
    volume=50000.0,
    volatility=0.02,
    price=0.55,
)
print(f"Invariant bet size: {v_star:.1f} contracts")
ParameterTypeDescription
volumefloatTrading volume over the period (positive)
volatilityfloatReturn volatility over the same period (positive)
pricefloatCurrent market price (positive)
Returns float: the invariant bet size in contracts.

hz.kyle_obizhaeva_impact

Estimate price impact using the Kyle-Obizhaeva square-root model. Impact scales as lambda * sqrt(size / V*) where lambda is derived from trading activity.
import horizon as hz

impact = hz.kyle_obizhaeva_impact(
    size=500.0,           # trade size in contracts
    volume=50000.0,
    volatility=0.02,
    price=0.55,
)
print(f"Expected impact: {impact:.4f}")  # in price units
print(f"Impact in bps: {impact / 0.55 * 10000:.1f}")
ParameterTypeDescription
sizefloatTrade size to evaluate (positive)
volumefloatTrading volume over the period (positive)
volatilityfloatReturn volatility (positive)
pricefloatCurrent market price (positive)
Returns float: estimated price impact in price units.

hz.is_informed_trade

Test whether a trade size exceeds a multiple of the invariant bet size, flagging potential informed trading.
import horizon as hz

informed = hz.is_informed_trade(
    size=2000.0,
    volume=50000.0,
    volatility=0.02,
    price=0.55,
    threshold=3.0,        # flag if size > 3x invariant bet size
)
print(f"Informed trade: {informed}")  # True or False
ParameterTypeDescription
sizefloatObserved trade size (positive)
volumefloatTrading volume over the period (positive)
volatilityfloatReturn volatility (positive)
pricefloatCurrent market price (positive)
thresholdfloatMultiple of invariant bet size to flag (default: 2.0)
Returns bool.

hz.invariance_analysis

Run a complete invariance analysis, returning all derived quantities in a single call.
import horizon as hz

result = hz.invariance_analysis(
    volume=50000.0,
    volatility=0.02,
    price=0.55,
    trade_size=500.0,
)
print(f"Trading activity:    {result.trading_activity:.4f}")
print(f"Invariant bet size:  {result.invariant_bet_size:.1f}")
print(f"Impact coefficient:  {result.impact_coefficient:.6f}")
print(f"Expected impact:     {result.expected_impact:.4f}")
print(f"Size ratio:          {result.size_ratio:.2f}")
print(f"Is informed:         {result.is_informed}")
ParameterTypeDescription
volumefloatTrading volume (positive)
volatilityfloatReturn volatility (positive)
pricefloatCurrent market price (positive)
trade_sizefloatTrade size to evaluate (positive)

InvarianceResult Type

FieldTypeDescription
trading_activityfloatKyle-Obizhaeva gamma measure
invariant_bet_sizefloatNatural bet size V* in contracts
impact_coefficientfloatKyle’s lambda from invariance scaling
expected_impactfloatEstimated price impact for the given trade size
size_ratiofloattrade_size / invariant_bet_size
is_informedboolWhether size_ratio exceeds 2.0

Pipeline Integration

hz.invariance_monitor

Pipeline function that computes invariance metrics each cycle and injects them into ctx.params["invariance"].
import horizon as hz

def impact_aware_strategy(ctx):
    inv = ctx.params.get("invariance")
    if inv is None:
        return []

    mid = ctx.feed.price

    # Scale position size inversely with impact
    base_size = 50
    if inv["expected_impact"] > 0.01:
        size = max(5, int(base_size * 0.01 / inv["expected_impact"]))
    else:
        size = base_size

    # Widen spread when informed trading is detected
    spread = 0.05 if inv["is_informed"] else 0.02

    return [
        hz.quote(ctx, hz.Side.Yes, hz.OrderSide.Buy, mid - spread, size),
        hz.quote(ctx, hz.Side.Yes, hz.OrderSide.Sell, mid + spread, size),
    ]

hz.run(
    name="invariance-mm",
    markets=["0xcondition..."],
    pipeline=[
        hz.invariance_monitor(),
        impact_aware_strategy,
    ],
    interval=1.0,
)
The ctx.params["invariance"] dict contains:
KeyTypeDescription
trading_activityfloatCurrent gamma measure
invariant_bet_sizefloatCurrent V* estimate
impact_coefficientfloatKyle’s lambda
expected_impactfloatImpact for a 1x V* trade
is_informedboolWhether recent flow exceeds 2x V*

Mathematical Background

Kyle and Obizhaeva (2016) propose that the distribution of a “bet” — a portfolio decision by an informed trader — is invariant across markets and time when measured in business time. Business time is defined by trading activity gamma = sigma * V / P, where sigma is volatility, V is volume, and P is price.The key predictions are:
  • Bet size scales as V * gamma^(-2/3)
  • Number of bets per period scales as gamma^(2/3)
  • Price impact per bet scales as gamma^(1/3)
These scaling laws have been validated empirically across equities, futures, and FX markets.
The Kyle-Obizhaeva impact model predicts that temporary price impact scales as the square root of trade size normalized by the invariant bet size:Delta_P = lambda * sigma * sqrt(Q / V*)where lambda is a universal constant (approximately 1), sigma is volatility, Q is trade size, and V* is the invariant bet size. The square-root law arises from the assumption that informed traders optimally split their bets.
Under invariance, the natural trade size V* represents the equilibrium bet of an informed trader. Trades significantly larger than V* (e.g., 2-3x) are unlikely to come from informed traders optimally splitting orders. Instead they suggest either: (1) highly informed traders with very short-lived information, or (2) uninformed institutional flow. Both cases warrant wider spreads.