Thursday, November 12, 2026

Mesured move AGPro source code

 // -------------------------------------------------

// Measured Move Projection Zones [AGPro Series]

//

// Author  : AGProLabs | AGPro Series

// Version : 1.5

//

// A measured move projection overlay that detects a qualified impulse leg,

// waits for a compact base range, then maps a projected target band,

// invalidation context, and live projection progress. The script is built

// for clean target-zone visualization, impulse quality reading, and

// structured chart planning without directional claims. V1.5 anchors impulse

// qualification to the confirmed pivot bar, centers every zone badge inside

// its zone, and hardens forward projection bounds across higher timeframes.

// -------------------------------------------------


//@version=6

indicator("Measured Move Projection Zones [AGPro Series]",

     shorttitle = "AG Pro MMP",

     overlay = true,

     max_boxes_count = 80,

     max_labels_count = 80,

     max_lines_count = 80)


// -------------------------------------------------

// AGPro Color Palette

// -------------------------------------------------

headerBg      = color.rgb(37, 99, 235)

headerText    = color.rgb(245, 247, 250)

panelBg       = color.rgb(15, 23, 42)

panelBorder   = color.rgb(51, 65, 85)

panelLabelCol = color.rgb(148, 163, 184)

panelValueCol = color.rgb(226, 232, 240)

bullColor     = color.rgb(34, 197, 166)

bearColor     = color.rgb(244, 114, 182)

neutralColor  = color.rgb(245, 189, 92)

accentColor   = color.rgb(129, 140, 248)

targetBlue    = color.rgb(59, 130, 246)


// -------------------------------------------------

// Inputs

// -------------------------------------------------

detectionGroup = "Detection"

pivotLen = input.int(4, "Pivot Confirmation Length", minval = 2, maxval = 20, group = detectionGroup, tooltip = "Confirms the chart-timeframe swing points used to anchor the measured impulse leg.")

atrLen = input.int(14, "ATR Length", minval = 5, maxval = 100, group = detectionGroup, tooltip = "ATR baseline used for impulse qualification, base tightness, target-band width, and label offsets.")

minImpulseAtr = input.float(1.8, "Minimum Impulse Size", minval = 0.5, maxval = 10.0, step = 0.1, group = detectionGroup, tooltip = "Minimum chart-timeframe impulse-leg size measured in ATR units.")

minImpulseBars = input.int(4, "Minimum Impulse Bars", minval = 1, maxval = 80, group = detectionGroup, tooltip = "Minimum number of bars allowed between the start and end pivot of the impulse leg.")

maxImpulseBars = input.int(36, "Maximum Impulse Bars", minval = 5, maxval = 200, group = detectionGroup, tooltip = "Maximum number of chart-timeframe bars allowed between the start and end pivot of the impulse leg.")

baseBars = input.int(6, "Base Range Bars", minval = 3, maxval = 40, group = detectionGroup, tooltip = "Number of chart-timeframe bars used to validate the compact base after the impulse leg.")

maxBaseAtr = input.float(1.55, "Maximum Base Height", minval = 0.2, maxval = 5.0, step = 0.05, group = detectionGroup, tooltip = "Maximum base height measured in ATR units.")

maxBaseRetrace = input.float(0.50, "Maximum Base Retracement", minval = 0.10, maxval = 0.90, step = 0.05, group = detectionGroup, tooltip = "Maximum retracement allowed from the impulse end before the setup is considered too deep.")

maxBaseWaitBars = input.int(26, "Maximum Base Wait", minval = 8, maxval = 120, group = detectionGroup, tooltip = "Maximum number of chart-timeframe bars to wait for a valid base after the impulse leg is confirmed.")


projectionGroup = "Projection"

breakBufferAtr = input.float(0.08, "Base Break Buffer", minval = 0.0, maxval = 1.0, step = 0.01, group = projectionGroup, tooltip = "ATR buffer required beyond the base boundary before the projection changes from armed to active.")

targetMultiplier = input.float(1.00, "Measured Move Multiplier", minval = 0.50, maxval = 2.50, step = 0.05, group = projectionGroup, tooltip = "Projection multiplier applied to the impulse leg.")

targetBandAtr = input.float(0.40, "Target Band Width", minval = 0.05, maxval = 2.00, step = 0.05, group = projectionGroup, tooltip = "Width of the target band measured in ATR units.")

invalidBufferAtr = input.float(0.30, "Invalidation Buffer", minval = 0.00, maxval = 2.00, step = 0.05, group = projectionGroup, tooltip = "ATR buffer beyond the opposite side of the base range used for invalidation context.")

projectionBars = input.int(42, "Projection Bars", minval = 15, maxval = 180, group = projectionGroup, tooltip = "Chart-timeframe forward distance used for target bands, base extensions, and projection guide lines.")

adaptiveProjection = input.bool(true, "Adaptive HTF Projection", group = projectionGroup, tooltip = "Automatically widens projections on higher timeframes so target and base zones remain readable.")

expireBars = input.int(54, "Projection Expiration", minval = 20, maxval = 240, group = projectionGroup, tooltip = "Maximum chart-timeframe bars an armed or active projection can remain open before it is marked as expired.")


visualGroup = "Visuals"

