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

Based on the provided “PSAR Trend Filter” indicator, here is the architectural blueprint for its transformation into a production-grade automated execution framework.

1. Execution Triggers (Entry & Direction)

The core of the execution logic will be the filtered signals generated by the original script. We will translate these signals into concrete order commands.

2. Multi-Tiered Exit Logic

A simple reversal is insufficient for robust risk management. We will implement a multi-layered exit system that operates independently of new entry signals.

3. Capital Allocation & Risk Management

Position sizing is arguably more important than the entry signal itself. We will move from a fixed-lot system to a dynamic, risk-based model.

4. Implementation Snippet (Pine Logic)

This snippet demonstrates the transformation into a strategy with the described professional-grade components.

//@version=5
// 1. STRATEGY DECLARATION: Professional parameters for slippage, commission, and execution.
strategy("PSAR Execution Framework", 
     overlay=true, 
     pyramiding=0, // Set to >0 to allow pyramiding
     initial_capital=100000, 
     commission_type=strategy.commission.percent, 
     commission_value=0.04, // Realistic commission
     slippage=2, // Realistic slippage in ticks
     process_orders_on_close=true, // Ensures execution on next bar's open for reliability
     calc_on_every_tick=false) // Prevents repainting

// --- Original Inputs (start, increment, maximum, filters, etc.) ---
// [User would paste the original input section here]
start     = input.float(0.02, "PSAR Start")
increment = input.float(0.02, "PSAR Increment")
maximum   = input.float(0.20, "PSAR Maximum")
useEMAFilter = input.bool(true, "Use EMA Trend Filter")
emaLength = input.int(50, "EMA Length")
useADXFilter = input.bool(true, "Use ADX Strength Filter")
adxLength = input.int(14, "ADX Length")
adxMin = input.float(15.0, "Minimum ADX")
useCooldown = input.bool(true, "Use Cooldown Between Signals")
cooldownBars = input.int(1, "Cooldown Bars")

// --- 2. NEW RISK & EXIT MANAGEMENT INPUTS ---
const string riskGroup = "Risk & Exit Management"
riskPercent     = input.float(1.0, title="Risk Per Trade %", minval=0.1, maxval=10, step=0.1, group=riskGroup)
useAtrStop      = input.bool(true, title="Use ATR for Stop Loss", group=riskGroup)
atrLength       = input.int(14, title="ATR Length", group=riskGroup)
atrMultiplier   = input.float(2.5, title="ATR Multiplier for Stop", group=riskGroup)
useTimeExit     = input.bool(true, title="Use End of Session Exit", group=riskGroup)
exitTime        = input.session("1545-1600", title="Exit Session", group=riskGroup) // Example for US Equities

// --- Original Calculations (PSAR, EMA, ADX) ---
psar = ta.sar(start, increment, maximum)
emaValue = ta.ema(close, emaLength)
[_, _, adxValue] = ta.dmi(adxLength, adxLength)
atrValue = ta.atr(atrLength)

// --- Original Signal Logic ---
rawBuySignal  = ta.crossover(close, psar)
rawSellSignal = ta.crossunder(close, psar)
trendLongOk  = not useEMAFilter or close > emaValue
trendShortOk = not useEMAFilter or close < emaValue
strengthOk = not useADXFilter or (not na(adxValue) and adxValue >= adxMin)
var int lastSignalBar = 0
cooldownOk = not useCooldown or (bar_index - lastSignalBar > cooldownBars)
buySignal  = rawBuySignal and trendLongOk and strengthOk and cooldownOk
sellSignal = rawSellSignal and trendShortOk and strengthOk and cooldownOk

if buySignal or sellSignal
    lastSignalBar := bar_index

// --- 3. DYNAMIC POSITION SIZING & EXECUTION LOGIC ---
// Calculate Stop Loss Price *before* entry
stopLossDistance = useAtrStop ? atrValue * atrMultiplier : ta.valuewhen(rawBuySignal or rawSellSignal, psar, 0) - close
longStopPrice  = close - stopLossDistance
shortStopPrice = close + stopLossDistance

// Calculate Position Size based on risk
capitalToRisk = (riskPercent / 100) * strategy.equity
positionSize = capitalToRisk / (stopLossDistance * syminfo.pointvalue)
positionSize := positionSize < 1 ? 1 : positionSize // Ensure minimum size is 1 contract/share

// ENTRY LOGIC
if (buySignal and strategy.opentrades == 0)
    // Entry command with calculated size
    strategy.entry("PSAR_Enter", strategy.long, qty=positionSize)
    // Exit command to place the initial Stop Loss
    strategy.exit("SL", from_entry="PSAR_Enter", stop=longStopPrice)

if (sellSignal and strategy.opentrades == 0)
    // Entry command with calculated size
    strategy.entry("PSAR_Enter", strategy.short, qty=positionSize)
    // Exit command to place the initial Stop Loss
    strategy.exit("SL", from_entry="PSAR_Enter", stop=shortStopPrice)

// EXIT LOGIC
// Time-based Exit
isWithinExitTime = time(timeframe.period, exitTime)
if useTimeExit and isWithinExitTime
    strategy.close_all(comment="End of Session Exit")

// Update trailing stop based on PSAR on each bar
if strategy.position_size > 0
    strategy.exit("PSAR_Trail", from_entry="PSAR_Enter", stop=psar)
if strategy.position_size < 0
    strategy.exit("PSAR_Trail", from_entry="PSAR_Enter", stop=psar)

// --- PLOTTING for visualization ---
plot(psar, "PSAR", color=color.gray, style=plot.style_cross)
plot(strategy.opentrades.entry_price(0), "Entry Price", color.blue, style=plot.style_linebr)
plot(strategy.opentrades.stop_price(0), "Stop Price", color.red, style=plot.style_linebr)