Skip to main content
Glama

analyzePeriodicReturns

Calculate and analyze daily, weekly, or monthly returns for a specified account and trading period using the CCXT MCP Server. Supports customizable intervals and optional symbol filtering for precise insights.

Instructions

Analyze daily and monthly returns for a configured account

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
accountNameYesAccount name defined in the configuration file (e.g., 'bybit_main')
intervalNoReturn calculation intervaldaily
periodNoAnalysis period: '30d', '90d', '180d', or '1y'90d
symbolNoOptional trading symbol (e.g., 'BTC/USDT') to filter trades

Implementation Reference

  • Primary implementation: server.tool() call that registers the tool, defines input schema with Zod, and provides the async handler function. The handler fetches historical trades using CCXT's fetchMyTrades, determines the time range based on period, and delegates computation to the calculatePeriodicReturns helper.
    server.tool( "analyzePeriodicReturns", "Analyze daily and monthly returns for a configured account", { accountName: z .string() .describe( "Account name defined in the configuration file (e.g., 'bybit_main')" ), symbol: z .string() .optional() .describe("Optional trading symbol (e.g., 'BTC/USDT') to filter trades"), period: z .enum(["30d", "90d", "180d", "1y"]) .default("90d") .describe("Analysis period: '30d', '90d', '180d', or '1y'"), interval: z .enum(["daily", "weekly", "monthly"]) .default("daily") .describe("Return calculation interval"), }, async ({ accountName, symbol, period, interval }) => { try { const exchange = ccxtServer.getExchangeInstance(accountName); // fetchMyTrades 메서드가 지원되는지 확인 if (!exchange.has["fetchMyTrades"]) { return { content: [ { type: "text", text: `Account '${accountName}' (Exchange: ${exchange.id}) does not support fetching personal trades for periodic returns analysis`, }, ], isError: true, }; } // 기간에 따른 since 값 계산 const now = Date.now(); let since; switch (period) { case "30d": since = now - 30 * 24 * 60 * 60 * 1000; break; case "90d": since = now - 90 * 24 * 60 * 60 * 1000; break; case "180d": since = now - 180 * 24 * 60 * 60 * 1000; break; case "1y": since = now - 365 * 24 * 60 * 60 * 1000; break; } // 거래 내역 가져오기 const trades = await exchange.fetchMyTrades(symbol, since, undefined); // 기간별 수익률 계산 const returns = calculatePeriodicReturns(trades, interval); return { content: [ { type: "text", text: JSON.stringify(returns, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error analyzing periodic returns for account '${accountName}': ${ (error as Error).message }`, }, ], isError: true, }; } } );
  • Input schema defined using Zod for parameters: accountName (required string), symbol (optional string), period (enum with default), interval (enum with default).
    { accountName: z .string() .describe( "Account name defined in the configuration file (e.g., 'bybit_main')" ), symbol: z .string() .optional() .describe("Optional trading symbol (e.g., 'BTC/USDT') to filter trades"), period: z .enum(["30d", "90d", "180d", "1y"]) .default("90d") .describe("Analysis period: '30d', '90d', '180d', or '1y'"), interval: z .enum(["daily", "weekly", "monthly"]) .default("daily") .describe("Return calculation interval"), },
  • Helper function that processes trade data, groups by specified interval (daily/weekly/monthly), computes profit per period using simplified P&L calculation, and returns statistics including total profit, average, best/worst periods, and ratio of profitable periods. Uses getWeekNumber for weekly grouping.
    function calculatePeriodicReturns(trades: any[], interval: string) { if (!trades || trades.length === 0) { return { totalTrades: 0, message: "No trades found for the specified period.", }; } // 거래를 시간순으로 정렬 trades.sort((a, b) => a.timestamp - b.timestamp); // 기간별로 그룹화 const periodicData: Record<string, { profit: number, trades: number }> = {}; trades.forEach(trade => { const date = new Date(trade.timestamp); let key: string; switch(interval) { case 'weekly': // 주차 계산 (ISO 주 - 1부터 53까지) const weekOfYear = getWeekNumber(date); key = `${date.getFullYear()}-W${weekOfYear}`; break; case 'monthly': // 월 (1월은 0) key = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}`; break; case 'daily': default: // 일 (YYYY-MM-DD) key = date.toISOString().split('T')[0]; } if (!periodicData[key]) { periodicData[key] = { profit: 0, trades: 0 }; } // 단순화된 손익 계산 const profit = trade.side === 'buy' ? -(trade.amount * trade.price) : (trade.amount * trade.price); periodicData[key].profit += profit - (trade.fee?.cost || 0); periodicData[key].trades++; }); // 결과 처리 const returns = Object.entries(periodicData).map(([period, data]) => ({ period, profit: data.profit.toFixed(8), trades: data.trades })).sort((a, b) => a.period.localeCompare(b.period)); // 통계 계산 const profitValues = returns.map(r => parseFloat(r.profit)); const totalProfit = profitValues.reduce((sum, profit) => sum + profit, 0); const averagePeriodProfit = profitValues.length > 0 ? totalProfit / profitValues.length : 0; const positiveReturns = profitValues.filter(p => p > 0); const negativeReturns = profitValues.filter(p => p < 0); return { interval, totalPeriods: returns.length, totalProfit: totalProfit.toFixed(8), averagePeriodProfit: averagePeriodProfit.toFixed(8), profitablePeriods: positiveReturns.length, lossPeriods: negativeReturns.length, profitablePeriodRatio: returns.length > 0 ? ((positiveReturns.length / returns.length) * 100).toFixed(2) + '%' : '0%', bestPeriod: profitValues.length > 0 ? returns[profitValues.indexOf(Math.max(...profitValues))] : null, worstPeriod: profitValues.length > 0 ? returns[profitValues.indexOf(Math.min(...profitValues))] : null, periodicReturns: returns }; }
  • src/server.ts:375-375 (registration)
    Top-level call to registerAnalysisTools within CcxtMcpServer.registerTools(), which in turn registers the analyzePeriodicReturns tool among others.
    registerAnalysisTools(this.server, this);

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/lazy-dinosaur/ccxt-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server