showImpulseBox = input.bool(true, "Show Impulse Box", group = visualGroup, tooltip = "Displays a translucent box around the measured impulse leg.")

showBaseBox = input.bool(true, "Show Base Range Box", group = visualGroup, tooltip = "Displays the compact base range that anchors the measured-move projection.")

showTargetBand = input.bool(true, "Show Projected Target Band", group = visualGroup, tooltip = "Displays the projected target band derived from the measured impulse leg.")

showInvalidationGuide = input.bool(true, "Show Invalidation Guide", group = visualGroup, tooltip = "Displays a clean invalidation guide beyond the opposite side of the base range.")

showProgressMarker = input.bool(true, "Show Progress Marker", group = visualGroup, tooltip = "Displays a live marker showing how far price has progressed from the base boundary toward the projected band.")

showEventLabels = input.bool(true, "Show Event Labels", group = visualGroup, tooltip = "Displays compact labels for armed projections, active projections, target-band interaction, and invalidation.")

showCenteredZoneBadges = input.bool(true, "Show Zone Badges", group = visualGroup, tooltip = "Shows a centered badge inside every impulse, base, and target zone so no active box appears unlabeled.")

zoneBadgeAnchor = input.float(0.50, "Zone Badge Horizontal Position", minval = 0.35, maxval = 0.65, step = 0.05, group = visualGroup, tooltip = "Controls the badge position inside each zone. The default 0.50 places it at the horizontal center.")

eventLabelMode = input.string("Balanced", "Event Label Retention", options = ["Balanced", "All Events", "Latest Only"], group = visualGroup, tooltip = "Controls event-label retention. Balanced keeps enough structure without crowding the chart.")

eventLabelCooldown = input.int(8, "Event Label Cooldown", minval = 0, maxval = 80, group = visualGroup, tooltip = "Minimum bars between new event labels. This keeps repeated armed/active labels from clustering.")

showExpiredLabels = input.bool(false, "Show Expired Labels", group = visualGroup, tooltip = "Displays labels when a measured-move structure expires. Disabled by default to keep the public chart clean.")

showProjectionGuide = input.bool(false, "Show Projection Guide Line", group = visualGroup, tooltip = "Displays the dotted guide from the base boundary to the projected target.")

fadeClosedSetups = input.bool(true, "Fade Archived Setups", group = visualGroup, tooltip = "Softens target-hit, invalidated, and expired projection drawings so the active structure stands out.")

zoneOpacity = input.int(84, "Zone Opacity", minval = 60, maxval = 96, group = visualGroup, tooltip = "Controls the transparency of impulse, base, and target-zone boxes.")

labelOffsetAtr = input.float(0.80, "Label Offset", minval = 0.10, maxval = 4.00, step = 0.05, group = visualGroup, tooltip = "ATR-based vertical offset used to keep event labels away from candle bodies.")

maxVisibleLabels = input.int(5, "Balanced Event Labels", minval = 4, maxval = 50, group = visualGroup, tooltip = "Caps retained event labels so the chart keeps useful visual context without becoming crowded.")

maxSetupHistory = input.int(1, "Structure History", minval = 1, maxval = 14, group = visualGroup, tooltip = "Controls how many historical measured-move structures can remain visible before older drawings are removed. The default focuses the chart on the most recent structure for the cleanest view.")


styleGroup = "Style"

labelFontInput = input.string("Normal", "Label Font Size", options = ["Tiny", "Small", "Normal", "Large", "Huge"], group = styleGroup, tooltip = "Controls the size of all chart labels created by the script.")

panelFontInput = input.string("Normal", "Panel Font Size", options = ["Tiny", "Small", "Normal", "Large", "Huge"], group = styleGroup, tooltip = "Controls the AG Pro panel font size.")

panelLocation = input.string("Top Right", "Panel Location", options = ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group = styleGroup, tooltip = "Controls where the AG Pro summary panel is anchored on the chart.")

panelTheme = input.string("Elite Dark", "Panel Theme", options = ["Elite Dark", "Slate", "Clean Light"], group = styleGroup, tooltip = "Controls the panel color theme without changing the core chart objects.")

showPanel = input.bool(true, "Show Panel", group = styleGroup, tooltip = "Shows or hides the compact AG Pro measured-move summary panel.")


// -------------------------------------------------

// Helpers

// -------------------------------------------------

f_clamp(value, minValue, maxValue) =>

    math.max(minValue, math.min(maxValue, value))


f_size(sizeInput) =>

    switch sizeInput

        "Tiny" => size.tiny

        "Small" => size.small

        "Large" => size.large

        "Huge" => size.huge

        => size.normal


f_text_on(_bg) =>

    brightness = color.r(_bg) + color.g(_bg) + color.b(_bg)

    brightness >= 360 ? color.rgb(15, 23, 42) : color.white


f_panel_position(locationInput) =>

    switch locationInput

        "Top Left" => position.top_left

        "Top Center" => position.top_center

        "Middle Left" => position.middle_left

        "Middle Center" => position.middle_center

        "Middle Right" => position.middle_right

        "Bottom Left" => position.bottom_left

        "Bottom Center" => position.bottom_center

        "Bottom Right" => position.bottom_right

        => position.top_right


