feat(positioning): standalone 24/7 Positioning panel with directional gauges#3141
Conversation
… gauges Extract Hyperliquid perp positioning from the "Perp Flow" tab inside CommoditiesPanel into a dedicated "24/7 Positioning" panel (#3140). New panel features: - SVG arc gauge (0-100) per asset with color encoding: green = bullish/longs crowded, red = bearish/shorts crowded - Grouped by asset class: Commodities, Crypto, FX - Visual elevation (glow + border) for scores above 40 - Click-through to relevant panels (crypto, commodities) - Hover tooltips with score, funding rate, OI delta - Warmup banner, stale badges, unavailable state - Compact secondary metrics (funding rate + OI delta 1h) - Mobile responsive (2-column grid below 600px) Registration: panel-layout.ts, App.ts (primeTask + 5min refresh), commands.ts (CMD+K), panels.ts (all variants), index.ts export, en.json i18n keys. Removals from CommoditiesPanel: - "Perp Flow" tab and its tab type - fetchHyperliquidFlow() method - _renderFlow() and _renderFlowGrid() methods - Associated state (_flow, _flowLoading) - App.ts primeTask and refreshScheduler entries No backend changes. Reuses existing getHyperliquidFlow RPC and market:hyperliquid-flow:v1 Redis key.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR extracts Hyperliquid perp positioning from the buried "Perp Flow" tab in
Confidence Score: 4/5Safe to merge after fixing the oiDelta1h lookback window; all other changes are clean and well-structured One P1 data-correctness bug: the new oiDelta1h function computes a 5-minute OI change while every label says "OI delta 1h" — a direct behavioral regression from the original 12-sample lookback. A one-line fix resolves it. The P2 dead-code issue in MarketPanel.ts is cleanup only and does not block merge. src/components/PositioningPanel.ts (oiDelta1h lookback), src/components/MarketPanel.ts (orphaned flow helpers) Important Files Changed
Sequence DiagramsequenceDiagram
participant App as App.ts
participant PP as PositioningPanel
participant BS as Bootstrap Cache
participant RPC as MarketServiceClient
App->>PP: primeVisiblePanelData() → fetchData()
PP->>BS: getHydratedData('hyperliquidFlow')
BS-->>PP: hydrated seed (or undefined)
PP->>PP: mapResponse(assets, fromSeed=true) → _render()
PP->>RPC: client.getHyperliquidFlow({})
RPC-->>PP: { assets[], warmup, unavailable }
PP->>PP: mapResponse(assets, fromSeed=false) → _render()
Note over App,PP: Every 5 min (REFRESH_INTERVALS.hyperliquidFlow) if panel is near viewport
App->>PP: fetchData()
PP->>RPC: client.getHyperliquidFlow({})
RPC-->>PP: updated snapshot → _render()
|
| function oiDelta1h(sparkOi: number[] | undefined): number | null { | ||
| if (!Array.isArray(sparkOi) || sparkOi.length < 2) return null; | ||
| const last = sparkOi[sparkOi.length - 1]!; | ||
| const lookback = sparkOi[sparkOi.length - 2]!; | ||
| if (lookback <= 0 || !Number.isFinite(last)) return null; | ||
| return (last - lookback) / lookback; | ||
| } |
There was a problem hiding this comment.
oiDelta1h computes a 5-minute delta, not a 1-hour delta
The new function looks back only 1 sample (sparkOi.length - 2), but the original in MarketPanel.ts looks back 12 samples and explicitly documents this as "12 samples back = 1h at 5min cadence" (sparkOi[sparkOi.length - 13]). Every display path — the card tooltip ("OI delta ${oiStr}"), the title attribute, and the field name oiDelta1h — labels this as an hourly metric, so users will see a 5-minute OI change presented as an hour's worth of data.
| function oiDelta1h(sparkOi: number[] | undefined): number | null { | |
| if (!Array.isArray(sparkOi) || sparkOi.length < 2) return null; | |
| const last = sparkOi[sparkOi.length - 1]!; | |
| const lookback = sparkOi[sparkOi.length - 2]!; | |
| if (lookback <= 0 || !Number.isFinite(last)) return null; | |
| return (last - lookback) / lookback; | |
| } | |
| function oiDelta1h(sparkOi: number[] | undefined): number | null { | |
| if (!Array.isArray(sparkOi) || sparkOi.length < 13) return null; | |
| const last = sparkOi[sparkOi.length - 1]!; | |
| const lookback = sparkOi[sparkOi.length - 13]!; // 12 samples = 1h at 5min cadence | |
| if (lookback <= 0 || !Number.isFinite(last)) return null; | |
| return (last - lookback) / lookback; | |
| } |
…te (review) P1 #1: oiDelta1h used 2-point lookback (5min delta mislabeled as 1h). Restored to 13-point lookback matching the original MarketPanel.ts implementation (12 samples back = 1h at 5min cadence). P1 #2: CLICK_TARGETS used display-style names (GOLD, WTI, BRENT) but seeded symbols are xyz:-prefixed Hyperliquid names (xyz:GOLD, xyz:CL, xyz:BRENTOIL). Fixed to match actual seeder output from seed-hyperliquid-flow.mjs. P2: Empty/unavailable snapshots (cold seed, fresh deploy) now show warmup guidance instead of a hard error. The prior Perp Flow tab intentionally treated this as normal bootstrap. The new panel regressed to showError which is confusing on first deploy.
…s (review)
P1: Click-through used getElementById('panel-X') but panels mount with
data-panel="X" attribute. Changed to querySelector('[data-panel="X"]').
P2: Crypto cards (BTC/ETH/SOL) target the 'crypto' panel which doesn't
exist in the commodity variant. resolveClickTarget() now checks if the
target panel is actually in the DOM before adding clickable styling and
data-pos-navigate. Cards whose target panel is absent render as
non-clickable (no cursor pointer, no navigate attribute).
Summary
Closes #3140
Extract Hyperliquid perp positioning from the "Perp Flow" tab (buried inside Metals & Materials panel) into a dedicated 24/7 Positioning panel with:
Removes the "Perp Flow" tab,
fetchHyperliquidFlow(),_renderFlow(),_renderFlowGrid(), and associated state from CommoditiesPanel.No backend changes. Reuses existing
getHyperliquidFlowRPC andmarket:hyperliquid-flow:v1Redis key.Design brief:
docs/plans/redesign-247-positioning-panel.mdTest plan
tsc --noEmitclean (0 errors)node --test tests/hyperliquid-flow-seed.test.mjs(37/37 pass)Post-deploy monitoring
[PositioningPanel] RPC failederrors)Known follow-up