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,51 @@
|
|
|
1
|
+
import { Keypair } from "@solana/web3.js";
|
|
2
|
+
import { PacificaClient, type Network } from "../pacifica/index.js";
|
|
3
|
+
import type { ExchangeAdapter, ExchangeMarketInfo, ExchangePosition, ExchangeOrder, ExchangeBalance, ExchangeTrade, ExchangeFundingPayment, ExchangeKline } from "./interface.js";
|
|
4
|
+
export declare class PacificaAdapter implements ExchangeAdapter {
|
|
5
|
+
readonly name = "pacifica";
|
|
6
|
+
private client;
|
|
7
|
+
readonly keypair: Keypair;
|
|
8
|
+
private account;
|
|
9
|
+
private signMessage;
|
|
10
|
+
constructor(keypair: Keypair, network?: Network, builderCode?: string);
|
|
11
|
+
private _getPrices;
|
|
12
|
+
private _getPositions;
|
|
13
|
+
get publicKey(): string;
|
|
14
|
+
get sdk(): PacificaClient;
|
|
15
|
+
get signer(): (msg: Uint8Array) => Promise<Uint8Array>;
|
|
16
|
+
getMarkets(): Promise<ExchangeMarketInfo[]>;
|
|
17
|
+
getOrderbook(symbol: string): Promise<{
|
|
18
|
+
bids: [string, string][];
|
|
19
|
+
asks: [string, string][];
|
|
20
|
+
}>;
|
|
21
|
+
getBalance(): Promise<ExchangeBalance>;
|
|
22
|
+
getPositions(): Promise<ExchangePosition[]>;
|
|
23
|
+
getOpenOrders(): Promise<ExchangeOrder[]>;
|
|
24
|
+
marketOrder(symbol: string, side: "buy" | "sell", size: string): Promise<unknown>;
|
|
25
|
+
limitOrder(symbol: string, side: "buy" | "sell", price: string, size: string, opts?: {
|
|
26
|
+
reduceOnly?: boolean;
|
|
27
|
+
tif?: string;
|
|
28
|
+
}): Promise<unknown>;
|
|
29
|
+
cancelOrder(symbol: string, orderId: string): Promise<unknown>;
|
|
30
|
+
cancelAllOrders(_symbol?: string): Promise<unknown>;
|
|
31
|
+
editOrder(symbol: string, orderId: string, price: string, size: string): Promise<unknown>;
|
|
32
|
+
setLeverage(symbol: string, leverage: number, marginMode?: "cross" | "isolated"): Promise<{
|
|
33
|
+
symbol: string;
|
|
34
|
+
leverage: number;
|
|
35
|
+
marginMode: "cross" | "isolated";
|
|
36
|
+
}>;
|
|
37
|
+
stopOrder(symbol: string, side: "buy" | "sell", size: string, triggerPrice: string, opts?: {
|
|
38
|
+
limitPrice?: string;
|
|
39
|
+
reduceOnly?: boolean;
|
|
40
|
+
}): Promise<unknown>;
|
|
41
|
+
getRecentTrades(symbol: string, _limit?: number): Promise<ExchangeTrade[]>;
|
|
42
|
+
getFundingHistory(symbol: string, limit?: number): Promise<{
|
|
43
|
+
time: number;
|
|
44
|
+
rate: string;
|
|
45
|
+
price: string;
|
|
46
|
+
}[]>;
|
|
47
|
+
getKlines(symbol: string, interval: string, startTime: number, endTime: number): Promise<ExchangeKline[]>;
|
|
48
|
+
getOrderHistory(limit?: number): Promise<ExchangeOrder[]>;
|
|
49
|
+
getTradeHistory(limit?: number): Promise<ExchangeTrade[]>;
|
|
50
|
+
getFundingPayments(limit?: number): Promise<ExchangeFundingPayment[]>;
|
|
51
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import nacl from "tweetnacl";
|
|
2
|
+
import { PacificaClient } from "../pacifica/index.js";
|
|
3
|
+
function makeSignMessage(keypair) {
|
|
4
|
+
return async (message) => {
|
|
5
|
+
return nacl.sign.detached(message, keypair.secretKey);
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
export class PacificaAdapter {
|
|
9
|
+
name = "pacifica";
|
|
10
|
+
client;
|
|
11
|
+
keypair;
|
|
12
|
+
account;
|
|
13
|
+
signMessage;
|
|
14
|
+
// In-memory caches removed — using file-based cache (src/cache.ts) for cross-process dedup
|
|
15
|
+
constructor(keypair, network = "mainnet", builderCode) {
|
|
16
|
+
this.keypair = keypair;
|
|
17
|
+
this.account = keypair.publicKey.toBase58();
|
|
18
|
+
this.client = new PacificaClient({ network, builderCode });
|
|
19
|
+
this.signMessage = makeSignMessage(keypair);
|
|
20
|
+
}
|
|
21
|
+
async _getPrices() {
|
|
22
|
+
const { fetchAndCache, TTL_ACCOUNT } = await import("../cache.js");
|
|
23
|
+
return fetchAndCache(`acct:pac:prices:${this.account.slice(0, 8)}`, TTL_ACCOUNT, () => this.client.getPrices());
|
|
24
|
+
}
|
|
25
|
+
async _getPositions() {
|
|
26
|
+
const { fetchAndCache, TTL_ACCOUNT } = await import("../cache.js");
|
|
27
|
+
return fetchAndCache(`acct:pac:positions:${this.account.slice(0, 8)}`, TTL_ACCOUNT, () => this.client.getPositions(this.account));
|
|
28
|
+
}
|
|
29
|
+
get publicKey() {
|
|
30
|
+
return this.account;
|
|
31
|
+
}
|
|
32
|
+
get sdk() {
|
|
33
|
+
return this.client;
|
|
34
|
+
}
|
|
35
|
+
get signer() {
|
|
36
|
+
return this.signMessage;
|
|
37
|
+
}
|
|
38
|
+
async getMarkets() {
|
|
39
|
+
const [markets, prices] = await Promise.all([
|
|
40
|
+
this.client.getInfo(),
|
|
41
|
+
this._getPrices(),
|
|
42
|
+
]);
|
|
43
|
+
const priceMap = new Map(prices.map((p) => [p.symbol, p]));
|
|
44
|
+
return markets.map((m) => {
|
|
45
|
+
const p = priceMap.get(m.symbol);
|
|
46
|
+
return {
|
|
47
|
+
symbol: m.symbol,
|
|
48
|
+
markPrice: p?.mark ?? "-",
|
|
49
|
+
indexPrice: p?.oracle ?? "-",
|
|
50
|
+
fundingRate: m.next_funding_rate ?? m.funding_rate,
|
|
51
|
+
volume24h: p?.volume_24h ?? "-",
|
|
52
|
+
openInterest: p?.open_interest ?? "-",
|
|
53
|
+
maxLeverage: m.max_leverage,
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
async getOrderbook(symbol) {
|
|
58
|
+
const book = await this.client.getBook(symbol);
|
|
59
|
+
return {
|
|
60
|
+
bids: book.l[0].map((e) => [e.p, e.a]),
|
|
61
|
+
asks: book.l[1].map((e) => [e.p, e.a]),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
async getBalance() {
|
|
65
|
+
const [info, positions, prices] = await Promise.all([
|
|
66
|
+
this.client.getAccount(this.account),
|
|
67
|
+
this._getPositions(),
|
|
68
|
+
this._getPrices(),
|
|
69
|
+
]);
|
|
70
|
+
const raw = info;
|
|
71
|
+
const priceMap = new Map(prices.map((p) => [p.symbol, Number(p.mark)]));
|
|
72
|
+
// Sum actual unrealized PnL from positions (mark vs entry)
|
|
73
|
+
let totalPnl = 0;
|
|
74
|
+
for (const p of positions) {
|
|
75
|
+
const mark = priceMap.get(p.symbol) ?? 0;
|
|
76
|
+
const entry = Number(p.entry_price);
|
|
77
|
+
const amount = Number(p.amount);
|
|
78
|
+
const side = p.side === "bid" ? "long" : "short";
|
|
79
|
+
totalPnl += side === "long"
|
|
80
|
+
? (mark - entry) * amount
|
|
81
|
+
: (entry - mark) * amount;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
equity: info.account_equity,
|
|
85
|
+
available: info.available_to_spend,
|
|
86
|
+
marginUsed: String(raw.total_margin_used ?? raw.margin_used ?? "0"),
|
|
87
|
+
unrealizedPnl: totalPnl.toFixed(4),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async getPositions() {
|
|
91
|
+
const [positions, prices] = await Promise.all([
|
|
92
|
+
this._getPositions(),
|
|
93
|
+
this._getPrices(),
|
|
94
|
+
]);
|
|
95
|
+
const priceMap = new Map(prices.map((p) => [p.symbol, p]));
|
|
96
|
+
let levMap = new Map();
|
|
97
|
+
try {
|
|
98
|
+
const settings = await this.client.getAccountSettings(this.account);
|
|
99
|
+
if (Array.isArray(settings)) {
|
|
100
|
+
levMap = new Map(settings.map((s) => [s.symbol, s.leverage]));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Settings API may not be available
|
|
105
|
+
}
|
|
106
|
+
return positions.map((p) => {
|
|
107
|
+
const mark = priceMap.get(p.symbol)?.mark ?? "0";
|
|
108
|
+
const entryPrice = Number(p.entry_price);
|
|
109
|
+
const amount = Number(p.amount);
|
|
110
|
+
const markNum = Number(mark);
|
|
111
|
+
const side = p.side === "bid" ? "long" : "short";
|
|
112
|
+
const pnl = side === "long"
|
|
113
|
+
? (markNum - entryPrice) * amount
|
|
114
|
+
: (entryPrice - markNum) * amount;
|
|
115
|
+
return {
|
|
116
|
+
symbol: p.symbol,
|
|
117
|
+
side: side,
|
|
118
|
+
size: String(p.amount),
|
|
119
|
+
entryPrice: String(p.entry_price),
|
|
120
|
+
markPrice: mark,
|
|
121
|
+
liquidationPrice: String(p.liquidation_price ?? "N/A"),
|
|
122
|
+
unrealizedPnl: pnl.toFixed(4),
|
|
123
|
+
leverage: levMap.get(String(p.symbol)) ?? 1,
|
|
124
|
+
};
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
async getOpenOrders() {
|
|
128
|
+
const orders = await this.client.getOrders(this.account);
|
|
129
|
+
return orders.map((o) => {
|
|
130
|
+
const raw = o;
|
|
131
|
+
return {
|
|
132
|
+
orderId: String(raw.order_id),
|
|
133
|
+
symbol: String(raw.symbol),
|
|
134
|
+
side: raw.side === "bid" ? "buy" : "sell",
|
|
135
|
+
price: String(raw.price),
|
|
136
|
+
size: String(raw.initial_amount ?? raw.amount ?? ""),
|
|
137
|
+
filled: String(raw.filled_amount ?? raw.filled ?? "0"),
|
|
138
|
+
status: String(raw.order_status ?? "open"),
|
|
139
|
+
type: String(raw.order_type ?? ""),
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async marketOrder(symbol, side, size) {
|
|
144
|
+
return this.client.createMarketOrder({ symbol, amount: size, side: side === "buy" ? "bid" : "ask", reduce_only: false, slippage_percent: "1" }, this.account, this.signMessage);
|
|
145
|
+
}
|
|
146
|
+
async limitOrder(symbol, side, price, size, opts) {
|
|
147
|
+
return this.client.createLimitOrder({ symbol, price, amount: size, side: side === "buy" ? "bid" : "ask", reduce_only: opts?.reduceOnly ?? false, tif: (opts?.tif ?? "GTC") }, this.account, this.signMessage);
|
|
148
|
+
}
|
|
149
|
+
async cancelOrder(symbol, orderId) {
|
|
150
|
+
return this.client.cancelOrder({ symbol, order_id: Number(orderId) }, this.account, this.signMessage);
|
|
151
|
+
}
|
|
152
|
+
async cancelAllOrders(_symbol) {
|
|
153
|
+
return this.client.cancelAllOrders({ all_symbols: true, exclude_reduce_only: false }, this.account, this.signMessage);
|
|
154
|
+
}
|
|
155
|
+
async editOrder(symbol, orderId, price, size) {
|
|
156
|
+
return this.client.editOrder({ symbol, order_id: Number(orderId), price, amount: size }, this.account, this.signMessage);
|
|
157
|
+
}
|
|
158
|
+
async setLeverage(symbol, leverage, marginMode = "cross") {
|
|
159
|
+
await this.client.updateLeverage({ symbol, leverage }, this.account, this.signMessage);
|
|
160
|
+
if (marginMode === "isolated") {
|
|
161
|
+
await this.client.updateMarginMode({ symbol, is_isolated: true }, this.account, this.signMessage);
|
|
162
|
+
}
|
|
163
|
+
return { symbol, leverage, marginMode };
|
|
164
|
+
}
|
|
165
|
+
async stopOrder(symbol, side, size, triggerPrice, opts) {
|
|
166
|
+
return this.client.createStopOrder({
|
|
167
|
+
symbol,
|
|
168
|
+
side: side === "buy" ? "bid" : "ask",
|
|
169
|
+
reduce_only: opts?.reduceOnly ?? false,
|
|
170
|
+
stop_order: { stop_price: triggerPrice, amount: size, limit_price: opts?.limitPrice },
|
|
171
|
+
}, this.account, this.signMessage);
|
|
172
|
+
}
|
|
173
|
+
async getRecentTrades(symbol, _limit = 20) {
|
|
174
|
+
const trades = await this.client.getTrades(symbol);
|
|
175
|
+
return trades.slice(0, _limit).map((t) => ({
|
|
176
|
+
time: new Date(t.created_at).getTime(),
|
|
177
|
+
symbol,
|
|
178
|
+
side: t.side === "bid" ? "buy" : "sell",
|
|
179
|
+
price: t.price,
|
|
180
|
+
size: t.amount,
|
|
181
|
+
fee: "0",
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
async getFundingHistory(symbol, limit = 10) {
|
|
185
|
+
const result = await this.client.getFundingHistory(symbol, { limit });
|
|
186
|
+
const data = (result.data ?? result);
|
|
187
|
+
if (!Array.isArray(data))
|
|
188
|
+
return [];
|
|
189
|
+
return data.map((h) => ({
|
|
190
|
+
time: Number(h.created_at ?? 0),
|
|
191
|
+
rate: String(h.funding_rate ?? "0"),
|
|
192
|
+
price: String(h.oracle_price ?? "0"),
|
|
193
|
+
}));
|
|
194
|
+
}
|
|
195
|
+
async getKlines(symbol, interval, startTime, endTime) {
|
|
196
|
+
const klines = await this.client.getKline(symbol, interval, startTime, endTime);
|
|
197
|
+
return klines.map((k) => ({
|
|
198
|
+
time: k.t,
|
|
199
|
+
open: k.o,
|
|
200
|
+
high: k.h,
|
|
201
|
+
low: k.l,
|
|
202
|
+
close: k.c,
|
|
203
|
+
volume: k.v,
|
|
204
|
+
trades: k.n,
|
|
205
|
+
}));
|
|
206
|
+
}
|
|
207
|
+
async getOrderHistory(limit = 30) {
|
|
208
|
+
const result = await this.client.getOrderHistory(this.account);
|
|
209
|
+
const data = (result.data ?? result.orders ?? []);
|
|
210
|
+
if (!Array.isArray(data))
|
|
211
|
+
return [];
|
|
212
|
+
return data.slice(0, limit).map((o) => ({
|
|
213
|
+
orderId: String(o.order_id ?? ""),
|
|
214
|
+
symbol: String(o.symbol ?? ""),
|
|
215
|
+
side: String(o.side) === "bid" ? "buy" : "sell",
|
|
216
|
+
price: String(o.initial_price ?? o.price ?? "0"),
|
|
217
|
+
size: String(o.amount ?? o.initial_amount ?? ""),
|
|
218
|
+
filled: String(o.filled_amount ?? "0"),
|
|
219
|
+
status: String(o.order_status ?? "done"),
|
|
220
|
+
type: String(o.order_type ?? ""),
|
|
221
|
+
}));
|
|
222
|
+
}
|
|
223
|
+
async getTradeHistory(limit = 30) {
|
|
224
|
+
const raw = await this.client.getTradeHistory(this.account);
|
|
225
|
+
const trades = (raw.data ?? raw);
|
|
226
|
+
if (!Array.isArray(trades))
|
|
227
|
+
return [];
|
|
228
|
+
return trades.slice(0, limit).map((t) => ({
|
|
229
|
+
time: Number(t.created_at ?? 0),
|
|
230
|
+
symbol: String(t.symbol ?? ""),
|
|
231
|
+
side: String(t.side) === "bid" ? "buy" : "sell",
|
|
232
|
+
price: String(t.price ?? "0"),
|
|
233
|
+
size: String(t.amount ?? ""),
|
|
234
|
+
fee: String(t.fee ?? "0"),
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
async getFundingPayments(limit = 30) {
|
|
238
|
+
const raw = await this.client.getFundingAccountHistory(this.account);
|
|
239
|
+
const history = (raw.data ?? raw);
|
|
240
|
+
if (!Array.isArray(history))
|
|
241
|
+
return [];
|
|
242
|
+
return history.slice(0, limit).map((h) => ({
|
|
243
|
+
time: Number(h.created_at ?? 0),
|
|
244
|
+
symbol: String(h.symbol ?? ""),
|
|
245
|
+
payment: String(h.amount ?? "0"),
|
|
246
|
+
}));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface ExecutionRecord {
|
|
2
|
+
id: string;
|
|
3
|
+
timestamp: string;
|
|
4
|
+
type: "market_order" | "limit_order" | "cancel" | "stop_order" | "edit_order" | "rebalance" | "arb_entry" | "arb_close" | "bridge";
|
|
5
|
+
exchange: string;
|
|
6
|
+
symbol: string;
|
|
7
|
+
side: string;
|
|
8
|
+
size: string;
|
|
9
|
+
price?: string;
|
|
10
|
+
notional?: number;
|
|
11
|
+
status: "success" | "failed" | "simulated";
|
|
12
|
+
error?: string;
|
|
13
|
+
meta?: Record<string, unknown>;
|
|
14
|
+
dryRun: boolean;
|
|
15
|
+
}
|
|
16
|
+
/** Append an execution record to the log */
|
|
17
|
+
export declare function logExecution(record: Omit<ExecutionRecord, "id" | "timestamp">): ExecutionRecord;
|
|
18
|
+
/** Read execution log with optional filters */
|
|
19
|
+
export declare function readExecutionLog(opts?: {
|
|
20
|
+
limit?: number;
|
|
21
|
+
exchange?: string;
|
|
22
|
+
symbol?: string;
|
|
23
|
+
type?: string;
|
|
24
|
+
since?: string;
|
|
25
|
+
dryRunOnly?: boolean;
|
|
26
|
+
}): ExecutionRecord[];
|
|
27
|
+
/** Get execution stats summary */
|
|
28
|
+
export declare function getExecutionStats(since?: string): {
|
|
29
|
+
totalTrades: number;
|
|
30
|
+
successRate: number;
|
|
31
|
+
byExchange: Record<string, number>;
|
|
32
|
+
byType: Record<string, number>;
|
|
33
|
+
recentErrors: string[];
|
|
34
|
+
};
|
|
35
|
+
/** Clear old records (keep last N days) */
|
|
36
|
+
export declare function pruneExecutionLog(keepDays?: number): number;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { existsSync, appendFileSync, readFileSync, mkdirSync, writeFileSync } from "fs";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
const PERP_DIR = resolve(process.env.HOME || "~", ".perp");
|
|
4
|
+
const LOG_FILE = resolve(PERP_DIR, "executions.jsonl");
|
|
5
|
+
function ensureDir() {
|
|
6
|
+
if (!existsSync(PERP_DIR))
|
|
7
|
+
mkdirSync(PERP_DIR, { recursive: true, mode: 0o700 });
|
|
8
|
+
}
|
|
9
|
+
function genId() {
|
|
10
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
11
|
+
}
|
|
12
|
+
/** Append an execution record to the log */
|
|
13
|
+
export function logExecution(record) {
|
|
14
|
+
ensureDir();
|
|
15
|
+
const full = {
|
|
16
|
+
id: genId(),
|
|
17
|
+
timestamp: new Date().toISOString(),
|
|
18
|
+
...record,
|
|
19
|
+
};
|
|
20
|
+
appendFileSync(LOG_FILE, JSON.stringify(full) + "\n", { mode: 0o600 });
|
|
21
|
+
return full;
|
|
22
|
+
}
|
|
23
|
+
/** Read execution log with optional filters */
|
|
24
|
+
export function readExecutionLog(opts) {
|
|
25
|
+
if (!existsSync(LOG_FILE))
|
|
26
|
+
return [];
|
|
27
|
+
const lines = readFileSync(LOG_FILE, "utf-8").trim().split("\n").filter(Boolean);
|
|
28
|
+
let records = lines.map(line => {
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(line);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}).filter(Boolean);
|
|
36
|
+
// Apply filters
|
|
37
|
+
if (opts?.exchange) {
|
|
38
|
+
records = records.filter(r => r.exchange === opts.exchange);
|
|
39
|
+
}
|
|
40
|
+
if (opts?.symbol) {
|
|
41
|
+
records = records.filter(r => r.symbol.toUpperCase().includes(opts.symbol.toUpperCase()));
|
|
42
|
+
}
|
|
43
|
+
if (opts?.type) {
|
|
44
|
+
records = records.filter(r => r.type === opts.type);
|
|
45
|
+
}
|
|
46
|
+
if (opts?.since) {
|
|
47
|
+
const sinceDate = new Date(opts.since).getTime();
|
|
48
|
+
records = records.filter(r => new Date(r.timestamp).getTime() >= sinceDate);
|
|
49
|
+
}
|
|
50
|
+
if (opts?.dryRunOnly) {
|
|
51
|
+
records = records.filter(r => r.dryRun);
|
|
52
|
+
}
|
|
53
|
+
// Sort newest first
|
|
54
|
+
records.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
|
55
|
+
// Limit
|
|
56
|
+
if (opts?.limit) {
|
|
57
|
+
records = records.slice(0, opts.limit);
|
|
58
|
+
}
|
|
59
|
+
return records;
|
|
60
|
+
}
|
|
61
|
+
/** Get execution stats summary */
|
|
62
|
+
export function getExecutionStats(since) {
|
|
63
|
+
const records = readExecutionLog({ since });
|
|
64
|
+
const successful = records.filter(r => r.status === "success");
|
|
65
|
+
const byExchange = {};
|
|
66
|
+
const byType = {};
|
|
67
|
+
const recentErrors = [];
|
|
68
|
+
for (const r of records) {
|
|
69
|
+
byExchange[r.exchange] = (byExchange[r.exchange] || 0) + 1;
|
|
70
|
+
byType[r.type] = (byType[r.type] || 0) + 1;
|
|
71
|
+
if (r.status === "failed" && r.error && recentErrors.length < 5) {
|
|
72
|
+
recentErrors.push(`[${r.exchange}] ${r.error}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
totalTrades: records.length,
|
|
77
|
+
successRate: records.length > 0 ? (successful.length / records.length) * 100 : 0,
|
|
78
|
+
byExchange,
|
|
79
|
+
byType,
|
|
80
|
+
recentErrors,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Clear old records (keep last N days) */
|
|
84
|
+
export function pruneExecutionLog(keepDays = 30) {
|
|
85
|
+
if (!existsSync(LOG_FILE))
|
|
86
|
+
return 0;
|
|
87
|
+
const lines = readFileSync(LOG_FILE, "utf-8").trim().split("\n").filter(Boolean);
|
|
88
|
+
const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1000;
|
|
89
|
+
const kept = lines.filter(line => {
|
|
90
|
+
try {
|
|
91
|
+
const r = JSON.parse(line);
|
|
92
|
+
return new Date(r.timestamp).getTime() >= cutoff;
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
const pruned = lines.length - kept.length;
|
|
99
|
+
ensureDir();
|
|
100
|
+
writeFileSync(LOG_FILE, kept.join("\n") + (kept.length > 0 ? "\n" : ""), { mode: 0o600 });
|
|
101
|
+
return pruned;
|
|
102
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Historical funding rate tracking.
|
|
3
|
+
*
|
|
4
|
+
* Stores funding rate snapshots as JSONL files in ~/.perp/funding-rates/
|
|
5
|
+
* organized by month (YYYY-MM.jsonl). Provides averaging and trend analysis
|
|
6
|
+
* over configurable time windows.
|
|
7
|
+
*/
|
|
8
|
+
import type { ExchangeFundingRate } from "./rates.js";
|
|
9
|
+
export interface FundingHistoryEntry {
|
|
10
|
+
ts: string;
|
|
11
|
+
symbol: string;
|
|
12
|
+
exchange: string;
|
|
13
|
+
rate: number;
|
|
14
|
+
hourlyRate: number;
|
|
15
|
+
}
|
|
16
|
+
export interface HistoricalAverages {
|
|
17
|
+
avg1h: number | null;
|
|
18
|
+
avg8h: number | null;
|
|
19
|
+
avg24h: number | null;
|
|
20
|
+
avg7d: number | null;
|
|
21
|
+
}
|
|
22
|
+
export declare function cleanupOldFiles(): void;
|
|
23
|
+
export declare function _resetCleanupFlag(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Save current funding rates as a historical snapshot.
|
|
26
|
+
* Deduplicates: skips entries if same symbol+exchange was saved within 5 minutes.
|
|
27
|
+
*/
|
|
28
|
+
export declare function saveFundingSnapshot(rates: ExchangeFundingRate[]): void;
|
|
29
|
+
/**
|
|
30
|
+
* Get average funding rate for a symbol+exchange over the last N hours.
|
|
31
|
+
* Returns null if no data available.
|
|
32
|
+
*/
|
|
33
|
+
export declare function getAvgFundingRate(symbol: string, exchange: string, hours: number): number | null;
|
|
34
|
+
/**
|
|
35
|
+
* Get all historical rates for a symbol+exchange in a time range.
|
|
36
|
+
*/
|
|
37
|
+
export declare function getHistoricalRates(symbol: string, exchange: string, startTime: Date, endTime: Date): {
|
|
38
|
+
ts: string;
|
|
39
|
+
rate: number;
|
|
40
|
+
hourlyRate: number;
|
|
41
|
+
}[];
|
|
42
|
+
/**
|
|
43
|
+
* Get averaged rates for all symbols across time windows.
|
|
44
|
+
* Returns a Map with key format "SYMBOL:exchange".
|
|
45
|
+
*/
|
|
46
|
+
export declare function getHistoricalAverages(symbols: string[], exchanges: string[]): Map<string, HistoricalAverages>;
|
|
47
|
+
/**
|
|
48
|
+
* Calculate effective annualized return considering compounding frequency.
|
|
49
|
+
*
|
|
50
|
+
* All three main exchanges (HL, PAC, LT) compound every 1h (8760 times/year).
|
|
51
|
+
*
|
|
52
|
+
* Formula: (1 + rate)^(8760/compoundingHours) - 1
|
|
53
|
+
*
|
|
54
|
+
* @param hourlyRate - the per-hour funding rate
|
|
55
|
+
* @param compoundingHours - how often the exchange compounds (1 for all main exchanges)
|
|
56
|
+
* @returns effective annualized return as a decimal (not percentage)
|
|
57
|
+
*/
|
|
58
|
+
export declare function getCompoundedAnnualReturn(hourlyRate: number, compoundingHours: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* Get the compounding hours for an exchange.
|
|
61
|
+
* All main exchanges (HL, PAC, LT) compound every 1h.
|
|
62
|
+
*/
|
|
63
|
+
export declare function getExchangeCompoundingHours(exchange: string): number;
|