silverbull-lux02.pine
Copy
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © fluxchart
//
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Version History
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// v2.0 (2025-08-23)
// - FIX: Corrected the backtesting profit calculation to be relative to the entry price, ensuring accurate metrics.
// - REFACTOR: Created a dedicated function `setTradeTargets` to consolidate and simplify TP/SL calculation logic.
// - REFACTOR: De-nested the main operational logic into smaller, more manageable functions
// (e.g., `handleSessionEvents`, `handleTradeProgression`) for improved readability and maintainability.
//
// v1.0 (Initial Release)
// - Base implementation of the ICT Silver Bullet strategy.
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
//@version=5
const bool DEBUG = false
const int showLastXLiqs = 10
const int maxDistanceToLastBar = 50000 // Affects Running Time
const int atrLen = 5
const bool maxTPLastHour = false
const int extendFVG = 15
var initRun = true
indicator("ICT Silver Bullet | Flux Charts v2", shorttitle = "Silver Bullet v2 | Flux Charts", overlay = true, max_boxes_count = 500, max_labels_count = 500, max_lines_count = 500)
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Inputs & Settings
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
nySession1 = "1000-1100"
nySession2 = "1400-1500"
londonSession = "0300-0400"
// --- General Configuration
executionType = input.string("FVG", "Execution Type", options = ["FVG", "MSS"], group = "General Configuration", tooltip = "FVG -> recommended for 1-min timeframe.\n\nMSS -> recommended for 5-min timeframe.")
mssOffset = input.int(10, "MSS Swing Length", group = "General Configuration")
breakoutMethod = input.string("Wick", "Breakout Method", options = ["Close", "Wick"], group = "General Configuration")
fvgBars = input.string("Same Type", "FVG Detection", options = ["Same Type", "All"], tooltip = "Same Type -> All 3 bars that formed the FVG should be the same type. (Bullish / Bearish) \n\nAll -> Bar types may vary between bullish / bearish.", group = "General Configuration")
fvgSensEnabled = input.bool(true, "", "", "sens", "General Configuration")
fvgSensitivityText = input.string("Extreme", "FVG Detection Sensitivity", options = ["Extreme", "High", "Normal", "Low"], inline = "sens", group = "General Configuration")
fvgEntry = DEBUG ? input.string("Along", "FVG Entry", options = ["All", "Along", "Inverse"], group = "General Configuration") : "Along"
allowGaps = true
fvgSensitivity = fvgSensitivityText == "Extreme" ? 6 : fvgSensitivityText == "High" ? 2 : fvgSensitivityText == "Normal" ? 1.5 : 1
showSessions = input.bool(true, "Show Sessions", inline = "2", group = "General Configuration")
showHL = input.bool(false, "Highs / Lows", inline = "2", group = "General Configuration")
showFVGs = input.bool(true, "FVGs", inline = "2", group = "General Configuration")
showTPSL = input.bool(true, "TP / SL", inline = "2", group = "General Configuration")
// --- TP / SL
tpslMethod = input.string("Dynamic", "TP / SL Method", options = ["Dynamic", "Fixed"], group = "TP / SL")
riskAmount = input.string("Normal", "Risk", options = ["High", "Normal", "Low"], group = "TP / SL", tooltip = "The risk amount when Dynamic TP / SL method is selected.")
tpPercent = input.float(0.3, "Take Profit %", group = "TP / SL")
slPercent = input.float(0.4, "Stop Loss %", group = "TP / SL")
closePositionAtEndSession = input.bool(true, "Close Position @ Session End", group = "TP / SL")
// --- Debug & Strategy Parameters
RR = DEBUG ? input.float(0.99, "Risk:Reward Ratio", group = "Debug") : 0.99
requireFVGRetracement = DEBUG ? input.bool(true, "[DBG] Require FVG Retracement", group = "Debug") : true
slATRMult = riskAmount == "High" ? 6.5 : riskAmount == "Normal" ? 5.5 : 3.5
executeInsideSession = DEBUG ? input.bool(true, "Must Execute Inside Session", group = "Debug", display = display.none) : true
ny1Enabled = DEBUG ? input.bool(true, "NY 1", group = "Debug", display = display.none) : true
ny2Enabled = DEBUG ? input.bool(true, "NY 2", group = "Debug", display = display.none) : true
lnEnabled = DEBUG ? input.bool(true, "London", group = "Debug", display = display.none) : true
// --- Visuals & Alerts
highColor = input.color(color.green, "Buy", inline = "colors", group = "Visuals")
lowColor = input.color(color.red, "Sell", inline = "colors", group = "Visuals")
textColor = input.color(color.white, "Text", inline = "colors", group = "Visuals")
buyAlertEnabled = input.bool(true, "Buy Signal", inline = "BS", group = "Alerts")
sellAlertEnabled = input.bool(true, "Sell Signal", inline = "BS", group = "Alerts")
tpAlertEnabled = input.bool(true, "Take-Profit Signal", inline = "TS", group = "Alerts")
slAlertEnabled = input.bool(true, "Stop-Loss Signal ", inline = "TS", group = "Alerts")
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Custom Types
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
type FVG
int startTime
int endTime
float top
float bottom
bool isBull
type Sweep
int startTime
int endTime
string side
float price
type SilverBullet
string state
int startTime
int lastHour = na
float lastHourHigh = na
float lastHourLow = na
Sweep brokenSweep = na
float slTarget
float tpTarget
string entryType
int entryTime
int exitTime
float entryPrice
float exitPrice
int dayEndedBeforeExit
FVG fvg
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Global Variables
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
var lineX = array.new<line>()
var boxX = array.new<box>()
var labelX = array.new<label>()
var SilverBullet[] sbList = array.new<SilverBullet>(0)
var SilverBullet lastSB = na
var bool buyAlertTick = false
var bool sellAlertTick = false
var bool tpAlertTick = false
var bool slAlertTick = false
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Core Calculations & Session Logic
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
atr = ta.atr(atrLen)
tfInMin = timeframe.in_seconds() / 60
if tfInMin > 5
runtime.error("Silver Bullet indicator only works timeframes <= 5 Minutes.")
// --- Session Time Variables
barLength = 60 / tfInMin
high12 = ta.highest(barLength)
low12 = ta.lowest(barLength)
highMSS = ta.highest(mssOffset)
lowMSS = ta.lowest(mssOffset)
lastHourTime = time[barLength]
// --- Session Detection
sessionBegins(sess) =>
t = time("1440", sess, "America/New_York")
is_first = na(t[1]) and not na(t) or t[1] < t
newSession = (ny1Enabled and sessionBegins(nySession1)) or (ny2Enabled and sessionBegins(nySession2)) or (lnEnabled and sessionBegins(londonSession))
insideSession = (ny1Enabled and not na(time("", nySession1, "America/New_York"))) or (ny2Enabled and not na (time("", nySession2, "America/New_York"))) or (lnEnabled and not na(time("", londonSession, "America/New_York")))
endSession = not insideSession and insideSession[1]
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Helper Functions
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Calculates percentage difference of val2 relative to val1
diffPercent(float val1, float val2) =>
((val2 - val1) / val1) * 100.0
// Sets the SL and TP targets for a given trade
setTradeTargets(SilverBullet sb) =>
entryPrice = sb.entryPrice
sl = 0.0
tp = 0.0
isLong = sb.entryType == "Long"
if tpslMethod == "Fixed"
sl := isLong ? entryPrice * (1 - slPercent / 100.0) : entryPrice * (1 + slPercent / 100.0)
tp := isLong ? entryPrice * (1 + tpPercent / 100.0) : entryPrice * (1 - tpPercent / 100.0)
else // Dynamic
sl_base = isLong ? lowMSS : highMSS
sl_atr_offset = atr * slATRMult
sl := isLong ? sl_base - sl_atr_offset : sl_base + sl_atr_offset
tp := entryPrice + (isLong ? 1 : -1) * (math.abs(entryPrice - sl) * RR)
[sl, tp]
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Strategy Logic: State Machine
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// --- Function to handle session start and end events
handleSessionEvents() =>
// Handle FVG end on session close
if not na(lastSB) and endSession and not na(lastSB.fvg) and na(lastSB.fvg.endTime)
lastSB.fvg.endTime := time
// Handle new session creation
if newSession
createNewSB = true
if not na(lastSB) and not na(lastSB.entryPrice) and na(lastSB.exitPrice)
if closePositionAtEndSession
lastSB.dayEndedBeforeExit := time[1]
lastSB.exitTime := time
lastSB.exitPrice := close
else
createNewSB := false // Don't create new SB if a trade is already open
if createNewSB
newSB = SilverBullet.new("Waiting For Liquidity Break", time)
newSB.lastHourHigh := high12
newSB.lastHourLow := low12
newSB.lastHour := lastHourTime
sbList.unshift(newSB)
lastSB := newSB
// --- Function to check for liquidity breaks
checkForLiquidityBreak() =>
if time > lastSB.startTime
if (breakoutMethod == "Close" ? close : low) < lastSB.lastHourLow
lastSB.brokenSweep := Sweep.new(lastSB.lastHour, time, "Sellside", lastSB.lastHourLow)
lastSB.entryType := "Long"
lastSB.state := "Waiting For Execution"
else if (breakoutMethod == "Close" ? close : high) > lastSB.lastHourHigh
lastSB.brokenSweep := Sweep.new(lastSB.lastHour, time, "Buyside", lastSB.lastHourHigh)
lastSB.entryType := "Short"
lastSB.state := "Waiting For Execution"
// --- Function to check for an execution signal (MSS or FVG)
checkForExecutionSignal() =>
if time > lastSB.brokenSweep.endTime
if executionType == "MSS"
if lastSB.brokenSweep.side == "Buyside" and (breakoutMethod == "Close" ? close : low) < lowMSS[1] // Short
sellAlertTick := true
lastSB.state := "Entry Taken"
lastSB.entryTime := time
lastSB.entryPrice := (breakoutMethod == "Close" ? close : lowMSS[1])
[lastSB.slTarget, lastSB.tpTarget] = setTradeTargets(lastSB)
else if lastSB.brokenSweep.side == "Sellside" and (breakoutMethod == "Close" ? close : high) > highMSS[1] // Long
buyAlertTick := true
lastSB.state := "Entry Taken"
lastSB.entryTime := time
lastSB.entryPrice := (breakoutMethod == "Close" ? close : highMSS[1])
[lastSB.slTarget, lastSB.tpTarget] = setTradeTargets(lastSB)
else if executionType == "FVG" and not na(lastSB.fvg)
if not requireFVGRetracement
lastSB.state := "Entry Taken"
lastSB.entryTime := time
lastSB.entryPrice := close
if lastSB.entryType == "Long"
buyAlertTick := true
else
sellAlertTick := true
[lastSB.slTarget, lastSB.tpTarget] = setTradeTargets(lastSB)
else
lastSB.state := "FVG Retracement"
// --- Function to check for FVG retracement entry
checkForFVGRetracementEntry() =>
if (lastSB.fvg.isBull and (breakoutMethod == "Close" ? close : low) < lastSB.fvg.top) or (not lastSB.fvg.isBull and (breakoutMethod == "Close" ? close : high) > lastSB.fvg.bottom)
lastSB.state := "Entry Taken"
lastSB.entryTime := time
lastSB.entryPrice := close
if lastSB.entryType == "Long"
buyAlertTick := true
else
sellAlertTick := true
[lastSB.slTarget, lastSB.tpTarget] = setTradeTargets(lastSB)
// --- Function to manage an open position (check for TP/SL)
manageOpenPosition() =>
isLong = lastSB.entryType == "Long"
// Check for Stop Loss
if (isLong and low <= lastSB.slTarget) or (not isLong and high >= lastSB.slTarget)
slAlertTick := true
lastSB.exitPrice := lastSB.slTarget
lastSB.exitTime := time
lastSB.state := "Stop Loss"
return
// Check for Take Profit
if tpslMethod == "Fixed"
if (isLong and ((high / lastSB.entryPrice) - 1) * 100 >= tpPercent)
tpAlertTick := true
lastSB.exitPrice := lastSB.entryPrice * (1 + tpPercent / 100.0)
lastSB.exitTime := time
lastSB.state := "Take Profit"
if (not isLong and ((low / lastSB.entryPrice) - 1) * 100 <= -tpPercent)
tpAlertTick := true
lastSB.exitPrice := lastSB.entryPrice * (1 - tpPercent / 100.0)
lastSB.exitTime := time
lastSB.state := "Take Profit"
else // Dynamic TP
if (isLong and ((maxTPLastHour and high >= lastSB.lastHourHigh) or high >= lastSB.tpTarget))
tpAlertTick := true
lastSB.exitPrice := (high >= math.max(lastSB.lastHourHigh, lastSB.tpTarget) ? math.max(lastSB.lastHourHigh, lastSB.tpTarget) : math.min(lastSB.lastHourHigh, lastSB.tpTarget))
lastSB.exitTime := time
lastSB.state := "Take Profit"
if (not isLong and ((maxTPLastHour and low <= lastSB.lastHourLow) or low <= lastSB.tpTarget))
tpAlertTick := true
lastSB.exitPrice := (low <= math.min(lastSB.lastHourLow, lastSB.tpTarget) ? math.min(lastSB.lastHourLow, lastSB.tpTarget) : math.max(lastSB.lastHourLow, lastSB.tpTarget))
lastSB.exitTime := time
lastSB.state := "Take Profit"
// --- Function to detect FVG formations
detectFVG() =>
fvgEnter = false
if na(lastSB.fvg) and lastSB.state != "Waiting For Liquidity Break" and ((not executeInsideSession) or insideSession)
firstBarSize = math.abs(open - close)
secondBarSize = math.abs(open[1] - close[1])
thirdBarSize = math.abs(open[2] - close[2])
barSizeSum = firstBarSize + secondBarSize + thirdBarSize
fvgBarsCheck = fvgBars == "All" or (open > close and open[1] > close[1] and open[2] > close[2]) or (open <= close and open[1] <= close[1] and open[2] <= close[2])
if fvgBarsCheck
maxCODiff = math.max(math.abs(close[2] - open[1]), math.abs(close[1] - open))
condition = ((not fvgSensEnabled) or (barSizeSum * fvgSensitivity > atr / 1.5)) and (allowGaps or (maxCODiff <= atr))
bearFVG = high < low[2] and close[1] < low[2] and condition
bullFVG = low > high[2] and close[1] > high[2] and condition
FVGSize = bearFVG ? math.abs(low[2] - high) : bullFVG ? math.abs(low - high[2]) : 0
FVGSizeEnough = (FVGSize * fvgSensitivity > atr)
if (bearFVG or bullFVG) and (FVGSizeEnough or not fvgSensEnabled)
if fvgEntry == "All" or (fvgEntry == "Along" and ((lastSB.entryType == "Long" and bullFVG) or (lastSB.entryType == "Short" and bearFVG))) or (fvgEntry == "Inverse" and ((lastSB.entryType == "Long" and bearFVG) or (lastSB.entryType == "Short" and bullFVG)))
lastSB.fvg := FVG.new(time, na, bearFVG ? low[2] : low, bearFVG ? high : high[2], bullFVG)
// --- Main trade progression logic
handleTradeProgression() =>
// Handle FVG invalidation
if not na(lastSB.fvg) and na(lastSB.fvg.endTime) and ((lastSB.fvg.isBull and low <= lastSB.fvg.bottom) or (not lastSB.fvg.isBull and high >= lastSB.fvg.top))
lastSB.fvg.endTime := time
detectFVG()
// State machine
switch lastSB.state
"Waiting For Liquidity Break" => checkForLiquidityBreak()
"Waiting For Execution" => if (not executeInsideSession) or insideSession { checkForExecutionSignal() }
"FVG Retracement" => if (not executeInsideSession) or insideSession { checkForFVGRetracementEntry() }
"Entry Taken" => manageOpenPosition()
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Main Execution Block
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
if bar_index > last_bar_index - maxDistanceToLastBar
handleSessionEvents()
if not na(lastSB)
handleTradeProgression()
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Plotting & Visuals
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
tfs = (30 / (timeframe.in_seconds(timeframe.period) / 60))
plotColor = color.new(textColor, 60)
plotchar(not na(time("", nySession1, "America/New_York")) and newSession and showSessions, title = 'NY Session 1', location = location.top, text='10-11 AM\nNY', color=color.new(color.black, 100), textcolor=plotColor, offset= +tfs)
plotchar(not na(time("", nySession2, "America/New_York")) and newSession and showSessions, title = 'NY Session 2', location = location.top, text='2-3 PM\nNY', color=color.new(color.black, 100), textcolor=plotColor, offset= +tfs)
plotchar(not na(time("", londonSession, "America/New_York")) and newSession and showSessions, title = 'London Session', location = location.top, text='3-4 AM\nNY', color=color.new(color.black, 100), textcolor=plotColor, offset= +tfs)
if (newSession or endSession) and showSessions
line.new(bar_index, close, bar_index, close + syminfo.mintick, color = plotColor, extend=extend.both)
// --- Render Trade Outcomes
if not na(lastSB)
renderTopSL = false
renderBottomSL = false
renderTopTP = false
renderBottomTP = false
if lastSB.state == "Stop Loss" and time > lastSB.exitTime
if lastSB.entryType == "Long"
renderBottomSL := true
else
renderTopSL := true
lastSB.state := "Done"
if lastSB.state == "Take Profit"
if lastSB.entryType == "Long"
renderTopTP := true
else
renderBottomTP := true
lastSB.state := "Done"
plotshape(renderTopSL, "", shape.circle, location.abovebar, color.red, textcolor = textColor, text = "SL", size = size.tiny)
plotshape(renderBottomSL, "", shape.circle, location.belowbar, color.red, textcolor = textColor, text = "SL", size = size.tiny)
plotshape(renderTopTP, "", shape.xcross, location.abovebar, color.blue, textcolor = textColor, text = "TP", size = size.tiny)
plotshape(renderBottomTP, "", shape.xcross, location.belowbar, color.blue, textcolor = textColor, text = "TP", size = size.tiny)
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Alerts
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
if barstate.islastconfirmedhistory
initRun := false
alertcondition(buyAlertTick and not initRun, "Buy Signal", "A buy signal has been triggered.")
alertcondition(sellAlertTick and not initRun, "Sell Signal", "A sell signal has been triggered.")
alertcondition(tpAlertTick and not initRun, "Take-Profit Signal", "Take-Profit has been hit.")
alertcondition(slAlertTick and not initRun, "Stop-Loss Signal", "Stop-Loss has been hit.")
if not initRun
if buyAlertTick and buyAlertEnabled
alert("Buy Signal")
if sellAlertTick and sellAlertEnabled
alert("Sell Signal")
if tpAlertTick and tpAlertEnabled
alert("Take-Profit Signal")
if slAlertTick and slAlertEnabled
alert("Stop-Loss Signal")
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Drawing Management (On every confirmed bar)
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
if barstate.isconfirmed
// --- Clear previous drawings
for l in lineX
line.delete(l)
for b in boxX
box.delete(b)
for lbl in labelX
label.delete(lbl)
lineX.clear()
boxX.clear()
labelX.clear()
// --- Redraw historical trades
if sbList.size() > 0
for i = 0 to math.min(sbList.size() - 1, showLastXLiqs)
curSB = sbList.get(i)
// Last Hour High/Low
if not na(curSB.brokenSweep) and showHL
lineX.push(line.new(curSB.brokenSweep.startTime, curSB.lastHourHigh, curSB.brokenSweep.endTime, curSB.lastHourHigh, xloc = xloc.bar_time, color = lowColor, style = (curSB.brokenSweep.price == curSB.lastHourHigh ? line.style_dashed : line.style_solid)))
lineX.push(line.new(curSB.brokenSweep.startTime, curSB.lastHourLow, curSB.brokenSweep.endTime, curSB.lastHourLow, xloc = xloc.bar_time, color =highColor, style = (curSB.brokenSweep.price == curSB.lastHourLow ? line.style_dashed : line.style_solid)))
if not na(curSB.entryTime)
// Entry Label
if curSB.entryType == "Long"
labelX.push(label.new(curSB.entryTime, curSB.entryPrice, "Buy", xloc = xloc.bar_time, yloc = yloc.belowbar, textcolor = textColor, color = highColor, style = label.style_label_up, size = size.small))
else
labelX.push(label.new(curSB.entryTime, curSB.entryPrice, "Sell", xloc = xloc.bar_time, yloc = yloc.abovebar, textcolor = textColor, color = lowColor, style = label.style_label_down, size = size.small))
// FVG Box
if showFVGs and not na(curSB.fvg)
boxX.push(box.new(curSB.fvg.startTime, curSB.fvg.top, nz(curSB.fvg.endTime, time + timeframe.in_seconds() * 1000 * extendFVG), curSB.fvg.bottom, border_width = 0, xloc = xloc.bar_time, bgcolor = color.new(curSB.fvg.isBull ? highColor : lowColor, 80)))
// TP / SL Boxes
if showTPSL
offset = atr / 3.0
boxX.push(box.new(curSB.entryTime, curSB.tpTarget + offset, nz(curSB.exitTime, time + timeframe.in_seconds() * 1000 * extendFVG), curSB.tpTarget - offset, text = "TAKE PROFIT (" + str.tostring(curSB.tpTarget, format.mintick) + ")", text_color = textColor, xloc = xloc.bar_time, border_width = 0, bgcolor = color.new(highColor, 50), text_size = size.small))
boxX.push(box.new(curSB.entryTime, curSB.slTarget + offset, nz(curSB.exitTime, time + timeframe.in_seconds() * 1000 * extendFVG), curSB.slTarget - offset, text = "STOP LOSS (" + str.tostring(curSB.slTarget, format.mintick) + ")", text_color = textColor, xloc = xloc.bar_time, border_width = 0, bgcolor = color.new(lowColor, 50) , text_size = size.small))
if not na(curSB.dayEndedBeforeExit)
labelX.push(label.new(curSB.dayEndedBeforeExit, curSB.exitPrice, "Exit", xloc = xloc.bar_time, yloc = yloc.belowbar, textcolor = textColor, color = color.yellow, style = label.style_circle, size = size.tiny))
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
// Backtesting Dashboard
// —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
if barstate.islast
var table backtestDisplay = table.new(position.top_right, 2, 10, bgcolor = #1B1F2B, frame_width = 2, frame_color = color.black, border_width = 1, border_color = color.black)
float totalSBProfitPercent = 0
int successfulTrades = 0
int unsuccessfulTrades = 0
if sbList.size() > 0
for i = 0 to sbList.size() - 1
curSB = sbList.get(i)
if not na(curSB.entryPrice) and not na(curSB.exitPrice)
// CORRECTED: Profit calculation is now relative to the entry price.
profit_percent = diffPercent(curSB.entryPrice, curSB.exitPrice)
// For short trades, a lower exit price is a profit, so we invert the sign
if curSB.entryType == "Short"
profit_percent := -profit_percent
totalSBProfitPercent += profit_percent
if profit_percent > 0
successfulTrades += 1
else
unsuccessfulTrades += 1
totalTrades = successfulTrades + unsuccessfulTrades
winrate = totalTrades > 0 ? (successfulTrades / totalTrades) * 100 : 0
avgProfit = totalTrades > 0 ? totalSBProfitPercent / totalTrades : 0
// --- Populate Table
table.cell(backtestDisplay, 0, 0, "SB Backtesting", text_color = color.white, bgcolor = #1B1F2B)
table.merge_cells(backtestDisplay, 0, 0, 1, 0)
table.cell(backtestDisplay, 0, 1, "Total Entries", text_color = color.white)
table.cell(backtestDisplay, 1, 1, str.tostring(totalTrades), text_color = color.white)
table.cell(backtestDisplay, 0, 2, "Wins", text_color = color.white)
table.cell(backtestDisplay, 1, 2, str.tostring(successfulTrades), text_color = color.white)
table.cell(backtestDisplay, 0, 3, "Losses", text_color = color.white)
table.cell(backtestDisplay, 1, 3, str.tostring(unsuccessfulTrades), text_color = color.white)
table.cell(backtestDisplay, 0, 4, "Winrate", text_color = color.white)
table.cell(backtestDisplay, 1, 4, str.tostring(winrate, "#.##") + "%", text_color = winrate >= 50 ? color.green : color.red)
table.cell(backtestDisplay, 0, 5, "Average Profit", text_color = color.white)
table.cell(backtestDisplay, 1, 5, str.tostring(avgProfit, "#.##") + "%", text_color = avgProfit > 0 ? color.green : color.red)
table.cell(backtestDisplay, 0, 6, "Total Profit", text_color = color.white)
table.cell(backtestDisplay, 1, 6, str.tostring(totalSBProfitPercent, "#.##") + "%", text_color = totalSBProfitPercent > 0 ? color.green : color.red)
Show all 488 lines