perp-cli 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. package/README.ja.md +243 -0
  2. package/README.ko.md +243 -0
  3. package/README.md +129 -535
  4. package/README.zh.md +243 -0
  5. package/dist/api/public/lighter.d.ts +5 -0
  6. package/dist/api/public/lighter.js +5 -0
  7. package/dist/api/public/pacifica.js +2 -2
  8. package/dist/arb/sizing.js +4 -2
  9. package/dist/arb/state.d.ts +6 -0
  10. package/dist/balance-by-address.d.ts +39 -0
  11. package/dist/balance-by-address.js +44 -0
  12. package/dist/bot/quote-rules.js +1 -1
  13. package/dist/bot/strategies/funding-arb-strategy.js +10 -3
  14. package/dist/bot/strategies/funding-arb-utils.d.ts +14 -2
  15. package/dist/bot/strategies/funding-arb-utils.js +48 -15
  16. package/dist/bot/strategies/spot-perp-arb-strategy.d.ts +1 -1
  17. package/dist/bot/strategies/spot-perp-arb-strategy.js +4 -2
  18. package/dist/bot/strategies/spot-perp-executor.js +2 -2
  19. package/dist/cli-program.js +1 -1
  20. package/dist/commands/account.js +168 -24
  21. package/dist/commands/arb/index.js +19 -77
  22. package/dist/commands/arb-auto.d.ts +24 -0
  23. package/dist/commands/arb-auto.js +155 -81
  24. package/dist/commands/arb.d.ts +8 -0
  25. package/dist/commands/arb.js +49 -23
  26. package/dist/commands/backtest.js +23 -21
  27. package/dist/commands/market.d.ts +48 -0
  28. package/dist/commands/market.js +241 -78
  29. package/dist/commands/options.d.ts +5 -0
  30. package/dist/commands/options.js +20 -3
  31. package/dist/commands/pacifica-earn.js +8 -21
  32. package/dist/commands/portfolio.d.ts +16 -0
  33. package/dist/commands/portfolio.js +51 -8
  34. package/dist/commands/trade.js +4 -4
  35. package/dist/cross-chain-margin.d.ts +3 -1
  36. package/dist/cross-chain-margin.js +4 -2
  37. package/dist/dashboard/server.js +2 -2
  38. package/dist/dashboard/ui.js +9 -6
  39. package/dist/errors.d.ts +5 -0
  40. package/dist/errors.js +10 -0
  41. package/dist/exchanges/address.d.ts +8 -0
  42. package/dist/exchanges/address.js +47 -0
  43. package/dist/exchanges/arcus/adapter.d.ts +10 -6
  44. package/dist/exchanges/arcus/adapter.js +33 -6
  45. package/dist/exchanges/arcus/public.js +3 -2
  46. package/dist/exchanges/aster.d.ts +27 -7
  47. package/dist/exchanges/aster.js +124 -42
  48. package/dist/exchanges/canonical-symbols.d.ts +7 -0
  49. package/dist/exchanges/canonical-symbols.js +20 -0
  50. package/dist/exchanges/capabilities.d.ts +2 -0
  51. package/dist/exchanges/capabilities.js +2 -1
  52. package/dist/exchanges/derive/options.d.ts +5 -0
  53. package/dist/exchanges/derive/options.js +20 -1
  54. package/dist/exchanges/extended/adapter.d.ts +16 -2
  55. package/dist/exchanges/extended/adapter.js +96 -34
  56. package/dist/exchanges/extended/public.d.ts +3 -0
  57. package/dist/exchanges/extended/public.js +9 -2
  58. package/dist/exchanges/grvt/adapter.d.ts +16 -2
  59. package/dist/exchanges/grvt/adapter.js +98 -21
  60. package/dist/exchanges/hyperliquid-outcome.d.ts +2 -0
  61. package/dist/exchanges/hyperliquid-outcome.js +29 -1
  62. package/dist/exchanges/hyperliquid-spot.d.ts +8 -0
  63. package/dist/exchanges/hyperliquid-spot.js +20 -12
  64. package/dist/exchanges/hyperliquid.d.ts +180 -13
  65. package/dist/exchanges/hyperliquid.js +487 -185
  66. package/dist/exchanges/interface.d.ts +64 -8
  67. package/dist/exchanges/interface.js +31 -0
  68. package/dist/exchanges/lighter-public.d.ts +66 -11
  69. package/dist/exchanges/lighter-public.js +249 -81
  70. package/dist/exchanges/lighter-spot.d.ts +7 -1
  71. package/dist/exchanges/lighter-spot.js +99 -90
  72. package/dist/exchanges/lighter.d.ts +70 -11
  73. package/dist/exchanges/lighter.js +285 -92
  74. package/dist/exchanges/ondo/adapter.d.ts +34 -8
  75. package/dist/exchanges/ondo/adapter.js +73 -16
  76. package/dist/exchanges/ondo/public.d.ts +27 -2
  77. package/dist/exchanges/ondo/public.js +63 -31
  78. package/dist/exchanges/outcome-interface.d.ts +4 -0
  79. package/dist/exchanges/pacifica-spot.d.ts +6 -0
  80. package/dist/exchanges/pacifica-spot.js +23 -15
  81. package/dist/exchanges/pacifica-vaults.d.ts +9 -0
  82. package/dist/exchanges/pacifica-vaults.js +14 -1
  83. package/dist/exchanges/pacifica.d.ts +77 -6
  84. package/dist/exchanges/pacifica.js +321 -130
  85. package/dist/exchanges/public-balances.d.ts +25 -0
  86. package/dist/exchanges/public-balances.js +158 -0
  87. package/dist/exchanges/public-positions.d.ts +43 -0
  88. package/dist/exchanges/public-positions.js +154 -0
  89. package/dist/exchanges/qfex/adapter.d.ts +52 -9
  90. package/dist/exchanges/qfex/adapter.js +74 -9
  91. package/dist/exchanges/qfex/public.d.ts +62 -9
  92. package/dist/exchanges/qfex/public.js +128 -22
  93. package/dist/exchanges/qfex/ws-transport.d.ts +1 -1
  94. package/dist/exchanges/risex/adapter.d.ts +19 -7
  95. package/dist/exchanges/risex/adapter.js +58 -12
  96. package/dist/exchanges/spot-factory.js +1 -1
  97. package/dist/exchanges/txflow/adapter.d.ts +14 -7
  98. package/dist/exchanges/txflow/adapter.js +51 -19
  99. package/dist/exchanges/txflow/client.js +3 -1
  100. package/dist/exchanges/txflow/public.d.ts +12 -1
  101. package/dist/exchanges/txflow/public.js +82 -11
  102. package/dist/exchanges/unknown-symbol.d.ts +6 -1
  103. package/dist/exchanges/unknown-symbol.js +7 -2
  104. package/dist/exchanges/variational/adapter.d.ts +2 -6
  105. package/dist/exchanges/venue-types.d.ts +46 -3
  106. package/dist/exchanges/venue-types.js +80 -9
  107. package/dist/exchanges/venue.d.ts +99 -3
  108. package/dist/exchanges/venue.js +72 -0
  109. package/dist/exchanges/venues/hyperliquid.js +9 -0
  110. package/dist/exchanges/venues/lighter-rh.js +13 -3
  111. package/dist/exchanges/venues/lighter.js +13 -3
  112. package/dist/exchanges/venues/pacifica.js +9 -0
  113. package/dist/exchanges/venues/risex.js +15 -0
  114. package/dist/funding/legacy-public.d.ts +5 -3
  115. package/dist/funding/legacy-public.js +33 -37
  116. package/dist/funding/rates.d.ts +54 -5
  117. package/dist/funding/rates.js +49 -13
  118. package/dist/liquidity.d.ts +3 -1
  119. package/dist/liquidity.js +8 -4
  120. package/dist/mcp-pacifica-earn.js +11 -5
  121. package/dist/mcp-server.js +104 -15
  122. package/dist/pacifica/client.d.ts +2 -1
  123. package/dist/pacifica/client.js +11 -0
  124. package/dist/positions-by-address.d.ts +34 -0
  125. package/dist/positions-by-address.js +46 -0
  126. package/dist/strategies/funding-arb.js +35 -62
  127. package/dist/utils.d.ts +11 -1
  128. package/dist/venue-health.js +1 -1
  129. package/dist/ws/arcus-market.d.ts +9 -0
  130. package/dist/ws/arcus-market.js +9 -0
  131. package/dist/ws/aster-market.d.ts +33 -0
  132. package/dist/ws/aster-market.js +30 -0
  133. package/dist/ws/index.d.ts +30 -9
  134. package/dist/ws/index.js +180 -15
  135. package/dist/ws/lighter-market.d.ts +14 -0
  136. package/dist/ws/lighter-market.js +13 -0
  137. package/dist/ws/node.d.ts +30 -9
  138. package/dist/ws/node.js +204 -16
  139. package/dist/ws/unclassified.d.ts +16 -0
  140. package/dist/ws/unclassified.js +43 -0
  141. package/package.json +15 -2
  142. package/skills/perp-cli/SKILL.md +97 -33
  143. package/skills/perp-cli/package.json +15 -2
  144. package/skills/perp-cli/references/agent-operations.md +15 -12
  145. package/skills/perp-cli/references/commands.md +67 -12
  146. package/skills/perp-cli/references/strategies.md +38 -3
  147. package/skills/perp-cli/references/ws-library.md +55 -24
  148. package/skills/perp-cli/scripts/arb-monitor.sh +13 -10
  149. package/skills/perp-cli/scripts/funding-analysis.sh +57 -29
  150. package/skills/perp-cli/scripts/preflight.sh +27 -19
  151. package/skills/perp-cli/scripts/spot-perp-scan.sh +58 -38
  152. package/skills/perp-cli/scripts/validate-arb.sh +56 -39
  153. package/dist/cli-spec.d.ts +0 -46
  154. package/dist/cli-spec.js +0 -81
