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),
      };
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden of behavioral disclosure. It states the tool analyzes trades but doesn't describe what the analysis entails (e.g., statistical summaries, visual outputs, or specific metrics), whether it requires specific permissions or data availability, or any limitations like rate limits or data freshness. This leaves significant gaps in understanding the tool's behavior beyond its basic purpose.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence with zero waste. It's appropriately sized for a tool with a clear but narrow purpose, and it's front-loaded with the core action ('analyze') and target ('consecutive winning and losing trades'), making it easy to parse quickly.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (analyzing trade patterns), lack of annotations, and no output schema, the description is incomplete. It doesn't explain what the analysis outputs (e.g., statistics, charts, or insights), how results are formatted, or any behavioral traits like error handling. For a tool that likely returns detailed trade analysis, this leaves the agent with insufficient context to use it effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description adds no parameter semantics beyond what the input schema provides. With 100% schema description coverage, the schema fully documents the three parameters (accountName, symbol, period), including their types, descriptions, enums, and defaults. The description doesn't compensate or add context, such as explaining how these parameters affect the analysis, so it meets the baseline of 3 for high schema coverage without adding value.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose3/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Analyze consecutive winning and losing trades' clearly states the verb ('analyze') and resource ('consecutive winning and losing trades'), but it's somewhat vague about what specific analysis is performed. It distinguishes from siblings like 'analyzePeriodicReturns' or 'calculateWinRate' by focusing on consecutive patterns rather than periodic returns or win rate calculations, but the distinction isn't explicitly articulated in the description itself.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'analyzeTradingPerformance' or 'calculateWinRate'. It doesn't mention prerequisites, such as needing trade data from the specified account, or exclusions, like whether it works for all trade types. Without any usage context, the agent must infer based on tool names alone.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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