# Equity Broker Execution

> Use when placing, managing, or reconciling US equity orders through real broker APIs (Alpaca, Tradier, IBKR stocks, Schwab) — equity order types (market/limit/marketable-limit, bracket/OCO, trailing stop, time-in-force and extended-hours flags), order lifecycle state machine with partial-fill reconciliation, per-broker idempotency and cancel/replace semantics, slippage handling, reject/disconnect recovery, liquidation-only flatten procedure, SSR (Reg SHO) and LULD-halt handling at order time, and realtime equity data acceptance criteria (NBBO/SIP semantics, Level 2 order book, halt processing). Trigger on - place stock order, bracket order, OCO, trailing stop, partial fill, order reconciliation, broker API equities, LULD halt, short sale restriction, flatten position, paper trading. Crypto exchange orders live in crypto-exchange-integration; position sizing and kill-switch live in trading-risk-management; day-trade permission and locate rules live in equity-trading-compliance.

- Skill: `joogy06/equity-broker-execution` (Agent Skill)
- Install (CLI): `npx skillmds@latest add joogy06/equity-broker-execution`
- Raw SKILL.md: https://api.skillmd.com/api/skills/joogy06/equity-broker-execution/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: joogy06 (https://skillmd.com/u/joogy06)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/joogy06/equity-broker-execution

---


# Equity Broker Execution

## 1. Overview + routing + the exposure-intent gate chain

This skill places and reconciles **US equity** orders through real broker APIs.
It authors only the equity-specific layer; it **references, never duplicates**,
the generic machinery already owned elsewhere (see Routing).

### Safety HARD-RULE (binding)

1. **Classify every order by EXPOSURE INTENT before submission** —
   `increase` (new/added risk), `neutral` (replace at same size), or `reduce`
   (closes/reduces, reduce-only exits, cancels).
2. **Exposure-increasing orders pass the full pre-trade gate chain** — the
   `trading-risk-management` `KillSwitch` **and**
   `equity-trading-compliance.day_trade_permitted(...)` — and **FAIL CLOSED**
   on any unknown/stale input. **Exposure-reducing orders and cancels are
   NEVER blocked** by an unknown regime or a tripped kill switch: halt /
   `EMERGENCY_EXIT` states authorize cancel-and-reduce only. Fail-closed means
   "no new risk", never "trapped in a position".
3. **Paper/sandbox is the default environment, ASSERTED (not assumed) at
   adapter setup.** Live execution requires a **one-shot user confirmation
   bound to an exact order digest** (§8).
4. **Every order code sample parametrizes side (buy/sell) and validates
   required params** — the hardcoded-side trap (the historical crypto IBKR
   stub) is a named anti-pattern (§9).

```python
def classify_exposure(current_position_qty: int, side: str, quantity: int,
                      reduce_only: bool = False) -> str:
    """Return 'increase' | 'neutral' | 'reduce' from broker-authoritative state."""
    signed = quantity if side.lower() in ("buy", "buy_to_cover") else -quantity
    if reduce_only:
        return "reduce"
    if current_position_qty == 0:
        return "increase"
    same_direction = (current_position_qty > 0) == (signed > 0)
    if same_direction:
        return "increase"
    # Opposite direction: reduces (or flips) — reduces until it crosses zero.
    return "reduce" if abs(signed) <= abs(current_position_qty) else "increase"
```

### Routing

- Order-lifecycle FSM skeleton, WebSocket reconnection, rate limiting, generic
  reject taxonomy → **`crypto-exchange-integration`** (do NOT re-derive it).
- Tick/bar cleaning, dedup, resample → **`market-data-engineering`**.
- Position sizing, kill switch, circuit breakers → **`trading-risk-management`**.
- Day-trade permission, locate/SSR **rule model**, wash-sale/tax →
  **`equity-trading-compliance`**.
- Universe/watchlist selection → **`equity-scanning-and-watchlists`**.
- Crypto exchange orders → **`crypto-exchange-integration`**.

