perp-cli 0.12.13 → 0.12.14
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/dist/mcp-server.js +13 -12
- package/dist/trade-validator.js +7 -5
- package/package.json +1 -1
- package/skills/perp-cli/SKILL.md +2 -2
package/dist/mcp-server.js
CHANGED
|
@@ -530,11 +530,11 @@ server.tool("suggest_command", "Given a natural language trading goal, suggest t
|
|
|
530
530
|
steps.push({ step: 1, command: "perp strategy plan example", description: "Show example execution plan format" }, { step: 2, command: "perp --json strategy plan validate <file>", description: "Validate plan file" }, { step: 3, command: "perp --json strategy plan execute <file> --dry-run", description: "Dry-run the plan", dangerous: true });
|
|
531
531
|
}
|
|
532
532
|
else if (g.includes("status") || g.includes("check") || g.includes("overview") || g.includes("portfolio")) {
|
|
533
|
-
steps.push({ step: 1, command: `perp -e ${ex} --json
|
|
533
|
+
steps.push({ step: 1, command: `perp -e ${ex} --json portfolio`, description: "Single-exchange portfolio overview" }, { step: 2, command: `perp -e ${ex} --json account positions`, description: "Detailed positions" }, { step: 3, command: `perp -e ${ex} --json account orders`, description: "Open orders" }, { step: 4, command: "perp --json portfolio --arb", description: "Cross-exchange portfolio + top arb opportunities" });
|
|
534
534
|
}
|
|
535
535
|
else if (g.includes("deposit")) {
|
|
536
536
|
const amount = extractNumber(g) || "<amount>";
|
|
537
|
-
steps.push({ step: 1, command: "perp --json wallet balance", description: "Check wallet balance" }, { step: 2, command: `perp --json funds deposit ${ex} ${amount}`, description: `Deposit $${amount} to ${ex}`, dangerous: true }, { step: 3, command: `perp -e ${ex} --json
|
|
537
|
+
steps.push({ step: 1, command: "perp --json wallet balance", description: "Check wallet balance" }, { step: 2, command: `perp --json funds deposit ${ex} ${amount}`, description: `Deposit $${amount} to ${ex}`, dangerous: true }, { step: 3, command: `perp -e ${ex} --json portfolio`, description: "Verify deposit arrived" });
|
|
538
538
|
}
|
|
539
539
|
else if (g.includes("withdraw")) {
|
|
540
540
|
const amount = extractNumber(g) || "<amount>";
|
|
@@ -567,7 +567,7 @@ server.tool("suggest_command", "Given a natural language trading goal, suggest t
|
|
|
567
567
|
steps.push({ step: 1, command: "perp settings show", description: "Show current settings" }, { step: 2, command: "perp settings referrals on", description: "Enable referral codes (no extra fees, just volume tracking)" });
|
|
568
568
|
}
|
|
569
569
|
else {
|
|
570
|
-
steps.push({ step: 1, command: `perp -e ${ex} --json
|
|
570
|
+
steps.push({ step: 1, command: `perp -e ${ex} --json portfolio`, description: "Check account overview" }, { step: 2, command: `perp --json portfolio --health --exchanges ${ex}`, description: "Check exchange connectivity" });
|
|
571
571
|
}
|
|
572
572
|
return {
|
|
573
573
|
content: [{
|
|
@@ -1055,10 +1055,10 @@ server.resource("cli_schema", "perp://schema", { mimeType: "application/json", d
|
|
|
1055
1055
|
const schema = {
|
|
1056
1056
|
schemaVersion: "2.0",
|
|
1057
1057
|
name: "perp",
|
|
1058
|
-
description: "Multi-DEX Perpetual Futures CLI (Pacifica, Hyperliquid, Lighter)",
|
|
1059
|
-
exchanges: ["pacifica", "hyperliquid", "lighter"],
|
|
1058
|
+
description: "Multi-DEX Perpetual Futures CLI (Pacifica, Hyperliquid, Lighter, Aster)",
|
|
1059
|
+
exchanges: ["pacifica", "hyperliquid", "lighter", "aster"],
|
|
1060
1060
|
globalFlags: [
|
|
1061
|
-
{ flag: "-e, --exchange <name>", description: "Exchange to use (pacifica, hyperliquid, lighter)", default: "pacifica" },
|
|
1061
|
+
{ flag: "-e, --exchange <name>", description: "Exchange to use (pacifica, hyperliquid, lighter, aster)", default: "pacifica" },
|
|
1062
1062
|
{ flag: "--json", description: "Output as JSON for structured parsing" },
|
|
1063
1063
|
{ flag: "-n, --network <net>", description: "Network: mainnet or testnet", default: "mainnet" },
|
|
1064
1064
|
{ flag: "--dry-run", description: "Simulate without executing (for trade commands)" },
|
|
@@ -1080,7 +1080,7 @@ server.resource("cli_schema", "perp://schema", { mimeType: "application/json", d
|
|
|
1080
1080
|
account: {
|
|
1081
1081
|
description: "Account data (read-only)",
|
|
1082
1082
|
subcommands: {
|
|
1083
|
-
info: { usage: "perp
|
|
1083
|
+
info: { usage: "perp portfolio -e <exchange>", description: "Single-exchange balance, equity, margin, PnL" },
|
|
1084
1084
|
positions: { usage: "perp account positions", description: "Open positions" },
|
|
1085
1085
|
orders: { usage: "perp account orders", description: "Open/pending orders" },
|
|
1086
1086
|
history: { usage: "perp account history", description: "Order history" },
|
|
@@ -1240,7 +1240,7 @@ server.resource("cli_schema", "perp://schema", { mimeType: "application/json", d
|
|
|
1240
1240
|
},
|
|
1241
1241
|
portfolio: { usage: "perp portfolio", description: "Cross-exchange portfolio summary" },
|
|
1242
1242
|
status: { usage: "perp portfolio", description: "Full account overview" },
|
|
1243
|
-
health: { usage: "perp
|
|
1243
|
+
health: { usage: "perp portfolio --health", description: "Exchange connectivity check" },
|
|
1244
1244
|
settings: {
|
|
1245
1245
|
description: "CLI settings",
|
|
1246
1246
|
subcommands: {
|
|
@@ -1418,7 +1418,7 @@ server.tool("get_funding_analysis", "Analyze funding income across exchanges —
|
|
|
1418
1418
|
symbol: z.string().optional().describe("Filter by symbol"),
|
|
1419
1419
|
}, async ({ period, exchange: exFilter, symbol: symFilter }) => {
|
|
1420
1420
|
try {
|
|
1421
|
-
const exchanges = exFilter ? [exFilter] : ["pacifica", "hyperliquid", "lighter"];
|
|
1421
|
+
const exchanges = exFilter ? [exFilter] : ["pacifica", "hyperliquid", "lighter", "aster"];
|
|
1422
1422
|
const periodMatch = period.match(/^(\d+)(d|w|m)$/);
|
|
1423
1423
|
const sinceMs = periodMatch
|
|
1424
1424
|
? Date.now() - parseInt(periodMatch[1]) * ({ d: 86400000, w: 604800000, m: 2592000000 }[periodMatch[2]] ?? 86400000)
|
|
@@ -1488,7 +1488,7 @@ server.tool("get_pnl_analysis", "P&L analysis combining trading fees and funding
|
|
|
1488
1488
|
exchange: z.string().optional().describe("Filter by exchange"),
|
|
1489
1489
|
}, async ({ period, exchange: exFilter }) => {
|
|
1490
1490
|
try {
|
|
1491
|
-
const exchanges = exFilter ? [exFilter] : ["pacifica", "hyperliquid", "lighter"];
|
|
1491
|
+
const exchanges = exFilter ? [exFilter] : ["pacifica", "hyperliquid", "lighter", "aster"];
|
|
1492
1492
|
const periodMatch = period.match(/^(\d+)(d|w|m)$/);
|
|
1493
1493
|
const sinceMs = periodMatch
|
|
1494
1494
|
? Date.now() - parseInt(periodMatch[1]) * ({ d: 86400000, w: 604800000, m: 2592000000 }[periodMatch[2]] ?? 86400000)
|
|
@@ -1602,7 +1602,7 @@ server.tool("get_arb_compare", "Compare active arbitrage positions side by side
|
|
|
1602
1602
|
// ============================================================
|
|
1603
1603
|
server.resource("market_prices", "market://prices", { mimeType: "application/json", description: "Live cross-exchange prices for all perpetual futures markets" }, async () => {
|
|
1604
1604
|
const prices = {};
|
|
1605
|
-
await Promise.allSettled(["pacifica", "hyperliquid", "lighter"].map(async (ex) => {
|
|
1605
|
+
await Promise.allSettled(["pacifica", "hyperliquid", "lighter", "aster"].map(async (ex) => {
|
|
1606
1606
|
try {
|
|
1607
1607
|
const adapter = await getOrCreateAdapter(ex);
|
|
1608
1608
|
const markets = await adapter.getMarkets();
|
|
@@ -1669,7 +1669,8 @@ server.prompt("trading-guide", "Step-by-step guide for trading with perp-cli via
|
|
|
1669
1669
|
## Supported Exchanges
|
|
1670
1670
|
- **pacifica** (Solana) — SOL-based perpetual futures
|
|
1671
1671
|
- **hyperliquid** (HyperEVM) — Low-fee perpetuals with HIP-3 DEXes
|
|
1672
|
-
- **lighter** (Ethereum) — Ethereum-based perpetual futures
|
|
1672
|
+
- **lighter** (Ethereum) — Ethereum-based perpetual futures
|
|
1673
|
+
- **aster** (BNB Chain) — BNB Chain perpetual futures`,
|
|
1673
1674
|
},
|
|
1674
1675
|
}],
|
|
1675
1676
|
}));
|
package/dist/trade-validator.js
CHANGED
|
@@ -10,21 +10,20 @@ export async function validateTrade(adapter, params) {
|
|
|
10
10
|
const checks = [];
|
|
11
11
|
const warnings = [];
|
|
12
12
|
const sym = params.symbol.toUpperCase();
|
|
13
|
-
// Fetch market
|
|
13
|
+
// Fetch market list, balance, and positions in parallel.
|
|
14
|
+
// Defer the orderbook call until symbol membership is confirmed so invalid
|
|
15
|
+
// symbols return a structured symbol_valid failure instead of a venue error.
|
|
14
16
|
// SSOT rule #2: errors must propagate, never silently substitute defaults.
|
|
15
17
|
// getMarkets retains a tracked-error wrapper so the failed-load case surfaces
|
|
16
18
|
// a structured symbol_valid check (not a generic exception).
|
|
17
19
|
let marketsError = null;
|
|
18
|
-
const [markets, balance, positions
|
|
20
|
+
const [markets, balance, positions] = await Promise.all([
|
|
19
21
|
adapter.getMarkets().catch((e) => {
|
|
20
22
|
marketsError = e instanceof Error ? e.message : String(e);
|
|
21
23
|
return [];
|
|
22
24
|
}),
|
|
23
25
|
adapter.getBalance(),
|
|
24
26
|
adapter.getPositions(),
|
|
25
|
-
params.type !== "limit"
|
|
26
|
-
? adapter.getOrderbook(sym)
|
|
27
|
-
: Promise.resolve({ bids: [], asks: [] }),
|
|
28
27
|
]);
|
|
29
28
|
if (markets.length === 0 && marketsError) {
|
|
30
29
|
checks.push({ check: "symbol_valid", passed: false, message: `Failed to load markets from ${adapter.name}: ${marketsError}` });
|
|
@@ -42,6 +41,9 @@ export async function validateTrade(adapter, params) {
|
|
|
42
41
|
checks.push({ check: "symbol_valid", passed: false, message: `${sym} not found on ${adapter.name}` });
|
|
43
42
|
return { valid: false, checks, warnings, timestamp: new Date().toISOString() };
|
|
44
43
|
}
|
|
44
|
+
const orderbook = params.type !== "limit"
|
|
45
|
+
? await adapter.getOrderbook(sym)
|
|
46
|
+
: { bids: [], asks: [] };
|
|
45
47
|
const markPrice = Number(market.markPrice);
|
|
46
48
|
if (params.type === "limit" && (params.price === undefined || !Number.isFinite(params.price))) {
|
|
47
49
|
throw new Error(`Limit order requires explicit price (received ${params.price}); refusing silent mark-price substitution.`);
|
package/package.json
CHANGED
package/skills/perp-cli/SKILL.md
CHANGED
|
@@ -5,7 +5,7 @@ allowed-tools: "Bash(perp:*), Bash(npx perp-cli:*), Bash(npx -y perp-cli:*)"
|
|
|
5
5
|
license: MIT
|
|
6
6
|
metadata:
|
|
7
7
|
author: hypurrquant
|
|
8
|
-
version: "0.12.
|
|
8
|
+
version: "0.12.14"
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
# perp-cli Agent Guide
|
|
@@ -41,7 +41,7 @@ Multi-DEX perpetual futures CLI + MCP server — Pacifica (Solana), Hyperliquid
|
|
|
41
41
|
## Install
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
perp --version 2>/dev/null # check if exists (must be >= 0.12.
|
|
44
|
+
perp --version 2>/dev/null # check if exists (must be >= 0.12.14)
|
|
45
45
|
npm install -g perp-cli@latest 2>/dev/null || npx -y perp-cli@latest --json --version
|
|
46
46
|
```
|
|
47
47
|
|