perp-cli 0.14.2 → 0.15.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 (313) hide show
  1. package/README.md +152 -13
  2. package/dist/agent-wallet/store.d.ts +3 -2
  3. package/dist/agent-wallet/store.js +2 -2
  4. package/dist/api/public/hyperliquid.js +4 -0
  5. package/dist/api/public/pacifica.js +83 -13
  6. package/dist/api/public/urls.d.ts +2 -0
  7. package/dist/api/public/urls.js +3 -0
  8. package/dist/arb/utils.d.ts +1 -2
  9. package/dist/arb/utils.js +1 -2
  10. package/dist/bot/apex/pulse.js +3 -0
  11. package/dist/bot/apex/radar.js +2 -1
  12. package/dist/bot/engine.d.ts +10 -1
  13. package/dist/bot/engine.js +14 -6
  14. package/dist/bot/indicators.d.ts +7 -0
  15. package/dist/bot/indicators.js +16 -0
  16. package/dist/bot/perp-order.d.ts +4 -4
  17. package/dist/bot/perp-order.js +5 -5
  18. package/dist/bot/quarantine.d.ts +1 -1
  19. package/dist/bot/quarantine.js +1 -1
  20. package/dist/bot/strategies/apex-strategy.js +3 -2
  21. package/dist/bot/strategies/engine-mm.js +6 -5
  22. package/dist/bot/strategies/funding-arb-strategy.js +9 -9
  23. package/dist/bot/strategies/funding-arb-utils.d.ts +15 -2
  24. package/dist/bot/strategies/funding-arb-utils.js +43 -24
  25. package/dist/bot/strategies/funding-auto.js +2 -2
  26. package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
  27. package/dist/bot/strategies/liquidation-mm.js +8 -2
  28. package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
  29. package/dist/bot/strategies/momentum-breakout.js +11 -3
  30. package/dist/bot/strategies/spot-perp-arb-strategy.js +23 -12
  31. package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
  32. package/dist/bot/strategies/spot-perp-executor.js +1 -1
  33. package/dist/bot/strategy-types.d.ts +2 -1
  34. package/dist/bot/tui/BotDashboard.d.ts +1 -1
  35. package/dist/bot/tui/BotDashboard.js +1 -1
  36. package/dist/cache.js +12 -6
  37. package/dist/cli-program.d.ts +15 -0
  38. package/dist/cli-program.js +125 -5
  39. package/dist/commands/agent-import.d.ts +7 -0
  40. package/dist/commands/agent-import.js +30 -1
  41. package/dist/commands/agent.d.ts +6 -3
  42. package/dist/commands/agent.js +160 -12
  43. package/dist/commands/arb/index.js +4 -4
  44. package/dist/commands/arb-auto.js +21 -17
  45. package/dist/commands/arb.d.ts +12 -0
  46. package/dist/commands/arb.js +61 -143
  47. package/dist/commands/bot.js +1 -1
  48. package/dist/commands/builder.js +1 -1
  49. package/dist/commands/funds.js +113 -3
  50. package/dist/commands/init.d.ts +1 -1
  51. package/dist/commands/init.js +27 -7
  52. package/dist/commands/manage.js +23 -21
  53. package/dist/commands/market.js +168 -36
  54. package/dist/commands/options.d.ts +9 -0
  55. package/dist/commands/options.js +134 -0
  56. package/dist/commands/pacifica-earn.d.ts +5 -0
  57. package/dist/commands/pacifica-earn.js +468 -0
  58. package/dist/commands/pacifica-orders.d.ts +14 -0
  59. package/dist/commands/pacifica-orders.js +142 -0
  60. package/dist/commands/portfolio.js +32 -47
  61. package/dist/commands/rebalance.d.ts +1 -0
  62. package/dist/commands/rebalance.js +15 -0
  63. package/dist/commands/risk.d.ts +11 -2
  64. package/dist/commands/risk.js +39 -44
  65. package/dist/commands/trade.d.ts +1 -0
  66. package/dist/commands/trade.js +63 -58
  67. package/dist/commands/wallet.js +1 -1
  68. package/dist/config.d.ts +2 -2
  69. package/dist/config.js +15 -4
  70. package/dist/constants.js +6 -0
  71. package/dist/dashboard/server.js +1 -1
  72. package/dist/dashboard/ui.js +1 -1
  73. package/dist/errors.d.ts +24 -1
  74. package/dist/errors.js +25 -3
  75. package/dist/exchanges/arcus/adapter.d.ts +7 -7
  76. package/dist/exchanges/arcus/adapter.js +12 -12
  77. package/dist/exchanges/arcus/agent.d.ts +1 -1
  78. package/dist/exchanges/arcus/agent.js +1 -1
  79. package/dist/exchanges/arcus/client.js +1 -1
  80. package/dist/exchanges/arcus/signing.d.ts +2 -3
  81. package/dist/exchanges/arcus/signing.js +1 -1
  82. package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
  83. package/dist/exchanges/arcus/spot-chain.js +1 -1
  84. package/dist/exchanges/arcus/spot-router.d.ts +1 -1
  85. package/dist/exchanges/arcus/spot-router.js +2 -2
  86. package/dist/exchanges/arcus/spot.d.ts +2 -3
  87. package/dist/exchanges/arcus/spot.js +2 -3
  88. package/dist/exchanges/aster-spot.d.ts +3 -3
  89. package/dist/exchanges/aster-spot.js +6 -9
  90. package/dist/exchanges/aster-typed-data.d.ts +1 -1
  91. package/dist/exchanges/aster-typed-data.js +1 -1
  92. package/dist/exchanges/aster.d.ts +9 -4
  93. package/dist/exchanges/aster.js +125 -55
  94. package/dist/exchanges/builder-fee.d.ts +1 -1
  95. package/dist/exchanges/builder-fee.js +13 -2
  96. package/dist/exchanges/canonical-symbols.d.ts +1 -2
  97. package/dist/exchanges/canonical-symbols.js +26 -2
  98. package/dist/exchanges/derive/client.d.ts +37 -0
  99. package/dist/exchanges/derive/client.js +135 -0
  100. package/dist/exchanges/derive/options.d.ts +68 -0
  101. package/dist/exchanges/derive/options.js +360 -0
  102. package/dist/exchanges/extended/adapter.d.ts +82 -0
  103. package/dist/exchanges/extended/adapter.js +179 -0
  104. package/dist/exchanges/extended/client.d.ts +38 -0
  105. package/dist/exchanges/extended/client.js +88 -0
  106. package/dist/exchanges/extended/public.d.ts +130 -0
  107. package/dist/exchanges/extended/public.js +429 -0
  108. package/dist/exchanges/grvt/adapter.d.ts +86 -0
  109. package/dist/exchanges/grvt/adapter.js +206 -0
  110. package/dist/exchanges/grvt/client.d.ts +25 -0
  111. package/dist/exchanges/grvt/client.js +86 -0
  112. package/dist/exchanges/grvt/public.d.ts +151 -0
  113. package/dist/exchanges/grvt/public.js +438 -0
  114. package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
  115. package/dist/exchanges/hyperliquid-spot.js +5 -5
  116. package/dist/exchanges/hyperliquid.d.ts +22 -16
  117. package/dist/exchanges/hyperliquid.js +80 -55
  118. package/dist/exchanges/index.d.ts +1 -1
  119. package/dist/exchanges/index.js +1 -1
  120. package/dist/exchanges/interface.d.ts +97 -8
  121. package/dist/exchanges/interface.js +19 -2
  122. package/dist/exchanges/ioc-outcome.d.ts +1 -1
  123. package/dist/exchanges/ioc-outcome.js +1 -1
  124. package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
  125. package/dist/exchanges/legacy-trading-v2.js +7 -2
  126. package/dist/exchanges/lighter-public.d.ts +2 -2
  127. package/dist/exchanges/lighter-public.js +2 -2
  128. package/dist/exchanges/lighter-signer.d.ts +1 -1
  129. package/dist/exchanges/lighter-signer.js +1 -1
  130. package/dist/exchanges/lighter-spot.d.ts +3 -4
  131. package/dist/exchanges/lighter-spot.js +6 -7
  132. package/dist/exchanges/lighter.d.ts +11 -11
  133. package/dist/exchanges/lighter.js +28 -14
  134. package/dist/exchanges/ondo/adapter.d.ts +5 -6
  135. package/dist/exchanges/ondo/adapter.js +16 -14
  136. package/dist/exchanges/ondo/agent.d.ts +1 -1
  137. package/dist/exchanges/ondo/agent.js +1 -1
  138. package/dist/exchanges/ondo/client.d.ts +1 -1
  139. package/dist/exchanges/ondo/client.js +2 -2
  140. package/dist/exchanges/ondo/public.d.ts +8 -1
  141. package/dist/exchanges/ondo/public.js +13 -2
  142. package/dist/exchanges/ondo/spot.d.ts +2 -2
  143. package/dist/exchanges/ondo/spot.js +3 -3
  144. package/dist/exchanges/options-interface.d.ts +158 -0
  145. package/dist/exchanges/options-interface.js +17 -0
  146. package/dist/exchanges/options-venues.d.ts +12 -0
  147. package/dist/exchanges/options-venues.js +35 -0
  148. package/dist/exchanges/pacifica-atomic.d.ts +211 -0
  149. package/dist/exchanges/pacifica-atomic.js +440 -0
  150. package/dist/exchanges/pacifica-decimal.d.ts +10 -0
  151. package/dist/exchanges/pacifica-decimal.js +51 -0
  152. package/dist/exchanges/pacifica-presigned.d.ts +53 -0
  153. package/dist/exchanges/pacifica-presigned.js +141 -0
  154. package/dist/exchanges/pacifica-print.d.ts +200 -0
  155. package/dist/exchanges/pacifica-print.js +417 -0
  156. package/dist/exchanges/pacifica-spot.d.ts +2 -2
  157. package/dist/exchanges/pacifica-spot.js +3 -3
  158. package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
  159. package/dist/exchanges/pacifica-typed-data.js +1 -1
  160. package/dist/exchanges/pacifica-vaults.d.ts +121 -0
  161. package/dist/exchanges/pacifica-vaults.js +264 -0
  162. package/dist/exchanges/pacifica.d.ts +32 -5
  163. package/dist/exchanges/pacifica.js +114 -48
  164. package/dist/exchanges/precision.d.ts +3 -1
  165. package/dist/exchanges/precision.js +9 -2
  166. package/dist/exchanges/qfex/adapter.d.ts +18 -5
  167. package/dist/exchanges/qfex/adapter.js +32 -12
  168. package/dist/exchanges/qfex/agent.d.ts +1 -1
  169. package/dist/exchanges/qfex/agent.js +1 -1
  170. package/dist/exchanges/qfex/client.d.ts +1 -1
  171. package/dist/exchanges/qfex/client.js +1 -1
  172. package/dist/exchanges/qfex/errors.d.ts +1 -1
  173. package/dist/exchanges/qfex/errors.js +2 -2
  174. package/dist/exchanges/qfex/public.d.ts +3 -3
  175. package/dist/exchanges/qfex/public.js +3 -3
  176. package/dist/exchanges/qfex/signing.d.ts +2 -2
  177. package/dist/exchanges/qfex/signing.js +4 -5
  178. package/dist/exchanges/qfex/ws-transport.d.ts +2 -2
  179. package/dist/exchanges/qfex/ws-transport.js +3 -3
  180. package/dist/exchanges/rate-limiter.d.ts +1 -1
  181. package/dist/exchanges/rate-limiter.js +5 -2
  182. package/dist/exchanges/registry.d.ts +1 -1
  183. package/dist/exchanges/registry.js +1 -1
  184. package/dist/exchanges/risex/adapter.d.ts +148 -0
  185. package/dist/exchanges/risex/adapter.js +796 -0
  186. package/dist/exchanges/risex/agent.d.ts +154 -0
  187. package/dist/exchanges/risex/agent.js +384 -0
  188. package/dist/exchanges/risex/client.d.ts +47 -0
  189. package/dist/exchanges/risex/client.js +174 -0
  190. package/dist/exchanges/risex/nonce.d.ts +43 -0
  191. package/dist/exchanges/risex/nonce.js +212 -0
  192. package/dist/exchanges/risex/public.d.ts +64 -0
  193. package/dist/exchanges/risex/public.js +150 -0
  194. package/dist/exchanges/risex/session.d.ts +71 -0
  195. package/dist/exchanges/risex/session.js +171 -0
  196. package/dist/exchanges/risex/typed-data.d.ts +173 -0
  197. package/dist/exchanges/risex/typed-data.js +240 -0
  198. package/dist/exchanges/risex/units.d.ts +29 -0
  199. package/dist/exchanges/risex/units.js +104 -0
  200. package/dist/exchanges/slippage.d.ts +1 -1
  201. package/dist/exchanges/slippage.js +1 -1
  202. package/dist/exchanges/spot-factory.d.ts +3 -3
  203. package/dist/exchanges/spot-factory.js +7 -7
  204. package/dist/exchanges/spot-interface.d.ts +5 -5
  205. package/dist/exchanges/spot-interface.js +2 -2
  206. package/dist/exchanges/spot-result.d.ts +3 -3
  207. package/dist/exchanges/spot-result.js +3 -3
  208. package/dist/exchanges/trading-v2.d.ts +3 -3
  209. package/dist/exchanges/trading-v2.js +9 -6
  210. package/dist/exchanges/txflow/adapter.d.ts +54 -0
  211. package/dist/exchanges/txflow/adapter.js +118 -0
  212. package/dist/exchanges/txflow/client.d.ts +47 -0
  213. package/dist/exchanges/txflow/client.js +153 -0
  214. package/dist/exchanges/txflow/public.d.ts +76 -0
  215. package/dist/exchanges/txflow/public.js +284 -0
  216. package/dist/exchanges/unknown-symbol.d.ts +29 -0
  217. package/dist/exchanges/unknown-symbol.js +51 -0
  218. package/dist/exchanges/variational/adapter.d.ts +71 -0
  219. package/dist/exchanges/variational/adapter.js +137 -0
  220. package/dist/exchanges/variational/client.d.ts +21 -0
  221. package/dist/exchanges/variational/client.js +66 -0
  222. package/dist/exchanges/variational/public.d.ts +126 -0
  223. package/dist/exchanges/variational/public.js +430 -0
  224. package/dist/exchanges/venue-types.d.ts +6 -7
  225. package/dist/exchanges/venue-types.js +2 -2
  226. package/dist/exchanges/venue.d.ts +51 -8
  227. package/dist/exchanges/venue.js +54 -4
  228. package/dist/exchanges/venues/arcus.d.ts +1 -2
  229. package/dist/exchanges/venues/arcus.js +5 -4
  230. package/dist/exchanges/venues/aster.d.ts +2 -2
  231. package/dist/exchanges/venues/aster.js +6 -4
  232. package/dist/exchanges/venues/create-helpers.d.ts +1 -1
  233. package/dist/exchanges/venues/create-helpers.js +3 -3
  234. package/dist/exchanges/venues/extended.d.ts +21 -0
  235. package/dist/exchanges/venues/extended.js +96 -0
  236. package/dist/exchanges/venues/grvt.d.ts +30 -0
  237. package/dist/exchanges/venues/grvt.js +98 -0
  238. package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
  239. package/dist/exchanges/venues/hyperliquid.js +10 -8
  240. package/dist/exchanges/venues/index.d.ts +7 -2
  241. package/dist/exchanges/venues/index.js +8 -2
  242. package/dist/exchanges/venues/lighter-family.d.ts +7 -9
  243. package/dist/exchanges/venues/lighter-family.js +9 -11
  244. package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
  245. package/dist/exchanges/venues/lighter-rh.js +7 -5
  246. package/dist/exchanges/venues/lighter.d.ts +1 -1
  247. package/dist/exchanges/venues/lighter.js +5 -3
  248. package/dist/exchanges/venues/ondo.d.ts +1 -1
  249. package/dist/exchanges/venues/ondo.js +5 -3
  250. package/dist/exchanges/venues/pacifica.d.ts +2 -2
  251. package/dist/exchanges/venues/pacifica.js +6 -4
  252. package/dist/exchanges/venues/qfex.d.ts +1 -1
  253. package/dist/exchanges/venues/qfex.js +4 -2
  254. package/dist/exchanges/venues/risex-release.d.ts +14 -0
  255. package/dist/exchanges/venues/risex-release.js +14 -0
  256. package/dist/exchanges/venues/risex.d.ts +31 -0
  257. package/dist/exchanges/venues/risex.js +116 -0
  258. package/dist/exchanges/venues/txflow.d.ts +18 -0
  259. package/dist/exchanges/venues/txflow.js +77 -0
  260. package/dist/exchanges/venues/variational.d.ts +21 -0
  261. package/dist/exchanges/venues/variational.js +85 -0
  262. package/dist/execution-log.d.ts +1 -1
  263. package/dist/funding/legacy-public.d.ts +1 -1
  264. package/dist/funding/legacy-public.js +9 -4
  265. package/dist/funding/normalize.d.ts +1 -1
  266. package/dist/funding/normalize.js +1 -1
  267. package/dist/funding/rates.d.ts +44 -4
  268. package/dist/funding/rates.js +56 -12
  269. package/dist/index.js +7 -6
  270. package/dist/jobs.d.ts +1 -0
  271. package/dist/jobs.js +25 -1
  272. package/dist/landing.d.ts +2 -0
  273. package/dist/landing.js +1 -1
  274. package/dist/mcp-pacifica-earn.d.ts +9 -0
  275. package/dist/mcp-pacifica-earn.js +58 -0
  276. package/dist/mcp-server.js +113 -59
  277. package/dist/pacifica/client.d.ts +67 -4
  278. package/dist/pacifica/client.js +170 -8
  279. package/dist/pacifica/rate-limit.d.ts +144 -0
  280. package/dist/pacifica/rate-limit.js +267 -0
  281. package/dist/pacifica/types/order.d.ts +2 -1
  282. package/dist/plan-executor.js +7 -2
  283. package/dist/rebalance.js +1 -1
  284. package/dist/risk.d.ts +2 -1
  285. package/dist/risk.js +2 -4
  286. package/dist/settings.d.ts +32 -3
  287. package/dist/signer/ows-evm.d.ts +1 -1
  288. package/dist/signer/ows-evm.js +1 -1
  289. package/dist/smart-order.d.ts +1 -1
  290. package/dist/smart-order.js +1 -1
  291. package/dist/strategies/funding-arb.d.ts +1 -1
  292. package/dist/strategies/funding-arb.js +1 -1
  293. package/dist/trade-validator.d.ts +1 -1
  294. package/dist/trade-validator.js +9 -4
  295. package/dist/utils.d.ts +12 -3
  296. package/dist/utils.js +11 -3
  297. package/dist/venue-health.d.ts +40 -0
  298. package/dist/venue-health.js +75 -0
  299. package/dist/withdraw-gate.d.ts +1 -1
  300. package/dist/withdraw-gate.js +1 -1
  301. package/dist/ws/index.d.ts +959 -0
  302. package/dist/ws/index.js +4295 -0
  303. package/dist/ws/node.d.ts +975 -0
  304. package/dist/ws/node.js +4366 -0
  305. package/dist/ws/pacifica-instrument.d.ts +8 -0
  306. package/dist/ws/pacifica-instrument.js +8 -0
  307. package/dist/ws-cli/credentials.d.ts +62 -0
  308. package/dist/ws-cli/credentials.js +126 -0
  309. package/package.json +58 -10
  310. package/skills/perp-cli/SKILL.md +62 -9
  311. package/skills/perp-cli/references/agent-operations.md +7 -0
  312. package/skills/perp-cli/references/commands.md +76 -5
  313. package/skills/perp-cli/references/ws-library.md +492 -0
