feat(gold): Gold Intelligence v2 — positioning depth, returns, drivers (#3034)

* feat(gold): richer Gold Intelligence panel with positioning, returns, drivers

* fix(gold): restore leveragedFunds fields and derive P/S netPct in legacy fallback

Review catch on PR #3034:

1. seed-cot.mjs stopped emitting leveragedFundsLong/Short during the v2
   refactor, which would zero out the Leveraged Funds bars in the existing
   CotPositioningPanel on the next seed run. Re-read lev_money_* from the
   TFF rows and keep the fields on the output (commodity rows don't have
   this breakdown, stay at 0).
2. get-gold-intelligence legacy fallback hardcoded producerSwap.netPct to 0,
   meaning a pre-v2 COT snapshot rendered a neutral 0% Producer/Swap bar
   on deploy until seed-cot reran. Derive netPct from dealerLong/dealerShort
   (same formula as the v2 seeder). OI share stays 0 because open_interest
   wasn't captured pre-migration; clearly documented now.

Tests: added two regression guards (leveragedFunds preserved for TFF,
commodity rows emit 0 for those fields).

* fix(gold): make enrichment layer monitored and honest about freshness

Review catch on PR #3034:

- seed-commodity-quotes now writes seed-meta:market:gold-extended via
  writeExtraKeyWithMeta on every successful run. Partial / failed fetches
  skip BOTH the data write and the meta bump, so health correctly reports
  STALE_SEED instead of masking a broken Yahoo fetch with a green check.
- Require both gold (core) AND at least one driver/silver before writing,
  so a half-successful run doesn't overwrite healthy prior data with a
  degraded payload.
- Handler no longer stamps updatedAt with new Date() when the enrichment
  key is missing. Emits empty string so the panel's freshness indicator
  shows "Updated —" with a dim dot, matching reality — enrichment is
  missing, not fresh.
- Health: goldExtended entry in STANDALONE_KEYS + SEED_META (maxStaleMin
  30, matching commodity quotes), and seed-health.js advertises the
  domain so upstream monitors pick it up.

The panel already gates session/returns/drivers sections on presence, so
legacy panels without the enrichment layer stay fully functional.
This commit is contained in:
Elie Habib
2026-04-12 22:53:32 +04:00
committed by GitHub
parent d19b32708c
commit ee66b6b5c2
12 changed files with 966 additions and 158 deletions

View File

@@ -1939,6 +1939,16 @@ components:
type: string
unavailable:
type: boolean
session:
$ref: '#/components/schemas/GoldSessionRange'
returns:
$ref: '#/components/schemas/GoldReturns'
range52w:
$ref: '#/components/schemas/GoldRange52w'
drivers:
type: array
items:
$ref: '#/components/schemas/GoldDriver'
GoldCrossCurrencyPrice:
type: object
properties:
@@ -1954,18 +1964,85 @@ components:
properties:
reportDate:
type: string
managedMoneyLong:
type: number
format: double
managedMoneyShort:
type: number
format: double
nextReleaseDate:
type: string
openInterest:
type: string
format: int64
managedMoney:
$ref: '#/components/schemas/GoldCotCategory'
producerSwap:
$ref: '#/components/schemas/GoldCotCategory'
GoldCotCategory:
type: object
properties:
longPositions:
type: string
format: int64
shortPositions:
type: string
format: int64
netPct:
type: number
format: double
dealerLong:
oiSharePct:
type: number
format: double
dealerShort:
wowNetDelta:
type: string
format: int64
GoldSessionRange:
type: object
properties:
dayHigh:
type: number
format: double
dayLow:
type: number
format: double
prevClose:
type: number
format: double
GoldReturns:
type: object
properties:
w1:
type: number
format: double
m1:
type: number
format: double
ytd:
type: number
format: double
y1:
type: number
format: double
GoldRange52w:
type: object
properties:
hi:
type: number
format: double
lo:
type: number
format: double
positionPct:
type: number
format: double
GoldDriver:
type: object
properties:
symbol:
type: string
label:
type: string
value:
type: number
format: double
changePct:
type: number
format: double
correlation30d:
type: number
format: double