perp-cli 0.6.4 → 0.7.2

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.
Files changed (181) hide show
  1. package/README.md +7 -2
  2. package/dist/api/public/hyperliquid.d.ts +2 -2
  3. package/dist/api/public/hyperliquid.js +0 -10
  4. package/dist/api/public/lighter.d.ts +2 -1
  5. package/dist/api/public/pacifica.d.ts +2 -1
  6. package/dist/arb/index.d.ts +7 -4
  7. package/dist/arb/index.js +5 -4
  8. package/dist/arb/sizing.d.ts +1 -1
  9. package/dist/arb/state.d.ts +1 -1
  10. package/dist/arb/state.js +4 -3
  11. package/dist/arb/utils.d.ts +0 -10
  12. package/dist/arb/utils.js +0 -42
  13. package/dist/bot/apex/guard.d.ts +26 -0
  14. package/dist/bot/apex/guard.js +86 -0
  15. package/dist/bot/apex/index.d.ts +6 -0
  16. package/dist/bot/apex/index.js +4 -0
  17. package/dist/bot/apex/pulse.d.ts +22 -0
  18. package/dist/bot/apex/pulse.js +107 -0
  19. package/dist/bot/apex/radar.d.ts +17 -0
  20. package/dist/bot/apex/radar.js +150 -0
  21. package/dist/bot/conditions.d.ts +1 -1
  22. package/dist/bot/config.d.ts +6 -1
  23. package/dist/bot/config.js +2 -2
  24. package/dist/bot/engine.d.ts +23 -2
  25. package/dist/bot/engine.js +331 -351
  26. package/dist/bot/index.d.ts +16 -0
  27. package/dist/bot/index.js +10 -0
  28. package/dist/bot/indicators.d.ts +29 -0
  29. package/dist/bot/indicators.js +144 -0
  30. package/dist/bot/reflect.d.ts +34 -0
  31. package/dist/bot/reflect.js +155 -0
  32. package/dist/bot/strategies/aggressive-taker.d.ts +36 -0
  33. package/dist/bot/strategies/aggressive-taker.js +125 -0
  34. package/dist/bot/strategies/apex-strategy.d.ts +36 -0
  35. package/dist/bot/strategies/apex-strategy.js +185 -0
  36. package/dist/bot/strategies/avellaneda-mm.d.ts +27 -0
  37. package/dist/bot/strategies/avellaneda-mm.js +111 -0
  38. package/dist/bot/strategies/basis-arb.d.ts +27 -0
  39. package/dist/bot/strategies/basis-arb.js +97 -0
  40. package/dist/bot/strategies/claude-agent.d.ts +31 -0
  41. package/dist/bot/strategies/claude-agent.js +155 -0
  42. package/dist/bot/strategies/dca-strategy.d.ts +25 -0
  43. package/dist/bot/strategies/dca-strategy.js +59 -0
  44. package/dist/bot/strategies/engine-mm.d.ts +29 -0
  45. package/dist/bot/strategies/engine-mm.js +141 -0
  46. package/dist/bot/strategies/funding-arb-strategy.d.ts +32 -0
  47. package/dist/bot/strategies/funding-arb-strategy.js +168 -0
  48. package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
  49. package/dist/bot/strategies/funding-arb-v2.js +222 -0
  50. package/dist/bot/strategies/funding-auto.d.ts +1 -0
  51. package/dist/bot/strategies/funding-auto.js +572 -0
  52. package/dist/bot/strategies/grid-mm.d.ts +25 -0
  53. package/dist/bot/strategies/grid-mm.js +99 -0
  54. package/dist/bot/strategies/grid-strategy.d.ts +39 -0
  55. package/dist/bot/strategies/grid-strategy.js +176 -0
  56. package/dist/bot/strategies/hedge-agent.d.ts +25 -0
  57. package/dist/bot/strategies/hedge-agent.js +79 -0
  58. package/dist/bot/strategies/liquidation-mm.d.ts +25 -0
  59. package/dist/bot/strategies/liquidation-mm.js +108 -0
  60. package/dist/bot/strategies/mean-reversion.d.ts +35 -0
  61. package/dist/bot/strategies/mean-reversion.js +118 -0
  62. package/dist/bot/strategies/momentum-breakout.d.ts +36 -0
  63. package/dist/bot/strategies/momentum-breakout.js +159 -0
  64. package/dist/bot/strategies/regime-mm.d.ts +25 -0
  65. package/dist/bot/strategies/regime-mm.js +111 -0
  66. package/dist/bot/strategies/rfq-agent.d.ts +25 -0
  67. package/dist/bot/strategies/rfq-agent.js +71 -0
  68. package/dist/bot/strategies/simple-mm.d.ts +25 -0
  69. package/dist/bot/strategies/simple-mm.js +76 -0
  70. package/dist/bot/strategy-registry.d.ts +9 -0
  71. package/dist/bot/strategy-registry.js +14 -0
  72. package/dist/bot/strategy-types.d.ts +64 -0
  73. package/dist/bot/strategy-types.js +5 -0
  74. package/dist/bot/trade-journal.d.ts +27 -0
  75. package/dist/bot/trade-journal.js +53 -0
  76. package/dist/bot/tui/BotDashboard.d.ts +56 -0
  77. package/dist/bot/tui/BotDashboard.js +94 -0
  78. package/dist/bot/tui/index.d.ts +6 -0
  79. package/dist/bot/tui/index.js +8 -0
  80. package/dist/bridge-engine.d.ts +0 -1
  81. package/dist/bridge-engine.js +0 -1
  82. package/dist/cache.d.ts +0 -1
  83. package/dist/cache.js +0 -1
  84. package/dist/commands/account.d.ts +1 -1
  85. package/dist/commands/account.js +94 -91
  86. package/dist/commands/agent.d.ts +1 -1
  87. package/dist/commands/arb/index.d.ts +1 -1
  88. package/dist/commands/arb/index.js +1 -1
  89. package/dist/commands/arb-auto.d.ts +1 -1
  90. package/dist/commands/arb-auto.js +5 -3
  91. package/dist/commands/arb.d.ts +2 -1
  92. package/dist/commands/arb.js +2 -0
  93. package/dist/commands/bot.d.ts +1 -1
  94. package/dist/commands/bot.js +236 -15
  95. package/dist/commands/bridge.js +1 -1
  96. package/dist/commands/dashboard.d.ts +1 -1
  97. package/dist/commands/dashboard.js +1 -1
  98. package/dist/commands/funds.d.ts +1 -1
  99. package/dist/commands/funds.js +34 -21
  100. package/dist/commands/history.d.ts +1 -1
  101. package/dist/commands/history.js +10 -5
  102. package/dist/commands/manage.d.ts +2 -3
  103. package/dist/commands/manage.js +11 -18
  104. package/dist/commands/market.d.ts +1 -1
  105. package/dist/commands/market.js +26 -17
  106. package/dist/commands/plan.d.ts +1 -1
  107. package/dist/commands/rebalance.d.ts +1 -1
  108. package/dist/commands/rebalance.js +8 -9
  109. package/dist/commands/risk.d.ts +1 -1
  110. package/dist/commands/settings.d.ts +1 -1
  111. package/dist/commands/settings.js +3 -1
  112. package/dist/commands/trade.d.ts +1 -1
  113. package/dist/commands/trade.js +42 -55
  114. package/dist/commands/wallet.d.ts +0 -2
  115. package/dist/commands/wallet.js +0 -5
  116. package/dist/constants.d.ts +0 -3
  117. package/dist/constants.js +0 -4
  118. package/dist/cross-chain-margin.d.ts +1 -1
  119. package/dist/dashboard/index.d.ts +4 -0
  120. package/dist/dashboard/index.js +3 -0
  121. package/dist/dashboard/server.d.ts +1 -1
  122. package/dist/dashboard/server.js +1 -1
  123. package/dist/dashboard/ws-feeds.d.ts +1 -1
  124. package/dist/event-stream.d.ts +1 -1
  125. package/dist/exchanges/aster.d.ts +62 -0
  126. package/dist/exchanges/aster.js +420 -0
  127. package/dist/exchanges/capabilities.d.ts +74 -0
  128. package/dist/exchanges/capabilities.js +44 -0
  129. package/dist/exchanges/edgex.d.ts +62 -0
  130. package/dist/exchanges/edgex.js +552 -0
  131. package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
  132. package/dist/exchanges/hyperliquid-spot.js +20 -12
  133. package/dist/exchanges/hyperliquid.d.ts +8 -2
  134. package/dist/exchanges/hyperliquid.js +21 -6
  135. package/dist/exchanges/index.d.ts +11 -0
  136. package/dist/exchanges/index.js +10 -0
  137. package/dist/exchanges/interface.d.ts +3 -0
  138. package/dist/exchanges/lighter-spot.d.ts +13 -0
  139. package/dist/exchanges/lighter-spot.js +98 -26
  140. package/dist/exchanges/lighter.d.ts +9 -3
  141. package/dist/exchanges/lighter.js +18 -8
  142. package/dist/exchanges/nado.d.ts +52 -0
  143. package/dist/exchanges/nado.js +223 -0
  144. package/dist/exchanges/pacifica.d.ts +7 -1
  145. package/dist/exchanges/pacifica.js +17 -2
  146. package/dist/exchanges/registry.d.ts +13 -0
  147. package/dist/exchanges/registry.js +37 -0
  148. package/dist/exchanges/spot-interface.d.ts +9 -0
  149. package/dist/funding/rates.js +1 -1
  150. package/dist/index.js +2 -6
  151. package/dist/jobs.js +1 -1
  152. package/dist/liquidity.d.ts +1 -2
  153. package/dist/liquidity.js +12 -5
  154. package/dist/mcp-server.js +4 -2
  155. package/dist/pacifica/constants.d.ts +0 -3
  156. package/dist/pacifica/constants.js +0 -4
  157. package/dist/pacifica/types/account.d.ts +0 -8
  158. package/dist/pacifica/types/lake.d.ts +0 -6
  159. package/dist/pacifica/types/position.d.ts +0 -12
  160. package/dist/pacifica/types/ws.d.ts +0 -24
  161. package/dist/pacifica/types/ws.js +1 -41
  162. package/dist/plan-executor.d.ts +1 -1
  163. package/dist/rebalance.d.ts +1 -7
  164. package/dist/rebalance.js +0 -6
  165. package/dist/risk.d.ts +1 -1
  166. package/dist/risk.js +3 -1
  167. package/dist/smart-order.d.ts +1 -1
  168. package/dist/strategies/dca.d.ts +1 -1
  169. package/dist/strategies/funding-arb.d.ts +1 -1
  170. package/dist/strategies/funding-arb.js +193 -181
  171. package/dist/strategies/grid.d.ts +1 -1
  172. package/dist/strategies/grid.js +1 -1
  173. package/dist/strategies/index.d.ts +12 -0
  174. package/dist/strategies/index.js +7 -0
  175. package/dist/strategies/split-order.d.ts +1 -1
  176. package/dist/strategies/trailing-stop.d.ts +1 -1
  177. package/dist/strategies/twap.d.ts +1 -1
  178. package/dist/trade-validator.d.ts +1 -1
  179. package/package.json +7 -1
  180. package/skills/perp-cli/SKILL.md +20 -0
  181. package/skills/perp-cli/references/commands.md +22 -8
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Strategy registry for bot engine.
3
+ * Strategies register here — engine uses registry to resolve by name.
4
+ */
5
+ const registry = new Map();
6
+ export function registerStrategy(name, factory) {
7
+ registry.set(name.toLowerCase(), factory);
8
+ }
9
+ export function getStrategy(name) {
10
+ return registry.get(name.toLowerCase());
11
+ }
12
+ export function listStrategies() {
13
+ return [...registry.keys()];
14
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Strategy interface for Bot Engine v2.
3
+ * All trading strategies implement this interface.
4
+ */
5
+ import type { ExchangeAdapter, ExchangeKline } from "../exchanges/index.js";
6
+ import type { MarketSnapshot } from "./conditions.js";
7
+ export interface EnrichedSnapshot extends MarketSnapshot {
8
+ klines: ExchangeKline[];
9
+ orderbook: {
10
+ bids: [string, string][];
11
+ asks: [string, string][];
12
+ };
13
+ openInterest: string;
14
+ }
15
+ export type StrategyAction = {
16
+ type: "place_order";
17
+ side: "buy" | "sell";
18
+ price: string;
19
+ size: string;
20
+ orderType: "limit" | "market";
21
+ reduceOnly?: boolean;
22
+ tif?: string;
23
+ } | {
24
+ type: "cancel_order";
25
+ orderId: string;
26
+ } | {
27
+ type: "cancel_all";
28
+ } | {
29
+ type: "edit_order";
30
+ orderId: string;
31
+ price: string;
32
+ size: string;
33
+ } | {
34
+ type: "set_leverage";
35
+ leverage: number;
36
+ marginMode?: "cross" | "isolated";
37
+ } | {
38
+ type: "noop";
39
+ };
40
+ export interface StrategyContext {
41
+ adapter: ExchangeAdapter;
42
+ symbol: string;
43
+ config: Record<string, unknown>;
44
+ state: Map<string, unknown>;
45
+ tick: number;
46
+ log: (msg: string) => void;
47
+ }
48
+ export interface ParamDef {
49
+ name: string;
50
+ type: "number" | "string" | "boolean";
51
+ required: boolean;
52
+ default?: unknown;
53
+ description: string;
54
+ }
55
+ export interface Strategy {
56
+ readonly name: string;
57
+ describe(): {
58
+ description: string;
59
+ params: ParamDef[];
60
+ };
61
+ init(ctx: StrategyContext, snapshot: EnrichedSnapshot): Promise<void>;
62
+ onTick(ctx: StrategyContext, snapshot: EnrichedSnapshot): Promise<StrategyAction[]>;
63
+ onStop(ctx: StrategyContext): Promise<StrategyAction[]>;
64
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Strategy interface for Bot Engine v2.
3
+ * All trading strategies implement this interface.
4
+ */
5
+ export {};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Trade journal: JSONL-based persistent trade log
3
+ * Stores: { timestamp, strategy, symbol, side, size, price, pnl, fees, holdingPeriod }
4
+ * Location: ~/.perp/trade-journal.jsonl
5
+ */
6
+ export interface JournalEntry {
7
+ timestamp: number;
8
+ strategy: string;
9
+ symbol: string;
10
+ exchange: string;
11
+ side: "buy" | "sell";
12
+ size: string;
13
+ entryPrice: string;
14
+ exitPrice?: string;
15
+ pnl?: number;
16
+ fees?: number;
17
+ holdingPeriodMs?: number;
18
+ tags?: string[];
19
+ }
20
+ export declare function appendJournal(entry: JournalEntry): void;
21
+ export declare function readJournal(filter?: {
22
+ strategy?: string;
23
+ symbol?: string;
24
+ from?: number;
25
+ to?: number;
26
+ }): JournalEntry[];
27
+ export declare function clearJournal(): void;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Trade journal: JSONL-based persistent trade log
3
+ * Stores: { timestamp, strategy, symbol, side, size, price, pnl, fees, holdingPeriod }
4
+ * Location: ~/.perp/trade-journal.jsonl
5
+ */
6
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, unlinkSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join, dirname } from "node:path";
9
+ function journalPath() {
10
+ return join(homedir(), ".perp", "trade-journal.jsonl");
11
+ }
12
+ function ensureDir(filePath) {
13
+ const dir = dirname(filePath);
14
+ if (!existsSync(dir)) {
15
+ mkdirSync(dir, { recursive: true });
16
+ }
17
+ }
18
+ export function appendJournal(entry) {
19
+ const path = journalPath();
20
+ ensureDir(path);
21
+ appendFileSync(path, JSON.stringify(entry) + "\n", "utf-8");
22
+ }
23
+ export function readJournal(filter) {
24
+ const path = journalPath();
25
+ if (!existsSync(path))
26
+ return [];
27
+ const lines = readFileSync(path, "utf-8").split("\n").filter(Boolean);
28
+ const entries = [];
29
+ for (const line of lines) {
30
+ try {
31
+ const entry = JSON.parse(line);
32
+ if (filter?.strategy && entry.strategy !== filter.strategy)
33
+ continue;
34
+ if (filter?.symbol && entry.symbol !== filter.symbol)
35
+ continue;
36
+ if (filter?.from !== undefined && entry.timestamp < filter.from)
37
+ continue;
38
+ if (filter?.to !== undefined && entry.timestamp > filter.to)
39
+ continue;
40
+ entries.push(entry);
41
+ }
42
+ catch {
43
+ // skip malformed lines
44
+ }
45
+ }
46
+ return entries;
47
+ }
48
+ export function clearJournal() {
49
+ const path = journalPath();
50
+ if (existsSync(path)) {
51
+ unlinkSync(path);
52
+ }
53
+ }
@@ -0,0 +1,56 @@
1
+ export interface ExchangeBalance {
2
+ exchange: string;
3
+ equity: string;
4
+ available: string;
5
+ }
6
+ export interface BotTuiState {
7
+ phase: string;
8
+ equity: number;
9
+ peakEquity: number;
10
+ dailyPnl: number;
11
+ fills: number;
12
+ totalPnl: number;
13
+ runtime: number;
14
+ strategy: string;
15
+ symbol: string;
16
+ exchange: string;
17
+ price: number;
18
+ volume24h: number;
19
+ openInterest: string;
20
+ fundingRate: number;
21
+ volatility24h: number;
22
+ positions: Position[];
23
+ openOrders: OpenOrder[];
24
+ strategyState: Record<string, unknown>;
25
+ tick: number;
26
+ exchangeBalances?: ExchangeBalance[];
27
+ }
28
+ export interface Position {
29
+ symbol: string;
30
+ side: string;
31
+ size: string;
32
+ entryPrice: string;
33
+ markPrice: string;
34
+ unrealizedPnl: string;
35
+ exchange?: string;
36
+ }
37
+ export interface OpenOrder {
38
+ orderId: string;
39
+ side: string;
40
+ price: string;
41
+ size: string;
42
+ type: string;
43
+ }
44
+ export interface LogEntry {
45
+ time: string;
46
+ message: string;
47
+ }
48
+ export type StateListener = (state: BotTuiState) => void;
49
+ export type LogListener = (entry: LogEntry) => void;
50
+ export interface DashboardProps {
51
+ initialState: BotTuiState;
52
+ onQuit: () => void;
53
+ onPause: () => void;
54
+ subscribe: (onState: StateListener, onLog: LogListener) => () => void;
55
+ }
56
+ export declare function BotDashboard({ initialState, onQuit, onPause, subscribe }: DashboardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,94 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback } from "react";
3
+ import { Box, Text, useApp, useInput } from "ink";
4
+ // ── Helpers ──
5
+ function formatDuration(sec) {
6
+ if (sec < 60)
7
+ return `${sec}s`;
8
+ const m = Math.floor(sec / 60);
9
+ if (m < 60)
10
+ return `${m}m${sec % 60}s`;
11
+ const h = Math.floor(m / 60);
12
+ return `${h}h${m % 60}m`;
13
+ }
14
+ function pnlColor(val) {
15
+ if (val > 0)
16
+ return "green";
17
+ if (val < 0)
18
+ return "red";
19
+ return "white";
20
+ }
21
+ function phaseIndicator(phase) {
22
+ switch (phase) {
23
+ case "monitoring": return "[ MONITORING ]";
24
+ case "entering": return "[ ENTERING ]";
25
+ case "running": return "[ RUNNING ]";
26
+ case "exiting": return "[ EXITING ]";
27
+ case "paused": return "[ PAUSED ]";
28
+ case "stopped": return "[ STOPPED ]";
29
+ default: return `[ ${phase.toUpperCase()} ]`;
30
+ }
31
+ }
32
+ function phaseColor(phase) {
33
+ switch (phase) {
34
+ case "monitoring": return "cyan";
35
+ case "entering": return "yellow";
36
+ case "running": return "green";
37
+ case "exiting": return "yellow";
38
+ case "paused": return "gray";
39
+ case "stopped": return "red";
40
+ default: return "cyan";
41
+ }
42
+ }
43
+ const MAX_LOG_LINES = 15;
44
+ // ── Component ──
45
+ export function BotDashboard({ initialState, onQuit, onPause, subscribe }) {
46
+ const { exit } = useApp();
47
+ const [state, setState] = useState(initialState);
48
+ const [logs, setLogs] = useState([]);
49
+ const [paused, setPaused] = useState(false);
50
+ useEffect(() => {
51
+ const unsubscribe = subscribe((newState) => setState(newState), (entry) => setLogs((prev) => [...prev.slice(-(MAX_LOG_LINES - 1)), entry]));
52
+ return unsubscribe;
53
+ }, [subscribe]);
54
+ const handleQuit = useCallback(() => {
55
+ onQuit();
56
+ exit();
57
+ }, [onQuit, exit]);
58
+ useInput((input, key) => {
59
+ if (input === "q" || (key.ctrl && input === "c")) {
60
+ handleQuit();
61
+ }
62
+ if (input === "p") {
63
+ setPaused((v) => !v);
64
+ onPause();
65
+ }
66
+ });
67
+ const drawdown = state.peakEquity - state.equity;
68
+ const drawdownPct = state.peakEquity > 0 ? (drawdown / state.peakEquity) * 100 : 0;
69
+ const bidOrders = state.openOrders.filter((o) => o.side.toLowerCase() === "buy");
70
+ const askOrders = state.openOrders.filter((o) => o.side.toLowerCase() === "sell");
71
+ const hasMultiExchange = (state.exchangeBalances?.length ?? 0) > 1;
72
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 1, children: [_jsxs(Box, { flexDirection: "row", justifyContent: "space-between", marginBottom: 1, children: [_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, color: "cyan", children: [state.strategy, " | ", state.symbol, " | ", state.exchange] }), _jsxs(Text, { color: "gray", children: ["Runtime: ", formatDuration(state.runtime), " | Tick: ", state.tick] })] }), _jsx(Box, { children: _jsx(Text, { bold: true, color: phaseColor(state.phase), children: phaseIndicator(state.phase) }) })] }), _jsxs(Box, { borderStyle: "single", borderColor: "gray", paddingX: 1, flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Text, { children: ["Price: ", _jsxs(Text, { bold: true, color: "white", children: ["$", state.price.toFixed(2)] })] }), _jsxs(Text, { color: "gray", children: ["Vol24h: ", _jsxs(Text, { color: "white", children: [state.volatility24h.toFixed(1), "%"] })] }), _jsxs(Text, { color: "gray", children: ["FR: ", _jsxs(Text, { color: state.fundingRate >= 0 ? "green" : "red", children: [(state.fundingRate * 100).toFixed(4), "%"] })] }), _jsxs(Text, { color: "gray", children: ["OI: ", _jsx(Text, { color: "white", children: state.openInterest })] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Account" }), _jsxs(Text, { children: ["Equity: ", _jsxs(Text, { bold: true, color: "white", children: ["$", state.equity.toFixed(2)] }), _jsxs(Text, { color: "gray", children: [" (peak: $", state.peakEquity.toFixed(2), ")"] })] }), _jsxs(Text, { children: ["Daily P&L: ", _jsxs(Text, { bold: true, color: pnlColor(state.dailyPnl), children: ["$", state.dailyPnl.toFixed(2)] })] })] }), _jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsxs(Text, { children: ["Drawdown: ", _jsxs(Text, { bold: true, color: drawdown > 0 ? "red" : "green", children: ["$", drawdown.toFixed(2), " (", drawdownPct.toFixed(1), "%)"] })] }), _jsxs(Text, { children: ["Fills: ", _jsx(Text, { bold: true, color: "white", children: state.fills }), _jsx(Text, { color: "gray", children: " | Total P&L: " }), _jsxs(Text, { bold: true, color: pnlColor(state.totalPnl), children: ["$", state.totalPnl.toFixed(2)] })] })] })] }), state.exchangeBalances && state.exchangeBalances.length > 1 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Exchange Balances" }), _jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "gray", children: pad("Exchange", 16) }), _jsx(Text, { color: "gray", children: pad("Equity", 14) }), _jsx(Text, { color: "gray", children: "Available" })] }), state.exchangeBalances.map((eb, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: "white", children: pad(eb.exchange, 16) }), _jsx(Text, { color: "white", children: pad(`$${parseFloat(eb.equity).toFixed(2)}`, 14) }), _jsxs(Text, { color: "gray", children: ["$", parseFloat(eb.available).toFixed(2)] })] }, i)))] })), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Positions" }), state.positions.length === 0 ? (_jsx(Text, { color: "gray", children: " No open positions" })) : (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [hasMultiExchange && _jsx(Text, { color: "gray", children: pad("Exchange", 14) }), _jsx(Text, { color: "gray", children: pad("Symbol", 10) }), _jsx(Text, { color: "gray", children: pad("Side", 6) }), _jsx(Text, { color: "gray", children: pad("Size", 12) }), _jsx(Text, { color: "gray", children: pad("Entry", 12) }), _jsx(Text, { color: "gray", children: pad("Mark", 12) }), _jsx(Text, { color: "gray", children: "uPnL" })] }), state.positions.map((p, i) => {
73
+ const upnl = parseFloat(p.unrealizedPnl);
74
+ return (_jsxs(Box, { flexDirection: "row", children: [hasMultiExchange && _jsx(Text, { color: "cyan", children: pad(p.exchange ?? "—", 14) }), _jsx(Text, { color: "white", children: pad(p.symbol, 10) }), _jsx(Text, { color: p.side.toLowerCase() === "long" ? "green" : "red", children: pad(p.side, 6) }), _jsx(Text, { color: "white", children: pad(p.size, 12) }), _jsx(Text, { color: "white", children: pad(p.entryPrice, 12) }), _jsx(Text, { color: "white", children: pad(p.markPrice, 12) }), _jsxs(Text, { color: pnlColor(upnl), children: ["$", upnl.toFixed(2)] })] }, i));
75
+ })] }))] }), _jsxs(Box, { marginTop: 1, flexDirection: "row", justifyContent: "space-between", children: [_jsxs(Box, { flexDirection: "column", width: "50%", children: [_jsxs(Text, { color: "green", bold: true, children: ["Bids (", bidOrders.length, ")"] }), bidOrders.length === 0 ? (_jsx(Text, { color: "gray", children: " --" })) : (bidOrders.slice(0, 8).map((o, i) => (_jsxs(Text, { color: "green", children: [pad(o.price, 12), " ", pad(o.size, 10), " ", o.type] }, i))))] }), _jsxs(Box, { flexDirection: "column", width: "50%", children: [_jsxs(Text, { color: "red", bold: true, children: ["Asks (", askOrders.length, ")"] }), askOrders.length === 0 ? (_jsx(Text, { color: "gray", children: " --" })) : (askOrders.slice(0, 8).map((o, i) => (_jsxs(Text, { color: "red", children: [pad(o.price, 12), " ", pad(o.size, 10), " ", o.type] }, i))))] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { color: "cyan", bold: true, children: "Strategy State" }), _jsxs(Box, { flexDirection: "row", flexWrap: "wrap", children: [Object.entries(state.strategyState).slice(0, 12).map(([k, v]) => (_jsxs(Box, { marginRight: 2, children: [_jsxs(Text, { color: "gray", children: [k, ": "] }), _jsx(Text, { color: "white", children: String(v) })] }, k))), Object.keys(state.strategyState).length === 0 && (_jsx(Text, { color: "gray", children: " --" }))] })] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", borderStyle: "single", borderColor: "gray", paddingX: 1, height: MAX_LOG_LINES + 2, children: [_jsx(Text, { color: "cyan", bold: true, children: "Log" }), logs.length === 0 ? (_jsx(Text, { color: "gray", children: " Waiting for events..." })) : (logs.map((entry, i) => (_jsxs(Text, { children: [_jsxs(Text, { color: "gray", children: [entry.time, " "] }), _jsx(Text, { children: colorizeLog(entry.message) })] }, i))))] }), _jsx(Box, { marginTop: 1, flexDirection: "row", justifyContent: "center", children: _jsxs(Text, { color: "gray", children: ["[", _jsx(Text, { color: "white", bold: true, children: "Q" }), "]uit [", _jsx(Text, { color: "white", bold: true, children: "P" }), "]", paused ? "resume" : "ause"] }) })] }));
76
+ }
77
+ // ── Utility ──
78
+ function pad(s, width) {
79
+ return s.length >= width ? s.slice(0, width) : s + " ".repeat(width - s.length);
80
+ }
81
+ function colorizeLog(msg) {
82
+ // Strip ANSI codes for TUI since ink handles colors
83
+ const clean = msg.replace(/\x1b\[[0-9;]*m/g, "");
84
+ if (clean.includes("ERROR") || clean.includes("✗") || clean.includes("failed")) {
85
+ return _jsx(Text, { color: "red", children: clean });
86
+ }
87
+ if (clean.includes("WARNING") || clean.includes("⚠")) {
88
+ return _jsx(Text, { color: "yellow", children: clean });
89
+ }
90
+ if (clean.includes("✓") || clean.includes("started") || clean.includes("profit")) {
91
+ return _jsx(Text, { color: "green", children: clean });
92
+ }
93
+ return _jsx(Text, { color: "gray", children: clean });
94
+ }
@@ -0,0 +1,6 @@
1
+ export { BotDashboard } from "./BotDashboard.js";
2
+ export type { BotTuiState, ExchangeBalance, Position, OpenOrder, LogEntry, StateListener, LogListener, DashboardProps } from "./BotDashboard.js";
3
+ import type { DashboardProps } from "./BotDashboard.js";
4
+ export declare function startDashboard(props: DashboardProps): {
5
+ unmount: () => void;
6
+ };
@@ -0,0 +1,8 @@
1
+ export { BotDashboard } from "./BotDashboard.js";
2
+ import { render } from "ink";
3
+ import React from "react";
4
+ import { BotDashboard } from "./BotDashboard.js";
5
+ export function startDashboard(props) {
6
+ const { unmount } = render(React.createElement(BotDashboard, props));
7
+ return { unmount };
8
+ }
@@ -77,7 +77,6 @@ export declare function getDebridgeQuote(srcChain: string, dstChain: string, amo
77
77
  */
78
78
  export declare function executeDebridgeBridge(bridgeQuote: BridgeQuote, signerKey: string): Promise<BridgeResult>;
79
79
  export declare const CCTP_DOMAINS: Record<string, number>;
80
- export declare const EVM_TOKEN_MINTER_V2 = "0xfd78EE919681417d192449715b2594ab58f5D002";
81
80
  /**
82
81
  * Get a CCTP V2 bridge quote. Supports EVM ↔ EVM, Solana ↔ EVM, and → HyperCore.
83
82
  *
@@ -433,7 +433,6 @@ export const CCTP_DOMAINS = {
433
433
  // EVM V2 contracts — ALL EVM chains use the same V2 proxy addresses (per Circle docs)
434
434
  const EVM_TOKEN_MESSENGER_V2 = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d";
435
435
  const EVM_MESSAGE_TRANSMITTER_V2 = "0x81D40F21F12A8F0E3252Bccb954D722d4c464B64";
436
- export const EVM_TOKEN_MINTER_V2 = "0xfd78EE919681417d192449715b2594ab58f5D002";
437
436
  // Populate per-chain lookups (all EVM chains share identical V2 addresses)
438
437
  const EVM_CCTP_CHAINS = Object.keys(CCTP_DOMAINS).filter(c => c !== "solana");
439
438
  const CCTP_TOKEN_MESSENGER = Object.fromEntries(EVM_CCTP_CHAINS.map(c => [c, EVM_TOKEN_MESSENGER_V2]));
package/dist/cache.d.ts CHANGED
@@ -6,7 +6,6 @@
6
6
  */
7
7
  export declare const TTL_ACCOUNT = 5000;
8
8
  export declare const TTL_MARKET = 30000;
9
- export declare const TTL_META = 300000;
10
9
  export declare function getCached<T>(key: string): T | null;
11
10
  export declare function setCached<T>(key: string, data: T, ttlMs: number): void;
12
11
  /**
package/dist/cache.js CHANGED
@@ -11,7 +11,6 @@ const CACHE_DIR = join(tmpdir(), "perp-cli-cache");
11
11
  // TTL presets
12
12
  export const TTL_ACCOUNT = 5_000; // 5s — balance, positions, orders
13
13
  export const TTL_MARKET = 30_000; // 30s — funding rates, prices, markets
14
- export const TTL_META = 300_000; // 5min — dex universe, static metadata
15
14
  let dirReady = false;
16
15
  function ensureDir() {
17
16
  if (dirReady)
@@ -1,3 +1,3 @@
1
1
  import { Command } from "commander";
2
- import type { ExchangeAdapter } from "../exchanges/interface.js";
2
+ import type { ExchangeAdapter } from "../exchanges/index.js";
3
3
  export declare function registerAccountCommands(program: Command, getAdapter: () => Promise<ExchangeAdapter>, isJson: () => boolean, getAdapterForExchange?: (exchange: string) => Promise<ExchangeAdapter>): void;