perp-cli 0.6.4 → 0.7.1
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/README.md +7 -2
- package/dist/api/public/hyperliquid.d.ts +2 -2
- package/dist/api/public/hyperliquid.js +0 -10
- package/dist/api/public/lighter.d.ts +2 -1
- package/dist/api/public/pacifica.d.ts +2 -1
- package/dist/arb/index.d.ts +7 -4
- package/dist/arb/index.js +5 -4
- package/dist/arb/sizing.d.ts +1 -1
- package/dist/arb/state.d.ts +1 -1
- package/dist/arb/state.js +4 -3
- package/dist/arb/utils.d.ts +0 -10
- package/dist/arb/utils.js +0 -42
- package/dist/bot/apex/guard.d.ts +26 -0
- package/dist/bot/apex/guard.js +86 -0
- package/dist/bot/apex/index.d.ts +6 -0
- package/dist/bot/apex/index.js +4 -0
- package/dist/bot/apex/pulse.d.ts +22 -0
- package/dist/bot/apex/pulse.js +107 -0
- package/dist/bot/apex/radar.d.ts +17 -0
- package/dist/bot/apex/radar.js +150 -0
- package/dist/bot/conditions.d.ts +1 -1
- package/dist/bot/config.d.ts +5 -1
- package/dist/bot/config.js +1 -1
- package/dist/bot/engine.d.ts +23 -2
- package/dist/bot/engine.js +296 -351
- package/dist/bot/index.d.ts +16 -0
- package/dist/bot/index.js +10 -0
- package/dist/bot/indicators.d.ts +29 -0
- package/dist/bot/indicators.js +144 -0
- package/dist/bot/reflect.d.ts +34 -0
- package/dist/bot/reflect.js +155 -0
- package/dist/bot/strategies/aggressive-taker.d.ts +36 -0
- package/dist/bot/strategies/aggressive-taker.js +125 -0
- package/dist/bot/strategies/apex-strategy.d.ts +36 -0
- package/dist/bot/strategies/apex-strategy.js +185 -0
- package/dist/bot/strategies/avellaneda-mm.d.ts +27 -0
- package/dist/bot/strategies/avellaneda-mm.js +111 -0
- package/dist/bot/strategies/basis-arb.d.ts +27 -0
- package/dist/bot/strategies/basis-arb.js +97 -0
- package/dist/bot/strategies/claude-agent.d.ts +31 -0
- package/dist/bot/strategies/claude-agent.js +155 -0
- package/dist/bot/strategies/dca-strategy.d.ts +25 -0
- package/dist/bot/strategies/dca-strategy.js +59 -0
- package/dist/bot/strategies/engine-mm.d.ts +29 -0
- package/dist/bot/strategies/engine-mm.js +141 -0
- package/dist/bot/strategies/funding-arb-strategy.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-strategy.js +168 -0
- package/dist/bot/strategies/funding-arb-v2.d.ts +32 -0
- package/dist/bot/strategies/funding-arb-v2.js +222 -0
- package/dist/bot/strategies/funding-auto.d.ts +1 -0
- package/dist/bot/strategies/funding-auto.js +572 -0
- package/dist/bot/strategies/grid-mm.d.ts +25 -0
- package/dist/bot/strategies/grid-mm.js +99 -0
- package/dist/bot/strategies/grid-strategy.d.ts +39 -0
- package/dist/bot/strategies/grid-strategy.js +176 -0
- package/dist/bot/strategies/hedge-agent.d.ts +25 -0
- package/dist/bot/strategies/hedge-agent.js +79 -0
- package/dist/bot/strategies/liquidation-mm.d.ts +25 -0
- package/dist/bot/strategies/liquidation-mm.js +108 -0
- package/dist/bot/strategies/mean-reversion.d.ts +35 -0
- package/dist/bot/strategies/mean-reversion.js +118 -0
- package/dist/bot/strategies/momentum-breakout.d.ts +36 -0
- package/dist/bot/strategies/momentum-breakout.js +159 -0
- package/dist/bot/strategies/regime-mm.d.ts +25 -0
- package/dist/bot/strategies/regime-mm.js +111 -0
- package/dist/bot/strategies/rfq-agent.d.ts +25 -0
- package/dist/bot/strategies/rfq-agent.js +71 -0
- package/dist/bot/strategies/simple-mm.d.ts +25 -0
- package/dist/bot/strategies/simple-mm.js +76 -0
- package/dist/bot/strategy-registry.d.ts +9 -0
- package/dist/bot/strategy-registry.js +14 -0
- package/dist/bot/strategy-types.d.ts +64 -0
- package/dist/bot/strategy-types.js +5 -0
- package/dist/bot/trade-journal.d.ts +27 -0
- package/dist/bot/trade-journal.js +53 -0
- package/dist/bot/tui/BotDashboard.d.ts +49 -0
- package/dist/bot/tui/BotDashboard.js +93 -0
- package/dist/bot/tui/index.d.ts +6 -0
- package/dist/bot/tui/index.js +8 -0
- package/dist/bridge-engine.d.ts +0 -1
- package/dist/bridge-engine.js +0 -1
- package/dist/cache.d.ts +0 -1
- package/dist/cache.js +0 -1
- package/dist/commands/account.d.ts +1 -1
- package/dist/commands/account.js +94 -91
- package/dist/commands/agent.d.ts +1 -1
- package/dist/commands/arb/index.d.ts +1 -1
- package/dist/commands/arb/index.js +1 -1
- package/dist/commands/arb-auto.d.ts +1 -1
- package/dist/commands/arb-auto.js +1 -1
- package/dist/commands/arb.d.ts +1 -1
- package/dist/commands/bot.d.ts +1 -1
- package/dist/commands/bot.js +198 -14
- package/dist/commands/dashboard.d.ts +1 -1
- package/dist/commands/dashboard.js +1 -1
- package/dist/commands/funds.d.ts +1 -1
- package/dist/commands/funds.js +33 -20
- package/dist/commands/history.d.ts +1 -1
- package/dist/commands/history.js +5 -4
- package/dist/commands/manage.d.ts +2 -3
- package/dist/commands/manage.js +11 -18
- package/dist/commands/market.d.ts +1 -1
- package/dist/commands/market.js +7 -7
- package/dist/commands/plan.d.ts +1 -1
- package/dist/commands/rebalance.d.ts +1 -1
- package/dist/commands/rebalance.js +8 -9
- package/dist/commands/risk.d.ts +1 -1
- package/dist/commands/settings.d.ts +1 -1
- package/dist/commands/settings.js +3 -1
- package/dist/commands/trade.d.ts +1 -1
- package/dist/commands/trade.js +39 -52
- package/dist/commands/wallet.d.ts +0 -2
- package/dist/commands/wallet.js +0 -5
- package/dist/constants.d.ts +0 -3
- package/dist/constants.js +0 -4
- package/dist/cross-chain-margin.d.ts +1 -1
- package/dist/dashboard/index.d.ts +4 -0
- package/dist/dashboard/index.js +3 -0
- package/dist/dashboard/server.d.ts +1 -1
- package/dist/dashboard/server.js +1 -1
- package/dist/dashboard/ws-feeds.d.ts +1 -1
- package/dist/event-stream.d.ts +1 -1
- package/dist/exchanges/aster.d.ts +58 -0
- package/dist/exchanges/aster.js +409 -0
- package/dist/exchanges/capabilities.d.ts +74 -0
- package/dist/exchanges/capabilities.js +44 -0
- package/dist/exchanges/hyperliquid-spot.d.ts +1 -1
- package/dist/exchanges/hyperliquid-spot.js +20 -12
- package/dist/exchanges/hyperliquid.d.ts +8 -2
- package/dist/exchanges/hyperliquid.js +21 -6
- package/dist/exchanges/index.d.ts +11 -0
- package/dist/exchanges/index.js +10 -0
- package/dist/exchanges/interface.d.ts +3 -0
- package/dist/exchanges/lighter-spot.d.ts +13 -0
- package/dist/exchanges/lighter-spot.js +98 -26
- package/dist/exchanges/lighter.d.ts +9 -3
- package/dist/exchanges/lighter.js +18 -8
- package/dist/exchanges/nado.d.ts +52 -0
- package/dist/exchanges/nado.js +223 -0
- package/dist/exchanges/pacifica.d.ts +7 -1
- package/dist/exchanges/pacifica.js +17 -2
- package/dist/exchanges/registry.d.ts +13 -0
- package/dist/exchanges/registry.js +37 -0
- package/dist/exchanges/spot-interface.d.ts +9 -0
- package/dist/funding/rates.js +1 -1
- package/dist/index.js +2 -6
- package/dist/jobs.js +1 -1
- package/dist/liquidity.d.ts +1 -2
- package/dist/liquidity.js +12 -5
- package/dist/mcp-server.js +4 -2
- package/dist/pacifica/constants.d.ts +0 -3
- package/dist/pacifica/constants.js +0 -4
- package/dist/pacifica/types/account.d.ts +0 -8
- package/dist/pacifica/types/lake.d.ts +0 -6
- package/dist/pacifica/types/position.d.ts +0 -12
- package/dist/pacifica/types/ws.d.ts +0 -24
- package/dist/pacifica/types/ws.js +1 -41
- package/dist/plan-executor.d.ts +1 -1
- package/dist/rebalance.d.ts +1 -7
- package/dist/rebalance.js +0 -6
- package/dist/risk.d.ts +1 -1
- package/dist/risk.js +3 -1
- package/dist/smart-order.d.ts +1 -1
- package/dist/strategies/dca.d.ts +1 -1
- package/dist/strategies/funding-arb.d.ts +1 -1
- package/dist/strategies/funding-arb.js +193 -181
- package/dist/strategies/grid.d.ts +1 -1
- package/dist/strategies/grid.js +1 -1
- package/dist/strategies/index.d.ts +12 -0
- package/dist/strategies/index.js +7 -0
- package/dist/strategies/split-order.d.ts +1 -1
- package/dist/strategies/trailing-stop.d.ts +1 -1
- package/dist/strategies/twap.d.ts +1 -1
- package/dist/trade-validator.d.ts +1 -1
- package/package.json +7 -1
- package/skills/perp-cli/SKILL.md +20 -0
- 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,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,49 @@
|
|
|
1
|
+
export interface BotTuiState {
|
|
2
|
+
phase: string;
|
|
3
|
+
equity: number;
|
|
4
|
+
peakEquity: number;
|
|
5
|
+
dailyPnl: number;
|
|
6
|
+
fills: number;
|
|
7
|
+
totalPnl: number;
|
|
8
|
+
runtime: number;
|
|
9
|
+
strategy: string;
|
|
10
|
+
symbol: string;
|
|
11
|
+
exchange: string;
|
|
12
|
+
price: number;
|
|
13
|
+
volume24h: number;
|
|
14
|
+
openInterest: string;
|
|
15
|
+
fundingRate: number;
|
|
16
|
+
volatility24h: number;
|
|
17
|
+
positions: Position[];
|
|
18
|
+
openOrders: OpenOrder[];
|
|
19
|
+
strategyState: Record<string, unknown>;
|
|
20
|
+
tick: number;
|
|
21
|
+
}
|
|
22
|
+
export interface Position {
|
|
23
|
+
symbol: string;
|
|
24
|
+
side: string;
|
|
25
|
+
size: string;
|
|
26
|
+
entryPrice: string;
|
|
27
|
+
markPrice: string;
|
|
28
|
+
unrealizedPnl: string;
|
|
29
|
+
}
|
|
30
|
+
export interface OpenOrder {
|
|
31
|
+
orderId: string;
|
|
32
|
+
side: string;
|
|
33
|
+
price: string;
|
|
34
|
+
size: string;
|
|
35
|
+
type: string;
|
|
36
|
+
}
|
|
37
|
+
export interface LogEntry {
|
|
38
|
+
time: string;
|
|
39
|
+
message: string;
|
|
40
|
+
}
|
|
41
|
+
export type StateListener = (state: BotTuiState) => void;
|
|
42
|
+
export type LogListener = (entry: LogEntry) => void;
|
|
43
|
+
export interface DashboardProps {
|
|
44
|
+
initialState: BotTuiState;
|
|
45
|
+
onQuit: () => void;
|
|
46
|
+
onPause: () => void;
|
|
47
|
+
subscribe: (onState: StateListener, onLog: LogListener) => () => void;
|
|
48
|
+
}
|
|
49
|
+
export declare function BotDashboard({ initialState, onQuit, onPause, subscribe }: DashboardProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
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)] })] })] })] }), _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: [_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) => {
|
|
72
|
+
const upnl = parseFloat(p.unrealizedPnl);
|
|
73
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_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));
|
|
74
|
+
})] }))] }), _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"] }) })] }));
|
|
75
|
+
}
|
|
76
|
+
// ── Utility ──
|
|
77
|
+
function pad(s, width) {
|
|
78
|
+
return s.length >= width ? s.slice(0, width) : s + " ".repeat(width - s.length);
|
|
79
|
+
}
|
|
80
|
+
function colorizeLog(msg) {
|
|
81
|
+
// Strip ANSI codes for TUI since ink handles colors
|
|
82
|
+
const clean = msg.replace(/\x1b\[[0-9;]*m/g, "");
|
|
83
|
+
if (clean.includes("ERROR") || clean.includes("✗") || clean.includes("failed")) {
|
|
84
|
+
return _jsx(Text, { color: "red", children: clean });
|
|
85
|
+
}
|
|
86
|
+
if (clean.includes("WARNING") || clean.includes("⚠")) {
|
|
87
|
+
return _jsx(Text, { color: "yellow", children: clean });
|
|
88
|
+
}
|
|
89
|
+
if (clean.includes("✓") || clean.includes("started") || clean.includes("profit")) {
|
|
90
|
+
return _jsx(Text, { color: "green", children: clean });
|
|
91
|
+
}
|
|
92
|
+
return _jsx(Text, { color: "gray", children: clean });
|
|
93
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { BotDashboard } from "./BotDashboard.js";
|
|
2
|
+
export type { BotTuiState, 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
|
+
}
|
package/dist/bridge-engine.d.ts
CHANGED
|
@@ -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
|
*
|
package/dist/bridge-engine.js
CHANGED
|
@@ -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/
|
|
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;
|