Skip to content
Your desk

Executor: code, not a model

The one part of Bellwether that talks to the broker is plain tested code. The risk checks it runs on every order, its 5-second loop, and what it does when things go wrong.

Who it is for
New to Bellwether
Reading time
12 min read
Updated

The is ordinary Python with no model inside. It is the only process that holds broker credentials and the only one that sends an order: the PM (shown as on ) writes to the database, and the Executor decides, on its own clock, whether each becomes a limit .

The PM proposes. Code disposes. Every safety rule that matters lives here, not in a prompt.

Owns
Orders, fills, positions, halts, verdicts, stop and target guards
Reads
Proposals, live broker account, positions and orders, a fresh quote, your limits and flags
Tools
None: the broker API and the database; the only process holding broker credentials
Model
None (it is code)
Cadence
One tick every 5 seconds; stop and target guards re-priced every 30 seconds; the end-of-day routine once, 10 minutes after the close
Change it at
Risk › Limits and Strategy › Profile › Autonomy for what it enforces, picked up on the next tick; the loop itself has no settings in the console
Hands off to
You (cards in the Inbox) and the PM (outcomes in its next )
Judged by
Tests, not outcomes: the is pure, fully tested code, and every verdict is auditable

What it owns and reads

Everything that touches the broker: orders, fills, positions, halts and the verdict on every proposal. It reads proposals oldest-first, live broker state, a fresh quote, and your limits and flags, all re-read, uncached, before every submission.

Tools

None in the model sense: the broker credentials and a database connection, which nothing else holds. No prompt can change what it enforces.

The risk checks

Every proposal goes through the risk policy, which runs every check in order (18 today; the numbers are stable labels, so the list skips a couple reserved for later) and reports each one on Agent › proposal checks; the first failure decides. Defaults below are the Moderate ; order rejected says what to do about each.

One proposal on the session page with its checks open: the Executor's verdict and each numbered policy check with the numbers it used.
One proposal on the session page with its checks open: the Executor's verdict and each numbered policy check with the numbers it used.
#CheckWhat it asksOn failure
1Broker env matchesIs the process pointed at the environment it was started for?
2Market hours / Open, and not within 15 minutes of the open or close?reject
3US-listed, not halted, above $3, liquid enough, tight spread, not ?reject
4Long-onlyA buy, or a sell covered by shares you hold?reject
5Limit day orderThe only order type allowedreject
6Limit near quoteWithin 1% of the last quote?reject
7Quote freshQuote younger than 30 seconds?reject
8Order capAt most $500 per order (or clamped down to it)reject
9Position % capResulting position at most 10% of equity (or clamped)reject
10 capTotal long exposure at most 60% of equity, open buys countedreject
11Orders/day capAt most 6 orders todayreject
12 guardA fourth day trade (buy and sell of one name in one session) in 5 sessions on a margin account under $25k? Counted from the Executor's own fills; the allowance can only be lowered, the floor only raisedreject
13No buys within 2 days of earningsreject
14Day P&L down $150 or moreHALT
158% or more below the high-water markHALT
16No duplicateSame symbol and side in the last 60 minutes?reject
17Above the amount set on Strategy › Profile › Autonomy?waits for you
20Settled fundsDoes the buy fit in the account's own cash (it never borrows) and, on a cash account, in settled cash (today's sales settle the next session)?reject

Check 17 never rejects; it only decides whether the order waits for you. Checks 8 and 9 shrink a buy to fit rather than reject when they can. Sells are never trapped: an exit skips the listing, liquidity and denylist rules and only needs the name to be tradable at the broker and not halted; the order-size cap (8) and the duplicate rule (16) pass a sell of shares you hold with a note, and check 4 (never more than the shares you hold free) is the one sizing rule for a sell. While the book is halted check 4 also refuses any buy outright. Before the policy runs, the Executor also applies your and any set to Required; both only ever block buys, and a blocked buy shows the note or the algorithm as its reason. One more rule sits beside the 17 for bracket buys: after the price is resolved, a buy whose working limit would land on top of its own stop (within the minimum stop room, 25 basis points by default, a deployment setting) is not placed. The card reads Rejected with a note that begins arrival_breached_stop, the owner gets a "Not placed … the price is on top of your stop" mail, and the PM is woken to re-think the rather than re-propose the same price. Changing an order that is already working (a tighter stop, a new limit, a cancel) goes through a separate, shorter set of management checks numbered from 101: a stop may only ever tighten, a model may not cancel or loosen a protective exit the Executor placed (109), and a model may not disarm a stop by cancelling its guard (110).