f_panel_bg() =>

    panelTheme == "Clean Light" ? color.rgb(241, 245, 249) : panelTheme == "Slate" ? color.rgb(30, 41, 59) : panelBg


f_panel_alt_bg(int row) =>

    bool evenRow = row % 2 == 0

    panelTheme == "Clean Light" ? (evenRow ? color.rgb(226, 232, 240) : color.rgb(241, 245, 249)) : panelTheme == "Slate" ? (evenRow ? color.rgb(37, 49, 70) : color.rgb(30, 41, 59)) : (evenRow ? color.rgb(20, 31, 52) : panelBg)


f_panel_border() =>

    panelTheme == "Clean Light" ? color.rgb(148, 163, 184) : panelTheme == "Slate" ? color.rgb(71, 85, 105) : panelBorder


f_panel_label() =>

    panelTheme == "Clean Light" ? color.rgb(71, 85, 105) : panelLabelCol


f_panel_value() =>

    panelTheme == "Clean Light" ? color.rgb(15, 23, 42) : panelValueCol


f_state_on_panel(_state) =>

    if panelTheme == "Clean Light"

        _state == bullColor ? color.rgb(13, 118, 99) : _state == bearColor ? color.rgb(178, 44, 112) : _state == neutralColor ? color.rgb(133, 77, 14) : _state == accentColor ? color.rgb(67, 56, 202) : _state

    else

        _state


f_trim_boxes(array<box> items, int limit) =>

    while array.size(items) > limit

        box oldItem = array.shift(items)

        box.delete(oldItem)


f_trim_lines(array<line> items, int limit) =>

    while array.size(items) > limit

        line oldItem = array.shift(items)

        line.delete(oldItem)


f_trim_labels(array<label> items, int limit) =>

    while array.size(items) > limit

        label oldItem = array.shift(items)

        label.delete(oldItem)


f_clear_labels(array<label> items) =>

    while array.size(items) > 0

        label oldItem = array.shift(items)

        label.delete(oldItem)


f_state_text(int stateValue) =>

    switch stateValue

        1 => "BUILDING BASE"

        2 => "ARMED"

        3 => "PROJECTING"

        4 => "TARGET BAND"

        5 => "INVALIDATED"

        6 => "EXPIRED"

        => "WAITING"


f_action_text(int stateValue) =>

    switch stateValue

        1 => "Wait For Base"

        2 => "Watch Break"

        3 => "Hold To Target"

        4 => "Manage Target"

        5 => "Stand Down"

        6 => "Re-scan"

        => "Scan Impulse"


f_state_color(int stateValue, int dirValue) =>

    color stateColor = neutralColor

    if stateValue == 3 or stateValue == 4

        stateColor := dirValue == 1 ? bullColor : bearColor

    else if stateValue == 5

        stateColor := bearColor

    else if stateValue == 6

        stateColor := panelLabelCol

    else if stateValue == 2

        stateColor := accentColor

    stateColor


f_direction_text(int dirValue) =>

    switch dirValue

        1 => "Bullish"

        -1 => "Bearish"

        => "Neutral"


f_format_price(float priceValue) =>

    na(priceValue) ? "n/a" : str.tostring(priceValue, format.mintick)


f_format_pct(float pctValue) =>

    na(pctValue) ? "n/a" : str.tostring(pctValue, "0.0") + "%"


f_format_atr(float atrValue) =>

    na(atrValue) ? "n/a" : str.format("{0,number,#.00} ATR", atrValue)


f_panel_cell(table panelRef, int col, int row, string txt, color txtColor, color bgColor, string sizeInput, string alignInput) =>

    table.cell(panelRef, col, row, txt, text_color = txtColor, bgcolor = bgColor, text_size = f_size(sizeInput), text_halign = alignInput == "left" ? text.align_left : alignInput == "right" ? text.align_right : text.align_center)


// -------------------------------------------------

// Object storage

// -------------------------------------------------

var box[] boxArchive = array.new_box()

var line[] lineArchive = array.new_line()

var label[] labelArchive = array.new_label()

var label[] zoneLabelArchive = array.new_label()


var box activeImpulseBox = na

var box activeBaseBox = na

var box activeTargetBox = na

var line activeInvalidLine = na

var line activeGuideLine = na

var line activeProgressLine = na

var label activeProgressLabel = na

var label activeImpulseBadge = na

var label activeBaseBadge = na

var label activeTargetBadge = na

var int lastEventLabelBar = na


f_can_label(int xValue) =>

    showEventLabels and (eventLabelCooldown == 0 or na(lastEventLabelBar) or xValue - lastEventLabelBar >= eventLabelCooldown)


f_add_label(int xValue, float yValue, string labelText, color bgColor, color textColor, string styleChoice) =>

    if eventLabelMode == "Latest Only"

        f_clear_labels(labelArchive)

    label newLabel = label.new(xValue, yValue, labelText, xloc = xloc.bar_index, yloc = yloc.price, style = styleChoice, color = bgColor, textcolor = textColor, size = f_size(labelFontInput))

    array.push(labelArchive, newLabel)

    int keepCount = eventLabelMode == "Balanced" ? int(math.min(maxVisibleLabels, 8)) : maxVisibleLabels

    f_trim_labels(labelArchive, keepCount)

    newLabel