## 2. Broker adapter matrix

One row per adapter; **capability flags are not universal** — read them, do not
assume. (See `research/equity-day-trading-skills/broker-api-research.md` for the
authoring citations; verify against live vendor docs before shipping.)

| Adapter | Auth | Idempotency | Cancel/replace | Bracket/OCO | Paper fidelity |
|---|---|---|---|---|---|
| **Alpaca v2** | key/secret or OAuth; strict paper vs live host | `client_order_id` gives ACTIVE-order duplicate protection (reuse → 422); NOT permanent exactly-once — reconcile by id before retrying | replace returns NEW id; can race a fill | `order_class` bracket/oco/oto; child exits activate after entry fill | fills at NBBO, no queue/impact; free data = IEX, SIP paid |
| **Tradier** | OAuth2 bearer; sandbox vs prod host | NO hard dedup; `tag` is reference-only | per-order cancel/replace | `class` otoco/oco/oto | sandbox market data delayed 15m; delayed-market-data streaming NOT available in sandbox, but sandbox account/order-event streaming exists |
| **IBKR Web API** | Client Portal gateway/OAuth session (times out) | `cOID` supported (≤64 chars, unique 24h) | REST cancel/replace | contingent order types | paper via paper account |
| **IBKR TWS via `ib_async`** | attach to running TWS/Gateway | integer order-id sequencing per clientId; resync `nextValidId` on reconnect | socket cancel/modify | parent/child + `transmit` flag | paper by gateway/port |
| **Schwab Trader API** | 3-legged OAuth2 (verify token lifetime, preview endpoints, order-strategy enums + streamer in the authenticated Schwab developer specification) | preview endpoints | `orderStrategyType` | SINGLE/OCO/TRIGGER | streamer for activity |

> **`ib_async` is the ACTIVE third-party successor to the archived
> `ib_insync`** — a community wrapper over the socket TWS API, **not an
> official IBKR SDK**. The IBKR Web API and the TWS socket API are TWO distinct
> adapters with different auth, sessions, reconnect, and order-id behavior.

## 3. Equity order types + when to use them

- **Marketable limit is the day-trading entry default** (a limit priced through
  the opposite side of the NBBO) — NEVER a naked market order — with a
  **price-protection procedure** run before pricing:

```python
def price_protected_limit(quote, side: str, max_spread_bps: float,
                          max_quote_age_ms: int, luld_band, min_increment):
    """Validate freshness/spread/tick/LULD, then price a marketable limit.

    Raises if protection fails — caller must NOT fall back to a market order.
    min_increment is the security's CURRENT permitted minimum price increment,
    obtained from broker/venue reference data — do NOT assume $0.01. Sub-dollar
    NMS stocks may quote in $0.0001, and the Rule 612 amendments (SEC compliance
    date extended to the first business day of November 2027, review ongoing)
    add symbol-dependent increments.
    """
    if quote.age_ms > max_quote_age_ms:
        raise ValueError("stale quote — refuse to price")
    spread_bps = (quote.ask - quote.bid) / quote.mid * 10_000
    if spread_bps > max_spread_bps:
        raise ValueError("spread too wide — refuse to price")
    if luld_band is not None and not luld_band.contains(quote.mid):
        raise ValueError("price outside LULD band")
    aggress = quote.ask if side.lower() == "buy" else quote.bid
    # Quantize to the permitted increment — never a blanket 2-decimal round.
    return round(aggress / min_increment) * min_increment
```

- **Bracket / OCO:** per-broker activation semantics are explicit. On many
  brokers **bracket child exits activate only after the entry FULLY fills** —
  a partial entry can leave filled shares unprotected. Client-synthesized OCO
  is **PROHIBITED** unless atomic quantity reconciliation + overfill recovery
  are documented.
- **Trailing stop, TIF (DAY/IOC/FOK/GTC), extended-hours flags** — per the §2
  matrix.
- **SESSION STATE (regular / pre / after-hours / overnight) is an explicit
  order input.** LULD protections do NOT extend to all sessions (§7).

