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:
-
Position Tracking: Uses the
positions
array to check if we already have a position in each token and its current profit/loss. -
Dynamic Risk Management:
- Fixed 5% stop loss
- 15% take profit target
- Trailing stop that activates after 10% profit
-
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
- Tracks recently stopped-out tokens using the
-
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.