f_badge_x(int leftX, int rightX) =>

    int(math.round(leftX + (rightX - leftX) * zoneBadgeAnchor))


f_zone_badge(int leftX, int rightX, float topValue, float bottomValue, string badgeText, color badgeColor) =>

    label badge = na

    if showCenteredZoneBadges

        badge := label.new(f_badge_x(leftX, rightX), (topValue + bottomValue) * 0.5, badgeText, xloc = xloc.bar_index, yloc = yloc.price, style = label.style_label_center, color = color.new(badgeColor, 8), textcolor = f_text_on(badgeColor), size = f_size(labelFontInput))

        array.push(zoneLabelArchive, badge)

        f_trim_labels(zoneLabelArchive, maxSetupHistory * 3)

    badge


f_update_zone_badge(label badge, int leftX, int rightX, float topValue, float bottomValue, string badgeText, color badgeColor) =>

    if showCenteredZoneBadges and not na(badge)

        label.set_x(badge, f_badge_x(leftX, rightX))

        label.set_y(badge, (topValue + bottomValue) * 0.5)

        label.set_text(badge, badgeText)

        label.set_color(badge, color.new(badgeColor, 8))

        label.set_textcolor(badge, f_text_on(badgeColor))

        label.set_size(badge, f_size(labelFontInput))

        label.set_style(badge, label.style_label_center)


// -------------------------------------------------

// Core state

// -------------------------------------------------

atr = ta.atr(atrLen)

ph = ta.pivothigh(high, pivotLen, pivotLen)

pl = ta.pivotlow(low, pivotLen, pivotLen)

baseWindowHigh = ta.highest(high, baseBars)

baseWindowLow = ta.lowest(low, baseBars)


var int lastSwingType = 0

var int lastSwingBar = na

var float lastSwingPrice = na


var int setupState = 0

var int setupDir = 0

var int impulseStartBar = na

var int impulseEndBar = na

var float impulseStartPrice = na

var float impulseEndPrice = na

var float impulseSize = na

var float impulseAtrSize = na

var float impulseQuality = na

var int baseStartBar = na

var int baseEndBar = na

var float baseHigh = na

var float baseLow = na

var float baseHeight = na

var float projectionOrigin = na

var float targetMid = na

var float targetTop = na

var float targetBottom = na

var float invalidationPrice = na

var int breakoutBar = na

var float projectionProgress = na

var bool eventArmed = false

var bool eventProjection = false

var bool eventTarget = false

var bool eventInvalid = false

var bool eventExpired = false


eventArmed := false

eventProjection := false

eventTarget := false

eventInvalid := false

eventExpired := false


canStartNew = setupState == 0 or setupState >= 4

detectedImpulse = false

detectedDir = 0

int detectedStartBar = na

int detectedEndBar = na

float detectedStartPrice = na

float detectedEndPrice = na

float detectedLegSize = na

float detectedLegAtr = na

float detectedQuality = na


if not na(pl)

    int pivotBar = bar_index - pivotLen

    float pivotPrice = pl

    if lastSwingType == 1 and not na(lastSwingBar) and canStartNew

        int legBars = pivotBar - lastSwingBar

        float legSize = lastSwingPrice - pivotPrice

        float pivotAtr = atr[pivotLen]

        float legAtr = pivotAtr > 0 ? legSize / pivotAtr : na

        bool legQualifies = legSize > 0 and legBars >= minImpulseBars and legBars <= maxImpulseBars and legAtr >= minImpulseAtr

        if legQualifies

            float sizeScore = f_clamp(legAtr / (minImpulseAtr * 2.0), 0.0, 1.0) * 55.0

            float tempoDenom = math.max(1.0, float(maxImpulseBars - minImpulseBars))

            float tempoScore = f_clamp(1.0 - (float(legBars - minImpulseBars) / tempoDenom), 0.0, 1.0) * 25.0

            detectedImpulse := true

            detectedDir := -1

            detectedStartBar := lastSwingBar

            detectedEndBar := pivotBar

            detectedStartPrice := lastSwingPrice

            detectedEndPrice := pivotPrice

            detectedLegSize := legSize

            detectedLegAtr := legAtr

            detectedQuality := f_clamp(sizeScore + tempoScore + 10.0, 0.0, 80.0)

    lastSwingType := -1

    lastSwingBar := pivotBar

    lastSwingPrice := pivotPrice


