[C] Orion Message — Phase 7.2 CLOB Switch Ready
Source variable — confirmed¶
anchor_error_bps is StrategyEngine.last_anchor_divergence_bps, computed at neo_engine/strategy_engine.py:203:
This is the post-cap divergence — i.e., under capped_amm at cap=10, a 15 bps raw AMM-vs-CLOB divergence pins last_anchor_divergence_bps at ±10, not ±15. Per your brief that's the correct quantity to threshold against.
clob_mid is snapshot.mid_price, which this repo already defines as (best_bid + best_ask) / 2 in MarketDataAdapter — no recomputation needed; I used mid_price directly.
Exact lines changed¶
File: neo_engine/strategy_engine.py
Insertion after line 206 (immediately after self.last_cap_applied = cap_applied):
# Phase 7.2: binary CLOB/anchor switch for quote placement.
# When the anchor is in unreliable territory (|anchor_error_bps| > 3),
# use CLOB mid as the reference_mid for quote placement; otherwise
# use the selected anchor. Atlas-locked threshold is 3 bps (control
# threshold — distinct from the 5 bps evaluation reliability floor).
# Binary only — no blending, no weighted average.
if (
self.last_anchor_divergence_bps is not None
and abs(self.last_anchor_divergence_bps) > 3.0
):
reference_mid = mid_price
reference_source = "clob_mid_phase7_switch"
else:
reference_mid = quote_anchor_price
reference_source = quote_anchor_source
Replacement at buy/sell quote placement (was lines 255–256):
- buy_price = quote_anchor_price * (1.0 - (final_bid_offset_bps / 10000.0))
- sell_price = quote_anchor_price * (1.0 + (final_ask_offset_bps / 10000.0))
+ buy_price = reference_mid * (1.0 - (final_bid_offset_bps / 10000.0))
+ sell_price = reference_mid * (1.0 + (final_ask_offset_bps / 10000.0))
Replacement in the bid-ladder loop (was line 356):
- level_price = quote_anchor_price * (1.0 - ((final_bid_offset_bps + level_offset) / 10000.0))
+ level_price = reference_mid * (1.0 - ((final_bid_offset_bps + level_offset) / 10000.0))
Log additions for diagnostic continuity (inserted in the Quote calculated log.debug extras dict):
"quote_anchor_source": quote_anchor_source,
"quote_anchor_price": quote_anchor_price,
+ "reference_mid": reference_mid,
+ "reference_source": reference_source,
quote_anchor_price and quote_anchor_source are preserved in the log so historical diagnostic tooling still reads the pre-switch anchor. reference_mid / reference_source give the post-switch value actually used for quote placement. reference_source takes one of two values: clob_mid_phase7_switch when the switch flipped, or whatever quote_anchor_source was (amm_within_cap, capped_amm_upper, etc.) when it didn't.
That is the entire behavioral change. Spread offsets, size, skew/drift, momentum, all bid-protection logic, risk engine, fill paths, and schema are untouched.
What does NOT move¶
quote_anchor_priceitself is unchanged — only its use in quote placement is replaced. Downstream uses (self._anchor_history.append(...),bid_protection_delta_bps,anchor_divergence_bpsfor Phase 3E) still referencequote_anchor_price. That preserves existing Phase 3D/3E behavior as-is.last_anchor_divergence_bpsis computed and persisted before the switch — the session summary|err|>5bpsstat (Item 5 on the cleanup branch) measures anchor drift, not post-switch drift. Those two signals now live side-by-side.
Test coverage¶
File: tests/test_phase_7_2_clob_switch.py — 5 tests, all passing:
| # | Test | Covers |
|---|---|---|
| 1 | test_divergence_below_threshold_uses_anchor |
amm 2 bps above mid → anchor path (buy/sell priced off amm, not mid) |
| 2 | test_divergence_exactly_3_uses_anchor |
|err| == 3 → anchor path (strict > keeps anchor at the boundary) |
| 3 | test_positive_divergence_above_threshold_uses_clob |
amm +5 bps → CLOB path (buy/sell priced off mid) |
| 4 | test_negative_divergence_above_threshold_uses_clob |
amm −5 bps → CLOB path (switch is symmetric via abs()) |
| 5 | test_at_anchor_cap_uses_clob |
raw +15 bps pinned at cap=10 → CLOB path, last_cap_applied == True |
Each test drives a live StrategyEngine.calculate_quote() with crafted MarketSnapshot(mid, amm) values and asserts on the buy/sell intent prices, so the exact code path taken is observed end-to-end rather than mocked.
Regression check: phase3d, phase4a, task5, and main_loop tests have the same pass/fail count before and after my change (I stashed and reran against the baseline to confirm). The pre-existing failures are FLAG-016 territory; my change doesn't touch them. No new regressions.
Branch¶
feat/phase-7.2-clob-switch — off current main (i.e., branched from the feat/flag-031 tip on my sandbox, content-equivalent to Katja's main).
Single commit: feat(phase-7.2): binary CLOB/anchor switch at 3 bps threshold (no cleanup, no refactor, no bundled fixes).
Patch delivered at:
02 Projects/NEO Trading Engine/orion-patches-2026-04-18-phase7.2/0001-feat-phase-7.2-binary-CLOB-anchor-switch-at-3-bps-th.patch
One file, one commit.
Git commands for Katja¶
Run in your VS Code terminal from the repo root. Assumes the cleanup branch has landed (or not — Phase 7.2 doesn't depend on it; different files).
Block 1 — create the branch off current main
Block 2 — apply the patch
# adjust the path to where the patches folder actually is on your machine
$patch = "C:\path\to\Claude Homebase Neo\02 Projects\NEO Trading Engine\orion-patches-2026-04-18-phase7.2\0001-feat-phase-7.2-binary-CLOB-anchor-switch-at-3-bps-th.patch"
git am --3way "$patch"
I don't expect any conflicts — strategy_engine.py hasn't been touched by the cleanup branch or by any config push, and tests/test_phase_7_2_clob_switch.py is new. If you see a conflict, git am --abort and ping me; that would mean main moved underneath me.
Block 3 — verify
Expected: one commit (feat(phase-7.2): binary CLOB/anchor switch at 3 bps threshold); 5 passed.
Block 4 — push
Then merge via GitHub UI. No PR description needed beyond the commit message unless you want one; the commit body covers rationale, implementation, and what's out of scope.
Expected effect (per your brief)¶
Given S37's 69.4% |err|>5bps rate, the engine will price against CLOB for the majority of ticks instead of the lagging anchor. The session after merge is the controlled comparison against S37 baseline (29 fills, VW +3.40 bps). If displacement was the driver of S37's sell-side drag, Phase 7.2 should narrow it.
The reference_source log field also gives you direct post-session observability on how often the switch flipped — you'll be able to tell whether the observed fill-rate change correlates with the share of ticks that took the CLOB path.
— Orion