Files
siloqy/prod/docs/VIOLET_PART_SPEC_OA_TODO_PASS4.md
Codex 80ce1a1afe VIOLET OA: partial spec PASS 4 — next plan units (entry gate, exits, accounting, exec-intent, feed)
VIOLET_PART_SPEC_OA_TODO_PASS4.md — six plan-derived independent units continuing from PASS 3,
extending the shared contracts_v3 vocabulary (adds ExecIntent / CapitalState / VolGateResult /
AlphaBar):
  14. Volatility regime gate (faithful vol_ok — the entry precondition).
  15. AlphaExitEngineV7 wrapper (V3a deferred — wrap BLUE's exit engine as-is, discretionary pri 2).
  16. Capital/economics provenance ledger (capital = anchor + Σ deltas, event_seq, exactly-one-row).
  17. Execution-intent emitter, DARK (V4 boundary — ShadowDecision → ExecIntent, log-only sink).
  18. Alpha-side reactor data feed (V2 NT Binance DATA spike → AlphaDataFeedPort, data-only).
  19. MAX_HOLD + MEAN_REVERSION scan-driven exit timers.
Each ships + tests standalone; composition map + recommended order (14→16→15→19→17→18) + the same
HARD RULES + commit/branch policy. VIBRISS/adaptive-TP explicitly excluded.

Also: added PASS 3 + PASS 4 to the review queue in VIOLET_TODO_CRITICAL.md §2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 09:27:23 +02:00

12 KiB

VIOLET — partial spec for another agent, PASS 4 (OA TODO)

Date: 2026-06-17. Continues PASS 3 (same V0→V6 plan, same independent-unit discipline). PASS 3 delivered the venue-feed / mechanical-exit / restore / slippage / cadence layer; PASS 4 adds the ENTRY GATE, the EXIT KERNELS (V7 + time-based), the ACCOUNTING ledger, the EXECUTION-INTENT boundary, and the ALPHA-SIDE data feed. Drawn strictly from VIOLET_DEV_SPEC_AND_PLAN.md — nothing invented. Each task is an independent, separately-testable unit; the ONLY coupling is the shared contracts_v3.py vocabulary (extended in §I).


0. HARD RULES (identical to PASS 1/2/3 — summarized)

  • Never edit shared files (prod/nautilus_event_trader.py, clean_arch/dita_v2/**, dita/decision.py, nautilus_dolphin/**, blue_parity.py, prod/bingx/leverage.py). READ only.
  • VIOLET DARK — no orders, no execution, no keys, no service/HZ control, no PROGREEN.
  • V-TYPES on all new code; faithful poison-guards only.
  • NEW-FILE-ONLY; do NOT modify in-flight files (live_blue_source.py, shadow_live_factors.py, live_factor_source.py, live_factors.py, decision_engine.py, sizing.py, shadow_journal.py, cadence.py, clock.py, divergence.py, alpha_wrappers.py, exchange_leverage.py, 22_violet_decisions.sql). READ + IMPORT them.
  • VIBRISS stays DARK — do NOT build adaptive-TP / VIBRISS-governable mechanisms here.

0a. COMMIT / BRANCH POLICY (3 shared-index collisions on 2026-06-16 — non-negotiable)

Own git worktree (git worktree add ../vp-oa4 -b agent/oa-violet4) strongly preferred. Else never git add -A; git commit -F msg -- <files> with explicit pathspec; verify git show --stat --format="" HEAD lists ONLY your files. One commit/task, prefix VIOLET OA:, Co-Authored-By trailer. Tests on /home/dolphin/siloqy_env/bin/python3 -m pytest -q. git grep only (recursive grep/find time out on CIFS).

0b. ClickHouse (read-only): http://localhost:8123, user dolphin/key dolphin_ch_2026;

VIOLET db dolphin_violet, BLUE db dolphin. NEVER write/alter production tables.


I. SHARED INTERFACE EXTENSIONS (add to contracts_v3.py from PASS 3; never fork a parallel type)

Reuse PASS-3 VenueTick, OpenPositionView, ExitDecision. ADD:

  1. ExecIntent (StrictModel) — a DARK would-be order: asset: Symbol, side: str, qty: Qty, exchange_leverage: Annotated[int, Field(ge=1)], maker_policy: str, target_notional: float, ts_ns: MonoNs, reason: str ("ENTRY"/"EXIT"). NEVER sent.
  2. CapitalState (StrictModel) — capital: float (ge=0, finite), anchor: float, delta_sum: float, event_seq: Seq, pnl_source: str, capital_source: str. Capital is ALWAYS anchor + Σ deltas (never a last-value snapshot — the zombie-trade lesson).
  3. VolGateResult (StrictModel) — vol_ok: bool, metric: float, threshold: float.

If a task needs a field not listed, ADD it to contracts_v3.py and note it.


TASK 14 — Volatility regime gate [plan: entry precondition vol_ok / vol_p60_threshold]

Why. BLUE gates entries on a volatility regime check (vol_ok, vol_p60_threshold); VioletDecisionEngine.decide(... vol_ok=...) already takes it as a param but nothing computes it faithfully. This unit computes vol_ok the way BLUE does so the entry gate is real, not assumed.

Affected files (NEW only): prod/clean_arch/violet/vol_gate.py, prod/clean_arch/violet/test_violet_vol_gate.py. (imports contracts_v3.)

Interface/approach. First READ how BLUE computes the vol gate (search the orchestrator / the engine for vol_regime_ok / vol_p60 / the percentile threshold; transcribe the EXACT formula). Define compute_vol_gate(price_histories: dict[str, list[float]], *, threshold: float) -> VolGateResult (@typed) reproducing BLUE's metric + comparison. Pure function.

Tests / pass criteria. Parity: hand-built histories where BLUE's formula gives a known vol_ok; assert match. Boundary: at/just-below/just-above threshold. Hypothesis: result finite, vol_ok bool. DONE when the formula is transcribed from live BLUE (cite file:line in a docstring), tests pass, no edits outside the 2 files.


TASK 15 — AlphaExitEngineV7 wrapper [plan: V3a deferred — "run exit-v7 as-is first, refactor later"]

Why. The plan wraps BLUE's live multi-leg exit engine AlphaExitEngineV7 behind a V-TYPES boundary and runs it AS-IS. Its outputs are DISCRETIONARY exits (priority 2) that, per the exit doctrine, can NEVER override the mechanical TP/SL (PASS-3 Task 9).

Affected files (NEW only): prod/clean_arch/violet/exit_v7_wrapper.py, prod/clean_arch/violet/test_violet_exit_v7_wrapper.py. (imports contracts_v3.)

Interface/approach. WRAP, don't reimplement: import nautilus_dolphin.nautilus.alpha_exit_v7_engine.AlphaExitEngineV7; build its context (make_context) and call evaluate over an OpenPositionView + market state; map its result to an ExitDecision(reason=…, priority=2). Refined domain types in/out. The wrapper must NOT change V7's logic. Document any state V7 needs.

Tests / pass criteria. For crafted inputs, the wrapper's ExitDecision matches what AlphaExitEngineV7.evaluate returns (same exit/hold + reason), priority always 2. DONE when wrap parity tests pass, no edits outside the 2 files.


TASK 16 — Capital / economics provenance ledger [plan: V1 inheritance — capital = anchor + Σ deltas]

Why. Binding VIOLET doctrine (from the zombie-trade incident + malformed-open audit): capital is ALWAYS anchor + Σ signed per-trade deltas, NEVER a last-value snapshot; every row carries pnl_source/capital_source provenance + event_seq; exactly-one-row-per-event. Per-trade sizer feedback uses trade-realized PnL, never capital deltas (shared-account foreign-fill immunity).

Affected files (NEW only): prod/clean_arch/violet/economics_ledger.py, prod/clean_arch/violet/test_violet_economics_ledger.py. (emits CapitalState.)

Interface/approach. EconomicsLedger(anchor: float) with apply(delta: float, *, event_seq: int, pnl_source: str) -> CapitalState (monotonic event_seq; rejects out-of-order / duplicate seq; capital = anchor + Σ deltas). capital() returns the derived value, never a stored snapshot. Reject NaN/inf deltas. No CH writes (pure in-memory ledger + a to_row() for a future sink).

Tests / pass criteria. Σ-delta correctness over a sequence; duplicate/out-of-order event_seq rejected; capital never goes negative below a floor guard; Hypothesis: capital == anchor + sum of accepted deltas, always. DONE when tests pass, no edits outside the 2 files.


TASK 17 — Execution-intent emitter (DARK) [plan: V4 prep — feeds the ExecDeadlineDriver]

Why. V2 built the ExecDeadlineDriver + synthetic intents; V4 will turn decisions into real orders. This unit is the DARK boundary that converts a decision into an ExecIntent (a would-be order) — shadow-logged, NEVER sent — so V4 only has to flip the sink from log to venue.

Affected files (NEW only): prod/clean_arch/violet/exec_intent.py, prod/clean_arch/violet/test_violet_exec_intent.py. (imports contracts_v3; composes PASS-2 tradeability.py if present, else recompute via exchange_leverage.py.)

Interface/approach. to_exec_intent(decision: ShadowDecision, *, capital: float, maker_policy: str = "maker_both") -> ExecIntent — derive qty from notional/price, exchange_leverage from the L3 mapping, attach maker policy. Pure projection; an IntentSink protocol with a default LoggingIntentSink (shadow-logs, never sends). Hard guard: this module imports NOTHING that can place an order; assert no venue/order symbol is importable here (a test enforces it).

Tests / pass criteria. Intent fields correct vs the decision + L3 mapping; LoggingIntentSink records, never sends; a test asserts the module has no order-placing dependency. DONE when tests pass, no edits outside the 2 files.


TASK 18 — Alpha-side reactor data-feed adapter [plan: V2 NT Binance DATA client spike → feed]

Why. The V2 spike GO-qualified the Nautilus Binance DATA client as VIOLET's alpha-side feed (separate feed process recommended; public-data only; dummy keys for the factory). This unit delivers that feed behind a clean port so the reactor consumes normalized bars without coupling to NT internals.

Affected files (NEW only): prod/clean_arch/violet/alpha_data_feed.py, prod/clean_arch/violet/test_violet_alpha_data_feed.py.

Interface/approach. Define AlphaDataFeedPort (ABC): start(), latest_bar(asset), stop(), yielding a normalized bar type (AlphaBar — add to contracts_v3: asset, open/high/low/ close > 0, volume ≥ 0, ts_ns, source). Implement NautilusBinanceDataFeed (READ the V2 spike notes / dolphin_actor*.py / live_price_feed.py for the NT data-client wiring; public-data, dummy keys, NO execution). Implement MockDataFeed (deterministic). NO orders, data-only.

Tests / pass criteria. Mock feed yields valid AlphaBars; poison bars rejected; the NT adapter parses a recorded bar message into a correct AlphaBar (captured-frame fixture, no live connection in the unit test). DONE when contract finalized + parse-tested, no live feed in unit tests, no edits outside the 2 files.


TASK 19 — MAX_HOLD + MEAN_REVERSION scan-driven exit timers [plan: scan-driven exits, V6 bible]

Why. The plan keeps MAX_HOLD and MEAN_REVERSION as SCAN-driven exits (champion params are 5s-bar-denominated; re-timing requires VBT re-cert). They are mechanical/time-based exits that compose with the exit framework (PASS-3 Task 9) at the right priority.

Affected files (NEW only): prod/clean_arch/violet/time_exits.py, prod/clean_arch/violet/test_violet_time_exits.py. (imports contracts_v3.)

Interface/approach. READ BLUE's exit manager for the EXACT MAX_HOLD (max_hold_bars) and mean-reversion rules (AlphaExitManager / the orchestrator). Define evaluate_time_exits(pos: OpenPositionView, *, bars_held: int, vel_div: float) -> ExitDecisionbars_held >= max_hold_barsExitDecision(EXIT, "MAX_HOLD"); mean-reversion condition → ExitDecision(EXIT, "MEAN_REVERSION"); else HOLD. Scan-cadence (no sub-second). Priority below mechanical SL/TP, above pure discretionary (document the chosen priority vs Task 9/15).

Tests / pass criteria. MAX_HOLD fires exactly at the bar threshold; mean-reversion fires on the BLUE condition (transcribed, cited); neither fires early. DONE when tests pass with the rule cited from live BLUE, no edits outside the 2 files.


Composition map

contracts_v3  (VenueTick, OpenPositionView, ExitDecision, ExecIntent, CapitalState, VolGateResult, AlphaBar)
   ├── 14 vol_gate(histories)            → VolGateResult     (entry gate; feeds decide vol_ok)
   ├── 18 alpha_data_feed                → AlphaBar stream    (alpha-side reactor feed)
   ├── 15 exit_v7_wrapper(pos, state)    → ExitDecision(pri 2)  ┐
   ├── 19 time_exits(pos, bars, veldiv)  → ExitDecision         ├─ exit stack, ranked by priority,
   │      (PASS-3 9 mechanical = pri 0/1; 10 SL floor = pri 0)  ┘  mechanical always wins
   ├── 16 economics_ledger(anchor,Δ…)    → CapitalState      (accounting; never last-value)
   └── 17 exec_intent(decision,capital)  → ExecIntent (DARK)  (V4 boundary; log-only sink)

Integration (ranking the exit stack, wiring the feed into the reactor, the ledger into the journal, the intent sink into the driver) is the OWNER's job later — NOT part of these units.

14 (vol gate) → 16 (ledger) → 15 (V7 wrap) → 19 (time exits) → 17 (exec intent) → 18 (data feed). 14/16 are small + foundational; 18 is the heaviest (NT adapter).

Pass/sprint nomenclature (re-asked) — see VIOLET_TODO_CRITICAL.md §3

"pass" = a sub-sprint work-package (a batch of self-contained tasks for one agent); a V-stage (V0…V6) = the project's "Sprint N" / epic. Renaming passes to "sprints" over-claims scope. The later REVIEW + INTEGRATE + E2E of all passes is Claude's queued work.

Still NOT in scope (operator/owner only)

DARK soak start; V4 live order placement / BingX ExecutionClient; HZ-bridge refactor; VIBRISS / adaptive-TP; the V3.4c parity root-cause (CRITICAL #1); any edit to the in-flight files in §0.