Files
sentiment-engine/MALKHUT/EXT_CHANGES.md

5.2 KiB

EXT_CHANGES — external additions to MALKHUT (Fable, 2026-07-11/12)

For mm_: exact record of what I added to your codebase, where, and why — so you can reshape, absorb, or replace it with full knowledge. Operator directive 2026-07-11: "We shall use MALKHUT's asset directory-methods to comprise our UV-VIOLET known-asset-universe directory... MALKHUT's asset store as our overall system asset store." Everything below is additive — none of your existing files were edited.

Why this exists (the incident that forced it)

UV-PRIME's 2026-07-10/11 testnet audit found 25 phantom journal rows. Root cause chain included: the asset picker has no concept of what the execution venue actually trades — 15 of the 50 feed symbols are offline/delisted on BingX VST (BAND, CELR, COS, CVC, DENT, FUN, HOT, ICX, TFUEL, TUSD, USDC, WAN, WIN, XTZ, ZIL), and orders for them died at the venue while the kernel believed they filled. The fix needed a per-exchange listing-status store. Your asset store was the designated home.

What was added

1. malkhut/assets/ — new subpackage (the operational directory)

File What it is
malkhut/assets/__init__.py re-exports
malkhut/assets/directory.py AssetDirectory — JSON-backed universe store
malkhut/assets/asset_directory.json the live data file (versioned in git)
malkhut/assets/compiled_profiles.json full taxonomy dumps from YOUR AssetCompiler
malkhut/tests/test_asset_directory.py 8 tests (round-trip, normalization, status filter, idempotence)

Core concepts in directory.py:

  • normalize_symbol() — canonical form: "BAND-USDT"/"band_usdt" → "BANDUSDT".
  • KNOWN_EXCHANGES — the aux "known exchanges" table (operator-specified): BINANCE, BINGX, BINGX_VST. VST is deliberately a separate venue — its universe differs from BingX live (observed).
  • ExchangeListing — per-venue: venue_symbol (venue-local spelling), status (TRADING/OFFLINE/UNKNOWN), last_checked, source.
  • AssetRecord — canonical symbol, base/quote, exchanges: dict, profile_ref (key into YOUR ASSET_PROFILES when taxonomy exists), notes.
  • AssetDirectory — load/save (atomic tmp-rename), upsert, set_listing (rejects unknown exchanges), import_symbols (bulk, idempotent), symbols_for_exchange(exchange, status=TRADING), venue_symbol().
  • Storage: JSON file at $MALKHUT_ASSET_DIR_PATH or package-local default. Chosen because listing STATUS is runtime-mutable truth (probed from venue APIs) — it can't live in frozen in-code dataclasses, and JSON keeps it language-agnostic / GraalVM-safe / no DB dependency.

2. Data seeded into it

  • 50 canonical symbols = the full DOLPHIN NG7 scan-feed universe (what BLUE actually picks from), imported as BINANCE listings, source dolphin_ng7_scan_feed.
  • BINGX_VST statuses from a live /openApi/swap/v2/quote/contracts probe: 35 TRADING / 15 OFFLINE (list above). Tradable = status==1 AND apiStateOpen=="true"; absent-from-contracts = OFFLINE.
  • Full taxonomy for all 50 compiled via your AssetCompiler (compile_and_register, rate-limited, 0 failures) → persisted to compiled_profiles.json (your registries are in-memory only; this file is the durable copy). Each record's profile_ref set on success.

3. Consumers OUTSIDE MALKHUT (so you know who depends on what)

  • prod/clean_arch/violet/uv/uv_asset_universe.py — the UV bridge: seeds from scans, refreshes VST listings, and exposes init_asset_universe(execution_exchange) → frozenset of tradable canonical symbols. This is the single UV→MALKHUT contract: AssetDirectory().symbols_for_exchange(exchange). Change that API and ping Fable; everything else is yours to reshape.
  • The UV-PRIME runner loads it at boot (UV_EXEC_EXCHANGE, default BINGX_VST) and its promotion bridge suppresses ENTERs for assets outside the set (asset_not_on_venue). Live since flight-2 (2026-07-11 21:57:43 UTC), log line: UV UNIVERSE: 35 tradable assets.

Relationship to YOUR generalization (d2d0c5e)

You added ExchangeProfile + AssetProfile.exchanges + cross-exchange queries — the taxonomy layer (rich, in-code, semi-static). This directory is the operational layer (thin, probed, runtime-mutable). They compose; acknowledged on the bus (#5d2fdaee). Proposed seam, yours to own if you want it: onboarding populates AssetProfile.exchanges from the directory's TRADING statuses (directory → profiles, one-way).

What I did NOT touch

No edits to asset_classification.py, asset_behavior.py, asset_compiler.py, state.py, engine/planner/risk/venue/ipc/clock/ training code, or any test of yours. README.md untouched by me (its working-tree modification predates this work). All 1156 of your tests were green before and after (verified via the compiler run + directory suite).

Commits (branch tools/pi_wake_agent, /mnt repo)

  • a0625076 — directory + tests + seed + UV bridge
  • 3efb8749 — compiled_profiles.json + profile_ref links (onboarding 50/50)

North-star context (operator, in POST_FINISH.md): future selection from the entire ~500-asset Binance universe — the directory is built to that scale; today's 50 is the feed's limit, not the store's.

— Fable