Skip to content

[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:

self.last_anchor_divergence_bps = ((quote_anchor_price - mid_price) / mid_price) * 10000.0

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_price itself is unchanged — only its use in quote placement is replaced. Downstream uses (self._anchor_history.append(...), bid_protection_delta_bps, anchor_divergence_bps for Phase 3E) still reference quote_anchor_price. That preserves existing Phase 3D/3E behavior as-is.
  • last_anchor_divergence_bps is computed and persisted before the switch — the session summary |err|>5bps stat (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

git checkout main
git pull origin main
git checkout -b feat/phase-7.2-clob-switch

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

git log --oneline main..HEAD
python -m pytest tests/test_phase_7_2_clob_switch.py -v

Expected: one commit (feat(phase-7.2): binary CLOB/anchor switch at 3 bps threshold); 5 passed.

Block 4 — push

git push origin feat/phase-7.2-clob-switch

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