One free indicator that puts paid signal services and courses to shame — a live trade HUD with a single alert, sent straight to your phone at exactly the right moment.
Open Legacy is a single free Pine Script v6 indicator that works on any market — forex, indices, metals, crypto, you name it. It calculates fixed take-profit and stop-loss levels the moment a signal fires, plots the matching BUY / SELL labels and SL / TP lines directly on price, keeps a heads-up display panel showing the live status of the current signal, and automatically detects the right inverse-correlation benchmark for whatever you're charting — no second script or second chart required. It fires alerts the moment a target is hit or a new signal appears.
Three things to get right
Each card jumps to a full walkthrough further down the page.
How to Install
Copy the Pine Script into TradingView's Pine Editor and add Open Legacy to any chart you want to trade.
View stepsKey Settings
What each input group controls, and how the fixed risk engine locks TP/SL the moment a signal fires.
View settingsSetting Up Alerts
Set up one TradingView alert on Open Legacy — it covers every event, from new signals to TP/SL hits.
View alert setupHow to Install
One script, one chart, any market. Open Legacy runs entirely on whichever chart you put it on — there's nothing else to add.
Open the chart for the market you want to trade
Forex, indices, metals, crypto — Open Legacy auto-detects what you're charting and adapts to it.
Open the Pine Editor
Click Pine Editor in the bottom panel of the chart window.
Clear the default template
Select all the placeholder code in the editor and delete it, leaving a blank file.
Paste the script
Copy the full source for Open Legacy from the Get the Scripts section below and paste it in.
Click "Add to Chart"
The indicator compiles and appears on your chart — BUY/SELL labels, TP/SL lines, and the HUD panel all show up together, in one shot.
Visual Order, then Bring to Front.Key Settings
Everything — the plotted labels, the TP/SL lines, and the HUD table — comes from one script now, so there's no more matching settings across scripts. Here's what each input group actually controls.
Wait for Candle Close
A BUY or SELL label showing up on your chart isn't the moment to act — the candle it appeared on has to fully close first.
See a label appear? Don't act yet
Note it, but treat it as provisional — it's still live and can be erased by the next tick.
Wait for that candle to close
Once the candle the label appeared on finishes and the next candle opens, the signal is locked in — no more repainting.
Still there after close? Now it's solidified
That's the moment to consider entering — a confirmed signal, not a mid-candle flicker.
Reading the TP Hit Odds
The HUD's "TP Hit Odds" figure is a heuristic, not a guarantee — but it's still useful as a filter for when to stay cautious. A simple rule of thumb:
Below 60% before entry
If a signal is active but the HUD's TP Hit Odds reads below 60%, treat that as a caution flag. Consider holding off on entering that trade rather than taking every signal automatically.
40–50% while in a trade
If you're already in a trade and the odds drift down into the 40–50% range or lower, consider closing the position early rather than waiting for SL to be hit.
The Correlation Confirmation Secret
A second, independent read on strength — checking your instrument against the benchmark that naturally moves opposite it — is what separates a confirmed trade from a rushed one. Open Legacy figures out the right benchmark for whatever you're charting automatically, and surfaces it right in its own HUD table — no second chart, no second script.
Nothing to add — it's already built in
Open Legacy's HUD table includes a "[Benchmark] Structure" row automatically, right on whichever chart you put it on. No second chart, no separate script — and no manual symbol picking, unless you want to override it yourself.
Before a trade — the Buddha heart
While you're waiting for a signal, the Structure row simply reads Ready ⏳ — calm, steady, no verdict yet. Nothing to act on, just watching.
Once a trade is live — the demon hands
The instant a BUY or SELL becomes active, that same row flips decisively: Confirmed ✅ means correlation backs your trade — your green light. Invalid ❌ means the trend is running against the expected correlation — a caution flag. You may also see CLOSED 💤 — that shows up when the underlying market is shut, or (for crypto) during the scheduled window when forex markets — and DXY along with them — are offline. Open Legacy bypasses the correlation check automatically during that window rather than penalizing a trade for a benchmark that isn't trading.
Only treat a signal as fully confirmed on Confirmed ✅
When Open Legacy fires a BUY or SELL, check the Structure row before acting. Invalid doesn't mean don't trade — it means treat the signal with extra caution.
How the benchmark gets picked
"To flow in the markets you must have a Buddha heart and demon hands. The Buddha heart is the calm, steady wait for what plausibly is right. The demon hands are the decisive verdict the moment a trade goes live, stating where we are planning and already operating our next move — bullish or bearish."
Calm patience, decisive execution — the same row tells you both.
Best Times to Trade — a Gold Example
Signals mean more when they fire during hours where your market typically sees real liquidity and follow-through. Since Open Legacy now works across markets, exact windows vary by instrument — here's a concrete example using XAUUSD's two prime windows, both in Irish Standard Time (IST). Trading something else? Look up your own market's peak-liquidity hours; the same avoid-the-dead-zones principle applies everywhere.
07:00 – 11:30 IST
The morning window. London liquidity is online and price action tends to be cleaner and more directional.
13:00 – 16:00 IST
The afternoon window. London and New York overlap here, which is typically where gold sees its strongest follow-through.
Avoid the Midday Lull
Between the two prime XAUUSD windows above sits a gap worth respecting on purpose. (Other markets have their own version of this dead zone — check yours.)
News Awareness
Every market is sensitive to its own macro surprises. Open Legacy isn't designed to trade through that kind of volatility, whatever you're charting.
Setting Up Alerts
Alerts are configured on Open Legacy with a single alert. That one alert covers all four events — new buy trade, new sell trade, TP hit, and SL hit — automatically, so there's nothing to split into multiple alerts anymore.
Right-click the chart and choose "Add Alert"
Or click the alarm clock icon in the right-hand toolbar.
Set the condition to Open Legacy
In the "Condition" dropdown, select the Open Legacy indicator — it's the only one on your chart now.
Choose "Any alert() function call"
Open Legacy now fires everything through one built-in alert() call, so this is the only option you need — no more picking between separate conditions.
Set your preferred interval
Pick whichever timeframe matches what you're trading. If you're not sure, 10 minutes is a solid default — frequent enough to catch signals early without flooding you with noise.
Name it and customize the message if you like
Give the alert any name you want — e.g. "Open Legacy Alert". The actual notification content (entry, TP, SL, or a hit confirmation) is generated automatically by the script, so the message field is just for your own reference.
Connect a notification channel
Turn on app push, email, SMS, or webhook delivery in the "Notifications" tab of the alert dialog, then click Create.
alert() call sends a different, detailed message depending on what just happened — new buy trade, new sell trade, TP hit, or SL hit — all four covered by the one alert you set up. Nothing else to configure.All four events, one alert
Get the Scripts
Full Pine Script v6 source for both. Copy each into its own Pine Editor tab as described in How to Install.
//@version=6
indicator("Open Legacy", overlay = true)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// ⚙️ INPUTS & CONTROLS
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
replayMode = input.bool(true, "Bar Replay / Backtest Mode", group = "Mode Controls", tooltip = "Disables real-time session closure checks so HUD functions during Bar Replay")
keyValue = input.float(1.0, "UT Key Value", minval = 0.1, step = 0.1, group = "UT Bot Core")
atrPeriod = input.int(9, "UT ATR Period", minval = 1, group = "UT Bot Core")
enableTrade2 = input.bool(true, "Enable Trade 2 Signal", group = "Scale-In Toggles")
enableTrade3 = input.bool(true, "Enable Trade 3 Signal", group = "Scale-In Toggles")
minBarsDelay2 = input.int(3, "Min Bars: Trade 1 ➔ Trade 2", minval = 1, group = "Scale-In Timing")
minBarsDelay3 = input.int(3, "Min Bars: Trade 2 ➔ Trade 3", minval = 1, group = "Scale-In Timing")
fastEmaLen = input.int(9, "Fast EMA Length", minval = 1, group = "Trend Filters")
slowEmaLen = input.int(21, "Slow EMA Length", minval = 2, group = "Trend Filters")
useCorrFilter = input.bool(true, "Require Correct Correlation", group = "Smart Correlation Engine")
overrideBench = input.bool(false, "Enable Manual Benchmark Override", group = "Smart Correlation Engine")
manualSymbol = input.symbol("TVC:DXY", "Manual Benchmark Symbol", group = "Smart Correlation Engine")
manualInverse = input.bool(true, "Manual Benchmark Is Inverse Correlation", group = "Smart Correlation Engine", tooltip = "ON: benchmark moving toward asset's SL direction = Invalid. OFF: benchmark moving toward asset's SL direction (same direction as asset) = Invalid.")
corrEmaLen = input.int(9, "Benchmark EMA Length", minval = 1, group = "Smart Correlation Engine")
corrConfBars = input.int(3, "Benchmark Confirmation Bars", minval = 1, tooltip="Bars required to sustain cross before validating structure", group = "Smart Correlation Engine")
gracePeriodBars = input.int(6, "Structure Grace Period (Bars)", minval = 1, tooltip = "Brief misalignment within this window does not penalize the score or flip the HUD label", group = "Scoring Persistence Engine")
maxRampBars = input.int(12, "Bars To Reach Max Bonus / Penalty", minval = 1, tooltip = "How long sustained alignment/misalignment must persist to reach full bonus/penalty", group = "Scoring Persistence Engine")
autoScaleRisk = input.bool(true, "Auto-Adapt TP/SL Bounds to Asset Scale", group = "Fixed Dynamic Risk Engine")
manualMinTp = input.float(3.0, "Manual Min TP Target", minval = 0.1, group = "Fixed Dynamic Risk Engine")
manualMaxTp = input.float(8.0, "Manual Max TP Target", minval = 0.1, group = "Fixed Dynamic Risk Engine")
manualMinSl = input.float(8.0, "Manual Min SL Target", minval = 0.1, group = "Fixed Dynamic Risk Engine")
manualMaxSl = input.float(15.0, "Manual Max SL Target", minval = 0.1, group = "Fixed Dynamic Risk Engine")
tpAtrMult = input.float(1.3, "TP ATR Multiplier", minval = 0.5, step = 0.1, group = "Fixed Dynamic Risk Engine")
slAtrMult = input.float(2.2, "SL ATR Multiplier", minval = 0.5, step = 0.1, group = "Fixed Dynamic Risk Engine")
riskRewardRatio = input.float(1.0, "Risk-to-Reward Ratio", minval = 0.1, group = "Fixed Dynamic Risk Engine")
buyBgColor = input.color(#4CAF50, "BUY Background Color", group = "Visual Customization")
buyTextColor = input.color(color.white, "BUY Text Color", group = "Visual Customization")
sellBgColor = input.color(#FF5252, "SELL Background Color", group = "Visual Customization")
sellTextColor = input.color(color.white, "SELL Text Color", group = "Visual Customization")
hudPos = input.string("Bottom Right", "HUD Screen Position", options=["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group = "Visual Customization")
getTablePosition(string pos) =>
switch pos
"Top Left" => position.top_left
"Top Right" => position.top_right
"Bottom Left" => position.bottom_left
"Bottom Right" => position.bottom_right
=> position.bottom_right
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 🌐 UNIVERSAL HANDS-FREE BENCHMARK ROUTER
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Returns [benchmarkSymbol, isInverseRelationship]
// isInverseRelationship = true -> benchmark moving UP pushes asset toward SL if asset is a BUY
// (i.e. benchmark should move OPPOSITE the asset to favor TP)
// isInverseRelationship = false -> benchmark should move WITH the asset to favor TP
getUniversalInverseBenchmark() =>
string p = syminfo.prefix
string t = str.upper(syminfo.ticker)
string base = syminfo.basecurrency
string quote = syminfo.currency
string symbol = "TVC:DXY"
bool inverse = true
if t == "USDX" or t == "DXY" or t == "USDOLLAR" or t == "DX"
symbol := "TVC:GOLD"
inverse := true
else if p == "NSE" or p == "BSE" or quote == "INR"
symbol := "NSE:INDIAVIX"
inverse := true
else if str.contains(t, "SPX") or str.contains(t, "US500") or str.contains(t, "SP500") or str.contains(t, "NDX") or str.contains(t, "US100") or str.contains(t, "NAS100") or str.contains(t, "US30") or str.contains(t, "DJI") or str.contains(t, "GER") or str.contains(t, "DAX") or str.contains(t, "UK100") or str.contains(t, "EU50") or p == "NASDAQ" or p == "NYSE"
symbol := "TVC:VIX"
inverse := true
else if base == "USD"
if quote == "JPY"
symbol := "TVC:JXY"
inverse := true
else if quote == "CAD"
symbol := "TVC:CXY"
inverse := true
else if quote == "CHF"
symbol := "TVC:SXY"
inverse := true
else
// USD is the BASE here (USDSEK, USDMXN, USDZAR, USDTRY, USDHKD, etc.)
// DXY moves WITH these pairs, not against them — DIRECT correlation
symbol := "TVC:DXY"
inverse := false
else if quote == "USD"
symbol := "TVC:DXY"
inverse := true
else if quote == "JPY"
symbol := "TVC:JXY"
inverse := true
else if quote == "GBP"
symbol := "TVC:BXY"
inverse := true
else if quote == "EUR"
symbol := "TVC:EXY"
inverse := true
[symbol, inverse]
getBenchmarkDisplayName(string symbol) =>
switch symbol
"NSE:INDIAVIX" => "India VIX"
"TVC:VIX" => "VIX"
"TVC:DXY" => "DXY"
"TVC:GOLD" => "Gold"
"TVC:JXY" => "Yen Index"
"TVC:BXY" => "Pound Index"
"TVC:EXY" => "Euro Index"
"TVC:CXY" => "CAD Index"
"TVC:SXY" => "CHF Index"
=> symbol
[routedBenchSymbol, routedBenchInverse] = getUniversalInverseBenchmark()
string activeBenchSymbol = overrideBench ? manualSymbol : routedBenchSymbol
bool activeBenchInverse = overrideBench ? manualInverse : routedBenchInverse
string activeBenchName = getBenchmarkDisplayName(activeBenchSymbol)
float autoMinTp = close * 0.0008
float autoMaxTp = close * 0.0025
float autoMinSl = close * 0.0018
float autoMaxSl = close * 0.0045
float minTpPoints = autoScaleRisk ? autoMinTp : manualMinTp
float maxTpPoints = autoScaleRisk ? autoMaxTp : manualMaxTp
float minSlPoints = autoScaleRisk ? autoMinSl : manualMinSl
float maxSlPoints = autoScaleRisk ? autoMaxSl : manualMaxSl
float minScaleDist = autoScaleRisk ? (close * 0.0005) : 2.0
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 🌙 CRYPTO DXY SCHEDULE & STANDARD STALL DETECTOR
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
bool isCrypto = syminfo.type == "crypto" or str.contains(syminfo.ticker, "BTC") or str.contains(syminfo.ticker, "ETH")
int istTime = time + 19800000
int dayOfWeekIST = dayofweek(istTime, timezone = "UTC")
int hourIST = hour(istTime, timezone = "UTC")
bool isCryptoWeekdayOffline = (dayOfWeekIST >= 2 and dayOfWeekIST <= 6) and (hourIST >= 22 and hourIST < 23)
bool isCryptoWeekendOffline = (dayOfWeekIST == 6 and hourIST >= 22) or (dayOfWeekIST == 7) or (dayOfWeekIST == 1 and hourIST < 23)
bool isCryptoDxyOffline = not replayMode and isCrypto and (isCryptoWeekdayOffline or isCryptoWeekendOffline)
bool isMarketClosedRT = not replayMode and not isCrypto and barstate.isrealtime and barstate.islast and (timenow > time_close + 900000)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 📊 CORE ENGINE & SUSTAINED BENCHMARK LOGIC
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
src = close
atrVal = ta.atr(atrPeriod)
atrAvg = ta.sma(atrVal, 30)
atrLoss = keyValue * atrVal
var float utStop = na
prevStop = nz(utStop[1], src)
if src > prevStop and src[1] > prevStop
utStop := math.max(prevStop, src - atrLoss)
else if src < prevStop and src[1] < prevStop
utStop := math.min(prevStop, src + atrLoss)
else
utStop := src > prevStop ? src - atrLoss : src + atrLoss
signalEma = ta.ema(src, 1)
utBuy = ta.crossover(signalEma, utStop) and src > utStop
utSell = ta.crossover(utStop, signalEma) and src < utStop
fastEma = ta.ema(close, fastEmaLen)
slowEma = ta.ema(close, slowEmaLen)
rawBenchClose = request.security(activeBenchSymbol, timeframe.period, close, barmerge.gaps_off, barmerge.lookahead_off)
rawBenchEma = request.security(activeBenchSymbol, timeframe.period, ta.ema(close, corrEmaLen), barmerge.gaps_off, barmerge.lookahead_off)
benchClose = nz(rawBenchClose, close)
benchEma = nz(rawBenchEma, ta.ema(close, corrEmaLen))
bool rawBenchBullish = benchClose > benchEma
bool rawBenchBearish = benchClose < benchEma
bool benchBullish = ta.lowest(rawBenchBullish ? 1 : 0, corrConfBars) == 1
bool benchBearish = ta.lowest(rawBenchBearish ? 1 : 0, corrConfBars) == 1
// benchFavorsBuy/Sell = benchmark is moving in the direction that supports price
// heading toward TP (not SL) for the current trade direction.
bool benchFavorsBuy = activeBenchInverse ? benchBearish : benchBullish
bool benchFavorsSell = activeBenchInverse ? benchBullish : benchBearish
bool corrBuyConfirm = not useCorrFilter or isCryptoDxyOffline or benchFavorsBuy
bool corrSellConfirm = not useCorrFilter or isCryptoDxyOffline or benchFavorsSell
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 🧠 STATE MACHINE & MULTI-TRADE SCALE-IN LOGIC
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var int activeTrend = 0
var int barTrade1 = na
var int barTrade2 = na
var int barTrade3 = na
var float priceTrade1 = na
var float priceTrade2 = na
var float priceTrade3 = na
var bool trade2Sent = false
var bool trade3Sent = false
var bool sawPullback2 = false
var bool sawPullback3 = false
if utBuy
activeTrend := 1
barTrade1 := bar_index
priceTrade1 := close
barTrade2 := na
barTrade3 := na
trade2Sent := false
trade3Sent := false
sawPullback2 := false
sawPullback3 := false
else if utSell
activeTrend := -1
barTrade1 := bar_index
priceTrade1 := close
barTrade2 := na
barTrade3 := na
trade2Sent := false
trade3Sent := false
sawPullback2 := false
sawPullback3 := false
if activeTrend == 1
if not trade2Sent and (close < open or low <= fastEma)
sawPullback2 := true
if trade2Sent and not trade3Sent and (close < open or low <= fastEma)
sawPullback3 := true
if activeTrend == -1
if not trade2Sent and (close > open or high >= fastEma)
sawPullback2 := true
if trade2Sent and not trade3Sent and (close > open or high >= fastEma)
sawPullback3 := true
readyTrade2 = not na(barTrade1) and (bar_index - barTrade1 >= minBarsDelay2)
readyTrade3 = not na(barTrade2) and (bar_index - barTrade2 >= minBarsDelay3)
distFromTrade1 = na(priceTrade1) ? 9999.0 : math.abs(close - priceTrade1)
distFromTrade2 = na(priceTrade2) ? 9999.0 : math.abs(close - priceTrade2)
buyTrigger2 = (sawPullback2 or bar_index - barTrade1 >= minBarsDelay2 + 2) and close > open and close > fastEma and distFromTrade1 >= minScaleDist and corrBuyConfirm
sellTrigger2 = (sawPullback2 or bar_index - barTrade1 >= minBarsDelay2 + 2) and close < open and close < fastEma and distFromTrade1 >= minScaleDist and corrSellConfirm
buyTrigger3 = (sawPullback3 or bar_index - barTrade2 >= minBarsDelay3 + 2) and close > open and close > fastEma and distFromTrade2 >= minScaleDist and corrBuyConfirm
sellTrigger3 = (sawPullback3 or bar_index - barTrade2 >= minBarsDelay3 + 2) and close < open and close < fastEma and distFromTrade2 >= minScaleDist and corrSellConfirm
buy1 = utBuy
sell1 = utSell
buy2Cond = activeTrend == 1 and readyTrade2 and not trade2Sent and buyTrigger2
sell2Cond = activeTrend == -1 and readyTrade2 and not trade2Sent and sellTrigger2
if buy2Cond or sell2Cond
trade2Sent := true
barTrade2 := bar_index
priceTrade2 := close
buy2 = enableTrade2 and buy2Cond
sell2 = enableTrade2 and sell2Cond
buy3Cond = activeTrend == 1 and trade2Sent and readyTrade3 and not trade3Sent and buyTrigger3
sell3Cond = activeTrend == -1 and trade2Sent and readyTrade3 and not trade3Sent and sellTrigger3
if buy3Cond or sell3Cond
trade3Sent := true
barTrade3 := bar_index
priceTrade3 := close
buy3 = enableTrade3 and buy3Cond
sell3 = enableTrade3 and sell3Cond
newBuySignal = buy1 or buy2 or buy3
newSellSignal = sell1 or sell2 or sell3
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 🔒 MULTI-TIER PERSISTENT FIXED RISK ENGINE
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var bool tradeActive = false
var string activeSignalTag = "WAITING"
var float lockedEntry = na
var float lockedTpPrice = na
var float lockedSlPrice = na
var bool activeIsBuy = false
var string lastTpText = "---"
var string lastSlText = "---"
var int benchConfirmStreak = 0
var int benchInvalidStreak = 0
var int trendConfirmStreak = 0
var int trendInvalidStreak = 0
var line lineSL = na
var line lineTP = na
var label labelSL = na
var label labelTP = na
string masterAlertMsg = ""
if (newBuySignal or newSellSignal) and (barstate.isconfirmed or replayMode)
activeIsBuy := newBuySignal
volatilityRatio = atrVal / atrAvg
rawTpDist = atrVal * tpAtrMult * volatilityRatio
rawSlDist = atrVal * slAtrMult
finalTpDist = math.min(math.max(rawTpDist, minTpPoints), maxTpPoints)
finalSlDist = math.min(math.max(rawSlDist, minSlPoints), maxSlPoints)
lockedEntry := close
lockedTpPrice := activeIsBuy ? close + finalTpDist : close - finalTpDist
lockedSlPrice := activeIsBuy ? close - finalSlDist : close + finalSlDist
string tag = buy1 ? "BUY" : (buy2 ? "BUY 2" : (buy3 ? "BUY 3" : (sell1 ? "SELL" : (sell2 ? "SELL 2" : (sell3 ? "SELL 3" : "")))))
activeSignalTag := tag + " ACTIVE"
tradeActive := true
lastTpText := "---"
lastSlText := "---"
benchConfirmStreak := 0
benchInvalidStreak := 0
trendConfirmStreak := 0
trendInvalidStreak := 0
masterAlertMsg := "🚀 " + syminfo.ticker + " New " + tag + " Active!\n📌 Entry: " + str.tostring(lockedEntry, "#.##") + "\n🎯 TP: " + str.tostring(lockedTpPrice, "#.##") + "\n🛡️ SL: " + str.tostring(lockedSlPrice, "#.##")
if not na(lineSL)
line.delete(lineSL)
if not na(lineTP)
line.delete(lineTP)
if not na(labelSL)
label.delete(labelSL)
if not na(labelTP)
label.delete(labelTP)
int endX = bar_index + 12
lineSL := line.new(bar_index, lockedSlPrice, endX, lockedSlPrice, color = color.red, width = 2, style = line.style_dashed)
labelSL := label.new(endX, lockedSlPrice, tag + " SL: " + str.tostring(lockedSlPrice, "#.##"), style = label.style_label_left, color = color.red, textcolor = color.white, size = size.tiny)
lineTP := line.new(bar_index, lockedTpPrice, endX, lockedTpPrice, color = color.green, width = 2, style = line.style_dashed)
labelTP := label.new(endX, lockedTpPrice, tag + " TP: " + str.tostring(lockedTpPrice, "#.##"), style = label.style_label_left, color = color.green, textcolor = color.white, size = size.tiny)
if tradeActive and not na(lockedTpPrice) and not na(lockedSlPrice)
bool tpHit = activeIsBuy ? (high >= lockedTpPrice) : (low <= lockedTpPrice)
bool slHit = activeIsBuy ? (low <= lockedSlPrice) : (high >= lockedSlPrice)
if tpHit or slHit
if tpHit
float tpDistance = math.abs(lockedTpPrice - lockedEntry)
activeSignalTag := "TP HIT 🎉"
lastTpText := "Hit (+ " + str.tostring(tpDistance, "#.##") + ")"
masterAlertMsg := "🎯 " + syminfo.ticker + " Take Profit Hit!\n✨ Reached Target (+ " + str.tostring(tpDistance, "#.##") + " pts)"
else if slHit
float slDistance = math.abs(lockedSlPrice - lockedEntry)
activeSignalTag := "SL HIT ❌"
lastSlText := "Hit (- " + str.tostring(slDistance, "#.##") + ")"
masterAlertMsg := "🛑 " + syminfo.ticker + " Stop Loss Hit!\n⚠️ Breached (- " + str.tostring(slDistance, "#.##") + " pts)"
tradeActive := false
if not na(lineSL)
line.delete(lineSL)
lineSL := na
if not na(lineTP)
line.delete(lineTP)
lineTP := na
if not na(labelSL)
label.delete(labelSL)
labelSL := na
if not na(labelTP)
label.delete(labelTP)
labelTP := na
if masterAlertMsg != ""
alert(masterAlertMsg, alert.freq_once_per_bar)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 🧠 UPGRADED PROBABILITY & BENCHMARK SCORING ENGINE
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
bool isCorrelationConfirmedNow = (activeIsBuy and benchFavorsBuy) or (not activeIsBuy and benchFavorsSell)
bool isTrendAlignedNow = (activeIsBuy and close > slowEma) or (not activeIsBuy and close < slowEma)
if tradeActive
if isCorrelationConfirmedNow
benchConfirmStreak := benchConfirmStreak + 1
benchInvalidStreak := 0
else
benchInvalidStreak := benchInvalidStreak + 1
benchConfirmStreak := 0
if isTrendAlignedNow
trendConfirmStreak := trendConfirmStreak + 1
trendInvalidStreak := 0
else
trendInvalidStreak := trendInvalidStreak + 1
trendConfirmStreak := 0
f_persistBonus(confirmStreak, invalidStreak, maxBonus, grace, rampBars) =>
float bonus = 0.0
if confirmStreak > 0
float rampProgress = math.min(confirmStreak, rampBars) / rampBars
bonus := (maxBonus * 0.5) + (maxBonus * 0.5) * rampProgress
else if invalidStreak > grace
float decayBars = invalidStreak - grace
bonus := -math.min(maxBonus, maxBonus * (decayBars / rampBars))
bonus
float rawBenchBonus = isCryptoDxyOffline ? 0.0 : f_persistBonus(benchConfirmStreak, benchInvalidStreak, 10.0, gracePeriodBars, maxRampBars)
float rawTrendBonus = f_persistBonus(trendConfirmStreak, trendInvalidStreak, 6.0, gracePeriodBars, maxRampBars)
float smoothTrendBonus = ta.sma(rawTrendBonus, 5)
float smoothBenchBonus = ta.sma(rawBenchBonus, 5)
float currentProgress = tradeActive and not na(lockedTpPrice) and not na(lockedSlPrice) ? (activeIsBuy ? ((close - lockedSlPrice) / (lockedTpPrice - lockedSlPrice)) : ((lockedSlPrice - close) / (lockedSlPrice - lockedTpPrice))) : 0.0
float smoothedProg = ta.sma(currentProgress, 2)
float tpOdds = na
if activeSignalTag == "TP HIT 🎉"
tpOdds := 100.0
else if activeSignalTag == "SL HIT ❌"
tpOdds := 0.0
else if tradeActive and not na(lockedTpPrice) and not na(lockedSlPrice)
float distOdds = math.min(100.0, math.max(0.0, smoothedProg * 100.0))
tpOdds := math.min(99.0, math.max(1.0, (distOdds * 0.80) + 10.0 + smoothTrendBonus + smoothBenchBonus))
// HUD label uses the same streaks as the odds score — only flips to Invalid once
// the benchmark has pushed toward SL for longer than the grace period, and flips
// back to Confirmed as soon as it favors TP again.
bool isCorrelationConfirmedForLabel = benchConfirmStreak > 0 or benchInvalidStreak <= gracePeriodBars
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 📱 DYNAMIC HUD TABLE RENDERER
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
var table hud = table.new(position = getTablePosition(hudPos), columns = 2, rows = 7, bgcolor = color.rgb(18, 18, 24, 10), border_color = color.rgb(40, 40, 50), border_width = 1)
if barstate.islast or replayMode
color headerBg = color.rgb(25, 30, 45)
color rowBg1 = color.rgb(20, 20, 28)
color rowBg2 = color.rgb(14, 14, 20)
color baseBg = color.rgb(8, 8, 12)
bool hasHitTargetOrStop = (activeSignalTag == "TP HIT 🎉" or activeSignalTag == "SL HIT ❌")
string topHeaderStatus = isMarketClosedRT ? "CLOSED 💤" : (hasHitTargetOrStop ? "Waiting ⏳" : activeSignalTag)
color topHeaderColor = isMarketClosedRT ? color.rgb(150, 150, 160) : (hasHitTargetOrStop ? color.rgb(241, 196, 15) : (str.contains(activeSignalTag, "BUY") ? color.rgb(46, 204, 113) : color.rgb(231, 76, 60)))
string correlStatus = isCryptoDxyOffline ? "CLOSED 💤" : (isMarketClosedRT ? "CLOSED 💤" : (activeSignalTag == "WAITING" ? "Ready ⏳" : (isCorrelationConfirmedForLabel ? "Confirmed ✅" : "Invalid ❌")))
table.cell(hud, 0, 0, "Open Legacy (" + syminfo.ticker + ")", text_color=color.rgb(220, 225, 235), bgcolor=headerBg, text_size=size.small)
table.cell(hud, 1, 0, topHeaderStatus, text_color=topHeaderColor, bgcolor=headerBg, text_size=size.small)
string statusOddsVal = isMarketClosedRT ? "Market Closed 💤" : (hasHitTargetOrStop ? "Waiting ⏳" : (activeSignalTag == "WAITING" ? "Waiting..." : str.tostring(tpOdds, "#.##") + "%"))
string fixedEntryVal = hasHitTargetOrStop ? "Waiting ⏳" : (na(lockedEntry) ? "Waiting..." : str.tostring(lockedEntry, "#.##"))
string takeProfitVal = hasHitTargetOrStop ? "Waiting ⏳" : (lastTpText != "---" ? lastTpText : (na(lockedTpPrice) ? "Waiting..." : str.tostring(lockedTpPrice, "#.##")))
string stopLossVal = hasHitTargetOrStop ? "Waiting ⏳" : (lastSlText != "---" ? lastSlText : (na(lockedSlPrice) ? "Waiting..." : str.tostring(lockedSlPrice, "#.##")))
string benchStructVal = correlStatus
table.cell(hud, 0, 1, "🎯 Status / Odds", text_color=color.rgb(180, 185, 195), bgcolor=rowBg1, text_size=size.small)
table.cell(hud, 1, 1, statusOddsVal, text_color=isMarketClosedRT ? color.rgb(150, 150, 160) : (hasHitTargetOrStop ? color.rgb(241, 196, 15) : color.rgb(241, 196, 15)), bgcolor=rowBg1, text_size=size.small)
table.cell(hud, 0, 2, "📌 Fixed Entry", text_color=color.rgb(180, 185, 195), bgcolor=rowBg2, text_size=size.small)
table.cell(hud, 1, 2, fixedEntryVal, text_color=color.white, bgcolor=rowBg2, text_size=size.small)
table.cell(hud, 0, 3, "🚀 Take Profit", text_color=color.rgb(180, 185, 195), bgcolor=rowBg1, text_size=size.small)
table.cell(hud, 1, 3, takeProfitVal, text_color=color.rgb(46, 204, 113), bgcolor=rowBg1, text_size=size.small)
table.cell(hud, 0, 4, "🛡️ Stop Loss", text_color=color.rgb(180, 185, 195), bgcolor=rowBg2, text_size=size.small)
table.cell(hud, 1, 4, stopLossVal, text_color=color.rgb(231, 76, 60), bgcolor=rowBg2, text_size=size.small)
table.cell(hud, 0, 5, "🌐 " + activeBenchName + " Structure", text_color=color.rgb(200, 210, 230), bgcolor=rowBg1, text_size=size.small)
table.cell(hud, 1, 5, benchStructVal, text_color=isCryptoDxyOffline ? color.rgb(150, 150, 160) : (isMarketClosedRT ? color.rgb(150, 150, 160) : (correlStatus == "Ready ⏳" ? color.rgb(150, 150, 160) : (isCorrelationConfirmedForLabel ? color.rgb(46, 204, 113) : color.rgb(231, 76, 60)))), bgcolor=rowBg1, text_size=size.small)
table.cell(hud, 0, 6, "", text_color=color.rgb(0,0,0,100), bgcolor=baseBg, text_size=size.small)
table.cell(hud, 1, 6, "", text_color=color.rgb(0,0,0,100), bgcolor=baseBg, text_size=size.small)
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// 🎨 UNIFIED PLOTSHAPES
//━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
plotshape(buy1, title = "BUY Label", style = shape.labelup, location = location.belowbar, color = buyBgColor, text = "BUY", textcolor = buyTextColor, size = size.small)
plotshape(sell1, title = "SELL Label", style = shape.labeldown, location = location.abovebar, color = sellBgColor, text = "SELL", textcolor = sellTextColor, size = size.small)
plotshape(buy2, title = "BUY 2 Label", style = shape.labelup, location = location.belowbar, color = buyBgColor, text = "BUY 2", textcolor = buyTextColor, size = size.small)
plotshape(sell2, title = "SELL 2 Label", style = shape.labeldown, location = location.abovebar, color = sellBgColor, text = "SELL 2", textcolor = sellTextColor, size = size.small)
plotshape(buy3, title = "BUY 3 Label", style = shape.labelup, location = location.belowbar, color = buyBgColor, text = "BUY 3", textcolor = buyTextColor, size = size.small)
plotshape(sell3, title = "SELL 3 Label", style = shape.labeldown, location = location.abovebar, color = sellBgColor, text = "SELL 3", textcolor = sellTextColor, size = size.small)
//@version=6
indicator("Galaxy Theme", overlay=true)
// Create a 10x10 grid. The table background is 100% transparent so your candles remain completely visible!
var table space = table.new(position.top_left, 10, 10, bgcolor=color.new(color.black, 100), border_width=0)
// Deterministic seeds so the stars stay perfectly still and don't flicker when the price moves
var int seed1 = 17
var int seed2 = 23
if barstate.islast
for r = 0 to 9
for c = 0 to 9
// Complex pseudo-randomization for a highly natural scatter effect
int starRand = (r * seed1 + c * seed2) % 12
string stardust = ""
color starColor = color.new(color.white, 100)
string tSize = size.small
// Organic placement using varied line breaks (\n) and spacing so it doesn't look like a grid
if starRand == 0
stardust := "✦"
starColor := color.new(#babbff, 30) // Soft glowing purple
tSize := size.normal
else if starRand == 1
stardust := " \n\n ★"
starColor := color.new(color.white, 50)
else if starRand == 2
stardust := " ·"
starColor := color.new(#ffd1ff, 30) // Faint pink nebula dust
else if starRand == 3
stardust := " \n ✦ \n "
starColor := color.new(#9bf6ff, 30) // Distant cyan twinkle
else if starRand == 5
stardust := "★\n\n"
starColor := color.new(color.white, 60)
tSize := size.tiny
else if starRand == 7
stardust := " · "
starColor := color.new(#c8b6ff, 40) // Lavender
else
// Leave empty cells to create the dark void of deep space
stardust := ""
// THE MAGIC TRICK: width=10.0 and height=10.0 forces each cell to stretch.
// 10 cells x 10% = exactly 100% full screen coverage edge-to-edge!
table.cell(space, c, r, text=stardust, text_color=starColor, text_size=tSize, width=10.0, height=10.0)
Finish the look
Galaxy Background Setup
Galaxy Theme draws the stars — this is the last step to get the deep-space look behind them, straight from TradingView's own chart settings.
Open chart settings
Click the gear icon (⚙️) at the top of your chart, or right-click anywhere on the chart and choose Settings.
Go to the "Canvas" tab
This is where TradingView controls the chart's overall background.
Set Background to "Gradient"
You'll see a background style dropdown — switch it from Solid to Gradient. Two color swatches will appear next to it.
Set the two gradient colors
Set the first color swatch to a dark purple, and the second to pure black. This gives the chart a deep-space fade that Galaxy Theme's stars sit beautifully on top of.