Imperative API
Place orders directly, no pipeline required
Sometimes you just want to place a trade. Use hz.connect() to get an ImperativeClient wrapped around any venue.
Simplest example
import horizon as hz
ex = hz.connect("paper", initial_cash_usd=100_000)
order = ex.buy("AAPL", qty=10, limit=180.0)
print(order)
print(ex.balance())
Context manager
Recommended pattern. close() is called automatically on exit:
with hz.connect("paper", initial_cash_usd=100_000) as ex:
ex.buy("AAPL", qty=10, limit=180.0)
ex.buy("MSFT", qty=5, limit=400.0)
print("Open orders:", ex.open_orders())
print("Balance:", ex.balance())
Common operations
with hz.connect("paper", initial_cash_usd=100_000) as ex:
# Place. buy/sell/short/cover
order1 = ex.buy("AAPL", qty=10, limit=180.0)
order2 = ex.sell("MSFT", qty=5, market=True) # market order
order3 = ex.short("SPY", qty=100, limit=450)
# Modify / cancel
ex.amend(order1.id, new_quantity=20, new_price=179.0)
ex.cancel(order3.id)
ex.cancel_all(market_id="AAPL") # cancel all orders on AAPL
ex.cancel_all() # cancel everything
# Close positions
ex.flatten("AAPL") # close all AAPL positions
ex.flatten_all() # close everything
Asset-class helpers
# Options
ex.buy_option(
underlying="AAPL",
strike=180,
expiry="2025-01-17",
right="call",
qty=5,
limit=3.50,
)
# Prediction markets
ex.buy_prediction(
market="polymarket:trump-2028",
qty=100,
limit=0.45,
side="yes",
)
# Perps
ex.buy_perp(
"hyperliquid:BTC-PERP",
qty=0.1,
leverage=3,
limit=95_000,
)
State queries
with hz.connect("paper", initial_cash_usd=100_000) as ex:
ex.buy("AAPL", qty=10, limit=180.0)
# Current state
positions = ex.positions() # list[VenuePosition]
orders = ex.open_orders() # list[VenueOrder]
balance = ex.balance() # VenueCapital
realized = ex.realized_pnl() # float
# Drain pending fills (removes them from the queue)
fills = ex.fills() # list[VenueFill]
From inside a strategy (escape hatch)
Normally strategies return signals, not orders. But you can reach for imperative actions from inside a strategy via ctx.exchange(venue_name):
class MyStrategy(Strategy):
asset_classes = [Equity]
features = {"z": Zscore(20)}
def evaluate(self, f, universe, ctx):
if self._emergency_condition():
# Bypass the pipeline. flatten everything at market
ctx.exchange("paper").flatten_all()
return []
return [Signal.increase(m, edge_bps=30) for m in universe if f.z[m.id] < -2]
What it returns
with hz.connect("paper", initial_cash_usd=100_000) as ex:
order = ex.buy("AAPL", qty=10, limit=180.0)
order is a VenueOrder:
VenueOrder(
id="p1",
market_id="AAPL",
side="buy",
quantity=10,
filled_quantity=0.0,
price=180.0,
order_type="limit",
status="new",
submitted_at=datetime(...),
venue_name="paper",
metadata={},
)
For a market order (market=True), the order fills immediately at the last known price + slippage, so filled_quantity will equal quantity and status="filled".
For a limit order, it rests in the paper venue’s book until paper.tick(price) brings a crossing price. typically called by the run loop during a backtest. Standalone imperative use places limits that sit forever until you cancel them.
Going live (a future release)
Once a future release wires the Rust venue clients via PyO3, the same connect() call works against live brokers:
ex = hz.connect(
"alpaca",
paper=False,
api_key_env="ALPACA_KEY",
api_secret_env="ALPACA_SECRET",
)
ex.buy("AAPL", qty=10, limit=180.0)
The imperative API is the same; only the venue argument changes.