From c73e097ce671e6ef06078f8f9e6d655696811d4d Mon Sep 17 00:00:00 2001 From: 0xFantomMenace <93462858+0xFantomMenace@users.noreply.github.com> Date: Tue, 29 Sep 2026 04:23:29 +0000 Subject: [PATCH] Core Hyperliquid breadth on client.hyperliquid.breadth --- CHANGELOG.md | 2 ++ README.md | 1 + src/exchanges.ts | 4 ++++ tests/core-breadth.test.ts | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 46 insertions(+) create mode 100644 tests/core-breadth.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c6b11a..3938a0f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ semver in spirit. ## 1.12.0 (2026-09-28) ### Added +- `client.hyperliquid.breadth` (`current()`, `history()`): core Hyperliquid breadth above the + UTC-session VWAP, the same shape as HIP-3 breadth. - Lighter on Robinhood Chain, the second deployment of Lighter, as `client.rhLighter` (`/v1/rh-lighter`). It has the same resources as `client.lighter` except the L3 order book: `instruments`, `orderbook`, diff --git a/README.md b/README.md index 1c156cd..83e6031 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,7 @@ snapshots; `interval` uses the last snapshot in each bucket. ```typescript const breadthNow = await client.hyperliquid.hip3.breadth.current(); +const coreBreadthNow = await client.hyperliquid.breadth.current(); // core perps, same shape const breadthHistory = await client.hyperliquid.hip3.breadth.history({ start: Date.parse('2026-08-28T00:00:00Z'), end: Date.now(), diff --git a/src/exchanges.ts b/src/exchanges.ts index d3fa1ac..97bb88b 100644 --- a/src/exchanges.ts +++ b/src/exchanges.ts @@ -145,6 +145,9 @@ export class HyperliquidClient { private http: HttpClient; + /** Aggregate market breadth above the current UTC-session VWAP (core Hyperliquid perps). */ + public readonly breadth: Hip3BreadthResource; + constructor(http: HttpClient) { this.http = http; const basePath = '/v1/hyperliquid'; @@ -161,6 +164,7 @@ export class HyperliquidClient { this.positions = new HyperliquidPositionsResource(http, basePath); this.cvd = new CvdResource(http, basePath); this.wallets = new WalletsResource(http, basePath); + this.breadth = new Hip3BreadthResource(http, basePath); this.hip3 = new Hip3Client(http); this.hip4 = new Hip4Client(http); } diff --git a/tests/core-breadth.test.ts b/tests/core-breadth.test.ts new file mode 100644 index 0000000..d4aa21d --- /dev/null +++ b/tests/core-breadth.test.ts @@ -0,0 +1,39 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { OxArchive } from '../src'; + +const snapshot = { + session_date: '2026-09-29', + calculated_at: '2026-09-29T04:00:00Z', + value_pct: 41.5, + coverage_ratio: 0.93, + counts: { candidates: 200, eligible: 186, above: 77, at: 0, below: 109, excluded_no_session_volume: 10, excluded_stale_price: 4 }, + namespaces: { eligible: {}, above: {}, at: {}, below: {} }, +}; + +const ok = (body: unknown) => ({ + ok: true, + status: 200, + headers: new Headers({ 'content-type': 'application/json' }), + json: async () => body, + text: async () => JSON.stringify(body), +}); + +afterEach(() => vi.unstubAllGlobals()); + +describe('core Hyperliquid breadth', () => { + it('reads /v1/hyperliquid/breadth/above-vwap for current and history', async () => { + const fetchMock = vi.fn() + .mockResolvedValueOnce(ok({ success: true, data: snapshot, meta: { count: 1, request_id: 'c' } })) + .mockResolvedValueOnce(ok({ success: true, data: [snapshot], meta: { count: 1, request_id: 'h', next_cursor: '1790650800000' } })); + vi.stubGlobal('fetch', fetchMock); + const client = new OxArchive({ apiKey: 'test-key', baseUrl: 'https://api.example.test', validate: true }); + + const current = await client.hyperliquid.breadth.current(); + const history = await client.hyperliquid.breadth.history({ start: 1790640000000, end: 1790650800000, interval: '1h' }); + + expect(current.valuePct).toBe(41.5); + expect(history.nextCursor).toBe('1790650800000'); + expect(new URL(String(fetchMock.mock.calls[0]?.[0])).pathname).toBe('/v1/hyperliquid/breadth/above-vwap/current'); + expect(new URL(String(fetchMock.mock.calls[1]?.[0])).pathname).toBe('/v1/hyperliquid/breadth/above-vwap'); + }); +});