@@ -1,5 +1,6 @@
1
1
  import { printJson, jsonOk, jsonError, makeTable, formatUsd } from "../utils.js";
2
2
  import { getHistoricalRates } from "../funding-history.js";
3
+ import { HyperliquidAdapter, hlEarliestCandleStart } from "../exchanges/hyperliquid.js";
3
4
  import chalk from "chalk";
4
5
  export function registerBacktestCommands(program, isJson) {
5
6
  const backtest = program.command("backtest").description("Backtest trading strategies on historical data");
@@ -32,10 +33,13 @@ export function registerBacktestCommands(program, isJson) {
32
33
  const ratesA = getHistoricalRates(sym, exchA, startTime, endTime);
33
34
  const ratesB = getHistoricalRates(sym, exchB, startTime, endTime);
34
35
  if (ratesA.length === 0 && ratesB.length === 0) {
36
+ // The funding history is written by `arb scan --rates` (saveFundingSnapshot; verified venues
37
+ // only). There is no `perp funding snapshot` command (0.15.0 named one).
38
+ const collect = `perp arb scan --rates --symbol ${sym}`;
35
39
  if (isJson())
36
- return printJson(jsonError("NO_DATA", `No historical funding data for ${sym}. Run 'perp funding snapshot' first to collect data.`));
40
+ return printJson(jsonError("NO_DATA", `No historical funding data for ${sym}. Run '${collect}' periodically to collect data first.`));
37
41
  console.log(chalk.yellow(`\n No historical funding data for ${sym} on ${exchA}/${exchB}.`));
38
- console.log(chalk.yellow(` Run 'perp funding snapshot' periodically to collect data first.\n`));
42
+ console.log(chalk.yellow(` Run '${collect}' periodically to collect data first.\n`));
39
43
  return;
40
44
  }
41
45
  // Build time-aligned rate pairs
@@ -180,31 +184,25 @@ export function registerBacktestCommands(program, isJson) {
180
184
  console.error(chalk.red("Error: Need at least 2 grid lines"));
181
185
  process.exit(1);
182
186
  }
187
+ // Hyperliquid serves only the most recent 5000 candles per interval (docs "Candle snapshot");
188
+ // with 1h candles a --days above ~208 cannot be read. The start is clamped to the available
189
+ // window (one hour inside it) and the result says so — never presented as the full period.
190
+ const DAY_MS = 24 * 60 * 60 * 1000;
183
191
  const endTime = Date.now();
184
- const startTime = endTime - days * 24 * 60 * 60 * 1000;
185
- // Fetch historical klines from Hyperliquid
192
+ const requestedStart = endTime - days * DAY_MS;
193
+ const earliest = hlEarliestCandleStart("1h", endTime) + 60 * 60 * 1000;
194
+ const truncated = requestedStart < earliest;
195
+ const startTime = truncated ? earliest : requestedStart;
196
+ const dataFrom = startTime;
197
+ const coveredDays = (endTime - startTime) / DAY_MS;
198
+ // Fetch historical klines from Hyperliquid (adapter: symbol resolution, interval / window checks)
186
199
  if (!isJson()) {
187
200
  console.log(chalk.gray(`\n Fetching ${days}d of 1h klines for ${sym}...`));
188
201
  }
189
202
  let klines;
190
203
  try {
191
- const resp = await fetch("https://api.hyperliquid.xyz/info", {
192
- method: "POST",
193
- headers: { "Content-Type": "application/json" },
194
- body: JSON.stringify({
195
- type: "candleSnapshot",
196
- req: {
197
- coin: sym,
198
- interval: "1h",
199
- startTime,
200
- endTime,
201
- },
202
- }),
203
- });
204
- if (!resp.ok)
205
- throw new Error(`HTTP ${resp.status}`);
206
- const data = await resp.json();
207
- klines = data.map(k => ({ t: k.t, o: k.o, h: k.h, l: k.l, c: k.c }));
204
+ const rows = await new HyperliquidAdapter(undefined, false).getKlines(sym, "1h", startTime, endTime);
205
+ klines = rows.map(k => ({ t: k.time, o: k.open, h: k.high, l: k.low, c: k.close }));
208
206
  }
209
207
  catch (err) {
210
208
  const msg = err instanceof Error ? err.message : String(err);
@@ -291,6 +289,8 @@ export function registerBacktestCommands(program, isJson) {
291
289
  symbol: sym,
292
290
  period: `${days} days`,
293
291
  klines: klines.length,
292
+ dataFrom: new Date(dataFrom).toISOString(),
293
+ truncated,
294
294
  priceRange,
295
295
  gridRange: `$${formatUsd(String(lowerPrice))} - $${formatUsd(String(upperPrice))}`,
296
296
  grids,
@@ -307,6 +307,8 @@ export function registerBacktestCommands(program, isJson) {
307
307
  return printJson(jsonOk(summary));
308
308
  console.log(chalk.cyan.bold(`\n Grid Backtest — ${sym}\n`));
309
309
  console.log(` Period: ${days} days (${klines.length} candles)`);
310
+ if (truncated)
311
+ console.log(chalk.yellow(` Truncated: Hyperliquid serves only the most recent 5000 1h candles — start clamped to ${new Date(dataFrom).toISOString()} (${coveredDays.toFixed(1)} of ${days} days)`));
310
312
  console.log(` Price range: ${priceRange}`);
311
313
  console.log(` Grid range: $${formatUsd(String(lowerPrice))} - $${formatUsd(String(upperPrice))}`);
312
314
  console.log(` Grid lines: ${grids} (step: $${step.toFixed(2)})`);
@@ -1,6 +1,54 @@
1
1
  import { Command } from "commander";
2
2
  import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  import "../exchanges/venues/index.js";
4
+ /**
5
+ * Venues of the multi-venue `market list|prices` (no -e): every venue with the marketData role
6
+ * and a (public) adapter — market-data-only venues (TxFlow, Variational, GRVT, Extended) included.
7
+ * They were left out in 0.15.0 because the list was the account-role venues.
8
+ */
9
+ export declare const MARKET_DATA_VENUES: import("../exchanges/venue-types.js").VenueId[];
10
+ /**
11
+ * `--limit` / `-l` / `--depth`: a positive integer, the same rule on every venue (parseInt took
12
+ * "abc" as NaN and "-5" as a negative slice). Anything else → INVALID_PARAMS before any request.
13
+ */
14
+ export declare function parsePositiveIntArg(raw: string, flag: string): number;
15
+ /**
16
+ * Most order-book levels per side each venue serves (measured live 2026-10-19; the request size
17
+ * where the venue takes one): one rule for every venue — `--depth` above it is INVALID_PARAMS
18
+ * naming the max, never clamped or silently short. Absent = the full book (Extended).
19
+ */
20
+ export declare const BOOK_DEPTH_MAX: Readonly<Record<string, number>>;
21
+ export declare function assertBookDepth(venue: string, depth: number): void;
22
+ /**
23
+ * `--start` / `--end`: unix ms (digits) or an ISO-8601 date / time (`Date.parse`; "2026-10-18" is
24
+ * UTC midnight). parseInt("2026-10-18") read 2026 ms. Non-finite → INVALID_PARAMS.
25
+ */
26
+ export declare function parseTimeArg(raw: string, flag: string): number;
27
+ /**
28
+ * Bare market symbol of a CLI argument as `market info` matches it against getMarkets() rows:
29
+ * "btc-perp" → "BTC", Extended "BTC-USD" / "TSLA_24_5-USD" → "BTC" / "TSLA", TxFlow "BTC-USDC" → "BTC"
30
+ * (book / funding resolve these through the venue; info read the raw argument and missed them).
31
+ * HIP-3 "xyz:XYZ100" keeps its prefix.
32
+ */
33
+ export declare function bareSymbolOf(arg: string): string;
34
+ /**
35
+ * History order, one rule for every venue: trades and funding history newest first, candles
36
+ * oldest first (Aster / QFEX answered funding oldest first, GRVT trades oldest first). A stable
37
+ * copy — the adapter's array (and its non-enumerable tape metadata) is left as is.
38
+ */
39
+ export declare function newestFirst<T extends {
40
+ time: number;
41
+ }>(rows: readonly T[]): T[];
42
+ export declare function notTradableReason(m: {
43
+ tradable?: boolean;
44
+ status?: string | null;
45
+ }): string | null;
4
46
  export declare function registerMarketCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>,
5
47
  /** Credentialed adapter of the selected venue — for market data the venue serves only to an authenticated account. */
6
48
  getAccountAdapter?: () => Promise<ExchangeAdapter>): void;
49
+ /**
50
+ * Why a listed market is not tradable, from the row's `status` when the venue states it (QFEX
51
+ * "nonUsdQuote", Ondo "closed" / "reduceOnly"); the generic off-hours text only when it does not.
52
+ * null = tradable or no tradability flag.
53
+ */
54
+ export declare function notTradableNote(m: object): string | null;