Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Indicators to Strategy Blueprint

Here is the transformation of the “JTrader EMA Zone Inversion” indicator into a production-ready algorithmic trading strategy framework.

1. Execution Triggers (Entry & Direction)

The core logic of the provided script is sound, but it only identifies potential setups. To trade it, we must define precise, non-repainting entry conditions.

2. Multi-Tiered Exit Logic

A professional strategy moves beyond a single, static take-profit. It employs a layered defense to protect capital and lock in gains.

3. Capital Allocation & Risk Management

Position sizing is arguably more important than the entry signal itself. We will implement a fixed-fractional risk model.

4. Implementation Snippet (Pine Logic)

This snippet demonstrates the conversion from an indicator to a strategy, incorporating the professional-grade execution and risk management components.

//@version=5
// --- STRATEGY DECLARATION ---
strategy("JTrader EMA Zone Inversion - Strategy", 
     overlay=true, 
     pyramiding=0, 
     initial_capital=25000, 
     process_orders_on_close=true, // Execute on next bar's open
     default_qty_type=strategy.fixed, // We will calculate quantity manually
     commission_type=strategy.commission.percent,
     commission_value=0.07, // Realistic commission
     slippage=2) // Realistic slippage in ticks

// --- RISK MANAGEMENT INPUTS ---
riskPercent     = input.float(1.0, "Risk per Trade (%)", group="Risk Management", minval=0.1, maxval=10)
useTrailingStop = input.bool(true, "Use Trailing Stop after TP1?", group="Risk Management")
atrLen          = input.int(14, "ATR Length (for SL/Trail)", group="Risk Management")
trailAtrMult    = input.float(2.5, "Trailing Stop ATR Multiplier", group="Risk Management")
stagnationBars  = input.int(40, "Max Bars in Trade (Stagnation Exit)", group="Risk Management")

// ... [Paste the entire original indicator code for EMAs, FVG detection, and signal logic here] ...
// The variables `ifvgValidL`, `ifvgValidS`, `pivLowPx`, and `pivHighPx` must be available from the original code.

// --- STRATEGY EXECUTION LOGIC ---

// Calculate ATR for dynamic stops
atrValue = ta.atr(atrLen)

// --- LONG TRADE MANAGEMENT ---
if (ifvgValidL and strategy.opentrades == 0)
    // 1. Define Trade Levels
    longEntryPrice = open // Entry on the open of the next bar
    longStopPrice = pivLowPx - (atrValue * 0.25)
    riskRewardRatio = 1.5
    longTakeProfit1 = longEntryPrice + (abs(longEntryPrice - longStopPrice) * riskRewardRatio)

    // 2. Calculate Position Size
    riskAmount = (strategy.equity * riskPercent) / 100
    priceRisk = abs(longEntryPrice - longStopPrice)
    positionSize = riskAmount / priceRisk

    // 3. Execute Orders
    if (positionSize > 0)
        strategy.entry("Long", strategy.long, qty=positionSize)
        // Set initial SL and TP1 for 50% of the position
        strategy.exit("Long TP1/SL", from_entry="Long", qty_percent=50, profit=longTakeProfit1, loss=longStopPrice)
        // Set the same initial SL for the remaining 50% (TP is managed by trailing logic)
        strategy.exit("Long SL2", from_entry="Long", loss=longStopPrice)

// --- SHORT TRADE MANAGEMENT ---
if (ifvgValidS and strategy.opentrades == 0)
    // 1. Define Trade Levels
    shortEntryPrice = open
    shortStopPrice = pivHighPx + (atrValue * 0.25)
    riskRewardRatio = 1.5
    shortTakeProfit1 = shortEntryPrice - (abs(shortEntryPrice - shortStopPrice) * riskRewardRatio)

    // 2. Calculate Position Size
    riskAmount = (strategy.equity * riskPercent) / 100
    priceRisk = abs(shortEntryPrice - shortStopPrice)
    positionSize = riskAmount / priceRisk

    // 3. Execute Orders
    if (positionSize > 0)
        strategy.entry("Short", strategy.short, qty=positionSize)
        strategy.exit("Short TP1/SL", from_entry="Short", qty_percent=50, profit=shortTakeProfit1, loss=shortStopPrice)
        strategy.exit("Short SL2", from_entry="Short", loss=shortStopPrice)

// --- DYNAMIC TRAILING STOP & TIME EXIT LOGIC ---
var float trailStopPrice = na

// Check if we are in a position
if (strategy.position_size > 0) // In a long position
    // Activate trailing stop after TP1 is hit (i.e., when position size is reduced)
    isTrailingActive = useTrailingStop and strategy.opentrades.size(0) < strategy.opentrades.size(1)
    
    if (isTrailingActive)
        // Calculate new trail stop
        newTrailStop = high - (atrValue * trailAtrMult)
        // If it's the first bar of trailing, set the stop to breakeven. Otherwise, only move it up.
        trailStopPrice := na(trailStopPrice) ? strategy.opentrades.entry_price(0) : math.max(trailStopPrice, newTrailStop)
        strategy.exit("Long Trail", from_entry="Long", stop=trailStopPrice)
    
    // Stagnation Exit
    if (bar_index - strategy.opentrades.entry_bar_index(0) > stagnationBars)
        strategy.close("Long", comment="Stagnation Exit")

else if (strategy.position_size < 0) // In a short position
    isTrailingActive = useTrailingStop and strategy.opentrades.size(0) < strategy.opentrades.size(1)

    if (isTrailingActive)
        newTrailStop = low + (atrValue * trailAtrMult)
        trailStopPrice := na(trailStopPrice) ? strategy.opentrades.entry_price(0) : math.min(trailStopPrice, newTrailStop)
        strategy.exit("Short Trail", from_entry="Short", stop=trailStopPrice)

    // Stagnation Exit
    if (bar_index - strategy.opentrades.entry_bar_index(0) > stagnationBars)
        strategy.close("Short", comment="Stagnation Exit")

// Reset trail stop price when flat
if (strategy.position_size == 0)
    trailStopPrice := na

// EOD Exit (Example for US Stock Market)
isEod = year(time_close) == year(timenow) and month(time_close) == month(timenow) and dayofmonth(time_close) == dayofmonth(timenow)
if (isEod)
    strategy.close_all(comment="EOD Exit")