Commit Graph

88 Commits

Author SHA1 Message Date
Codex
9bef1f6b01 dita_v2: VenueTelemetrySnapshot + Zinc venue plane (4th shm partition) + local Rust target dir
Upstream-canonical commit of the ~366 lines that sat uncommitted in the working
tree (surveyed in DITAV2_SOA_SURVEY_20260702). Venue telemetry published at every
venue boundary (submit/cancel/reconcile) over the zinc venue_region; SOA per
UV DITAv2 adjudication 2026-07-03.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 00:42:20 +02:00
Codex
37748cf180 feat(violet): V4 arm-readiness reporter (PASS MM1)
Read-only aggregation module answering 'is V4 ready to ARM the live canary?'
by consuming gate outcomes into a typed StrictModel report.

Files:
- v4_readiness.py: V4ReadinessReport (6 bool gates + reasons) + assess_v4_readiness()
- test_violet_v4_readiness.py: 13 tests (happy, parametrized single-false,
  hypothesis 2^6 invariant, mutation litmus, StrictModel poison)

Co-Authored-By: mm_VIOLET1 <mm_VIOLET1@h5i>
2026-06-24 14:01:51 +02:00
Codex
12b768bc4f VIOLET OA: add venue OB provider seam
Co-authored-by: Codex <codex@openai.com>
2026-06-16 23:01:17 +02:00
Codex
53bdd908f4 VIOLET OA: add sizing parity pin tests
Co-authored-by: Codex <codex@openai.com>
2026-06-16 22:51:07 +02:00
Codex
0f3d7650c3 VIOLET V3.4d: launcher OB-persistence + boost/beta prior; OB-feed/HZ-bridge/coordination doc
Launcher shadow path now keeps the persistent state BLUE keeps across scans (faithful, not
per-scan single-shot): ONE OBFeatureEngine wired lazily on first assets and kept for the
service lifetime, reading BLUE's EXTANT published OBF feed via HZOBProvider (read-only HZ
entry-listener cache — NO new OB storage; provider is the swap seam for a future direct
BingX/3rd-party OB stream); per-scan ob_bar_idx into step_live/get_market; and prior_boost_beta
carried across scans (stale exf keeps the prior). shadow_decision_step threads ob_engine +
bar_idx + prior into source_live_blue_sizing_factors; build_shadow_live_source seeds the state
+ a default ob_engine_factory (injectable for tests).

TODO_HZBRIDGE markers added at all 3 VIOLET->Hazelcast touch points: per operator the upcoming
dolphinng5_predict/hzbridge must become the sanctioned HZ connection (silent client-death /
lockup / dropout mitigation) — refactor ASAP once it ships.

