Liquidity Gravity Map
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «indicator liquidity gravity map for tradingview.pine» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Liquidity Gravity Map — это индикатор для TradingView, написанный на Pine Script v6. Он отображает на графике зоны ликвидности, формируемые на основе подтверждённых свинг-хаев и свинг-лоу, а также зон стоп-хант (длинные хвосты свечей за предыдущими максимумами или минимумами). Каждая зона получает комплексную оценку по нескольким факторам.
Индикатор включает расчёт сессионного VWAP с полосами стандартных отклонений и анализ принятия или отклонения цены на уровне VWAP. Зоны ранжируются по приоритету, и на графике отображается ограниченное количество наиболее значимых с градиентной заливкой и опциональными метками.
Параметры пользователя позволяют настроить период обзора, чувствительность зон, лимит памяти, скорость затухания оценок, тип VWAP-сессии, цветовую тему и видимость полос и меток.
Настройки программы
| Настройка | Описание |
|---|---|
| lookback_period | Период обзора — Количество баров, используемых для определения устаревших и неактивных зон. |
| zone_sensitivity | Чувствительность зон — Параметр, влияющий на ширину зон и допуск при слиянии близких уровней. |
| max_zones | Максимум зон — Максимальное количество зон, хранимых в памяти индикатора. |
| max_visible_zones | Максимум видимых зон — Максимальное количество зон, отображаемых на графике одновременно. |
| score_decay_rate | Скорость затухания оценки — Скорость экспоненциального уменьшения оценки у неприкасаемых зон со временем. |
| vwap_session_type | Тип сессии VWAP — Тип сессии для расчёта VWAP: RTH, день, неделя или месяц. |
| rth_session | Сессия RTH — Временной диапазон обычной торговой сессии для расчёта VWAP в режиме RTH. |
| show_vwap_bands | Показывать полосы VWAP — Включает или отключает отображение линий VWAP и полос стандартных отклонений. |
| color_theme | Цветовая тема — Цветовая схема отображения зон: Classic, Neon или Monochrome. |
| show_labels | Показывать метки зон — Включает или отключает отображение текстовых меток на зонах. |
Поиск по коду файла
indicator("Liquidity Gravity Map", shorttitle="LGM", overlay=true, max_boxes_count=300, max_labels_count=300)
groupCore = "Core"
groupVWAP = "VWAP Acceptance"
groupVis = "Visuals"
lookback_period = input.int(120, "Lookback Period", minval=30, maxval=500, group=groupCore)
zone_sensitivity = input.float(4.0, "Zone Sensitivity", minval=0.5, maxval=10.0, step=0.25, group=groupCore)
max_zones = input.int(40, "Max Zones (Memory Cap)", minval=10, maxval=150, group=groupCore)
max_visible_zones = input.int(4, "Max Visible Zones", minval=1, maxval=20, group=groupCore)
score_decay_rate = input.float(0.010, "Score Decay Rate", minval=0.001, maxval=0.050, step=0.001, group=groupCore)
vwap_session_type = input.string("Day", "VWAP Session Type", options=["RTH", "Day", "Week", "Month"], group=groupVWAP)
rth_session = input.session("0930-1600", "RTH Session", group=groupVWAP)
show_vwap_bands = input.bool(true, "Show VWAP Bands", group=groupVWAP)
color_theme = input.string("Classic", "Color Theme", options=["Classic", "Neon", "Monochrome"], group=groupVis)
show_labels = input.bool(true, "Show Zone Labels", group=groupVis)
var zonePrice = array.new_float()
var zoneTop = array.new_float()
var zoneBottom = array.new_float()
var zoneDir = array.new_int() // 1 = bullish support, -1 = bearish resistance
var zoneBirthBar = array.new_int()
var zoneLastTouch = array.new_int()
var zoneRevisits = array.new_int()
var zoneStructSum = array.new_float() // structural event intensity accumulator
var zoneStructObs = array.new_int() // structural observations
var zoneWickSum = array.new_float() // relevant wick ratio accumulator
var zoneWickObs = array.new_int() // wick observations
var zoneAbsorbSum = array.new_float() // absorption / defended retest accumulator
var zoneBreakSum = array.new_float() // clean break accumulator
var zoneScore = array.new_float() // live composite score 0-100
var zoneProxScore = array.new_float() // live proximity score 0-100
var zoneBoxes = array.new_box()
var zoneLabels = array.new_label()
var recentHighPivots = array.new_float()
var recentLowPivots = array.new_float()
f_clamp(float x, float lo, float hi) =>
math.max(lo, math.min(hi, x))
f_safe_div(float a, float b) =>
b != 0.0 ? a / b : 0.0
f_zone_halfwidth(float atrv) =>
math.max(syminfo.mintick * (2.0 + zone_sensitivity * 2.0), atrv * (0.035 + zone_sensitivity * 0.012))
f_gradient(bool bull, float score, bool border) =>
float t = f_clamp(score / 100.0, 0.0, 1.0)
color cool = na
color warm = na
if color_theme == "Classic"
cool := bull ? color.rgb(32, 166, 179) : color.rgb(111, 89, 224)
warm := bull ? color.rgb(255, 193, 7) : color.rgb(255, 82, 82)
else if color_theme == "Neon"
cool := bull ? color.rgb(0, 255, 170) : color.rgb(180, 90, 255)
warm := bull ? color.rgb(255, 235, 59) : color.rgb(255, 23, 68)
else
cool := bull ? color.rgb(115, 145, 155) : color.rgb(125, 125, 140)
warm := bull ? color.rgb(235, 235, 235) : color.rgb(245, 210, 210)
int r = int(math.round(color.r(cool) + (color.r(warm) - color.r(cool)) * t))
int g = int(math.round(color.g(cool) + (color.g(warm) - color.g(cool)) * t))
int b = int(math.round(color.b(cool) + (color.b(warm) - color.b(cool)) * t))
int alpha = border ? int(math.round(40.0 - 30.0 * t)) : int(math.round(88.0 - 58.0 * t))
color.rgb(r, g, b, f_clamp(alpha, 0, 100))
f_delete_zone_objects(int idx) =>
box bx = array.get(zoneBoxes, idx)
label lb = array.get(zoneLabels, idx)
if not na(bx)
box.delete(bx)
if not na(lb)
label.delete(lb)
array.set(zoneBoxes, idx, na)
array.set(zoneLabels, idx, na)
f_remove_zone(int idx) =>
f_delete_zone_objects(idx)
array.remove(zonePrice, idx)
array.remove(zoneTop, idx)
array.remove(zoneBottom, idx)
array.remove(zoneDir, idx)
array.remove(zoneBirthBar, idx)
array.remove(zoneLastTouch, idx)
array.remove(zoneRevisits, idx)
array.remove(zoneStructSum, idx)
array.remove(zoneStructObs, idx)
array.remove(zoneWickSum, idx)
array.remove(zoneWickObs, idx)
array.remove(zoneAbsorbSum, idx)
array.remove(zoneBreakSum, idx)
array.remove(zoneScore, idx)
array.remove(zoneProxScore, idx)
array.remove(zoneBoxes, idx)
array.remove(zoneLabels, idx)
f_prune_one() =>
if array.size(zonePrice) > 0
int worstIdx = 0
float worstMetric = 1e10
for i = 0 to array.size(zonePrice) - 1
float metric = array.get(zoneScore, i) - (bar_index - array.get(zoneBirthBar, i)) * 0.02
if metric < worstMetric
worstMetric := metric
worstIdx := i
f_remove_zone(worstIdx)
f_add_or_merge_zone(float price, int dir, int eventBar, float halfWidth, float structBoost, float wickBoost) =>
int found = -1
float mergeTol = math.max(halfWidth * 1.25, syminfo.mintick * zone_sensitivity * 2.0)
if array.size(zonePrice) > 0
for i = 0 to array.size(zonePrice) - 1
if array.get(zoneDir, i) == dir and math.abs(array.get(zonePrice, i) - price) <= mergeTol
found := i
break
if found != -1
float oldPrice = array.get(zonePrice, found)
float oldTop = array.get(zoneTop, found)
float oldBot = array.get(zoneBottom, found)
float oldStructSum = array.get(zoneStructSum, found)
float weightOld = math.max(oldStructSum, 1.0)
float weightNew = math.max(structBoost, 1.0)
float mergedPrice = (oldPrice * weightOld + price * weightNew) / (weightOld + weightNew)
float mergedHalf = math.max((oldTop - oldBot) * 0.5, halfWidth)
array.set(zonePrice, found, mergedPrice)
array.set(zoneTop, found, mergedPrice + mergedHalf)
array.set(zoneBottom, found, mergedPrice - mergedHalf)
array.set(zoneBirthBar, found, math.min(array.get(zoneBirthBar, found), eventBar))
array.set(zoneStructSum, found, array.get(zoneStructSum, found) + structBoost)
array.set(zoneStructObs, found, array.get(zoneStructObs, found) + 1)
if wickBoost > 0
array.set(zoneWickSum, found, array.get(zoneWickSum, found) + wickBoost)
array.set(zoneWickObs, found, array.get(zoneWickObs, found) + 1)
else
if array.size(zonePrice) >= max_zones
f_prune_one()
array.push(zonePrice, price)
array.push(zoneTop, price + halfWidth)
array.push(zoneBottom, price - halfWidth)
array.push(zoneDir, dir)
array.push(zoneBirthBar, eventBar)
array.push(zoneLastTouch, na)
array.push(zoneRevisits, 0)
array.push(zoneStructSum, structBoost)
array.push(zoneStructObs, 1)
array.push(zoneWickSum, math.max(wickBoost, 0.0))
array.push(zoneWickObs, wickBoost > 0 ? 1 : 0)
array.push(zoneAbsorbSum, 0.0)
array.push(zoneBreakSum, 0.0)
array.push(zoneScore, 0.0)
array.push(zoneProxScore, 0.0)
array.push(zoneBoxes, na)
array.push(zoneLabels, na)
float atrv = ta.atr(14)
float volSma = ta.sma(volume, 20)
float barRange = math.max(high - low, syminfo.mintick)
float bodyRatio = math.abs(close - open) / barRange
float upperWickRatio = (high - math.max(open, close)) / barRange
float lowerWickRatio = (math.min(open, close) - low) / barRange
int swing_len = int(math.round(math.max(2.0, zone_sensitivity + 1.0)))
int pivot_memory = 20
int sweep_window = int(math.max(5.0, math.round(lookback_period * 0.25)))
var float cumPV = 0.0
var float cumV = 0.0
var float cumPV2 = 0.0
var float sessVWAP = na
var float sessSD = na
var int barsFarBelow = 0
var int barsBelow1 = 0
var int barsMid = 0
var int barsAbove1 = 0
var int barsFarAbove = 0
var int sessionBars = 0
var int bullReclaims = 0
var int bearRejects = 0
bool inRth = not na(time(timeframe.period, rth_session))
bool prevInRth = bar_index > 0 ? inRth[1] : false
bool vwapActive = vwap_session_type == "RTH" ? inRth : true
bool newAnchor =
vwap_session_type == "RTH" ? (inRth and not prevInRth) :
vwap_session_type == "Week" ? timeframe.change("W") :
vwap_session_type == "Month"? timeframe.change("M") :
timeframe.change("D")
if barstate.isconfirmed
if newAnchor and vwapActive
cumPV := hlc3 * volume
cumV := volume
cumPV2 := hlc3 * hlc3 * volume
barsFarBelow := 0
barsBelow1 := 0
barsMid := 0
barsAbove1 := 0
barsFarAbove := 0
sessionBars := 0
bullReclaims := 0
bearRejects := 0
else if vwapActive
cumPV += hlc3 * volume
cumV += volume
cumPV2 += hlc3 * hlc3 * volume
if vwapActive and cumV > 0
sessVWAP := cumPV / cumV
float variance = math.max(cumPV2 / cumV - sessVWAP * sessVWAP, 0.0)
sessSD := math.sqrt(variance)
sessionBars += 1
float zClose = sessSD > 0 ? (close - sessVWAP) / sessSD : 0.0
if zClose <= -2.0
barsFarBelow += 1
else if zClose < -1.0
barsBelow1 += 1
else if zClose <= 1.0
barsMid += 1
else if zClose < 2.0
barsAbove1 += 1
else
barsFarAbove += 1
bool bullReclaimEvent = not na(sessVWAP[1]) and close > sessVWAP and close[1] <= sessVWAP[1]
bool bearRejectEvent = close < sessVWAP and high >= sessVWAP
if bullReclaimEvent
bullReclaims += 1
if bearRejectEvent
bearRejects += 1
if barstate.isconfirmed
float ph = ta.pivothigh(high, swing_len, swing_len)
if not na(ph)
float atrAtPivot = nz(atrv[swing_len], atrv)
float tol = math.max(syminfo.mintick * zone_sensitivity * 2.0, atrAtPivot * zone_sensitivity * 0.08)
int eqCount = 0
if array.size(recentHighPivots) > 0
int startIdx = math.max(0, array.size(recentHighPivots) - pivot_memory)
for i = startIdx to array.size(recentHighPivots) - 1
if math.abs(array.get(recentHighPivots, i) - ph) <= tol
eqCount += 1
float pivotRange = math.max(high[swing_len] - low[swing_len], syminfo.mintick)
float pivotUpperWick = high[swing_len] - math.max(open[swing_len], close[swing_len])
float pivotWickRatio = pivotUpperWick / pivotRange
float structBoost = eqCount > 0 ? math.min(95.0, 60.0 + eqCount * 12.0) : 52.0
f_add_or_merge_zone(ph, -1, bar_index - swing_len, f_zone_halfwidth(atrAtPivot), structBoost, pivotWickRatio)
array.push(recentHighPivots, ph)
if array.size(recentHighPivots) > pivot_memory
array.shift(recentHighPivots)
float pl = ta.pivotlow(low, swing_len, swing_len)
if not na(pl)
float atrAtPivot = nz(atrv[swing_len], atrv)
float tol = math.max(syminfo.mintick * zone_sensitivity * 2.0, atrAtPivot * zone_sensitivity * 0.08)
int eqCount = 0
if array.size(recentLowPivots) > 0
int startIdx = math.max(0, array.sizeПоказано первые 50% кода (символов: 16198)
Открыть по тарифу
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
Разовая покупка файла
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.