Orion Delivery — Branch #6 feat/distance-to-touch-diagnostic¶
To: Katja CC: Vesper, Atlas From: Orion Date: 2026-04-19
Status¶
Branch #6 complete. Three commits on feat/distance-to-touch-diagnostic, cut off 059f528 (Branch #5 tip). Full Phase 7.3 primary-metric pipeline now wired end-to-end: schema → per-tick write → session aggregate. Zero regressions — +25 passing / 0 new failures vs Branch #5 baseline (466 / 373 → 491 / 371; the −2 on failed is two pre-existing TestPaperTickTelemetryPersistence tests that fell out green as a side-effect of Commit 2's fixture fix).
| Commit | SHA | Subject |
|---|---|---|
| 1 | a288d8e |
feat(state): add distance_to_touch columns to system_metrics (Branch #6 Phase 7.3) |
| 2 | 50e40e5 |
feat(main-loop): compute, log, and persist distance-to-touch per tick |
| 3 | 99483c6 |
feat(summary): per-side distance-to-touch aggregate block (Branch #6 Phase 7.3) |
Patches at 02 Projects/NEO Trading Engine/patches/branch-6-distance-to-touch-diagnostic/ — three numbered .patch files.
Built against Vesper's Q1/Q2/Q3 rulings (columns on system_metrics, signed values, live-order source, NULL when no live order). All three rulings are pinned by tests.
What landed, per commit¶
Commit 1 — schema migration¶
Two new REAL columns on system_metrics: distance_to_touch_bid_bps, distance_to_touch_ask_bps. Both NULLable. Dual-path initialization:
CREATE TABLE IF NOT EXISTS system_metricsextended for fresh DBs._ensure_column(...)migration entries added for existing DBs (the liveneo_live_stage1.dbtakes this path).
record_system_metric() gained two optional kwargs defaulting to None — pre-Branch-6 callers leave the columns NULL (confirmed by unchanged test_record_system_metric_persisted after an assertion was added to check the new columns default to NULL).
Tests (in tests/test_state_manager.py):
test_record_system_metric_persisted— extended: new columns default to NULL when caller omits them.test_record_system_metric_distance_to_touch_persisted(new) — explicit negative BUY (-1.25) and positive ASK (+11.75) round-trip intact. Pins the signed contract at the persistence layer.test_system_metrics_schema_has_distance_to_touch_columns(new) — PRAGMA table_info confirms both columns exist afterinitialize_database().
Commit 2 — per-tick compute + persist + log¶
New helper NEOEngine._compute_distance_to_touch(snapshot) returning tuple[Optional[float], Optional[float]]. Called from _persist_tick_telemetry. The bid side measures (best_bid − our_live_buy_price) / best_bid × 10 000; the ask side mirrors on the sell side. Source of the "our" price is self._state.get_live_order_by_side(...) — actual resting order, not intent (Vesper Q3).
Null semantics, per Vesper Q3:
- Missing live order on a side → NULL for that side.
- Missing or zero
best_bid/best_ask→ NULL for that side. - Any exception computing a side → NULL for that side (telemetry-swallows-exceptions contract honored per side, tick never affected).
Signed, per Vesper Q2. No abs() anywhere on the write path. A negative value means our quote is at or through the contra touch — the Phase 7.3 alarm signal.
Structured log emitted when at least one side is non-None:
Values passed straight through to record_system_metric(distance_to_touch_bid_bps=..., distance_to_touch_ask_bps=...).
Tests (in tests/test_main_loop.py):
test_paper_tick_persists_system_metrics— extended: both new kwargs NULL when no live order.test_paper_tick_distance_to_touch_computed_from_live_orders(new) — BUY 0.999 at best_bid 1.0 → +10.0 bps; SELL 1.1011 at best_ask 1.1 → +10.0 bps.test_paper_tick_distance_to_touch_preserves_negative_sign(new) — BUY 1.0001 at best_bid 1.0 → −1.0 bps. Sign round-trips through the full tick.test_paper_tick_distance_to_touch_per_side_null_when_touch_missing(new) — best_bid=None → BUY NULL, SELL still computed independently.
Fixture note (incidental): the _make_engine fixture uses NEOEngine.__new__(...) to bypass __init__, so it was missing _last_valuation_snapshot_ts and valuation_snapshot_interval_seconds. Two pre-existing TestPaperTickTelemetryPersistence tests were failing for this reason (verified pre-existing by stashing my changes). Added two fixture lines to initialize both; these two tests now pass green as a side-effect. Documented in the commit body.
Commit 3 — session aggregates in summarize_paper_run.py¶
New public functions:
load_distance_to_touch_summary(db_path)— readssystem_metricsfor the latest session, drops NULL samples per side, returns:{ "session_id": <int>, "bid": {"n": ..., "min": ..., "p50": ..., "p95": ..., "max": ..., "crossings": ...}, "ask": {"n": ..., "min": ..., "p50": ..., "p95": ..., "max": ..., "crossings": ...}, }crossings= count of ticks where the signed distance is< 0. ReturnsNonewhen no session, no rows, or all-NULL distances.render_distance_to_touch_summary(dt)— matches house style ("Title:"header +"\n".join(lines)).
Wired into main() after render_execution_quality_summary, same print-with-blank-line pattern as quote-quality.
Parallel to load_quote_quality_summary, not an extension of it — load_quote_quality_summary early-returns when mid is None, which would silence the alarm block on any run where the live engine_state mid is unset. Keeping them independent means the distance-to-touch block fires off system_metrics alone.
Also new: private helper _percentile(values, p) doing linear interpolation (numpy method='linear' semantics). stdlib statistics only covers quartiles/deciles + median; we need exact p50/p95 at small sample counts.
Legacy dist_to_bid / dist_to_ask / near_5bps retained unchanged per Vesper's ruling — remove in a follow-up branch after S40 confirms coherent numbers.
Tests (new file, tests/test_distance_to_touch_summary.py, 18 tests):
_percentile: empty, single-value, even/odd count medians, p95 linear interp on 1..20, unsorted input.load_distance_to_touch_summary:- None paths: no session / no rows / all-NULL.
- Per-side population: bid-only, ask-only, both populated with interleaved NULLs.
- Crossings count strictly
< 0(zero does not count — Vesper's contract). - NULL rows not counted as crossings.
- Latest-session scoping (100 crossings in an older session do not leak).
- p50/p95 math pinned against numpy linear (1..20 → p50=10.5, p95=19.05).
render_distance_to_touch_summary:- Shape: header + BUY + SELL; crossings phrase on every side.
- Signed formatting: negatives print with
-, positives with+. - No-samples side still shows
crossings(<0): 0for operator clarity.
What the new block looks like¶
A healthy calibration session:
Distance-to-Touch Summary:
BUY: n=412 | min=-0.87 | p50=+5.24 | p95=+11.93 | max=+14.10 bps | crossings(<0): 3
SELL: n=408 | min=+0.12 | p50=+6.08 | p95=+12.45 | max=+15.02 bps | crossings(<0): 0
An early session with only ask-side live order history:
Distance-to-Touch Summary:
BUY: no samples | crossings(<0): 0
SELL: n=18 | min=+10.20 | p50=+11.40 | p95=+12.10 | max=+12.30 bps | crossings(<0): 0
crossings(<0) is the Phase 7.3 alarm line. Non-zero on a passive-mode session means we're at or through the contra touch — pennying or a pricing bug.
Test posture¶
- Branch #6-specific tests: 18 new (test_distance_to_touch_summary) + 3 new (test_state_manager) + 3 new (test_main_loop) = 24 new passing, plus 2 pre-existing
TestPaperTickTelemetryPersistencenow green from the Commit 2 fixture fix. - Full suite vs Branch #5 baseline: 491 passed / 371 failed (was 466 / 373). Net +25 passed, −2 failed. No new regressions.
- The 371 remaining failures are the same pre-existing FLAG-034 fixture-orphan cluster and other baseline issues carried from Branches #1–#5. Unchanged by Branch #6.
Copy-paste for Windows VS Code terminal¶
# Mirror branch from commits (cut off Branch #5 tip)
git fetch origin
git checkout -b feat/distance-to-touch-diagnostic 059f528
# Apply the three patches
git am "02 Projects\NEO Trading Engine\patches\branch-6-distance-to-touch-diagnostic\0001-feat-state-add-distance_to_touch-columns-to-system_m.patch"
git am "02 Projects\NEO Trading Engine\patches\branch-6-distance-to-touch-diagnostic\0002-feat-main-loop-compute-log-and-persist-distance-to-t.patch"
git am "02 Projects\NEO Trading Engine\patches\branch-6-distance-to-touch-diagnostic\0003-feat-summary-per-side-distance-to-touch-aggregate-bl.patch"
# Verify
git log --oneline -4
python -m pytest tests/test_state_manager.py tests/test_main_loop.py tests/test_distance_to_touch_summary.py -q
# After Vesper sign-off:
git push -u origin feat/distance-to-touch-diagnostic
What this unlocks¶
- Phase 7.3 calibration has a signed, per-tick, persisted primary metric now. Every offset sweep session's summary block will surface p50/p95/max passive distance and the crossings count.
- Operational alarm.
crossings(<0)on a paper session should be exactly 0 under normal passive market-making. Non-zero = we're at or through the contra touch, which means either (a) a pricing bug shipped or (b) offset calibration drifted too aggressive. Visible in the terminal at session end. - Phase 7.3 sweep readability. Comparing
p50andp95across offset configurations will tell us the distribution cost of each offset choice, not just a mid-based average.
Branch queue after this¶
- Branch #7
fix/wal-checkpoint-hardening— FLAG-035, the paper shakedown before live. - S40 after 6+7 merge, then Phase 7.3 offset calibration sweeps.
Standing by for Vesper review.
— Orion