Doc VIOLET_OB_FEED_AND_AGENT_COORDINATION.md: OB-feed sourcing doctrine, HZ-bridge TODO, and
the multi-agent worktree + doctrine/release/status scheme (prompted by the shared-index
incidents where agents' staged files cross-contaminate commits).

6 launcher tests (+wired-once/bar_idx-increments, +prior-carries). violet-only; partial commit
(only my 4 paths) to avoid sweeping a concurrent agent's staged files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 21:44:03 +02:00
Codex
262cada664 VIOLET V3.4c: ACB boost/beta — keep-prior on stale exf, decision logging, semantics doc
Per operator: VIOLET must keep a "prior" on stale ExF rather than collapse to neutral, and
the ACB's role must be commented-in + logged.

- KEEP-PRIOR on stale/missing exf: BLUE keeps _day_base_boost/_day_beta on the stale branch
  (update_acb_boost isn't called). _source_boost_beta now takes `prior` (boost,beta) and
  returns it on ValueError / no-exf, seeded by the first successful compute. Threaded as
  `prior_boost_beta` through source_live_blue_sizing_factors (caller persists last good).
- LOGGING: violet.live_blue_source logger emits live boost/beta + the full sourced plane.
- SEMANTICS: documents the ACB in effect (inverse v6) and CONFIRMS ACB IS in the sizing layer
  via _day_base_boost/_day_beta → _update_regime_size_mult (esf_alpha_orchestrator.py:771-772,
  898-909). OB Sub-4 beta modulation dormant in live — per operator found NON-PERFORMANT and
  deliberately bypassed; TODO_SOMEDAY in-code to find documentary confirmation.

37 tests. violet-only; no shared-file edits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 18:22:29 +02:00
Codex
fac287d678 VIOLET V3.4c: make boost/beta, signal-gen, OB bit-identical to BLUE (+ exhaustive tests)
Operator directive: VIOLET must do IDENTICALLY what BLUE does for the three parity
flags — approximation cannot guarantee bit-for-bit functioning. Reworked
live_blue_source.py to call BLUE's OWN code paths, not reconstruct/substitute them.

boost/beta — was reading the published DOLPHIN_FEATURES.acb_boost scalar
(acb_processor_service's daily value). BLUE's trader does NOT use that for sizing; it
recomputes live via acb.get_dynamic_boost_from_hz(exf_latest, w750_velocity, direction)
with a bare AdaptiveCircuitBreaker() and NO ob_engine (nautilus_event_trader.py
on_exf_update:4769 / rollover prewarm:2710). New _source_boost_beta replicates that call
exactly (reads exf_latest + latest_eigen_scan.w750_velocity; 0.0→None like BLUE; on stale
exf ValueError → neutral, mirroring BLUE's "ACB Stale Data Fallback"). The published
acb_boost is never read. Test pins bit-identity against the real ACB.

signal-gen (dc_status) — was AlphaSignalGenerator() bare defaults; coincidentally equal to
BLUE today, but BLUE builds it from ENGINE_KWARGS (trader:128-133, threaded at
esf_alpha_orchestrator.py:180-191), so a champion retune would silently diverge. Now
constructed with BLUE_SIGNAL_GEN_KWARGS (vel_div_* imported from the kernel constants).
Test parses ENGINE_KWARGS from the trader source and asserts each param matches — drift
becomes a red test, not a silent miss.

OB — was a reinvented HazelcastOBProvider reading asset_*_ob with custom parsing. Now uses
BLUE's OWN HZOBProvider + OBFeatureEngine, wired exactly as _wire_obf
(nautilus_event_trader.py:4967-4980): step_live(assets, bar_idx) then get_market. Engine
is injectable + persistent so OB accumulation matches BLUE across scans (caller owns it).

Deleted: HazelcastOBProvider, _extract_acb, the status-label mc path. mc_scale fix
(begin_day cat/env thresholds) retained. Module docstring + structural-divergence doc
updated: all three flags FIXED; only _derive_mc_scale remains hand-replicated (pinned by
formula test). OPEN follow-up: launcher shadow_decision_step should pass a persistent
ob_engine + bar_idx for cross-scan OB history.

34 tests (33 + live-HZ smoke deselected): boost/beta-vs-ACB bit-identity (incl. w750=0→None,
no-exf, stale ValueError, ignores acb_boost), signal-gen ENGINE_KWARGS pin (parametrized),
OB wiring (step_live call, HZ coords, neutral paths), mc_scale formula, sequence dc/selector
parity, anomaly handling. violet-only; no shared-file edits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 17:33:01 +02:00
Codex
1415a65670 VIOLET V3.4c review: fix mc_scale BLUE-parity bug + document re-derivation debt
REVIEW of the V3.4c/d/e work (other agents) for BLUE-algo compliance found one
real bug and one architectural concern.

BUG FIXED — mc_scale derivation (live_blue_source.py):
The adapter mapped `mc_forewarner_latest.status == "ORANGE"` → 0.5. But BLUE's sizing
mc_scale is NOT the MC service's `status` label (that label, from mc_forewarner_flow.py,
uses GREEN<0.10/ORANGE<0.30/RED — observability only). The live trader re-derives the
haircut in begin_day (esf_alpha_orchestrator.py:956-962) from the SAME published fields
`catastrophic_prob` + `envelope_score` with DIFFERENT thresholds:
    mc_red    = cat>0.25 or env<-1.0
    mc_orange = (not mc_red) and (env<0 or cat>0.10)
    mc_scale  = 0.5 if mc_orange else 1.0
The two disagree (e.g. cat=0.05/env=-0.5 → label GREEN→1.0 but BLUE orange→0.5;
cat=0.28 → label ORANGE→0.5 but BLUE red→1.0). Rewrote `_derive_mc_scale` to mirror
begin_day exactly on the source fields. Per operator: there is genuine ambiguity over
which surface is "nominal" — we go with the SOURCE FIELDS + begin_day formula (the path
that actually drives BLUE's sizing); the ambiguity + decision are noted in the docstring.

TESTS — the old fixtures were complicit: they fed `{"status":"ORANGE"}`, a payload shape
BLUE never emits, so they "passed" against fiction. Replaced with BLUE's real payload
(`catastrophic_prob`/`envelope_score`) and added a parametrized formula test covering the
exact divergence cases the status-based code got wrong, plus missing/garbage-field neutral.
15 passed (live-HZ smoke deselected).

ARCHITECTURAL DEBT documented — VIOLET_BLUE_PARITY_STRUCTURAL_DIVERGENCE.md:
VIOLET imitates BLUE's computations in a DIFFERENT module/scope structure. Kernels are
WRAPPED (safe), but orchestration arithmetic (compose/regime/ob/strength/mc_scale) is
HAND-REPLICATED out of the monolithic NDAlphaEngine — making orderly, verifiable BLUE↔VIOLET
parity comparison and refactoring hard. Doc inventories every re-derivation with BLUE's
file:line authority, the drift risk, and mitigations (parity-pin tests; single canonical
surface per ambiguous factor; eventual DITAv2 Rust-backplane convergence). Pointer added to
live_blue_source.py's module docstring.

Also reviewed (no change needed): launcher launch_dolphin_violet.py is DARK-safe
(ObserveOnlyVenue + shadow gated default-OFF, no order path); shadow_live_factors.py glue
is correct; trade_slot_compare.py reimplements no BLUE algo. fb34431's launcher tests —
which the authoring agent never ran on the slow mount — pass (14 green).

violet-only; no shared-file edits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 16:54:35 +02:00
Codex
2d37ef0ae0 VIOLET V3.4c: trade-slot comparison harness 2026-06-16 15:38:25 +02:00
Codex
fb344318aa VIOLET V3.4b: launcher shadow live-factor wiring 2026-06-16 15:14:57 +02:00
Codex
1ac3f627df VIOLET V3.4c: read-only BLUE live source parity 2026-06-16 14:34:49 +02:00
Codex
a632c595ba VIOLET V3.4b: validate live-factor field paths + HZ sourcing adapter
#1 (validation) — VIOLET_V34B_LIVE_FACTOR_FIELD_VALIDATION.md documents the
ground truth from BLUE's own code (esf_alpha_orchestrator / adaptive_circuit_breaker
/ nautilus_event_trader): of the 8 sizing inputs, ONLY `posture` is a flat HZ key.
`esof_score` is in HZ but as a payload to parse. The other five — boost, beta,
mc_scale, ob_median_imbalance, ob_agreement_pct, dc_status — are BLUE-organ outputs
(ACB over DOLPHIN_FEATURES.exf_latest, MC flag derivation, OBFeatureEngine, the
per-asset signal generator) and are NOT present as scalars in any HZ map. This
inverts live_factors.py's flat-snapshot premise; its speculative alternate paths
(acb_boost / s_acb_boost / ("acb","boost") / …) match nothing real. The full live
sourcing of the five is a multi-organ sprint (V3.4c), not a HZ scrape.

#2 (sourcing adapter) — live_factor_source.py sources what BLUE actually publishes,
read-only and pure (callers pass already-fetched HZ blobs; no client, no I/O):
  - posture     ← engine_snapshot['posture'] (DOLPHIN_STATE_BLUE), default APEX
  - esof_score  ← DOLPHIN_FEATURES['esof_latest'] via BLUE's OWN parse_esof_payload
                  + esof_score_from_payload (wrap, don't reimplement; staleness gate
                  honored when max_age_s supplied)
  - boost/beta/mc_scale/ob_*/dc_status ← BLUE's neutral sentinels (1.0/0.0/1.0/None/
                  None/"NONE") until V3.4c — explicit, never silently faked.
Flows through the validated extract_live_sizing_factors normalizer. ORGAN_DERIVED_
FACTORS names the six deferred to V3.4c so the journal can mark them NEUTRAL.

9 new tests green (posture default/upper, esof dict+raw-JSON+staleness, neutral
integration, all-neutral-when-empty). violet-only; no shared-file edits; no soak.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:22:59 +02:00
Codex
722fd9f054 VIOLET V3.4b/V3e: journal the full-sizing breakdown (DDL + row guard)
ShadowDecision already carries the V3.4 5-factor breakdown (base_leverage,
dc_lev_mult, regime_size_mult, market_ob_mult, esof_size_mult), but the journal
row and CH DDL dropped it — so a DARK soak would record decisions WITHOUT the
factor decomposition that V3.4 exists to expose. Thread it through:

- 22_violet_decisions.sql: 5 additive Nullable(Float64) breakdown columns. NULL
  on the legacy base-only path; populated once the launcher feeds live factor
  planes. Note added: on a pre-existing table use ALTER ... ADD COLUMN instead of
  the CREATE IF NOT EXISTS (no live table yet — VIOLET is DARK, never soaked).
- shadow_journal.py: DecisionRow gains the 5 Optional[float] fields (ge=0.0,
  finite-guarded); journal() populates them via getattr(..., None) so the
  base-only path and duck-typed reject tests stay NULL/rejected rather than
  raising on attribute access.
- test_violet_shadow_journal.py: breakdown round-trips on the full path; NULL on
  base-only; a negative multiplier is rejected at the row guard. The existing
  DecisionRow-fields == DDL-columns parity test still holds with the new columns.

violet-only; no shared-file edits; no soak. 29 violet tests green
(7 journal + 22 engine/DDL-apply).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 13:04:30 +02:00
Codex
2629795a35 VIOLET V3.4b: live-factor normalization helper (OA slice, reviewed)
extract_live_factor_plane / extract_live_sizing_factors: pure boundary
helper normalizing scan payload + HZ snapshot into SizingFactors for the
shadow decide path. Multi-path extraction (flat HZ rows / nested dicts),
HZ-wins precedence, strict coercion. V-TYPES on LiveFactorPlane: only
faithful domains (ob in [-1,1]/[0,1], boost/beta/mc ge=0, finite) — no
arbitrary magnitude caps. No I/O, no launcher coupling.

Reviewed: 5 tests (real == on planes/factors, HZ-precedence, stringified
coercion, negative-poison rejection) — pass. Shared files CLEAN.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:52:59 +02:00
Codex
3ca249df8e VIOLET V3.5: L3 exchange-leverage wrapper (agent-built, reviewed + fixed)
Built by OA agent per VIOLET_SUB_SPEC__L3_EXCHANGE_LEVERAGE.md; reviewed for
compliance/flaws. VERIFIED: wraps real prod/bingx/leverage.py (untouched), constants
imported from it, NO arbitrary upper caps, exact == bit-identity (1e6 gate 0 mismatches),
ROUND_HALF_EVEN explicitly tested (1.5->2 AND 2.5->2), clamping+non-default caps+frozen
model. 38 tests pass on independent rerun.

REVIEW FIX: to_exchange clamped negative internal_conviction to 0 in the trace field
(reused ConvictionLeverage ge=0); changed trace field to plain float (poison guard only)
so it records the ACTUAL input faithfully; dropped the clamp + unused import. Relocated
8 off-spec leverage-spike scratch files off repo root -> prod/VIOLET_dev/l3_spike/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 11:45:54 +02:00
Codex
a97bb90bf6 VIOLET V3.4: integrate full 5-factor VioletSizer into VioletDecisionEngine
Additive (non-breaking): decide(factors=None) keeps the V3a base-only path (existing
11 tests unchanged); decide(factors=SizingFactors(...)) produces BLUE-complete
conviction via VioletSizer (base_max=8 + dc/regime(ACB)/ob/esof, capped@9) with the
full factor breakdown on ShadowDecision (base_leverage/dc_lev_mult/regime_size_mult/
market_ob_mult/esof_size_mult, None on the base path). SizingFactors value object =
the live-plane inputs the launcher will source (V3.4b). 6 new tests incl. consistency
vs VioletSizer, STALKER cap, EsoF-stale haircut. 17 pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 23:35:00 +02:00
Codex
dc3d0970ad VIOLET V3.3 review: note that extreme MC gate covers boost>2.5/beta=0/mc=0
Reverted a redundant widening of the main MC gate (typical ranges) after confirming
test_gate_mc_extreme_multipliers already bit-identity-tests boost in [1,5], beta in
{0,0.2,0.8,1}, mc in {0,0.5,1}, and the OB agreement boundary (N=200k, exact !=).
Added a cross-reference note. All 6 gates green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:19:02 +02:00
Codex
d3431cd18a VIOLET V3.3: full sizing parity (orchestrator wrap-all) — reviewed + doctrine fixes
Build by dev agent (Crush); reviewed for compliance/flaws/doctrine. VERIFIED:
transcriptions verbatim vs BLUE (_strength_cubic/_update_regime_size_mult/OB/compose),
gates use exact != bit-identity (not approx), reference uses REAL kernels, no
shared-file edits. Bit-identity gate PASSES 0/1e6 mismatches; all 6 gates green;
173 non-gate pass. upstream replay r=0.937.

REVIEW FIXES (doctrinal adherence):
- Removed arbitrary magnitude caps (SizeMult/Boost le=64, Beta/McScale le=4) — a
  'no-hygiene-BLUE-lacks' liberty that could reject a valid extreme BLUE value;
  kept only V-TYPES poison guards (ge=0 + allow_inf_nan=False). 173 pass unchanged.
- Strengthened near-vacuous upstream gate (was r>0) -> r>=0.80 AND median_err<=3.0
  (observed 0.937/1.44). Now passes meaningfully.
- Relocated 3 untracked spike scripts off repo root -> prod/VIOLET_dev/sizing_spike/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:08:18 +02:00
Codex
5c90c8f351 VIOLET V3.2: EsoF size-modulation fold (BLUE SC haircut, exact)
modulation.py: VioletSizeModulation wraps BLUE's canonical esof_size_mult_from_score
+ esof_score_from_payload (exact ESOF_* constants), applies the SC haircut
step-for-step as _apply_sc_entry_size_multiplier (nautilus_event_trader.py:3307):
mult clamped [0,1] HAIRCUT-ONLY (:3316), near-1 no-op (:3318), round(lev*mult,6)/
round(notional*mult,12). 8 tests pass. Empirical mult-recovery on recorded BLUE:
median 1.000, EsoF haircut bands (0.65/0.8/0.9/0.3) visible. NOTE: 28% upward tail
(recorded>base) = NEXT parity step (base mid-range param OR gold/gauge up-mult);
EsoF is haircut-only by design. Not yet wired into decision_engine (needs EsoF HZ
score plane + restart, held).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 07:53:08 +02:00
Codex
a168d0bee5 VIOLET V3.1: BLUE stablecoin exclusion (parity fix)
Soak surfaced USDCUSDT being shorted. BLUE has a hardcoded exclusion gate around
its (muted-IRP) picking: _STABLECOIN_SYMBOLS removed from prices_dict pre-select
(nautilus_event_trader.py:24/3906). Replicate exactly: VioletDecisionEngine skips
the same 10 symbols in observe() so IRP never sees them. Following BLUE in all
regards (picking unchanged; this is BLUE's separate gate). Set-equality drift
guard vs BLUE source + never-selected test. 11 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 21:51:09 +02:00
Codex
6f5aa80ed0 VIOLET V3e: shadow-decision journal + DDL (reject-at-source)
22_violet_decisions.sql (dolphin_violet.violet_decisions, DDL-first) +
shadow_journal.py: VioletDecisionJournal validates each actuated ShadowDecision
via DecisionRow (V-TYPES, allow_inf_nan=False) before the CH sink -- malformed
dies at source, never at the spool head. NEVER an order. DecisionRow field set ==
DDL columns (asserted). 4 tests pass. Launcher wiring + DARK soak = operator step.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:29:53 +02:00
Codex
1a449074ae VIOLET V3d: base-sizer parity harness + gate vs recorded BLUE
parity_harness.py: median-curve parity of V3a VioletBetSizer vs recorded
dolphin.trade_events (vel_div->leverage), restricted to short-signal domain.
GATE PASSES on prod host: pearson 0.9998, max_abs_err 0.238 (budget 1.0) over
23 bins -> base conviction sizer reproduces BLUE's central tendency. Per-trade
scatter is the deferred SC/ACB/OB/gold modulation layer (separate finding doc).
3 unit + 1 gate green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 20:26:08 +02:00
Codex
1e331d80bc VIOLET V3c: VioletDecisionEngine — reactor-resident SHADOW engine
Composes V3a live-kernel wrappers + V3b cadence into a muted decision engine:
scans in -> ShadowDecision out, NO execution (distinct from PINK's disabled
dita DecisionEngine; runs alongside as pure shadow). Short-regime gate mirrors
BLUE/dita (vel_div<threshold + vol_ok + IRP survivor); cadence-gated actuation
(ENTRY Q=scan), evaluate-always. Verified non-vacuous: produces AAAUSDT SHORT,
conviction 9.0, exposure=capital x notional_fraction. 9 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:51:27 +02:00
Codex
77e3eddb40 VIOLET V3b: Cadence Control Plane — universal per-action quantization
Spec prod/docs/VIOLET_SPEC__CADENCE_CONTROL_PLANE.md + cadence.py: every
scan-governed action (entry/sizing/each exit reason/each input plane) carries an
INDEPENDENT, runtime-tunable Q knob surfaced in a control plane (HZ-backed,
code defaults as floor). Evaluate-every-tick / actuate-at-Q; SL defaults insta,
TP/entry default scan, OBF ~1s — all loosenable per-action. 9 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:39:12 +02:00
Codex
b295447bdf VIOLET V3a: V-TYPES alpha-kernel wrappers (selector/sizer/exit-v7) + parity/drift guards
L1 pure-alpha layer: wrap BLUE's live AlphaAssetSelector/AlphaBetSizer/
AlphaExitEngineV7 behind V-TYPES boundaries (wrap, not reimplement). max_leverage
is a required explicit param (live default 5.0 / blue_parity 8.0 / recorded 9.0);
smoke + tests confirm max_leverage=9.0 reproduces recorded sizing exactly
(notional_fraction 0.20x9=1.8 = recorded our_leverage max). 7 tests pass.
Exchange-agnostic: conviction leverage sizes quantity; exchange-lev map is L3.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 17:39:44 +02:00
Codex
fefb18626e VIOLET V2d: V2 exec gate PASSED on prod host + beartype ADOPT verdict
200 scenario cycles @100ms TTL with the V0 storm as concurrent background
load: deadline_jitter p99 8.47ms (<25, zero early fires), ttl_resolution
fire->CANCEL p99 5.89ms (<50) p50 0.08ms (<10), all terminals correct,
zero stuck orders/deadlines, capital never froze, run-to-run determinism
(identical outcomes_hash). V0 latency gate re-run: still PASSED.

beartype A/B (subprocess, import-time kill-switch): jitter p99 delta
+0.42ms, ttl p99 delta +0.025ms -> ADOPT (<1ms budget); 'typed' stays on
by default, DOLPHIN_VIOLET_BEARTYPE=0 escape hatch retained.

exec_harness: CLI runner (the A/B vehicle), is_capital_frozen() for the
accounting verdict, early-fires derived from negative jitter. Dedicated
non-gate test: full exec path vs ObserveOnlyVenue - inner venue untouched,
slot stays free. Violet suite 97 green; router 77 green; shared clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 00:48:38 +02:00
Codex
7ae49c587e VIOLET V2c: synthetic intent scripts + ExecStormHarness + 9-scenario matrix
synthetic_intents.py: seeded IntentScriptSpec -> CycleSpec scripts
(script_hash + outcomes_hash determinism, V0 discipline); per-scenario
router ExecConfig constructed directly (no env mutation); cycle executor
runs full ENTER->terminal->flatten lifecycles with per-scenario terminal
predicates and cycle-end invariants (working registry empty, driver
drained, slot flat).

exec_harness.py: composition root — production bundle (MOCK, injected
ScriptedVenue), ExecDeadlineDriver ports wired, pump = venue.reconcile()
-> kernel + driver.on_fill forwarding (the production seam), gate report
via the ExecGateReport schema, archive next to V0 reports.

scripted_venue.py amendment: MARKET orders never rest (venue realism —
directives are keyed by trade_id and the R1 MARKET fallback shares the
position's trade_id).

Matrix green through the REAL kernel at 100ms TTL: immediate fill,
rest-then-fill (deadline cancelled), fill-races-cancel (no retry),
rest-expire-retry (-r1 opens), retry-exhaust skip|market, exit-expire ->
MARKET same trade_id, post-only reject, cancel-reject (no strand). Two
runs same seed -> identical outcomes_hash. Router 77 green; shared clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 00:31:09 +02:00
Codex
dfe7136404 VIOLET V2b: ScriptedVenue + ExecDeadlineDriver (event-driven TTL @sub-second)
exec_driver.py lifts PINK's TTL-resolution logic (_exec_after_submit /
_handle_expired_working, live-proven) into a standalone driver with
injected ports (router/submit/pump/slot_view/venue_flat/ref-price) and
replaces the 1s polling sweep with one DeadlineScheduler deadline per
working order. The driver is the TIMING authority (router clamps TTLs
>=0.5s — its internal deadline is vestigial here); the router stays the
POLICY authority. R1 preserved verbatim: exit TTL -> MARKET escalation on
the SAME trade_id; post-only reject -> schedule_in(0) through the one
shared resolution path; venue-truth requote gate fails safe.

scripted_venue.py subclasses MockVenueAdapter (zero shared edits) with
per-trade directives: IMMEDIATE_FILL / REST_THEN_FILL / REST_THEN_EXPIRE /
POST_ONLY_REJECT / CANCEL_REJECT / FILL_RACES_CANCEL; deferred fills
release through reconcile() (the production pump seam), never the 50ms
subscribe() poll.

17 new tests incl. full-kernel CANCEL->venue mapping, fill-races-cancel,
bounded retry chains, on_fill deadline cancellation, fail-safe probe.
Router 77 green untouched; shared files clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 00:18:54 +02:00
Codex
ba01b914ce VIOLET V2a: V-TYPES domain layer + hypothesis properties + divergence reject-at-source
domain.py: refined scalar aliases (BarsHeld kills the bars_held=-106
UInt16 poison class by construction), DivergenceRow (DDL-shaped, frozen,
extra=forbid), ExecDriverSettings (env boundary for the V2 driver; ttl
override exists because the shared router clamps TTLs >= 0.5s),
ExecGateReport schema, beartype 'typed' decorator with
DOLPHIN_VIOLET_BEARTYPE=0 kill-switch.

divergence.py: rows now parse through DivergenceRow before the sink —
malformed rows die at the source with a rate-limited WARNING + counter,
never at the head of the CH spool.

Properties (hypothesis, derandomized): ExecutionRouter state machine
(fill/retry mutual exclusion via pop-semantics, R1 exit escalation same
trade_id, bounded retry chains, <=1 working ENTER), LatencyHistogram
percentile laws (member-of-samples, monotone, extremes), DivergenceRow
parse laws. 34 new tests; violet suite 64 green; router 77 green; zero
shared-file edits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 00:08:18 +02:00
Codex
970c33cb8e VIOLET V1c: observe-only launcher + hard guard + divergence monitor + service (DARK)
launch_dolphin_violet.py: own namespaces hard-set (CH dolphin_violet, HZ
DOLPHIN_STATE_VIOLET/PNL, Zinc prefix violet, DOLPHIN-VIOLET-001); own
credentials (BINGX_VIOLET_API_KEY/SECRET) — DARK idle with periodic WARNING
until provisioned; CH preflight SELECT-probes the required tables and NEVER
creates (DDL-before-code); kernel snapshot path repointed away from PINK's
fixed /tmp/.pink_kernel_state.json; mainnet hard-disabled; observe loop
never calls runtime.step(). ObserveOnlyVenue: submit/cancel raise
ObserveOnlyViolation with full attribute delegation — the kernel's
venue-submit-failure rollback converts a refusal into a synthetic REJECT
(slot back to IDLE), proven against the real kernel. FeedDivergenceMonitor:
per-asset scan-vs-venue divergence rows (bookTicker WS via
prod/bingx/market_stream, REST fallback) with stale-mid suppression and
plane seq propagation — the FET 0.2176-vs-0.1878 detector; runs even DARK
(public data). Supervisord [program:dolphin_violet] autostart=false, no
keys in conf by design. Violet package: 42 tests green + V0 gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 16:09:04 +02:00
Codex
d639a69307 VIOLET V1b: violet CH sinks + journal routing + namespace-isolation gate
ch_writer: _writer_violet (db=dolphin_violet) + ch_put_violet, the existing
per-strategy singleton pattern. bingx/journal: explicit violet entries in
_STRATEGY_DB_MAP/_SINK_MAP/_SINK_NAME_MAP — closes the hazard where the
unknown-strategy fallback routes venue journals into BLUE's dolphin DB
(regression-pinned in the new test, which documents the footgun for any
future strategy color). test_violet_namespace_isolation: violet sink
targets dolphin_violet exclusively; journal resolves violet explicitly;
Zinc region names for prefix violet disjoint from pink; persistence wired
with the violet sink routes only to dolphin_violet; HZ map-name contract
pinned for the C4 launcher. Shared-lib regression: 57/57 across PINK
kernel/runtime + BLUE tp_floor/malformed-open + bingx http safety.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 15:51:19 +02:00
Codex
4b52fff8dd VIOLET V0: reactor clock primitives + latency harness (gate PASSED)
clock.py: mono_ns single timebase; LatencyHistogram (raw reservoir, exact
nearest-rank percentiles); PlaneClock per-plane seq clocks with strict
staleness budgets (age==budget not stale — MHS FIX-9 lesson);
DeadlineScheduler — single-driver timer heap with EARLY-WAKE on
earlier-than-head insert (the jitter-budget mechanism), isolated callbacks.

harness.py: seeded deterministic event storms (sequence-hash asserted)
driving the REAL Rust ExecutionKernel via the MOCK bundle; reaction latency
measured producer-stamp→post-fold across the queue hop exactly as the
production account stream consumes; ACCOUNT_UPDATE wallet sentinel tracks
kernel k_capital so synthetic storms never trip capital_frozen; sustained
throughput reported alongside the gate.

V0 GATE (prod host, 50k events, 5k concurrent deadlines, burst 8/12ms ≈
667 ev/s offered): venue_event_reaction p99 7.19ms (<10ms budget),
deadline_jitter p99 4.86ms (<25ms), zero early fires. Capacity artifacts:
the 32/1ms and 16/12ms storms (archived reports) show intra-burst queueing
dominating beyond ~1.3k ev/s offered at ~0.33ms/fold. 17 unit tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 15:32:13 +02:00
Codex
84e4a50e3f repo hygiene: track the PINK launcher import closure
67 production .py modules that the running PINK service imports but which
were never committed: prod/bingx/ (HTTP client, market/user streams,
journal, config), prod/clean_arch/ adapters/persistence/runtime/dita/dita_v2
production modules and their co-located tests. Rule going forward: every
module imported by launch_dolphin_pink.py / pink_direct.py must appear in
git ls-files. Excludes _backup dirs, __pycache__, and non-code files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 15:09:32 +02:00
Codex
f4ff1cd9b7 blue_parity: hyphen-tolerant price_of (first add of module)
PinkAssetPicker/PinkAlphaSizer (BLUE-parity IRP picker + sizer, 2026-06-10)
plus the 2026-06-11 fix: price_of falls back to the dehyphenated symbol —
reconcile/fill paths write venue format (FET-USDT) into the slot while the
scan universe keys are Binance-style (FETUSDT); without the fallback an
adopted slot had no price and TP/SL/MAX_HOLD never evaluated (the unmanaged
FET position incident).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 15:03:19 +02:00
Codex
4929087f7a PINK kernel fix-of-fixes: slot-PnL repair plumbing
Review of PINK_ACCOUNTING_EXEC_FIX execution found the Phase 3.2 repair
path triply broken: (1) !closed guard blocked repair on terminal fills —
the common price-less case; (2) wrapper on_account_event was a raw FFI
passthrough so repairs never settled into published capital; (3) live
FILL_SETTLED carried no slot_id and realized_pnl=0 (pre-folded) — repair
was dead code. Fixes: repair allowed on closed slots (flag+dedup keep it
idempotent); wrapper settles the baseline diff on FILL_SETTLED-with-slot_id;
dedicated repair_realized_pnl field avoids double-folding the K-ledger;
_FakeKernelAccount fixture mirrors the Phase 1 anchor_to_exchange contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 14:58:30 +02:00
Codex
d280407327 PINK Phases 1-4: E-anchored capital, atomic snapshot, sizer feedback, kernel hardening
Phase 1: account.py anchor_to_exchange, capital_source provenance, settle
includes fees in capital delta.
Phase 2: atomic snapshot swap, CH provenance DDL (08_provenance.sql),
naive-UTC timestamps, ch_writer wait_for_async_insert=1 for all tables,
head-of-line stuck-row logging at WARNING per 100 attempts.
Phase 3: sizer feedback uses slot realized_pnl (not capital delta),
FILL_SETTLED repairs slot-level PnL for price-less exit legs.
Phase 4: resolve_slot returns Option<usize>, UNRESOLVED_SLOT diagnostic.
bars_held clamped to max(0, ...) at row-build time.
2026-06-11 21:44:24 +02:00
Codex
2c9da8f592 PINK Phase 0: FET -$5,990 fix batch — leverage-free PnL, true fill prices, reconcile baseline anchors
Defects fix (FET -$5,990 replay, 2026-06-11):
- realized_pnl() and mark_price(): PnL = qty × Δprice, side-signed; no ×leverage inflation (was 3× every leg).
- BingX MARKET fill events carry true fill price (avgPrice/lastFillPrice), never the order's nominal price (protective bound ±20-25% from mark, poisoned PnL to -$5,990 on a +$164 round-trip).
- Fill routing by ORDER IDENTITY first, FSM state second — late entry-remainder fills during EXIT_WORKING no longer misclassify as exits.
- Entry basis = VWAP across entry fills, not last fill price.
- reconcile_from_slots / restore_state: re-anchor _last_settled_pnl / _slot_was_closed to adopted slot state (cross-restart double-book of carried PnL).
- ACCOUNT_UPDATE with wallet_balance=0 dropped (margin-only frames no longer zero e_available_margin).
- Foreign-fill skip on shared VST account (PRODGREEN collision filter).
- exec_router TTL: entry-requote venue-truth gate (recent own fill + live exchange position probes prevent double-entry).
- bingx_direct: openOrders fetched BEFORE positions (sequential ordering prevents dangerous tear → double-entries).
- Dual-leverage translation via map_internal_conviction_to_exchange_leverage() (strategy conviction → integer at-exchange leverage, bankers rounding).
- BLUE-parity alpha components wired: asset picker (IRP universe ranking) + alpha sizer (cubic-convex dynamic leverage, 0.5-8.0 range).
- ch_writer: date_time_input_format=best_effort on insert URLs; flush error logging at WARNING with counter.
- blue_parity.price_of(): hyphen-tolerant fallback (FET-USDT → FETUSDT).
- Fill test updated to incremental filled_size semantics (BingX WS lastFilledQty).
- Env-override base URLs, supervisord autorestart, per-asset DC histories, single-slot invariant, fill-attribution filter.

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
2026-06-11 20:53:49 +02:00
Codex
9e210b5a02 PINK: fix fee calibration poisoning + fee_settled trade_id linkage
Two related accounting fixes:

1. _calibrate_fee_model startup guard: before calling calibrate_fee,
   compute raw deviation from the published taker/maker rate (ignoring
   any stale calibration_ratio). If >15%, skip and log WARNING rather
   than letting a bad REST fill set calibration_ratio to ~0.8 and cause
   ESTIMATED fees to understate actuals by 20% for the entire session.

2. fee_settled_events trade_id: BingX WS does not echo back our
   clientOrderId in fill events (field "c" is empty). Was falling back
   to BingX's internal orderId (p-e-mq5.../p-x-mq5...) which can't
   be joined to trade_events. Now reads trade_id from kernel slot 0
   (which retains the trade_id until the next ENTER) so
   fee_settled_events.trade_id = BTCUSDT-T-N. Added venue_order_id
   field to persist_fee_settled for bidirectional reconciliation.

128/128 tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 20:19:10 +02:00
Codex
c16b5aaaa4 PINK: fix trade_seq always-zero — unique trade_id per position
trade_seq was missing from the kernel account snapshot dict, so the
intent engine always computed trade_id = "BTCUSDT-T-000000000001".
The Rust FSM SLOT_BUSY guard only fires on *different* trade_ids; with
the same ID it resets the slot and submits a new exchange order on each
ENTER signal tick (~86 duplicate orders observed in one session).

Fix:
- Add _slot_was_closed dict to ExecutionKernel; set False on ENTER
  accepted (both sync/async), True on on_venue_event when slot.closed
- Increment account.snapshot.trade_seq on the IDLE→CLOSED transition
- Expose trade_seq in snapshot()["account"] so DecisionContext carries
  the correct counter → intent engine generates unique IDs per trade

451/451 tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 18:23:31 +02:00
Codex
62553424ab PINK: fix vel_div always-None in HazelcastDataFeed — normalize_ng7_scan hoists vel_div to top-level data dict but feed was reading scan (inner result sub-dict) which has no vel_div. Now checks data.get('vel_div') first (top-level), then scan, then recomputes from multi_window_results. PINK was receiving velocity_divergence=None on every snapshot and never entering trades even when BLUE (same threshold) did.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 15:18:53 +02:00
Codex
0eac51d2e9 PINK: docs v7 + reset_and_seed startup fix — 451/451 tests green
- SYSTEM_BIBLE.md → v7.0: documents fee-sign fix (Defect A), opening-fee
  fix (Defect B), WARN-unfreeze, orphan prevention, reset_and_seed startup,
  and vel_div env-override.
- CAPITAL_BOOKKEEPING_DESIGN.md: status updated to PHASE-1 BUGFIXES APPLIED;
  sections 8.1-8.4 (applied fixes + 34-test coverage) were already present.
- rust_backend.py: expose dita_kernel_reset_and_seed() via _RustKernelLib +
  ExecutionKernel.reset_and_seed(); zeros stale K-accumulators at startup so
  K=E=live_capital → delta=0 → capital_frozen=False on every clean restart.
- pink_direct.py: call kernel.reset_and_seed(live_capital) after
  _restore_kernel_snapshot() so BingX is always the ledger of record.
- launch_dolphin_pink.py: DOLPHIN_PINK_VEL_DIV_THRESHOLD env-var override
  for on-exchange debugging; BLUE unaffected.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 12:33:50 +02:00
Codex
e38ec77221 PINK: fix fee-sign bug + WARN-unfreeze — 451/451 tests green
Defect A (fee sign): bingx_user_stream._normalise_order flipped to
  fee = -raw_fee so BingX negative-n costs arrive as positive kernel
  costs.  k_maker_rebates no longer accumulates phantom rebates.

Defect B (opening fee dropped): fill_qty now falls back to "z"
  (cumFilledQty) when "l" (lastFilledQty) is zero/absent, so
  apply_predicted_fill computes a non-zero opening-leg fee.

Architectural fix (WARN unfreezes): lib.rs reconcile() now unfreezes
  capital_frozen on WARN as well as OK.  WARN (0.01-20 USDT delta) is
  normal in-flight settlement — only ERROR (≥20, unexplained) should
  halt ENTERs.  The old keep-state logic trapped the kernel permanently
  frozen after the first trade's ENTER predicted-fee phase pushed delta
  briefly into ERROR.

Acceptance criterion: |k_capital - bingx_balance| < 1 USDT, frozen=False
after every round-trip trade — verified numerically against T-1/T-2
ground truth from the CRITICAL doc.

Docs: CRITICAL_AGENT-TODO_ACCOUNTING_BUGFIX.md §12-13 (fix record),
      CAPITAL_BOOKKEEPING_DESIGN.md §8 (kernel spec), SYSTEM_BIBLE §11.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 11:08:31 +02:00
Codex
7e83a5c5c5 PINK: fix event-loop corruption in open_positions/open_orders + 23-test orphan suite
Root cause: open_positions()/open_orders() called _backend_snapshot() ->
_call_backend() -> _run() -> pool.submit(asyncio.run, coro) which spawned a
temporary event loop in a worker thread. httpx AsyncClient created inside that
temp loop, loop closed immediately. All subsequent HTTP calls raised Event loop
is closed or asyncio.locks.Event bound to different loop. Crash triggered WS
stream reconnects; each reconnect re-ran reconcile with N>1 BingX positions and
orphaned all but the largest.

Fix: open_positions()/open_orders() now read backend._state (populated by
await backend.connect() in the main loop). Fallback to _backend_snapshot()
for callers without a connected backend.

Fixes test_bingx_bugs::TestConnectNoDoubleRefresh: connect() is now async.

New test_orphan_prevention.py: 23 tests covering all 5 orphan mechanisms:
  A. open_positions/open_orders use backend._state, never hit thread pool
  B. connect() awaitable, backend.connect() runs in main event loop
  C. Reconcile guard: >1 position logs ERROR and takes only largest
  D. clientOrderId p-action-base36-rand4 on every order
  E. EXIT sizing capped to kernel slot_size

391 passed, 2 skipped, 0 failed across all 14 test files.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:53:41 +02:00
Codex
a3169b762d PINK: reconcile guard — refuse to silently drop orphan positions on restart
_reconcile_position_slot passed all N BingX positions (all slot_id=0) to
reconcile_from_slots; with N>1 the kernel silently took one and forgot the
rest. Now: sort by size desc, take only the largest, log ERROR naming every
ignored orphan symbol. Caller must flatten exchange to 0 before restarting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 18:12:07 +02:00
Codex
10a44d86b1 PINK: wire CH persistence to monitor + add missing friction DDL
- migrate_pink_sink_schema.sql: ALTER TABLE adds fee/fee_source/is_maker/
  slippage_bps/mark_at_submit/exchange_ts to trade_events and trade_exit_legs;
  CREATE TABLE fee_settled_events (was missing entirely). DDL already applied.
- monitor_pink.py: wire real PinkClickHousePersistence so monitor roundtrips
  write to dolphin_pink CH tables. Adds _make_decision_intent() helper; calls
  persist_step() after ENTER and EXIT. Persistence failure is non-fatal (warns
  and continues). 42 persistence tests green.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:10:49 +02:00
Codex
33d8e855c8 PINK: fix EXIT position not closing — 3 root causes, 368/368 tests green
Root cause 1 (http.py): duplicate signature= in POST body — canonical_query
included signature key after build_signed_params injected it, then body
appended &signature= again. Fix: exclude 'signature' from canonical.

Root cause 2 (bingx_direct + http.py): HTTP retry sent same MARKET order to
backup URL (bingx.pro), which hits the same VST account. Without clientOrderId,
each retry opened a new SHORT position; EXIT BUY 10 only closed one. Fix:
restore clientOrderId in hyphen format p-{e/x}-{base36_ts}-{rand4} (pure
alphanumeric rejected by VST; hyphen format accepted). Adds max_retries_override
+ urls_to_try to _request_json for non-idempotent override path.

Root cause 3 (flat_and_start_pink): k.venue.connect() ran backend.connect()
inside asyncio.run() in a thread-pool. httpx session created there references
a dead event loop; order POSTs raise RuntimeError("Event loop is closed").
Fix: await adapter.connect() directly from main event loop.

Also: enter_wall_ms + tight _is_our_position createTime filter to separate
PINK's position from concurrent strategies on shared VST account. 1.5s
settle sleep before flat check.

New test suite test_bingx_http_safety.py: 20 tests covering idempotency,
retry correctness, backup-URL dedup, event-loop hygiene, signing correctness.

Live result: ENTER 290ms, EXIT 260ms — both sub-second. Position flat.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 01:39:35 +02:00
Codex
535eea855d PINK: cancel_async, S2 task guard, 29 new regression tests — 346/346 green
Bug fixes:
  1. bingx_venue.py: add cancel_async() — async cancel that awaits backend.cancel()
     directly in the main event loop. The sync cancel() path goes through _run()
     → thread-pool → asyncio.run() in a new thread, but aiohttp is bound to the
     main loop → deadlock. Identical root cause as the old sync submit() → fixed
     via submit_async. Remove dead cancel_order branch (BingxDirectExecutionAdapter
     has cancel, not cancel_order).

  2. rust_backend.py: process_intent_async CANCEL path now uses cancel_async when
     available (matching the submit_async pattern for ENTER/EXIT). Sync cancel()
     fallback kept for MockVenueAdapter compat.

  3. bingx_direct.py: guard S2 background refresh task per symbol. Old code discarded
     the task reference; rapid submits piled up concurrent _refresh_state_background
     calls all writing self._state in arbitrary completion order (stale last-writer-
     wins). Now: skip creating a new task if one is already pending for the symbol;
     store reference and clear via done-callback.

Test additions (test_bingx_bugs.py, 29 tests):
  - cancel_async: awaitable, calls backend.cancel directly, maps all statuses
  - process_intent_async CANCEL: dispatches cancel_async / falls back to sync
  - S2 guard: task stored, no duplicates while pending, new task after done
  - _events_from_submit with None snapshots: FILLED/NEW/REJECTED/PARTIAL/RATE_LIMITED
  - _filled_size_from_snapshots(None, None): safe 0.0 return
  - _events_from_cancel: before/after completely ignored
  - connect(): no double refresh_state, no-op if backend has no connect
  - submit() sync with None snapshots: FULL_FILL still emitted
  - cancel() branch audit: uses cancel not cancel_order, raises for no-cancel backend

Fix: test_exchange_event_seam_parity.py TestMockSubscribe — replace deprecated
asyncio.get_event_loop().run_until_complete() with asyncio.run() (Python 3.12
raises RuntimeError when event loop is closed by earlier suite tests).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 16:02:13 +02:00
Codex
f2596e1155 PINK: S3 dead-snapshot removal — connect/cancel/submit overhead cuts
Fix 1: connect() — remove redundant _backend_snapshot(include_history=True).
backend.connect() already called refresh_state(); this was a second identical
network round-trip at startup (~400ms wasted).

Fix 2: cancel() — remove snapshot_before + snapshot_after. _events_from_cancel
never reads 'before' or 'after' — two gratuitous round-trips per cancel with
zero benefit.

Fix 3: submit() (sync/legacy path) — drop both _backend_snapshot calls, pass
None like submit_async already does. Receipt executedQty fields take precedence;
_filled_size_from_snapshots returning 0.0 is the correct safe fallback.

117/117 tests pass (2 pre-existing pytest-ordering failures in TestMockSubscribe
are unrelated — asyncio.get_event_loop() contamination from other suite files,
25/25 pass when the file runs alone).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 14:07:45 +02:00
Codex
c864e9c550 PINK: S1 leverage cache, S2 background refresh, Gap 1/2/3 fee+slippage logging
S1 — Leverage cache (bingx_direct.py):
  _ensure_leverage(): per-symbol asyncio.Lock + cached value check; skips ~350ms
  POST when exchange already has the requested leverage.  Saves ~350ms/trade.
  Cache updated ONLY on success; failed POST leaves cache stale → correct retry.
  Persist: JSON sidecar /tmp/.bingx_leverage_cache_{env}.json; survives restarts.
  connect(): _verify_leverage_drift() detects when another process changed leverage
  at the exchange and updates cache to exchange truth (logs WARNING on drift).
  Multi-runner contract: leverage is account-level on BingX; documented that
  concurrent runners with different leverage desires for same symbol conflict.
  20 mock tests: same-lev skip, change-triggers-POST, failure-no-cache-update,
  concurrent-same-symbol (lock prevents race), drift-detect, persist/restore,
  multi-runner known-limitation documentation test.

S2 — Background state refresh (bingx_direct.py):
  MARKET fills: asyncio.create_task(_refresh_state_background) — does not block
  submit path.  WS FILL_SETTLED + ACCOUNT_UPDATE deliver capital truth anyway.
  LIMIT fills: synchronous refresh retained (include_history=False, not True) —
  needed to detect resting order state for next pump cycle.
  Saves ~600–900ms/trade on MARKET exits. ENTER similarly improved.

Gap 1 — VenueEvent friction fields (contracts.py):
  Added: fee, fee_asset, fee_source, is_maker, exchange_ts, slippage_bps,
  mark_at_submit — all with defaults so existing callers are unaffected.
  Detailed inline docs for sign conventions and provenance codes.

Gap 2 — Fee estimation + WS_SETTLED provenance (bingx_direct.py, pink_clickhouse.py):
  submit_intent: estimates fee from fill_price × fill_qty × taker/maker rate;
  annotates ack_row with _fee_estimated, _fee_source, _is_maker_est.
  persist_fee_settled(): new method writes fee_settled_events row when WS
  ORDER_TRADE_UPDATE delivers actual commission ("n" field); fee_source="WS_SETTLED".
  pink_direct._run_account_stream: calls persist_fee_settled on FILL_SETTLED.

Gap 3 — Slippage measurement (bingx_direct.py, bingx_venue.py, pink_clickhouse.py):
  Captures mark_at_submit before the order POST; computes slippage_bps signed
  by side: positive = adverse (taker overpaid / maker undersold), negative =
  price improvement.  Measured for BOTH taker and maker fills for symmetry.
  Flows through VenueEvent → trade_events.slippage_bps + trade_exit_legs.slippage_bps.

S3 / SOR — Maker order placement: comprehensive TODO block in submit_intent with:
  SHORT/LONG-aware price offset design, OBF integration requirements,
  TODO_ADD_PARAMSET_VIBRISS for spread_bps threshold, intelligent timeout_s
  calibration requirements, price-impact awareness gap, SOR abstraction CRITICAL TODO.
  REST/WS split: documented why BingX (and all retail venues) separate these
  and why a unified VenueAdapter protocol is the long-term solution.

151/151 existing tests green + 20 new leverage cache tests = 171 total.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 12:25:12 +02:00
Codex
714913bab6 PINK: flat_and_start_pink — persistence check + asset-scoped flatness test
Startup roundtrip now verifies persistence accounting inline:
  - Wires PinkClickHousePersistence to a mock sink after EXIT
  - Checks trade_events: exit_price != entry_price, pnl finite, capital finite
  - Checks trade_exit_legs: pnl_leg, exit_qty, exit_price all populated
  - Logs ALL FIELDS CORRECT / ISSUES DETECTED

Flatness check scoped to PINK's traded asset only (other strategies
may have open positions; those are reported informational, not failure).
Adds _normalize_asset() for TRX-USDT → TRXUSDT symbol normalization.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 09:38:59 +02:00