## 4. Order lifecycle FSM + reconciliation

States: `new → accepted → partial → filled` plus `canceled / rejected /
replaced` and the `pending_cancel` / `pending_replace` invariants. Reuse the
FSM skeleton in `crypto-exchange-integration`; author here only the
equity-specific reconciliation rules:

- **AMBIGUOUS-SUBMISSION rule:** on a timeout after submit, **QUERY before
  retrying** — never blind-retry (a blind retry doubles exposure). Use the
  broker's idempotency guard (§2) where it exists.
- **Reconcile by immutable execution IDs and cumulative filled quantity**,
  never by status alone.
- **Cancel/replace fill-race:** a successful replace RESPONSE does not guarantee
  the old order was replaced before it filled — reconcile executions after.
- **Reconcile-on-reconnect:** after any gap, re-fetch broker-authoritative
  orders and positions; never assume local state matches the broker.

### Boot reconciliation — the equity implementation of the runtime protocol

`trading-automation-runtime` defines ONE asset-neutral `ReconciliationRequest →
ReconciliationResult` protocol (open orders, executions/fills by immutable/composite
execution key, positions, balances; account/venue scope; snapshot watermark; pagination
completeness; discrepancy list; and an explicit `complete | incomplete | unknown` status).
**The four §4 invariants above ARE the equity implementation of that protocol** —
query-before-retry, reconcile by immutable execution id + cumulative filled quantity,
cancel/replace fill-race reconciliation, and reconcile-on-reconnect. The runtime calls this
adapter at TWO sites: intraday on reconnect (above) **and at boot**, before the first
strategy callback. An `incomplete` result (a paginated fetch that did not fully drain, or a
broker endpoint that timed out) MUST be surfaced as `incomplete` — it **blocks new
exposure** (the runtime admits to QUARANTINED), never silently treated as "no open state".

## 5. Failure handling + flatten-all (liquidation state machine)

Equity-specific reject causes authored here (buying power, **locate**, **halt**,
**PDT/regime block**); the generic reject taxonomy + disconnect/duplicate
protection are referenced from `crypto-exchange-integration`.

**Flatten-all is a BOUNDED STATE MACHINE, not a one-shot:**

```python
def flatten_all(broker, tolerance: int = 0, max_rounds: int = 5):
    """Liquidation-only mode. Cancels new-risk orders, then reduces to tolerance.

    Halted symbols and residuals are SURFACED, never reported as success.
    """
    broker.enter_liquidation_only_mode()
    broker.cancel_exposure_increasing_and_child_orders()
    residuals = []
    for _ in range(max_rounds):
        broker.reconcile_executions()
        positions = broker.authoritative_positions()
        open_qty = {p.symbol: p.qty for p in positions if abs(p.qty) > tolerance}
        if not open_qty:
            return {"status": "flat", "residuals": []}
        for symbol, qty in open_qty.items():
            if broker.is_halted(symbol):
                residuals.append({"symbol": symbol, "qty": qty, "reason": "halted"})
                continue
            exit_side = "sell" if qty > 0 else "buy_to_cover"
            broker.submit_reduce_only(symbol, exit_side, abs(qty))
    return {"status": "incomplete", "residuals": residuals}   # SURFACED, not success
```

Note the exits use a computed `exit_side` — never a hardcoded side.

## 6. Order-time regulatory enforcement

The D1 side of the compliance split (the **rule model** lives in
`equity-trading-compliance` §7 — reference, never restate):

- **Order marking** — long / short / short-exempt.
- **Locate/borrow availability** checked at order time for shorts.
- **SSR (Rule 201)** as an order input: when active, non-exempt shorts may only
  rest strictly ABOVE the current national best bid (unless a Rule 201
  exception applies) — enforce at pricing time.
- **LULD halt states:** during a limit-state / trading pause, handle
  new/resting orders per the halt; on resumption, re-validate price protection
  before re-pricing. Each broker's rejection for these must be handled.