if not na(ph)

    int pivotBar = bar_index - pivotLen

    float pivotPrice = ph

    if not detectedImpulse and lastSwingType == -1 and not na(lastSwingBar) and canStartNew

        int legBars = pivotBar - lastSwingBar

        float legSize = pivotPrice - lastSwingPrice

        float pivotAtr = atr[pivotLen]

        float legAtr = pivotAtr > 0 ? legSize / pivotAtr : na

        bool legQualifies = legSize > 0 and legBars >= minImpulseBars and legBars <= maxImpulseBars and legAtr >= minImpulseAtr

        if legQualifies

            float sizeScore = f_clamp(legAtr / (minImpulseAtr * 2.0), 0.0, 1.0) * 55.0

            float tempoDenom = math.max(1.0, float(maxImpulseBars - minImpulseBars))

            float tempoScore = f_clamp(1.0 - (float(legBars - minImpulseBars) / tempoDenom), 0.0, 1.0) * 25.0

            detectedImpulse := true

            detectedDir := 1

            detectedStartBar := lastSwingBar

            detectedEndBar := pivotBar

            detectedStartPrice := lastSwingPrice

            detectedEndPrice := pivotPrice

            detectedLegSize := legSize

            detectedLegAtr := legAtr

            detectedQuality := f_clamp(sizeScore + tempoScore + 10.0, 0.0, 80.0)

    lastSwingType := 1

    lastSwingBar := pivotBar

    lastSwingPrice := pivotPrice


int projectionBoost = adaptiveProjection ? timeframe.isweekly ? 2 : timeframe.ismonthly ? 3 : 1 : 1

int forwardBars = int(math.min(projectionBars * projectionBoost, 480))


if detectedImpulse

    setupState := 1

    setupDir := detectedDir

    impulseStartBar := detectedStartBar

    impulseEndBar := detectedEndBar

    impulseStartPrice := detectedStartPrice

    impulseEndPrice := detectedEndPrice

    impulseSize := detectedLegSize

    impulseAtrSize := detectedLegAtr

    impulseQuality := detectedQuality

    baseStartBar := na

    baseEndBar := na

    baseHigh := na

    baseLow := na

    baseHeight := na

    projectionOrigin := na

    targetMid := na

    targetTop := na

    targetBottom := na

    invalidationPrice := na

    breakoutBar := na

    projectionProgress := na

    activeBaseBox := na

    activeTargetBox := na

    activeInvalidLine := na

    activeGuideLine := na

    activeImpulseBadge := na

    activeBaseBadge := na

    activeTargetBadge := na

    if showImpulseBox

        color impulseColor = setupDir == 1 ? bullColor : bearColor

        float impulseTop = math.max(impulseStartPrice, impulseEndPrice)

        float impulseBottom = math.min(impulseStartPrice, impulseEndPrice)

        activeImpulseBox := box.new(impulseStartBar, impulseTop, impulseEndBar, impulseBottom, xloc = xloc.bar_index, bgcolor = color.new(impulseColor, zoneOpacity), border_color = color.new(impulseColor, 15), border_width = 1)

        array.push(boxArchive, activeImpulseBox)

        if showCenteredZoneBadges

            int impulseBadgeX = int(math.round((impulseStartBar + impulseEndBar) / 2))

            activeImpulseBadge := label.new(impulseBadgeX, (impulseTop + impulseBottom) * 0.5, "IMPULSE LEG\n" + f_format_atr(impulseAtrSize), xloc = xloc.bar_index, yloc = yloc.price, style = label.style_label_center, color = color.new(impulseColor, 8), textcolor = f_text_on(impulseColor), size = f_size(labelFontInput))

            array.push(zoneLabelArchive, activeImpulseBadge)

            f_trim_labels(zoneLabelArchive, maxSetupHistory * 3)

        f_trim_boxes(boxArchive, maxSetupHistory * 3)


