minara 0.2.9 → 0.3.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.
@@ -286,6 +286,8 @@ const cancelCmd = new Command('cancel')
286
286
  const closeCmd = new Command('close')
287
287
  .description('Close an open perps position at market price')
288
288
  .option('-y, --yes', 'Skip confirmation')
289
+ .option('-a, --all', 'Close all open positions (non-interactive)')
290
+ .option('-s, --symbol <symbol>', 'Close position by symbol (non-interactive, e.g. BTC, ETH)')
289
291
  .action(wrapAction(async (opts) => {
290
292
  const creds = requireAuth();
291
293
  const spin = spinner('Fetching positions…');
@@ -307,21 +309,119 @@ const closeCmd = new Command('close')
307
309
  const color = n >= 0 ? chalk.green : chalk.red;
308
310
  return color(`${n >= 0 ? '+' : ''}${fmt(n)}`);
309
311
  };
310
- const selected = await select({
311
- message: 'Select position to close:',
312
- choices: positions.map((p) => {
312
+ // Helper to close specific positions (used by --all and --symbol)
313
+ const closePositions = async (positionsToClose, title) => {
314
+ console.log('');
315
+ console.log(chalk.bold(title));
316
+ console.log(` Positions to close: ${positionsToClose.length}`);
317
+ positionsToClose.forEach((p) => {
313
318
  const symbol = String(p.symbol ?? '');
314
319
  const side = String(p.side ?? '').toLowerCase();
315
- const sideLabel = side === 'long' || side === 'buy' ? chalk.green('LONG') : chalk.red('SHORT');
320
+ const sideLabel = side === 'long' || side === 'buy' ? 'LONG' : 'SHORT';
316
321
  const sz = String(p.size ?? '');
317
- const entry = fmt(Number(p.entryPrice ?? 0));
318
- const pnl = pnlFmt(Number(p.unrealizedPnl ?? 0));
319
- return {
320
- name: `${chalk.bold(symbol.padEnd(6))} ${sideLabel} ${sz} @ ${chalk.yellow(entry)} PnL: ${pnl}`,
321
- value: p,
322
+ console.log(` - ${symbol} ${sideLabel} ${sz}`);
323
+ });
324
+ console.log('');
325
+ if (!opts.yes) {
326
+ await requireTransactionConfirmation(`Close ${positionsToClose.length} position(s) @ Market`);
327
+ }
328
+ await requireTouchId();
329
+ const orderSpin = spinner('Closing positions…');
330
+ const results = [];
331
+ for (const pos of positionsToClose) {
332
+ const symbol = String(pos.symbol ?? '');
333
+ const side = String(pos.side ?? '').toLowerCase();
334
+ const sz = String(pos.size ?? '');
335
+ const isLong = side === 'long' || side === 'buy';
336
+ const isBuy = !isLong;
337
+ const assetMeta = assets.find((a) => a.name.toUpperCase() === symbol.toUpperCase());
338
+ const marketPx = assetMeta?.markPx;
339
+ if (!marketPx || marketPx <= 0) {
340
+ results.push({ symbol, side, success: false, error: 'Could not fetch price' });
341
+ continue;
342
+ }
343
+ const slippagePx = isBuy ? marketPx * 1.01 : marketPx * 0.99;
344
+ const limitPx = slippagePx.toPrecision(5);
345
+ const order = {
346
+ a: symbol,
347
+ b: isBuy,
348
+ p: limitPx,
349
+ s: sz,
350
+ r: true,
351
+ t: { trigger: { triggerPx: String(marketPx), tpsl: 'tp', isMarket: true } },
322
352
  };
323
- }),
353
+ try {
354
+ const orderRes = await perpsApi.placeOrders(creds.accessToken, { orders: [order], grouping: 'na' });
355
+ if (orderRes.success) {
356
+ results.push({ symbol, side, success: true });
357
+ }
358
+ else {
359
+ const errMsg = orderRes.error ? `${orderRes.error.code}: ${orderRes.error.message}` : 'Unknown error';
360
+ results.push({ symbol, side, success: false, error: errMsg });
361
+ }
362
+ }
363
+ catch (e) {
364
+ results.push({ symbol, side, success: false, error: String(e) });
365
+ }
366
+ }
367
+ orderSpin.stop();
368
+ // Report results
369
+ const succeeded = results.filter((r) => r.success);
370
+ const failed = results.filter((r) => !r.success);
371
+ if (succeeded.length > 0) {
372
+ success(`Closed ${succeeded.length} position(s):`);
373
+ succeeded.forEach((r) => {
374
+ console.log(` ✓ ${r.symbol} ${r.side.toUpperCase()}`);
375
+ });
376
+ }
377
+ if (failed.length > 0) {
378
+ warn(`Failed to close ${failed.length} position(s):`);
379
+ failed.forEach((r) => {
380
+ console.log(` ✗ ${r.symbol} ${r.side.toUpperCase()}: ${r.error}`);
381
+ });
382
+ }
383
+ };
384
+ // If --all flag is set, close all positions directly (non-interactive)
385
+ if (opts.all) {
386
+ await closePositions(positions, 'Close ALL Positions:');
387
+ return;
388
+ }
389
+ // If --symbol flag is set, close positions matching the symbol (non-interactive)
390
+ if (opts.symbol) {
391
+ const symbolUpper = opts.symbol.toUpperCase();
392
+ const matchingPositions = positions.filter((p) => String(p.symbol ?? '').toUpperCase() === symbolUpper);
393
+ if (matchingPositions.length === 0) {
394
+ warn(`No open positions found for symbol: ${opts.symbol}`);
395
+ return;
396
+ }
397
+ await closePositions(matchingPositions, `Close ${opts.symbol.toUpperCase()} Positions:`);
398
+ return;
399
+ }
400
+ const positionChoices = positions.map((p) => {
401
+ const symbol = String(p.symbol ?? '');
402
+ const side = String(p.side ?? '').toLowerCase();
403
+ const sideLabel = side === 'long' || side === 'buy' ? chalk.green('LONG') : chalk.red('SHORT');
404
+ const sz = String(p.size ?? '');
405
+ const entry = fmt(Number(p.entryPrice ?? 0));
406
+ const pnl = pnlFmt(Number(p.unrealizedPnl ?? 0));
407
+ return {
408
+ name: `${chalk.bold(symbol.padEnd(6))} ${sideLabel} ${sz} @ ${chalk.yellow(entry)} PnL: ${pnl}`,
409
+ value: p,
410
+ };
324
411
  });
412
+ // Add "ALL POSITIONS" option at the beginning
413
+ const allOption = { name: chalk.bold.cyan('[ CLOSE ALL POSITIONS ]'), value: '__ALL__' };
414
+ const choices = [allOption, ...positionChoices];
415
+ const selected = await select({
416
+ message: 'Select position to close:',
417
+ choices,
418
+ });
419
+ // Handle "ALL POSITIONS" selection
420
+ if (selected === '__ALL__') {
421
+ await closePositions(positions, 'Close ALL Positions:');
422
+ return;
423
+ }
424
+ // Single position close (existing logic)
325
425
  const symbol = String(selected.symbol ?? '');
326
426
  const side = String(selected.side ?? '').toLowerCase();
327
427
  const sz = String(selected.size ?? '');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minara",
3
- "version": "0.2.9",
3
+ "version": "0.3.1",
4
4
  "description": "CLI client for Minara.ai — login, trade, deposit/withdraw, chat and more from your terminal.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",