## 7. Realtime data acceptance criteria

Acceptance criteria the equity feed must meet (transport plumbing is referenced,
not duplicated — WS reconnect → `crypto-exchange-integration`; tick/bar cleaning
→ `market-data-engineering`):

- **NBBO / SIP vs direct feed:** know which you consume; the SIP is
  structurally slower than direct exchange feeds. Quote freshness feeds the §3
  price-protection bound.
- **Level 2 / order book** maintenance if depth is used (add/modify/cancel/
  execute, not just top-of-book).
- **Halt processing:** LULD pauses and regulatory halts are explicit states.
- **LULD bands apply during regular hours (09:30–16:00 ET) only.**
- **WATCH (as of 2026-07-14):** an SEC overnight price-band amendment (SR filing
  34-105596) is pending — the regular-hours-only statement is correct now but
  could go stale; re-verify before extending band protection to overnight.

> **UNATTENDED EXTENDED-HOURS PROHIBITION (binding, first-class).** In an
> UNATTENDED/automated context, **extended-hours (pre / after-hours /
> overnight) orders are PROHIBITED, or hard-collared, whenever authoritative
> halt/band state is unavailable** — because LULD does not protect those
> sessions and no human is watching. Attended mode may permit extended-hours
> with an explicit, per-order human confirmation. Fail closed to "no
> extended-hours entry" when band/halt state is stale, degraded, or absent.

*(TW-1 note: this section is deliberately acceptance-criteria only. If NBBO/SIP
+ L2 + halt-processing content later grows its own data sources, state machines,
freshness lifecycle, or consumers beyond order placement, split it into a
standalone `equity-realtime-market-data` skill.)*

## 8. Credentials + privacy (binding)

- **Live requires a one-shot confirmation bound to an exact order digest** —
  masked account, symbol, side, quantity, order type, limit price, TIF,
  session, and worst-case notional — sourced from the **direct user channel
  only** (never from scanned/ingested content). **Any mutation of the order
  invalidates the consent**; confirmations **expire** and never authorize a
  subsequent order.
- **Secrets in a secret store, never inline.** Strict **live/paper credential
  separation**. **No credential echo** into agent transcripts or logs.
- **Mask account IDs** in every displayed artifact; **redact structured logs**.

## Anti-Patterns

| Anti-Pattern | Why It Fails | Correct Approach |
|---|---|---|
| Hardcoding order side | Cannot place a SELL; the historical IBKR stub raised on limit orders | Parametrize `side` and validate required params on every order |
| Limit order without price validation | Pays through a wide spread / stale quote / outside LULD | Run the §3 price-protection procedure before pricing; refuse if it fails |
| Blind retry after a submit timeout | Doubles exposure when the first order actually landed | AMBIGUOUS-SUBMISSION: query broker state before retrying; use idempotency guard |
| Replace without fill-race handling | A replace response does not prove the old order was replaced pre-fill | Reconcile executions by immutable id + cumulative qty after any replace |
| Polling status without reconcile | Status alone hides partial fills and races | Reconcile by execution ids and cumulative filled quantity |
| Assuming atomic fills | Partial fills leave bracket children unprotected | Treat partials explicitly; verify protective legs cover filled qty |
| Client-synthesized OCO without reconciliation | Overfill / double-exit when both legs touch | Prohibited unless atomic qty reconciliation + overfill recovery documented |
| Live trading without paper validation | A logic bug drains the account | Paper is the asserted default; live needs the one-shot order-digest confirmation |
| Bypassing the exposure-intent gate chain | Unpermitted new risk reaches the market | Classify exposure; increasing orders pass KillSwitch AND day_trade_permitted |
| Fail-closed applied to exits | Traps the account in a losing position | Reducing orders/cancels are never blocked; halts authorize cancel-and-reduce |
| Unattended extended-hours order without band/halt state | LULD does not protect those sessions; no human is watching | Prohibit or hard-collar unattended extended-hours when halt/band state is unavailable |

