103 lines
5.2 KiB
Markdown
103 lines
5.2 KiB
Markdown
|
|
# 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
|