OPEN-SOURCE SCRIPT
Basee

//version=6
indicator("Trade, Travel, Talk AI DATA — Base M5 v1", shorttitle="TTT Base M5", overlay=true, max_boxes_count=300, max_labels_count=50)
// ─────────────────────────────────────────────────────────────────────────────
// Trade, Travel, Talk AI DATA — Base M5
// Detects a compact 1–3 candle base immediately before displacement + BOS.
// Default zone boundaries use the complete wick range (High–Low).
// Designed for XAUUSD M5. Use alerts only after confirming the symbol/session.
// ─────────────────────────────────────────────────────────────────────────────
groupDetect = "1) Base Detection"
enforceM5 = input.bool(true, "ทำงานเฉพาะกราฟ M5", group=groupDetect)
maxBaseBars = input.int(3, "จำนวนแท่ง Base สูงสุด", minval=1, maxval=5, group=groupDetect)
impulseBars = input.int(2, "จำนวนแท่ง Impulse ที่ใช้ยืนยัน", minval=1, maxval=4, group=groupDetect)
atrLength = input.int(14, "ATR Length", minval=2, group=groupDetect)
impulseAtr = input.float(1.20, "แรง Impulse รวมขั้นต่ำ (ATR)", minval=0.20, step=0.05, group=groupDetect)
maxBaseBodyAtr = input.float(0.55, "Body ของ Base สูงสุด (ATR)", minval=0.05, step=0.05, group=groupDetect)
maxBaseRangeAtr = input.float(1.00, "ความกว้าง Base สูงสุด (ATR)", minval=0.10, step=0.05, group=groupDetect)
zoneBoundary = input.string("Wick", "ขอบเขต Base", options=["Wick", "Body"], group=groupDetect)
requireOpposite = input.bool(true, "แท่ง Base ใกล้ Impulse ต้องเป็นสีตรงข้าม", group=groupDetect)
requireSameImpulseColor = input.bool(true, "แท่ง Impulse ต้องไปทิศเดียวกันทั้งหมด", group=groupDetect)
confirmOnClose = input.bool(true, "ยืนยัน Base หลังแท่งปิด (ลด Repaint)", group=groupDetect)
groupStructure = "2) Structure Confirmation"
requireBos = input.bool(true, "ต้องมี BOS", group=groupStructure)
bosLookback = input.int(10, "BOS Lookback", minval=3, maxval=100, group=groupStructure)
fvgBonus = input.bool(true, "เพิ่มคะแนนเมื่อมี FVG", group=groupStructure)
groupLife = "3) Zone Lifecycle"
maxZones = input.int(24, "จำนวนโซนสูงสุด", minval=2, maxval=100, group=groupLife)
rightPaddingBars = input.int(8, "ยื่นกรอบเลยแท่งปัจจุบัน (แท่ง)", minval=1, maxval=100, group=groupLife)
invalidationMode = input.string("Close", "ยกเลิกโซนด้วย", options=["Close", "Wick"], group=groupLife)
invalidationAtrBuffer = input.float(0.05, "Invalidation Buffer (ATR)", minval=0.0, step=0.01, group=groupLife)
deleteInvalid = input.bool(true, "ลบโซนเมื่อ Invalid", group=groupLife)
fadeAfterFirstRetest = input.bool(true, "ลดสีหลัง First Retest", group=groupLife)
showScore = input.bool(true, "แสดงคะแนน Base", group=groupLife)
groupStyle = "4) Style"
buyBorder = input.color(color.rgb(70, 125, 255), "Buy Base Border", group=groupStyle)
sellBorder = input.color(color.rgb(70, 125, 255), "Sell Base Border", group=groupStyle)
buyFill = input.color(color.new(color.rgb(35, 60, 140), 72), "Buy Base Fill", group=groupStyle)
sellFill = input.color(color.new(color.rgb(35, 60, 140), 72), "Sell Base Fill", group=groupStyle)
testedFill = input.color(color.new(color.gray, 88), "Tested Base Fill", group=groupStyle)
showDashboard = input.bool(true, "แสดง Dashboard", group=groupStyle)
bool isM5 = timeframe.isminutes and timeframe.multiplier == 5
bool enabled = not enforceM5 or isM5
float atr = ta.atr(atrLength)
// Zone storage: direction 1 = Buy/Demand, -1 = Sell/Supply.
var box[] zoneBoxes = array.new_box()
var float[] zoneTops = array.new_float()
var float[] zoneBottoms = array.new_float()
var int[] zoneDirections = array.new_int()
var int[] zoneStates = array.new_int() // 0 Fresh, 1 Tested, 2 Invalid (when retained)
var int[] zoneCreatedBars = array.new_int()
var int[] zoneScores = array.new_int()
bool newBuyZone = false
bool newSellZone = false
bool buyRetest = false
bool sellRetest = false
bool buyInvalidated = false
bool sellInvalidated = false
// Confirm a displacement sequence after the base.
float impulseBodySum = 0.0
bool impulseAllBull = true
bool impulseAllBear = true
for j = 0 to impulseBars - 1
impulseBodySum += math.abs(close[j] - open[j])
impulseAllBull := impulseAllBull and close[j] > open[j]
impulseAllBear := impulseAllBear and close[j] < open[j]
bool enoughHistory = bar_index > impulseBars + maxBaseBars + bosLookback + 2
float priorHigh = ta.highest(high[impulseBars + maxBaseBars], bosLookback)
float priorLow = ta.lowest(low[impulseBars + maxBaseBars], bosLookback)
bool strongDisplacement = impulseBodySum >= atr * impulseAtr
bool bullImpulse = strongDisplacement and (not requireSameImpulseColor or impulseAllBull)
bool bearImpulse = strongDisplacement and (not requireSameImpulseColor or impulseAllBear)
// Grow a compact base from the candle nearest the impulse, up to maxBaseBars.
// A lone opposite candle naturally remains a one-candle base; a true compact
// cluster is retained as 2–3 candles instead of being cut down artificially.
int chosenBaseBars = 0
float candidateTop = na
float candidateBottom = na
if enabled and enoughHistory
for n = 1 to maxBaseBars
float tempTop = na
float tempBottom = na
bool bodiesCompact = true
for k = 0 to n - 1
int idx = impulseBars + k
float candleTop = zoneBoundary == "Wick" ? high[idx] : math.max(open[idx], close[idx])
float candleBottom = zoneBoundary == "Wick" ? low[idx] : math.min(open[idx], close[idx])
tempTop := na(tempTop) ? candleTop : math.max(tempTop, candleTop)
tempBottom := na(tempBottom) ? candleBottom : math.min(tempBottom, candleBottom)
bodiesCompact := bodiesCompact and math.abs(close[idx] - open[idx]) <= atr[idx] * maxBaseBodyAtr
bool rangeCompact = tempTop - tempBottom <= atr[impulseBars] * maxBaseRangeAtr
if bodiesCompact and rangeCompact
chosenBaseBars := n
candidateTop := tempTop
candidateBottom := tempBottom
bool bullBaseColor = not requireOpposite or close[impulseBars] <= open[impulseBars]
bool bearBaseColor = not requireOpposite or close[impulseBars] >= open[impulseBars]
bool bullBos = close > priorHigh
bool bearBos = close < priorLow
bool bullFvg = low > high[impulseBars]
bool bearFvg = high < low[impulseBars]
bool confirmationReady = not confirmOnClose or barstate.isconfirmed
bool rawBuySetup = confirmationReady and enabled and enoughHistory and chosenBaseBars > 0 and bullImpulse and bullBaseColor and close > candidateTop and (not requireBos or bullBos)
bool rawSellSetup = confirmationReady and enabled and enoughHistory and chosenBaseBars > 0 and bearImpulse and bearBaseColor and close < candidateBottom and (not requireBos or bearBos)
// Do not recreate an almost identical active zone.
bool duplicateBuy = false
bool duplicateSell = false
if array.size(zoneBoxes) > 0 and chosenBaseBars > 0
for i = 0 to array.size(zoneBoxes) - 1
float oldTop = array.get(zoneTops, i)
float oldBottom = array.get(zoneBottoms, i)
int oldDir = array.get(zoneDirections, i)
float tolerance = atr * 0.10
bool sameArea = math.abs(candidateTop - oldTop) <= tolerance and math.abs(candidateBottom - oldBottom) <= tolerance
duplicateBuy := duplicateBuy or (oldDir == 1 and sameArea)
duplicateSell := duplicateSell or (oldDir == -1 and sameArea)
if rawBuySetup and not duplicateBuy
int score = int(math.min(100.0, 35.0 + math.min(30.0, impulseBodySum / atr * 15.0) + (bullBos ? 20.0 : 0.0) + (fvgBonus and bullFvg ? 10.0 : 0.0) + (chosenBaseBars == 1 ? 5.0 : 0.0)))
string zoneText = "BUY BASE M5 • FRESH" + (showScore ? " • " + str.tostring(score) + "/100" : "")
box newBox = box.new(left=bar_index - (impulseBars + chosenBaseBars - 1), top=candidateTop, right=bar_index + rightPaddingBars, bottom=candidateBottom, xloc=xloc.bar_index, extend=extend.none, border_color=buyBorder, border_width=1, bgcolor=buyFill, text=zoneText, text_color=color.white, text_size=size.tiny, text_halign=text.align_right, text_valign=text.align_center)
array.push(zoneBoxes, newBox)
array.push(zoneTops, candidateTop)
array.push(zoneBottoms, candidateBottom)
array.push(zoneDirections, 1)
array.push(zoneStates, 0)
array.push(zoneCreatedBars, bar_index)
array.push(zoneScores, score)
newBuyZone := true
if rawSellSetup and not duplicateSell
int score = int(math.min(100.0, 35.0 + math.min(30.0, impulseBodySum / atr * 15.0) + (bearBos ? 20.0 : 0.0) + (fvgBonus and bearFvg ? 10.0 : 0.0) + (chosenBaseBars == 1 ? 5.0 : 0.0)))
string zoneText = "SELL BASE M5 • FRESH" + (showScore ? " • " + str.tostring(score) + "/100" : "")
box newBox = box.new(left=bar_index - (impulseBars + chosenBaseBars - 1), top=candidateTop, right=bar_index + rightPaddingBars, bottom=candidateBottom, xloc=xloc.bar_index, extend=extend.none, border_color=sellBorder, border_width=1, bgcolor=sellFill, text=zoneText, text_color=color.white, text_size=size.tiny, text_halign=text.align_right, text_valign=text.align_center)
array.push(zoneBoxes, newBox)
array.push(zoneTops, candidateTop)
array.push(zoneBottoms, candidateBottom)
array.push(zoneDirections, -1)
array.push(zoneStates, 0)
array.push(zoneCreatedBars, bar_index)
array.push(zoneScores, score)
newSellZone := true
// Keep chart objects within the user's selected limit.
while array.size(zoneBoxes) > maxZones
box oldest = array.shift(zoneBoxes)
box.delete(oldest)
array.shift(zoneTops)
array.shift(zoneBottoms)
array.shift(zoneDirections)
array.shift(zoneStates)
array.shift(zoneCreatedBars)
array.shift(zoneScores)
// Update Fresh/Tested/Invalid lifecycle.
if array.size(zoneBoxes) > 0
for i = array.size(zoneBoxes) - 1 to 0
box z = array.get(zoneBoxes, i)
float zTop = array.get(zoneTops, i)
float zBottom = array.get(zoneBottoms, i)
int zDir = array.get(zoneDirections, i)
int zState = array.get(zoneStates, i)
int zCreated = array.get(zoneCreatedBars, i)
int zScore = array.get(zoneScores, i)
float buffer = atr * invalidationAtrBuffer
bool invalidBuy = zState < 2 and zDir == 1 and (invalidationMode == "Close" ? close < zBottom - buffer : low < zBottom - buffer)
bool invalidSell = zState < 2 and zDir == -1 and (invalidationMode == "Close" ? close > zTop + buffer : high > zTop + buffer)
bool invalid = invalidBuy or invalidSell
if invalid
buyInvalidated := buyInvalidated or invalidBuy
sellInvalidated := sellInvalidated or invalidSell
if deleteInvalid
box.delete(z)
array.remove(zoneBoxes, i)
array.remove(zoneTops, i)
array.remove(zoneBottoms, i)
array.remove(zoneDirections, i)
array.remove(zoneStates, i)
array.remove(zoneCreatedBars, i)
array.remove(zoneScores, i)
else
array.set(zoneStates, i, 2)
box.set_extend(z, extend.none)
box.set_right(z, bar_index)
box.set_bgcolor(z, color.new(color.gray, 92))
box.set_border_color(z, color.new(color.gray, 65))
box.set_text(z, (zDir == 1 ? "BUY" : "SELL") + " BASE M5 • INVALID")
else
if zState < 2
box.set_right(z, bar_index + rightPaddingBars)
bool overlapsNow = zState < 2 and high >= zBottom and low <= zTop
bool overlapsPrev = high[1] >= zBottom and low[1] <= zTop
bool firstEntry = bar_index > zCreated and overlapsNow and not overlapsPrev
if zState == 0 and firstEntry
array.set(zoneStates, i, 1)
buyRetest := buyRetest or zDir == 1
sellRetest := sellRetest or zDir == -1
if fadeAfterFirstRetest
box.set_bgcolor(z, testedFill)
box.set_text(z, (zDir == 1 ? "BUY" : "SELL") + " BASE M5 • TESTED" + (showScore ? " • " + str.tostring(zScore) + "/100" : ""))
// Compact dashboard.
var table dashboard = table.new(position.top_right, 2, 4, border_width=1, border_color=color.new(color.aqua, 45))
if barstate.islast and showDashboard
int freshBuy = 0
int freshSell = 0
int tested = 0
if array.size(zoneBoxes) > 0
for i = 0 to array.size(zoneBoxes) - 1
int d = array.get(zoneDirections, i)
int s = array.get(zoneStates, i)
freshBuy += d == 1 and s == 0 ? 1 : 0
freshSell += d == -1 and s == 0 ? 1 : 0
tested += s == 1 ? 1 : 0
table.cell(dashboard, 0, 0, "TTT BASE M5", text_color=color.white, bgcolor=color.rgb(5, 30, 45))
table.cell(dashboard, 1, 0, isM5 ? "ACTIVE" : "USE M5", text_color=isM5 ? color.lime : color.yellow, bgcolor=color.rgb(5, 30, 45))
table.cell(dashboard, 0, 1, "Fresh Buy", text_color=color.white)
table.cell(dashboard, 1, 1, str.tostring(freshBuy), text_color=buyBorder)
table.cell(dashboard, 0, 2, "Fresh Sell", text_color=color.white)
table.cell(dashboard, 1, 2, str.tostring(freshSell), text_color=sellBorder)
table.cell(dashboard, 0, 3, "Tested", text_color=color.white)
table.cell(dashboard, 1, 3, str.tostring(tested), text_color=color.silver)
// Alert events. Create alerts from TradingView's Create Alert dialog.
alertcondition(newBuyZone, "New Buy Base M5", "TTT AI DATA: New BUY Base M5 detected")
alertcondition(newSellZone, "New Sell Base M5", "TTT AI DATA: New SELL Base M5 detected")
alertcondition(buyRetest, "Buy Base First Retest", "TTT AI DATA: Price entered a BUY Base M5 for the first retest")
alertcondition(sellRetest, "Sell Base First Retest", "TTT AI DATA: Price entered a SELL Base M5 for the first retest")
alertcondition(buyInvalidated, "Buy Base Invalidated", "TTT AI DATA: A BUY Base M5 was invalidated")
alertcondition(sellInvalidated, "Sell Base Invalidated", "TTT AI DATA: A SELL Base M5 was invalidated")
indicator("Trade, Travel, Talk AI DATA — Base M5 v1", shorttitle="TTT Base M5", overlay=true, max_boxes_count=300, max_labels_count=50)
// ─────────────────────────────────────────────────────────────────────────────
// Trade, Travel, Talk AI DATA — Base M5
// Detects a compact 1–3 candle base immediately before displacement + BOS.
// Default zone boundaries use the complete wick range (High–Low).
// Designed for XAUUSD M5. Use alerts only after confirming the symbol/session.
// ─────────────────────────────────────────────────────────────────────────────
groupDetect = "1) Base Detection"
enforceM5 = input.bool(true, "ทำงานเฉพาะกราฟ M5", group=groupDetect)
maxBaseBars = input.int(3, "จำนวนแท่ง Base สูงสุด", minval=1, maxval=5, group=groupDetect)
impulseBars = input.int(2, "จำนวนแท่ง Impulse ที่ใช้ยืนยัน", minval=1, maxval=4, group=groupDetect)
atrLength = input.int(14, "ATR Length", minval=2, group=groupDetect)
impulseAtr = input.float(1.20, "แรง Impulse รวมขั้นต่ำ (ATR)", minval=0.20, step=0.05, group=groupDetect)
maxBaseBodyAtr = input.float(0.55, "Body ของ Base สูงสุด (ATR)", minval=0.05, step=0.05, group=groupDetect)
maxBaseRangeAtr = input.float(1.00, "ความกว้าง Base สูงสุด (ATR)", minval=0.10, step=0.05, group=groupDetect)
zoneBoundary = input.string("Wick", "ขอบเขต Base", options=["Wick", "Body"], group=groupDetect)
requireOpposite = input.bool(true, "แท่ง Base ใกล้ Impulse ต้องเป็นสีตรงข้าม", group=groupDetect)
requireSameImpulseColor = input.bool(true, "แท่ง Impulse ต้องไปทิศเดียวกันทั้งหมด", group=groupDetect)
confirmOnClose = input.bool(true, "ยืนยัน Base หลังแท่งปิด (ลด Repaint)", group=groupDetect)
groupStructure = "2) Structure Confirmation"
requireBos = input.bool(true, "ต้องมี BOS", group=groupStructure)
bosLookback = input.int(10, "BOS Lookback", minval=3, maxval=100, group=groupStructure)
fvgBonus = input.bool(true, "เพิ่มคะแนนเมื่อมี FVG", group=groupStructure)
groupLife = "3) Zone Lifecycle"
maxZones = input.int(24, "จำนวนโซนสูงสุด", minval=2, maxval=100, group=groupLife)
rightPaddingBars = input.int(8, "ยื่นกรอบเลยแท่งปัจจุบัน (แท่ง)", minval=1, maxval=100, group=groupLife)
invalidationMode = input.string("Close", "ยกเลิกโซนด้วย", options=["Close", "Wick"], group=groupLife)
invalidationAtrBuffer = input.float(0.05, "Invalidation Buffer (ATR)", minval=0.0, step=0.01, group=groupLife)
deleteInvalid = input.bool(true, "ลบโซนเมื่อ Invalid", group=groupLife)
fadeAfterFirstRetest = input.bool(true, "ลดสีหลัง First Retest", group=groupLife)
showScore = input.bool(true, "แสดงคะแนน Base", group=groupLife)
groupStyle = "4) Style"
buyBorder = input.color(color.rgb(70, 125, 255), "Buy Base Border", group=groupStyle)
sellBorder = input.color(color.rgb(70, 125, 255), "Sell Base Border", group=groupStyle)
buyFill = input.color(color.new(color.rgb(35, 60, 140), 72), "Buy Base Fill", group=groupStyle)
sellFill = input.color(color.new(color.rgb(35, 60, 140), 72), "Sell Base Fill", group=groupStyle)
testedFill = input.color(color.new(color.gray, 88), "Tested Base Fill", group=groupStyle)
showDashboard = input.bool(true, "แสดง Dashboard", group=groupStyle)
bool isM5 = timeframe.isminutes and timeframe.multiplier == 5
bool enabled = not enforceM5 or isM5
float atr = ta.atr(atrLength)
// Zone storage: direction 1 = Buy/Demand, -1 = Sell/Supply.
var box[] zoneBoxes = array.new_box()
var float[] zoneTops = array.new_float()
var float[] zoneBottoms = array.new_float()
var int[] zoneDirections = array.new_int()
var int[] zoneStates = array.new_int() // 0 Fresh, 1 Tested, 2 Invalid (when retained)
var int[] zoneCreatedBars = array.new_int()
var int[] zoneScores = array.new_int()
bool newBuyZone = false
bool newSellZone = false
bool buyRetest = false
bool sellRetest = false
bool buyInvalidated = false
bool sellInvalidated = false
// Confirm a displacement sequence after the base.
float impulseBodySum = 0.0
bool impulseAllBull = true
bool impulseAllBear = true
for j = 0 to impulseBars - 1
impulseBodySum += math.abs(close[j] - open[j])
impulseAllBull := impulseAllBull and close[j] > open[j]
impulseAllBear := impulseAllBear and close[j] < open[j]
bool enoughHistory = bar_index > impulseBars + maxBaseBars + bosLookback + 2
float priorHigh = ta.highest(high[impulseBars + maxBaseBars], bosLookback)
float priorLow = ta.lowest(low[impulseBars + maxBaseBars], bosLookback)
bool strongDisplacement = impulseBodySum >= atr * impulseAtr
bool bullImpulse = strongDisplacement and (not requireSameImpulseColor or impulseAllBull)
bool bearImpulse = strongDisplacement and (not requireSameImpulseColor or impulseAllBear)
// Grow a compact base from the candle nearest the impulse, up to maxBaseBars.
// A lone opposite candle naturally remains a one-candle base; a true compact
// cluster is retained as 2–3 candles instead of being cut down artificially.
int chosenBaseBars = 0
float candidateTop = na
float candidateBottom = na
if enabled and enoughHistory
for n = 1 to maxBaseBars
float tempTop = na
float tempBottom = na
bool bodiesCompact = true
for k = 0 to n - 1
int idx = impulseBars + k
float candleTop = zoneBoundary == "Wick" ? high[idx] : math.max(open[idx], close[idx])
float candleBottom = zoneBoundary == "Wick" ? low[idx] : math.min(open[idx], close[idx])
tempTop := na(tempTop) ? candleTop : math.max(tempTop, candleTop)
tempBottom := na(tempBottom) ? candleBottom : math.min(tempBottom, candleBottom)
bodiesCompact := bodiesCompact and math.abs(close[idx] - open[idx]) <= atr[idx] * maxBaseBodyAtr
bool rangeCompact = tempTop - tempBottom <= atr[impulseBars] * maxBaseRangeAtr
if bodiesCompact and rangeCompact
chosenBaseBars := n
candidateTop := tempTop
candidateBottom := tempBottom
bool bullBaseColor = not requireOpposite or close[impulseBars] <= open[impulseBars]
bool bearBaseColor = not requireOpposite or close[impulseBars] >= open[impulseBars]
bool bullBos = close > priorHigh
bool bearBos = close < priorLow
bool bullFvg = low > high[impulseBars]
bool bearFvg = high < low[impulseBars]
bool confirmationReady = not confirmOnClose or barstate.isconfirmed
bool rawBuySetup = confirmationReady and enabled and enoughHistory and chosenBaseBars > 0 and bullImpulse and bullBaseColor and close > candidateTop and (not requireBos or bullBos)
bool rawSellSetup = confirmationReady and enabled and enoughHistory and chosenBaseBars > 0 and bearImpulse and bearBaseColor and close < candidateBottom and (not requireBos or bearBos)
// Do not recreate an almost identical active zone.
bool duplicateBuy = false
bool duplicateSell = false
if array.size(zoneBoxes) > 0 and chosenBaseBars > 0
for i = 0 to array.size(zoneBoxes) - 1
float oldTop = array.get(zoneTops, i)
float oldBottom = array.get(zoneBottoms, i)
int oldDir = array.get(zoneDirections, i)
float tolerance = atr * 0.10
bool sameArea = math.abs(candidateTop - oldTop) <= tolerance and math.abs(candidateBottom - oldBottom) <= tolerance
duplicateBuy := duplicateBuy or (oldDir == 1 and sameArea)
duplicateSell := duplicateSell or (oldDir == -1 and sameArea)
if rawBuySetup and not duplicateBuy
int score = int(math.min(100.0, 35.0 + math.min(30.0, impulseBodySum / atr * 15.0) + (bullBos ? 20.0 : 0.0) + (fvgBonus and bullFvg ? 10.0 : 0.0) + (chosenBaseBars == 1 ? 5.0 : 0.0)))
string zoneText = "BUY BASE M5 • FRESH" + (showScore ? " • " + str.tostring(score) + "/100" : "")
box newBox = box.new(left=bar_index - (impulseBars + chosenBaseBars - 1), top=candidateTop, right=bar_index + rightPaddingBars, bottom=candidateBottom, xloc=xloc.bar_index, extend=extend.none, border_color=buyBorder, border_width=1, bgcolor=buyFill, text=zoneText, text_color=color.white, text_size=size.tiny, text_halign=text.align_right, text_valign=text.align_center)
array.push(zoneBoxes, newBox)
array.push(zoneTops, candidateTop)
array.push(zoneBottoms, candidateBottom)
array.push(zoneDirections, 1)
array.push(zoneStates, 0)
array.push(zoneCreatedBars, bar_index)
array.push(zoneScores, score)
newBuyZone := true
if rawSellSetup and not duplicateSell
int score = int(math.min(100.0, 35.0 + math.min(30.0, impulseBodySum / atr * 15.0) + (bearBos ? 20.0 : 0.0) + (fvgBonus and bearFvg ? 10.0 : 0.0) + (chosenBaseBars == 1 ? 5.0 : 0.0)))
string zoneText = "SELL BASE M5 • FRESH" + (showScore ? " • " + str.tostring(score) + "/100" : "")
box newBox = box.new(left=bar_index - (impulseBars + chosenBaseBars - 1), top=candidateTop, right=bar_index + rightPaddingBars, bottom=candidateBottom, xloc=xloc.bar_index, extend=extend.none, border_color=sellBorder, border_width=1, bgcolor=sellFill, text=zoneText, text_color=color.white, text_size=size.tiny, text_halign=text.align_right, text_valign=text.align_center)
array.push(zoneBoxes, newBox)
array.push(zoneTops, candidateTop)
array.push(zoneBottoms, candidateBottom)
array.push(zoneDirections, -1)
array.push(zoneStates, 0)
array.push(zoneCreatedBars, bar_index)
array.push(zoneScores, score)
newSellZone := true
// Keep chart objects within the user's selected limit.
while array.size(zoneBoxes) > maxZones
box oldest = array.shift(zoneBoxes)
box.delete(oldest)
array.shift(zoneTops)
array.shift(zoneBottoms)
array.shift(zoneDirections)
array.shift(zoneStates)
array.shift(zoneCreatedBars)
array.shift(zoneScores)
// Update Fresh/Tested/Invalid lifecycle.
if array.size(zoneBoxes) > 0
for i = array.size(zoneBoxes) - 1 to 0
box z = array.get(zoneBoxes, i)
float zTop = array.get(zoneTops, i)
float zBottom = array.get(zoneBottoms, i)
int zDir = array.get(zoneDirections, i)
int zState = array.get(zoneStates, i)
int zCreated = array.get(zoneCreatedBars, i)
int zScore = array.get(zoneScores, i)
float buffer = atr * invalidationAtrBuffer
bool invalidBuy = zState < 2 and zDir == 1 and (invalidationMode == "Close" ? close < zBottom - buffer : low < zBottom - buffer)
bool invalidSell = zState < 2 and zDir == -1 and (invalidationMode == "Close" ? close > zTop + buffer : high > zTop + buffer)
bool invalid = invalidBuy or invalidSell
if invalid
buyInvalidated := buyInvalidated or invalidBuy
sellInvalidated := sellInvalidated or invalidSell
if deleteInvalid
box.delete(z)
array.remove(zoneBoxes, i)
array.remove(zoneTops, i)
array.remove(zoneBottoms, i)
array.remove(zoneDirections, i)
array.remove(zoneStates, i)
array.remove(zoneCreatedBars, i)
array.remove(zoneScores, i)
else
array.set(zoneStates, i, 2)
box.set_extend(z, extend.none)
box.set_right(z, bar_index)
box.set_bgcolor(z, color.new(color.gray, 92))
box.set_border_color(z, color.new(color.gray, 65))
box.set_text(z, (zDir == 1 ? "BUY" : "SELL") + " BASE M5 • INVALID")
else
if zState < 2
box.set_right(z, bar_index + rightPaddingBars)
bool overlapsNow = zState < 2 and high >= zBottom and low <= zTop
bool overlapsPrev = high[1] >= zBottom and low[1] <= zTop
bool firstEntry = bar_index > zCreated and overlapsNow and not overlapsPrev
if zState == 0 and firstEntry
array.set(zoneStates, i, 1)
buyRetest := buyRetest or zDir == 1
sellRetest := sellRetest or zDir == -1
if fadeAfterFirstRetest
box.set_bgcolor(z, testedFill)
box.set_text(z, (zDir == 1 ? "BUY" : "SELL") + " BASE M5 • TESTED" + (showScore ? " • " + str.tostring(zScore) + "/100" : ""))
// Compact dashboard.
var table dashboard = table.new(position.top_right, 2, 4, border_width=1, border_color=color.new(color.aqua, 45))
if barstate.islast and showDashboard
int freshBuy = 0
int freshSell = 0
int tested = 0
if array.size(zoneBoxes) > 0
for i = 0 to array.size(zoneBoxes) - 1
int d = array.get(zoneDirections, i)
int s = array.get(zoneStates, i)
freshBuy += d == 1 and s == 0 ? 1 : 0
freshSell += d == -1 and s == 0 ? 1 : 0
tested += s == 1 ? 1 : 0
table.cell(dashboard, 0, 0, "TTT BASE M5", text_color=color.white, bgcolor=color.rgb(5, 30, 45))
table.cell(dashboard, 1, 0, isM5 ? "ACTIVE" : "USE M5", text_color=isM5 ? color.lime : color.yellow, bgcolor=color.rgb(5, 30, 45))
table.cell(dashboard, 0, 1, "Fresh Buy", text_color=color.white)
table.cell(dashboard, 1, 1, str.tostring(freshBuy), text_color=buyBorder)
table.cell(dashboard, 0, 2, "Fresh Sell", text_color=color.white)
table.cell(dashboard, 1, 2, str.tostring(freshSell), text_color=sellBorder)
table.cell(dashboard, 0, 3, "Tested", text_color=color.white)
table.cell(dashboard, 1, 3, str.tostring(tested), text_color=color.silver)
// Alert events. Create alerts from TradingView's Create Alert dialog.
alertcondition(newBuyZone, "New Buy Base M5", "TTT AI DATA: New BUY Base M5 detected")
alertcondition(newSellZone, "New Sell Base M5", "TTT AI DATA: New SELL Base M5 detected")
alertcondition(buyRetest, "Buy Base First Retest", "TTT AI DATA: Price entered a BUY Base M5 for the first retest")
alertcondition(sellRetest, "Sell Base First Retest", "TTT AI DATA: Price entered a SELL Base M5 for the first retest")
alertcondition(buyInvalidated, "Buy Base Invalidated", "TTT AI DATA: A BUY Base M5 was invalidated")
alertcondition(sellInvalidated, "Sell Base Invalidated", "TTT AI DATA: A SELL Base M5 was invalidated")
Açık kaynak kodlu komut dosyası
Gerçek TradingView ruhuyla, bu komut dosyasının mimarı, yatırımcıların işlevselliğini inceleyip doğrulayabilmesi için onu açık kaynaklı hale getirdi. Yazarı tebrik ederiz! Ücretsiz olarak kullanabilseniz de, kodu yeniden yayınlamanın Topluluk Kurallarımıza tabi olduğunu unutmayın.
Feragatname
Bilgiler ve yayınlar, TradingView tarafından sağlanan veya onaylanan finansal, yatırım, alım satım veya diğer türden tavsiye veya öneriler anlamına gelmez ve teşkil etmez. Kullanım Koşulları bölümünde daha fazlasını okuyun.
Açık kaynak kodlu komut dosyası
Gerçek TradingView ruhuyla, bu komut dosyasının mimarı, yatırımcıların işlevselliğini inceleyip doğrulayabilmesi için onu açık kaynaklı hale getirdi. Yazarı tebrik ederiz! Ücretsiz olarak kullanabilseniz de, kodu yeniden yayınlamanın Topluluk Kurallarımıza tabi olduğunu unutmayın.
Feragatname
Bilgiler ve yayınlar, TradingView tarafından sağlanan veya onaylanan finansal, yatırım, alım satım veya diğer türden tavsiye veya öneriler anlamına gelmez ve teşkil etmez. Kullanım Koşulları bölümünde daha fazlasını okuyun.