minara 0.3.0 → 0.4.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/README.md CHANGED
@@ -21,7 +21,8 @@
21
21
  - **AI Chat** — Crypto-native AI for on-chain analysis, market research, and DeFi due diligence. Interactive REPL & single-shot queries with `fast` / `quality` / `thinking` modes
22
22
  - **Wallet & Balance** — Unified balance view, spot holdings with PnL, perps account overview, deposits, withdrawals, and credit card on-ramp via MoonPay
23
23
  - **Chain-Abstracted Trading** — Cross-chain swaps with automatic chain detection, perpetual futures, and limit orders. Accepts `$TICKER`, token name, or contract address
24
- - **AI Autopilot & Analysis** — Fully managed AI trading strategies for perps, plus on-demand long/short analysis with one-click quick order
24
+ - **Multi-Wallet Perps** — Manage multiple sub-wallets per account: create, rename, sweep, transfer USDC between wallets. All trading commands support `--wallet` targeting
25
+ - **AI Autopilot & Analysis** — Multi-strategy AI trading per wallet with performance dashboard, strategy comparison table, and one-click config management. Plus on-demand long/short analysis with quick order
25
26
  - **Market Discovery** — Trending tokens & stocks, Fear & Greed Index, on-chain metrics, and search
26
27
 
27
28
  ## Installation
@@ -135,28 +136,44 @@ minara swap --dry-run # Simulate without executing
135
136
  | `minara perps deposit` | Deposit USDC to perps (or use `minara deposit perps`) |
136
137
  | `minara perps withdraw` | Withdraw USDC from perps account |
137
138
  | `minara perps fund-records` | View fund deposit/withdrawal records |
138
- | `minara perps autopilot` | Manage AI autopilot trading strategy (on/off/config) |
139
+ | `minara perps wallets` | List all sub-wallets with balances and autopilot info |
140
+ | `minara perps create-wallet`| Create a new perps sub-wallet |
141
+ | `minara perps rename-wallet`| Rename a sub-wallet |
142
+ | `minara perps sweep` | Consolidate funds from sub-wallet to default |
143
+ | `minara perps transfer` | Transfer USDC between sub-wallets |
144
+ | `minara perps autopilot` | Manage AI autopilot strategies per wallet |
139
145
  | `minara perps ask` | AI long/short analysis with quick order |
140
146
 
141
147
  ```bash
142
- minara perps positions # List positions with equity, margin, PnL
143
- minara perps order # Interactive: symbol selector side size → confirm
148
+ minara perps positions # Positions per wallet with equity, margin, PnL
149
+ minara perps positions --wallet Bot-1 # Positions for a specific wallet only
150
+ minara perps order # Interactive: symbol → side → size → confirm
151
+ minara perps order --wallet Bot-1 # Place order on a specific wallet
144
152
  minara perps close # Close a position: pick from list → market close
153
+ minara perps close --all # Close all positions at once
145
154
  minara perps cancel # Cancel an order: pick from open orders list
146
155
  minara perps leverage # Interactive: shows max leverage per asset
147
156
  minara perps trades # Recent fills from Hyperliquid (default 7 days)
148
157
  minara perps trades -d 30 # Last 30 days of trade history
149
158
  minara perps deposit -a 100 # Deposit 100 USDC to perps
150
159
  minara perps withdraw -a 50 # Withdraw 50 USDC from perps
151
- minara perps autopilot # Toggle AI autopilot, create/update strategy
160
+ minara perps wallets # All wallets: equity, margin, PnL, strategies
161
+ minara perps create-wallet -n Bot-2 # Create a new sub-wallet
162
+ minara perps sweep # Move funds from a sub-wallet to default
163
+ minara perps transfer # Transfer USDC between any two wallets
164
+ minara perps autopilot # Strategy dashboard: enable/disable, config, perf
152
165
  minara perps ask # AI analysis → optional quick order
153
166
  ```
154
167
 
168
+ > **Multi-wallet support:** All trading commands (`order`, `deposit`, `withdraw`, `close`, `cancel`, `leverage`, `trades`, `fund-records`, `ask`) accept `--wallet <name>` to target a specific sub-wallet. If omitted, you'll be prompted to pick one interactively.
169
+ >
170
+ > **Autopilot dashboard:** Each wallet can have multiple AI strategies. The dashboard shows strategy name, status, config, and a performance comparison table across all available strategies with the active one highlighted.
171
+ >
155
172
  > **Close position:** Select an open position from the list, and it will be closed at market price with a reduce-only order in the opposite direction — no manual price or size entry needed.
156
173
  >
157
174
  > **Cancel order:** Open orders are fetched from Hyperliquid and shown as a selectable list with coin, side, size, and price — no need to look up order IDs.
158
175
  >