@@ -1,7 +1,7 @@
1
1
  import chalk from "chalk";
2
2
  import { makeTable, formatUsd, formatPnl, printJson, jsonOk, jsonError, withJsonErrors } from "../utils.js";
3
3
  import { isDexCapable } from "../exchanges/capabilities.js";
4
- import { getVenue, listVenuesWithRole, tryResolveVenueId } from "../exchanges/venue.js";
4
+ import { getVenue, listVenuesWithRole, roleUnsupported, tryResolveVenueId } from "../exchanges/venue.js";
5
5
  import { PerpError } from "../errors.js";
6
6
  import "../exchanges/venues/index.js";
7
7
  import { listConfiguredAccountVenues } from "../config.js";
@@ -9,6 +9,7 @@ import { HYPERLIQUID_API_URL } from "../api/public/urls.js";
9
9
  import { fetchValuedSpotBalances, getSpotAdapter, spotSharesPerpMargin, withSpotTimeout } from "../exchanges/spot-factory.js";
10
10
  import { scrubSecretEnv } from "../secret-env.js";
11
11
  import { networkOf } from "../cli-flags.js";
12
+ import { probeVenueHealth } from "../venue-health.js";
12
13
  // ── Types ──────────────────────────────────────────────────────────────────
13
14
  /** Venues with an account API (market-data-only venues are excluded). */