Equity tier, splits and holds

Small accounts get tighter caps automatically: the Executor bands the account by equity (under $2k, $2k–10k, $10k–50k, $50k and up by default) and each band carries ceilings for the dollar limits only: the order cap, orders per day and the daily loss halt. The percentage limits (position size, how invested, drawdown) are your posture's, whatever the account size. A ceiling only ever tightens what you stored. Falling into a smaller band applies the next session; climbing into a larger one only after equity has held there for three sessions. Risk › Limits shows the band, the table and a tier badge on every rule it is tightening. When a stock you hold splits, the Executor rescales its own lot and armed stops before the open and tells the PM to restate its thesis levels. If a held name opens more than 35% away from yesterday's close with no split on record, the Executor puts it on hold (no orders either way, its stop frozen) and pages you; clearing the hold is on Risk under Symbols on hold and asks for your .

The 5-second loop

There is no model and no cadence setting: the loop is deploy-time, and what it enforces is on Risk › Limits. Every 5 seconds it runs one tick:

  1. Adopt any broker order it has no record of and write the heartbeat. If a request is waiting, run it now (see the kill switch). Then re-read your flags, effective limits and active hard constraints; if the set of hard constraints changed since the last tick, sweep the resting buy orders and waiting buy cards it now forbids.
  2. Expire proposals whose has lapsed, and release any proposal the has kept waiting past its 90 seconds; there is no separate .
  3. If trading is halted or disabled, go reduce-only: block every queued buy and order-management request, cancel every buy order still working at the broker, and run only the sells in the queue through step 4 (with the two halt checks passing a sell with a note).
  4. Otherwise take each proposal whose window has opened, oldest-first, one transaction each: live broker state, a fresh quote, the risk checks, then reject, halt, hold for approval, or submit a limit day order whose broker id is the proposal id. A proposal you approved is re-quoted to the current price (never more than 2% past its working limit or past its own cap, otherwise nothing is placed and the card says the price moved) and the full policy runs again at that moment; a bracket buy whose working price lands on top of its stop is not placed either. An id the broker already knows is adopted, never sent twice.
  5. The protective tail, which runs even if step 3 or 4 hit an error (the tick is then still marked failed): cancel orders whose window ended, sync fills, rebuild the book, arm and poll the stop and target guards on held positions, re-price any protective exit that is lagging the bid, halt on a daily-loss or drawdown breach, and run the end-of-day routine once, 10 minutes after the close.

Stops, targets and protective exits

The broker only ever receives limit day orders, so stops and targets are not resting at the broker: the Executor keeps them itself as guards on each bracket position and re-reads a quote for every guarded name every 30 seconds while the market is open. Trailing stops ratchet up, never down. When the last trade crosses a stop or reaches a target, the Executor writes its own sell for the guarded shares and sends it through the same risk checks, with three differences that exist so protection is fast, because a protective exit that dawdles is the risk:

  • It is pre-authorized by the entry. You (or the threshold) already approved the bracket when the buy went out, so the exit never waits in the Inbox, whatever its size and whatever your ; it uses its own 1-minute no-trade window (a deployment setting) instead of 15, so a stop hit at 08:31 or 14:50 CT exits; a hit in the first minute after the open is held until that minute has passed rather than refused. A hit in the last minute before the close waits for the next session.
  • It is priced to get out and chased. The sell is pegged just under the bid (by the exit offset, 10 basis points) and, while it is working, re-priced every 30 seconds whenever its limit sits more than the chase distance (5 basis points) above the bid; each re-price runs the checks again and replaces the order. It is never priced below a slippage floor (2%) under the trigger; if the tape falls through the floor the order keeps working there and a critical alert pages you. Before the exit goes out, any other sell resting on that symbol (an earlier trim, say) is cancelled so its shares are free for the stop.
  • It is retried, loudly. If the exit is rejected, expires or is cancelled, the guard re-arms and fires again no sooner than the retry interval (60 seconds) later, at most the daily attempt limit (6) times a day; after that it stays armed but quiet until the next day and a critical alert says the position is unprotected. A failure caused by something the Executor could not read (a stale or missing quote) or by a halt does not use up an attempt.

