> ## Documentation Index
> Fetch the complete documentation index at: https://mathematicalcompany.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Horizon + Alpaca

> Trade stocks and ETFs on Alpaca Markets. Commission-free equities with paper and live modes.

<Note>
  **Partially verified.** As of v0.8.8+, the Alpaca client has been byte-for-byte
  audited against `alpaca-py`. Several critical bugs were fixed:

  * Fill polling now uses `/v2/account/activities/FILL` instead of the invalid
    `/v2/orders?status=filled` query (which was silently returning zero fills).
    Each execution now arrives as its own dedup'd record, so partial fills are
    no longer lost.
  * `client_order_id` is generated per submit so retries on network errors are
    idempotent (no risk of duplicate orders).
  * Limit-price formatter no longer rounds to 2 decimal places — sub-dollar
    stocks and crypto pairs trade at their native tick size.
  * `Day` time-in-force is now supported (required for equity options).
  * Short positions parse with their signed quantity preserved.

  These fixes have not been live-tested against a real Alpaca account.
  Polymarket remains the only exchange with end-to-end verified live trading.
  Use small size first.
</Note>

[Alpaca Markets](https://alpaca.markets) is a commission-free stock trading API. Horizon supports Alpaca as a first-class exchange for equity trading, stock/ETF hedging, and multi-asset strategies.

## Quick Setup

```python theme={null}
hz.run(
    name="stock_hedge",
    exchange=hz.Alpaca(api_key="...", api_secret="..."),
    mode="live",
    ...
)
```

### Standalone Equity Trading

```python theme={null}
import horizon as hz
from horizon import Side, OrderSide, OrderRequest

hz.run(
    name="equity_strategy",
    exchange=hz.Alpaca(paper=True),
    markets=[hz.Market(id="AAPL", exchange="alpaca", ticker="AAPL", asset_class="equity")],
    feeds={"aapl": hz.AlpacaFeed(symbols=["AAPL"])},
    pipeline=[my_strategy],
    risk=hz.Risk.equity(max_position=100, max_notional=50000),
)
```

## Credentials

<Tabs>
  <Tab title="Explicit">
    ```python theme={null}
    hz.Alpaca(api_key="PKXXXXXX", api_secret="XXXXXX")
    ```
  </Tab>

  <Tab title="Environment variables">
    ```bash theme={null}
    export ALPACA_API_KEY="PKXXXXXX"
    export ALPACA_API_SECRET="XXXXXXXX"
    # or use Alpaca's native env var names:
    export APCA_API_KEY_ID="PKXXXXXX"
    export APCA_API_SECRET_KEY="XXXXXXXX"
    ```

    ```python theme={null}
    hz.Alpaca()  # reads from env
    ```
  </Tab>
</Tabs>

## Paper vs Live

```python theme={null}
# Paper trading (default)
hz.Alpaca(paper=True)   # https://paper-api.alpaca.markets

# Live trading
hz.Alpaca(paper=False)  # https://api.alpaca.markets
```

<Tip>
  Test with `paper=True` before switching to live. Paper mode uses the same API with simulated fills, so your strategy logic runs the same way.
</Tip>

## Alpaca Configuration

```python theme={null}
@dataclass
class Alpaca:
    api_key: str | None = None
    api_secret: str | None = None
    paper: bool = True
    api_url: str | None = None
    data_source: str = "iex"
```

| Field         | Default | Description                                                               |
| ------------- | ------- | ------------------------------------------------------------------------- |
| `api_key`     | `None`  | Alpaca API key ID (env: `ALPACA_API_KEY` or `APCA_API_KEY_ID`)            |
| `api_secret`  | `None`  | Alpaca API secret key (env: `ALPACA_API_SECRET` or `APCA_API_SECRET_KEY`) |
| `paper`       | `True`  | Use paper trading URL                                                     |
| `api_url`     | Auto    | Override API URL (set automatically from `paper` flag)                    |
| `data_source` | `"iex"` | Market data source: `"iex"` (free) or `"sip"` (paid, full NBBO)           |

## Authentication

Alpaca uses header-based auth. No token refresh needed.

* `APCA-API-KEY-ID: {api_key}`
* `APCA-API-SECRET-KEY: {api_secret}`

Both headers are sent on every request.

## Market Data Feed

Stock quotes via WebSocket:

```python theme={null}
hz.run(
    feeds={
        "spy": hz.AlpacaFeed(symbols=["SPY"]),
        "tlt": hz.AlpacaFeed(symbols=["TLT"]),
    },
    ...
)
```

The feed connects to `wss://stream.data.alpaca.markets/v2/{source}` and subscribes to both trades and quotes for each symbol.

Each symbol gets its own snapshot entry keyed by `{feed_name}:{SYMBOL}` (e.g., `tlt:TLT`), plus a feed-level entry under the feed name with the last update across all symbols.

| Field         | Type        | Default  | Description                                                                       |
| ------------- | ----------- | -------- | --------------------------------------------------------------------------------- |
| `symbols`     | `list[str]` | required | Stock/ETF tickers to subscribe (e.g., `["TLT", "SPY"]`)                           |
| `api_key`     | `str`       | `None`   | Override API key (defaults to env vars)                                           |
| `api_secret`  | `str`       | `None`   | Override API secret (defaults to env vars)                                        |
| `data_source` | `str`       | `"iex"`  | `"iex"` (free, 15-min delayed for some symbols) or `"sip"` (paid, real-time NBBO) |

## Symbol Mapping

Alpaca uses standard stock tickers. No token ID resolution needed:

```python theme={null}
# Market ID maps directly to the Alpaca symbol
market = hz.Market(id="TLT", name="iShares 20+ Year Treasury Bond ETF")
```

| Horizon Field             | Alpaca Field  | Notes                                        |
| ------------------------- | ------------- | -------------------------------------------- |
| `OrderRequest.market_id`  | `symbol`      | Standard ticker (e.g., "AAPL", "SPY", "TLT") |
| `OrderRequest.order_side` | `side`        | `"buy"` or `"sell"`                          |
| `OrderRequest.price`      | `limit_price` | Limit order price                            |
| `OrderRequest.size`       | `qty`         | Supports fractional shares                   |

<Note>
  Alpaca uses `Side.Long` for all positions. Prediction market concepts like `Side.Yes` / `Side.No` do not apply to stock orders.
</Note>

## Fill Polling

Alpaca fills are polled from the `GET /v2/orders?status=filled` endpoint. Each cycle:

1. Queries recently filled orders
2. Deduplicates against previously seen fill IDs (FIFO eviction at 10k)
3. Maps Alpaca's response to Horizon `Fill` objects with exchange-assigned timestamps

## Multi-Exchange Hedging

Use Alpaca alongside Polymarket or Kalshi for hedged strategies:

```python theme={null}
hz.run(
    name="hedged_fed_rate",
    exchange=[
        hz.Polymarket(),       # prediction market
        hz.Alpaca(paper=True), # stock hedge
    ],
    feeds={
        "fed_rate": hz.PolymarketBook("will-fed-cut-rates"),
        "tlt": hz.AlpacaFeed(symbols=["TLT"]),
    },
    pipeline=[
        hz.stock_hedge(hz.HedgeConfig(
            prediction_feed="fed_rate",
            stock_feed="tlt",
            stock_symbol="TLT",
            auto_rebalance=True,
        )),
        my_strategy,
    ],
)
```

The first exchange (Polymarket) becomes the primary. Orders are routed based on `market.exchange`.

## Feature Support Matrix

| Feature                | Polymarket | Kalshi     | Alpaca   |
| ---------------------- | ---------- | ---------- | -------- |
| Trading (orders/fills) | Yes        | Yes        | Yes      |
| Orderbook feed         | Yes (WS)   | Yes (REST) | Yes (WS) |
| Market discovery       | Yes        | Yes        | Yes      |
| Hedge pipeline         | No         | No         | Yes      |
| Paper trading          | No         | Yes (demo) | Yes      |
| Fractional shares      | No         | No         | Yes      |
| LLM forecasting        | Yes        | Yes        | No       |
| Resolution analysis    | Yes        | Yes        | No       |

## Getting API Keys

1. Sign up at [alpaca.markets](https://alpaca.markets)
2. Go to **Paper Trading** > **API Keys**
3. Generate a new key pair
4. For live trading, switch to **Live Trading** and generate live keys

See [Stock Market Hedging](/stock-hedging) for the full hedging guide and [Stock Hedge Strategy](/examples/stock-hedge-strategy) for a complete worked example.
