Skip to main content

Position-Based Stop Loss and Take Profit Strategy

This strategy demonstrates how to use position tracking to implement stop-loss and take-profit rules. It combines:

  • A simple moving average crossover for entry signals
  • Position tracking for managing exits based on profit/loss thresholds
  • Trade history analysis for avoiding recently stopped-out tokens
registerIndicator("SMA", "BUILTIN", { period: 20, field: "close" }, "SMA_20");
registerIndicator("SMA", "BUILTIN", { period: 50, field: "close" }, "SMA_50");

function onBar(data, indicators, positions, trades) {
const rowCount = data.close.length;
const signals = new Array(rowCount).fill("HOLD");
const cooldownMinutes = 60;

// Precompute time-related values
const now = new Date(data.minute[0]).getTime();
const recentStopouts = new Set();

trades.forEach((trade) => {
if (trade.action === "SELL" && trade.realizedPnl < 0) {
const tradeTime = new Date(trade.timestamp).getTime();
const minutesSince = (now - tradeTime) / (60 * 1000);
if (minutesSince < cooldownMinutes) {
recentStopouts.add(trade.token);
}
}
});

// Create a map for quick position lookups
const positionMap = new Map(positions.map((p) => [p.token, p]));

// Main loop for signal computation
for (let i = 0; i < rowCount; i++) {
const token = data.token_address[i];
const currentPrice = data.close[i];
const position = positionMap.get(token);

if (position) {
// Compute PnL percentage
const pnlPct = (currentPrice - position.avgCost) / position.avgCost;

// Stop-loss and take-profit logic
if (pnlPct <= -0.05 || pnlPct >= 0.15) {
signals[i] = "SELL";
continue;
}
} else {
// SMA-based entry logic for non-positions
if (recentStopouts.has(token)) {
continue;
}

const sma20 = indicators.getValue("SMA_20", token);
const sma50 = indicators.getValue("SMA_50", token);

if (sma20 > sma50) {
signals[i] = "BUY";
}
}
}

return signals;
}

This strategy showcases several advanced concepts:

  1. Position Tracking: Uses the positions array to check if we already have a position in each token and its current profit/loss.

  2. Dynamic Risk Management:

    • Fixed 5% stop loss
    • 15% take profit target
    • Trailing stop that activates after 10% profit
  3. Trade History Analysis:

    • Tracks recently stopped-out tokens using the trades array
    • Implements a cooldown period before re-entering after a loss
    • Helps avoid repeatedly trading tokens in choppy conditions
  4. Hybrid Approach:

    • Technical analysis (SMA crossover) for entries
    • Position-based rules for exits
    • Historical trade analysis for risk management

The strategy demonstrates how to combine multiple data sources:

  • Price action through indicators
  • Current positions for managing risk
  • Trade history for improving entry rules

Performance Optimization Note

The strategy uses Map for position lookups instead of Array.find():

// Optimized approach using Map - O(1) lookup
const positionMap = new Map(positions.map((p) => [p.token, p]));
const position = positionMap.get(token);

// Instead of slower Array.find() - O(n) lookup
// const position = positions.find(p => p.token === token);

This optimization significantly improves performance:

  • Reduces time complexity from O(n * m) to O(n) for the main loop
  • Position lookups become O(1) instead of O(n)
  • Critical for high-frequency strategies with many tokens
  • Especially important when positions array is large

This more sophisticated approach to position management can help protect profits and limit losses while still capturing trending moves in the market.