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

python
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:

python
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

python
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

python
# 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

python
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):

python
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

python
with hz.connect("paper", initial_cash_usd=100_000) as ex:
    order = ex.buy("AAPL", qty=10, limit=180.0)

order is a VenueOrder:

python
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:

python
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.

Next