photon-algonexta.pine · 286 lines · 18.8 KB
Not yet verified on the native platform — test it before relying on it.
The original uploaded source.
//@version=6
indicator("AlgoNexta Photon Trader V1 — Structure", "Photon.AlgoNexta", overlay = true, max_labels_count = 500, max_lines_count = 100, max_boxes_count = 50)
// TradingView indicator port of the causal Phase 1–2 cTrader milestone.
// No POI, liquidity, entry, order, SL/TP, or performance simulation is present.
// Higher-timeframe requests use lookahead_off and events are emitted only after bar close.
string G_GENERAL = "01 General"
string G_TIME = "02 Timeframes"
string G_STRUCTURE = "03 Structure"
string G_DISPLAY = "13 Display"
string G_ALERTS = "14 Alerts"
string profile = input.string("ClassicPhoton", "Photon Profile", options = ["ClassicPhoton", "GoldResearch", "Custom"], group = G_GENERAL)
string customHtf = input.timeframe("240", "Higher Timeframe", group = G_TIME)
string customMtf = input.timeframe("15", "Middle Timeframe", group = G_TIME)
string customLtf = input.timeframe("1", "Entry Timeframe", group = G_TIME)
int swingStrength = input.int(3, "Swing Pivot Strength", minval = 1, maxval = 20, group = G_STRUCTURE)
float bosBufferPips = input.float(0.0, "BOS Buffer Pips", minval = 0.0, step = 0.1, group = G_STRUCTURE)
float pipSize = input.float(0.1, "Pip Size", minval = 0.0000001, step = 0.01, group = G_STRUCTURE, tooltip = "For many XAUUSD feeds this is 0.1. Match this to your broker/feed convention.")
int atrPeriod = input.int(14, "ATR Period", minval = 2, group = G_STRUCTURE)
float swingAtrMultiplier = input.float(1.0, "Swing ATR Multiplier", minval = 0.1, step = 0.05, group = G_STRUCTURE)
float internalAtrMultiplier = input.float(0.35, "Internal ATR Multiplier", minval = 0.05, step = 0.05, group = G_STRUCTURE)
int minimumSwingBars = input.int(3, "Minimum Swing Bars", minval = 1, group = G_STRUCTURE)
int minimumInternalBars = input.int(1, "Minimum Internal Bars", minval = 1, group = G_STRUCTURE)
bool requirePremiumDiscount = input.bool(false, "Require Premium/Discount", group = G_STRUCTURE, tooltip = "Context-only in this structure milestone; it does not filter events.")
bool showHtf = input.bool(true, "Show HTF Events", group = G_DISPLAY)
bool showMtf = input.bool(true, "Show MTF Events", group = G_DISPLAY)
bool showLtf = input.bool(false, "Show LTF Events", group = G_DISPLAY)
bool showSwingPivots = input.bool(true, "Show Swing High/Low", group = G_DISPLAY)
bool showInternal = input.bool(false, "Show Internal High/Low", group = G_DISPLAY)
bool showBos = input.bool(true, "Show BOS", group = G_DISPLAY)
bool showChoch = input.bool(true, "Show CHoCH", group = G_DISPLAY)
bool showStrongWeak = input.bool(true, "Show Strong/Weak Levels", group = G_DISPLAY)
bool showEquilibrium = input.bool(true, "Show Equilibrium", group = G_DISPLAY)
bool showDashboard = input.bool(true, "Show Dashboard", group = G_DISPLAY)
int maximumEventLabels = input.int(250, "Maximum Event Labels", minval = 20, maxval = 480, group = G_DISPLAY)
bool enableBosAlerts = input.bool(true, "Enable BOS Alerts", group = G_ALERTS)
bool enableChochAlerts = input.bool(true, "Enable CHoCH Alerts", group = G_ALERTS)
string htf = profile == "ClassicPhoton" ? "240" : profile == "GoldResearch" ? "60" : customHtf
string mtf = profile == "ClassicPhoton" ? "15" : profile == "GoldResearch" ? "5" : customMtf
string ltf = profile == "ClassicPhoton" ? "1" : profile == "GoldResearch" ? "1" : customLtf
f_dirText(int direction) => direction == 1 ? "Bull" : direction == -1 ? "Bear" : "Unknown"
f_phaseText(int phase) => phase == 1 ? "Impulse" : phase == -1 ? "Pullback" : "Unknown"
f_flowText(int flow) => flow == 1 ? "Bull Continuation" : flow == 2 ? "Bull Pullback" : flow == -1 ? "Bear Continuation" : flow == -2 ? "Bear Pullback" : "Unknown"
f_priceText(float price) => na(price) ? "—" : str.tostring(price, format.mintick)
// MECHANICAL_INTERPRETATION:
// Photon contains discretionary judgment in swing significance and internal mapping.
// This implementation freezes a pivot after right-side confirmation and uses deterministic ATR qualification.
f_structure() =>
var float swingHigh = na
var float swingLow = na
var int swingHighOrigin = na
var int swingLowOrigin = na
var bool swingHighBroken = false
var bool swingLowBroken = false
var float internalHigh = na
var float internalLow = na
var int internalHighOrigin = na
var int internalLowOrigin = na
var bool internalHighBroken = false
var bool internalLowBroken = false
var int swingDirection = 0
var int internalDirection = 0
var float strongHigh = na
var float strongLow = na
var float weakHigh = na
var float weakLow = na
var int lastBosTime = na
var int lastBosDirection = 0
var int lastChochTime = na
var int lastChochDirection = 0
var int lastSwingEventTime = na
var int lastSwingEventType = 0
var float lastSwingEventPrice = na
var int lastInternalEventTime = na
var int lastInternalEventType = 0
var float lastInternalEventPrice = na
float atr = ta.atr(atrPeriod)
float ph = ta.pivothigh(high, swingStrength, swingStrength)
float pl = ta.pivotlow(low, swingStrength, swingStrength)
bool confirmed = barstate.isconfirmed
if confirmed and not na(ph)
float opposingLow = ta.lowest(low[swingStrength], minimumSwingBars + 1)
bool qualifiedHigh = ph - opposingLow >= atr[swingStrength] * swingAtrMultiplier
if qualifiedHigh
swingHigh := ph
swingHighOrigin := bar_index - swingStrength
swingHighBroken := false
lastSwingEventTime := time
lastSwingEventType := 1
lastSwingEventPrice := ph
if swingDirection == -1
strongHigh := ph
else
weakHigh := ph
if confirmed and not na(pl)
float opposingHigh = ta.highest(high[swingStrength], minimumSwingBars + 1)
bool qualifiedLow = opposingHigh - pl >= atr[swingStrength] * swingAtrMultiplier
if qualifiedLow
swingLow := pl
swingLowOrigin := bar_index - swingStrength
swingLowBroken := false
lastSwingEventTime := time
lastSwingEventType := -1
lastSwingEventPrice := pl
if swingDirection == 1
strongLow := pl
else
weakLow := pl
// Prior candle becomes a candidate when this closed candle fails to extend its extreme.
float minInternalMove = atr * internalAtrMultiplier
bool highCandidate = confirmed and high <= high[1] and (na(internalLow) or high[1] - internalLow >= minInternalMove)
bool lowCandidate = confirmed and low >= low[1] and (na(internalHigh) or internalHigh - low[1] >= minInternalMove)
if highCandidate and (na(internalLowOrigin) or bar_index - internalLowOrigin >= minimumInternalBars)
internalHigh := high[1]
internalHighOrigin := bar_index - 1
internalHighBroken := false
lastInternalEventTime := time
lastInternalEventType := 1
lastInternalEventPrice := internalHigh
if lowCandidate and (na(internalHighOrigin) or bar_index - internalHighOrigin >= minimumInternalBars)
internalLow := low[1]
internalLowOrigin := bar_index - 1
internalLowBroken := false
lastInternalEventTime := time
lastInternalEventType := -1
lastInternalEventPrice := internalLow
// PHOTON_ORIGINAL: Swing BOS requires a candle close beyond the mapped wick level.
bool bullBos = confirmed and not swingHighBroken and not na(swingHigh) and close > swingHigh + bosBufferPips * pipSize
bool bearBos = confirmed and not swingLowBroken and not na(swingLow) and close < swingLow - bosBufferPips * pipSize
if bullBos
swingHighBroken := true
swingDirection := 1
strongLow := swingLow
weakHigh := swingHigh
lastBosTime := time
lastBosDirection := 1
if bearBos
swingLowBroken := true
swingDirection := -1
strongHigh := swingHigh
weakLow := swingLow
lastBosTime := time
lastBosDirection := -1
// PHOTON_ORIGINAL: CHoCH is the first aggressive wick break against mapped Internal behaviour.
bool bullChoch = confirmed and internalDirection == -1 and not internalHighBroken and not na(internalHigh) and high > internalHigh
bool bearChoch = confirmed and internalDirection == 1 and not internalLowBroken and not na(internalLow) and low < internalLow
if bullChoch
internalHighBroken := true
internalDirection := 1
lastChochTime := time
lastChochDirection := 1
if bearChoch
internalLowBroken := true
internalDirection := -1
lastChochTime := time
lastChochDirection := -1
// Bootstrap internal direction from a mapped break; this is not emitted as CHoCH.
if confirmed and internalDirection == 0
if not na(internalHigh) and high > internalHigh
internalDirection := 1
internalHighBroken := true
else if not na(internalLow) and low < internalLow
internalDirection := -1
internalLowBroken := true
bool counter = swingDirection != 0 and internalDirection != 0 and swingDirection != internalDirection
int phase = swingDirection == 0 ? 0 : counter ? -1 : 1
int expectedFlow = swingDirection == 1 ? (counter ? 2 : 1) : swingDirection == -1 ? (counter ? -2 : -1) : 0
float rangeHigh = not na(strongHigh) ? strongHigh : weakHigh
float rangeLow = not na(strongLow) ? strongLow : weakLow
float relativePosition = not na(rangeHigh) and not na(rangeLow) and rangeHigh > rangeLow ? (close - rangeLow) / (rangeHigh - rangeLow) : na
float equilibrium = not na(rangeHigh) and not na(rangeLow) ? (rangeHigh + rangeLow) * 0.5 : na
[swingDirection, internalDirection, phase, expectedFlow, strongHigh, strongLow, weakHigh, weakLow, equilibrium, relativePosition,
lastBosTime, lastBosDirection, lastChochTime, lastChochDirection, lastSwingEventTime, lastSwingEventType, lastSwingEventPrice,
lastInternalEventTime, lastInternalEventType, lastInternalEventPrice]
[hSwing, hInternal, hPhase, hFlow, hStrongHigh, hStrongLow, hWeakHigh, hWeakLow, hEq, hPosition, hBosTime, hBosDir, hChochTime, hChochDir, hSwingTime, hSwingType, hSwingPrice, hInternalTime, hInternalType, hInternalPrice] = request.security(syminfo.tickerid, htf, f_structure(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
[mSwing, mInternal, mPhase, mFlow, mStrongHigh, mStrongLow, mWeakHigh, mWeakLow, mEq, mPosition, mBosTime, mBosDir, mChochTime, mChochDir, mSwingTime, mSwingType, mSwingPrice, mInternalTime, mInternalType, mInternalPrice] = request.security(syminfo.tickerid, mtf, f_structure(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
[lSwing, lInternal, lPhase, lFlow, lStrongHigh, lStrongLow, lWeakHigh, lWeakLow, lEq, lPosition, lBosTime, lBosDir, lChochTime, lChochDir, lSwingTime, lSwingType, lSwingPrice, lInternalTime, lInternalType, lInternalPrice] = request.security(syminfo.tickerid, ltf, f_structure(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off)
bool hNewBos = not na(hBosTime) and (na(hBosTime[1]) or hBosTime != hBosTime[1])
bool mNewBos = not na(mBosTime) and (na(mBosTime[1]) or mBosTime != mBosTime[1])
bool lNewBos = not na(lBosTime) and (na(lBosTime[1]) or lBosTime != lBosTime[1])
bool hNewChoch = not na(hChochTime) and (na(hChochTime[1]) or hChochTime != hChochTime[1])
bool mNewChoch = not na(mChochTime) and (na(mChochTime[1]) or mChochTime != mChochTime[1])
bool lNewChoch = not na(lChochTime) and (na(lChochTime[1]) or lChochTime != lChochTime[1])
bool hNewSwing = not na(hSwingTime) and (na(hSwingTime[1]) or hSwingTime != hSwingTime[1])
bool mNewSwing = not na(mSwingTime) and (na(mSwingTime[1]) or mSwingTime != mSwingTime[1])
bool lNewSwing = not na(lSwingTime) and (na(lSwingTime[1]) or lSwingTime != lSwingTime[1])
bool hNewInternal = not na(hInternalTime) and (na(hInternalTime[1]) or hInternalTime != hInternalTime[1])
bool mNewInternal = not na(mInternalTime) and (na(mInternalTime[1]) or mInternalTime != mInternalTime[1])
bool lNewInternal = not na(lInternalTime) and (na(lInternalTime[1]) or lInternalTime != lInternalTime[1])
var label[] eventLabels = array.new_label()
f_event(bool event, string role, string eventName, int direction, float eventPrice, color eventColor) =>
if event
float y = na(eventPrice) ? close : eventPrice
label item = label.new(bar_index, y, role + " " + eventName + (direction == 1 ? " ↑" : " ↓"), style = direction == 1 ? label.style_label_up : label.style_label_down, color = eventColor, textcolor = color.white, size = size.tiny)
array.push(eventLabels, item)
while array.size(eventLabels) > maximumEventLabels
label.delete(array.shift(eventLabels))
if showBos
f_event(showHtf and hNewBos, "HTF", "BOS", hBosDir, hBosDir == 1 ? hWeakHigh : hWeakLow, color.new(hBosDir == 1 ? color.lime : color.red, 0))
f_event(showMtf and mNewBos, "MTF", "BOS", mBosDir, mBosDir == 1 ? mWeakHigh : mWeakLow, color.new(mBosDir == 1 ? color.lime : color.red, 15))
f_event(showLtf and lNewBos, "LTF", "BOS", lBosDir, lBosDir == 1 ? lWeakHigh : lWeakLow, color.new(lBosDir == 1 ? color.lime : color.red, 30))
if showChoch
f_event(showHtf and hNewChoch, "HTF", "CHoCH", hChochDir, close, color.purple)
f_event(showMtf and mNewChoch, "MTF", "CHoCH", mChochDir, close, color.new(color.purple, 15))
f_event(showLtf and lNewChoch, "LTF", "CHoCH", lChochDir, close, color.new(color.purple, 30))
if showSwingPivots
f_event(showHtf and hNewSwing, "HTF", hSwingType == 1 ? "SH" : "SL", hSwingType == 1 ? -1 : 1, hSwingPrice, color.new(color.blue, 20))
f_event(showMtf and mNewSwing, "MTF", mSwingType == 1 ? "SH" : "SL", mSwingType == 1 ? -1 : 1, mSwingPrice, color.new(color.blue, 40))
f_event(showLtf and lNewSwing, "LTF", lSwingType == 1 ? "SH" : "SL", lSwingType == 1 ? -1 : 1, lSwingPrice, color.new(color.blue, 60))
if showInternal
f_event(showHtf and hNewInternal, "HTF", hInternalType == 1 ? "IH" : "IL", hInternalType == 1 ? -1 : 1, hInternalPrice, color.new(color.gray, 20))
f_event(showMtf and mNewInternal, "MTF", mInternalType == 1 ? "IH" : "IL", mInternalType == 1 ? -1 : 1, mInternalPrice, color.new(color.gray, 40))
f_event(showLtf and lNewInternal, "LTF", lInternalType == 1 ? "IH" : "IL", lInternalType == 1 ? -1 : 1, lInternalPrice, color.new(color.gray, 60))
plot(showStrongWeak ? hStrongHigh : na, "HTF Strong High", color = color.new(color.red, 5), linewidth = 2, style = plot.style_linebr)
plot(showStrongWeak ? hStrongLow : na, "HTF Strong Low", color = color.new(color.lime, 5), linewidth = 2, style = plot.style_linebr)
plot(showStrongWeak ? hWeakHigh : na, "HTF Weak High", color = color.new(color.orange, 20), linewidth = 1, style = plot.style_linebr)
plot(showStrongWeak ? hWeakLow : na, "HTF Weak Low", color = color.new(color.orange, 20), linewidth = 1, style = plot.style_linebr)
plot(showStrongWeak ? mStrongHigh : na, "MTF Strong High", color = color.new(color.red, 55), linewidth = 1, style = plot.style_linebr)
plot(showStrongWeak ? mStrongLow : na, "MTF Strong Low", color = color.new(color.lime, 55), linewidth = 1, style = plot.style_linebr)
plot(showEquilibrium ? hEq : na, "HTF Equilibrium", color = color.new(color.aqua, 35), linewidth = 1, style = plot.style_linebr)
bool anyBullBos = (showHtf and hNewBos and hBosDir == 1) or (showMtf and mNewBos and mBosDir == 1) or (showLtf and lNewBos and lBosDir == 1)
bool anyBearBos = (showHtf and hNewBos and hBosDir == -1) or (showMtf and mNewBos and mBosDir == -1) or (showLtf and lNewBos and lBosDir == -1)
bool anyBullChoch = (showHtf and hNewChoch and hChochDir == 1) or (showMtf and mNewChoch and mChochDir == 1) or (showLtf and lNewChoch and lChochDir == 1)
bool anyBearChoch = (showHtf and hNewChoch and hChochDir == -1) or (showMtf and mNewChoch and mChochDir == -1) or (showLtf and lNewChoch and lChochDir == -1)
alertcondition(enableBosAlerts and anyBullBos, "Photon Bullish BOS", "Photon bullish close-confirmed BOS on {{ticker}} at {{close}}")
alertcondition(enableBosAlerts and anyBearBos, "Photon Bearish BOS", "Photon bearish close-confirmed BOS on {{ticker}} at {{close}}")
alertcondition(enableChochAlerts and anyBullChoch, "Photon Bullish CHoCH", "Photon bullish wick-based CHoCH on {{ticker}} at {{close}}")
alertcondition(enableChochAlerts and anyBearChoch, "Photon Bearish CHoCH", "Photon bearish wick-based CHoCH on {{ticker}} at {{close}}")
var table dashboard = table.new(position.top_right, 3, 10, bgcolor = color.new(color.black, 12), border_width = 1)
if barstate.islast and showDashboard
table.cell(dashboard, 0, 0, "ALGONEXTA PHOTON V1", text_color = color.white, bgcolor = color.new(color.blue, 40))
table.merge_cells(dashboard, 0, 0, 2, 0)
table.cell(dashboard, 0, 1, "Role", text_color = color.silver)
table.cell(dashboard, 1, 1, "HTF " + htf, text_color = color.aqua)
table.cell(dashboard, 2, 1, "MTF " + mtf, text_color = color.aqua)
table.cell(dashboard, 0, 2, "Swing", text_color = color.silver)
table.cell(dashboard, 1, 2, f_dirText(hSwing), text_color = hSwing == 1 ? color.lime : hSwing == -1 ? color.red : color.orange)
table.cell(dashboard, 2, 2, f_dirText(mSwing), text_color = mSwing == 1 ? color.lime : mSwing == -1 ? color.red : color.orange)
table.cell(dashboard, 0, 3, "Internal", text_color = color.silver)
table.cell(dashboard, 1, 3, f_dirText(hInternal), text_color = hInternal == 1 ? color.lime : hInternal == -1 ? color.red : color.orange)
table.cell(dashboard, 2, 3, f_dirText(mInternal), text_color = mInternal == 1 ? color.lime : mInternal == -1 ? color.red : color.orange)
table.cell(dashboard, 0, 4, "Phase", text_color = color.silver)
table.cell(dashboard, 1, 4, f_phaseText(hPhase), text_color = color.white)
table.cell(dashboard, 2, 4, f_phaseText(mPhase), text_color = color.white)
table.cell(dashboard, 0, 5, "Expected Flow", text_color = color.silver)
table.cell(dashboard, 1, 5, f_flowText(hFlow), text_color = color.white)
table.cell(dashboard, 2, 5, f_flowText(mFlow), text_color = color.white)
table.cell(dashboard, 0, 6, "Strong H", text_color = color.silver)
table.cell(dashboard, 1, 6, f_priceText(hStrongHigh), text_color = color.white)
table.cell(dashboard, 2, 6, f_priceText(mStrongHigh), text_color = color.white)
table.cell(dashboard, 0, 7, "Strong L", text_color = color.silver)
table.cell(dashboard, 1, 7, f_priceText(hStrongLow), text_color = color.white)
table.cell(dashboard, 2, 7, f_priceText(mStrongLow), text_color = color.white)
table.cell(dashboard, 0, 8, "Range Pos.", text_color = color.silver)
table.cell(dashboard, 1, 8, na(hPosition) ? "—" : str.tostring(hPosition, "#.0%"), text_color = color.white)
table.cell(dashboard, 2, 8, na(mPosition) ? "—" : str.tostring(mPosition, "#.0%"), text_color = color.white)
table.cell(dashboard, 0, 9, "Status", text_color = color.silver)
table.cell(dashboard, 1, 9, "STRUCTURE ONLY", text_color = color.yellow)
table.merge_cells(dashboard, 1, 9, 2, 9)
else if barstate.islast
table.clear(dashboard, 0, 0, 2, 9)