mirror of
https://github.com/himself65/finance-skills.git
synced 2026-09-14 14:40:40 +08:00
8072082fda
* feat: add hyperliquid-reader skill + opencli plugin Add a read-only Hyperliquid (app.hyperliquid.xyz) reader, mirroring the tradingview-reader pattern. Unlike TradingView's CDP attach, Hyperliquid exposes a fully public info API (POST https://api.hyperliquid.xyz/info), so this needs no API key, wallet, login, or desktop app. opencli plugin (opencli-plugins/hyperliquid/) — 12 read-only commands: - Market data: markets, spot-markets, mids, book, candles, funding-history, funding-compare (cross-venue HL/Binance/Bybit funding-arb screen). - Account by 0x address: account, positions, spot-balances, open-orders, fills. Skill (plugins/data-providers/skills/hyperliquid-reader/) — SKILL.md (5-step + error reference), README.md, references/commands.md. Registrations: root opencli-plugin.json, data-providers plugin.json (description + keywords), marketplace.json, root README table. Verification: 31 unit tests pass against captured live wire shapes; all 12 command data paths smoke-tested end-to-end against the live API. SKILL.md description is 1019 chars (< 1024 lint cap), no angle brackets. No trade execution: the plugin exposes no write/exchange endpoints. 🤖 Generated with [Claude Code](https://claude.com/claude-code) * refactor: scope hyperliquid-reader to market data only Drop the account-by-address commands (account, positions, spot-balances, open-orders, fills) and their lib/account.js + lib/orders.js helpers and tests. Remove the now-unused normalizeAddress helper from lib/api.js. The reader is now market-data only: markets, spot-markets, mids, book, candles, funding-history, funding-compare (7 commands). Updated SKILL.md, both READMEs, references/commands.md, the plugin/marketplace manifests, and the root README table accordingly. 21 unit tests pass; all 7 market-data data paths re-verified live.
39 lines
1.3 KiB
JavaScript
39 lines
1.3 KiB
JavaScript
/**
|
|
* Order-book normalizer for `l2Book`.
|
|
* Payload: { coin, time, levels: [ bids[], asks[] ] }, each level { px, sz, n }.
|
|
*/
|
|
|
|
import { num } from './api.js';
|
|
|
|
/**
|
|
* Flatten an l2 snapshot into rows capped at `depth` levels per side.
|
|
* Bids first (best/highest px first as returned), then asks.
|
|
*/
|
|
export function normalizeBook(payload, depth) {
|
|
const levels = payload?.levels ?? [[], []];
|
|
const d = Number.isFinite(Number(depth)) && Number(depth) > 0 ? Number(depth) : 10;
|
|
const mk = (side) => (lvl, i) => ({
|
|
side,
|
|
level: i + 1,
|
|
px: num(lvl.px),
|
|
sz: num(lvl.sz),
|
|
orders: lvl.n ?? null,
|
|
});
|
|
const bids = (levels[0] ?? []).slice(0, d).map(mk('bid'));
|
|
const asks = (levels[1] ?? []).slice(0, d).map(mk('ask'));
|
|
return [...bids, ...asks];
|
|
}
|
|
|
|
/** Best-bid/ask spread summary from a raw l2 payload (for skill prose). */
|
|
export function bookSpread(payload) {
|
|
const levels = payload?.levels ?? [[], []];
|
|
const bestBid = num(levels[0]?.[0]?.px);
|
|
const bestAsk = num(levels[1]?.[0]?.px);
|
|
if (bestBid == null || bestAsk == null) {
|
|
return { bestBid, bestAsk, mid: null, spread: null, spreadBps: null };
|
|
}
|
|
const mid = (bestBid + bestAsk) / 2;
|
|
const spread = bestAsk - bestBid;
|
|
return { bestBid, bestAsk, mid, spread, spreadBps: mid ? (spread / mid) * 10000 : null };
|
|
}
|