These knobs are deployment settings with bounds in code, and for them "stricter" means faster and more persistent, not slower. Three more rules close the gaps around a stop:

  • An entry that fills at or below its own stop is a stop hit. The guard is armed and fired in the same step, the exit goes out at once, and a critical alert says the entry filled through its stop. A bracket entry is never left unguarded; if arming was somehow missed, the next tick arms it late and says so.
  • A buy that would arrive on top of its stop is not placed (the stop-room rule under the risk checks).
  • A model cannot weaken protection. The PM may tighten a stop, lower an exit's limit, or sell; it may not cancel an Executor exit, raise its limit, or disarm a stop, and while an exit is in flight it may not touch that position's guard at all.

If the exchange halts a single name, its guard freezes (no ratchet, no trigger, no attempt used) and the PM is told; when the name reopens the guard re-quotes, and a stop the gap crossed exits against the reopening bid. (This depends on the market-data vendor reporting halts; the vendor used on paper today does not, so treat this path as inert until it does; a stale quote on a halted name still fails check 7.) If the quote feed fails for guarded names a warning says the guards could not be checked this pass; if the guard pass itself fails the tick is marked failed and a critical alert is raised, and one bad guard never stops the others from being checked.

Halt and resume

Halt in the top bar sets a flag the Executor reads within 5 seconds and the book goes reduce-only: queued buys and order-management requests are blocked, buy orders still resting at the broker are cancelled, open positions are kept, and sells (the PM's and the Executor's own stop and target exits) still go through the checks and out. Resuming asks for your authenticator code and also resets the broker circuit breaker. Flatten all positions on Risk halts at once and records the request with your code; the Executor picks the request up on its next tick, cancels every open order and places one marketable limit sell per position outside the risk checks, clears the request when everything was placed, and retries with a critical alert when something was not. See the kill switch.

How it hands off

A proposal above your approval line becomes a card in the Inbox; everything else is decided in the tick. The line the Executor applies follows your autonomy mode, not only the stored figure: on Manual every buy waits for you whatever number is on file, on Semi-automatic it uses your pinned line (or the automatic one from live equity) and never a line at or above the per-order cap, so an order at the cap always asks. Buys approved but not sent inside their window expire rather than carry into a later session, and a covered sell is never held back by the per-order size cap: a position that grew past one order's worth can still be closed in one go. Every verdict, fill, expiry and halt is written back so the PM's next state card opens with what became of its last decisions.

How it is judged

By its tests. The risk policy is a pure function with no I/O, covered branch by branch, and each verdict names its check on the card, so a wrong decision is a reproducible bug, not a judgment call.

When things go wrong

  • Orphan orders. Every tick looks for broker orders it has no record of: one of its own that is no longer wanted is cancelled; a manual order is tracked, never cancelled. Both raise an alert.
  • Circuit breaker. Five consecutive broker failures halt trading (broker_circuit_open); a rejected login halts at once (broker_auth_error). Resume clears the halt and resets the breaker. See broker disconnected.
  • A halt it cannot write stays pending and is retried every tick; nothing is processed until it lands.
  • Bad quotes. A missing, zero or crossed quote rejects that one proposal with no_quote; a premarket proposal pegged to the open instead waits for a usable quote until its window runs out. Neither stops the loop, and a bad quote for a guarded name leaves a working protective exit where it is rather than re-pricing it on garbage.
  • A protective exit that will not go. Every failure re-arms the guard and retries; an exit resting at its slippage floor, an exit that ran out of attempts, and a replacement the broker lost each raise a critical alert naming the symbol, because at that moment the position may be unprotected. Those are pages: look at the position and, if need be, sell it at the broker.

Health

The Executor rewrites a heartbeat row on every tick, failed ticks included; the API reports ok, error (the last tick failed closed) or down (no tick within 30 seconds). The console does not draw that state yet, so a stalled Executor shows as frozen equity and queued proposals. Five failed ticks in a row raise one critical alert.