14
15
  function accountVenueIds() {
@@ -51,6 +52,12 @@ function validateExchanges(list) {
51
52
  const valid = accountVenueIds();
52
53
  // Aliases (hl, pac, lt, lrh, ast, …) resolve through the venue registry, like every other command.
53
54
  const ids = list.map(s => tryResolveVenueId(s) ?? s.toLowerCase());
55
+ // A registered market-data-only venue (TxFlow) is a capability refusal, not an unknown name.
56
+ for (const id of ids) {
57
+ const known = tryResolveVenueId(id);
58
+ if (known !== null && !valid.includes(known))
59
+ throw roleUnsupported(getVenue(known), "account");
60
+ }
54
61
  const unknown = ids.filter(e => !valid.includes(e));
55
62
  if (unknown.length > 0) {
56
63
  throw new PerpError("INVALID_PARAMS", `Unknown exchange: ${unknown.join(", ")}`, { remediation: `Use one of: ${valid.join(", ")} (or an alias such as hl, pac, lt, lrh, ast)` });
@@ -178,7 +185,7 @@ async function fetchExchangeEntry(exName, getAdapter) {
178
185
  adapter.getOpenOrders(),
179
186
  adapter.getFundingPayments(200).then((rows) => ({ rows, error: null }), (e) => ({ rows: null, error: e instanceof Error ? e.message : String(e) })),
180
187
  ]);
181
- // Spot balances for every venue with def.spot (§12). Failures are surfaced on
188
+ // Spot balances for every venue with def.spot. Failures are surfaced on
182
189
  // stderr (the perp snapshot is still reported); unpriced tokens are excluded + warned.
183
190
  let spot = [];
184
191
  let isUnified = false;
@@ -205,7 +212,7 @@ async function fetchExchangeEntry(exName, getAdapter) {
205
212
  const funding24h = fundingPayments.rows === null
206
213
  ? null
207
214
  : fundingPayments.rows.filter(f => f.time >= cutoff24h).reduce((sum, f) => sum + Number(f.payment), 0);
208
- // Shared balances (Pacifica USDC, HL unified USDC) are already in perp equity (§12.4 #8).
215
+ // Shared balances (Pacifica USDC, HL unified USDC) are already in perp equity.
209
216
  const nonUsdcSpotValue = spot.filter(b => !b.shared).reduce((s, b) => s + b.valueUsd, 0);
210
217
  return {
211
218
  name: exName,
@@ -288,42 +295,12 @@ function buildTotals(entries) {
288
295
  }
289
296
  // ── Health check (no balance fetches) ──────────────────────────────────────
290
297
  async function runHealth(exchangeList, network) {
291
- const { pingPacifica, pingHyperliquid, pingLighter } = await import("../shared-api.js");
292
- const pingAster = async () => {
293
- const start = Date.now();
294
- try {
295
- const res = await fetch("https://fapi.asterdex.com/fapi/v1/time");
296
- return { ok: res.ok, latencyMs: Date.now() - start, status: res.status };
297
- }
298
- catch {
299
- return { ok: false, latencyMs: Date.now() - start, status: 0 };
300
- }
301
- };
302
- const pingMap = {
303
- pacifica: pingPacifica,
304
- hyperliquid: pingHyperliquid,
305
- lighter: pingLighter,
306
- aster: pingAster,
307
- };
298
+ // The same probe as `perp health` and the MCP health_check, over the selected venues.
299
+ const h = await probeVenueHealth(network, exchangeList);
308
300
  const exchanges = {};
309
- await Promise.all(exchangeList.map(async (ex) => {
310
- const fn = pingMap[ex];
311
- try {
312
- if (!fn) {
313
- // No dedicated ping helper: probe the venue's public market-data path.
314
- await getVenue(ex).publicData.fetchMarkets(network);
315
- exchanges[ex] = "ok";
316
- return;
317
- }
318
- const r = await fn();
319
- exchanges[ex] = r.ok ? "ok" : "error";
320
- }
321
- catch {
322
- exchanges[ex] = "error";
323
- }
324
- }));
325
- const anyDown = Object.values(exchanges).some(v => v === "error");
326
- return { exchanges, anyDown };
301
+ for (const p of h.probes)
302
+ exchanges[p.venue] = p.ok ? "ok" : "error";
303
+ return { exchanges, anyDown: !h.allHealthy, probes: h.probes, skipped: h.skipped };
327
304
  }
328
305
  // ── Arb top-5 ──────────────────────────────────────────────────────────────
329
306
  async function fetchArbTop() {
@@ -514,11 +491,15 @@ function renderSnapshotText(data) {
514
491
  }
515
492
  function renderHealthText(h) {
516
493
  console.log(chalk.cyan.bold("\n Exchange Health Check\n"));
517
- const rows = Object.entries(h.exchanges).map(([ex, status]) => {
518
- const statusIcon = status === "ok" ? chalk.green("OK") : chalk.red("DOWN");
519
- return [chalk.white.bold(ex), statusIcon];
520
- });
521
- console.log(makeTable(["Exchange", "Status"], rows));
494
+ const rows = h.probes.map((p) => [
495
+ chalk.white.bold(p.venue),
496
+ p.ok ? chalk.green("OK") : chalk.red("DOWN"),
497
+ `${p.latencyMs}ms`,
498
+ p.error !== null ? chalk.red(p.error) : chalk.gray("-"),
499
+ ]);
500
+ console.log(makeTable(["Exchange", "Status", "Latency", "Error"], rows));
501
+ for (const s of h.skipped)
502
+ console.log(chalk.gray(` ${s.venue}: not probed — ${s.reason}`));
522
503
  const overall = h.anyDown ? chalk.red("ISSUES DETECTED") : chalk.green("ALL HEALTHY");
523
504
  console.log(`\n Overall: ${overall}\n`);
524
505
  }
@@ -773,7 +754,11 @@ export function registerPortfolioCommand(program, getAdapterForExchange, isJson,
773
754
  catch (err) {
774
755
  const msg = err instanceof Error ? err.message : String(err);
775
756
  if (json) {
776
- console.log(JSON.stringify(jsonError("INVALID_PARAMS", msg)));
757
+ // A typed refusal (CAPABILITY_UNSUPPORTED for a market-data-only venue) keeps its code.
758
+ const s = err instanceof PerpError ? err.structured : null;
759
+ console.log(JSON.stringify(s !== null
760
+ ? jsonError(s.code, msg, { ...(s.details !== undefined ? { details: s.details } : {}), ...(s.remediation !== undefined ? { remediation: s.remediation } : {}) })
761
+ : jsonError("INVALID_PARAMS", msg)));
777
762
  }
778
763
  else {
779
764
  console.error(chalk.red(`Error: ${msg}`));
@@ -794,9 +779,9 @@ export function registerPortfolioCommand(program, getAdapterForExchange, isJson,
794
779
  version,
795
780
  scope: resolved.scope,
796
781
  mode: "health",
797
- exchanges: [],
798
- totals: { equity: 0, spotValueUsd: 0, accountValueUsd: 0, marginUsed: 0, marginPct: 0, funding24h: 0, fundingIncomplete: false },
799
- risk: { level: "LOW", marginPct: 0 },
782
+ exchanges: null,
783
+ totals: null,
784
+ risk: null,
800
785
  health,
801
786
  };
802
787
  if (json)
@@ -1,4 +1,5 @@
1
1
  import { Command } from "commander";
2
2
  import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  import type { Network } from "../exchanges/venue-types.js";
4
+ import "../exchanges/venues/index.js";
4
5
  export declare function registerFundsRebalanceCommands(parent: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean, getNetwork: () => Network): void;
@@ -5,6 +5,10 @@ import { PerpError } from "../errors.js";
5
5
  import { fetchBalancesWithFailures, computeRebalancePlan, describeBridgeRoute, estimateMoveTime, } from "../rebalance.js";
6
6
  import { EXCHANGE_TO_CHAIN, assertBridgeNetwork, executeBestBridge } from "../bridge-engine.js";
7
7
  import { cliCredentialOptsFrom } from "../config.js";
8
+ import { getVenue, roleUnsupported, tryResolveVenueId } from "../exchanges/venue.js";
9
+ import { unsupported } from "../exchanges/trading-v2.js";
10
+ import { RISEX_FUNDS_APP_ONLY } from "../exchanges/venues/risex.js";
11
+ import "../exchanges/venues/index.js";
8
12
  export function registerFundsRebalanceCommands(parent, getAdapterForExchange, isJson, getNetwork) {
9
13
  const rebalance = parent.command("rebalance").description("Cross-exchange balance management");
10
14
  // ── rebalance check ──
@@ -149,6 +153,17 @@ export function registerFundsRebalanceCommands(parent, getAdapterForExchange, is
149
153
  if (opts.autoBridge && !dryRun)
150
154
  assertBridgeNetwork(getNetwork());
151
155
  const exchangeNames = opts.exchanges.split(",").map((e) => e.trim());
156
+ // Venues whose funds perp-cli cannot move are refused before any read (no partial plan).
157
+ for (const name of exchangeNames) {
158
+ const id = tryResolveVenueId(name);
159
+ if (id === null)
160
+ continue;
161
+ const def = getVenue(id);
162
+ if (!def.roles.includes("account"))
163
+ throw roleUnsupported(def, "rebalance");
164
+ if (id === "risex")
165
+ throw unsupported("risex", "rebalance", RISEX_FUNDS_APP_ONLY.reason, RISEX_FUNDS_APP_ONLY.remediation);
166
+ }
152
167
  const adapters = new Map();
153
168
  const unavailable = [];
154
169
  for (const name of exchangeNames) {
@@ -1,5 +1,14 @@
1
1
  import { Command } from "commander";
2
2
  import type { ExchangeAdapter } from "../exchanges/index.js";
3
- /** Run health check and return results + render. Exported for use by status --health. */
4
- export declare function runHealthCheck(isJson: () => boolean): Promise<void>;
3
+ import type { Network } from "../exchanges/venue-types.js";
4
+ /**
5
+ * `perp health`: probe EVERY registered venue on the selected network — the same probe as
6
+ * `perp portfolio --health` and the MCP health_check (src/venue-health.ts). A venue with no
7
+ * environment on that network is listed under `skipped`, never silently dropped.
8
+ *
9
+ * JSON: `exchanges` = official-API venues, `unofficial` = venues read through an undocumented API
10
+ * (TxFlow), each entry with `error` (null when ok). `healthy` = every OFFICIAL venue is up — an
11
+ * unofficial API being down does not make the CLI unhealthy; `allHealthy` includes them.
12
+ */
13
+ export declare function runHealthCheck(isJson: () => boolean, network: Network): Promise<void>;
5
14
  export declare function registerRiskCommands(program: Command, getAdapterForExchange: (exchange: string) => Promise<ExchangeAdapter>, isJson: () => boolean): void;
@@ -2,61 +2,55 @@ import { listConfiguredAccountVenues } from "../config.js";
2
2
  import chalk from "chalk";
3
3
  import { printJson, jsonOk, makeTable, formatUsd, withJsonErrors } from "../utils.js";
4
4
  import { loadRiskLimits, saveRiskLimits, assessRisk, getLiquidationDistances } from "../risk.js";
5
- import { pingPacifica, pingHyperliquid, pingLighter } from "../shared-api.js";
6
- /** Run health check and return results + render. Exported for use by status --health. */
7
- export async function runHealthCheck(isJson) {
8
- const pingAster = async () => {
9
- const start = Date.now();
10
- try {
11
- const res = await fetch("https://fapi.asterdex.com/fapi/v1/time");
12
- return { ok: res.ok, latencyMs: Date.now() - start, status: res.status };
13
- }
14
- catch {
15
- return { ok: false, latencyMs: Date.now() - start, status: 0 };
16
- }
17
- };
18
- const [pacPing, hlPing, ltPing, astPing] = await Promise.all([
19
- pingPacifica(),
20
- pingHyperliquid(),
21
- pingLighter(),
22
- pingAster(),
23
- ]);
24
- const toPingResult = (name, p) => ({
25
- exchange: name,
5
+ import { probeVenueHealth } from "../venue-health.js";
6
+ import { networkOf } from "../cli-flags.js";
7
+ /**
8
+ * `perp health`: probe EVERY registered venue on the selected network — the same probe as
9
+ * `perp portfolio --health` and the MCP health_check (src/venue-health.ts). A venue with no
10
+ * environment on that network is listed under `skipped`, never silently dropped.
11
+ *
12
+ * JSON: `exchanges` = official-API venues, `unofficial` = venues read through an undocumented API
13
+ * (TxFlow), each entry with `error` (null when ok). `healthy` = every OFFICIAL venue is up — an
14
+ * unofficial API being down does not make the CLI unhealthy; `allHealthy` includes them.
15
+ */
16
+ export async function runHealthCheck(isJson, network) {
17
+ const health = await probeVenueHealth(network);
18
+ const checks = health.probes.map(p => ({
19
+ exchange: p.venue,
26
20
  status: p.ok ? "ok" : "down",
27
21
  latency_ms: p.latencyMs,
28
- error: p.ok ? undefined : `HTTP ${p.status}`,
29
- });
30
- const checks = [
31
- toPingResult("pacifica", pacPing),
32
- toPingResult("hyperliquid", hlPing),
33
- toPingResult("lighter", ltPing),
34
- toPingResult("aster", astPing),
35
- ];
36
- const allOk = checks.every(c => c.status === "ok");
22
+ error: p.error,
23
+ probe: p.probe,
24
+ apiStatus: p.apiStatus,
25
+ }));
26
+ const official = checks.filter(c => c.apiStatus === "official");
27
+ const unofficial = checks.filter(c => c.apiStatus === "unofficial");
28
+ const allOk = official.every(c => c.status === "ok");
37
29
  if (isJson()) {
38
- return printJson(jsonOk({ healthy: allOk, exchanges: checks }));
30
+ return printJson(jsonOk({ healthy: allOk, allHealthy: health.allHealthy, network, exchanges: official, unofficial, skipped: health.skipped }));
39
31
  }
40
- console.log(chalk.cyan.bold("\n Exchange Health Check\n"));
32
+ console.log(chalk.cyan.bold(`\n Exchange Health Check (${network})\n`));
41
33
  const rows = checks.map(c => {
42
- const statusIcon = c.status === "ok"
43
- ? chalk.green("OK")
44
- : c.status === "degraded"
45
- ? chalk.yellow("DEGRADED")
46
- : chalk.red("DOWN");
34
+ const statusIcon = c.status === "ok" ? chalk.green("OK") : chalk.red("DOWN");
47
35
  const latency = c.latency_ms < 500
48
36
  ? chalk.green(`${c.latency_ms}ms`)
49
37
  : c.latency_ms < 2000
50
38
  ? chalk.yellow(`${c.latency_ms}ms`)
51
39
  : chalk.red(`${c.latency_ms}ms`);
52
40
  return [
53
- chalk.white.bold(c.exchange),
41
+ chalk.white.bold(c.exchange) + (c.apiStatus === "unofficial" ? chalk.yellow(" *") : ""),
54
42
  statusIcon,
55
43
  latency,
56
- c.error ? chalk.red(c.error) : chalk.gray("-"),
44
+ c.probe,
45
+ c.error !== null ? chalk.red(c.error) : chalk.gray("-"),
57
46
  ];
58
47
  });
59
- console.log(makeTable(["Exchange", "Status", "Latency", "Error"], rows));
48
+ console.log(makeTable(["Exchange", "Status", "Latency", "Probe", "Error"], rows));
49
+ if (unofficial.length > 0) {
50
+ console.log(chalk.yellow(" * unofficial (undocumented) venue API — may change without notice; not counted in Overall"));
51
+ }
52
+ for (const s of health.skipped)
53
+ console.log(chalk.gray(` ${s.venue}: not probed — ${s.reason}`));
60
54
  const overall = allOk ? chalk.green("ALL HEALTHY") : chalk.red("ISSUES DETECTED");
61
55
  console.log(`\n Overall: ${overall}\n`);
62
56
  }
@@ -122,11 +116,12 @@ export function registerRiskCommands(program, getAdapterForExchange, isJson) {
122
116
  console.log(` Positions: ${assessment.metrics.positionCount}`);
123
117
  console.log(` Largest Position: $${formatUsd(assessment.metrics.largestPositionUsd)}`);
124
118
  console.log(` Max Leverage Used: ${assessment.metrics.maxLeverageUsed}x`);
125
- if (assessment.metrics.minLiquidationDistancePct >= 0) {
126
- const ldColor = assessment.metrics.minLiquidationDistancePct < assessment.limits.minLiquidationDistance
119
+ const minLiq = assessment.metrics.minLiquidationDistancePct;
120
+ if (minLiq !== null) {
121
+ const ldColor = minLiq < assessment.limits.minLiquidationDistance
127
122
  ? chalk.red
128
123
  : chalk.green;
129
- console.log(` Min Liq Distance: ${ldColor(`${assessment.metrics.minLiquidationDistancePct.toFixed(1)}%`)}`);
124
+ console.log(` Min Liq Distance: ${ldColor(`${minLiq.toFixed(1)}%`)}`);
130
125
  }
131
126
  for (const { exchange, position: p } of positions.filter(x => x.position.liquidationPrice === null)) {
132
127
  console.log(` Liq Price ${exchange}:${p.symbol}: ${chalk.yellow("unknown (venue does not report)")} — not in Min Liq Distance`);
@@ -367,6 +362,6 @@ export function registerRiskCommands(program, getAdapterForExchange, isJson) {
367
362
  .command("health")
368
363
  .description("Adapter health check: ping venue REST endpoints (connectivity + latency)")
369
364
  .action(async () => {
370
- await runHealthCheck(isJson);
365
+ await runHealthCheck(isJson, networkOf(program));
371
366
  });
372
367
  }
@@ -3,6 +3,7 @@ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  import { PerpError } from "../errors.js";
4
4
  import "../exchanges/venues/index.js";
5
5
  import { type CancelResult } from "../exchanges/venue-types.js";
6
+ export declare function parseSlippageBpsFlag(raw: string | undefined): number | null;
6
7
  /**
7
8
  * A cancel with anything not proven cancelled (failed, or pending / still listed open after the
8
9
  * verifying re-list) → ORDER_PARTIAL carrying the whole result (non-zero exit, also in --json);