Repository navigation
Order-throughput harness: user-gated live capacity mode (never runs without the user's live key and go file) - #191
seathatflowsinourveins wants to merge 4 commits into
Conversation
capacity.py live reuses the paper engine (run loop, rate governor, trade_updates observation, cleanup, journal, reconciliation) against https://api.alpaca.markets. Execution-capacity probes only: DAY limit BUY qty 1 at least 500 bps below the bid, cancelled individually; never strategy trades. Gates before any request, with recorded refusal reasons, before the live credentials are read: --i-understand-live-orders; the user's go file (regular file, owner-only, mode 0600, go true, at most 24 h validity, unexpired; read, never written); an alpaca-live-<label>.env name (paper env files refused in live, live env files refused by paper/recover/audit); live config (band >= 500 bps, no cancel-all, zero acknowledged open orders); regular trading hours from the session calendar; the host and alpaca-live STOP files. Then: no paper base URL in the live env file, zero open orders at preflight and an open broker clock. Caps are min(CLI, go file, live ceilings): per-order <= 1000 USD, open <= 2000 USD, total submits+cancels <= max_order_actions with 6 cancels reserved per open probe so cleanup always fits. Any fill or position change (stream, periodic governed position read) stops submissions, cancels this run's probes and never sells; the run ends needs_attention with the fill quantity and price. The go file is re-read every 10 s; a revoked or changed go stops submissions. Receipt: evidence_class native_live, go file sha256, caps used, no account id or balance. Paper and offline behaviour is unchanged: CapacityConfig.validate stays paper-only, the paper engine refuses a live-origin port, all 68 existing tests pass unchanged, and offline/paper-refusal receipts are identical to the base commit apart from provenance and the random prefix. The live port was not run against Alpaca. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…edence, journal mode, headroom Review findings on f4d505a, each with a test that fails on that commit: 1. Live probes use SPY, QQQ, IWM or DIA only (config gate live_symbol_not_allowed before any request; the live port refuses any other symbol at construction and at submit). A probe is priced only from a quote at most 10 s old: live max_quote_age_seconds <= 10 and a refresh at least every 5 s are enforced; once the price's quote is older than 10 s, submissions hold and the refresh repeats at most once a second; a stale refresh is refused and counted, and the first fresh quote reprices and resumes submissions. The REST worker re-checks the age immediately before each submit. Probes stay >= 500 bps below that fresh bid. 2. SIGHUP (closed terminal or WSL session) and SIGQUIT now take the same request_stop path as SIGINT/SIGTERM, in every command (install_stop_signals), so cleanup, reconciliation, the receipt and the journal end record still run. 3. In live, any fill or position change ends needs_attention even when a port error was recorded (error_type stays in the receipt). live.fills has probe_fill, other_order_fill (symbol, side, quantity, average price from the stream and the listing; never ids) and position_change entries. 4. Journal start records carry mode and trading_origin. Paper recover/audit read the journal before any credential and refuse a live run's journal (also one without the new fields, judged by config.base_url); the engine's recover checks it too. A run refuses any file at its journal path before any request, naming another mode's journal. 5. Live headroom is held at 0.9 (live_headroom_must_be_0_9), so the budget is floor(min(--cap, observed limit) * 0.9). Also: the periodic position check now fails closed on any failed read, which makes its docstring true (the native port raises on 429/5xx; there is no retry), and the README evidence table no longer contradicts itself about alpaca-py 0.44.0: SDK objects are built under the pinned venv, while the order request, the SDK-client calls and a running stream are not exercised. Offline receipts are unchanged apart from provenance and the random prefix. No broker, network or credential was used. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
1. Quote age at the wire: LiveCapacityRun passes the price's quote wall time to the live port, whose final submit boundary (_before_request) re-checks age <= 10 s next to the STOP files and go expiry, after pool.get() and immediately before the POST. A refusal is not sent and is counted (live.quote_freshness.stale_at_wire). 2. SIGHUP exit code: a summary print that fails with OSError (EIO on a closed terminal) no longer raises; stdout's descriptor is pointed at /dev/null so the interpreter's final flush cannot turn the status into 120, and main returns the run's own exit code. 3. Clock skew: the live preflight brackets GET /v2/clock with host wall times and refuses (host_clock_skew) when |host - broker| plus half the round trip exceeds 1 s; an unreadable timestamp refuses (broker_clock_unreadable). The measurement is in live.host_clock. 4. 429 on the position read: the native port's positions() raises ReadFailed carrying its sanitized Response; the periodic check and _sync feed it to the rate governor before freezing, so cleanup cancels honour Retry-After. 5. Journal guards: read_journal reuses the lstat/S_ISREG/64 MiB guard of existing_journal_refusal, opens with O_NOFOLLOW|O_NONBLOCK, re-checks the opened inode and reads at most 64 MiB, so recover/audit refuse a FIFO, symlink or oversized journal (journal_not_regular_file, journal_too_large). Offline tests only (fake broker, stub SDK client, temp files); no network, broker call or credential read. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
…mode-20260924 # Conflicts: # blueprints/us-equities/order-throughput/README.md # tests/test_order_throughput.py
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
CI note (2026-09-25). Nothing in the PR needs to change. The next push, or a close and reopen, runs the current 🤖 Generated with Claude Code |
|
Closing: live-trading modes are out of scope for this repository. The owner's standing rule is that live is handled elsewhere and this repo resolves the paper lane only; see docs/paper-lane-policy.md and the lane split in docs/lanes.md. The branch is kept, so the work can be reopened here or carried to the private trading repository. Part of the 2026-09-25 noise cleanup approved by the repository owner; the trading-lane session acknowledged. |
What
capacity.py liveadds a live capacity mode to the #165 order-throughput harness, so the Alpaca Elite live limit (1,000 calls/min) can be measured once the user supplies a live key and an explicit go.Gates (every one refuses before any request):
~/.config/codex-ecosystem/secrets/alpaca-live-go.jsonwith an expiry of at most 24 h and its own caps;alpaca-live-<label>.envcredential name (paper names refused both ways);--i-understand-live-ordersflag;Probes:
needs_attention).Review trail
config_from_argsnow applies the paper default cap of 200 itself, and a fixture order id is no longer UUID-shaped, which publication validation flagged.Evidence
python3 -m unittest tests.test_order_throughput: 122 OK (2 SDK tests skipped without alpaca-py). With the pinned alpaca-py 0.44.0 venv: 122 OK.python3 -m unittest: 3,993 OK (421 skipped).scripts/validate.pyandevidence_manifest.py --check: passed.capacity.py offline: capacity criteria met (evidence class offline_fixture, never a pass).origin/main..HEAD: no leaks.Not authorized by this PR
Any live order. A live run needs the user's live key and a go file written by the user.
🤖 Generated with Claude Code