if setupState == 1 and not na(impulseEndBar)

    int barsAfterImpulse = bar_index - impulseEndBar

    bool waitedTooLong = barsAfterImpulse > maxBaseWaitBars

    if waitedTooLong

        setupState := 6

        eventExpired := true

    else if barsAfterImpulse >= baseBars

        float candidateHigh = baseWindowHigh

        float candidateLow = baseWindowLow

        float candidateHeight = candidateHigh - candidateLow

        float candidateHeightAtr = atr > 0 ? candidateHeight / atr : na

        float retrace = setupDir == 1 ? (impulseEndPrice - candidateLow) / impulseSize : (candidateHigh - impulseEndPrice) / impulseSize

        bool baseTight = candidateHeightAtr <= maxBaseAtr

        bool baseRetraceOk = retrace >= 0 and retrace <= maxBaseRetrace

        if baseTight and baseRetraceOk

            baseStartBar := bar_index - baseBars + 1

            baseEndBar := bar_index

            baseHigh := candidateHigh

            baseLow := candidateLow

            baseHeight := candidateHeight

            projectionOrigin := setupDir == 1 ? baseHigh : baseLow

            targetMid := setupDir == 1 ? projectionOrigin + impulseSize * targetMultiplier : projectionOrigin - impulseSize * targetMultiplier

            float bandHalf = atr * targetBandAtr * 0.5

            targetTop := targetMid + bandHalf

            targetBottom := targetMid - bandHalf

            invalidationPrice := setupDir == 1 ? baseLow - atr * invalidBufferAtr : baseHigh + atr * invalidBufferAtr

            float baseTightScore = f_clamp(1.0 - (candidateHeightAtr / maxBaseAtr), 0.0, 1.0) * 12.0

            float baseRetraceScore = f_clamp(1.0 - (retrace / maxBaseRetrace), 0.0, 1.0) * 8.0

            impulseQuality := f_clamp(impulseQuality + baseTightScore + baseRetraceScore, 0.0, 100.0)

            setupState := 2

            eventArmed := true

            color baseColor = setupDir == 1 ? bullColor : bearColor

            if showBaseBox

                activeBaseBox := box.new(baseStartBar, baseHigh, bar_index + forwardBars, baseLow, xloc = xloc.bar_index, bgcolor = color.new(neutralColor, zoneOpacity), border_color = color.new(neutralColor, 10), border_width = 1)

                array.push(boxArchive, activeBaseBox)

                activeBaseBadge := f_zone_badge(baseStartBar, bar_index + forwardBars, baseHigh, baseLow, "BASE RANGE\n" + f_format_atr(not na(baseHeight) and atr > 0 ? baseHeight / atr : na), neutralColor)

            if showTargetBand

                activeTargetBox := box.new(baseEndBar, targetTop, bar_index + forwardBars, targetBottom, xloc = xloc.bar_index, bgcolor = color.new(baseColor, zoneOpacity), border_color = color.new(baseColor, 0), border_width = 1)

                array.push(boxArchive, activeTargetBox)

                activeTargetBadge := f_zone_badge(baseEndBar, bar_index + forwardBars, targetTop, targetBottom, "TARGET BAND\n" + f_format_price(targetMid), baseColor)

            if showInvalidationGuide

                activeInvalidLine := line.new(baseStartBar, invalidationPrice, bar_index + forwardBars, invalidationPrice, xloc = xloc.bar_index, extend = extend.none, color = color.new(bearColor, 0), style = line.style_dashed, width = 1)

                array.push(lineArchive, activeInvalidLine)

            if showProjectionGuide

                activeGuideLine := line.new(baseEndBar, projectionOrigin, baseEndBar + forwardBars, targetMid, xloc = xloc.bar_index, extend = extend.none, color = color.new(baseColor, 45), style = line.style_dotted, width = 1)

                array.push(lineArchive, activeGuideLine)

            f_trim_boxes(boxArchive, maxSetupHistory * 3)

            f_trim_lines(lineArchive, maxSetupHistory * 2)

            float armedOffset = labelOffsetAtr + 0.35

            float labelY = setupDir == 1 ? baseLow - atr * armedOffset : baseHigh + atr * armedOffset

            string styleChoice = setupDir == 1 ? label.style_label_up : label.style_label_down

            if f_can_label(baseEndBar)

                f_add_label(baseEndBar, labelY, "ARMED", color.new(accentColor, 0), f_text_on(accentColor), styleChoice)

                lastEventLabelBar := baseEndBar


if setupState == 2

    bool breakoutNow = setupDir == 1 ? close > baseHigh + atr * breakBufferAtr : close < baseLow - atr * breakBufferAtr

    bool invalidNow = setupDir == 1 ? close < invalidationPrice : close > invalidationPrice

    bool expiredNow = bar_index - baseEndBar > expireBars

    if invalidNow

        setupState := 5

        eventInvalid := true

    else if breakoutNow

        setupState := 3

        breakoutBar := bar_index

        eventProjection := true

        float labelY = setupDir == 1 ? low - atr * labelOffsetAtr : high + atr * labelOffsetAtr

        string styleChoice = setupDir == 1 ? label.style_label_up : label.style_label_down

        if f_can_label(bar_index)

            f_add_label(bar_index, labelY, "ACTIVE", color.new(setupDir == 1 ? bullColor : bearColor, 0), f_text_on(setupDir == 1 ? bullColor : bearColor), styleChoice)

            lastEventLabelBar := bar_index

    else if expiredNow

        setupState := 6

        eventExpired := true


if setupState == 3

    bool targetNow = setupDir == 1 ? high >= targetBottom : low <= targetTop

    bool invalidNow = setupDir == 1 ? close < invalidationPrice : close > invalidationPrice

    bool expiredNow = bar_index - breakoutBar > expireBars

    if invalidNow

        setupState := 5

        eventInvalid := true

    else if targetNow

        setupState := 4

        eventTarget := true

    else if expiredNow

        setupState := 6

        eventExpired := true


if eventTarget

    float labelY = setupDir == 1 ? targetBottom : targetTop

    string styleChoice = setupDir == 1 ? label.style_label_down : label.style_label_up

    if f_can_label(bar_index)

        f_add_label(bar_index, labelY, "TARGET", color.new(setupDir == 1 ? bullColor : bearColor, 0), f_text_on(setupDir == 1 ? bullColor : bearColor), styleChoice)

        lastEventLabelBar := bar_index


if eventInvalid

    float labelY = setupDir == 1 ? high + atr * labelOffsetAtr : low - atr * labelOffsetAtr

    string styleChoice = setupDir == 1 ? label.style_label_down : label.style_label_up

    if f_can_label(bar_index)

        f_add_label(bar_index, labelY, "INVALID", color.new(bearColor, 0), f_text_on(bearColor), styleChoice)

        lastEventLabelBar := bar_index


if eventExpired and showExpiredLabels and setupState == 6 and f_can_label(bar_index)

    f_add_label(bar_index, close, "EXPIRED", color.new(panelBorder, 0), f_text_on(panelBorder), label.style_label_left)

    lastEventLabelBar := bar_index


// -------------------------------------------------

// Target hit-rate proof (Confirm Rate)

