@raintree-technology/perps 0.1.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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/dist/adapters/aevo.d.ts +64 -0
- package/dist/adapters/aevo.js +899 -0
- package/dist/adapters/certification.d.ts +33 -0
- package/dist/adapters/certification.js +99 -0
- package/dist/adapters/decibel/order-manager.d.ts +45 -0
- package/dist/adapters/decibel/order-manager.js +140 -0
- package/dist/adapters/decibel/rest-client.d.ts +176 -0
- package/dist/adapters/decibel/rest-client.js +155 -0
- package/dist/adapters/decibel/ws-feed.d.ts +28 -0
- package/dist/adapters/decibel/ws-feed.js +166 -0
- package/dist/adapters/decibel.d.ts +108 -0
- package/dist/adapters/decibel.js +1377 -0
- package/dist/adapters/hyperliquid.d.ts +63 -0
- package/dist/adapters/hyperliquid.js +797 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/interface.d.ts +310 -0
- package/dist/adapters/interface.js +15 -0
- package/dist/adapters/orderly.d.ts +70 -0
- package/dist/adapters/orderly.js +936 -0
- package/dist/adapters/paradex.d.ts +69 -0
- package/dist/adapters/paradex.js +862 -0
- package/dist/adapters/utils.d.ts +17 -0
- package/dist/adapters/utils.js +122 -0
- package/dist/cli/command-metadata.d.ts +2 -0
- package/dist/cli/command-metadata.js +44 -0
- package/dist/cli/context.d.ts +14 -0
- package/dist/cli/context.js +59 -0
- package/dist/cli/experience.d.ts +48 -0
- package/dist/cli/experience.js +243 -0
- package/dist/cli/ink/app/AppShell.d.ts +12 -0
- package/dist/cli/ink/app/AppShell.js +32 -0
- package/dist/cli/ink/app/MetricStrip.d.ts +6 -0
- package/dist/cli/ink/app/MetricStrip.js +14 -0
- package/dist/cli/ink/app/Panel.d.ts +9 -0
- package/dist/cli/ink/app/Panel.js +7 -0
- package/dist/cli/ink/app/ascii.d.ts +2 -0
- package/dist/cli/ink/app/ascii.js +46 -0
- package/dist/cli/ink/app/index.d.ts +5 -0
- package/dist/cli/ink/app/index.js +4 -0
- package/dist/cli/ink/app/types.d.ts +15 -0
- package/dist/cli/ink/app/types.js +1 -0
- package/dist/cli/ink/components/PnL.d.ts +12 -0
- package/dist/cli/ink/components/PnL.js +23 -0
- package/dist/cli/ink/components/Spinner.d.ts +13 -0
- package/dist/cli/ink/components/Spinner.js +13 -0
- package/dist/cli/ink/components/Table.d.ts +14 -0
- package/dist/cli/ink/components/Table.js +42 -0
- package/dist/cli/ink/components/WatchHeader.d.ts +10 -0
- package/dist/cli/ink/components/WatchHeader.js +18 -0
- package/dist/cli/ink/components/index.d.ts +4 -0
- package/dist/cli/ink/components/index.js +4 -0
- package/dist/cli/ink/index.d.ts +4 -0
- package/dist/cli/ink/index.js +4 -0
- package/dist/cli/ink/render.d.ts +12 -0
- package/dist/cli/ink/render.js +21 -0
- package/dist/cli/ink/theme.d.ts +29 -0
- package/dist/cli/ink/theme.js +40 -0
- package/dist/cli/network-defaults.d.ts +10 -0
- package/dist/cli/network-defaults.js +35 -0
- package/dist/cli/output.d.ts +11 -0
- package/dist/cli/output.js +115 -0
- package/dist/cli/program.d.ts +18 -0
- package/dist/cli/program.js +164 -0
- package/dist/cli/watch.d.ts +19 -0
- package/dist/cli/watch.js +35 -0
- package/dist/client/index.d.ts +55 -0
- package/dist/client/index.js +157 -0
- package/dist/commands/account/add.d.ts +2 -0
- package/dist/commands/account/add.js +510 -0
- package/dist/commands/account/balances-simple.d.ts +5 -0
- package/dist/commands/account/balances-simple.js +63 -0
- package/dist/commands/account/index.d.ts +2 -0
- package/dist/commands/account/index.js +17 -0
- package/dist/commands/account/ls.d.ts +2 -0
- package/dist/commands/account/ls.js +95 -0
- package/dist/commands/account/positions-simple.d.ts +5 -0
- package/dist/commands/account/positions-simple.js +77 -0
- package/dist/commands/account/remove.d.ts +2 -0
- package/dist/commands/account/remove.js +47 -0
- package/dist/commands/account/set-default.d.ts +2 -0
- package/dist/commands/account/set-default.js +47 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +126 -0
- package/dist/commands/arb/alert.d.ts +6 -0
- package/dist/commands/arb/alert.js +88 -0
- package/dist/commands/arb/basis-execute.d.ts +6 -0
- package/dist/commands/arb/basis-execute.js +332 -0
- package/dist/commands/arb/basis.d.ts +6 -0
- package/dist/commands/arb/basis.js +181 -0
- package/dist/commands/arb/compare.d.ts +6 -0
- package/dist/commands/arb/compare.js +216 -0
- package/dist/commands/arb/execute.d.ts +6 -0
- package/dist/commands/arb/execute.js +467 -0
- package/dist/commands/arb/funding.d.ts +6 -0
- package/dist/commands/arb/funding.js +201 -0
- package/dist/commands/arb/history.d.ts +6 -0
- package/dist/commands/arb/history.js +153 -0
- package/dist/commands/arb/index.d.ts +6 -0
- package/dist/commands/arb/index.js +29 -0
- package/dist/commands/arb/positions.d.ts +6 -0
- package/dist/commands/arb/positions.js +158 -0
- package/dist/commands/arb/spread.d.ts +6 -0
- package/dist/commands/arb/spread.js +253 -0
- package/dist/commands/arb/track.d.ts +6 -0
- package/dist/commands/arb/track.js +259 -0
- package/dist/commands/asset/book-simple.d.ts +5 -0
- package/dist/commands/asset/book-simple.js +77 -0
- package/dist/commands/asset/index.d.ts +2 -0
- package/dist/commands/asset/index.js +5 -0
- package/dist/commands/completion.d.ts +2 -0
- package/dist/commands/completion.js +161 -0
- package/dist/commands/config/index.d.ts +5 -0
- package/dist/commands/config/index.js +109 -0
- package/dist/commands/data/index.d.ts +31 -0
- package/dist/commands/data/index.js +1466 -0
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +201 -0
- package/dist/commands/exchange/index.d.ts +2 -0
- package/dist/commands/exchange/index.js +107 -0
- package/dist/commands/index.d.ts +2 -0
- package/dist/commands/index.js +48 -0
- package/dist/commands/markets/index.d.ts +2 -0
- package/dist/commands/markets/index.js +5 -0
- package/dist/commands/markets/ls-simple.d.ts +7 -0
- package/dist/commands/markets/ls-simple.js +277 -0
- package/dist/commands/operator/index.d.ts +2 -0
- package/dist/commands/operator/index.js +146 -0
- package/dist/commands/order/cancel-simple.d.ts +5 -0
- package/dist/commands/order/cancel-simple.js +104 -0
- package/dist/commands/order/index.d.ts +2 -0
- package/dist/commands/order/index.js +13 -0
- package/dist/commands/order/limit-simple.d.ts +5 -0
- package/dist/commands/order/limit-simple.js +195 -0
- package/dist/commands/order/market-simple.d.ts +5 -0
- package/dist/commands/order/market-simple.js +190 -0
- package/dist/commands/order/shared.d.ts +17 -0
- package/dist/commands/order/shared.js +51 -0
- package/dist/commands/order/trigger-simple.d.ts +5 -0
- package/dist/commands/order/trigger-simple.js +246 -0
- package/dist/commands/referral/index.d.ts +2 -0
- package/dist/commands/referral/index.js +7 -0
- package/dist/commands/referral/set.d.ts +2 -0
- package/dist/commands/referral/set.js +26 -0
- package/dist/commands/referral/status.d.ts +2 -0
- package/dist/commands/referral/status.js +31 -0
- package/dist/commands/replay/index.d.ts +2 -0
- package/dist/commands/replay/index.js +152 -0
- package/dist/commands/risk/analytics.d.ts +2 -0
- package/dist/commands/risk/analytics.js +64 -0
- package/dist/commands/risk/audit.d.ts +2 -0
- package/dist/commands/risk/audit.js +52 -0
- package/dist/commands/risk/index.d.ts +2 -0
- package/dist/commands/risk/index.js +9 -0
- package/dist/commands/risk/rules.d.ts +2 -0
- package/dist/commands/risk/rules.js +102 -0
- package/dist/commands/server.d.ts +2 -0
- package/dist/commands/server.js +208 -0
- package/dist/commands/setup/index.d.ts +2 -0
- package/dist/commands/setup/index.js +478 -0
- package/dist/commands/signal/index.d.ts +2 -0
- package/dist/commands/signal/index.js +129 -0
- package/dist/commands/state/index.d.ts +2 -0
- package/dist/commands/state/index.js +5 -0
- package/dist/commands/state/show.d.ts +2 -0
- package/dist/commands/state/show.js +105 -0
- package/dist/commands/strategy/index.d.ts +4 -0
- package/dist/commands/strategy/index.js +73 -0
- package/dist/commands/traces/index.d.ts +2 -0
- package/dist/commands/traces/index.js +76 -0
- package/dist/commands/ui/demo.d.ts +9 -0
- package/dist/commands/ui/demo.js +195 -0
- package/dist/commands/ui/index.d.ts +2 -0
- package/dist/commands/ui/index.js +7 -0
- package/dist/commands/ui/terminal.d.ts +2 -0
- package/dist/commands/ui/terminal.js +255 -0
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.js +98 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -0
- package/dist/lib/agent/audit.d.ts +12 -0
- package/dist/lib/agent/audit.js +13 -0
- package/dist/lib/agent/gateway.d.ts +13 -0
- package/dist/lib/agent/gateway.js +598 -0
- package/dist/lib/agent/metrics.d.ts +33 -0
- package/dist/lib/agent/metrics.js +175 -0
- package/dist/lib/agent/signature.d.ts +8 -0
- package/dist/lib/agent/signature.js +28 -0
- package/dist/lib/agent/tools.d.ts +28 -0
- package/dist/lib/agent/tools.js +453 -0
- package/dist/lib/agent/x402.d.ts +23 -0
- package/dist/lib/agent/x402.js +62 -0
- package/dist/lib/api-wallet.d.ts +69 -0
- package/dist/lib/api-wallet.js +101 -0
- package/dist/lib/balance-watcher.d.ts +25 -0
- package/dist/lib/balance-watcher.js +83 -0
- package/dist/lib/book-watcher.d.ts +25 -0
- package/dist/lib/book-watcher.js +48 -0
- package/dist/lib/config.d.ts +88 -0
- package/dist/lib/config.js +427 -0
- package/dist/lib/constants.d.ts +50 -0
- package/dist/lib/constants.js +84 -0
- package/dist/lib/contracts.d.ts +7 -0
- package/dist/lib/contracts.js +8 -0
- package/dist/lib/credential-vault.d.ts +22 -0
- package/dist/lib/credential-vault.js +109 -0
- package/dist/lib/db/accounts.d.ts +83 -0
- package/dist/lib/db/accounts.js +203 -0
- package/dist/lib/db/funding-history.d.ts +69 -0
- package/dist/lib/db/funding-history.js +183 -0
- package/dist/lib/db/index.d.ts +11 -0
- package/dist/lib/db/index.js +272 -0
- package/dist/lib/events/bus.d.ts +10 -0
- package/dist/lib/events/bus.js +17 -0
- package/dist/lib/events/types.d.ts +51 -0
- package/dist/lib/events/types.js +1 -0
- package/dist/lib/exchange.d.ts +30 -0
- package/dist/lib/exchange.js +84 -0
- package/dist/lib/execution/journal.d.ts +25 -0
- package/dist/lib/execution/journal.js +158 -0
- package/dist/lib/execution/safety.d.ts +34 -0
- package/dist/lib/execution/safety.js +197 -0
- package/dist/lib/exit-codes.d.ts +18 -0
- package/dist/lib/exit-codes.js +60 -0
- package/dist/lib/fetch.d.ts +18 -0
- package/dist/lib/fetch.js +66 -0
- package/dist/lib/fs-security.d.ts +10 -0
- package/dist/lib/fs-security.js +26 -0
- package/dist/lib/funding-tracker.d.ts +40 -0
- package/dist/lib/funding-tracker.js +118 -0
- package/dist/lib/logger.d.ts +27 -0
- package/dist/lib/logger.js +82 -0
- package/dist/lib/network-model.d.ts +13 -0
- package/dist/lib/network-model.js +30 -0
- package/dist/lib/onboarding.d.ts +133 -0
- package/dist/lib/onboarding.js +1459 -0
- package/dist/lib/operator-state.d.ts +25 -0
- package/dist/lib/operator-state.js +82 -0
- package/dist/lib/orders-watcher.d.ts +24 -0
- package/dist/lib/orders-watcher.js +74 -0
- package/dist/lib/paths.d.ts +20 -0
- package/dist/lib/paths.js +23 -0
- package/dist/lib/portfolio-watcher.d.ts +33 -0
- package/dist/lib/portfolio-watcher.js +95 -0
- package/dist/lib/position-watcher.d.ts +16 -0
- package/dist/lib/position-watcher.js +44 -0
- package/dist/lib/price-watcher.d.ts +15 -0
- package/dist/lib/price-watcher.js +84 -0
- package/dist/lib/prompts.d.ts +32 -0
- package/dist/lib/prompts.js +105 -0
- package/dist/lib/rate-limit.d.ts +32 -0
- package/dist/lib/rate-limit.js +88 -0
- package/dist/lib/risk/analytics.d.ts +39 -0
- package/dist/lib/risk/analytics.js +98 -0
- package/dist/lib/risk/drawdown.d.ts +18 -0
- package/dist/lib/risk/drawdown.js +49 -0
- package/dist/lib/risk/evaluation-log.d.ts +29 -0
- package/dist/lib/risk/evaluation-log.js +61 -0
- package/dist/lib/risk/index.d.ts +4 -0
- package/dist/lib/risk/index.js +4 -0
- package/dist/lib/risk/limits.d.ts +23 -0
- package/dist/lib/risk/limits.js +27 -0
- package/dist/lib/risk/manager.d.ts +32 -0
- package/dist/lib/risk/manager.js +85 -0
- package/dist/lib/risk/policy-middleware.d.ts +33 -0
- package/dist/lib/risk/policy-middleware.js +267 -0
- package/dist/lib/risk/position-sizer.d.ts +9 -0
- package/dist/lib/risk/position-sizer.js +14 -0
- package/dist/lib/risk/rules-store.d.ts +16 -0
- package/dist/lib/risk/rules-store.js +47 -0
- package/dist/lib/schema.d.ts +254 -0
- package/dist/lib/schema.js +199 -0
- package/dist/lib/secrets.d.ts +3 -0
- package/dist/lib/secrets.js +62 -0
- package/dist/lib/settings.d.ts +24 -0
- package/dist/lib/settings.js +86 -0
- package/dist/lib/signals.d.ts +73 -0
- package/dist/lib/signals.js +136 -0
- package/dist/lib/stable-stringify.d.ts +6 -0
- package/dist/lib/stable-stringify.js +17 -0
- package/dist/lib/state-context.d.ts +44 -0
- package/dist/lib/state-context.js +133 -0
- package/dist/lib/strategy/basis-trade.d.ts +2 -0
- package/dist/lib/strategy/basis-trade.js +24 -0
- package/dist/lib/strategy/funding-arb.d.ts +2 -0
- package/dist/lib/strategy/funding-arb.js +23 -0
- package/dist/lib/strategy/interface.d.ts +23 -0
- package/dist/lib/strategy/interface.js +1 -0
- package/dist/lib/strategy/registry.d.ts +4 -0
- package/dist/lib/strategy/registry.js +10 -0
- package/dist/lib/telemetry.d.ts +25 -0
- package/dist/lib/telemetry.js +101 -0
- package/dist/lib/trace-queries.d.ts +20 -0
- package/dist/lib/trace-queries.js +133 -0
- package/dist/lib/trace.d.ts +1 -0
- package/dist/lib/trace.js +4 -0
- package/dist/lib/trade-reputation.d.ts +6 -0
- package/dist/lib/trade-reputation.js +99 -0
- package/dist/lib/ui-tokens.d.ts +21 -0
- package/dist/lib/ui-tokens.js +26 -0
- package/dist/lib/validate.d.ts +39 -0
- package/dist/lib/validate.js +108 -0
- package/dist/lib/validation.d.ts +9 -0
- package/dist/lib/validation.js +64 -0
- package/dist/server/cache.d.ts +38 -0
- package/dist/server/cache.js +56 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +89 -0
- package/dist/server/ipc.d.ts +18 -0
- package/dist/server/ipc.js +159 -0
- package/dist/server/subscriptions.d.ts +18 -0
- package/dist/server/subscriptions.js +114 -0
- package/package.json +124 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
import { Box, Text, render, useApp, useInput } from "ink";
|
|
4
|
+
import { getContext } from "../../cli/program.js";
|
|
5
|
+
import { AppShell, MetricStrip, Panel, Table, colors, toBar, toSparkline, } from "../../cli/ink/index.js";
|
|
6
|
+
import { pollFundingRates } from "../../lib/funding-tracker.js";
|
|
7
|
+
import { getSpreadStats, getRecordCount, cleanupOldRecords } from "../../lib/db/funding-history.js";
|
|
8
|
+
import { getAvailableExchanges } from "../../lib/exchange.js";
|
|
9
|
+
import { COMMON_ASSETS, FUNDING_HISTORY_RETENTION_DAYS } from "../../lib/constants.js";
|
|
10
|
+
import { parseAssetList, parsePositiveInt } from "../../lib/validate.js";
|
|
11
|
+
function formatRatePct(rate) {
|
|
12
|
+
const sign = rate >= 0 ? "+" : "";
|
|
13
|
+
return `${sign}${(rate * 100).toFixed(4)}%`;
|
|
14
|
+
}
|
|
15
|
+
function findExchangeRecord(assetRecords, exchange) {
|
|
16
|
+
return assetRecords.find((record) => record.exchange.toLowerCase().includes(exchange.toLowerCase()));
|
|
17
|
+
}
|
|
18
|
+
function edgeToSignal(edgeAnnualized, avg24h) {
|
|
19
|
+
if (edgeAnnualized >= 30 && (avg24h ?? 0) >= 10)
|
|
20
|
+
return "HUNT";
|
|
21
|
+
if (edgeAnnualized >= 10)
|
|
22
|
+
return "WATCH";
|
|
23
|
+
return "IDLE";
|
|
24
|
+
}
|
|
25
|
+
function signalColor(signal) {
|
|
26
|
+
if (signal === "HUNT")
|
|
27
|
+
return colors.profit;
|
|
28
|
+
if (signal === "WATCH")
|
|
29
|
+
return colors.warning;
|
|
30
|
+
return colors.muted;
|
|
31
|
+
}
|
|
32
|
+
function edgeColor(edgeAnnualized) {
|
|
33
|
+
if (edgeAnnualized >= 30)
|
|
34
|
+
return colors.profit;
|
|
35
|
+
if (edgeAnnualized >= 10)
|
|
36
|
+
return colors.warning;
|
|
37
|
+
return colors.muted;
|
|
38
|
+
}
|
|
39
|
+
function buildAssetSummaryRows(assets, exchanges, records, stats) {
|
|
40
|
+
const baseRows = assets.map((asset) => {
|
|
41
|
+
const market = `${asset}-PERP`;
|
|
42
|
+
const assetRecords = records.filter((record) => record.market === market);
|
|
43
|
+
const sorted = [...assetRecords].sort((left, right) => right.rate - left.rate);
|
|
44
|
+
const high = sorted[0];
|
|
45
|
+
const low = sorted[sorted.length - 1];
|
|
46
|
+
const edge = high && low ? (high.rate - low.rate) * 100 * 24 * 365 : 0;
|
|
47
|
+
const stat = stats.get(asset);
|
|
48
|
+
const rates = exchanges
|
|
49
|
+
.map((exchange) => {
|
|
50
|
+
const record = findExchangeRecord(assetRecords, exchange);
|
|
51
|
+
return record ? `${exchange}:${formatRatePct(record.rate)}` : `${exchange}:--`;
|
|
52
|
+
})
|
|
53
|
+
.join(" ");
|
|
54
|
+
return {
|
|
55
|
+
asset,
|
|
56
|
+
rates,
|
|
57
|
+
trend: toSparkline(assetRecords.map((record) => record.rate * 100), 10),
|
|
58
|
+
edge,
|
|
59
|
+
edgeBar: "",
|
|
60
|
+
avg24h: stat?.avg ?? null,
|
|
61
|
+
signal: edgeToSignal(edge, stat?.avg ?? null),
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
const maxEdge = Math.max(1, ...baseRows.map((row) => row.edge));
|
|
65
|
+
return baseRows.map((row) => ({
|
|
66
|
+
...row,
|
|
67
|
+
edgeBar: toBar(row.edge, maxEdge, 10, "#", "."),
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
function buildOpportunityRows(assets, records) {
|
|
71
|
+
const rows = [];
|
|
72
|
+
for (const asset of assets) {
|
|
73
|
+
const market = `${asset}-PERP`;
|
|
74
|
+
const assetRecords = records.filter((record) => record.market === market);
|
|
75
|
+
if (assetRecords.length < 2)
|
|
76
|
+
continue;
|
|
77
|
+
const sorted = [...assetRecords].sort((left, right) => right.rate - left.rate);
|
|
78
|
+
const high = sorted[0];
|
|
79
|
+
const low = sorted[sorted.length - 1];
|
|
80
|
+
const edge = (high.rate - low.rate) * 100 * 24 * 365;
|
|
81
|
+
rows.push({
|
|
82
|
+
market,
|
|
83
|
+
pair: `${high.exchange} -> ${low.exchange}`,
|
|
84
|
+
edge,
|
|
85
|
+
rates: `${formatRatePct(high.rate)} / ${formatRatePct(low.rate)}`,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return rows.sort((left, right) => right.edge - left.edge).slice(0, 8);
|
|
89
|
+
}
|
|
90
|
+
function TrackerUI({ assets, exchanges, intervalMin, testnet }) {
|
|
91
|
+
const [records, setRecords] = useState([]);
|
|
92
|
+
const [pollCount, setPollCount] = useState(0);
|
|
93
|
+
const [lastUpdate, setLastUpdate] = useState(null);
|
|
94
|
+
const [errors, setErrors] = useState(new Map());
|
|
95
|
+
const [stats, setStats] = useState(new Map());
|
|
96
|
+
const [pollMs, setPollMs] = useState(0);
|
|
97
|
+
const { exit } = useApp();
|
|
98
|
+
useInput((input, key) => {
|
|
99
|
+
if (input.toLowerCase() === "q" || key.escape || (key.ctrl && input.toLowerCase() === "c")) {
|
|
100
|
+
exit();
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
let cancelled = false;
|
|
105
|
+
const doPoll = async () => {
|
|
106
|
+
const start = performance.now();
|
|
107
|
+
const pollErrors = new Map();
|
|
108
|
+
try {
|
|
109
|
+
const newRecords = await pollFundingRates(assets, exchanges, (err, exchange) => {
|
|
110
|
+
pollErrors.set(exchange, err.message);
|
|
111
|
+
}, testnet);
|
|
112
|
+
if (cancelled)
|
|
113
|
+
return;
|
|
114
|
+
setRecords(newRecords);
|
|
115
|
+
setLastUpdate(new Date());
|
|
116
|
+
setPollCount((count) => count + 1);
|
|
117
|
+
setPollMs(Math.round(performance.now() - start));
|
|
118
|
+
setErrors(pollErrors);
|
|
119
|
+
const newStats = new Map();
|
|
120
|
+
for (const asset of assets) {
|
|
121
|
+
const s = getSpreadStats(`${asset}-PERP`, 24);
|
|
122
|
+
if (s.samples > 0) {
|
|
123
|
+
newStats.set(asset, { avg: s.avgAnnualized, consistency: s.consistency });
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
setStats(newStats);
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
if (cancelled)
|
|
130
|
+
return;
|
|
131
|
+
pollErrors.set("tracker", err instanceof Error ? err.message : String(err));
|
|
132
|
+
setErrors(pollErrors);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
void doPoll();
|
|
136
|
+
const interval = setInterval(() => {
|
|
137
|
+
void doPoll();
|
|
138
|
+
}, intervalMin * 60 * 1000);
|
|
139
|
+
return () => {
|
|
140
|
+
cancelled = true;
|
|
141
|
+
clearInterval(interval);
|
|
142
|
+
};
|
|
143
|
+
}, [assets, exchanges, intervalMin, testnet]);
|
|
144
|
+
const assetRows = useMemo(() => buildAssetSummaryRows(assets, exchanges, records, stats), [assets, exchanges, records, stats]);
|
|
145
|
+
const opportunityRows = useMemo(() => buildOpportunityRows(assets, records), [assets, records]);
|
|
146
|
+
const assetColumns = useMemo(() => [
|
|
147
|
+
{ key: "asset", header: "Asset", width: 8 },
|
|
148
|
+
{ key: "rates", header: "Rates", width: Math.max(24, exchanges.length * 14) },
|
|
149
|
+
{ key: "trend", header: "Trend", width: 10 },
|
|
150
|
+
{
|
|
151
|
+
key: "edge",
|
|
152
|
+
header: "Edge",
|
|
153
|
+
align: "right",
|
|
154
|
+
width: 24,
|
|
155
|
+
render: (value, row) => {
|
|
156
|
+
const edge = value;
|
|
157
|
+
return (_jsxs(Text, { color: edgeColor(edge), children: [row.edgeBar, " ", edge.toFixed(1), "%/yr"] }));
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
key: "avg24h",
|
|
162
|
+
header: "24h Avg",
|
|
163
|
+
align: "right",
|
|
164
|
+
width: 11,
|
|
165
|
+
render: (value) => {
|
|
166
|
+
const average = value;
|
|
167
|
+
if (average === null)
|
|
168
|
+
return _jsx(Text, { color: colors.muted, children: "--" });
|
|
169
|
+
return _jsxs(Text, { color: edgeColor(average), children: [average.toFixed(1), "%/yr"] });
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
key: "signal",
|
|
174
|
+
header: "Signal",
|
|
175
|
+
align: "right",
|
|
176
|
+
width: 6,
|
|
177
|
+
render: (value) => {
|
|
178
|
+
const signal = value;
|
|
179
|
+
return _jsx(Text, { color: signalColor(signal), children: signal });
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
], [exchanges.length]);
|
|
183
|
+
const opportunityColumns = [
|
|
184
|
+
{ key: "market", header: "Market", width: 10 },
|
|
185
|
+
{ key: "pair", header: "Pair", width: 40 },
|
|
186
|
+
{
|
|
187
|
+
key: "edge",
|
|
188
|
+
header: "Edge",
|
|
189
|
+
align: "right",
|
|
190
|
+
width: 12,
|
|
191
|
+
render: (value) => {
|
|
192
|
+
const edge = value;
|
|
193
|
+
return _jsxs(Text, { color: edgeColor(edge), children: [edge.toFixed(1), "%/yr"] });
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
{ key: "rates", header: "Rates", width: 24 },
|
|
197
|
+
];
|
|
198
|
+
const totalRecords = getRecordCount();
|
|
199
|
+
const activeSignals = assetRows.filter((row) => row.signal !== "IDLE").length;
|
|
200
|
+
const metrics = [
|
|
201
|
+
{ label: "Polls", value: String(pollCount), tone: "neutral" },
|
|
202
|
+
{ label: "Rows", value: String(records.length), tone: "neutral" },
|
|
203
|
+
{ label: "Stored", value: String(totalRecords), tone: "neutral" },
|
|
204
|
+
{
|
|
205
|
+
label: "Latency",
|
|
206
|
+
value: pollMs > 0 ? `${pollMs}ms` : "--",
|
|
207
|
+
tone: pollMs > 2_000 ? "danger" : pollMs > 1_000 ? "warning" : "success",
|
|
208
|
+
},
|
|
209
|
+
{ label: "Signals", value: String(activeSignals), tone: activeSignals > 0 ? "info" : "neutral" },
|
|
210
|
+
];
|
|
211
|
+
return (_jsxs(AppShell, { title: "Funding Rate Tracker", subtitle: `${testnet ? "testnet" : "mainnet"} • poll interval ${intervalMin}m`, status: [
|
|
212
|
+
{ label: "LIVE", tone: "success" },
|
|
213
|
+
{ label: `${errors.size} ERR`, tone: errors.size > 0 ? "danger" : "neutral" },
|
|
214
|
+
], lastUpdated: lastUpdate ?? undefined, shortcuts: [
|
|
215
|
+
{ key: "q", label: "quit" },
|
|
216
|
+
{ key: "esc", label: "quit" },
|
|
217
|
+
{ key: "ctrl+c", label: "quit" },
|
|
218
|
+
], children: [_jsx(MetricStrip, { metrics: metrics }), _jsx(Box, { flexDirection: "column", marginBottom: 1, children: _jsx(Panel, { title: "Asset Matrix", subtitle: "Cross-exchange rates, edge bars, and 24h signal quality", tone: "info", children: assetRows.length === 0 ? (_jsx(Text, { color: colors.muted, children: "Waiting for first poll..." })) : (_jsx(Table, { data: assetRows, columns: assetColumns })) }) }), _jsx(Box, { flexDirection: "column", marginBottom: errors.size > 0 ? 1 : 0, children: _jsx(Panel, { title: "Top Opportunities", subtitle: "Best spread by asset from latest poll", children: opportunityRows.length === 0 ? (_jsx(Text, { color: colors.muted, children: "Need at least 2 exchange quotes per asset." })) : (_jsx(Table, { data: opportunityRows, columns: opportunityColumns })) }) }), errors.size > 0 && (_jsx(Panel, { title: "Errors", subtitle: "Latest polling errors by exchange", tone: "danger", children: _jsx(Box, { flexDirection: "column", children: Array.from(errors.entries()).map(([exchange, message]) => (_jsxs(Text, { color: colors.loss, children: [exchange, ": ", message] }, exchange))) }) }))] }));
|
|
219
|
+
}
|
|
220
|
+
export function registerArbTrackCommand(arb) {
|
|
221
|
+
arb
|
|
222
|
+
.command("track")
|
|
223
|
+
.description("Start live funding rate tracking")
|
|
224
|
+
.option("-a, --assets <assets>", "Comma-separated assets to track", COMMON_ASSETS.join(","))
|
|
225
|
+
.option("-i, --interval <minutes>", "Poll interval in minutes", "5")
|
|
226
|
+
.option("--once", "Poll once and exit (no live tracking)")
|
|
227
|
+
.option("--cleanup", `Clean up old records (>${FUNDING_HISTORY_RETENTION_DAYS} days)`)
|
|
228
|
+
.action(async function () {
|
|
229
|
+
const ctx = getContext(this);
|
|
230
|
+
const opts = this.opts();
|
|
231
|
+
const assets = parseAssetList(opts.assets, COMMON_ASSETS);
|
|
232
|
+
const intervalMin = parsePositiveInt(opts.interval, "interval", 5);
|
|
233
|
+
const exchanges = getAvailableExchanges();
|
|
234
|
+
const isTestnet = ctx.config.testnet;
|
|
235
|
+
if (opts.cleanup) {
|
|
236
|
+
const deleted = cleanupOldRecords(FUNDING_HISTORY_RETENTION_DAYS);
|
|
237
|
+
console.log(`Cleaned up ${deleted} old records`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (opts.once) {
|
|
241
|
+
console.log(`\nPolling funding rates for ${assets.join(", ")}...\n`);
|
|
242
|
+
const records = await pollFundingRates(assets, exchanges, undefined, isTestnet);
|
|
243
|
+
console.log("Exchange".padEnd(16) + "Market".padEnd(12) + "Rate".padEnd(14) + "Annualized");
|
|
244
|
+
console.log("─".repeat(55));
|
|
245
|
+
for (const r of records.sort((a, b) => a.market.localeCompare(b.market) || b.rate - a.rate)) {
|
|
246
|
+
const sign = r.rate >= 0 ? "+" : "";
|
|
247
|
+
console.log(`${r.exchange.padEnd(14)}` +
|
|
248
|
+
`${r.market.padEnd(12)}` +
|
|
249
|
+
`${sign}${(r.rate * 100).toFixed(4)}%`.padEnd(14) +
|
|
250
|
+
`${sign}${r.annualized.toFixed(2)}%`);
|
|
251
|
+
}
|
|
252
|
+
console.log(`\nStored ${records.length} records`);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// Live tracking UI
|
|
256
|
+
const { waitUntilExit } = render(_jsx(TrackerUI, { assets: assets, exchanges: exchanges, intervalMin: intervalMin, testnet: isTestnet }));
|
|
257
|
+
await waitUntilExit();
|
|
258
|
+
});
|
|
259
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple order book command using the adapter interface
|
|
3
|
+
*/
|
|
4
|
+
import { getContext, getOutputOptions } from "../../cli/program.js";
|
|
5
|
+
import { output, outputError } from "../../cli/output.js";
|
|
6
|
+
import { getExchangeAdapter } from "../../lib/exchange.js";
|
|
7
|
+
export function registerBookSimpleCommand(asset) {
|
|
8
|
+
asset
|
|
9
|
+
.command("book <symbol>")
|
|
10
|
+
.description("Show order book for an asset")
|
|
11
|
+
.option("-d, --depth <number>", "Order book depth", "10")
|
|
12
|
+
.action(async function (symbol) {
|
|
13
|
+
const ctx = getContext(this);
|
|
14
|
+
const outputOpts = getOutputOptions(this);
|
|
15
|
+
const opts = this.opts();
|
|
16
|
+
const depth = parseInt(opts.depth, 10);
|
|
17
|
+
const adapter = getExchangeAdapter();
|
|
18
|
+
let connected = false;
|
|
19
|
+
try {
|
|
20
|
+
await adapter.connect({ testnet: ctx.config.testnet });
|
|
21
|
+
connected = true;
|
|
22
|
+
// Normalize symbol (accept BTC, BTC-PERP, btc, etc.)
|
|
23
|
+
let market = symbol.toUpperCase();
|
|
24
|
+
if (!market.includes("-")) {
|
|
25
|
+
market = `${market}-PERP`;
|
|
26
|
+
}
|
|
27
|
+
const book = await adapter.getOrderBook(market, depth);
|
|
28
|
+
if (outputOpts.json) {
|
|
29
|
+
output(book, outputOpts);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
console.log(`\n${adapter.info.name} Order Book: ${book.market}\n`);
|
|
33
|
+
// Calculate max sizes for formatting
|
|
34
|
+
const maxBidSize = Math.max(...book.bids.map(b => parseFloat(b.size)));
|
|
35
|
+
const maxAskSize = Math.max(...book.asks.map(a => parseFloat(a.size)));
|
|
36
|
+
const maxSize = Math.max(maxBidSize, maxAskSize);
|
|
37
|
+
// Header
|
|
38
|
+
console.log(" " + "Price".padEnd(14) + "Size".padEnd(14) + "│".padEnd(3) + "Price".padEnd(14) + "Size");
|
|
39
|
+
console.log(" " + "─".repeat(14) + "─".repeat(14) + "│" + "─".repeat(14) + "─".repeat(14));
|
|
40
|
+
// Asks (reversed so lowest ask is at bottom)
|
|
41
|
+
const asks = [...book.asks].reverse();
|
|
42
|
+
for (const ask of asks) {
|
|
43
|
+
const bar = "█".repeat(Math.ceil((parseFloat(ask.size) / maxSize) * 10));
|
|
44
|
+
console.log(" " + " ".repeat(28) + "│ " +
|
|
45
|
+
`\x1b[31m${parseFloat(ask.price).toFixed(2).padEnd(13)}\x1b[0m` +
|
|
46
|
+
parseFloat(ask.size).toFixed(4).padEnd(10) +
|
|
47
|
+
`\x1b[31m${bar}\x1b[0m`);
|
|
48
|
+
}
|
|
49
|
+
// Spread
|
|
50
|
+
const bestBid = book.bids[0] ? parseFloat(book.bids[0].price) : 0;
|
|
51
|
+
const bestAsk = book.asks[0] ? parseFloat(book.asks[0].price) : 0;
|
|
52
|
+
const spread = bestAsk - bestBid;
|
|
53
|
+
const spreadPct = bestBid > 0 ? ((spread / bestBid) * 100).toFixed(4) : "0";
|
|
54
|
+
console.log(" " + " ".repeat(28) + "│ " + `Spread: $${spread.toFixed(2)} (${spreadPct}%)`);
|
|
55
|
+
// Bids
|
|
56
|
+
for (const bid of book.bids) {
|
|
57
|
+
const bar = "█".repeat(Math.ceil((parseFloat(bid.size) / maxSize) * 10));
|
|
58
|
+
console.log(" " +
|
|
59
|
+
`\x1b[32m${parseFloat(bid.price).toFixed(2).padEnd(13)}\x1b[0m` +
|
|
60
|
+
parseFloat(bid.size).toFixed(4).padEnd(10) +
|
|
61
|
+
`\x1b[32m${bar}\x1b[0m` +
|
|
62
|
+
" ".repeat(5) + "│");
|
|
63
|
+
}
|
|
64
|
+
console.log("");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
outputError(err instanceof Error ? err.message : String(err));
|
|
69
|
+
process.exitCode = 1;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (connected) {
|
|
73
|
+
await adapter.disconnect().catch(() => undefined);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { getAvailableExchanges } from "../lib/exchange.js";
|
|
2
|
+
/**
|
|
3
|
+
* Walk the Commander command tree to collect all command paths
|
|
4
|
+
*/
|
|
5
|
+
function collectCommandPaths(cmd, prefix = []) {
|
|
6
|
+
const paths = [];
|
|
7
|
+
for (const sub of cmd.commands) {
|
|
8
|
+
const current = [...prefix, sub.name()];
|
|
9
|
+
paths.push(current);
|
|
10
|
+
paths.push(...collectCommandPaths(sub, current));
|
|
11
|
+
}
|
|
12
|
+
return paths;
|
|
13
|
+
}
|
|
14
|
+
function generateBashCompletion(program) {
|
|
15
|
+
const paths = collectCommandPaths(program);
|
|
16
|
+
const exchanges = getAvailableExchanges();
|
|
17
|
+
// Build subcommand map: "perps" -> ["account", "markets", ...], "perps account" -> ["ls", "add", ...]
|
|
18
|
+
const subcommandMap = new Map();
|
|
19
|
+
subcommandMap.set("perps", new Set(program.commands.map((c) => c.name())));
|
|
20
|
+
for (const path of paths) {
|
|
21
|
+
const key = ["perps", ...path.slice(0, -1)].join(" ");
|
|
22
|
+
const child = path[path.length - 1];
|
|
23
|
+
if (!subcommandMap.has(key))
|
|
24
|
+
subcommandMap.set(key, new Set());
|
|
25
|
+
subcommandMap.get(key).add(child);
|
|
26
|
+
}
|
|
27
|
+
const cases = Array.from(subcommandMap.entries())
|
|
28
|
+
.map(([key, children]) => {
|
|
29
|
+
const words = Array.from(children).sort().join(" ");
|
|
30
|
+
return ` "${key}") COMPREPLY=( $(compgen -W "${words}" -- "\${cur}") ) ;;`;
|
|
31
|
+
})
|
|
32
|
+
.join("\n");
|
|
33
|
+
return `# bash completion for perps
|
|
34
|
+
# Add to ~/.bashrc: eval "$(perps completion bash)"
|
|
35
|
+
_perps_completions() {
|
|
36
|
+
local cur prev words
|
|
37
|
+
COMPREPLY=()
|
|
38
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
39
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
40
|
+
|
|
41
|
+
# Global flags
|
|
42
|
+
if [[ "\${cur}" == -* ]]; then
|
|
43
|
+
COMPREPLY=( $(compgen -W "--json --human --no-banner --testnet --mainnet --exchange --help --version" -- "\${cur}") )
|
|
44
|
+
return
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Exchange names for --exchange / -e
|
|
48
|
+
if [[ "\${prev}" == "--exchange" || "\${prev}" == "-e" ]]; then
|
|
49
|
+
COMPREPLY=( $(compgen -W "${exchanges.join(" ")}" -- "\${cur}") )
|
|
50
|
+
return
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Subcommands
|
|
54
|
+
local cmd_path="\${COMP_WORDS[*]:0:COMP_CWORD}"
|
|
55
|
+
case "\${cmd_path}" in
|
|
56
|
+
${cases}
|
|
57
|
+
esac
|
|
58
|
+
}
|
|
59
|
+
complete -F _perps_completions perps
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
function generateZshCompletion(program) {
|
|
63
|
+
const paths = collectCommandPaths(program);
|
|
64
|
+
const exchanges = getAvailableExchanges();
|
|
65
|
+
const subcommandMap = new Map();
|
|
66
|
+
subcommandMap.set("perps", new Set(program.commands.map((c) => c.name())));
|
|
67
|
+
for (const path of paths) {
|
|
68
|
+
const key = ["perps", ...path.slice(0, -1)].join(" ");
|
|
69
|
+
const child = path[path.length - 1];
|
|
70
|
+
if (!subcommandMap.has(key))
|
|
71
|
+
subcommandMap.set(key, new Set());
|
|
72
|
+
subcommandMap.get(key).add(child);
|
|
73
|
+
}
|
|
74
|
+
const cases = Array.from(subcommandMap.entries())
|
|
75
|
+
.map(([key, children]) => {
|
|
76
|
+
const words = Array.from(children).sort().join(" ");
|
|
77
|
+
return ` ${key}) compadd ${words} ;;`;
|
|
78
|
+
})
|
|
79
|
+
.join("\n");
|
|
80
|
+
return `# zsh completion for perps
|
|
81
|
+
# Add to ~/.zshrc: eval "$(perps completion zsh)"
|
|
82
|
+
_perps() {
|
|
83
|
+
local -a exchanges=(${exchanges.join(" ")})
|
|
84
|
+
|
|
85
|
+
# Global flags
|
|
86
|
+
if [[ "\${words[\${CURRENT}]}" == -* ]]; then
|
|
87
|
+
compadd -- --json --human --no-banner --testnet --mainnet --exchange --help --version
|
|
88
|
+
return
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
# Exchange names for --exchange / -e
|
|
92
|
+
if [[ "\${words[\${CURRENT}-1]}" == "--exchange" || "\${words[\${CURRENT}-1]}" == "-e" ]]; then
|
|
93
|
+
compadd -- \${exchanges[@]}
|
|
94
|
+
return
|
|
95
|
+
fi
|
|
96
|
+
|
|
97
|
+
# Subcommands
|
|
98
|
+
local cmd_path="\${words[1,\${CURRENT}-1]}"
|
|
99
|
+
case "\${cmd_path}" in
|
|
100
|
+
${cases}
|
|
101
|
+
esac
|
|
102
|
+
}
|
|
103
|
+
compdef _perps perps
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
function generateFishCompletion(program) {
|
|
107
|
+
const paths = collectCommandPaths(program);
|
|
108
|
+
const exchanges = getAvailableExchanges();
|
|
109
|
+
const lines = [
|
|
110
|
+
"# fish completion for perps",
|
|
111
|
+
'# Save to ~/.config/fish/completions/perps.fish or run: perps completion fish | source',
|
|
112
|
+
"",
|
|
113
|
+
"# Global flags",
|
|
114
|
+
"complete -c perps -l json -d 'Output in JSON format'",
|
|
115
|
+
"complete -c perps -l human -d 'Enable human-first output UX'",
|
|
116
|
+
"complete -c perps -l no-banner -d 'Disable session header'",
|
|
117
|
+
"complete -c perps -l testnet -d 'Force testnet network'",
|
|
118
|
+
"complete -c perps -l mainnet -d 'Force mainnet network'",
|
|
119
|
+
`complete -c perps -s e -l exchange -xa '${exchanges.join(" ")}' -d 'Exchange to use'`,
|
|
120
|
+
"",
|
|
121
|
+
];
|
|
122
|
+
// Top-level subcommands
|
|
123
|
+
for (const cmd of program.commands) {
|
|
124
|
+
const desc = cmd.description() || cmd.name();
|
|
125
|
+
lines.push(`complete -c perps -n '__fish_use_subcommand' -a '${cmd.name()}' -d '${desc.replace(/'/g, "\\'")}'`);
|
|
126
|
+
}
|
|
127
|
+
// Nested subcommands
|
|
128
|
+
for (const path of paths) {
|
|
129
|
+
if (path.length < 2)
|
|
130
|
+
continue;
|
|
131
|
+
const parent = path.slice(0, -1);
|
|
132
|
+
const child = path[path.length - 1];
|
|
133
|
+
const condition = parent
|
|
134
|
+
.map((p, _i) => `__fish_seen_subcommand_from ${p}`)
|
|
135
|
+
.join("; and ");
|
|
136
|
+
lines.push(`complete -c perps -n '${condition}' -a '${child}'`);
|
|
137
|
+
}
|
|
138
|
+
return lines.join("\n") + "\n";
|
|
139
|
+
}
|
|
140
|
+
export function registerCompletionCommand(program) {
|
|
141
|
+
program
|
|
142
|
+
.command("completion")
|
|
143
|
+
.description("Generate shell completion scripts")
|
|
144
|
+
.argument("[shell]", "Shell type: bash, zsh, or fish", "bash")
|
|
145
|
+
.action((shell) => {
|
|
146
|
+
switch (shell.toLowerCase()) {
|
|
147
|
+
case "bash":
|
|
148
|
+
console.log(generateBashCompletion(program));
|
|
149
|
+
break;
|
|
150
|
+
case "zsh":
|
|
151
|
+
console.log(generateZshCompletion(program));
|
|
152
|
+
break;
|
|
153
|
+
case "fish":
|
|
154
|
+
console.log(generateFishCompletion(program));
|
|
155
|
+
break;
|
|
156
|
+
default:
|
|
157
|
+
console.error(`Unknown shell: ${shell}. Supported: bash, zsh, fish`);
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Config commands for managing settings
|
|
3
|
+
*/
|
|
4
|
+
import { getOutputOptions } from "../../cli/program.js";
|
|
5
|
+
import { output, outputSuccess, outputError } from "../../cli/output.js";
|
|
6
|
+
import { getSetting, setSetting, getAllSettings, resetSettings } from "../../lib/settings.js";
|
|
7
|
+
import { getAvailableExchanges, isExchangeSupported } from "../../lib/exchange.js";
|
|
8
|
+
const VALID_SETTING_KEYS = ["defaultExchange", "testnetByDefault"];
|
|
9
|
+
function isConfigKey(key) {
|
|
10
|
+
return VALID_SETTING_KEYS.includes(key);
|
|
11
|
+
}
|
|
12
|
+
export function registerConfigCommands(program) {
|
|
13
|
+
const config = program
|
|
14
|
+
.command("config")
|
|
15
|
+
.description("Manage CLI configuration");
|
|
16
|
+
// Show all settings
|
|
17
|
+
config
|
|
18
|
+
.command("show")
|
|
19
|
+
.description("Show current configuration")
|
|
20
|
+
.action(function () {
|
|
21
|
+
const outputOpts = getOutputOptions(this);
|
|
22
|
+
const settings = getAllSettings();
|
|
23
|
+
if (outputOpts.json) {
|
|
24
|
+
output(settings, outputOpts);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
console.log("\nCurrent Configuration:\n");
|
|
28
|
+
console.log(` Default Exchange: ${settings.defaultExchange}`);
|
|
29
|
+
console.log(` Testnet Fallback: ${settings.testnetByDefault}`);
|
|
30
|
+
console.log(" Network Rules: data=mainnet, execution=testnet (unless overridden)");
|
|
31
|
+
console.log(`\nAvailable Exchanges: ${getAvailableExchanges().join(", ")}`);
|
|
32
|
+
console.log("");
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// Get a specific setting
|
|
36
|
+
config
|
|
37
|
+
.command("get <key>")
|
|
38
|
+
.description("Get a configuration value")
|
|
39
|
+
.action(function (key) {
|
|
40
|
+
const outputOpts = getOutputOptions(this);
|
|
41
|
+
if (!isConfigKey(key)) {
|
|
42
|
+
outputError(`Unknown setting: ${key}`);
|
|
43
|
+
console.error(`Valid settings: ${VALID_SETTING_KEYS.join(", ")}`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const value = getSetting(key);
|
|
47
|
+
if (outputOpts.json) {
|
|
48
|
+
output({ [key]: value }, outputOpts);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.log(value);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
// Set a specific setting
|
|
55
|
+
config
|
|
56
|
+
.command("set <key> <value>")
|
|
57
|
+
.description("Set a configuration value")
|
|
58
|
+
.action(function (key, value) {
|
|
59
|
+
const outputOpts = getOutputOptions(this);
|
|
60
|
+
// Validate key
|
|
61
|
+
if (!isConfigKey(key)) {
|
|
62
|
+
outputError(`Unknown setting: ${key}`);
|
|
63
|
+
console.error(`Valid settings: ${VALID_SETTING_KEYS.join(", ")}`);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
if (key === "defaultExchange") {
|
|
67
|
+
if (!isExchangeSupported(value)) {
|
|
68
|
+
outputError(`Unknown exchange: ${value}`);
|
|
69
|
+
console.error(`Available exchanges: ${getAvailableExchanges().join(", ")}`);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
const finalValue = value.toLowerCase();
|
|
73
|
+
setSetting(key, finalValue);
|
|
74
|
+
if (outputOpts.json) {
|
|
75
|
+
output({ [key]: finalValue, success: true }, outputOpts);
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
outputSuccess(`Set ${key} = ${finalValue}`);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!["true", "false"].includes(value.toLowerCase())) {
|
|
83
|
+
outputError("Value must be 'true' or 'false'");
|
|
84
|
+
process.exit(1);
|
|
85
|
+
}
|
|
86
|
+
const finalValue = value.toLowerCase() === "true";
|
|
87
|
+
setSetting(key, finalValue);
|
|
88
|
+
if (outputOpts.json) {
|
|
89
|
+
output({ [key]: finalValue, success: true }, outputOpts);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
outputSuccess(`Set ${key} = ${finalValue}`);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
// Reset all settings
|
|
96
|
+
config
|
|
97
|
+
.command("reset")
|
|
98
|
+
.description("Reset all settings to defaults")
|
|
99
|
+
.action(function () {
|
|
100
|
+
const outputOpts = getOutputOptions(this);
|
|
101
|
+
resetSettings();
|
|
102
|
+
if (outputOpts.json) {
|
|
103
|
+
output({ success: true, message: "Settings reset to defaults" }, outputOpts);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
outputSuccess("Settings reset to defaults");
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
export type JsonObject = Record<string, unknown>;
|
|
3
|
+
type DynamicClient = Record<string, unknown> & {
|
|
4
|
+
close?: () => Promise<unknown> | unknown;
|
|
5
|
+
setSandboxMode?: (enabled: boolean) => void;
|
|
6
|
+
};
|
|
7
|
+
interface CcxtFuturesOperationOptions {
|
|
8
|
+
symbol?: string;
|
|
9
|
+
symbols?: string[];
|
|
10
|
+
id?: string;
|
|
11
|
+
side?: string;
|
|
12
|
+
orderType?: string;
|
|
13
|
+
amount?: number;
|
|
14
|
+
price?: number;
|
|
15
|
+
since?: number;
|
|
16
|
+
limit?: number;
|
|
17
|
+
leverage?: number;
|
|
18
|
+
marginMode?: string;
|
|
19
|
+
positionMode?: string;
|
|
20
|
+
code?: string;
|
|
21
|
+
from?: string;
|
|
22
|
+
to?: string;
|
|
23
|
+
params: JsonObject;
|
|
24
|
+
rawMethod?: string;
|
|
25
|
+
rawArgs: unknown[];
|
|
26
|
+
}
|
|
27
|
+
export declare function parseJsonArray(raw: string): unknown[];
|
|
28
|
+
export declare function parseJsonObject(raw: string, label?: string): JsonObject;
|
|
29
|
+
export declare function executeCcxtFuturesOperation(instance: DynamicClient, operationRaw: string, input: CcxtFuturesOperationOptions): Promise<unknown>;
|
|
30
|
+
export declare function registerDataCommands(program: Command): void;
|
|
31
|
+
export {};
|