159
- > **Autopilot:** When autopilot is ON, manual order placement (`minara perps order`) is blocked to prevent conflicts with AI-managed trades. Turn off autopilot first via `minara perps autopilot`.
176
+ > **Autopilot:** When autopilot is ON for a wallet, manual order placement on that wallet is blocked to prevent conflicts with AI-managed trades. Turn off autopilot first via `minara perps autopilot`.
160
177
  >
161
178
  > **Ask AI → Quick Order:** After the AI analysis, you can instantly place a market order based on the recommended direction, entry price, and position size — no need to re-enter parameters.
162
179
 
@@ -340,12 +357,12 @@ minara --help
340
357
 
341
358
  ## Testing
342
359
 
343
- Test suite built with [Vitest](https://vitest.dev/) — 119 tests covering unit, API, and command integration layers.
360
+ Test suite built with [Vitest](https://vitest.dev/) — 251 tests covering unit, API, command integration, and formatter layers.
344
361
 
345
362
  ```bash
346
363
  npm test # Run all tests
347
364
  npm run test:watch # Watch mode
348
- npm run test:coverage # With coverage report
365
+ npm run test:coverage # With v8 coverage report
349
366
  ```
350
367
 
351
368
  ## Security
@@ -1,4 +1,4 @@
1
- import type { PerpsDepositDto, PerpsWithdrawDto, PerpsPlaceOrdersDto, PerpsCancelOrdersDto, UpdateLeverageDto, PerpsPosition, TokenPrice, TransactionResult } from '../types.js';
1
+ import type { PerpsDepositDto, PerpsWithdrawDto, PerpsPlaceOrdersDto, PerpsCancelOrdersDto, UpdateLeverageDto, PerpsPosition, TokenPrice, TransactionResult, PerpSubAccount, CreatePerpSubAccountDto, RenamePerpSubAccountDto, TransferFundsDto, SweepFundsDto } from '../types.js';
2
2
  /** Deposit USDC to perps (min 5 USDC) */
3
3
  export declare function deposit(token: string, dto: PerpsDepositDto): Promise<import("../types.js").ApiResponse<TransactionResult>>;
4
4
  /** Withdraw USDC from perps */
@@ -33,6 +33,7 @@ export declare function createStrategy(token: string, dto: {
33
33
  symbols: string[];
34
34
  strategyConfig?: Record<string, unknown>;
35
35
  language?: string;
36
+ subAccountId?: string;
36
37
  }): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
37
38
  export declare function enableStrategy(token: string, strategyId: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
38
39
  export declare function disableStrategy(token: string, strategyId: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
@@ -43,6 +44,29 @@ export declare function updateStrategy(token: string, dto: {
43
44
  language?: string;
44
45
  }): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
45
46
  export declare function getPerformanceMetrics(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
47
+ export declare function getMinEquityValue(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
48
+ export declare function setMinEquityValue(token: string, dto: Record<string, unknown>): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
49
+ export declare function getRecords(token: string, page: number, limit: number): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
50
+ /** List all perp sub-accounts */
51
+ export declare function listSubAccounts(token: string): Promise<import("../types.js").ApiResponse<PerpSubAccount[]>>;
52
+ /** Create a new perp sub-account */
53
+ export declare function createSubAccount(token: string, dto: CreatePerpSubAccountDto): Promise<import("../types.js").ApiResponse<PerpSubAccount>>;
54
+ /** Rename a perp sub-account */
55
+ export declare function renameSubAccount(token: string, dto: RenamePerpSubAccountDto): Promise<import("../types.js").ApiResponse<void>>;
56
+ /** Get summary for a specific sub-account */
57
+ export declare function getSubAccountSummary(token: string, subAccountId: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
58
+ /** Get aggregated PnL across all sub-accounts */
59
+ export declare function getAggregatedSummary(token: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>>>;
60
+ /** Get autopilot records for a specific sub-account */
61
+ export declare function getSubAccountRecords(token: string, subAccountId: string, page: number, limit: number): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
62
+ /** Get fills for a specific sub-account */
63
+ export declare function getSubAccountFills(token: string, subAccountId: string, startTime: number): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
64
+ /** Get open orders for a specific sub-account */
65
+ export declare function getSubAccountOpenOrders(token: string, subAccountId: string): Promise<import("../types.js").ApiResponse<Record<string, unknown>[]>>;
66
+ /** Transfer USDC between sub-accounts (internal, no gas) */
67
+ export declare function transferFunds(token: string, dto: TransferFundsDto): Promise<import("../types.js").ApiResponse<TransactionResult>>;
68
+ /** Sweep all funds from a sub-account back to the default account */
69
+ export declare function sweepFunds(token: string, dto: SweepFundsDto): Promise<import("../types.js").ApiResponse<TransactionResult>>;
46
70
  export declare function priceAnalysis(token: string, dto: {
47
71
  symbol: string;
48
72
  startTime?: number;
package/dist/api/perps.js CHANGED
@@ -77,6 +77,56 @@ export function updateStrategy(token, dto) {
77
77
  export function getPerformanceMetrics(token) {
78
78
  return get('/v1/fully-managed/performance/metrics/v2', { token });
79
79
  }
80
+ export function getMinEquityValue(token) {
81
+ return get('/v1/fully-managed/get-min-equity-value', { token });
82
+ }
83
+ export function setMinEquityValue(token, dto) {
84
+ return post('/v1/fully-managed/set-min-equity-value', { token, body: dto });
85
+ }
86
+ export function getRecords(token, page, limit) {
87
+ return get('/v1/fully-managed/records', { token, query: { page, limit } });
88
+ }
89
+ // ── Perp Wallets (multi sub-account) ──────────────────────────────────────
90
+ /** List all perp sub-accounts */
91
+ export function listSubAccounts(token) {
92
+ return get('/v1/perp-wallets', { token });
93
+ }
94
+ /** Create a new perp sub-account */
95
+ export function createSubAccount(token, dto) {
96
+ return post('/v1/perp-wallets', { token, body: dto });
97
+ }
98
+ /** Rename a perp sub-account */
99
+ export function renameSubAccount(token, dto) {
100
+ return post('/v1/perp-wallets/rename', { token, body: dto });
101
+ }
102
+ /** Get summary for a specific sub-account */
103
+ export function getSubAccountSummary(token, subAccountId) {
104
+ return get('/v1/perp-wallets/summary', { token, query: { subAccountId } });
105
+ }
106
+ /** Get aggregated PnL across all sub-accounts */
107
+ export function getAggregatedSummary(token) {
108
+ return get('/v1/perp-wallets/aggregated-summary', { token });
109
+ }
110
+ /** Get autopilot records for a specific sub-account */
111
+ export function getSubAccountRecords(token, subAccountId, page, limit) {
112
+ return get('/v1/perp-wallets/records', { token, query: { subAccountId, page, limit } });
113
+ }
114
+ /** Get fills for a specific sub-account */
115
+ export function getSubAccountFills(token, subAccountId, startTime) {
116
+ return get('/v1/perp-wallets/fills', { token, query: { subAccountId, startTime } });
117
+ }
118
+ /** Get open orders for a specific sub-account */
119
+ export function getSubAccountOpenOrders(token, subAccountId) {
120
+ return get('/v1/perp-wallets/open-orders', { token, query: { subAccountId } });
121
+ }
122
+ /** Transfer USDC between sub-accounts (internal, no gas) */
123
+ export function transferFunds(token, dto) {
124
+ return post('/v1/perp-wallets/transfer', { token, body: dto });
125
+ }
126
+ /** Sweep all funds from a sub-account back to the default account */
127
+ export function sweepFunds(token, dto) {
128
+ return post('/v1/perp-wallets/sweep', { token, body: dto });
129
+ }
80
130
  // ── Price Analysis (Ask Long/Short) ──────────────────────────────────────
81
131
  export function priceAnalysis(token, dto) {
82
132
  return post('/tokens/price-analysis', { token, body: dto });
@@ -79,9 +79,63 @@ const perpsCmd = new Command('perps')
79
79
  await showPerpsAssets(creds.accessToken);
80
80
  }));
81
81
  async function showPerpsAssets(token) {
82
- const spin = spinner('Fetching perps account…');
83
- const res = await perpsApi.getAccountSummary(token);
82
+ const spin = spinner('Fetching perps wallets…');
83
+ const walletsRes = await perpsApi.listSubAccounts(token);
84
84
  spin.stop();
85
+ let wallets = [];
86
+ if (walletsRes.success && walletsRes.data) {
87
+ const raw = walletsRes.data;
88
+ if (Array.isArray(raw))
89
+ wallets = raw;
90
+ else if (raw && typeof raw === 'object') {
91
+ const inner = raw.data
92
+ ?? raw.subAccounts
93
+ ?? raw.wallets;
94
+ if (Array.isArray(inner))
95
+ wallets = inner;
96
+ }
97
+ }
98
+ const fmt = (n) => `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
99
+ const pnlFmt = (n) => {
100
+ const color = n >= 0 ? chalk.green : chalk.red;
101
+ return color(`${n >= 0 ? '+' : ''}${fmt(n)}`);
102
+ };
103
+ if (wallets.length > 0) {
104
+ console.log('');
105
+ console.log(chalk.bold(`Perps Wallets (${wallets.length}):`));
106
+ for (const w of wallets) {
107
+ const name = String(w.name ?? 'Unnamed');
108
+ const def = w.isDefault ? chalk.cyan(' (default)') : '';
109
+ console.log('');
110
+ console.log(chalk.bold(` ${name}${def}:`));
111
+ console.log(` Equity : ${fmt(Number(w.equityValue ?? 0))}`);
112
+ console.log(` Available : ${fmt(Number(w.dispatchableValue ?? 0))}`);
113
+ console.log(` Margin Used : ${fmt(Number(w.totalMarginUsed ?? 0))}`);
114
+ console.log(` Unrealized PnL: ${pnlFmt(Number(w.totalUnrealizedPnl ?? 0))}`);
115
+ const positions = Array.isArray(w.positions) ? w.positions : [];
116
+ if (positions.length > 0) {
117
+ printTable(positions, POSITION_COLUMNS);
118
+ }
119
+ else {
120
+ console.log(chalk.dim(' No open positions.'));
121
+ }
122
+ }
123
+ // Aggregated summary
124
+ const aggRes = await perpsApi.getAggregatedSummary(token);
125
+ if (aggRes.success && aggRes.data) {
126
+ const d = aggRes.data;
127
+ console.log('');
128
+ console.log(chalk.bold(' Aggregated:'));
129
+ console.log(` Total Equity : ${fmt(Number(d.totalEquity ?? d.equityValue ?? 0))}`);
130
+ console.log(` Total Unrl. PnL : ${pnlFmt(Number(d.totalUnrealizedPnl ?? 0))}`);
131
+ }
132
+ console.log('');
133
+ return;
134
+ }
135
+ // Fallback to legacy single-account API
136
+ const legacySpin = spinner('Fetching perps account…');
137
+ const res = await perpsApi.getAccountSummary(token);
138
+ legacySpin.stop();
85
139
  if (!res.success || !res.data) {
86
140
  console.log(chalk.dim(' Could not fetch perps account.'));
87
141
  if (res.error?.message)
@@ -89,12 +143,6 @@ async function showPerpsAssets(token) {
89
143
  return;
90
144
  }
91
145
  const d = res.data;
92
- const fmt = (n) => `$${n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`;
93
- const pnlFmt = (n) => {
94
- const color = n >= 0 ? chalk.green : chalk.red;
95
- return color(`${n >= 0 ? '+' : ''}${fmt(n)}`);
96
- };
97
- // ── Account overview ───────────────────────────────────────────────
98
146
  console.log('');
99
147
  console.log(chalk.bold('Perps Account:'));
100
148
  console.log(` Equity : ${fmt(Number(d.equityValue ?? 0))}`);
@@ -102,7 +150,6 @@ async function showPerpsAssets(token) {
102
150
  console.log(` Margin Used : ${fmt(Number(d.totalMarginUsed ?? 0))}`);
103
151
  console.log(` Unrealized PnL: ${pnlFmt(Number(d.totalUnrealizedPnl ?? 0))}`);
104
152
  console.log(` Withdrawable : ${fmt(Number(d.withdrawableValue ?? 0))}`);
105
- // ── Positions ───────────────────────────────────────────────────────
106
153
  const positions = Array.isArray(d.positions) ? d.positions : [];
107
154
  console.log('');
108
155
  console.log(chalk.bold(`Open Positions (${positions.length}):`));
@@ -10,8 +10,9 @@ export const balanceCommand = new Command('balance')
10
10
  .action(wrapAction(async () => {
11
11
  const creds = requireAuth();
12
12
  const spin = spinner('Fetching balances…');
13
- const [spotRes, perpsRes] = await Promise.all([
13
+ const [spotRes, perpsAggRes, perpsLegacyRes] = await Promise.all([
14
14
  get('/users/pnls/all', { token: creds.accessToken }),
15
+ perpsApi.getAggregatedSummary(creds.accessToken),
15
16
  perpsApi.getAccountSummary(creds.accessToken),
16
17
  ]);
17
18
  spin.stop();
@@ -28,8 +29,12 @@ export const balanceCommand = new Command('balance')
28
29
  }
29
30
  }
30
31
  let perpsAvailable = 0;
31
- if (perpsRes.success && perpsRes.data) {
32
- const d = perpsRes.data;
32
+ if (perpsAggRes.success && perpsAggRes.data) {
33
+ const d = perpsAggRes.data;
34
+ perpsAvailable = Number(d.totalDispatchable ?? d.dispatchableValue ?? d.totalEquity ?? 0);
35
+ }
36
+ else if (perpsLegacyRes.success && perpsLegacyRes.data) {
37
+ const d = perpsLegacyRes.data;
33
38
  perpsAvailable = Number(d.dispatchableValue ?? 0);
34
39
  }
35
40
  const total = spotStable + perpsAvailable;