// -------------------------------------------------

var int  mmTotal = 0

var int  mmWon   = 0

var bool mmPend  = false


if barstate.isconfirmed

    if eventProjection

        mmPend := true

    if mmPend and eventTarget

        mmTotal += 1

        mmWon += 1

        mmPend := false

    if mmPend and eventInvalid

        mmTotal += 1

        mmPend := false

    if mmPend and eventExpired

        mmTotal += 1

        mmPend := false


float mmRate = mmTotal > 0 ? mmWon / mmTotal * 100.0 : na


if fadeClosedSetups and (eventTarget or eventInvalid or eventExpired)

    color closedColor = eventTarget ? (setupDir == 1 ? bullColor : bearColor) : panelBorder

    if not na(activeImpulseBox)

        box.set_bgcolor(activeImpulseBox, color.new(closedColor, 90))

        box.set_border_color(activeImpulseBox, color.new(closedColor, 55))

    if not na(activeBaseBox)

        box.set_bgcolor(activeBaseBox, color.new(neutralColor, 89))

        box.set_border_color(activeBaseBox, color.new(neutralColor, 55))

    if not na(activeTargetBox)

        box.set_bgcolor(activeTargetBox, color.new(closedColor, 88))

        box.set_border_color(activeTargetBox, color.new(closedColor, 50))

    if not na(activeInvalidLine)

        line.set_color(activeInvalidLine, color.new(closedColor, 58))

    if not na(activeGuideLine)

        line.set_color(activeGuideLine, color.new(closedColor, 62))


bool liveProjection = setupState == 2 or setupState == 3

if liveProjection

    int rightEdge = bar_index + forwardBars

    if not na(activeBaseBox)

        box.set_right(activeBaseBox, rightEdge)

    if not na(activeTargetBox)

        box.set_right(activeTargetBox, rightEdge)

    f_update_zone_badge(activeBaseBadge, baseStartBar, rightEdge, baseHigh, baseLow, "BASE RANGE\n" + f_format_atr(not na(baseHeight) and atr > 0 ? baseHeight / atr : na), neutralColor)

    f_update_zone_badge(activeTargetBadge, baseEndBar, rightEdge, targetTop, targetBottom, "TARGET BAND\n" + f_format_price(targetMid), setupDir == 1 ? bullColor : bearColor)

    if not na(activeInvalidLine)

        line.set_x2(activeInvalidLine, rightEdge)

        line.set_y1(activeInvalidLine, invalidationPrice)

        line.set_y2(activeInvalidLine, invalidationPrice)

    if not na(activeGuideLine)

        line.set_x2(activeGuideLine, rightEdge)

        line.set_y1(activeGuideLine, projectionOrigin)

        line.set_y2(activeGuideLine, targetMid)


float rawProgress = na

if setupState == 2

    rawProgress := 0.0

else if setupState == 3 or setupState == 4

    float denominator = math.max(math.abs(targetMid - projectionOrigin), syminfo.mintick)

    rawProgress := setupDir == 1 ? ((close - projectionOrigin) / denominator) * 100.0 : ((projectionOrigin - close) / denominator) * 100.0

projectionProgress := na(rawProgress) ? projectionProgress : f_clamp(rawProgress, 0.0, 100.0)


if (not showProgressMarker or setupState == 0 or setupState == 1 or setupState == 5 or setupState == 6) and not na(activeProgressLine)

    line.delete(activeProgressLine)

    activeProgressLine := na

if (not showProgressMarker or setupState == 0 or setupState == 1 or setupState == 5 or setupState == 6) and not na(activeProgressLabel)

    label.delete(activeProgressLabel)

    activeProgressLabel := na


if showProgressMarker and (setupState == 2 or setupState == 3 or setupState == 4)

    color markerColor = f_state_color(setupState, setupDir)

    float markerPrice = setupState == 2 ? projectionOrigin : close

    if na(activeProgressLine)

        activeProgressLine := line.new(bar_index, markerPrice, bar_index + 8, markerPrice, xloc = xloc.bar_index, extend = extend.none, color = markerColor, style = line.style_solid, width = 2)

    else

        line.set_x1(activeProgressLine, bar_index)

        line.set_x2(activeProgressLine, bar_index + 8)

        line.set_y1(activeProgressLine, markerPrice)

        line.set_y2(activeProgressLine, markerPrice)

        line.set_color(activeProgressLine, markerColor)

    string progressText = "Progress " + f_format_pct(projectionProgress)

    if na(activeProgressLabel)

        activeProgressLabel := label.new(bar_index + 9, markerPrice, progressText, xloc = xloc.bar_index, yloc = yloc.price, style = label.style_label_left, color = color.new(markerColor, 0), textcolor = f_text_on(markerColor), size = f_size(labelFontInput))

    else

        label.set_x(activeProgressLabel, bar_index + 9)

        label.set_y(activeProgressLabel, markerPrice)

        label.set_text(activeProgressLabel, progressText)

        label.set_color(activeProgressLabel, color.new(markerColor, 0))

        label.set_textcolor(activeProgressLabel, f_text_on(markerColor))

        label.set_size(activeProgressLabel, f_size(labelFontInput))


// -------------------------------------------------

