perp-cli 0.14.3 → 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.
- package/README.md +152 -13
- package/dist/agent-wallet/store.d.ts +3 -2
- package/dist/agent-wallet/store.js +2 -2
- package/dist/api/public/hyperliquid.js +4 -0
- package/dist/api/public/pacifica.js +83 -13
- package/dist/api/public/urls.d.ts +2 -0
- package/dist/api/public/urls.js +3 -0
- package/dist/arb/utils.d.ts +1 -2
- package/dist/arb/utils.js +1 -2
- package/dist/bot/apex/pulse.js +3 -0
- package/dist/bot/apex/radar.js +2 -1
- package/dist/bot/engine.d.ts +10 -1
- package/dist/bot/engine.js +14 -6
- package/dist/bot/indicators.d.ts +7 -0
- package/dist/bot/indicators.js +16 -0
- package/dist/bot/perp-order.d.ts +4 -4
- package/dist/bot/perp-order.js +5 -5
- package/dist/bot/quarantine.d.ts +1 -1
- package/dist/bot/quarantine.js +1 -1
- package/dist/bot/strategies/apex-strategy.js +3 -2
- package/dist/bot/strategies/engine-mm.js +6 -5
- package/dist/bot/strategies/funding-arb-strategy.js +2 -2
- package/dist/bot/strategies/funding-arb-utils.d.ts +1 -1
- package/dist/bot/strategies/funding-arb-utils.js +5 -5
- package/dist/bot/strategies/funding-auto.js +2 -2
- package/dist/bot/strategies/liquidation-mm.d.ts +1 -0
- package/dist/bot/strategies/liquidation-mm.js +8 -2
- package/dist/bot/strategies/momentum-breakout.d.ts +1 -0
- package/dist/bot/strategies/momentum-breakout.js +11 -3
- package/dist/bot/strategies/spot-perp-arb-strategy.js +2 -2
- package/dist/bot/strategies/spot-perp-executor.d.ts +1 -1
- package/dist/bot/strategies/spot-perp-executor.js +1 -1
- package/dist/bot/strategy-types.d.ts +2 -1
- package/dist/bot/tui/BotDashboard.d.ts +1 -1
- package/dist/bot/tui/BotDashboard.js +1 -1
- package/dist/cache.js +12 -6
- package/dist/cli-program.d.ts +15 -0
- package/dist/cli-program.js +125 -5
- package/dist/commands/agent-import.d.ts +7 -0
- package/dist/commands/agent-import.js +30 -1
- package/dist/commands/agent.d.ts +6 -3
- package/dist/commands/agent.js +160 -12
- package/dist/commands/arb/index.js +4 -4
- package/dist/commands/arb-auto.js +21 -17
- package/dist/commands/arb.d.ts +12 -0
- package/dist/commands/arb.js +61 -143
- package/dist/commands/bot.js +1 -1
- package/dist/commands/builder.js +1 -1
- package/dist/commands/funds.js +113 -3
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.js +27 -7
- package/dist/commands/manage.js +23 -21
- package/dist/commands/market.js +167 -35
- package/dist/commands/options.d.ts +9 -0
- package/dist/commands/options.js +134 -0
- package/dist/commands/pacifica-earn.d.ts +5 -0
- package/dist/commands/pacifica-earn.js +468 -0
- package/dist/commands/pacifica-orders.d.ts +14 -0
- package/dist/commands/pacifica-orders.js +142 -0
- package/dist/commands/portfolio.js +32 -47
- package/dist/commands/rebalance.d.ts +1 -0
- package/dist/commands/rebalance.js +15 -0
- package/dist/commands/risk.d.ts +11 -2
- package/dist/commands/risk.js +39 -44
- package/dist/commands/trade.d.ts +1 -0
- package/dist/commands/trade.js +63 -58
- package/dist/commands/wallet.js +1 -1
- package/dist/config.d.ts +2 -2
- package/dist/config.js +15 -4
- package/dist/constants.js +6 -0
- package/dist/dashboard/server.js +1 -1
- package/dist/errors.d.ts +24 -1
- package/dist/errors.js +25 -3
- package/dist/exchanges/arcus/adapter.d.ts +7 -7
- package/dist/exchanges/arcus/adapter.js +12 -12
- package/dist/exchanges/arcus/agent.d.ts +1 -1
- package/dist/exchanges/arcus/agent.js +1 -1
- package/dist/exchanges/arcus/client.js +1 -1
- package/dist/exchanges/arcus/signing.d.ts +2 -3
- package/dist/exchanges/arcus/signing.js +1 -1
- package/dist/exchanges/arcus/spot-chain.d.ts +1 -1
- package/dist/exchanges/arcus/spot-chain.js +1 -1
- package/dist/exchanges/arcus/spot-router.d.ts +1 -1
- package/dist/exchanges/arcus/spot-router.js +2 -2
- package/dist/exchanges/arcus/spot.d.ts +2 -3
- package/dist/exchanges/arcus/spot.js +2 -3
- package/dist/exchanges/aster-spot.d.ts +3 -3
- package/dist/exchanges/aster-spot.js +6 -9
- package/dist/exchanges/aster-typed-data.d.ts +1 -1
- package/dist/exchanges/aster-typed-data.js +1 -1
- package/dist/exchanges/aster.d.ts +4 -4
- package/dist/exchanges/aster.js +31 -13
- package/dist/exchanges/builder-fee.d.ts +1 -1
- package/dist/exchanges/builder-fee.js +13 -2
- package/dist/exchanges/canonical-symbols.d.ts +1 -2
- package/dist/exchanges/canonical-symbols.js +26 -2
- package/dist/exchanges/derive/client.d.ts +37 -0
- package/dist/exchanges/derive/client.js +135 -0
- package/dist/exchanges/derive/options.d.ts +68 -0
- package/dist/exchanges/derive/options.js +360 -0
- package/dist/exchanges/extended/adapter.d.ts +82 -0
- package/dist/exchanges/extended/adapter.js +179 -0
- package/dist/exchanges/extended/client.d.ts +38 -0
- package/dist/exchanges/extended/client.js +88 -0
- package/dist/exchanges/extended/public.d.ts +130 -0
- package/dist/exchanges/extended/public.js +429 -0
- package/dist/exchanges/grvt/adapter.d.ts +86 -0
- package/dist/exchanges/grvt/adapter.js +206 -0
- package/dist/exchanges/grvt/client.d.ts +25 -0
- package/dist/exchanges/grvt/client.js +86 -0
- package/dist/exchanges/grvt/public.d.ts +151 -0
- package/dist/exchanges/grvt/public.js +438 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
- package/dist/exchanges/hyperliquid-spot.js +5 -5
- package/dist/exchanges/hyperliquid.d.ts +22 -12
- package/dist/exchanges/hyperliquid.js +80 -34
- package/dist/exchanges/index.d.ts +1 -1
- package/dist/exchanges/index.js +1 -1
- package/dist/exchanges/interface.d.ts +94 -6
- package/dist/exchanges/interface.js +16 -0
- package/dist/exchanges/ioc-outcome.d.ts +1 -1
- package/dist/exchanges/ioc-outcome.js +1 -1
- package/dist/exchanges/legacy-trading-v2.d.ts +2 -2
- package/dist/exchanges/legacy-trading-v2.js +7 -2
- package/dist/exchanges/lighter-public.d.ts +2 -2
- package/dist/exchanges/lighter-public.js +2 -2
- package/dist/exchanges/lighter-signer.d.ts +1 -1
- package/dist/exchanges/lighter-signer.js +1 -1
- package/dist/exchanges/lighter-spot.d.ts +3 -4
- package/dist/exchanges/lighter-spot.js +6 -7
- package/dist/exchanges/lighter.d.ts +11 -11
- package/dist/exchanges/lighter.js +28 -14
- package/dist/exchanges/ondo/adapter.d.ts +5 -6
- package/dist/exchanges/ondo/adapter.js +16 -14
- package/dist/exchanges/ondo/agent.d.ts +1 -1
- package/dist/exchanges/ondo/agent.js +1 -1
- package/dist/exchanges/ondo/client.d.ts +1 -1
- package/dist/exchanges/ondo/client.js +2 -2
- package/dist/exchanges/ondo/public.d.ts +8 -1
- package/dist/exchanges/ondo/public.js +13 -2
- package/dist/exchanges/ondo/spot.d.ts +2 -2
- package/dist/exchanges/ondo/spot.js +3 -3
- package/dist/exchanges/options-interface.d.ts +158 -0
- package/dist/exchanges/options-interface.js +17 -0
- package/dist/exchanges/options-venues.d.ts +12 -0
- package/dist/exchanges/options-venues.js +35 -0
- package/dist/exchanges/pacifica-atomic.d.ts +211 -0
- package/dist/exchanges/pacifica-atomic.js +440 -0
- package/dist/exchanges/pacifica-decimal.d.ts +10 -0
- package/dist/exchanges/pacifica-decimal.js +51 -0
- package/dist/exchanges/pacifica-presigned.d.ts +53 -0
- package/dist/exchanges/pacifica-presigned.js +141 -0
- package/dist/exchanges/pacifica-print.d.ts +200 -0
- package/dist/exchanges/pacifica-print.js +417 -0
- package/dist/exchanges/pacifica-spot.d.ts +2 -2
- package/dist/exchanges/pacifica-spot.js +3 -3
- package/dist/exchanges/pacifica-typed-data.d.ts +1 -1
- package/dist/exchanges/pacifica-typed-data.js +1 -1
- package/dist/exchanges/pacifica-vaults.d.ts +121 -0
- package/dist/exchanges/pacifica-vaults.js +264 -0
- package/dist/exchanges/pacifica.d.ts +32 -5
- package/dist/exchanges/pacifica.js +114 -48
- package/dist/exchanges/precision.d.ts +3 -1
- package/dist/exchanges/precision.js +9 -2
- package/dist/exchanges/qfex/adapter.d.ts +18 -5
- package/dist/exchanges/qfex/adapter.js +32 -12
- package/dist/exchanges/qfex/agent.d.ts +1 -1
- package/dist/exchanges/qfex/agent.js +1 -1
- package/dist/exchanges/qfex/client.d.ts +1 -1
- package/dist/exchanges/qfex/client.js +1 -1
- package/dist/exchanges/qfex/errors.d.ts +1 -1
- package/dist/exchanges/qfex/errors.js +2 -2
- package/dist/exchanges/qfex/public.d.ts +3 -3
- package/dist/exchanges/qfex/public.js +3 -3
- package/dist/exchanges/qfex/signing.d.ts +2 -2
- package/dist/exchanges/qfex/signing.js +4 -5
- package/dist/exchanges/qfex/ws-transport.d.ts +2 -2
- package/dist/exchanges/qfex/ws-transport.js +3 -3
- package/dist/exchanges/rate-limiter.d.ts +1 -1
- package/dist/exchanges/rate-limiter.js +5 -2
- package/dist/exchanges/registry.d.ts +1 -1
- package/dist/exchanges/registry.js +1 -1
- package/dist/exchanges/risex/adapter.d.ts +148 -0
- package/dist/exchanges/risex/adapter.js +796 -0
- package/dist/exchanges/risex/agent.d.ts +154 -0
- package/dist/exchanges/risex/agent.js +384 -0
- package/dist/exchanges/risex/client.d.ts +47 -0
- package/dist/exchanges/risex/client.js +174 -0
- package/dist/exchanges/risex/nonce.d.ts +43 -0
- package/dist/exchanges/risex/nonce.js +212 -0
- package/dist/exchanges/risex/public.d.ts +64 -0
- package/dist/exchanges/risex/public.js +150 -0
- package/dist/exchanges/risex/session.d.ts +71 -0
- package/dist/exchanges/risex/session.js +171 -0
- package/dist/exchanges/risex/typed-data.d.ts +173 -0
- package/dist/exchanges/risex/typed-data.js +240 -0
- package/dist/exchanges/risex/units.d.ts +29 -0
- package/dist/exchanges/risex/units.js +104 -0
- package/dist/exchanges/slippage.d.ts +1 -1
- package/dist/exchanges/slippage.js +1 -1
- package/dist/exchanges/spot-factory.d.ts +3 -3
- package/dist/exchanges/spot-factory.js +7 -7
- package/dist/exchanges/spot-interface.d.ts +5 -5
- package/dist/exchanges/spot-interface.js +2 -2
- package/dist/exchanges/spot-result.d.ts +3 -3
- package/dist/exchanges/spot-result.js +3 -3
- package/dist/exchanges/trading-v2.d.ts +3 -3
- package/dist/exchanges/trading-v2.js +9 -6
- package/dist/exchanges/txflow/adapter.d.ts +54 -0
- package/dist/exchanges/txflow/adapter.js +118 -0
- package/dist/exchanges/txflow/client.d.ts +47 -0
- package/dist/exchanges/txflow/client.js +153 -0
- package/dist/exchanges/txflow/public.d.ts +76 -0
- package/dist/exchanges/txflow/public.js +284 -0
- package/dist/exchanges/unknown-symbol.d.ts +29 -0
- package/dist/exchanges/unknown-symbol.js +51 -0
- package/dist/exchanges/variational/adapter.d.ts +71 -0
- package/dist/exchanges/variational/adapter.js +137 -0
- package/dist/exchanges/variational/client.d.ts +21 -0
- package/dist/exchanges/variational/client.js +66 -0
- package/dist/exchanges/variational/public.d.ts +126 -0
- package/dist/exchanges/variational/public.js +430 -0
- package/dist/exchanges/venue-types.d.ts +6 -7
- package/dist/exchanges/venue-types.js +2 -2
- package/dist/exchanges/venue.d.ts +51 -8
- package/dist/exchanges/venue.js +54 -4
- package/dist/exchanges/venues/arcus.d.ts +1 -2
- package/dist/exchanges/venues/arcus.js +5 -4
- package/dist/exchanges/venues/aster.d.ts +2 -2
- package/dist/exchanges/venues/aster.js +6 -4
- package/dist/exchanges/venues/create-helpers.d.ts +1 -1
- package/dist/exchanges/venues/create-helpers.js +3 -3
- package/dist/exchanges/venues/extended.d.ts +21 -0
- package/dist/exchanges/venues/extended.js +96 -0
- package/dist/exchanges/venues/grvt.d.ts +30 -0
- package/dist/exchanges/venues/grvt.js +98 -0
- package/dist/exchanges/venues/hyperliquid.d.ts +2 -2
- package/dist/exchanges/venues/hyperliquid.js +10 -8
- package/dist/exchanges/venues/index.d.ts +7 -2
- package/dist/exchanges/venues/index.js +8 -2
- package/dist/exchanges/venues/lighter-family.d.ts +7 -9
- package/dist/exchanges/venues/lighter-family.js +9 -11
- package/dist/exchanges/venues/lighter-rh.d.ts +2 -2
- package/dist/exchanges/venues/lighter-rh.js +7 -5
- package/dist/exchanges/venues/lighter.d.ts +1 -1
- package/dist/exchanges/venues/lighter.js +5 -3
- package/dist/exchanges/venues/ondo.d.ts +1 -1
- package/dist/exchanges/venues/ondo.js +5 -3
- package/dist/exchanges/venues/pacifica.d.ts +2 -2
- package/dist/exchanges/venues/pacifica.js +6 -4
- package/dist/exchanges/venues/qfex.d.ts +1 -1
- package/dist/exchanges/venues/qfex.js +4 -2
- package/dist/exchanges/venues/risex-release.d.ts +14 -0
- package/dist/exchanges/venues/risex-release.js +14 -0
- package/dist/exchanges/venues/risex.d.ts +31 -0
- package/dist/exchanges/venues/risex.js +116 -0
- package/dist/exchanges/venues/txflow.d.ts +18 -0
- package/dist/exchanges/venues/txflow.js +77 -0
- package/dist/exchanges/venues/variational.d.ts +21 -0
- package/dist/exchanges/venues/variational.js +85 -0
- package/dist/execution-log.d.ts +1 -1
- package/dist/funding/legacy-public.d.ts +1 -1
- package/dist/funding/legacy-public.js +9 -4
- package/dist/funding/normalize.d.ts +1 -1
- package/dist/funding/normalize.js +1 -1
- package/dist/funding/rates.d.ts +44 -4
- package/dist/funding/rates.js +56 -12
- package/dist/index.js +7 -6
- package/dist/jobs.d.ts +1 -0
- package/dist/jobs.js +25 -1
- package/dist/landing.d.ts +2 -0
- package/dist/landing.js +1 -1
- package/dist/mcp-pacifica-earn.d.ts +9 -0
- package/dist/mcp-pacifica-earn.js +58 -0
- package/dist/mcp-server.js +113 -59
- package/dist/pacifica/client.d.ts +67 -4
- package/dist/pacifica/client.js +170 -8
- package/dist/pacifica/rate-limit.d.ts +144 -0
- package/dist/pacifica/rate-limit.js +267 -0
- package/dist/pacifica/types/order.d.ts +2 -1
- package/dist/plan-executor.js +7 -2
- package/dist/rebalance.js +1 -1
- package/dist/risk.d.ts +2 -1
- package/dist/risk.js +2 -4
- package/dist/settings.d.ts +32 -3
- package/dist/signer/ows-evm.d.ts +1 -1
- package/dist/signer/ows-evm.js +1 -1
- package/dist/smart-order.d.ts +1 -1
- package/dist/smart-order.js +1 -1
- package/dist/strategies/funding-arb.d.ts +1 -1
- package/dist/strategies/funding-arb.js +1 -1
- package/dist/trade-validator.js +2 -2
- package/dist/utils.d.ts +12 -3
- package/dist/utils.js +11 -3
- package/dist/venue-health.d.ts +40 -0
- package/dist/venue-health.js +75 -0
- package/dist/withdraw-gate.d.ts +1 -1
- package/dist/withdraw-gate.js +1 -1
- package/dist/ws/index.d.ts +959 -0
- package/dist/ws/index.js +4295 -0
- package/dist/ws/node.d.ts +975 -0
- package/dist/ws/node.js +4366 -0
- package/dist/ws/pacifica-instrument.d.ts +8 -0
- package/dist/ws/pacifica-instrument.js +8 -0
- package/dist/ws-cli/credentials.d.ts +62 -0
- package/dist/ws-cli/credentials.js +126 -0
- package/package.json +58 -10
- package/skills/perp-cli/SKILL.md +62 -9
- package/skills/perp-cli/references/agent-operations.md +7 -0
- package/skills/perp-cli/references/commands.md +76 -5
- package/skills/perp-cli/references/ws-library.md +492 -0
package/dist/mcp-server.js
CHANGED
|
@@ -23,7 +23,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
23
23
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
24
24
|
import { z } from "zod";
|
|
25
25
|
import { hasCredentials, listConfiguredAccountVenues, resolveCredentials, NO_CLI_CREDENTIAL_OPTS } from "./config.js";
|
|
26
|
-
import { getVenue, listVenues, listVenuesWithRole, networkConfig, READ_ONLY_CREDENTIALS, requireCapabilities } from "./exchanges/venue.js";
|
|
26
|
+
import { getVenue, listVenues, listVenuesWithRole, networkConfig, READ_ONLY_CREDENTIALS, requireCapabilities, roleUnsupported } from "./exchanges/venue.js";
|
|
27
27
|
import "./exchanges/venues/index.js";
|
|
28
28
|
import { newVenueClientOrderId, placeAndConfirm, validateLegacyTif, validateOrderRequest } from "./exchanges/trading-v2.js";
|
|
29
29
|
import { closePerpVerified, isOutcomeUnknown } from "./bot/perp-order.js";
|
|
@@ -32,7 +32,8 @@ import { fillSizeString, sizeDecimalsOf } from "./exchanges/spot-result.js";
|
|
|
32
32
|
import { tradingV2Of } from "./exchanges/legacy-trading-v2.js";
|
|
33
33
|
import { PerpError, classifyError, extractErrorMessage, isDefiniteOrderFailure, orderOutcomeUnknown, parsePositiveDecimal } from "./errors.js";
|
|
34
34
|
import { invalidateCache } from "./cache.js";
|
|
35
|
-
import {
|
|
35
|
+
import { probeVenueHealth } from "./venue-health.js";
|
|
36
|
+
import { getOptionsVenue, listOptionsVenueIds } from "./exchanges/options-venues.js";
|
|
36
37
|
import { fetchAllFundingRates } from "./funding-rates.js";
|
|
37
38
|
import { loadArbState } from "./arb/state.js";
|
|
38
39
|
import { validateTrade } from "./trade-validator.js";
|
|
@@ -41,6 +42,7 @@ import { logMcpExecution, mcpPreTradeGate, mcpReduceOnlyGate, resolveMcpNetwork
|
|
|
41
42
|
import { setSharedApiNetwork } from "./api/public/urls.js";
|
|
42
43
|
import { logClientId } from "./client-id-tracker.js";
|
|
43
44
|
import { loadSettings } from "./settings.js";
|
|
45
|
+
import { registerPacificaEarnMcpTools } from "./mcp-pacifica-earn.js";
|
|
44
46
|
/**
|
|
45
47
|
* The one network this server serves: PERP_NETWORK, validated like `-n` (an invalid value
|
|
46
48
|
* makes the server refuse to start). Unset = mainnet, the documented default. Reported in
|
|
@@ -52,8 +54,8 @@ setQuarantineNetwork(MCP_NETWORK);
|
|
|
52
54
|
setSharedApiNetwork(MCP_NETWORK);
|
|
53
55
|
// ── Adapter cache & factory ──
|
|
54
56
|
//
|
|
55
|
-
// Same creation path and signer tiers as the CLI (agent → OWS master → PK
|
|
56
|
-
//
|
|
57
|
+
// Same creation path and signer tiers as the CLI (agent → OWS master → PK).
|
|
58
|
+
// The MCP server has no CLI flags, so credentials come
|
|
57
59
|
// from the registered agent, settings.owsActiveWallet, and (legacy venues) env.
|
|
58
60
|
const adapters = new Map();
|
|
59
61
|
/** The network each adapter was created on (TradingV2 wrappers must use the same one). */
|
|
@@ -71,6 +73,9 @@ async function getOrCreateAdapter(exchange) {
|
|
|
71
73
|
const cached = adapters.get(def.id);
|
|
72
74
|
if (cached)
|
|
73
75
|
return cached;
|
|
76
|
+
// Market-data-only venue (TxFlow: unofficial read-only API): every account / trading tool refuses here.
|
|
77
|
+
if (!def.roles.includes("account"))
|
|
78
|
+
throw roleUnsupported(def, "account");
|
|
74
79
|
const create = requireCreate(def);
|
|
75
80
|
if (!(await hasCredentials(def.id, NO_CLI_CREDENTIAL_OPTS))) {
|
|
76
81
|
throw new PerpError("NO_SIGNER_AVAILABLE", `No credentials configured for ${def.id}`, {
|
|
@@ -118,9 +123,35 @@ async function tryGetAdapter(exchange) {
|
|
|
118
123
|
throw e;
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
|
-
/**
|
|
126
|
+
/**
|
|
127
|
+
* Fetch markets using the venue's public data path (no key needed). Unofficial-API rows carry
|
|
128
|
+
* venueApi; rows of a venue whose funding is unverified (QFEX, GRVT, TxFlow) carry fundingVerified false.
|
|
129
|
+
*/
|
|
122
130
|
async function fetchMarketsPublic(exchange) {
|
|
123
|
-
return getVenue(exchange).publicData.fetchMarkets(MCP_NETWORK);
|
|
131
|
+
return labelMarketRows(exchange, await getVenue(exchange).publicData.fetchMarkets(MCP_NETWORK));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The one row label for market rows of any source (public data path or a credentialed adapter):
|
|
135
|
+
* unofficial-API rows carry venueApi + fundingVerified; unverified-funding rows carry fundingVerified false.
|
|
136
|
+
*/
|
|
137
|
+
function labelMarketRows(exchange, rows) {
|
|
138
|
+
const def = getVenue(exchange);
|
|
139
|
+
if (def.apiStatus.kind === "unofficial")
|
|
140
|
+
return rows.map(r => ({ ...r, venueApi: "unofficial", fundingVerified: def.fundingVerified }));
|
|
141
|
+
return def.fundingVerified ? rows : rows.map(r => ({ ...r, fundingVerified: false }));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* meta fields every market-data tool adds for its venue: apiStatus, the note for an unofficial API,
|
|
145
|
+
* and fundingVerified false (with a note) when the venue's funding rate is unverified.
|
|
146
|
+
*/
|
|
147
|
+
function apiMeta(exchange) {
|
|
148
|
+
const def = getVenue(exchange);
|
|
149
|
+
if (def.apiStatus.kind === "unofficial") {
|
|
150
|
+
return { apiStatus: "unofficial", apiNote: `${def.displayName}: UNOFFICIAL API — ${def.apiStatus.note}`, fundingVerified: def.fundingVerified };
|
|
151
|
+
}
|
|
152
|
+
return def.fundingVerified
|
|
153
|
+
? { apiStatus: "official" }
|
|
154
|
+
: { apiStatus: "official", fundingVerified: false, fundingNote: `${def.displayName}: funding rate UNVERIFIED (not confirmed against settled payments) — display only, never a basis for trading` };
|
|
124
155
|
}
|
|
125
156
|
/** One book level from a venue payload; a level without a string price / size is a payload error (never "0"). */
|
|
126
157
|
function bookLevel(venue, symbol, price, size) {
|
|
@@ -219,6 +250,8 @@ export const MCP_WITHDRAW_VENUES = ["pacifica", "hyperliquid", "lighter", "aster
|
|
|
219
250
|
const _require = createRequire(import.meta.url);
|
|
220
251
|
const _pkg = _require("../package.json");
|
|
221
252
|
const server = new McpServer({ name: "perp-cli", version: _pkg.version }, { capabilities: { tools: {}, resources: {}, prompts: {} } });
|
|
253
|
+
// Pacifica Print + vaults: read-only tools (src/mcp-pacifica-earn.ts).
|
|
254
|
+
registerPacificaEarnMcpTools(server, MCP_NETWORK, ok, errOf);
|
|
222
255
|
// ============================================================
|
|
223
256
|
// Market Data tools (read-only, no private key needed)
|
|
224
257
|
// ============================================================
|
|
@@ -227,11 +260,11 @@ server.tool("get_markets", `Get all available perpetual futures markets on an ex
|
|
|
227
260
|
// Try adapter first (richer data), fall back to public API (no key needed)
|
|
228
261
|
const adapter = await tryGetAdapter(exchange);
|
|
229
262
|
if (adapter) {
|
|
230
|
-
const markets = await adapter.getMarkets();
|
|
231
|
-
return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length }) }] };
|
|
263
|
+
const markets = labelMarketRows(exchange, await adapter.getMarkets());
|
|
264
|
+
return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length, ...apiMeta(exchange) }) }] };
|
|
232
265
|
}
|
|
233
266
|
const markets = await fetchMarketsPublic(exchange);
|
|
234
|
-
return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length, source: "public" }) }] };
|
|
267
|
+
return { content: [{ type: "text", text: ok(markets, { exchange, count: markets.length, source: "public", ...apiMeta(exchange) }) }] };
|
|
235
268
|
}
|
|
236
269
|
catch (e) {
|
|
237
270
|
return { content: [{ type: "text", text: errOf(e, { exchange }) }], isError: true };
|
|
@@ -246,24 +279,25 @@ server.tool("get_orderbook", `Get the order book (bids and asks) for a symbol on
|
|
|
246
279
|
const adapter = await tryGetAdapter(exchange);
|
|
247
280
|
if (adapter) {
|
|
248
281
|
const book = await adapter.getOrderbook(symbol);
|
|
249
|
-
return { content: [{ type: "text", text: ok(book, { exchange, symbol }) }] };
|
|
282
|
+
return { content: [{ type: "text", text: ok(book, { exchange, symbol, ...apiMeta(exchange) }) }] };
|
|
250
283
|
}
|
|
251
284
|
const book = await fetchOrderbookPublic(exchange, symbol);
|
|
252
|
-
return { content: [{ type: "text", text: ok(book, { exchange, symbol, source: "public" }) }] };
|
|
285
|
+
return { content: [{ type: "text", text: ok(book, { exchange, symbol, source: "public", ...apiMeta(exchange) }) }] };
|
|
253
286
|
}
|
|
254
287
|
catch (e) {
|
|
255
288
|
return { content: [{ type: "text", text: errOf(e, { exchange, symbol }) }], isError: true };
|
|
256
289
|
}
|
|
257
290
|
});
|
|
258
|
-
server.tool("get_funding_rates", "Compare funding rates across every market-data venue. Returns rates per symbol with spread analysis", {
|
|
291
|
+
server.tool("get_funding_rates", "Compare funding rates across every market-data venue whose funding rate is verified. Returns rates per symbol with spread analysis. includeUnverified also returns venues whose rate is unverified (QFEX; TxFlow — unofficial API; Variational — a predicted rate not checked against settled payments; GRVT — indicative rate; Extended — not checked against settled payments), each row tagged fundingVerified / apiStatus — display only, never a basis for trading (mirrors the CLI `arb scan --include-unverified`).", {
|
|
259
292
|
symbols: z
|
|
260
293
|
.array(z.string())
|
|
261
294
|
.optional()
|
|
262
295
|
.describe("Filter to specific symbols (e.g. ['BTC','ETH']). Omit for all available"),
|
|
263
296
|
minSpread: z.number().optional().describe("Minimum annualized spread % to include (default: 0)"),
|
|
264
|
-
|
|
297
|
+
includeUnverified: z.boolean().optional().describe("Also include venues whose funding rate is unverified (default false); rows carry fundingVerified:false / apiStatus"),
|
|
298
|
+
}, async ({ symbols, minSpread, includeUnverified }) => {
|
|
265
299
|
try {
|
|
266
|
-
const snapshot = await fetchAllFundingRates({ symbols, minSpread });
|
|
300
|
+
const snapshot = await fetchAllFundingRates({ symbols, minSpread, includeUnverified: includeUnverified === true });
|
|
267
301
|
return {
|
|
268
302
|
content: [{
|
|
269
303
|
type: "text",
|
|
@@ -275,7 +309,7 @@ server.tool("get_funding_rates", "Compare funding rates across every market-data
|
|
|
275
309
|
return { content: [{ type: "text", text: errOf(e) }], isError: true };
|
|
276
310
|
}
|
|
277
311
|
});
|
|
278
|
-
server.tool("get_prices", "Get cross-exchange prices for symbols. Fetches mark prices from every market-data venue for comparison", {
|
|
312
|
+
server.tool("get_prices", "Get cross-exchange prices for symbols. Fetches mark prices from every market-data venue for comparison; a venue appears in a row only when it lists that symbol (e.g. QFEX lists no BTC). Each row's unofficialApiVenues names the venues in it read through an undocumented API (TxFlow) — like get_markets' venueApi tag.", {
|
|
279
313
|
symbols: z
|
|
280
314
|
.array(z.string())
|
|
281
315
|
.optional()
|
|
@@ -303,8 +337,13 @@ server.tool("get_prices", "Get cross-exchange prices for symbols. Fetches mark p
|
|
|
303
337
|
priceMap.get(sym)[ex] = Number(m.markPrice);
|
|
304
338
|
}
|
|
305
339
|
});
|
|
306
|
-
const
|
|
307
|
-
|
|
340
|
+
const unofficial = listVenuesWithRole("marketData").filter(v => v.apiStatus.kind === "unofficial").map(v => v.id);
|
|
341
|
+
const data = Array.from(priceMap.entries()).map(([symbol, prices]) => ({
|
|
342
|
+
symbol,
|
|
343
|
+
prices,
|
|
344
|
+
unofficialApiVenues: Object.keys(prices).filter(ex => unofficial.includes(ex)),
|
|
345
|
+
}));
|
|
346
|
+
return { content: [{ type: "text", text: ok(data, { count: data.length, errors, unofficialApiVenues: unofficial }) }] };
|
|
308
347
|
}
|
|
309
348
|
catch (e) {
|
|
310
349
|
return { content: [{ type: "text", text: errOf(e) }], isError: true };
|
|
@@ -362,6 +401,44 @@ server.tool("get_outcome_book", "Get the orderbook for one outcome side. Public
|
|
|
362
401
|
}
|
|
363
402
|
});
|
|
364
403
|
// ============================================================
|
|
404
|
+
// Options market data (read-only, keyless; venues: src/exchanges/options-venues.ts)
|
|
405
|
+
// ============================================================
|
|
406
|
+
const OPTIONS_VENUE_ARG = z.string().describe(`Options venue: ${listOptionsVenueIds().join(", ")}`);
|
|
407
|
+
server.tool("get_option_expiries", "List the active option expiries of an underlying on an options venue (Derive): per expiry the UTC date (YYYY-MM-DD), expiry ms, strike and instrument counts, soonest first. Public read — no API key needed.", { venue: OPTIONS_VENUE_ARG, underlying: z.string().describe("Underlying, e.g. ETH, BTC") }, async ({ venue, underlying }) => {
|
|
408
|
+
try {
|
|
409
|
+
const data = await (await getOptionsVenue(venue, MCP_NETWORK)).listExpiries(underlying);
|
|
410
|
+
return { content: [{ type: "text", text: ok(data, { venue, count: data.expiries.length }) }] };
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
return { content: [{ type: "text", text: errOf(e, { venue, underlying }) }], isError: true };
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
server.tool("get_option_chain", "Option chain for one expiry (Derive): rows per strike with call and put quotes — bid / ask (price + size; null = empty side), mark, index, forward, IV (mark / bid / ask; null = not computable), greeks (delta, gamma, theta, vega, rho), open interest, 24h volume — plus the ATM strike. Decimals are strings. One venue request per expiry. Public read.", {
|
|
417
|
+
venue: OPTIONS_VENUE_ARG,
|
|
418
|
+
underlying: z.string().describe("Underlying, e.g. ETH, BTC"),
|
|
419
|
+
expiry: z.string().optional().describe("Expiry date YYYY-MM-DD (UTC); omit for the nearest active expiry (result expirySelection: \"nearest\")"),
|
|
420
|
+
strikes: z.number().int().min(1).optional().describe("Only the N strikes closest to the ATM reference (forward, else index); omit for all strikes"),
|
|
421
|
+
}, async ({ venue, underlying, expiry, strikes }) => {
|
|
422
|
+
try {
|
|
423
|
+
const chain = await (await getOptionsVenue(venue, MCP_NETWORK)).getChain({
|
|
424
|
+
underlying, expiry: expiry === undefined ? null : expiry, strikesAroundAtm: strikes === undefined ? null : strikes,
|
|
425
|
+
});
|
|
426
|
+
return { content: [{ type: "text", text: ok(chain, { venue, expiry: chain.expiry, rows: chain.rows.length }) }] };
|
|
427
|
+
}
|
|
428
|
+
catch (e) {
|
|
429
|
+
return { content: [{ type: "text", text: errOf(e, { venue, underlying }) }], isError: true };
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
server.tool("get_option_quote", "Quote one option instrument (Derive name, e.g. ETH-20261225-3500-P): bid / ask (price + size), mark, index, forward, IV, greeks, open interest, 24h stats. Unknown instrument → SYMBOL_NOT_FOUND. Public read.", { venue: OPTIONS_VENUE_ARG, instrument: z.string().describe("Venue instrument name, e.g. ETH-20261225-3500-P") }, async ({ venue, instrument }) => {
|
|
433
|
+
try {
|
|
434
|
+
const q = await (await getOptionsVenue(venue, MCP_NETWORK)).getQuote(instrument);
|
|
435
|
+
return { content: [{ type: "text", text: ok(q, { venue, instrument: q.instrument }) }] };
|
|
436
|
+
}
|
|
437
|
+
catch (e) {
|
|
438
|
+
return { content: [{ type: "text", text: errOf(e, { venue, instrument }) }], isError: true };
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
// ============================================================
|
|
365
442
|
// Account tools (need private key)
|
|
366
443
|
// ============================================================
|
|
367
444
|
server.tool("get_outcome_positions", "Get the active account's open outcome positions (size, entry notional, mark price, unrealized PnL). Requires a configured Hyperliquid wallet or agent.", {}, async () => {
|
|
@@ -1125,6 +1202,9 @@ server.tool("arb_scan", "Scan for funding rate arbitrage opportunities across ex
|
|
|
1125
1202
|
symbol: s.symbol,
|
|
1126
1203
|
maxSpreadAnnual: `${s.maxSpreadAnnual.toFixed(2)}%`,
|
|
1127
1204
|
strategy: `Long ${s.longExchange} / Short ${s.shortExchange}`,
|
|
1205
|
+
// false = a leg is on a read-only venue (RISEx in this release): data only, not tradable here.
|
|
1206
|
+
executable: s.executable,
|
|
1207
|
+
readOnlyLegs: s.readOnlyLegs,
|
|
1128
1208
|
estHourlyIncomePerK: `$${s.estHourlyIncomeUsd.toFixed(4)}`,
|
|
1129
1209
|
rates: s.rates.map(r => ({
|
|
1130
1210
|
exchange: r.exchange,
|
|
@@ -1149,44 +1229,14 @@ server.tool("arb_scan", "Scan for funding rate arbitrage opportunities across ex
|
|
|
1149
1229
|
});
|
|
1150
1230
|
server.tool("health_check", "Ping every registered venue on the server's network and return connectivity status and latency. Pacifica / Hyperliquid / Lighter / Aster / QFEX: REST ping (status = HTTP status, 0 = no response); every other venue: its public market-data read (status null). A failure carries its error message.", {}, async () => {
|
|
1151
1231
|
try {
|
|
1152
|
-
const
|
|
1153
|
-
const start = Date.now();
|
|
1154
|
-
try {
|
|
1155
|
-
const r = await fn();
|
|
1156
|
-
return { ok: r.ok, latencyMs: Date.now() - start, status: r.status, error: r.ok ? null : `HTTP ${r.status}`, probe };
|
|
1157
|
-
}
|
|
1158
|
-
catch (e) {
|
|
1159
|
-
return { ok: false, latencyMs: Date.now() - start, status: probe === "ping" ? 0 : null, error: extractErrorMessage(e), probe };
|
|
1160
|
-
}
|
|
1161
|
-
};
|
|
1162
|
-
const restPing = (url) => timed("ping", async () => {
|
|
1163
|
-
const res = await fetch(url);
|
|
1164
|
-
return { ok: res.ok, status: res.status };
|
|
1165
|
-
});
|
|
1166
|
-
// Shared-api helpers report a transport failure as status 0 (no message).
|
|
1167
|
-
const helperPing = (fn) => async () => {
|
|
1168
|
-
const r = await fn();
|
|
1169
|
-
return { ...r, error: r.ok ? null : r.status === 0 ? "no response" : `HTTP ${r.status}`, probe: "ping" };
|
|
1170
|
-
};
|
|
1171
|
-
const pings = {
|
|
1172
|
-
pacifica: helperPing(pingPacifica),
|
|
1173
|
-
hyperliquid: helperPing(pingHyperliquid),
|
|
1174
|
-
lighter: helperPing(pingLighter),
|
|
1175
|
-
aster: () => restPing(`${networkConfig(getVenue("aster"), MCP_NETWORK).restUrl}/fapi/v1/time`),
|
|
1176
|
-
// QFEX: public GET /refdata for one instrument on the server's network.
|
|
1177
|
-
qfex: () => restPing(`${networkConfig(getVenue("qfex"), MCP_NETWORK).restUrl}/refdata?ticker=AAPL-USD`),
|
|
1178
|
-
};
|
|
1179
|
-
const venues = listVenues();
|
|
1180
|
-
const probes = await Promise.all(venues.map(v => {
|
|
1181
|
-
const ping = pings[v.id];
|
|
1182
|
-
return ping !== undefined
|
|
1183
|
-
? ping()
|
|
1184
|
-
: timed("markets", async () => { await v.publicData.fetchMarkets(MCP_NETWORK); return { ok: true, status: null }; });
|
|
1185
|
-
}));
|
|
1232
|
+
const health = await probeVenueHealth(MCP_NETWORK);
|
|
1186
1233
|
const result = {};
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
result.
|
|
1234
|
+
for (const { venue, ...p } of health.probes)
|
|
1235
|
+
result[venue] = { ...p, statusText: p.ok ? "healthy" : "unreachable" };
|
|
1236
|
+
result.covered = health.probes.map(p => p.venue);
|
|
1237
|
+
// A venue without an environment on the server's network (TxFlow has no testnet) is not probed.
|
|
1238
|
+
result.skipped = health.skipped;
|
|
1239
|
+
result.allHealthy = health.allHealthy;
|
|
1190
1240
|
return { content: [{ type: "text", text: ok(result) }] };
|
|
1191
1241
|
}
|
|
1192
1242
|
catch (e) {
|
|
@@ -1446,7 +1496,7 @@ function mcpOrderRequest(adapter, p) {
|
|
|
1446
1496
|
};
|
|
1447
1497
|
}
|
|
1448
1498
|
server.tool("trade_preview", `Preview a trade WITHOUT executing. Runs trade_execute's pre-send checks read-only (size parse, order validation incl. TIF, quarantine, local risk limits — never forced) and reports any refusal as valid:false with its code + remediation; also returns the estimated fill price from the book, notional, margin impact, the builder fee the order would carry, and the existing position. Venue trading fees are NOT estimated. ALWAYS call this before trade_execute and show the result to the user for confirmation. ${HL_SCOPE_NOTE}`, {
|
|
1449
|
-
exchange: z.string().describe(`Exchange: ${listVenues().filter(v => v.
|
|
1499
|
+
exchange: z.string().describe(`Exchange: ${listVenues().filter(v => v.roles.includes("trading")).map(v => v.id).join(", ")} (aliases accepted)`),
|
|
1450
1500
|
symbol: z.string().describe("Trading symbol (e.g., BTC, ETH, SOL)"),
|
|
1451
1501
|
side: z.enum(["buy", "sell"]).describe("Order side"),
|
|
1452
1502
|
size: z.string().describe("Order size (base currency units, e.g., '0.1' for 0.1 BTC)"),
|
|
@@ -1456,6 +1506,8 @@ server.tool("trade_preview", `Preview a trade WITHOUT executing. Runs trade_exec
|
|
|
1456
1506
|
reduceOnly: z.boolean().default(false).describe("Reduce-only order (close position only) — checked like trade_execute (quarantine applies only to non-reduce-only orders)"),
|
|
1457
1507
|
}, async ({ exchange, symbol, side, size, orderType, price, tif, reduceOnly }) => {
|
|
1458
1508
|
try {
|
|
1509
|
+
// No trading role (TxFlow, release-gated RISEx) → CAPABILITY_UNSUPPORTED before any credential.
|
|
1510
|
+
requireCapabilities(getVenue(exchange));
|
|
1459
1511
|
const adapter = await getOrCreateAdapter(exchange);
|
|
1460
1512
|
const sym = symbol.toUpperCase();
|
|
1461
1513
|
const isLimit = orderType === "limit";
|
|
@@ -1570,9 +1622,9 @@ server.tool("trade_preview", `Preview a trade WITHOUT executing. Runs trade_exec
|
|
|
1570
1622
|
});
|
|
1571
1623
|
/**
|
|
1572
1624
|
* Place one order through TradingV2 (native on ondo/arcus, legacy wrapper on the four
|
|
1573
|
-
* pre-existing venues) and return OrderResult + execution
|
|
1625
|
+
* pre-existing venues) and return OrderResult + execution. Pre-send
|
|
1574
1626
|
* validation failures stay definite; anything thrown after the request may have reached
|
|
1575
|
-
* the venue becomes ORDER_PENDING ("do NOT resubmit"
|
|
1627
|
+
* the venue becomes ORDER_PENDING ("do NOT resubmit").
|
|
1576
1628
|
*/
|
|
1577
1629
|
async function placeViaV2(adapter, req, what, remediation) {
|
|
1578
1630
|
const def = getVenue(adapter.name);
|
|
@@ -1589,7 +1641,7 @@ async function placeViaV2(adapter, req, what, remediation) {
|
|
|
1589
1641
|
}
|
|
1590
1642
|
}
|
|
1591
1643
|
server.tool("trade_execute", `Execute a REAL trade on the server's network (meta.network; PERP_NETWORK, default mainnet). IMPORTANT: Always call trade_preview first and get explicit user confirmation before calling this tool. Refused with RISK_VIOLATION when the order breaches the local risk limits (perp risk set; there is no override over MCP) or when the symbol is quarantined (only reduce-only orders pass). Every order is written to the execution log. Supports market and limit orders. Returns the uniform OrderResult (status, finality, filledSize, execution: slippage / protection price / TIF sent) with the raw venue response under \`legacy\`, plus top-level filledSize / requestedSize / partial. Outcomes: an unknown outcome (transport failure after send, or a non-final venue ack) → ok:false, code ORDER_PENDING, executed:false, pending:true and a "do NOT resubmit" remediation — the symbol is quarantined for opening orders until a human clears it; a final rejection / cancel with nothing filled → ORDER_REJECTED, executed:false; a cancel after a partial fill → executed:true, partial:true with the real filledSize. ${HL_SCOPE_NOTE}`, {
|
|
1592
|
-
exchange: z.string().describe(`Exchange: ${listVenues().filter(v => v.
|
|
1644
|
+
exchange: z.string().describe(`Exchange: ${listVenues().filter(v => v.roles.includes("trading")).map(v => v.id).join(", ")} (aliases accepted)`),
|
|
1593
1645
|
symbol: z.string().describe("Trading symbol (e.g., BTC, ETH, SOL)"),
|
|
1594
1646
|
side: z.enum(["buy", "sell"]).describe("Order side"),
|
|
1595
1647
|
size: z.string().describe("Order size (base currency units)"),
|
|
@@ -1602,6 +1654,7 @@ server.tool("trade_execute", `Execute a REAL trade on the server's network (meta
|
|
|
1602
1654
|
try {
|
|
1603
1655
|
// One parsed size for the risk gate and the order ("1,000" / "abc" refused before any read or send).
|
|
1604
1656
|
const size = parsePositiveDecimal(rawSize, "size");
|
|
1657
|
+
requireCapabilities(getVenue(exchange)); // trading role first, before any credential
|
|
1605
1658
|
const adapter = await getOrCreateAdapter(exchange);
|
|
1606
1659
|
const def = getVenue(adapter.name);
|
|
1607
1660
|
const caps = requireCapabilities(def);
|
|
@@ -1722,10 +1775,11 @@ server.tool("trade_execute", `Execute a REAL trade on the server's network (meta
|
|
|
1722
1775
|
}
|
|
1723
1776
|
});
|
|
1724
1777
|
server.tool("trade_close", `Close an existing position. IMPORTANT: Call trade_preview first with the opposite side to show impact, then get user confirmation. Verified close: \`closedSize\` is the measured / venue-final fill (one residual market close is attempted); a residual still open → ORDER_PARTIAL error (symbol quarantined). Includes the uniform OrderResult of the reduce-only market order. ${HL_SCOPE_NOTE}`, {
|
|
1725
|
-
exchange: z.string().describe(`Exchange: ${listVenues().filter(v => v.
|
|
1778
|
+
exchange: z.string().describe(`Exchange: ${listVenues().filter(v => v.roles.includes("trading")).map(v => v.id).join(", ")} (aliases accepted)`),
|
|
1726
1779
|
symbol: z.string().describe("Symbol to close (e.g., BTC, ETH)"),
|
|
1727
1780
|
}, async ({ exchange, symbol }) => {
|
|
1728
1781
|
try {
|
|
1782
|
+
requireCapabilities(getVenue(exchange)); // trading role first, before any credential
|
|
1729
1783
|
const adapter = await getOrCreateAdapter(exchange);
|
|
1730
1784
|
const def = getVenue(adapter.name);
|
|
1731
1785
|
const caps = requireCapabilities(def);
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { type Network } from "./constants.js";
|
|
2
|
-
import type { MarketInfo, PriceInfo, Orderbook, Trade, Kline, KlineInterval, AggLevel, AccountInfo, AccountSettings, Position, OrderInfo, MarketOrderParams, LimitOrderParams, StopOrderParams, EditOrderParams, CancelOrderParams, CancelAllOrdersParams, TWAPParams, CancelTWAPParams, TPSLParams, UpdateLeverageParams, WithdrawParams, TransferFundsParams, BatchAction, CreateLakeParams, LakeDepositParams, LakeWithdrawParams } from "./types/index.js";
|
|
2
|
+
import type { MarketInfo, PriceInfo, Orderbook, Trade, Kline, KlineInterval, AggLevel, AccountInfo, AccountSettings, Position, OrderInfo, MarketOrderParams, LimitOrderParams, StopOrderParams, EditOrderParams, CancelOrderParams, CancelAllOrdersParams, TWAPParams, CancelTWAPParams, TPSLParams, UpdateLeverageParams, WithdrawParams, TransferFundsParams, BatchAction, BatchActionType, CreateLakeParams, LakeDepositParams, LakeWithdrawParams } from "./types/index.js";
|
|
3
3
|
type SignMessageFn = (message: Uint8Array) => Promise<Uint8Array>;
|
|
4
4
|
/** Per-request timeout of a POST (fetch + body read). */
|
|
5
5
|
export declare const PACIFICA_POST_TIMEOUT_MS = 15000;
|
|
6
|
+
/**
|
|
7
|
+
* Credits ×10 reserved for one POST /orders/batch. The docs (api/rate-limits "Credit Costs") price
|
|
8
|
+
* a "Standard request/action" at 1 credit and an "Order cancellation" at 0.5 and say nothing
|
|
9
|
+
* batch-specific (batch-order page: no cost); every action is individually signed and processed,
|
|
10
|
+
* so the batch is reserved per ACTION — the conservative reading (over-reserving only throttles
|
|
11
|
+
* earlier; the venue's ratelimit headers on the response settle the real count).
|
|
12
|
+
*/
|
|
13
|
+
export declare function pacificaBatchCost(types: readonly BatchActionType[]): number;
|
|
6
14
|
export interface PacificaClientConfig {
|
|
7
15
|
network?: Network;
|
|
8
16
|
baseUrl?: string;
|
|
@@ -18,6 +26,10 @@ export declare class PacificaClient {
|
|
|
18
26
|
* against the agent key. Undefined for master/PK signers.
|
|
19
27
|
*/
|
|
20
28
|
private requestAgentWallet?;
|
|
29
|
+
/** Credit-quota state for this base URL + key tier (rate-limit.ts; process-local): unsigned GETs (IP quota) … */
|
|
30
|
+
private readonly rate;
|
|
31
|
+
/** … and signed POSTs (may report the account's quota) kept apart. */
|
|
32
|
+
private readonly signedRate;
|
|
21
33
|
/** Attach (or clear) the agent wallet used for subsequent signed requests. */
|
|
22
34
|
setRequestAgentWallet(agentWallet?: string): void;
|
|
23
35
|
/** Build a signed POST body, attaching agent_wallet when an agent is the active signer. */
|
|
@@ -31,8 +43,12 @@ export declare class PacificaClient {
|
|
|
31
43
|
* outcome unknown → ORDER_PENDING (sendFailure), never a retryable network error. No connection
|
|
32
44
|
* at all → not sent. A 4xx is the venue's refusal and keeps the plain `POST <path> failed
|
|
33
45
|
* (<status>): <body>` error the adapters map (422 business codes) — except on
|
|
34
|
-
* DEFINITE_4XX_PATHS, where it is a definite ACTION_REJECTED
|
|
35
|
-
*
|
|
46
|
+
* DEFINITE_4XX_PATHS, where it is a definite ACTION_REJECTED. HTTP 429 → RATE_LIMITED
|
|
47
|
+
* (details.sent === false, retryAfterMs) — never retried, a write is never resubmitted.
|
|
48
|
+
* "429 = not executed" is our reading of the docs ("When credits are exhausted, requests return
|
|
49
|
+
* HTTP 429"), not an explicit Pacifica statement. Before sending, the credit throttle may wait up
|
|
50
|
+
* to PACIFICA_SIGNED_MAX_WAIT_MS (the body is already signed) or fail fast with RATE_LIMITED
|
|
51
|
+
* (nothing sent). A write needs only its own cost, never the read reserve. A fetch timeout (PACIFICA_POST_TIMEOUT_MS) on a write is ORDER_PENDING too;
|
|
36
52
|
* on a listing read a timeout is TIMEOUT and any other transport failure EXCHANGE_UNREACHABLE.
|
|
37
53
|
*/
|
|
38
54
|
private post;
|
|
@@ -119,6 +135,27 @@ export declare class PacificaClient {
|
|
|
119
135
|
createLake(params: CreateLakeParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
|
|
120
136
|
depositToLake(params: LakeDepositParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
|
|
121
137
|
withdrawFromLake(params: LakeWithdrawParams, account: string, signMessage: SignMessageFn): Promise<unknown>;
|
|
138
|
+
/**
|
|
139
|
+
* Unauthenticated GET that keeps the whole {success, data, has_more, next_cursor} envelope.
|
|
140
|
+
* Same credit limiter as get() (unsigned IP quota, PACIFICA_READ_NEED: may wait for the window,
|
|
141
|
+
* else RATE_LIMITED sent:false); the response headers are recorded; HTTP 429 → RATE_LIMITED
|
|
142
|
+
* (structural, never retried). Timeout → TIMEOUT, transport failure → EXCHANGE_UNREACHABLE,
|
|
143
|
+
* non-JSON / non-object body → EXCHANGE_ERROR (a read: nothing can have changed).
|
|
144
|
+
*/
|
|
145
|
+
getEnvelope(path: string, params?: Record<string, string>): Promise<Record<string, unknown>>;
|
|
146
|
+
/**
|
|
147
|
+
* POST a body the caller already built and signed (Print / vault writes sign their own canonical
|
|
148
|
+
* message so `--dry-run` can show the exact body). Same unknown-outcome rules as every write.
|
|
149
|
+
*/
|
|
150
|
+
postPresigned(path: "/game/deposit" | "/game/end" | "/lake/deposit" | "/lake/withdraw", body: Record<string, unknown>): Promise<unknown>;
|
|
151
|
+
/**
|
|
152
|
+
* Pre-sign credit wait for a pre-signed write (same rule as signedBody / amend): call BEFORE
|
|
153
|
+
* building + signing, so the post-sign wait in post() (≤ PACIFICA_SIGNED_MAX_WAIT_MS, inside the
|
|
154
|
+
* 5 s expiry_window) finds credits. Cost: the docs' rate-limits table lists no Print / vault
|
|
155
|
+
* entry — "Standard request/action: 1 credit" applies (end_game is not an order cancellation).
|
|
156
|
+
* Fail-fast RATE_LIMITED here sent and signed nothing (details.sent === false).
|
|
157
|
+
*/
|
|
158
|
+
awaitPresignedCapacity(operationType: string): Promise<void>;
|
|
122
159
|
approveBuilderCode(params: {
|
|
123
160
|
builder_code: string;
|
|
124
161
|
max_fee_rate: string;
|
|
@@ -137,6 +174,32 @@ export declare class PacificaClient {
|
|
|
137
174
|
claimReferralCode(params: {
|
|
138
175
|
code: string;
|
|
139
176
|
}, account: string, signMessage: SignMessageFn): Promise<unknown>;
|
|
140
|
-
|
|
177
|
+
/**
|
|
178
|
+
* POST /orders/batch with the per-action cost (pacificaBatchCost) reserved. `maxWaitMs` caps the
|
|
179
|
+
* post-sign throttle wait (default PACIFICA_SIGNED_MAX_WAIT_MS, never more): a caller with a
|
|
180
|
+
* tighter signature deadline passes what is left of it, so a wait can never send an expired batch
|
|
181
|
+
* — a longer wait fails fast with RATE_LIMITED (sent: false, nothing sent).
|
|
182
|
+
*/
|
|
183
|
+
batchOrders(actions: BatchAction[], opts?: {
|
|
184
|
+
maxWaitMs?: number;
|
|
185
|
+
}): Promise<unknown>;
|
|
186
|
+
/**
|
|
187
|
+
* Pre-sign throttle for a batch (like signedBody does for single writes): wait — up to
|
|
188
|
+
* PACIFICA_MAX_THROTTLE_WAIT_MS, else RATE_LIMITED with nothing signed or sent — until the
|
|
189
|
+
* batch's whole cost fits, so the post-sign acquire in batchOrders normally finds the credits.
|
|
190
|
+
*/
|
|
191
|
+
awaitBatchCapacity(types: readonly BatchActionType[]): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* One signed batch action body (docs rest-api/orders/batch-order: "Batch orders are not signed
|
|
194
|
+
* as a whole, but rather by its individual actions components"; each action's `data` carries the
|
|
195
|
+
* fields of its standalone endpoint, signed with that endpoint's operation type). Nothing is sent.
|
|
196
|
+
* `header` fixes the signed timestamp / expiry window, so every action of one batch can share
|
|
197
|
+
* one timestamp. agent_wallet is attached when an agent is the active signer. A signing failure
|
|
198
|
+
* → ORDER_REJECTED (orderNotSent).
|
|
199
|
+
*/
|
|
200
|
+
signBatchAction(operationType: string, payload: Record<string, unknown>, account: string, signMessage: SignMessageFn, header: {
|
|
201
|
+
timestamp: number;
|
|
202
|
+
expiry_window: number;
|
|
203
|
+
}): Promise<Record<string, unknown>>;
|
|
141
204
|
}
|
|
142
205
|
export {};
|