Documentation Index
Fetch the complete documentation index at: https://mathematicalcompany.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
Equity Trading
Horizon supports equities as a first-class asset class. The same engine, risk pipeline, feed system, and persistence layer work for stocks — the only differences are Side.Long instead of Side.Yes/Side.No, and price ranges that aren’t limited to 0-1.
Quick Start
import horizon as hz
def fair_value(ctx: hz.Context) -> float:
return ctx.feeds["aapl"].price
def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
spread = fair * 0.001 # 10bps spread
return [hz.Quote(bid=fair - spread/2, ask=fair + spread/2, size=10)]
hz.run(
name="aapl_mm",
exchange=hz.Alpaca(paper=True),
markets=[hz.Market(id="AAPL", exchange="alpaca", ticker="AAPL", asset_class="equity")],
feeds={"aapl": hz.AlpacaFeed(symbols=["AAPL"])},
pipeline=[fair_value, quoter],
risk=hz.Risk.equity(max_position=100, max_notional=50_000),
mode="live",
)
What’s Different from Prediction Markets
| Prediction Markets | Equities |
|---|
| Side | Side.Yes / Side.No | Side.Long |
| Price range | 0.01 - 0.99 | 0.01 - 100,000 |
| Risk preset | Risk() | Risk.equity() |
| Token IDs | Yes (Polymarket) | None |
| Exchanges | Polymarket, Kalshi, Limitless | Alpaca, IBKR |
| Discovery | discover_markets("polymarket", ...) | discover_markets("alpaca", ...) |
Everything else is the same: pipeline composition, risk checks, order management, position tracking, persistence, monitoring, backtesting.
Side.Long
Equities don’t have Yes/No outcomes. The Side.Long variant represents a directional position:
from horizon import Side, OrderSide, OrderRequest
# Buy 10 shares of AAPL at $175
req = OrderRequest(
market_id="AAPL",
side=Side.Long,
order_side=OrderSide.Buy,
size=10,
price=175.50,
)
# Sell to close
req = OrderRequest(
market_id="AAPL",
side=Side.Long,
order_side=OrderSide.Sell,
size=10,
price=180.00,
)
The position tracker, risk pipeline, and P&L calculations all handle Side.Long the same way they handle Side.Yes — buy increases position, sell decreases it.
Risk Configuration
Risk.equity() provides defaults appropriate for stock trading:
risk = hz.Risk.equity(
max_position=1000, # max shares per symbol (default: 1000)
max_notional=100_000, # max portfolio value (default: 100,000)
max_drawdown_pct=5, # kill switch trigger (default: 5%)
max_order_size=500, # max single order size (default: 500)
price_min=0.01, # minimum valid price (default: 0.01)
price_max=100_000, # maximum valid price (default: 100,000)
)
The key difference from Risk() is price_max. Prediction market prices are bounded 0-1; stock prices aren’t. The Rust risk pipeline uses price_min/price_max from the config, so the same validation code works for both.
Market Discovery
Find stocks via Alpaca or IBKR:
import horizon as hz
# Search Alpaca for equities
stocks = hz.discover_markets(exchange="alpaca", query="AAPL", limit=10)
for s in stocks:
print(f"{s.ticker}: {s.name}")
# Search IBKR
contracts = hz.discover_markets(exchange="ibkr", query="AAPL", limit=10)
CLI Discovery
horizon discover "AAPL" --exchange alpaca
horizon discover "apple" --exchange ibkr
Market Object
Equity markets use optional fields for asset class metadata:
market = hz.Market(
id="AAPL",
name="Apple Inc.",
exchange="alpaca",
ticker="AAPL",
asset_class="equity",
)
# Options contracts use additional fields
option = hz.Market(
id="AAPL230120C00150000",
name="AAPL Jan 2023 150 Call",
exchange="ibkr",
ticker="AAPL",
asset_class="option",
underlying="AAPL",
strike_price=150.0,
option_type="call",
multiplier=100.0,
)
| Field | Used For | Example |
|---|
asset_class | Instrument type | "equity", "option", "crypto" |
underlying | Derivatives | "AAPL" for an AAPL option |
strike_price | Options | 150.0 |
option_type | Options | "call" or "put" |
multiplier | Contracts | 100.0 for US equity options |
All fields are optional and default to None. Prediction markets ignore them.
Feeds
Alpaca provides real-time equity data via WebSocket:
feeds = {
"aapl": hz.AlpacaFeed(symbols=["AAPL"]),
"spy": hz.AlpacaFeed(symbols=["SPY", "QQQ"]),
}
Multi-symbol feeds create per-symbol snapshots keyed as {feed_name}:{SYMBOL}:
ctx.feeds["spy:SPY"].price # SPY last trade
ctx.feeds["spy:QQQ"].price # QQQ last trade
ctx.feeds["spy:SPY"].bid # SPY best bid
ctx.feeds["spy:SPY"].ask # SPY best ask
The equity CLI group provides market data tools:
horizon equity quote AAPL # stock quote
horizon equity search "tech" # search stocks
horizon equity options-chain AAPL # options chain
horizon equity greeks SPY # Greek exposure
horizon equity iv-surface TSLA # IV term structure
horizon equity screener --sector tech # stock screener
These use Unusual Whales as the data source when UNUSUAL_WHALES_API_KEY is set.
Multi-Asset Strategies
Combine prediction markets and equities in one strategy:
hz.run(
name="cross_asset",
exchanges=[
hz.Polymarket(private_key="0x..."),
hz.Alpaca(paper=True),
],
markets=[
hz.Market(id="will-fed-cut-rates", exchange="polymarket"),
hz.Market(id="TLT", exchange="alpaca", asset_class="equity"),
],
feeds={
"rates": hz.PolymarketBook("will-fed-cut-rates"),
"tlt": hz.AlpacaFeed(symbols=["TLT"]),
},
pipeline=[cross_asset_strategy],
risk=hz.Risk(max_position=100, price_min=0.01, price_max=1000),
)
Orders route to the correct exchange based on market.exchange. The engine tracks positions and P&L across all exchanges in a single book.
What Works the Same
These features work identically for equities and prediction markets:
- Pipeline composition: chain functions, automatic signature introspection
- Risk pipeline: all 8 checks (kill switch, price/size, position, notional, drawdown, rate limit, dedup)
- Order management: submit, cancel, amend, bracket orders, stop-loss, take-profit
- Position tracking: real-time P&L, exposure calculations
- Persistence: SQLite crash recovery, fill journal, position snapshots
- Monitoring: Prometheus metrics, alerts, TUI dashboard
- Backtesting:
hz.backtest() with the same pipeline
- Autonomous fund: FundManager, strategy lifecycle, oversight loop