Skip to content

feat(positioning): standalone 24/7 Positioning panel with directional gauges#3141

Merged
koala73 merged 3 commits into
mainfrom
feat/positioning-247-panel
Apr 17, 2026
Merged

feat(positioning): standalone 24/7 Positioning panel with directional gauges#3141
koala73 merged 3 commits into
mainfrom
feat/positioning-247-panel

Conversation

@koala73

@koala73 koala73 commented Apr 17, 2026

Copy link
Copy Markdown
Owner

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:

  • SVG arc gauges (0-100) with directional color encoding: green = bullish/longs crowded, red = bearish/shorts crowded
  • Grouped by asset class: Commodities (Gold, WTI, Brent, Silver, Platinum, PAXG), Crypto (BTC, ETH, SOL), FX (EUR, JPY)
  • Visual elevation: scores >40 get a glow + border tint matching the direction color
  • Click-through: commodity/crypto cards navigate to the relevant existing panel
  • Always show all assets: quiet ones stay muted, hot ones pop visually
  • Mobile responsive: 2-column grid below 600px

Removes the "Perp Flow" tab, fetchHyperliquidFlow(), _renderFlow(), _renderFlowGrid(), and associated state from CommoditiesPanel.

No backend changes. Reuses existing getHyperliquidFlow RPC and market:hyperliquid-flow:v1 Redis key.

Design brief: docs/plans/redesign-247-positioning-panel.md

Test plan

  • tsc --noEmit clean (0 errors)
  • node --test tests/hyperliquid-flow-seed.test.mjs (37/37 pass)
  • Post-deploy: verify panel renders with gauge visualization
  • Post-deploy: verify CommoditiesPanel no longer shows "Perp Flow" tab
  • Post-deploy: verify click-through navigates to crypto/commodities panels

Post-deploy monitoring

  • Verify panel appears in CMD+K as "24/7 Positioning"
  • Verify 5min refresh cycle works (check console for [PositioningPanel] RPC failed errors)
  • Scores will cluster at 5-11 in current production (formula calibration is a follow-up per the design brief)

Known follow-up

… 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.
@vercel

vercel Bot commented Apr 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldmonitor Ready Ready Preview, Comment Apr 17, 2026 4:36am

Request Review

@greptile-apps

greptile-apps Bot commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR extracts Hyperliquid perp positioning from the buried "Perp Flow" tab in CommoditiesPanel into a standalone PositioningPanel with SVG arc gauges, proper asset-class grouping (Commodities, Crypto, FX), visual elevation for elevated scores, and click-through navigation. The removal of fetchHyperliquidFlow(), _renderFlow(), and associated state from CommoditiesPanel is clean and complete.

  • The new oiDelta1h function in PositioningPanel.ts uses a 1-sample lookback (sparkOi[length - 2]) instead of the 12-sample lookback in the original code (sparkOi[length - 13], documented as "12 samples back = 1h at 5min cadence"), so the metric displayed as "OI delta 1h" is actually showing a ~5-minute OI change.

Confidence Score: 4/5

Safe 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

Filename Overview
src/components/PositioningPanel.ts New standalone panel with SVG arc gauges and asset-class grouping; oiDelta1h uses 1-sample lookback instead of 12 (displays 5-min change as "OI delta 1h")
src/components/MarketPanel.ts Correctly removes fetchHyperliquidFlow, _renderFlow, _renderFlowGrid, and the 'flow' tab; leaves behind now-orphaned exported helpers (mapHyperliquidFlowSeed, mapHyperliquidFlowResponse, etc.) that are no longer called anywhere
src/App.ts Correctly wires positioning-247 panel into primeVisiblePanelData and refresh schedule using REFRESH_INTERVALS.hyperliquidFlow; no stale references to old fetchHyperliquidFlow
src/config/panels.ts Adds positioning-247 panel config to FULL, FINANCE, and COMMODITY variants with appropriate priority and display name
src/locales/en.json Adds positioning247 locale keys (title, infoTooltip, section headers, warmup/unavailable/footer strings); all strings look complete and accurate
src/styles/main.css Adds pos-* CSS classes for the new panel; responsive 2-column grid below 600px; uses color-mix() for glow/border effects

Sequence Diagram

sequenceDiagram
    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()
Loading

Comments Outside Diff (1)

  1. src/components/MarketPanel.ts, line 365-436 (link)

    P2 Dead code left from CommoditiesPanel flow removal

    mapHyperliquidFlowSeed, mapHyperliquidFlowResponse, oiDelta1h (line 365), HyperliquidAssetView, and HyperliquidFlowView are now unreachable — they were only called by CommoditiesPanel.fetchHyperliquidFlow() and _renderFlowGrid(), both of which were removed. The exported functions also keep unused imports (GetHyperliquidFlowResponse, HyperliquidAssetFlow) in scope. These should be removed to avoid confusing future contributors.

Reviews (1): Last reviewed commit: "feat(positioning): standalone 24/7 Posit..." | Re-trigger Greptile

Comment on lines +40 to +46
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;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Suggested change
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).
@koala73
koala73 merged commit 94571b5 into main Apr 17, 2026
10 checks passed
@koala73
koala73 deleted the feat/positioning-247-panel branch April 17, 2026 04:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(positioning): standalone 24/7 Positioning panel with directional gauges

1 participant