Skip to main content
Glama

calculate_trailing_stop

Calculate trailing stop loss levels with breakeven and profit protection for Bybit cryptocurrency positions to manage risk and lock in gains.

Instructions

Calculate trailing stop loss with breakeven and profit protection

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
categoryYesCategory (linear, inverse)
symbolYesSymbol (e.g., ETHUSDT)
entryPriceYesEntry price of the position
currentPriceYesCurrent market price
sideYesPosition side (Buy for long, Sell for short)
initialStopLossYesInitial stop loss price
trailingDistanceYesTrailing distance in price units
breakevenTriggerNoPrice distance from entry to activate breakeven protection (optional)
profitProtectionTriggerNoPrice distance from entry to activate profit protection (optional)

Implementation Reference

  • The core handler function that executes the calculate_trailing_stop tool logic, computing new stop loss levels with trailing, breakeven, and profit protection features.
    async calculateTrailingStop(
      category: string,
      symbol: string,
      entryPrice: number,
      currentPrice: number,
      side: 'Buy' | 'Sell',
      initialStopLoss: number,
      trailingDistance: number,
      breakevenTrigger?: number,
      profitProtectionTrigger?: number
    ): Promise<{
      newStopLoss: number;
      trailingActivated: boolean;
      breakevenProtection: boolean;
      profitProtected: number;
      unrealizedPnL: number;
      unrealizedPnLPercentage: number;
      recommendations: {
        shouldUpdateStop: boolean;
        newStopPrice: number;
        protectionLevel: 'none' | 'breakeven' | 'profit';
        reasoning: string;
      };
      calculations: {
        priceMovement: number;
        priceMovementPercentage: number;
        stopLossMovement: number;
        maxDrawdownProtection: number;
        profitLocked: number;
      };
      warnings: string[];
      error?: string;
    }> {
      try {
        const warnings: string[] = [];
        const isLong = side === 'Buy';
        
        // Validate inputs
        if (trailingDistance <= 0) {
          return {
            newStopLoss: initialStopLoss,
            trailingActivated: false,
            breakevenProtection: false,
            profitProtected: 0,
            unrealizedPnL: 0,
            unrealizedPnLPercentage: 0,
            recommendations: {
              shouldUpdateStop: false,
              newStopPrice: initialStopLoss,
              protectionLevel: 'none',
              reasoning: 'Invalid trailing distance'
            },
            calculations: {
              priceMovement: 0,
              priceMovementPercentage: 0,
              stopLossMovement: 0,
              maxDrawdownProtection: 0,
              profitLocked: 0
            },
            warnings: ['Trailing distance must be greater than 0'],
            error: 'Invalid trailing distance'
          };
        }
    
        // Calculate price movements
        const priceMovement = isLong ? currentPrice - entryPrice : entryPrice - currentPrice;
        const priceMovementPercentage = (priceMovement / entryPrice) * 100;
        
        // Calculate unrealized PnL
        const unrealizedPnL = priceMovement;
        const unrealizedPnLPercentage = priceMovementPercentage;
    
        // Set default triggers if not provided
        const breakevenTriggerDistance = breakevenTrigger || (Math.abs(entryPrice - initialStopLoss) * 1.5);
        const profitProtectionTriggerDistance = profitProtectionTrigger || (Math.abs(entryPrice - initialStopLoss) * 2.0);
    
        let newStopLoss = initialStopLoss;
        let trailingActivated = false;
        let breakevenProtection = false;
        let profitProtected = 0;
        let protectionLevel: 'none' | 'breakeven' | 'profit' = 'none';
        let reasoning = 'No trailing conditions met';
    
        // Check if price has moved favorably enough to activate trailing
        const favorableMovement = isLong ? currentPrice > entryPrice : currentPrice < entryPrice;
        
        if (favorableMovement) {
          // Calculate potential new stop loss based on trailing distance
          const potentialNewStop = isLong 
            ? currentPrice - trailingDistance
            : currentPrice + trailingDistance;
    
          // Only move stop loss if it's better than current stop
          const shouldMoveStop = isLong 
            ? potentialNewStop > initialStopLoss
            : potentialNewStop < initialStopLoss;
    
          if (shouldMoveStop) {
            newStopLoss = potentialNewStop;
            trailingActivated = true;
    
            // Check for breakeven protection
            const breakevenReached = isLong 
              ? currentPrice >= entryPrice + breakevenTriggerDistance
              : currentPrice <= entryPrice - breakevenTriggerDistance;
    
            if (breakevenReached) {
              const breakevenStop = entryPrice; // Move stop to breakeven
              if (isLong ? breakevenStop > newStopLoss : breakevenStop < newStopLoss) {
                newStopLoss = breakevenStop;
                breakevenProtection = true;
                protectionLevel = 'breakeven';
                reasoning = 'Breakeven protection activated - stop moved to entry price';
              }
            }
    
            // Check for profit protection
            const profitProtectionReached = isLong 
              ? currentPrice >= entryPrice + profitProtectionTriggerDistance
              : currentPrice <= entryPrice - profitProtectionTriggerDistance;
    
            if (profitProtectionReached) {
              const profitProtectionStop = isLong 
                ? entryPrice + (profitProtectionTriggerDistance * 0.5) // Lock in 50% of the trigger distance as profit
                : entryPrice - (profitProtectionTriggerDistance * 0.5);
              
              if (isLong ? profitProtectionStop > newStopLoss : profitProtectionStop < newStopLoss) {
                newStopLoss = profitProtectionStop;
                profitProtected = Math.abs(profitProtectionStop - entryPrice);
                protectionLevel = 'profit';
                reasoning = `Profit protection activated - locking in ${profitProtected.toFixed(2)} profit`;
              }
            }
    
            if (protectionLevel === 'none') {
              reasoning = `Trailing stop activated - following price with ${trailingDistance} distance`;
            }
          } else {
            reasoning = 'Price moved favorably but not enough to improve stop loss';
          }
        } else {
          reasoning = 'Price has not moved favorably - maintaining original stop loss';
        }
    
        // Calculate additional metrics
        const stopLossMovement = Math.abs(newStopLoss - initialStopLoss);
        const maxDrawdownProtection = Math.abs(currentPrice - newStopLoss);
        const profitLocked = Math.max(0, isLong ? newStopLoss - entryPrice : entryPrice - newStopLoss);
    
        // Add warnings for risk management
        if (unrealizedPnLPercentage < -5) {
          warnings.push('Position is down more than 5% - consider reviewing strategy');
        }
    
        if (trailingDistance > Math.abs(entryPrice - initialStopLoss) * 2) {
          warnings.push('Trailing distance is very large compared to initial risk - may give back too much profit');
        }
    
        if (Math.abs(unrealizedPnLPercentage) > 20) {
          warnings.push('Large unrealized PnL - consider taking partial profits');
        }
    
        // Get instrument info for price formatting
        const instrumentInfo = await this.getInstrumentsInfo(category, symbol);
        if ('result' in instrumentInfo) {
          const priceFilter = instrumentInfo.result.list[0].priceFilter;
          const tickSize = parseFloat(priceFilter.tickSize);
          const decimalPlaces = tickSize.toString().split('.')[1]?.length || 0;
          
          // Round new stop loss to valid tick size
          newStopLoss = Math.round(newStopLoss / tickSize) * tickSize;
          newStopLoss = parseFloat(newStopLoss.toFixed(decimalPlaces));
        }
    
        return {
          newStopLoss,
          trailingActivated,
          breakevenProtection,
          profitProtected,
          unrealizedPnL,
          unrealizedPnLPercentage,
          recommendations: {
            shouldUpdateStop: newStopLoss !== initialStopLoss,
            newStopPrice: newStopLoss,
            protectionLevel,
            reasoning
          },
          calculations: {
            priceMovement,
            priceMovementPercentage,
            stopLossMovement,
            maxDrawdownProtection,
            profitLocked
          },
          warnings
        };
    
      } catch (error: any) {
        return {
          newStopLoss: initialStopLoss,
          trailingActivated: false,
          breakevenProtection: false,
          profitProtected: 0,
          unrealizedPnL: 0,
          unrealizedPnLPercentage: 0,
          recommendations: {
            shouldUpdateStop: false,
            newStopPrice: initialStopLoss,
            protectionLevel: 'none',
            reasoning: 'Error calculating trailing stop'
          },
          calculations: {
            priceMovement: 0,
            priceMovementPercentage: 0,
            stopLossMovement: 0,
            maxDrawdownProtection: 0,
            profitLocked: 0
          },
          warnings: [],
          error: error.message
        };
      }
    }
  • Input schema definition for the calculate_trailing_stop tool, specifying parameters and validation.
    {
      name: 'calculate_trailing_stop',
      description: 'Calculate trailing stop loss with breakeven and profit protection',
      inputSchema: {
        type: 'object',
        properties: {
          category: {
            type: 'string',
            description: 'Category (linear, inverse)',
          },
          symbol: {
            type: 'string',
            description: 'Symbol (e.g., ETHUSDT)',
          },
          entryPrice: {
            type: 'number',
            description: 'Entry price of the position',
          },
          currentPrice: {
            type: 'number',
            description: 'Current market price',
          },
          side: {
            type: 'string',
            description: 'Position side (Buy for long, Sell for short)',
          },
          initialStopLoss: {
            type: 'number',
            description: 'Initial stop loss price',
          },
          trailingDistance: {
            type: 'number',
            description: 'Trailing distance in price units',
          },
          breakevenTrigger: {
            type: 'number',
            description: 'Price distance from entry to activate breakeven protection (optional)',
          },
          profitProtectionTrigger: {
            type: 'number',
            description: 'Price distance from entry to activate profit protection (optional)',
          },
        },
        required: ['category', 'symbol', 'entryPrice', 'currentPrice', 'side', 'initialStopLoss', 'trailingDistance'],
      },
    },
  • src/index.ts:1024-1044 (registration)
    Tool handler registration in the MCP CallToolRequest switch statement, dispatching arguments to BybitService.calculateTrailingStop and returning the result.
    case 'calculate_trailing_stop': {
      const result = await this.bybitService.calculateTrailingStop(
        typedArgs.category,
        typedArgs.symbol,
        typedArgs.entryPrice,
        typedArgs.currentPrice,
        typedArgs.side,
        typedArgs.initialStopLoss,
        typedArgs.trailingDistance,
        typedArgs.breakevenTrigger,
        typedArgs.profitProtectionTrigger
      );
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It states the tool calculates values but doesn't clarify if this is a read-only computation (likely) or if it modifies any state (e.g., placing orders). It lacks details on permissions, rate limits, error handling, or output format, which are critical for a tool with 9 parameters in a trading context.

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 that front-loads the core purpose without unnecessary words. It directly states what the tool does ('Calculate trailing stop loss') and adds key features ('with breakeven and profit protection') concisely, making every word count.

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 complexity (9 parameters in trading), lack of annotations, and no output schema, the description is insufficient. It doesn't explain the calculation logic, return values, or how parameters like 'side' affect the result. For a tool that likely outputs critical trading data, more context is needed to guide the agent 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?

Schema description coverage is 100%, so all parameters are documented in the schema. The description adds minimal value beyond the schema by hinting at 'breakeven and profit protection,' which relates to optional parameters like 'breakevenTrigger' and 'profitProtectionTrigger.' However, it doesn't explain how these parameters interact or provide examples, so it meets the baseline of 3.

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

Purpose4/5

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

The description clearly states the tool's purpose: 'Calculate trailing stop loss with breakeven and profit protection.' It specifies the verb ('calculate') and the resource ('trailing stop loss'), and mentions additional features. However, it doesn't explicitly differentiate from sibling tools like 'place_order_with_trailing_stop' or 'set_trading_stop', which prevents a score of 5.

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. It doesn't mention sibling tools like 'set_trading_stop' (which might set stops) or 'place_order_with_trailing_stop' (which might combine order placement with stop calculation), leaving the agent with no context for tool selection. Usage is implied only by the tool name and description.

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/kondisettyravi/mcp-bybit-node'

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