SmartDCA is a single-direction (Long or Short) step-based DCA strategy with adaptive take-profit and structured risk management.
All core parameters are fully user-configurable. The strategy logic does not enforce a fixed trading style; behavior depends entirely on user-selected settings.
General Structure
The strategy operates in one direction at a time (Long or Short).
All position management logic is applied relative to the selected direction.
The following components are fully controlled by the user:
Trade direction (Long / Short)
Entry model
Entry filters
Take-profit percentage
DCA distance percentage
TP/DCA increment scaling mode
Order size model
Maximum DCA steps
Risk management options
Exit model selection
The strategy plan is visualized either across the entire chart history or within a user-defined custom backtest date range.
Entry Configuration
Users can select the entry model:
Structural breakout
RSI reversal
Trend flip
None (manual disable)
Optional entry filters refine signal selection based on:
Reward space
Entry quality
RSI extremity
Fair value (VWMA)
Trend alignment
DCA & Position Scaling
After initial entry:
Additional DCA orders are triggered when price deviates from the average position price by the defined DCA percentage.
DCA distance and take-profit levels expand step-by-step according to the selected scaling mode.
Order size progression depends on the selected order size model.
Maximum DCA steps define the upper exposure limit.
This ensures the full theoretical risk is bounded and visible.
Exit Models
Users can choose the exit behavior:
Fixed take-profit
Structure-based exit
Adaptive Fast
Adaptive Slow
Protect Profit
Trend Ended
None
All exits are executed using market orders.
Additional Risk Controls
Optional risk features include:
DCA Compression (partial size reduction after recovery)
Defensive Profit Exit
Trend Soft Stop (breakeven protection after trend reversal)
These mechanisms are configurable and do not override the predefined maximum DCA limit.
Visualization & Reporting
The strategy provides:
Active position tracking
DCA step monitoring
Position notional transparency
Trend strength visualization
Historical DCA performance summary
Users may restrict execution to a custom backtest range or allow the strategy to operate across the full visible chart history.
Risk Disclosure
The maximum DCA step defines the full predefined risk envelope.
Strong and sustained one-directional trends may lead to full DCA utilization.
The strategy does not attempt unlimited recovery.
Users are responsible for configuring position sizing and DCA parameters relative to their capital.
This script is for educational and analytical purposes only and does not constitute investment advice.
![]()
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © trade_akademi
//@version=6
strategy("SmartDCA", overlay=true, fill_orders_on_standard_ohlc = true, initial_capital= 100000, default_qty_type=strategy.percent_of_equity, default_qty_value=0.2, commission_type=strategy.commission.percent, commission_value=0.02, pyramiding = 1000, slippage = 5, backtest_fill_limits_assumption = 5, margin_long = 0, margin_short = 0)
// User Settings
sideInput = input.string("Long", title="Trade Direction", options=["Long", "Short"], group="Entry", tooltip="Defines the trading direction of the strategy.")
mainStrategyInput = input.string("Break Default",title="Primary Entry Model",options=["Break Default","RSI Reversal","Trend Flip","None"],group="Entry",tooltip="Defines the core entry logic of the strategy.\n\n" +"Break Default: Enters on structural high/low breaks.\n" +"RSI Reversal: Enters on RSI extreme reversals (oversold/overbought).\n" +"Trend Flip: Enters when the detected main trend changes direction.\n" +"None: Disables new entries.\n\n" +"Entry Filters can be applied separately to refine signal quality.")
mainPeriodInput = input.int(100, title="Main Entry Period", minval=2, maxval=1000, group="Entry", tooltip="Lookback period used to determine local highs/lows for Break_* entry models. Lower values generate earlier and more aggressive entries. Not used in MA_Based mode.")
entryFilterInput = input.string("None",title="Primary Entry Filter",options=["None", "Reward Space", "Entry Quality", "RSI Extremity", "Fair Value", "Trend Alignment"],group="Entry",tooltip="Applies an additional high-level filter to all entry models. None allows unrestricted entries, Trend Only restricts entries to the main trend direction, Fair Price Zone blocks entries far from structurally active price areas, and Momentum Confirmed requires supportive momentum conditions before allowing entry.")
exitModelInput = input.string("Fixed",title = "Exit Model",options = ["Fixed", "Structure Break", "Adaptive Fast", "Adaptive Slow", "Protect Profit", "Trend Ended", "None"],group = "Exit / DCA / TP",tooltip ="Defines how positions are closed. All exits are executed using market orders for faster execution and simplified management.\n\n" +"Fixed: Closes at predefined take-profit level using market execution. No limit orders are placed.\n\n" +"Structure Break: Closes when price breaks the active structural boundary.\n\n" +"Adaptive Fast: Exits on short-term exhaustion signals for quicker profit capture.\n\n" +"Adaptive Slow: Uses a wider structural reference for extended trend participation.\n\n" +"Protect Profit: Locks in profits based on peak PNL and closes on meaningful giveback.\n\n" +"Trend Ended: Closes when the active trend direction changes. Recommended only when using Trend Flip entry mode.\n\n" +"None: Disables automated exits.")
takeProfitPercentInput = input.float(12, title="Take Profit (%)", step=0.01, minval=0, group="Exit / DCA / TP", tooltip="Defines the base take-profit percentage. When trend-following exit is enabled, this value represents the activation threshold.")
dcaPercentInput = input.float(4, title="DCA Distance (%)", step=0.01, minval=0, group="Exit / DCA / TP", tooltip="Percentage deviation from average price required to trigger an additional DCA order.")
tpDcaIncrementModeInput = input.string("Percent", title="TP/DCA Increment Scaling Mode", options=["Percent", "Fixed"], group="Exit / DCA / TP", tooltip="Percent: TP and DCA thresholds scale multiplicatively (aggressive).\nFixed: Linear increase for deeper and more resilient DCA structures.")
tpIncrementValueInput = input.float(5, title="TP Increment Value", step=0.1, group="Exit / DCA / TP", tooltip="Determines how the take-profit level increases after each DCA step, depending on the selected scaling mode.")
dcaIncrementValueInput = input.float(5, title="DCA Increment Value", step=0.1, group="Exit / DCA / TP", tooltip="Controls how the DCA distance expands after each additional DCA step.")
maxDcaStepsInput = input.int(15,title="Maximum DCA Steps",step=1,minval=1,group="Risk",tooltip="Maximum number of position layers including the initial entry. Higher values increase recovery flexibility but also increase capital exposure and structural risk.")
orderSizeModeInput = input.string("Progressive",title="Order Size Model",options=["Default", "Balanced", "Progressive", "Aggressive"],group="Risk",tooltip="Defines how DCA order sizes grow.\n\nDefault: Equal-sized orders.\nBalanced: Gradual laddered growth.\nProgressive: Linear step-based scaling.\nAggressive: Front-loaded aggressive scaling.")
defensiveProfitExitInput = input.bool(false,title="Defensive Profit Exit",group="Risk",tooltip="Enables early profit protection logic that closes the position before full target extension when short-term reversal risk increases. Designed to reduce giveback during unstable conditions.")
dcaCompressionModeInput = input.bool(false,title="DCA Compression Mode",group="Risk",tooltip="Reduces position size after recovery to release capital and lower structural exposure.\n\n" +"Off: No reduction is applied.\n" +"On: Automatically trims position size once recovery conditions are met.")
trendStopInput = input.bool(false,title="Trend Soft Stop",group="Risk",tooltip="Activates breakeven stop if trend flips against the current position. No immediate exit. Locks downside risk while allowing potential recovery.")
useCustomRangeInput = input.bool(true, title="Use Custom Backtest Range", group="Backtest", tooltip="Limits strategy execution to the specified date range for backtesting purposes.")
startDateInput = input.time(timestamp("2025-01-01 00:00"), title="Backtest Start Date", group="Backtest",confirm=true)
endDateInput = input.time(timestamp("2030-01-01 00:00"), title="Backtest End Date", group="Backtest",confirm=true)
activePositionTableInput = input.bool(true, title="Show Active Position Table", group="Table", tooltip="Displays detailed information about the currently active position.")
tableLeftInput = input.string("Off",title = "Left Panel Table",options = ["Off","Strategy Summary","DCA History","DCA & Order Summary"],group = "Table",tooltip = "Select which table to display on the left panel.\n\n" +"Off: No table displayed.\n" +"Strategy Summary: Shows current strategy configuration and key parameters.\n" +"DCA History: Displays historical DCA step statistics and close performance.\n" +"DCA & Order Summary: Shows order size progression and position scaling structure.")
showHighLowLevelsInput = input.bool(true, title="Show Structure High/Low Levels", group="Visual", tooltip="Visualizes the high/low structure used by Break_* entry models.")
showTrendInput = input.bool(false,title = "Show Trend Structure",group = "Visual",tooltip = "Highlights the detected main market trend on the chart using background coloring. The trend is based on confirmed structural breakouts to avoid false flips during pullbacks.")
showRsiInput = input.bool(false, title="Show RSI Overbought/Oversold Zones", group="Visual", tooltip="Highlights RSI <30 and RSI >70 zones using background shading.")
show4hRsiInput = input.bool(false, title="Show 4H RSI Zones", group="Visual", tooltip="Highlights overbought and oversold zones based on 4-hour RSI.")
showRsiDivergencesInput = input.bool(false, title="Show RSI Divergence Zones", group="Visual", tooltip="Displays visual markers for RSI divergence areas.")
inDateRange = not useCustomRangeInput or (time >= startDateInput and time <= endDateInput)
// Runtime Settings
takeProfitPercentEffective = takeProfitPercentInput
tpIncrementValueEffective = tpIncrementValueInput
dcaPercentEffective = dcaPercentInput
dcaIncrementValueEffective = dcaIncrementValueInput
exitModelEffective = exitModelInput
dcaCompressionEffective = dcaCompressionModeInput
defensiveProfitExitEffective = defensiveProfitExitInput
defensiveProfitExitLimitEffective = 4
defensiveProfitExitProximityEffective = 40
// Static Vars
var mainPeriod = mainPeriodInput
var baseOrderSize = float(na)
var adaptiveTakeProfitPrice = float(na)
var adaptiveDcaPrice = float(na)
var currentDcaStep = int(na)
var lastDcaPrice = float(na)
var lastDcaBar = int(na)
var lastExitBar = int(na)
var lastExitTime = int(na)
var entryBlocked = bool(false)
var dcaBlocked = bool(false)
var orderSize = float(na)
var orderQty = float(na)
var estimatedPnl = float(na)
var currentPnl = float(na)
var maxPnlSeen = float(na)
var minPnlSeen = float(na)
var maxClosePnlSeen = float(na)
var minClosePnlSeen = float(na)
var maxClosePnlSeenAt = float(na)
var minClosePnlSeenAt = float(na)
var protectRatio = float(na)
var protectFloorPnl = float(na)
var protectFloorClosePnl = float(na)
var compressionLock = bool(false)
var compressCount = int(0)
var waitBeforeReCompress = int(0)
var trendStopModeEnabled = bool(false)
var breakevenStopEnabled = bool(false)
var inefficiencyExitEnabled = bool(false)
var exitMode = bool(false)
var protectProfitMode = bool(false)
var totalPositionCount = 0
var totalDcaStepCount = 0
var allTimeMaxDca = int(na)
var allTimeMaxPnl = float(na)
var allTimeMinPnl = float(na)
var allTimeMaxNotional = float(na)
var crossTp = int(na)
var int[] dcaCloseCounts = array.new_int(maxDcaStepsInput + 1, 0)
var float[] dcaClosePnl = array.new_float(maxDcaStepsInput + 1, 0.0)
// Strategy
lowest = ta.lowest(low,mainPeriod)
highest = ta.highest(high,mainPeriod)
lowest20 = ta.lowest(close,20)
highest20 = ta.highest(close,20)
lowest50 = ta.lowest(close,50)
highest50 = ta.highest(close,50)
rsi=ta.rsi(close,14)
rsi_4h=request.security(syminfo.tickerid, "240", ta.rsi(close, 14),gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
rsi_divergence_short=close>ta.highest(high,100)[1] and rsi<ta.highest(rsi,100)[1]
rsi_divergence_long=close<ta.lowest(low,100)[1] and rsi>ta.lowest(rsi,100)[1]
no_recent_short = not (rsi_divergence_short[1] or rsi_divergence_short[2] or rsi_divergence_short[3] or rsi_divergence_short[4] or rsi_divergence_short[5])
no_recent_long = not (rsi_divergence_long[1] or rsi_divergence_long[2] or rsi_divergence_long[3] or rsi_divergence_long[4] or rsi_divergence_long[5])
fairValue = ta.vwma(close, 200)
// Trend tespiti
var float baseHigh = na
var float baseLow = na
var int watchDir = 0 // 1 = up watch, -1 = down watch
var int trend = 0 // 1 = up, -1 = down, 0 = neutral
if watchDir == 0
// yukarı aday
if highest > highest[2]
watchDir := 1
baseHigh := highest
// aşağı aday
else if lowest < lowest[2]
watchDir := -1
baseLow := lowest
if watchDir == 1
// ikinci, daha güçlü kırılım
if highest > baseHigh
trend := 1
watchDir := 0
baseHigh := na
// izleme iptali (yapı bozuldu)
else if lowest < lowest[2]
watchDir := 0
baseHigh := na
if watchDir == -1
// ikinci kırılım
if lowest < baseLow
trend := -1
watchDir := 0
baseLow := na
// izleme iptali
else if highest > highest[2]
watchDir := 0
baseLow := na
// final flag
trendOk = (sideInput == "Long" and trend == 1) or (sideInput == "Short" and trend == -1)
isLong = sideInput == "Long"
isShort = sideInput == "Short"
// === TREND STRENGTH ===
var int trendBreakCount = 0
var float lastBreakLevel = na
// Trend yeni başladığında reset
if trend != trend[1]
trendBreakCount := 0
lastBreakLevel := na
// UP trend continuation
if trend == 1
if na(lastBreakLevel)
lastBreakLevel := highest
if highest > lastBreakLevel
trendBreakCount += 1
lastBreakLevel := highest
// DOWN trend continuation
if trend == -1
if na(lastBreakLevel)
lastBreakLevel := lowest
if lowest < lastBreakLevel
trendBreakCount += 1
lastBreakLevel := lowest
// Trend bgcolor
maxStrength = 10
strengthRatio = math.min(trendBreakCount / maxStrength, 1.0)
clampedRatio = math.min(strengthRatio, 1.0)
// yumuşatma (1.0 = linear, 1.5–2.0 güzel akıyor)
smoothRatio = math.pow(clampedRatio, 1.6)
// opacity hesap
opacityRaw = 100 - int(smoothRatio * 50)
// minimum 50'ye sabitle
trendOopacity = math.max(60, math.min(90, opacityRaw))
// Cooldown
twoDaysMs = 2 * 24 * 60 * 60 * 1000
cooldownOk = na(lastExitTime) or (time - lastExitTime > twoDaysMs)
// Functions
// Calculate open profit or loss for the open positions.
tradeOpenPnl() =>
sumProfit = 0.0
for tradeNo = 0 to strategy.opentrades - 1
sumProfit += strategy.opentrades.profit(tradeNo)
result = sumProfit
f_openRealizedPnl() =>
var float openRealized = 0.0
var float lastNetProfit = na
// ilk çağrı
if na(lastNetProfit)
lastNetProfit := strategy.netprofit
// pozisyon kapandıysa reset
if strategy.position_size == 0
openRealized := 0.0
lastNetProfit := strategy.netprofit
// pozisyon açıkken netprofit düşüşünü yakala (realized)
if strategy.position_size != 0
delta = strategy.netprofit - lastNetProfit
// sadece realized kayıplar/kârlar (reduce, partial close)
if delta != 0
openRealized += delta
lastNetProfit := strategy.netprofit
openRealized
f_getOrderSize(baseSize, step, mode) =>
size = baseSize
tmpStep = na(step) ? 0 : step
pair = math.ceil((tmpStep + 1) / 2)
if mode == "Default" // Flat
size := baseSize
else if mode == "Balanced" // Ladder
m = 1.0 + (pair - 1) * 0.5
size := baseSize * m
else if mode == "Progressive" // eski Smart (istersen kaldırabilirsin)
size := baseSize * pair
else if mode == "Aggressive" // Hammer
m = 1.0 + (pair - 1) * 1.5
size := baseSize * m
else
size := baseSize
size
f_getMaxPositionSize(baseSize,maxSteps,mode) =>
total = 0.0
for step = 0 to maxSteps - 1
stepSize = f_getOrderSize(baseSize,step,mode)
total += stepSize
total
f_calcTpDca(baseTp, baseDca, step, mode, tpInc, dcaInc) =>
tp = baseTp
dca = baseDca
if step <= 1
[tp, dca]
else
for i = 2 to step
if mode == "Percent"
tp := tp * (1 + tpInc / 100)
dca := dca * (1 + dcaInc / 100)
else
tp := tp + tpInc
dca := dca + dcaInc
[tp, dca]
f_norm(val, minVal, maxVal)=>
math.min(math.max((val - minVal) / (maxVal - minVal), 0), 1)
trendColor(ma,opacity=50) =>
if (ma>ma[1])
color.new(color.green, opacity)
else
color.new(color.red, opacity)
// Position
positionSize = strategy.position_size
hasPosition = positionSize != 0
avgPrice = strategy.position_avg_price
openTrades = strategy.opentrades
changeClosedTrades = ta.change(strategy.closedtrades)
positionSide = positionSize>0 ? "Long" : "Short"
baseOrderSize := na(baseOrderSize) ? math.abs(strategy.opentrades.size(0) * strategy.opentrades.entry_price(0)) : baseOrderSize
positionOpenPrice = strategy.opentrades.entry_price(0)
lastEntryPrice = strategy.opentrades.entry_price(strategy.opentrades - 1)
lastOrderSize = strategy.opentrades.size(openTrades-1) * strategy.opentrades.entry_price(openTrades-1)
totalNotional = math.abs(positionSize*avgPrice)
dcaFillRatio = currentDcaStep / maxDcaStepsInput
avgDcaStep = math.round(totalDcaStepCount / totalPositionCount,2)
avgPnl = math.round(strategy.netprofit / totalPositionCount,2)
currentPnl := tradeOpenPnl()
firstOrderPnl = strategy.opentrades.profit(0)
lastOrderPnl = strategy.opentrades.profit(strategy.opentrades - 1)
openRealizedPnl = f_openRealizedPnl()
effectivePnl = openRealizedPnl + currentPnl
orderSize := f_getOrderSize(baseOrderSize,currentDcaStep,orderSizeModeInput)
orderQty := math.abs(orderSize / close)
maxPositionSize = f_getMaxPositionSize(baseOrderSize,maxDcaStepsInput,orderSizeModeInput)
// TP DCA calculations
[adaptiveTakeProfitPercent, adaptiveDcaPercent] = f_calcTpDca(takeProfitPercentEffective,dcaPercentEffective,currentDcaStep,tpDcaIncrementModeInput,tpIncrementValueEffective,dcaIncrementValueEffective)
if na(adaptiveTakeProfitPrice)
adaptiveTakeProfitPrice := positionSide == "Long" ? avgPrice * (1 + (adaptiveTakeProfitPercent / 100)) : avgPrice * (1 - (adaptiveTakeProfitPercent / 100))
tpExtension = (close - adaptiveTakeProfitPrice) / (adaptiveTakeProfitPrice - avgPrice)
tmpAdaptiveDcaPriceLast = positionSide == "Long" ? lastDcaPrice * (1 - adaptiveDcaPercent / 100) : lastDcaPrice * (1 + adaptiveDcaPercent / 100)
tmpAdaptiveDcaPriceAvg = positionSide == "Long" ? avgPrice * (1 - adaptiveDcaPercent / 100) : avgPrice * (1 + adaptiveDcaPercent / 100)
if na(adaptiveDcaPrice)
adaptiveDcaPrice := positionSide == "Long" ? math.min(tmpAdaptiveDcaPriceLast,tmpAdaptiveDcaPriceAvg) : math.max(tmpAdaptiveDcaPriceLast,tmpAdaptiveDcaPriceAvg)
// position calculations
if hasPosition
estimatedPnl:=math.abs(strategy.position_size*strategy.position_avg_price * (1 + adaptiveTakeProfitPercent / 100) - strategy.position_size*strategy.position_avg_price)
maxPnl = sideInput=="Long" ? (high - avgPrice) * math.abs(strategy.position_size) : (avgPrice - low) * math.abs(strategy.position_size)
minPnl = sideInput=="Long" ? (low - avgPrice) * math.abs(strategy.position_size) : (avgPrice - high) * math.abs(strategy.position_size)
maxPnlSeen := na(maxPnlSeen) ? maxPnl : math.max(maxPnlSeen, maxPnl)
minPnlSeen := na(minPnlSeen) ? minPnl : math.min(minPnlSeen, minPnl)
maxClosePnlSeen := na(maxClosePnlSeen) ? currentPnl : math.max(maxClosePnlSeen, currentPnl)
minClosePnlSeen := na(minClosePnlSeen) ? currentPnl : math.min(minClosePnlSeen, currentPnl)
maxClosePnlSeenAt := na(maxClosePnlSeenAt) ? close : math.max(maxClosePnlSeenAt, close)
minClosePnlSeenAt := na(minClosePnlSeenAt) ? close : math.min(minClosePnlSeenAt, close)
if minPnlSeen<allTimeMinPnl or na(allTimeMinPnl)
allTimeMinPnl:=minPnlSeen
if maxPnlSeen<allTimeMaxPnl or na(allTimeMaxPnl)
allTimeMaxPnl:=maxPnlSeen
if currentDcaStep>allTimeMaxDca or na(allTimeMaxDca)
allTimeMaxDca:=currentDcaStep
if totalNotional>allTimeMaxNotional or na(allTimeMaxNotional)
allTimeMaxNotional:=totalNotional
// Cross TP
if close<adaptiveTakeProfitPrice and (high>adaptiveTakeProfitPrice or high[1]>adaptiveTakeProfitPrice)
if na(crossTp)
crossTp := 1
else
crossTp += 1
// Profit Protect
profitStrength = maxClosePnlSeen / math.max(estimatedPnl, 1)
dynamicProtectRatio = profitStrength < 10 ? 0.40 : profitStrength < 20 ? 0.50 : profitStrength < 50 ? 0.60 : profitStrength < 60 ? 0.70 : 0.80
baseProtectRatio = 0.40
pnlStallRatio = currentPnl / math.max(maxClosePnlSeen, 1)
stallBoost = pnlStallRatio < 0.85 ? 0.10 : 0.0
// --- zaman bazlı drift
var int barsSinceMaxPnl = 0
if currentPnl >= maxClosePnlSeen
barsSinceMaxPnl := 0
else
barsSinceMaxPnl += 1
timeStepBars = 20
timeBoostStep = 0.02
maxTimeBoost = 0.15
timeBoost = math.min(math.floor(barsSinceMaxPnl / timeStepBars) * timeBoostStep,maxTimeBoost)
// --- nihai koruma oranı
protectRatio := math.min(math.max(baseProtectRatio,dynamicProtectRatio + stallBoost + timeBoost),0.85)
protectFloorClosePnl := maxClosePnlSeen * protectRatio
// Auto Exit Model
if exitModelInput == "Auto"
exitModelEffective := "Fixed"
if (hasPosition)
strengthOk = trendOk and (strengthRatio == 1 or strengthRatio>strengthRatio[3])
newTrend = isLong ? trendOk and trend[3] == -1 : trendOk and trend[3] == 1
if strengthOk or newTrend
exitModelEffective := "Protect Profit"
else if trendOk
exitModelEffective := "Adaptive Slow"
else if not trendOk
exitModelEffective := "Fixed"
if (currentPnl > estimatedPnl * 50)
exitModelEffective := "Adaptive Fast"
else if (currentPnl > estimatedPnl * 20)
exitModelEffective := "Adaptive Slow"
// Stop
if inDateRange and hasPosition
if trendStopInput and ((isLong and trend == -1) or (isShort and trend == 1)) and strengthRatio == 1 and currentDcaStep>1 and currentPnl < 0
breakevenStopEnabled := true
if breakevenStopEnabled and effectivePnl > 0
if sideInput == "Long"
strategy.close("Long", comment="Breakeven Stop")
else if sideInput=="Short"
strategy.close("Short", comment="Breakeven Stop")
entryBlocked := true
lastExitBar := bar_index
lastExitTime := time
if inefficiencyExitEnabled and math.abs(openRealizedPnl)>estimatedPnl and effectivePnl>math.abs(openRealizedPnl) * 0.1
strategy.close(isLong ? "Long" : "Short", comment="Inefficiency Exit")
entryBlocked := true
lastExitBar := bar_index
lastExitTime := time
// Defensive Profit Exit
if inDateRange and hasPosition and defensiveProfitExitEffective and currentDcaStep >= defensiveProfitExitLimitEffective and effectivePnl > 0
defensiveProfitExitProximity = defensiveProfitExitProximityEffective / 100.0
if sideInput == "Long" and (close - avgPrice) / (adaptiveTakeProfitPrice - avgPrice) >= defensiveProfitExitProximity
strategy.close("Long", comment="Defensive Profit Exit")
entryBlocked := true
lastExitBar := bar_index
lastExitTime := time
else if sideInput=="Short" and (avgPrice - close) / (avgPrice - adaptiveTakeProfitPrice) >= defensiveProfitExitProximity
strategy.close("Short", comment="Defensive Profit Exit")
entryBlocked := true
lastExitBar := bar_index
lastExitTime := time
// Range dışına çıkış anı
exitedDateRange = useCustomRangeInput and not inDateRange and inDateRange[1]
if exitedDateRange and strategy.position_size != 0
strategy.close_all(comment="Backtest Range End")
// Open Position
if inDateRange and not hasPosition and cooldownOk and not entryBlocked
entrySignal = false
entryFilter = true
openPosition = false
// ENTRY SIGNALS
if mainStrategyInput == "Break Default"
entrySignal := isLong ? ((low < lowest[1] and close < high[1]) or close < lowest[1]) : ((high > highest[1] and close > low[1]) or close > highest[1])
else if mainStrategyInput=="RSI Reversal"
entrySignal := isLong ? rsi <= 30 : rsi >= 70
else if mainStrategyInput=="Trend Flip"
entrySignal := isLong ? trend == 1 and trend[1] != 1 : trend == -1 and trend[1] != -1
// ENTRY FILTERS
if entryFilterInput == "Reward Space"
entryFilter := isLong ? highest[1] > close * (1 + adaptiveTakeProfitPercent / 100) : lowest[1] < close * (1 - adaptiveTakeProfitPercent / 100)
else if entryFilterInput == "Entry Quality"
entryFilter := isLong ? highest[1] > close * (1 + adaptiveTakeProfitPercent / 100) and (close - low) / (high - low) < 0.4 : lowest[1] < close * (1 - adaptiveTakeProfitPercent / 100) and (high - close) / (high - low) < 0.4
else if entryFilterInput == "RSI Extremity"
entryFilter := isLong ? rsi <= 30 : rsi >= 70
else if entryFilterInput == "Fair Value"
entryFilter := isLong ? close <= fairValue : close >= fairValue
else if entryFilterInput == "Trend Alignment"
entryFilter := isLong ? trend == 1 : trend == -1
openPosition := entrySignal and entryFilter
if openPosition
strategy.entry(isLong ? "Long" : "Short", isLong ? strategy.long : strategy.short, comment = isLong ? "Long" : "Short", qty = orderQty)
lastDcaPrice := close
currentDcaStep := 1
totalPositionCount += 1
adaptiveTakeProfitPrice := na
adaptiveDcaPrice := na
// Add Position
if inDateRange and hasPosition and not entryBlocked and not dcaBlocked
canDca = false
if sideInput=="Long"
canDca:=(close<lastDcaPrice * (1 - (adaptiveDcaPercent / 100)) and close<avgPrice * (1 - (adaptiveDcaPercent / 100)))
else
canDca:=(close>lastDcaPrice * (1 + (adaptiveDcaPercent / 100)) and close>avgPrice * (1 + (adaptiveDcaPercent / 100)))
if canDca and currentDcaStep<maxDcaStepsInput
strategy.entry(sideInput == "Long" ? "Long" : "Short", sideInput=="Long" ? strategy.long : strategy.short, qty=orderQty, comment = sideInput == "Long" ? "Long DCA" : "Short DCA")
lastDcaPrice:=close
lastDcaBar := bar_index
currentDcaStep += 1
adaptiveTakeProfitPrice := na
adaptiveDcaPrice := na
waitBeforeReCompress -= 1
if waitBeforeReCompress == 0
compressionLock := false
// Take Profit
if inDateRange and hasPosition
var exitSignal = bool(false)
// Fixed Exit (mevcut davranış)
if exitModelEffective=="Fixed"
exitSignal := effectivePnl>0 and ((sideInput == "Long" and close>adaptiveTakeProfitPrice) or (sideInput == "Short" and close<adaptiveTakeProfitPrice))
if exitSignal
if sideInput == "Long"
strategy.close("Long", comment="Fixed Close")
else
strategy.close("Short", comment="Fixed Close")
else if exitModelEffective=="Adaptive Fast"
exitSignal := effectivePnl>0 and (sideInput == "Long" and close < lowest20[1] and close>avgPrice * 1.01) or (sideInput == "Short" and close>highest20[1] and close < avgPrice * 0.99)
if exitSignal
if sideInput == "Long"
strategy.close("Long", comment="Adaptive Fast Exit")
else
strategy.close("Short", comment="Adaptive Fast Exit")
else if exitModelEffective=="Adaptive Slow"
exitSignal := effectivePnl>0 and (sideInput == "Long" and close < lowest50[1] and close>avgPrice * 1.01) or (sideInput == "Short" and close>highest50[1] and close < avgPrice * 0.99)
if exitSignal
if sideInput == "Long"
strategy.close("Long", comment="Adaptive Slow Exit")
else
strategy.close("Short", comment="Adaptive Slow Exit")
else if exitModelEffective=="Structure Break"
exitSignal := effectivePnl>0 and (sideInput == "Long" and high > highest[1] and close>avgPrice * 1.01) or (sideInput == "Short" and low<lowest[1] and close < avgPrice * 0.99)
if exitSignal
if sideInput == "Long"
strategy.close("Long", comment="Structure Break Exit")
else
strategy.close("Short", comment="Structure Break Exit")
else if exitModelEffective=="Protect Profit"
if effectivePnl>0 and (sideInput == "Long" and close < lowest50[1] and close>avgPrice * 1.01) or (sideInput == "Short" and close>highest50[1] and close < avgPrice * 0.99)
protectProfitMode := true
if protectProfitMode
exitSignal := effectivePnl > estimatedPnl and effectivePnl <= protectFloorClosePnl and maxClosePnlSeen > 0
if exitSignal
if sideInput == "Long"
strategy.close("Long", comment="Protect Profit Exit")
else
strategy.close("Short", comment="Protect Profit Exit")
protectProfitMode := false
if close<avgPrice
protectProfitMode := false
else if exitModelEffective=="Trend Ended"
exitSignal := (sideInput == "Long" and trend[1] == 1 and trend == -1) or (sideInput == "Short" and trend[1] == -1 and trend == 1)
if exitSignal
strategy.close(sideInput == "Long" ? "Long" : "Short", comment="Trend Exit")
if exitSignal
entryBlocked := true
lastExitBar := bar_index
// Dca Compression
if hasPosition and dcaCompressionEffective
canCompress = (currentDcaStep >= 3 and dcaFillRatio<1 and not compressionLock and
((sideInput == "Long" and ((dcaFillRatio>0.80 and currentPnl>minClosePnlSeen * 0.10) or (high>avgPrice and close>high[1]))) or
(sideInput == "Short" and ((dcaFillRatio>0.80 and currentPnl>minClosePnlSeen * 0.10) or (low<avgPrice and close<low[1]))))
)
if canCompress
stepReduction = dcaFillRatio <= 0.40 ? 1 : dcaFillRatio <= 0.60 ? 2 : dcaFillRatio <= 0.90 ? 3 : 4
compressStep = math.max(currentDcaStep - stepReduction, 1)
compressionComment = "Dca Compression (-" + str.tostring(stepReduction) + " DCA)"
reduceNotional = 0.0
for i = 0 to stepReduction - 1
stepIndex = (currentDcaStep - 1) - i
if stepIndex >= 0
reduceNotional += f_getOrderSize(baseOrderSize,stepIndex,orderSizeModeInput)
reduceQty = reduceNotional / close
if sideInput == "Long"
strategy.close("Long", qty = reduceQty, comment = compressionComment)
else
strategy.close("Short", qty = reduceQty, comment = compressionComment)
currentDcaStep := compressStep
lastDcaPrice := (close + lastDcaPrice) / 2
adaptiveDcaPrice := na
compressionLock := true
compressCount += 1
waitBeforeReCompress := compressStep
// Reset
if strategy.position_size == 0 and strategy.position_size[1] != 0
dcaIndex = math.min(currentDcaStep, maxDcaStepsInput)
array.set(dcaCloseCounts,dcaIndex,array.get(dcaCloseCounts, dcaIndex) + 1)
array.set(dcaClosePnl,dcaIndex,array.get(dcaClosePnl, dcaIndex) + strategy.netprofit - strategy.netprofit[1])
totalDcaStepCount := totalDcaStepCount + currentDcaStep
takeProfitPercentEffective := takeProfitPercentInput
dcaPercentEffective := dcaPercentInput
dcaBlocked := false
currentDcaStep := na
positionSize := na
lastDcaPrice := na
lastDcaBar := na
compressionLock := false
compressCount := 0
waitBeforeReCompress := 0
trendStopModeEnabled := false
breakevenStopEnabled := false
inefficiencyExitEnabled := false
exitMode := false
protectProfitMode := false
maxPnlSeen := na
minPnlSeen := na
maxClosePnlSeen := na
minClosePnlSeen := na
maxClosePnlSeenAt := na
minClosePnlSeenAt := na
crossTp := na
newPositionOpened = barstate.isconfirmed and strategy.position_size != 0 and strategy.position_size[1] == 0
if newPositionOpened
lastDcaPrice := na(lastDcaPrice) ? close : lastDcaPrice
currentDcaStep := 1
if entryBlocked and bar_index > lastExitBar
entryBlocked := false
// Shape
trendStopActivated = hasPosition and trendStopModeEnabled and not trendStopModeEnabled[1]
plotchar(trendStopActivated,char="S",location=location.abovebar,color=color.orange,size = size.tiny,display = display.pane)
// Bg color
bgcolor(useCustomRangeInput ? (inDateRange ? na : color.new(color.gray, 90)) : na, title="Outside Backtest Range")
bgcolor(inDateRange and showTrendInput ? trend == 1 ? color.new(color.green, trendOopacity) : trend == -1 ? color.new(color.red, trendOopacity) : na : na)
bgcolor(inDateRange and rsi<=30 ? color.new(color.green, showRsiInput ? 90 : 100) : na)
bgcolor(inDateRange and rsi>=70 ? color.new(color.red, showRsiInput ? 90 : 100) : na)
bgcolor(inDateRange and rsi_4h<=30 ? color.new(color.green, show4hRsiInput ? 90 : 100) : na)
bgcolor(inDateRange and rsi_4h>=70 ? color.new(color.red, show4hRsiInput ? 90 : 100) : na)
bgcolor(inDateRange and rsi_4h<=30 and rsi<=30 ? color.new(color.green, show4hRsiInput ? 80 : 100) : na)
bgcolor(inDateRange and rsi_4h>=70 and rsi>=70 ? color.new(color.red, show4hRsiInput ? 80 : 100) : na)
bgcolor(inDateRange and rsi_divergence_short and no_recent_short ? color.new(color.orange, showRsiDivergencesInput ? 90 : 100) : na)
bgcolor(inDateRange and rsi_divergence_long and no_recent_long ? color.new(color.lime, showRsiDivergencesInput ? 90 : 100) : na)
plot(lowest[1],color = color.new(color.green, 0),title = "Lowest",display = showHighLowLevelsInput ? display.pane : display.none)
plot(highest[1],color = color.new(color.red, 0),title = "Highest",display = showHighLowLevelsInput ? display.pane : display.none)
plot(hasPosition ? lastDcaPrice : na,title="Last Entry Price",display = display.status_line)
plot(strategy.position_avg_price,title="Position AVG Price (Strategy)",color=positionSide == "Long" ? color.green : color.red,linewidth = 1,style=plot.style_circles)
plot(hasPosition ? adaptiveTakeProfitPrice : na,title="TP Price",color=color.blue,linewidth = 1,style=plot.style_circles)
plot(hasPosition ? adaptiveDcaPrice : na,title="DCA Price",color=color.gray,linewidth = 1,style=plot.style_circles, display = display.pane)
plot(hasPosition ? adaptiveTakeProfitPercent : na,title="TP %", color=color.green, linewidth=1, display=display.status_line)
plot(hasPosition ? adaptiveDcaPercent : na,title="DCA %", color=color.orange, linewidth=1, display=display.status_line)
plot(hasPosition ? currentDcaStep : na,title="Current DCA Step",color=color.white,linewidth = 1,display=display.status_line)
plot(hasPosition ? lastOrderSize : na,title="Step Order Size",color=color.orange,linewidth = 1,display=display.status_line)
plot(hasPosition ? totalNotional : na,title="Position Notional Size",color=color.fuchsia,linewidth = 1,display=display.status_line)
plot(hasPosition ? currentPnl : na,title="Active PNL",color=currentPnl>0 ? color.green : color.red,linewidth = 1,display=display.status_line)
plot(hasPosition ? openRealizedPnl : na,title="Realized PNL",color=openRealizedPnl>0 ? color.green : color.red,linewidth = 1,display=display.status_line)
plot(hasPosition ? estimatedPnl : na,title="Target PNL",color=color.blue,linewidth = 1,display=display.status_line)
//plot(strengthRatio, title="Trend Strength", display = display.status_line)
plot(strategy.netprofit,title="Total Strategy PNL",color=strategy.netprofit>0 ? color.green : color.red,linewidth = 1,display=display.status_line)
// Visual Tables
clrHeaderBg = color.rgb(45, 45, 45)
clrLabelBg = color.rgb(60, 60, 60)
clrValueBg = color.rgb(20, 20, 20)
clrHeaderTxt = color.white
clrLabelTxt = color.silver
clrValueTxt = color.white
clrPos = color.rgb(46, 204, 113) // yeşil
clrNeg = color.rgb(231, 76, 60) // kırmızı
clrWarn = color.rgb(241, 196, 15) // sarı
clrCountBase = color.green
clrRatioBase = color.green
f_header(_table, _text, _row) =>
table.cell(_table,0,_row,_text,text_color=clrHeaderTxt,bgcolor=clrHeaderBg,text_halign=text.align_center,text_size = size.small)
table.merge_cells(_table,0,_row,1,_row)
f_row(_table,_row,_label,_value,_bgLabel,_bgValue,_txtValue) =>
table.cell(_table,0,_row," " + _label,text_color=clrLabelTxt,bgcolor=_bgLabel,text_halign=text.align_left,text_size = size.small)
table.cell(_table,1,_row,str.tostring(_value) + " ",text_color=_txtValue,bgcolor=_bgValue,text_halign=text.align_right,text_size = size.small)
f_header3(_table, _text, _row) =>
table.cell(_table,0,_row,_text,text_color=clrHeaderTxt,bgcolor=clrHeaderBg,text_halign=text.align_center,text_size = size.small)
table.merge_cells(_table,0,_row,2,_row)
f_row3(_table, _row, _label, _value1, _value2, _bgLabel, _bgValue1, _bgValue2, _txtValue) =>
table.cell(_table,0,_row," " + _label,text_color = clrLabelTxt,bgcolor = _bgLabel,text_halign = text.align_left,text_size = size.small)
table.cell(_table,1,_row,str.tostring(_value1) + " ",text_color = _txtValue,bgcolor = _bgValue1,text_halign = text.align_right,text_size = size.small)
table.cell(_table,2,_row,str.tostring(_value2) + " ",text_color = _txtValue,bgcolor = _bgValue2,text_halign = text.align_right,text_size = size.small)
f_header4(_table, _text, _row) =>
table.cell(_table,0,_row,_text,text_color=clrHeaderTxt,bgcolor=clrHeaderBg,text_halign=text.align_center,text_size = size.small)
table.merge_cells(_table,0,_row,3,_row)
f_row4(_table, _row, _label, _v1, _v2, _v3, _bgLabel, _bgV1, _bgV2, _bgV3, _txtValue) =>
table.cell(_table, 0, _row, " " + _label, text_color=clrLabelTxt, bgcolor=_bgLabel, text_halign=text.align_left,text_size = size.small)
table.cell(_table, 1, _row, str.tostring(_v1) + " ", text_color=_txtValue, bgcolor=_bgV1, text_halign=text.align_right,text_size = size.small)
table.cell(_table, 2, _row, str.tostring(_v2) + " ", text_color=_txtValue, bgcolor=_bgV2, text_halign=text.align_right,text_size = size.small)
table.cell(_table, 3, _row, str.tostring(_v3) + " ", text_color=_txtValue, bgcolor=_bgV3, text_halign=text.align_right,text_size = size.small)
f_dimColor(_clr, _isDim) =>
_isDim ? color.new(_clr, 80) : _clr
f_gradient(_value, _max, _baseClr) =>
_max > 0 ? color.new(_baseClr, 80 - int(60 * math.min(math.abs(_value) / _max, 1))) : color.new(_baseClr, 80)
if tableLeftInput == "Strategy Summary"
var table planTable = table.new(position = position.bottom_left,columns=2,rows=50,border_width = 1,border_color = color.gray)
f_header(planTable, "🔎 Strategy Overview", 0)
f_row(planTable,1, "Direction", sideInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,2, "Entry Model", mainStrategyInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,3, "Entry Filter", entryFilterInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,4, "Entry Lookback Period", mainPeriodInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,5, "Exit Model", exitModelEffective,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,6, "Order Size Model", orderSizeModeInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,7, "Max DCA Steps", maxDcaStepsInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,8, "Base Order Size", baseOrderSize,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,9, "Estimated Max Notional ~", math.ceil(maxPositionSize),clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,10, "Take Profit (%)", takeProfitPercentInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,11, "DCA Distance (%)", dcaPercentInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,12, "TP / DCA Scaling Mode", tpDcaIncrementModeInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,13, "TP Increment (%)", tpIncrementValueInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,14, "DCA Increment (%)", dcaIncrementValueInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,15, "Total Position Count", totalPositionCount,color.blue, color.blue, clrValueTxt)
f_row(planTable,16, "Total DCA Step Count", totalDcaStepCount,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,17, "Average DCA Steps", avgDcaStep,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,18, "All-Time Max DCA Steps", allTimeMaxDca,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,19, "All-Time Max Notional", math.round(allTimeMaxNotional),clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,20, "All-Time Realized PNL", math.round(strategy.netprofit,2),clrLabelBg, strategy.netprofit>0 ? clrPos : clrNeg, clrValueTxt)
f_row(planTable,21, "All-Time Avg Pnl", avgPnl,clrLabelBg, clrValueBg, clrValueTxt)
f_row(planTable,22, "All-Time Max Drawdown", math.round(allTimeMinPnl,2),clrLabelBg, clrValueBg, clrNeg)
else if tableLeftInput == "DCA History"
maxCount = array.max(dcaCloseCounts)
maxPnl = math.max(math.abs(array.max(dcaClosePnl)),math.abs(array.min(dcaClosePnl)))
var table dcaSummaryTable = table.new(position = position.bottom_left,columns = 4,rows = maxDcaStepsInput + 2,border_width = 1,border_color = color.gray)
f_header4(dcaSummaryTable, "📜 DCA History", 0)
for i = 1 to maxDcaStepsInput
count = array.get(dcaCloseCounts, i)
pnl = array.get(dcaClosePnl, i)
ratio = totalPositionCount > 0? (count / totalPositionCount) * 100: 0.0
isEmpty = count == 0
countBg = f_dimColor(f_gradient(count, maxCount, clrCountBase),isEmpty)
ratioBg = f_dimColor(f_gradient(ratio, 100, clrRatioBase),isEmpty)
pnlBaseClr = pnl > 0 ? clrPos : pnl < 0 ? clrNeg : clrValueBg
pnlBg = f_dimColor(f_gradient(pnl, maxPnl, pnlBaseClr),isEmpty)
f_row4(dcaSummaryTable,i,str.tostring(i) + " DCA",count,str.tostring(math.round(ratio, 2)) + "%",math.round(pnl, 2),f_dimColor(clrLabelBg, isEmpty),countBg,ratioBg,pnlBg,clrValueTxt)
else if tableLeftInput == "DCA & Order Summary"
// --- maksimum teorik pozisyon
maxPositionSizeTable = f_getMaxPositionSize(baseOrderSize,maxDcaStepsInput,orderSizeModeInput)
// --- max değerler (renk skalası için)
var float maxOrderSize = 0.0
var float maxTotalSize = 0.0
var float totalPosSize = 0.0
totalPosSize := 0
for i = 0 to maxDcaStepsInput - 1
orderSizeStep = f_getOrderSize(baseOrderSize, i, orderSizeModeInput)
totalPosSize += orderSizeStep
maxOrderSize := math.max(maxOrderSize, orderSizeStep)
maxTotalSize := math.max(maxTotalSize, totalPosSize)
// --- tablo
var table dcaOrderTable = table.new(position = position.bottom_left,columns = 4,rows = maxDcaStepsInput + 2,border_width = 1,border_color = color.gray)
f_header4(dcaOrderTable, "📦 DCA Order & Notional Summary", 0)
// --- satırlar
totalPosSize := 0
for i = 0 to maxDcaStepsInput - 1
step = i + 1
orderSizeStep = f_getOrderSize(baseOrderSize, i, orderSizeModeInput)
totalPosSize += orderSizeStep
shareRatio = maxPositionSizeTable > 0 ? (orderSizeStep / maxPositionSizeTable) * 100 : 0.0
orderBg = f_gradient(orderSizeStep, maxOrderSize, clrCountBase)
totalBg = f_gradient(totalPosSize, maxTotalSize, clrRatioBase)
f_row4(dcaOrderTable,step,"DCA " + str.tostring(step),math.round(orderSizeStep, 2),math.round(totalPosSize, 2),str.tostring(math.round(shareRatio, 2)) + "%",clrLabelBg,orderBg,totalBg,clrValueBg,clrValueTxt)
if activePositionTableInput
var table posTable = na
if not hasPosition and not na(posTable)
table.delete(posTable)
posTable := na
if hasPosition and na(posTable)
posTable := table.new(position = position.top_right,columns = 2,rows = 15,border_width = 1,border_color = color.gray)
if hasPosition and not na(posTable)
f_header(posTable, "🟢 Active Position", 0)
f_row(posTable, 2, "DCA Step", str.format("{0}/{1}", currentDcaStep, maxDcaStepsInput),clrLabelBg, clrValueBg, currentDcaStep >= maxDcaStepsInput * 0.7 ? clrNeg : currentDcaStep >= maxDcaStepsInput * 0.5 ? clrWarn : clrPos)
f_row(posTable, 3, "Max Notional ~", math.round(maxPositionSize),clrLabelBg, clrValueBg, clrValueTxt)
f_row(posTable, 4, "Take Profit (%)", math.round(adaptiveTakeProfitPercent,2),clrLabelBg, clrValueBg, clrValueTxt)
f_row(posTable, 5, "DCA (%)", math.round(adaptiveDcaPercent,2),clrLabelBg, clrValueBg, clrValueTxt)
f_row(posTable, 6, "Current Notional", math.round(totalNotional),clrLabelBg, clrValueBg, clrValueTxt)
f_row(posTable, 7, "Next Order Size ➡", math.round(orderSize),clrLabelBg, color.gray, clrValueTxt)
f_row(posTable, 8, "Target PNL ~", math.round(estimatedPnl, 2),clrLabelBg, clrValueBg, color.blue)
f_row(posTable, 9, "Unrealized PNL", math.round(currentPnl, 2),clrLabelBg, currentPnl > 0 ? clrPos : clrNeg, clrValueTxt)
f_row(posTable, 10, "Realized PNL", math.round(openRealizedPnl, 2),clrLabelBg, clrValueBg, openRealizedPnl > 0 ? color.green : color.red)
f_row(posTable, 11, "Max PNL", math.round(maxClosePnlSeen, 2),clrLabelBg, clrValueBg, maxClosePnlSeen > 0 ? color.green : color.red)
f_row(posTable, 12, "Min PNL", math.round(minClosePnlSeen, 2),clrLabelBg, clrValueBg, minClosePnlSeen > 0 ? color.green : color.red)
if not hasPosition and na(posTable)
nextPosTable = table.new(position = position.top_right,columns = 2,rows = 15,border_width = 1,border_color = color.gray)
f_header(nextPosTable, "⌛ Next Position", 0)
f_row(nextPosTable, 2, "Direction", sideInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(nextPosTable, 3, "Entry Model", mainStrategyInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(nextPosTable, 4, "Max DCA Steps", maxDcaStepsInput,clrLabelBg, clrValueBg, clrValueTxt)
f_row(nextPosTable, 5, "Max Notional ~", math.round(maxPositionSize),clrLabelBg, clrValueBg, clrValueTxt)
f_row(nextPosTable, 6, "Initial Order Size", math.round(baseOrderSize),clrLabelBg, color.blue, clrValueTxt)