Skip to main content
Ultra Feature. Requires an Ultra subscription. Get started at api.mathematicalcompany.com

Treasury Yield Curve

Prediction market positions tie up capital that could earn risk-free yield in Treasuries. When rates are high, a trade needs more edge to justify the opportunity cost. Horizon’s treasury module provides a live yield curve feed (via FRED), yield-adjusted Kelly sizing, breakeven edge calculation, and collateral optimization. All math is in Rust.

Overview

Yield-Adjusted Kelly

hz.yield_adjusted_kelly() reduces Kelly size by the risk-free rate opportunity cost.

Opportunity Cost

hz.opportunity_cost() and hz.breakeven_edge() quantify the hurdle rate for a trade.

Live Yield Feed

TreasuryFeed polls FRED for the full yield curve (1M through 30Y).

Collateral Optimizer

hz.collateral_optimizer() adjusts capital allocation based on curve steepness.

Core Functions

All math runs in Rust. Every output is guarded against NaN/Inf.

hz.yield_adjusted_kelly

Standard Kelly fraction minus the annualized opportunity cost of locked capital.
import horizon as hz

fraction = hz.yield_adjusted_kelly(
    prob=0.60,
    price=0.50,
    rfr=0.05,        # 5% risk-free rate
    duration_days=30,
    fraction=0.5,     # half-Kelly
)
print(f"Adjusted Kelly: {fraction:.4f}")
ParameterTypeDefaultDescription
probfloatrequiredEstimated probability of the event
pricefloatrequiredCurrent market price
rfrfloatrequiredRisk-free rate (annualized decimal)
duration_daysfloatrequiredExpected holding period in days
fractionfloat0.5Kelly fraction (0.5 = half-Kelly)
Returns 0.0 if the edge does not exceed the opportunity cost.

hz.yield_adjusted_kelly_size

Convert yield-adjusted Kelly fraction to a dollar position size.
size = hz.yield_adjusted_kelly_size(
    prob=0.60, price=0.50, rfr=0.05,
    duration_days=30, bankroll=10000.0,
    fraction=0.5, max_size=1000.0,
)
print(f"Position size: ${size:.2f}")
ParameterTypeDefaultDescription
probfloatrequiredEstimated probability
pricefloatrequiredMarket price
rfrfloatrequiredRisk-free rate
duration_daysfloatrequiredHolding period in days
bankrollfloatrequiredTotal bankroll
fractionfloat0.5Kelly fraction
max_sizefloatinfMaximum position size cap

hz.opportunity_cost

Dollar cost of locking capital at the risk-free rate.
cost = hz.opportunity_cost(notional=5000.0, rfr=0.05, duration_days=30)
print(f"Opportunity cost: ${cost:.2f}")  # ~$20.55
ParameterTypeDescription
notionalfloatCapital locked in the position
rfrfloatRisk-free rate (annualized)
duration_daysfloatHolding period in days

hz.breakeven_edge

Minimum edge required to beat the risk-free rate.
be = hz.breakeven_edge(rfr=0.05, duration_days=30)
print(f"Breakeven edge: {be:.4f}")  # ~0.41%
ParameterTypeDescription
rfrfloatRisk-free rate
duration_daysfloatHolding period in days

Feed

TreasuryFeed

Polls the Federal Reserve Economic Data (FRED) API for yield curve data.
import horizon as hz

feed = hz.TreasuryFeed(
    api_key="your_fred_api_key",  # or set FRED_API_KEY env var
    primary_series="DGS10",
)
ParameterTypeDefaultDescription
api_keystrFRED_API_KEY env varFRED API key
series_idslist[str]9 standard maturitiesTreasury series to fetch
primary_seriesstr"DGS10"Series used as main price
intervalfloat300.0Poll interval in seconds
Default series: DGS1MO, DGS3MO, DGS6MO, DGS1, DGS2, DGS5, DGS10, DGS20, DGS30. FeedSnapshot mapping:
  • price = primary series yield (decimal)
  • bid = short-term yield (DGS3MO)
  • ask = long-term yield (DGS30)
  • volume_24h = curve slope in basis points (long - short)

Pipeline Functions

hz.yield_adjusted_sizer

Size positions using yield-adjusted Kelly each cycle.
sizer = hz.yield_adjusted_sizer(
    rfr_feed="treasury",
    duration_days=30,
    fraction=0.5,
    bankroll=10000.0,
    max_size=1000.0,
)

hz.run(
    name="yield_aware",
    feeds={"treasury": hz.TreasuryFeed(), "market": hz.PolymarketBook("...")},
    pipeline=[sizer, my_strategy],
)
ParameterTypeDefaultDescription
rfr_feedstr"treasury"Feed name for the yield data
duration_daysfloat30.0Expected holding period
fractionfloat0.5Kelly fraction
bankrollfloat10000.0Total bankroll
max_sizefloat1000.0Maximum position size
Returns each cycle:
{
    "size": 245.0,
    "yield_adjusted_kelly": 0.049,
    "breakeven_edge": 0.004,
    "opportunity_cost": 20.55,
    "rfr": 0.05,
}

hz.opportunity_cost_filter

Skip trades where edge does not justify the opportunity cost.
filter_fn = hz.opportunity_cost_filter(
    rfr_feed="treasury",
    duration_days=30,
    min_edge_multiple=2.0,  # need 2x breakeven edge to trade
)
ParameterTypeDefaultDescription
rfr_feedstr"treasury"Feed name for yield data
duration_daysfloat30.0Holding period
min_edge_multiplefloat2.0Required multiple of breakeven edge

hz.collateral_optimizer

Adjusts capital allocation based on yield curve steepness.
optimizer = hz.collateral_optimizer(rfr_feed="treasury", max_utilization=0.8)
ParameterTypeDefaultDescription
rfr_feedstr"treasury"Feed name
max_utilizationfloat0.8Maximum capital utilization
Steeper curves reduce allocation (higher opportunity cost of capital).

Examples

Yield-Aware Market Making

import horizon as hz

def my_mm(ctx):
    if ctx.feed is None or ctx.feed.price <= 0:
        return []
    # Use yield-adjusted size from upstream pipeline
    size = ctx.params.get("size", 10)
    return hz.quotes(fair=ctx.feed.price, spread=0.04, size=size)

hz.run(
    name="yield_mm",
    exchange=hz.Polymarket(),
    feeds={
        "market": hz.PolymarketBook("will-fed-cut-rates"),
        "treasury": hz.TreasuryFeed(),
    },
    pipeline=[
        hz.yield_adjusted_sizer(rfr_feed="treasury", bankroll=10000),
        hz.opportunity_cost_filter(rfr_feed="treasury", min_edge_multiple=2.0),
        my_mm,
    ],
)

Mathematical Background

Standard Kelly: f = (p - q) / (b - 1)* where p is probability, q = 1-p, and b is the payout odds.Yield adjustment subtracts the annualized opportunity cost:f_adj = max(0, f - rfr * duration / 365) * fraction*When the risk-free rate is high relative to the edge, the adjusted fraction drops to zero, meaning the trade is not worth taking.
The minimum edge that justifies locking capital:breakeven = rfr * duration / 365At 5% rates and a 30-day hold, the breakeven is about 0.41%. Any edge below that is better invested in T-bills.
Dollar cost of capital deployment:cost = notional * rfr * duration / 365This represents the forgone interest on the locked collateral.