perp-cli 0.3.3
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/LICENSE +21 -0
- package/README.md +293 -0
- package/dist/__tests__/alert-logic.test.d.ts +1 -0
- package/dist/__tests__/alert-logic.test.js +107 -0
- package/dist/__tests__/arb-auto-3dex.test.d.ts +1 -0
- package/dist/__tests__/arb-auto-3dex.test.js +397 -0
- package/dist/__tests__/arb-history-stats.test.d.ts +1 -0
- package/dist/__tests__/arb-history-stats.test.js +176 -0
- package/dist/__tests__/arb-logic.test.d.ts +1 -0
- package/dist/__tests__/arb-logic.test.js +84 -0
- package/dist/__tests__/arb-manage.test.d.ts +1 -0
- package/dist/__tests__/arb-manage.test.js +253 -0
- package/dist/__tests__/arb-new-features.test.d.ts +1 -0
- package/dist/__tests__/arb-new-features.test.js +457 -0
- package/dist/__tests__/arb-sizing.test.d.ts +1 -0
- package/dist/__tests__/arb-sizing.test.js +48 -0
- package/dist/__tests__/arb-state.test.d.ts +1 -0
- package/dist/__tests__/arb-state.test.js +284 -0
- package/dist/__tests__/arb-userflow.test.d.ts +1 -0
- package/dist/__tests__/arb-userflow.test.js +945 -0
- package/dist/__tests__/arb-utils.test.d.ts +1 -0
- package/dist/__tests__/arb-utils.test.js +264 -0
- package/dist/__tests__/bot-conditions.test.d.ts +1 -0
- package/dist/__tests__/bot-conditions.test.js +341 -0
- package/dist/__tests__/client-id-tracker.test.d.ts +1 -0
- package/dist/__tests__/client-id-tracker.test.js +137 -0
- package/dist/__tests__/commands/new-atomic-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/new-atomic-commands.test.js +502 -0
- package/dist/__tests__/commands/order-intent.test.d.ts +1 -0
- package/dist/__tests__/commands/order-intent.test.js +600 -0
- package/dist/__tests__/commands/trade-commands.test.d.ts +1 -0
- package/dist/__tests__/commands/trade-commands.test.js +821 -0
- package/dist/__tests__/config.test.d.ts +1 -0
- package/dist/__tests__/config.test.js +86 -0
- package/dist/__tests__/cross-chain-margin.test.d.ts +1 -0
- package/dist/__tests__/cross-chain-margin.test.js +287 -0
- package/dist/__tests__/dex-asset-map.test.d.ts +1 -0
- package/dist/__tests__/dex-asset-map.test.js +191 -0
- package/dist/__tests__/errors.test.d.ts +1 -0
- package/dist/__tests__/errors.test.js +110 -0
- package/dist/__tests__/event-stream.test.d.ts +1 -0
- package/dist/__tests__/event-stream.test.js +276 -0
- package/dist/__tests__/exchanges/interface.test.d.ts +1 -0
- package/dist/__tests__/exchanges/interface.test.js +132 -0
- package/dist/__tests__/exchanges/mock-adapter.d.ts +69 -0
- package/dist/__tests__/exchanges/mock-adapter.js +137 -0
- package/dist/__tests__/execution-log.test.d.ts +1 -0
- package/dist/__tests__/execution-log.test.js +106 -0
- package/dist/__tests__/funding-calc.test.d.ts +1 -0
- package/dist/__tests__/funding-calc.test.js +71 -0
- package/dist/__tests__/funding-history.test.d.ts +1 -0
- package/dist/__tests__/funding-history.test.js +343 -0
- package/dist/__tests__/funding-rates.test.d.ts +1 -0
- package/dist/__tests__/funding-rates.test.js +342 -0
- package/dist/__tests__/funding.test.d.ts +1 -0
- package/dist/__tests__/funding.test.js +173 -0
- package/dist/__tests__/gap-logic.test.d.ts +1 -0
- package/dist/__tests__/gap-logic.test.js +43 -0
- package/dist/__tests__/hip3-dex.test.d.ts +1 -0
- package/dist/__tests__/hip3-dex.test.js +234 -0
- package/dist/__tests__/integration/agent-features.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/agent-features.integration.test.js +553 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/atomic-commands.integration.test.js +246 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-simulation.integration.test.js +453 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge-strict.integration.test.js +812 -0
- package/dist/__tests__/integration/bridge.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/bridge.integration.test.js +309 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/cli-e2e.integration.test.js +202 -0
- package/dist/__tests__/integration/dex-arb.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/dex-arb.integration.test.js +116 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.d.ts +13 -0
- package/dist/__tests__/integration/envelope-consistency.integration.test.js +205 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hip3-dex.integration.test.js +147 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/hyperliquid.integration.test.js +79 -0
- package/dist/__tests__/integration/lighter.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/lighter.integration.test.js +53 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.d.ts +9 -0
- package/dist/__tests__/integration/new-commands-e2e.integration.test.js +236 -0
- package/dist/__tests__/integration/order-verification.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/order-verification.integration.test.js +321 -0
- package/dist/__tests__/integration/pacifica.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/pacifica.integration.test.js +75 -0
- package/dist/__tests__/integration/response-shapes.integration.test.d.ts +1 -0
- package/dist/__tests__/integration/response-shapes.integration.test.js +278 -0
- package/dist/__tests__/liquidity.test.d.ts +1 -0
- package/dist/__tests__/liquidity.test.js +225 -0
- package/dist/__tests__/plan-executor.test.d.ts +1 -0
- package/dist/__tests__/plan-executor.test.js +314 -0
- package/dist/__tests__/position-history.test.d.ts +1 -0
- package/dist/__tests__/position-history.test.js +367 -0
- package/dist/__tests__/retry.test.d.ts +1 -0
- package/dist/__tests__/retry.test.js +310 -0
- package/dist/__tests__/risk-assessment.test.d.ts +1 -0
- package/dist/__tests__/risk-assessment.test.js +145 -0
- package/dist/__tests__/security-adversarial.test.d.ts +1 -0
- package/dist/__tests__/security-adversarial.test.js +574 -0
- package/dist/__tests__/strategies.test.d.ts +1 -0
- package/dist/__tests__/strategies.test.js +539 -0
- package/dist/__tests__/trade-execution.test.d.ts +1 -0
- package/dist/__tests__/trade-execution.test.js +129 -0
- package/dist/__tests__/trade-validator.test.d.ts +1 -0
- package/dist/__tests__/trade-validator.test.js +655 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +76 -0
- package/dist/api/public/hyperliquid.d.ts +18 -0
- package/dist/api/public/hyperliquid.js +82 -0
- package/dist/api/public/index.d.ts +8 -0
- package/dist/api/public/index.js +8 -0
- package/dist/api/public/lighter.d.ts +24 -0
- package/dist/api/public/lighter.js +100 -0
- package/dist/api/public/pacifica.d.ts +17 -0
- package/dist/api/public/pacifica.js +54 -0
- package/dist/api/public/urls.d.ts +12 -0
- package/dist/api/public/urls.js +33 -0
- package/dist/arb/history-stats.d.ts +44 -0
- package/dist/arb/history-stats.js +135 -0
- package/dist/arb/index.d.ts +4 -0
- package/dist/arb/index.js +4 -0
- package/dist/arb/sizing.d.ts +23 -0
- package/dist/arb/sizing.js +96 -0
- package/dist/arb/state.d.ts +51 -0
- package/dist/arb/state.js +112 -0
- package/dist/arb/utils.d.ts +81 -0
- package/dist/arb/utils.js +267 -0
- package/dist/arb-history-stats.d.ts +5 -0
- package/dist/arb-history-stats.js +5 -0
- package/dist/arb-sizing.d.ts +5 -0
- package/dist/arb-sizing.js +5 -0
- package/dist/arb-state.d.ts +5 -0
- package/dist/arb-state.js +5 -0
- package/dist/arb-utils.d.ts +5 -0
- package/dist/arb-utils.js +5 -0
- package/dist/bot/conditions.d.ts +32 -0
- package/dist/bot/conditions.js +141 -0
- package/dist/bot/config.d.ts +76 -0
- package/dist/bot/config.js +160 -0
- package/dist/bot/engine.d.ts +8 -0
- package/dist/bot/engine.js +519 -0
- package/dist/bot/presets.d.ts +11 -0
- package/dist/bot/presets.js +296 -0
- package/dist/bridge-engine.d.ts +133 -0
- package/dist/bridge-engine.js +1487 -0
- package/dist/cache.d.ts +25 -0
- package/dist/cache.js +99 -0
- package/dist/cli-spec.d.ts +50 -0
- package/dist/cli-spec.js +75 -0
- package/dist/client-id-tracker.d.ts +25 -0
- package/dist/client-id-tracker.js +76 -0
- package/dist/commands/account.d.ts +3 -0
- package/dist/commands/account.js +425 -0
- package/dist/commands/agent.d.ts +3 -0
- package/dist/commands/agent.js +386 -0
- package/dist/commands/alert.d.ts +2 -0
- package/dist/commands/alert.js +421 -0
- package/dist/commands/analytics.d.ts +3 -0
- package/dist/commands/analytics.js +311 -0
- package/dist/commands/arb/index.d.ts +3 -0
- package/dist/commands/arb/index.js +921 -0
- package/dist/commands/arb-auto.d.ts +54 -0
- package/dist/commands/arb-auto.js +1328 -0
- package/dist/commands/arb-manage.d.ts +5 -0
- package/dist/commands/arb-manage.js +5 -0
- package/dist/commands/arb.d.ts +2 -0
- package/dist/commands/arb.js +347 -0
- package/dist/commands/backtest.d.ts +2 -0
- package/dist/commands/backtest.js +327 -0
- package/dist/commands/bot.d.ts +3 -0
- package/dist/commands/bot.js +412 -0
- package/dist/commands/bridge.d.ts +2 -0
- package/dist/commands/bridge.js +396 -0
- package/dist/commands/dashboard.d.ts +3 -0
- package/dist/commands/dashboard.js +176 -0
- package/dist/commands/deposit.d.ts +4 -0
- package/dist/commands/deposit.js +573 -0
- package/dist/commands/dex.d.ts +3 -0
- package/dist/commands/dex.js +114 -0
- package/dist/commands/env.d.ts +2 -0
- package/dist/commands/env.js +136 -0
- package/dist/commands/funding.d.ts +2 -0
- package/dist/commands/funding.js +347 -0
- package/dist/commands/gap.d.ts +2 -0
- package/dist/commands/gap.js +305 -0
- package/dist/commands/health.d.ts +2 -0
- package/dist/commands/health.js +67 -0
- package/dist/commands/history.d.ts +2 -0
- package/dist/commands/history.js +235 -0
- package/dist/commands/init.d.ts +15 -0
- package/dist/commands/init.js +266 -0
- package/dist/commands/jobs.d.ts +2 -0
- package/dist/commands/jobs.js +133 -0
- package/dist/commands/manage.d.ts +4 -0
- package/dist/commands/manage.js +309 -0
- package/dist/commands/market.d.ts +3 -0
- package/dist/commands/market.js +225 -0
- package/dist/commands/plan.d.ts +3 -0
- package/dist/commands/plan.js +95 -0
- package/dist/commands/portfolio.d.ts +3 -0
- package/dist/commands/portfolio.js +169 -0
- package/dist/commands/rebalance.d.ts +3 -0
- package/dist/commands/rebalance.js +293 -0
- package/dist/commands/risk.d.ts +3 -0
- package/dist/commands/risk.js +169 -0
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.js +202 -0
- package/dist/commands/settings.d.ts +2 -0
- package/dist/commands/settings.js +102 -0
- package/dist/commands/stream.d.ts +5 -0
- package/dist/commands/stream.js +123 -0
- package/dist/commands/trade.d.ts +3 -0
- package/dist/commands/trade.js +1273 -0
- package/dist/commands/wallet.d.ts +14 -0
- package/dist/commands/wallet.js +602 -0
- package/dist/commands/withdraw.d.ts +3 -0
- package/dist/commands/withdraw.js +187 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.js +68 -0
- package/dist/cross-chain-margin.d.ts +46 -0
- package/dist/cross-chain-margin.js +107 -0
- package/dist/dashboard/server.d.ts +80 -0
- package/dist/dashboard/server.js +340 -0
- package/dist/dashboard/ui.d.ts +4 -0
- package/dist/dashboard/ui.js +538 -0
- package/dist/dashboard/ws-feeds.d.ts +29 -0
- package/dist/dashboard/ws-feeds.js +660 -0
- package/dist/dex-asset-map.d.ts +80 -0
- package/dist/dex-asset-map.js +201 -0
- package/dist/errors.d.ts +109 -0
- package/dist/errors.js +84 -0
- package/dist/event-stream.d.ts +25 -0
- package/dist/event-stream.js +168 -0
- package/dist/exchanges/hyperliquid.d.ts +212 -0
- package/dist/exchanges/hyperliquid.js +931 -0
- package/dist/exchanges/interface.d.ts +95 -0
- package/dist/exchanges/interface.js +5 -0
- package/dist/exchanges/lighter.d.ts +159 -0
- package/dist/exchanges/lighter.js +793 -0
- package/dist/exchanges/pacifica.d.ts +51 -0
- package/dist/exchanges/pacifica.js +248 -0
- package/dist/execution-log.d.ts +36 -0
- package/dist/execution-log.js +102 -0
- package/dist/funding/history.d.ts +63 -0
- package/dist/funding/history.js +266 -0
- package/dist/funding/index.d.ts +3 -0
- package/dist/funding/index.js +3 -0
- package/dist/funding/normalize.d.ts +39 -0
- package/dist/funding/normalize.js +66 -0
- package/dist/funding/rates.d.ts +45 -0
- package/dist/funding/rates.js +172 -0
- package/dist/funding-history.d.ts +5 -0
- package/dist/funding-history.js +5 -0
- package/dist/funding-rates.d.ts +5 -0
- package/dist/funding-rates.js +5 -0
- package/dist/funding.d.ts +5 -0
- package/dist/funding.js +5 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +458 -0
- package/dist/jobs.d.ts +37 -0
- package/dist/jobs.js +152 -0
- package/dist/liquidity.d.ts +34 -0
- package/dist/liquidity.js +100 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.js +1206 -0
- package/dist/pacifica/client.d.ts +111 -0
- package/dist/pacifica/client.js +310 -0
- package/dist/pacifica/constants.d.ts +27 -0
- package/dist/pacifica/constants.js +47 -0
- package/dist/pacifica/deposit.d.ts +14 -0
- package/dist/pacifica/deposit.js +78 -0
- package/dist/pacifica/index.d.ts +6 -0
- package/dist/pacifica/index.js +11 -0
- package/dist/pacifica/signing.d.ts +49 -0
- package/dist/pacifica/signing.js +97 -0
- package/dist/pacifica/types/account.d.ts +42 -0
- package/dist/pacifica/types/account.js +1 -0
- package/dist/pacifica/types/index.d.ts +6 -0
- package/dist/pacifica/types/index.js +6 -0
- package/dist/pacifica/types/lake.d.ts +18 -0
- package/dist/pacifica/types/lake.js +1 -0
- package/dist/pacifica/types/market.d.ts +64 -0
- package/dist/pacifica/types/market.js +1 -0
- package/dist/pacifica/types/order.d.ts +92 -0
- package/dist/pacifica/types/order.js +1 -0
- package/dist/pacifica/types/position.d.ts +25 -0
- package/dist/pacifica/types/position.js +1 -0
- package/dist/pacifica/types/ws.d.ts +34 -0
- package/dist/pacifica/types/ws.js +41 -0
- package/dist/pacifica/ws-client.d.ts +42 -0
- package/dist/pacifica/ws-client.js +180 -0
- package/dist/plan-executor.d.ts +48 -0
- package/dist/plan-executor.js +280 -0
- package/dist/position-history.d.ts +68 -0
- package/dist/position-history.js +222 -0
- package/dist/rebalance.d.ts +64 -0
- package/dist/rebalance.js +142 -0
- package/dist/retry.d.ts +74 -0
- package/dist/retry.js +129 -0
- package/dist/risk.d.ts +48 -0
- package/dist/risk.js +156 -0
- package/dist/settings.d.ts +19 -0
- package/dist/settings.js +45 -0
- package/dist/shared-api.d.ts +5 -0
- package/dist/shared-api.js +5 -0
- package/dist/strategies/dca.d.ts +25 -0
- package/dist/strategies/dca.js +114 -0
- package/dist/strategies/funding-arb.d.ts +15 -0
- package/dist/strategies/funding-arb.js +281 -0
- package/dist/strategies/grid.d.ts +34 -0
- package/dist/strategies/grid.js +185 -0
- package/dist/strategies/trailing-stop.d.ts +17 -0
- package/dist/strategies/trailing-stop.js +121 -0
- package/dist/strategies/twap.d.ts +20 -0
- package/dist/strategies/twap.js +78 -0
- package/dist/trade-validator.d.ts +39 -0
- package/dist/trade-validator.js +154 -0
- package/dist/utils.d.ts +38 -0
- package/dist/utils.js +110 -0
- package/package.json +63 -0
- package/skills/perp-cli/SKILL.md +149 -0
- package/skills/perp-cli/references/commands.md +143 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for new atomic commands against Hyperliquid mainnet (read-only).
|
|
3
|
+
*
|
|
4
|
+
* Tests:
|
|
5
|
+
* - market mid: real orderbook mid price
|
|
6
|
+
* - account margin: position-not-found for dummy/real account
|
|
7
|
+
* - trade status: order-not-found for dummy order
|
|
8
|
+
* - trade fills: empty fills for dummy account / real fills if any
|
|
9
|
+
* - Error response shapes and classification
|
|
10
|
+
*
|
|
11
|
+
* Requires: HYPERLIQUID_PRIVATE_KEY or HL_PRIVATE_KEY env var
|
|
12
|
+
*/
|
|
13
|
+
import "dotenv/config";
|
|
14
|
+
import { describe, it, expect, beforeAll } from "vitest";
|
|
15
|
+
import { HyperliquidAdapter } from "../../exchanges/hyperliquid.js";
|
|
16
|
+
const HL_KEY = process.env.HYPERLIQUID_PRIVATE_KEY || process.env.HL_PRIVATE_KEY;
|
|
17
|
+
const SKIP = !HL_KEY;
|
|
18
|
+
describe.skipIf(SKIP)("Atomic Commands — Hyperliquid Mainnet", { timeout: 30000 }, () => {
|
|
19
|
+
let adapter;
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
const pk = HL_KEY;
|
|
22
|
+
adapter = new HyperliquidAdapter(pk, false); // mainnet
|
|
23
|
+
await adapter.init();
|
|
24
|
+
});
|
|
25
|
+
// ══════════════════════════════════════════════════════════
|
|
26
|
+
// market mid: orderbook-based mid price
|
|
27
|
+
// ══════════════════════════════════════════════════════════
|
|
28
|
+
describe("market mid (orderbook mid price)", () => {
|
|
29
|
+
it("BTC orderbook has valid bids, asks, and computable mid", async () => {
|
|
30
|
+
const book = await adapter.getOrderbook("BTC");
|
|
31
|
+
expect(book.bids.length).toBeGreaterThan(0);
|
|
32
|
+
expect(book.asks.length).toBeGreaterThan(0);
|
|
33
|
+
const bestBid = parseFloat(book.bids[0][0]);
|
|
34
|
+
const bestAsk = parseFloat(book.asks[0][0]);
|
|
35
|
+
const mid = (bestBid + bestAsk) / 2;
|
|
36
|
+
expect(bestBid).toBeGreaterThan(0);
|
|
37
|
+
expect(bestAsk).toBeGreaterThan(bestBid);
|
|
38
|
+
expect(mid).toBeGreaterThan(100); // BTC is > $100
|
|
39
|
+
// Spread should be tiny for BTC
|
|
40
|
+
const spreadPct = ((bestAsk - bestBid) / mid) * 100;
|
|
41
|
+
expect(spreadPct).toBeLessThan(0.1); // < 0.1%
|
|
42
|
+
});
|
|
43
|
+
it("ETH orderbook has valid mid price", async () => {
|
|
44
|
+
const book = await adapter.getOrderbook("ETH");
|
|
45
|
+
const bestBid = parseFloat(book.bids[0][0]);
|
|
46
|
+
const bestAsk = parseFloat(book.asks[0][0]);
|
|
47
|
+
const mid = (bestBid + bestAsk) / 2;
|
|
48
|
+
expect(mid).toBeGreaterThan(10); // ETH > $10
|
|
49
|
+
expect(bestAsk).toBeGreaterThan(bestBid);
|
|
50
|
+
});
|
|
51
|
+
it("SOL orderbook has valid mid price", async () => {
|
|
52
|
+
const book = await adapter.getOrderbook("SOL");
|
|
53
|
+
const bestBid = parseFloat(book.bids[0][0]);
|
|
54
|
+
const bestAsk = parseFloat(book.asks[0][0]);
|
|
55
|
+
expect(bestBid).toBeGreaterThan(0);
|
|
56
|
+
expect(bestAsk).toBeGreaterThan(bestBid);
|
|
57
|
+
});
|
|
58
|
+
it("orderbook sizes are positive numbers", async () => {
|
|
59
|
+
const book = await adapter.getOrderbook("BTC");
|
|
60
|
+
for (const [price, size] of book.bids.slice(0, 5)) {
|
|
61
|
+
expect(parseFloat(price)).toBeGreaterThan(0);
|
|
62
|
+
expect(parseFloat(size)).toBeGreaterThan(0);
|
|
63
|
+
}
|
|
64
|
+
for (const [price, size] of book.asks.slice(0, 5)) {
|
|
65
|
+
expect(parseFloat(price)).toBeGreaterThan(0);
|
|
66
|
+
expect(parseFloat(size)).toBeGreaterThan(0);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
it("bids are sorted descending, asks are sorted ascending", async () => {
|
|
70
|
+
const book = await adapter.getOrderbook("BTC");
|
|
71
|
+
for (let i = 1; i < Math.min(book.bids.length, 5); i++) {
|
|
72
|
+
expect(parseFloat(book.bids[i - 1][0])).toBeGreaterThanOrEqual(parseFloat(book.bids[i][0]));
|
|
73
|
+
}
|
|
74
|
+
for (let i = 1; i < Math.min(book.asks.length, 5); i++) {
|
|
75
|
+
expect(parseFloat(book.asks[i - 1][0])).toBeLessThanOrEqual(parseFloat(book.asks[i][0]));
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
// ══════════════════════════════════════════════════════════
|
|
80
|
+
// account margin: position margin details
|
|
81
|
+
// ══════════════════════════════════════════════════════════
|
|
82
|
+
describe("account margin (position margin details)", () => {
|
|
83
|
+
it("getBalance returns all required fields as strings", async () => {
|
|
84
|
+
const balance = await adapter.getBalance();
|
|
85
|
+
expect(typeof balance.equity).toBe("string");
|
|
86
|
+
expect(typeof balance.available).toBe("string");
|
|
87
|
+
expect(typeof balance.marginUsed).toBe("string");
|
|
88
|
+
expect(typeof balance.unrealizedPnl).toBe("string");
|
|
89
|
+
// Numbers should be parseable
|
|
90
|
+
expect(isNaN(parseFloat(balance.equity))).toBe(false);
|
|
91
|
+
expect(isNaN(parseFloat(balance.available))).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
it("getPositions returns array with valid position shapes", async () => {
|
|
94
|
+
const positions = await adapter.getPositions();
|
|
95
|
+
expect(Array.isArray(positions)).toBe(true);
|
|
96
|
+
for (const pos of positions) {
|
|
97
|
+
expect(typeof pos.symbol).toBe("string");
|
|
98
|
+
expect(["long", "short"]).toContain(pos.side);
|
|
99
|
+
expect(typeof pos.size).toBe("string");
|
|
100
|
+
expect(parseFloat(pos.size)).toBeGreaterThan(0);
|
|
101
|
+
expect(typeof pos.entryPrice).toBe("string");
|
|
102
|
+
expect(typeof pos.markPrice).toBe("string");
|
|
103
|
+
expect(typeof pos.liquidationPrice).toBe("string");
|
|
104
|
+
expect(typeof pos.unrealizedPnl).toBe("string");
|
|
105
|
+
expect(typeof pos.leverage).toBe("number");
|
|
106
|
+
expect(pos.leverage).toBeGreaterThan(0);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
it("margin calculation is correct for any open position", async () => {
|
|
110
|
+
const [balance, positions] = await Promise.all([
|
|
111
|
+
adapter.getBalance(),
|
|
112
|
+
adapter.getPositions(),
|
|
113
|
+
]);
|
|
114
|
+
if (positions.length === 0) {
|
|
115
|
+
// No positions — margin used should be 0 or very small
|
|
116
|
+
expect(parseFloat(balance.marginUsed)).toBeLessThanOrEqual(1);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
// For each position, verify margin math
|
|
120
|
+
for (const pos of positions) {
|
|
121
|
+
const notional = Math.abs(parseFloat(pos.size) * parseFloat(pos.markPrice));
|
|
122
|
+
const marginRequired = pos.leverage > 0 ? notional / pos.leverage : 0;
|
|
123
|
+
// Sanity: margin should be less than equity
|
|
124
|
+
expect(marginRequired).toBeLessThan(parseFloat(balance.equity) * 10); // allow for extreme leverage
|
|
125
|
+
expect(notional).toBeGreaterThan(0);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
// ══════════════════════════════════════════════════════════
|
|
130
|
+
// trade status: order lookup
|
|
131
|
+
// ══════════════════════════════════════════════════════════
|
|
132
|
+
describe("trade status (order status query)", () => {
|
|
133
|
+
it("queryOrder returns result for non-existent order", async () => {
|
|
134
|
+
// HL returns a result object even for non-existent orders
|
|
135
|
+
const result = await adapter.queryOrder(999999999);
|
|
136
|
+
expect(result).toBeDefined();
|
|
137
|
+
// The order field should be absent or status should indicate not found
|
|
138
|
+
const status = result?.status;
|
|
139
|
+
const order = result?.order;
|
|
140
|
+
// HL returns { status: "order", order: { order: {...}, status: "unknownOid", statusTimestamp: ... } }
|
|
141
|
+
// or similar structure
|
|
142
|
+
expect(result).not.toBeNull();
|
|
143
|
+
});
|
|
144
|
+
it("open orders all have valid orderId for status lookup", async () => {
|
|
145
|
+
const orders = await adapter.getOpenOrders();
|
|
146
|
+
for (const order of orders) {
|
|
147
|
+
expect(typeof order.orderId).toBe("string");
|
|
148
|
+
expect(order.orderId.length).toBeGreaterThan(0);
|
|
149
|
+
// If we have open orders, try querying one
|
|
150
|
+
const result = await adapter.queryOrder(Number(order.orderId));
|
|
151
|
+
expect(result).toBeDefined();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
// ══════════════════════════════════════════════════════════
|
|
156
|
+
// trade fills: recent fills
|
|
157
|
+
// ══════════════════════════════════════════════════════════
|
|
158
|
+
describe("trade fills (recent trade history)", () => {
|
|
159
|
+
it("getTradeHistory returns array", async () => {
|
|
160
|
+
const trades = await adapter.getTradeHistory(10);
|
|
161
|
+
expect(Array.isArray(trades)).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
it("each fill has correct shape if any exist", async () => {
|
|
164
|
+
const trades = await adapter.getTradeHistory(10);
|
|
165
|
+
for (const trade of trades) {
|
|
166
|
+
expect(typeof trade.time).toBe("number");
|
|
167
|
+
expect(trade.time).toBeGreaterThan(1600000000000); // after 2020
|
|
168
|
+
expect(typeof trade.symbol).toBe("string");
|
|
169
|
+
expect(trade.symbol.length).toBeGreaterThan(0);
|
|
170
|
+
expect(["buy", "sell"]).toContain(trade.side);
|
|
171
|
+
expect(typeof trade.price).toBe("string");
|
|
172
|
+
expect(parseFloat(trade.price)).toBeGreaterThan(0);
|
|
173
|
+
expect(typeof trade.size).toBe("string");
|
|
174
|
+
expect(parseFloat(trade.size)).toBeGreaterThan(0);
|
|
175
|
+
expect(typeof trade.fee).toBe("string");
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
it("fills are returned in chronological order if multiple", async () => {
|
|
179
|
+
const trades = await adapter.getTradeHistory(20);
|
|
180
|
+
if (trades.length < 2)
|
|
181
|
+
return; // skip if not enough fills
|
|
182
|
+
for (let i = 1; i < trades.length; i++) {
|
|
183
|
+
// HL returns newest first, so time should be descending
|
|
184
|
+
expect(trades[i - 1].time).toBeGreaterThanOrEqual(trades[i].time);
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
// ══════════════════════════════════════════════════════════
|
|
189
|
+
// Error classification on real errors
|
|
190
|
+
// ══════════════════════════════════════════════════════════
|
|
191
|
+
describe("error classification", () => {
|
|
192
|
+
it("classifyError properly handles network-like errors", async () => {
|
|
193
|
+
const { classifyError } = await import("../../errors.js");
|
|
194
|
+
const timeout = classifyError(new Error("Request timed out"));
|
|
195
|
+
expect(timeout.code).toBe("TIMEOUT");
|
|
196
|
+
expect(timeout.retryable).toBe(true);
|
|
197
|
+
const network = classifyError(new Error("ECONNREFUSED"));
|
|
198
|
+
expect(network.code).toBe("EXCHANGE_UNREACHABLE");
|
|
199
|
+
expect(network.retryable).toBe(true);
|
|
200
|
+
const rateLimit = classifyError(new Error("429 Too Many Requests"));
|
|
201
|
+
expect(rateLimit.code).toBe("RATE_LIMITED");
|
|
202
|
+
expect(rateLimit.retryable).toBe(true);
|
|
203
|
+
});
|
|
204
|
+
it("classifyError properly handles trading errors", async () => {
|
|
205
|
+
const { classifyError } = await import("../../errors.js");
|
|
206
|
+
const balance = classifyError(new Error("Insufficient balance"));
|
|
207
|
+
expect(balance.code).toBe("INSUFFICIENT_BALANCE");
|
|
208
|
+
expect(balance.retryable).toBe(false);
|
|
209
|
+
const margin = classifyError(new Error("Not enough margin"));
|
|
210
|
+
expect(margin.code).toBe("MARGIN_INSUFFICIENT");
|
|
211
|
+
expect(margin.retryable).toBe(false);
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
// ══════════════════════════════════════════════════════════
|
|
215
|
+
// Cross-cutting: response consistency
|
|
216
|
+
// ══════════════════════════════════════════════════════════
|
|
217
|
+
describe("response consistency across adapter methods", () => {
|
|
218
|
+
it("all market data methods return consistent string-typed numbers", async () => {
|
|
219
|
+
const markets = await adapter.getMarkets();
|
|
220
|
+
expect(markets.length).toBeGreaterThan(0);
|
|
221
|
+
// Check first 5 markets
|
|
222
|
+
for (const m of markets.slice(0, 5)) {
|
|
223
|
+
expect(typeof m.markPrice).toBe("string");
|
|
224
|
+
expect(typeof m.indexPrice).toBe("string");
|
|
225
|
+
expect(typeof m.fundingRate).toBe("string");
|
|
226
|
+
expect(typeof m.volume24h).toBe("string");
|
|
227
|
+
expect(typeof m.openInterest).toBe("string");
|
|
228
|
+
expect(typeof m.maxLeverage).toBe("number");
|
|
229
|
+
// All parseable
|
|
230
|
+
expect(isNaN(parseFloat(m.markPrice))).toBe(false);
|
|
231
|
+
expect(isNaN(parseFloat(m.fundingRate))).toBe(false);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
it("balance, positions, orders all return at the same time without errors", async () => {
|
|
235
|
+
// This is what the status command does
|
|
236
|
+
const [balance, positions, orders] = await Promise.all([
|
|
237
|
+
adapter.getBalance(),
|
|
238
|
+
adapter.getPositions(),
|
|
239
|
+
adapter.getOpenOrders(),
|
|
240
|
+
]);
|
|
241
|
+
expect(balance).toBeDefined();
|
|
242
|
+
expect(Array.isArray(positions)).toBe(true);
|
|
243
|
+
expect(Array.isArray(orders)).toBe(true);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|