// Panel

// -------------------------------------------------

var table agPanel = table.new(f_panel_position(panelLocation), 4, 6, bgcolor = f_panel_bg(), frame_color = f_panel_border(), frame_width = 1, border_color = f_panel_border(), border_width = 1)


if barstate.islast

    table.set_position(agPanel, f_panel_position(panelLocation))

    table.clear(agPanel, 0, 0, 3, 5)

    if showPanel

        table.merge_cells(agPanel, 0, 0, 3, 0)

        f_panel_cell(agPanel, 0, 0, "AG Pro Measured Move Projection Zones", headerText, headerBg, panelFontInput, "center")

        color stateColor = f_state_color(setupState, setupDir)

        string qualityText = na(impulseQuality) ? "n/a" : str.format("{0,number,#}/100", impulseQuality)

        string progressPanelText = f_format_pct(projectionProgress)

        float targetDistance = not na(targetMid) ? math.abs(targetMid - close) : na

        float targetDistanceAtr = not na(targetDistance) and atr > 0 ? targetDistance / atr : na

        string distanceText = f_format_atr(targetDistanceAtr)

        string mmRateText = mmTotal > 0 ? str.tostring(int(math.round(mmRate))) + "% (" + str.tostring(mmTotal) + ")" : "Building"

        color mmRateCol = na(mmRate) ? f_panel_value() : mmRate >= 55.0 ? bullColor : mmRate >= 40.0 ? neutralColor : bearColor

        color row1 = f_panel_alt_bg(1)

        color row2 = f_panel_alt_bg(2)

        color row3 = f_panel_alt_bg(3)

        color row4 = f_panel_alt_bg(4)

        color row5 = f_panel_alt_bg(5)

        f_panel_cell(agPanel, 0, 1, "State", f_panel_label(), row1, panelFontInput, "left")

        f_panel_cell(agPanel, 1, 1, f_state_text(setupState), f_state_on_panel(stateColor), row1, panelFontInput, "right")

        f_panel_cell(agPanel, 2, 1, "Direction", f_panel_label(), row1, panelFontInput, "left")

        f_panel_cell(agPanel, 3, 1, f_direction_text(setupDir), f_panel_value(), row1, panelFontInput, "right")

        f_panel_cell(agPanel, 0, 2, "Impulse Quality", f_panel_label(), row2, panelFontInput, "left")

        f_panel_cell(agPanel, 1, 2, qualityText, f_panel_value(), row2, panelFontInput, "right")

        f_panel_cell(agPanel, 2, 2, "Progress", f_panel_label(), row2, panelFontInput, "left")

        f_panel_cell(agPanel, 3, 2, progressPanelText, f_panel_value(), row2, panelFontInput, "right")

        f_panel_cell(agPanel, 0, 3, "Target Distance", f_panel_label(), row3, panelFontInput, "left")

        f_panel_cell(agPanel, 1, 3, distanceText, f_panel_value(), row3, panelFontInput, "right")

        f_panel_cell(agPanel, 2, 3, "Target Band", f_panel_label(), row3, panelFontInput, "left")

        f_panel_cell(agPanel, 3, 3, f_format_price(targetMid), f_panel_value(), row3, panelFontInput, "right")

        f_panel_cell(agPanel, 0, 4, "Base Range", f_panel_label(), row4, panelFontInput, "left")

        f_panel_cell(agPanel, 1, 4, f_format_atr(not na(baseHeight) and atr > 0 ? baseHeight / atr : na), f_panel_value(), row4, panelFontInput, "right")

        f_panel_cell(agPanel, 2, 4, "Invalidation", f_panel_label(), row4, panelFontInput, "left")

        f_panel_cell(agPanel, 3, 4, f_format_price(invalidationPrice), f_panel_value(), row4, panelFontInput, "right")

        f_panel_cell(agPanel, 0, 5, "Action", f_panel_label(), row5, panelFontInput, "left")

        f_panel_cell(agPanel, 1, 5, f_action_text(setupState), f_state_on_panel(stateColor), row5, panelFontInput, "right")

        f_panel_cell(agPanel, 2, 5, "Band Hit Rate", f_panel_label(), row5, panelFontInput, "left")

        f_panel_cell(agPanel, 3, 5, mmRateText, mmRateCol, row5, panelFontInput, "right")


// -------------------------------------------------

// Alerts

// -------------------------------------------------

alertcondition(eventArmed, "Measured Move Armed", "Measured Move Projection Zones: a compact base has armed a new measured-move projection.")

alertcondition(eventProjection, "Projection Active", "Measured Move Projection Zones: price has moved beyond the base boundary and the projection is active.")

alertcondition(eventTarget, "Target Band Interaction", "Measured Move Projection Zones: price has interacted with the projected target band.")

alertcondition(eventInvalid, "Projection Invalidated", "Measured Move Projection Zones: price has closed beyond the invalidation guide.")

alertcondition(eventExpired, "Projection Expired", "Measured Move Projection Zones: the measured-move projection has expired without a final state.")


// Hidden reference plots

plot(mmRate, "Target Band Hit Rate", display = display.none)

plot(projectionProgress, "Projection Progress", display = display.none)


No comments:

Post a Comment