Skip to main content
Glama

analyzeConsecutiveProfitLoss

Analyze consecutive winning and losing trades from cryptocurrency exchange accounts to identify trading patterns and performance trends.

Instructions

Analyze consecutive winning and losing trades

Input Schema

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

Implementation Reference

  • Tool registration using server.tool() including name, description, input schema, and inline handler function for analyzeConsecutiveProfitLoss.
    server.tool( "analyzeConsecutiveProfitLoss", "Analyze consecutive winning and losing trades", { 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", "all"]) .default("all") .describe("Analysis period: '30d' or 'all'"), }, async ({ accountName, symbol, period }) => { 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 consecutive analysis`, }, ], isError: true, }; } // 기간에 따른 since 값 계산 const now = Date.now(); let since; switch (period) { case "30d": since = now - 30 * 24 * 60 * 60 * 1000; // 30일 break; case "all": default: since = undefined; // 전체 데이터 break; } // 거래 내역 가져오기 const trades = await exchange.fetchMyTrades(symbol, since, undefined); // 최대 연속 손실/이익 계산 const analysis = analyzeConsecutiveTrades(trades); return { content: [ { type: "text", text: JSON.stringify(analysis, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error analyzing consecutive trades for account '${accountName}': ${ (error as Error).message }`, }, ], isError: true, }; } }
  • Handler function fetches trades from the CCXT exchange for the given account and period, then calls the analyzeConsecutiveTrades helper to perform the analysis.
    async ({ accountName, symbol, period }) => { 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 consecutive analysis`, }, ], isError: true, }; } // 기간에 따른 since 값 계산 const now = Date.now(); let since; switch (period) { case "30d": since = now - 30 * 24 * 60 * 60 * 1000; // 30일 break; case "all": default: since = undefined; // 전체 데이터 break; } // 거래 내역 가져오기 const trades = await exchange.fetchMyTrades(symbol, since, undefined); // 최대 연속 손실/이익 계산 const analysis = analyzeConsecutiveTrades(trades); return { content: [ { type: "text", text: JSON.stringify(analysis, null, 2), }, ], }; } catch (error) { return { content: [ { type: "text", text: `Error analyzing consecutive trades for account '${accountName}': ${ (error as Error).message }`, }, ], isError: true, }; }
  • Zod input schema defining parameters: accountName (string), symbol (optional string), period (enum ["30d", "all"], default "all").
    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", "all"]) .default("all") .describe("Analysis period: '30d' or 'all'"),
  • Core helper function analyzeConsecutiveTrades that implements the logic: pairs trades to determine win/loss outcomes, calculates maximum consecutive winning/losing streaks including start/end dates.
    function analyzeConsecutiveTrades(trades: any[]) { 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 tradeResults: boolean[] = []; let currentSide = null; let entryPrice = 0; trades.forEach((trade) => { if (currentSide === null) { // 첫 거래는 진입으로 간주 currentSide = trade.side; entryPrice = trade.price; } else if (currentSide !== trade.side) { // 반대 방향 거래는 포지션 종료로 간주 const isWin = (currentSide === 'buy' && trade.price > entryPrice) || (currentSide === 'sell' && trade.price < entryPrice); tradeResults.push(isWin); // 새로운 포지션 시작 currentSide = trade.side; entryPrice = trade.price; } }); // 연속 승/패 분석 let maxConsecutiveWins = 0; let maxConsecutiveLosses = 0; let currentConsecutiveWins = 0; let currentConsecutiveLosses = 0; let currentWinStreak = 0; let currentLossStreak = 0; let maxWinStreak = { count: 0, startIndex: 0, endIndex: 0 }; let maxLossStreak = { count: 0, startIndex: 0, endIndex: 0 }; tradeResults.forEach((isWin, index) => { if (isWin) { currentConsecutiveWins++; currentConsecutiveLosses = 0; if (currentWinStreak === 0) { currentWinStreak = 1; currentLossStreak = 0; } else { currentWinStreak++; } if (currentWinStreak > maxWinStreak.count) { maxWinStreak = { count: currentWinStreak, startIndex: index - currentWinStreak + 1, endIndex: index }; } } else { currentConsecutiveLosses++; currentConsecutiveWins = 0; if (currentLossStreak === 0) { currentLossStreak = 1; currentWinStreak = 0; } else { currentLossStreak++; } if (currentLossStreak > maxLossStreak.count) { maxLossStreak = { count: currentLossStreak, startIndex: index - currentLossStreak + 1, endIndex: index }; } } maxConsecutiveWins = Math.max(maxConsecutiveWins, currentConsecutiveWins); maxConsecutiveLosses = Math.max(maxConsecutiveLosses, currentConsecutiveLosses); }); // 최대 연속 승/패 시 총 손익 계산 const maxWinStreakTrades = maxWinStreak.count > 0 ? tradeResults.slice(maxWinStreak.startIndex, maxWinStreak.endIndex + 1) : []; const maxLossStreakTrades = maxLossStreak.count > 0 ? tradeResults.slice(maxLossStreak.startIndex, maxLossStreak.endIndex + 1) : []; return { totalCompletedTrades: tradeResults.length, winCount: tradeResults.filter(result => result).length, lossCount: tradeResults.filter(result => !result).length, maxConsecutiveWins, maxConsecutiveLosses, maxWinStreak: { count: maxWinStreak.count, startDate: maxWinStreak.count > 0 ? trades[maxWinStreak.startIndex].datetime : null, endDate: maxWinStreak.count > 0 ? trades[maxWinStreak.endIndex].datetime : null, }, maxLossStreak: { count: maxLossStreak.count, startDate: maxLossStreak.count > 0 ? trades[maxLossStreak.startIndex].datetime : null, endDate: maxLossStreak.count > 0 ? trades[maxLossStreak.endIndex].datetime : null, }, currentStreakType: currentConsecutiveWins > 0 ? "win" : "loss", currentStreakCount: Math.max(currentConsecutiveWins, currentConsecutiveLosses), }; }

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