get_positions
Get open paper positions with live prices; the system automatically executes stop-loss or take-profit sells when price thresholds are hit.
Instructions
View all open paper positions with live prices. Automatically triggers stop-loss or take-profit sells if price thresholds are hit.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools.js:192-219 (registration)Registration of the 'get_positions' MCP tool with description, empty schema (no inputs), and metadata hints. Calls either traderFetch('/positions') for remote trading or paper.getPositions() for paper trading.
server.tool( 'get_positions', 'View all open paper positions with live prices. Automatically triggers stop-loss or take-profit sells if price thresholds are hit.', {}, { title: 'Get Positions', readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false, }, async () => { try { let result; if (useRemoteTrader) { result = await traderFetch('/positions'); } else { result = await paper.getPositions(); } if (!result.positions?.length) { return text('No open positions.'); } return text(JSON.stringify(result, null, 2)); } catch (err) { return error(err.message); } } ); - src/paper-engine.js:158-197 (handler)Core implementation of getPositions() in the PaperEngine class. Iterates over all open positions (#positions map), fetches current prices, calculates unrealized PnL %, checks stop-loss/take-profit thresholds, automatically executes triggered sells, and returns { positions, autoSells }.
async getPositions() { const results = []; const triggered = []; for (const [mint, pos] of this.#positions) { const currentPrice = await this.#fetchPrice(mint) || this.#priceCache.get(mint) || pos.entryPrice; this.#priceCache.set(mint, currentPrice); if (currentPrice > pos.highWaterMark) pos.highWaterMark = currentPrice; const pnlPct = ((currentPrice - pos.entryPrice) / pos.entryPrice) * 100; if (currentPrice <= pos.stopLoss) { triggered.push({ mint, reason: 'stop_loss', pnlPct }); } else if (currentPrice >= pos.takeProfit) { triggered.push({ mint, reason: 'take_profit', pnlPct }); } results.push({ mint, symbol: pos.symbol, entrySol: pos.entrySol, entryPrice: pos.entryPrice, currentPrice, unrealizedPnlPct: Math.round(pnlPct * 100) / 100, holdTimeMin: Math.round((Date.now() - pos.entryTime) / 60_000), stopLoss: pos.stopLoss, takeProfit: pos.takeProfit, highWaterMark: pos.highWaterMark, }); } // Auto-execute SL/TP const autoSells = []; for (const t of triggered) { const result = await this.sell(t.mint, t.reason); if (result.success) autoSells.push(result); } return { positions: results, autoSells }; } - src/tools.js:53-69 (helper)The traderFetch helper function used by the remote trader path of get_positions. Makes authenticated HTTP requests to the remote trader API.
async function traderFetch(path, options = {}) { const url = traderUrl.replace(/\/+$/, '') + path; const res = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', 'x-webhook-secret': traderKey, ...(options.headers || {}), }, signal: AbortSignal.timeout(TRADER_TIMEOUT_MS), }); if (!res.ok) { const body = await res.text().catch(() => ''); throw new Error(`Trader API ${res.status}: ${body}`); } return res.json(); }