ایچیموکو · اندیکاتور
ایچیموکو پیشرفته: ابر تایمفریم بالاتر، حمایت و مقاومت و جدول وضعیت
ایچیموکو کامل روی چارت برای TradingView، متاتریدر ۵ و متاتریدر ۴، بههمراه ابر تایمفریم بالاتر (بدون ریپینت)، سطوح حمایت و مقاومت رتبهبندیشده از کیجون و ابر، جدول وضعیت ۱۹ ردیفه با امتیاز ترکیبی و آلارم.
// Crypex.org | Ichimoku Advanced: HTF Kumo + S/R levels + status table. Educational tool, not investment advice.
// © Crypex.org — All rights reserved. | حق نشر محفوظ برای Crypex.org
// License: personal, non-commercial use only. Redistribution or resale without written permission is prohibited.
// مجوز: استفاده شخصی و غیرتجاری. بازنشر یا فروش مجدد بدون اجازهی کتبی ممنوع است.
//@version=6
// Crypex — Ichimoku Kinko Hyo overlay with higher-timeframe Kumo, S/R levels and alerts.
// Math: Tenkan/Kijun = Donchian mid; Senkou A = avg(Tenkan,Kijun); Senkou B = Donchian(spanB);
// Displacement counts the current bar (TradingView convention): Senkou drawn displacement-1 bars ahead,
// Chikou displacement-1 bars behind. MTF values are confirmed HTF bars only (no repaint).
indicator(title = 'Crypex Ichimoku', shorttitle = 'Crypex Ichimoku', overlay = true, max_bars_back = 5000, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500)
// Brand palette (crypex.org logo) + monochrome palette (fills use transparency)
STYLE_BRAND = 'Crypex'
STYLE_COLORED = 'Colored'
STYLE_MONO = 'Monochrome'
BRAND_GREEN = #0BA31C
BRAND_RED = #FF0A17
BRAND_GREY = #787B86
BRAND_DARK = #1E2229
M_LIGHT = color.rgb(178, 181, 190)
M_DARK = color.rgb(93, 96, 107)
M_MID = color.rgb(135, 139, 148)
M_ACCENT = color.rgb(157, 160, 168)
//=== 1) Inputs ==================================================================
GRP_THEME = '0 · Theme'
GRP_PERIODS = '1 · Periods (9 / 26 / 52)'
GRP_SHIFT = '2 · Displacement (Senkou + / Chikou −)'
GRP_LINES = '3 · Lines & visibility'
GRP_CLOUD = '4 · Kumo (chart TF)'
GRP_MTF = '5 · Higher timeframe Kumo'
GRP_MARK = '6 · Chart markers (visual only)'
GRP_BG = '7 · Price vs Kumo'
GRP_TBL = '8 · Status table'
stylePreset = input.string(
STYLE_BRAND,
'Style',
group = GRP_THEME,
options = [STYLE_BRAND, STYLE_COLORED, STYLE_MONO],
tooltip = 'Crypex = brand green / red theme (default).\nColored = theme uses your picker colours.\nMonochrome = grey/neutral Ichimoku for clean charts & screenshots.\nMono tints underlying candles whenever Color candles is OFF so underlying exchange candles do not bleed through (uses barcolor — low plot quota).')
isMono = stylePreset == STYLE_MONO
isBrand = stylePreset == STYLE_BRAND
conversionPeriods = input.int(9, 'Tenkan · Conversion', minval = 1, group = GRP_PERIODS)
basePeriods = input.int(26, 'Kijun · Base', minval = 1, group = GRP_PERIODS)
laggingSpanBPeriods = input.int(52, 'Senkou Span B', minval = 1, group = GRP_PERIODS)
flatBars = input.int(10, 'Min flat bars', minval = 2, maxval = 30, group = GRP_PERIODS, tooltip = 'Kijun / Senkou B count as flat only if they stayed at the same price for this many consecutive bars.\n3 = minimum | 5 = standard | 10+ = strong')
displacement = input.int(26, 'Displacement', minval = 1, group = GRP_SHIFT, tooltip = 'Classic: 26 (equals Kijun length). Counts the current bar, like the TradingView built-in: Senkou is drawn displacement-1 bars AHEAD, Chikou displacement-1 bars BEHIND.')
shift = displacement - 1
showTenkan = input.bool(true, 'Tenkan', group = GRP_LINES, inline = 'tk')
tenkanColor = input.color(color.new(#2962FF, 0), '', group = GRP_LINES, inline = 'tk')
tenkanWidth = input.int(2, 'W', minval = 1, maxval = 4, group = GRP_LINES, inline = 'tkw')
showKijun = input.bool(true, 'Kijun', group = GRP_LINES, inline = 'kj')
kijunColor = input.color(color.new(#B71C1C, 0), '', group = GRP_LINES, inline = 'kj')
kijunWidth = input.int(2, 'W', minval = 1, maxval = 4, group = GRP_LINES, inline = 'kjw')
showChikou = input.bool(true, 'Chikou', group = GRP_LINES, inline = 'ch')
chikouColor = input.color(color.new(#43A047, 0), '', group = GRP_LINES, inline = 'ch')
chikouWidth = input.int(2, 'W', minval = 1, maxval = 4, group = GRP_LINES, inline = 'chw')
showSenkouLines = input.bool(false, 'Senkou A/B outlines', group = GRP_LINES)
showKumoMid = input.bool(false, 'Kumo midpoint (equilibrium)', group = GRP_LINES, tooltip = 'Mid-price between current Kumo top & bottom (lagged Senkou).')
showKumo = input.bool(true, 'Kumo fill', group = GRP_CLOUD)
bullCloudColor = input.color(#089981, 'Bull', group = GRP_CLOUD, inline = 'cf')
bearCloudColor = input.color(#F23645, 'Bear', group = GRP_CLOUD, inline = 'cf')
cloudTransparency = input.int(75, 'Transparency', minval = 0, maxval = 95, group = GRP_CLOUD)
senkouAColor = input.color(color.new(#A5D6A7, 0), 'Line A', group = GRP_CLOUD, inline = 'sa')
senkouBColor = input.color(color.new(#EF9A9A, 0), 'Line B', group = GRP_CLOUD, inline = 'sb')
showMtfCloud = input.bool(true, 'Show HTF Kumo', group = GRP_MTF, tooltip = 'Overlay the Ichimoku cloud of a higher timeframe.\nAuto mode picks the next standard TF above the chart (1m→1H, 5m/15m/30m→4H, 1H→4H, 4H→D, D→W, W→M).')
mtfAutoTf = input.bool(true, 'Auto next TF', group = GRP_MTF, tooltip = 'When ON the higher TF is chosen automatically (chart TF → next standard step). Turn OFF to pick manually.')
mtfTimeframe = input.timeframe('240', 'Manual TF', group = GRP_MTF, tooltip = 'Used only when "Auto next TF" is OFF.')
mtfBullColor = input.color(color.new(#089981, 82), 'HTF bull', group = GRP_MTF, inline = 'mtfc')
mtfBearColor = input.color(color.new(#F23645, 82), 'HTF bear', group = GRP_MTF, inline = 'mtfc')
mtfCloudTransp = input.int(82, 'Opacity', minval = 50, maxval = 99, group = GRP_MTF, inline = 'mtfc', tooltip = 'Transparency of the HTF Kumo fill (50 = more opaque, 99 = nearly invisible).')
calendarMode = input.string('Auto', 'Market calendar', options = ['Auto', '24/7', 'Weekdays only'], group = GRP_MTF, tooltip = 'Positions the HTF Kumo ahead of price correctly.\nAuto: crypto = 24/7, everything else (forex, gold, indices, stocks) = weekdays only.\nOnly affects weekly/monthly HTF.')
ratioOverride = input.float(0.0, 'Chart bars per HTF bar (0 = auto)', minval = 0, step = 0.5, group = GRP_MTF, tooltip = 'Advanced. For short-session markets (e.g. stocks on intraday charts) type the real number of chart bars inside one HTF bar.')
mtfShowLines = input.bool(false, 'HTF Senkou A/B lines', group = GRP_MTF)
mtfShowKijun = input.bool(true, 'HTF Kijun', group = GRP_MTF, inline = 'htfkj')
mtfKijunColor = input.color(color.new(#B71C1C, 20), '', group = GRP_MTF, inline = 'htfkj')
mtfKijunWidth = input.int(1, 'W', minval = 1, maxval = 4, group = GRP_MTF, inline = 'htfkj')
mtfShowTenkan = input.bool(false, 'HTF Tenkan', group = GRP_MTF, inline = 'htftk')
mtfTenkanColor = input.color(color.new(#2962FF, 20), '', group = GRP_MTF, inline = 'htftk')
mtfTenkanWidth = input.int(1, 'W', minval = 1, maxval = 4, group = GRP_MTF, inline = 'htftk')
markTkCross = input.bool(true, 'TK cross', group = GRP_MARK, inline = 'tkx')
markTkSize = input.string('small', '', options = ['tiny', 'small', 'normal'], group = GRP_MARK, inline = 'tkx')
markKijunCross = input.bool(false, 'Price × Kijun', group = GRP_MARK, inline = 'pj')
markTenkanCross = input.bool(false, 'Price × Tenkan', group = GRP_MARK, inline = 'pt')
markKumoTwist = input.bool(false, 'Kumo twist', group = GRP_MARK, inline = 'ktw')
markKumoBreak = input.bool(false, 'Kumo breakout', group = GRP_MARK, tooltip = 'Close crosses Kumo top (bull) or Kumo bottom (bear).')
markTkFilter = input.bool(false, 'Only TK in trend', group = GRP_MARK, tooltip = 'If on: bullish TK only when price above Kumo & green cloud; bearish TK only when price below Kumo & red cloud.')
shadeVsKumo = input.bool(false, 'Tint bg vs Kumo', group = GRP_BG)
shadeBull = input.color(color.new(#089981, 93), 'Above', group = GRP_BG, inline = 'sh')
shadeBear = input.color(color.new(#F23645, 93), 'Below', group = GRP_BG, inline = 'sh')
shadeInside = input.color(color.new(#787B86, 94), 'Inside', group = GRP_BG, inline = 'sh')
colorBars = input.bool(false, 'Color candles', group = GRP_BG, tooltip = 'Overlay OHLC tint by regime.')
barPalette = input.string('Kumo', 'Palette', options = ['Kumo', 'TK stack', 'Both'], group = GRP_BG, tooltip = 'Kumo = above/inside/below cloud. TK stack = Tenkan vs Kijun bias layered. Both = average of the two tints.')
neutralBarColor = input.color(color.new(#787B86, 78), 'Neutral', group = GRP_BG, inline = 'nb')
showTable = input.bool(true, 'Show table', group = GRP_TBL)
tablePos = input.string(position.top_right, 'Position', options = [position.top_right, position.top_left, position.bottom_right, position.bottom_left], group = GRP_TBL)
tableSizeStr = input.string('small', 'Text', options = ['tiny', 'small', 'normal', 'large', 'huge'], group = GRP_TBL)
showDistRows = input.bool(true, 'Rows: thickness & distance', group = GRP_TBL)
showAtrRow = input.bool(true, 'Row: ATR context', group = GRP_TBL)
showMtfInTable = input.bool(true, 'Row: HTF regime', group = GRP_TBL)
showSRLines = input.bool(true, 'S/R levels on chart', group = GRP_TBL)
srExtend = input.int(20, 'Extend (bars →)', minval = 1, maxval = 200, group = GRP_TBL, inline = 'sre')
srCluster = input.bool(true, 'Cluster zones', group = GRP_TBL, tooltip = 'Levels within 0.25 ATR of each other are merged into a shaded zone instead of separate lines.')
srMaxLevels = input.int(10, 'Max levels per side', minval = 1, maxval = 20, group = GRP_TBL, tooltip = 'Maximum number of support levels and resistance levels to draw. Nearest ones are always shown first.')
GRP_WM = '9 · Watermark'
showWatermark = input.bool(true, 'Show crypex.org watermark', group = GRP_WM)
wmPos = input.string(position.bottom_left, 'Position', options = [position.bottom_left, position.bottom_center, position.bottom_right, position.middle_center, position.top_center, position.top_left], group = GRP_WM)
wmGap = input.float(5.0, 'Gap from left (%)', minval = 0, maxval = 30, step = 0.5, group = GRP_WM, tooltip = 'Bottom-left only: empty space (in % of chart width) so the watermark sits right beside the TradingView logo instead of on top of it.')
wmSizeStr = input.string('large', 'Size', options = ['small', 'normal', 'large', 'huge'], group = GRP_WM)
wmTransp = input.int(0, 'Transparency', minval = 0, maxval = 95, group = GRP_WM, tooltip = '0 = exact logo colours, 95 = barely visible.')
tableSize = tableSizeStr == 'tiny' ? size.tiny : tableSizeStr == 'normal' ? size.normal : tableSizeStr == 'large' ? size.large : tableSizeStr == 'huge' ? size.huge : size.small
// ── Auto next-TF selection ────────────────────────────────────────────────────
// Maps current chart TF → next standard step (1m→1H, ≤30m→4H, 1H→4H, 4H→D, D→W, W/M→M)
int _chartSec0 = timeframe.in_seconds()
string _autoNextTf =
_chartSec0 <= 60 ? '60' :
_chartSec0 <= 3600 ? '240' :
_chartSec0 <= 14400 ? '1D' :
_chartSec0 <= 86400 ? '1W' : '1M'
string mtfTfStr = mtfAutoTf ? _autoNextTf : mtfTimeframe
// Effective colours (Crypex = brand palette; Monochrome = greyscale; Colored = inputs)
cBull = isBrand ? BRAND_GREEN : #089981
cBear = isBrand ? BRAND_RED : #F23645
effTenkanCol = isMono ? M_LIGHT : isBrand ? BRAND_GREEN : tenkanColor
effKijunCol = isMono ? M_DARK : isBrand ? BRAND_RED : kijunColor
effChikouCol = isMono ? M_ACCENT : isBrand ? BRAND_GREY : chikouColor
effSenkAline = isMono ? color.new(M_LIGHT, 12) : isBrand ? color.new(BRAND_GREEN, 30) : senkouAColor
effSenkBline = isMono ? color.new(M_DARK, 12) : isBrand ? color.new(BRAND_RED, 30) : senkouBColor
effBullCloud = isMono ? M_LIGHT : isBrand ? BRAND_GREEN : bullCloudColor
effBearCloud = isMono ? M_DARK : isBrand ? BRAND_RED : bearCloudColor
effMtfBullFill = isMono ? color.new(M_LIGHT, mtfCloudTransp) : color.new(isBrand ? BRAND_GREEN : mtfBullColor, mtfCloudTransp)
effMtfBearFill = isMono ? color.new(M_DARK, mtfCloudTransp) : color.new(isBrand ? BRAND_RED : mtfBearColor, mtfCloudTransp)
effMtfKijun = isMono ? color.new(M_DARK, 20) : isBrand ? color.new(BRAND_RED, 20) : mtfKijunColor
effMtfTenkan = isMono ? color.new(M_LIGHT, 20) : isBrand ? color.new(BRAND_GREEN, 20) : mtfTenkanColor
effKumoMidCol = isMono ? color.new(M_MID, 25) : color.new(#B8860B, 30)
effShadeAbove = isMono ? color.new(M_LIGHT, 93) : isBrand ? color.new(BRAND_GREEN, 93) : shadeBull
effShadeBelow = isMono ? color.new(M_DARK, 93) : isBrand ? color.new(BRAND_RED, 93) : shadeBear
effShadeIn = isMono ? color.new(M_MID, 94) : shadeInside
effTblHdrBg = isMono ? color.new(M_DARK, 25) : isBrand ? color.new(BRAND_GREEN, 15) : color.new(#2157F3, 15)
effTwistUpCol = isMono ? M_LIGHT : isBrand ? BRAND_GREEN : color.lime
effTwistDnCol = isMono ? M_DARK : isBrand ? BRAND_RED : color.red
effKboBull = isMono ? M_LIGHT : cBull
effKboBear = isMono ? M_DARK : cBear
effNeutralBar = isMono ? color.new(M_MID, 0) : neutralBarColor
//=== 2) Core series ==============================================================
donchianMid(int len) =>
(ta.lowest(low, len) + ta.highest(high, len)) / 2.0
// True flatness: the value stayed within one tick over the whole window, not just at both ends.
isFlat(float src, int len) =>
ta.highest(src, len) - ta.lowest(src, len) <= syminfo.mintick
tenkan = donchianMid(conversionPeriods)
kijun = donchianMid(basePeriods)
senkouA = (tenkan + kijun) / 2.0
senkouB = donchianMid(laggingSpanBPeriods)
//=== 3) Current-bar Kumo (Senkou values from `shift` bars ago) ==================
float kumoTop = math.max(senkouA[shift], senkouB[shift])
float kumoBottom = math.min(senkouA[shift], senkouB[shift])
bool kumoBull = senkouA[shift] > senkouB[shift]
bool hasKumo = not na(kumoTop) and not na(kumoBottom)
float kumoThick = hasKumo ? kumoTop - kumoBottom : na
bool aboveKumo = hasKumo and close > kumoTop
bool belowKumo = hasKumo and close < kumoBottom
bool insideKumo = hasKumo and not aboveKumo and not belowKumo
bool tkStackBull = tenkan > kijun
bool tkStackBear = tenkan < kijun
tintKumoMono = not hasKumo ? effNeutralBar : aboveKumo ? color.new(M_LIGHT, 0) : belowKumo ? color.new(M_DARK, 0) : color.new(M_MID, 0)
tintTkMono = tkStackBull ? color.new(M_LIGHT, 0) : tkStackBear ? color.new(M_DARK, 0) : effNeutralBar
tintBothMono =
hasKumo and aboveKumo and tkStackBull ? color.new(M_LIGHT, 0) :
hasKumo and belowKumo and tkStackBear ? color.new(M_DARK, 0) :
hasKumo and insideKumo ? color.new(M_MID, 0) :
not hasKumo ? effNeutralBar : color.new(M_ACCENT, 0)
//=== 4) ATR & distances ============================================================
atr14 = ta.atr(14)
float dTop = hasKumo ? kumoTop - close : na
float dBot = hasKumo ? close - kumoBottom : na
float distNearest = hasKumo ? math.min(math.abs(dTop), math.abs(dBot)) : na
float distNearestAtr = atr14 > 0 and not na(distNearest) ? distNearest / atr14 : na
float thickTicks = hasKumo and syminfo.mintick > 0 ? kumoThick / syminfo.mintick : na
float dTopTicks = hasKumo and syminfo.mintick > 0 ? dTop / syminfo.mintick : na
float dBotTicks = hasKumo and syminfo.mintick > 0 ? dBot / syminfo.mintick : na
//=== 5) MTF Ichimoku (same parameters on HTF OHLC) =================================
chartSec = timeframe.in_seconds()
mtfSec = timeframe.in_seconds(mtfTfStr)
bool mtfHigherTf = mtfSec > chartSec and mtfSec > 0 and chartSec > 0
// Weekday-only markets (forex, gold, stocks) have 5 trading days per week, not 7: a weekly/monthly HTF bar holds
// only 5/7 of the chart bars that pure time maths gives. Crypto trades 24/7, so no correction.
bool weekdaysOnly = calendarMode == 'Weekdays only' or (calendarMode == 'Auto' and syminfo.type != 'crypto')
float weekFactor = weekdaysOnly and mtfSec >= 604800 and chartSec < 604800 ? 5.0 / 7.0 : 1.0
// Chart bars per HTF bar (exact on 24/7 markets; corrected for the 5-day week; short-session stocks: use the override).
float mtfRatio = mtfHigherTf ? (ratioOverride > 0 ? ratioOverride : float(mtfSec) / chartSec * weekFactor) : 1.0
// A confirmed HTF bar reaches the chart one HTF bar late, so the forward shift is (shift - 1) HTF bars.
int mtfForwardBars = mtfHigherTf ? math.max(0, math.round((shift - 1) * mtfRatio)) : shift
// Too large a gap (e.g. 1m chart with 1D HTF) would exceed max_bars_back and break the script.
bool mtfTooFar = mtfHigherTf and mtfForwardBars > 4900
bool mtfIsHigher = mtfHigherTf and not mtfTooFar
// Confirmed values only ([1] + lookahead_on): no repainting on the live HTF bar.
// The current HTF Kumo (Senkou from `shift` HTF bars ago) is read on the HTF itself, so it is exact.
htfIchimoku() =>
t = donchianMid(conversionPeriods)
k = donchianMid(basePeriods)
a = (t + k) / 2.0
b = donchianMid(laggingSpanBPeriods)
kLag = math.max(shift, 1)
// Flatness measured in HTF bars (on the chart TF the HTF series is a staircase and always looks flat).
fTop = isFlat(math.max(a, b), flatBars + 1)
fBot = isFlat(math.min(a, b), flatBars + 1)
fB = isFlat(b, flatBars + 1)
[t[1], k[1], a[1], b[1], a[kLag], b[kLag], fTop[kLag], fBot[kLag], fB[kLag]]
[mtfTenkan, mtfKijun, mtfSenkouA, mtfSenkouB, mtfKumoA, mtfKumoB, mtfTopFlat, mtfBotFlat, mtfKumoBFlat] = request.security(syminfo.tickerid, mtfTfStr, htfIchimoku(), lookahead = barmerge.lookahead_on)
float mtfTop = math.max(mtfKumoA, mtfKumoB)
float mtfBot = math.min(mtfKumoA, mtfKumoB)
bool mtfHas = not na(mtfTop) and not na(mtfBot)
bool mtfAbove = mtfHas and close > mtfTop
bool mtfBelow = mtfHas and close < mtfBot
bool mtfCloudBull = mtfKumoA > mtfKumoB
lineColorA = showSenkouLines ? effSenkAline : color(na)
lineColorB = showSenkouLines ? effSenkBline : color(na)
pA = plot(showKumo or showSenkouLines ? senkouA : na, title = 'Senkou Span A', color = lineColorA, linewidth = 1, offset = shift)
pB = plot(showKumo or showSenkouLines ? senkouB : na, title = 'Senkou Span B', color = lineColorB, linewidth = 1, offset = shift)
fill(pA, pB, title = 'Kumo', color = showKumo ? (senkouA > senkouB ? color.new(effBullCloud, cloudTransparency) : color.new(effBearCloud, cloudTransparency)) : na)
mtfLineColA = mtfShowLines ? effSenkAline : color(na)
mtfLineColB = mtfShowLines ? effSenkBline : color(na)
pMtfA = plot(showMtfCloud and mtfIsHigher ? mtfSenkouA : na, title = 'HTF Senkou A', color = mtfLineColA, linewidth = 1, offset = mtfForwardBars)
pMtfB = plot(showMtfCloud and mtfIsHigher ? mtfSenkouB : na, title = 'HTF Senkou B', color = mtfLineColB, linewidth = 1, offset = mtfForwardBars)
fill(
pMtfA,
pMtfB,
title = 'HTF Kumo',
color = showMtfCloud and mtfIsHigher ? (mtfSenkouA > mtfSenkouB ? effMtfBullFill : effMtfBearFill) : na)
// Tenkan / Kijun are not displaced — they belong at the current bar.
plot(showMtfCloud and mtfIsHigher and mtfShowKijun ? mtfKijun : na, title = 'HTF Kijun', color = effMtfKijun, linewidth = mtfKijunWidth)
plot(showMtfCloud and mtfIsHigher and mtfShowTenkan ? mtfTenkan : na, title = 'HTF Tenkan', color = effMtfTenkan, linewidth = mtfTenkanWidth)
//=== 6) Lines ====================================================================
plot(showTenkan ? tenkan : na, title = 'Tenkan-sen', color = effTenkanCol, linewidth = tenkanWidth)
plot(showKijun ? kijun : na, title = 'Kijun-sen', color = effKijunCol, linewidth = kijunWidth)
plot(showChikou ? close : na, title = 'Chikou Span', color = effChikouCol, linewidth = chikouWidth, offset = -shift)
kumoMid = hasKumo ? (kumoTop + kumoBottom) / 2 : na
plot(showKumoMid and hasKumo ? kumoMid : na, title = 'Kumo midpoint', color = effKumoMidCol, linewidth = 1, style = plot.style_line)
//=== 7) Marker logic =============================================================
tkBull = ta.crossover(tenkan, kijun)
tkBear = ta.crossunder(tenkan, kijun)
tkBullF = markTkFilter ? tkBull and aboveKumo and kumoBull : tkBull
tkBearF = markTkFilter ? tkBear and belowKumo and not kumoBull : tkBear
pkBull = ta.crossover(close, kijun)
pkBear = ta.crossunder(close, kijun)
ptBull = ta.crossover(close, tenkan)
ptBear = ta.crossunder(close, tenkan)
kumoTwistUp = ta.crossover(senkouA, senkouB)
kumoTwistDn = ta.crossunder(senkouA, senkouB)
// Breakouts vs cloud boundaries (lagged Kumo at this bar).
kumoBullBO = ta.crossover(close, kumoTop)
kumoBearBO = ta.crossunder(close, kumoBottom)
plotshape(markTkCross and tkBullF and markTkSize == 'tiny', title = 'TK+ tiny', style = shape.triangleup, location = location.belowbar, size = size.tiny, color = color.new(effTenkanCol, 10), text = 'TK+', textcolor = effTenkanCol)
plotshape(markTkCross and tkBullF and markTkSize == 'small', title = 'TK+ sm', style = shape.triangleup, location = location.belowbar, size = size.small, color = color.new(effTenkanCol, 10), text = 'TK+', textcolor = effTenkanCol)
plotshape(markTkCross and tkBullF and markTkSize == 'normal', title = 'TK+ md', style = shape.triangleup, location = location.belowbar, size = size.normal, color = color.new(effTenkanCol, 10), text = 'TK+', textcolor = effTenkanCol)
plotshape(markTkCross and tkBearF and markTkSize == 'tiny', title = 'TK− tiny', style = shape.triangledown, location = location.abovebar, size = size.tiny, color = color.new(effKijunCol, 10), text = 'TK−', textcolor = effKijunCol)
plotshape(markTkCross and tkBearF and markTkSize == 'small', title = 'TK− sm', style = shape.triangledown, location = location.abovebar, size = size.small, color = color.new(effKijunCol, 10), text = 'TK−', textcolor = effKijunCol)
plotshape(markTkCross and tkBearF and markTkSize == 'normal', title = 'TK− md', style = shape.triangledown, location = location.abovebar, size = size.normal, color = color.new(effKijunCol, 10), text = 'TK−', textcolor = effKijunCol)
plotshape(markKijunCross and pkBull, title = 'C>K', style = shape.circle, location = location.belowbar, size = size.tiny, color = color.new(effKijunCol, 25))
plotshape(markKijunCross and pkBear, title = 'C<K', style = shape.circle, location = location.abovebar, size = size.tiny, color = color.new(effKijunCol, 25))
plotshape(markTenkanCross and ptBull, title = 'C>T', style = shape.square, location = location.belowbar, size = size.tiny, color = color.new(effTenkanCol, 35))
plotshape(markTenkanCross and ptBear, title = 'C<T', style = shape.square, location = location.abovebar, size = size.tiny, color = color.new(effTenkanCol, 35))
plotshape(markKumoTwist and kumoTwistUp, title = 'Twist+', style = shape.xcross, location = location.belowbar, size = size.tiny, color = color.new(effTwistUpCol, 0), offset = shift, text = 'Kt', textcolor = effTwistUpCol)
plotshape(markKumoTwist and kumoTwistDn, title = 'Twist−', style = shape.xcross, location = location.abovebar, size = size.tiny, color = color.new(effTwistDnCol, 0), offset = shift, text = 'Kt', textcolor = effTwistDnCol)
plotshape(markKumoBreak and kumoBullBO, title = 'Kumo↑', style = shape.diamond, location = location.belowbar, size = size.tiny, color = color.new(effKboBull, 0), text = 'Kb', textcolor = color.new(effKboBull, 0))
plotshape(markKumoBreak and kumoBearBO, title = 'Kumo↓', style = shape.diamond, location = location.abovebar, size = size.tiny, color = color.new(effKboBear, 0), text = 'Kb', textcolor = color.new(effKboBear, 0))
alertcondition(tkBullF, 'TK Cross Bullish', 'Ichimoku: Tenkan crossed above Kijun')
alertcondition(tkBearF, 'TK Cross Bearish', 'Ichimoku: Tenkan crossed below Kijun')
alertcondition(kumoBullBO, 'Kumo Breakout Up', 'Ichimoku: price closed above the Kumo')
alertcondition(kumoBearBO, 'Kumo Breakout Down', 'Ichimoku: price closed below the Kumo')
alertcondition(kumoTwistUp, 'Kumo Twist Bullish', 'Ichimoku: Senkou A crossed above Senkou B (future Kumo turns bullish)')
alertcondition(kumoTwistDn, 'Kumo Twist Bearish', 'Ichimoku: Senkou A crossed below Senkou B (future Kumo turns bearish)')
//=== 8) Bar colors ================================================================
tintKumo =
not hasKumo ? neutralBarColor :
aboveKumo ? color.new(cBull, 78) :
belowKumo ? color.new(cBear, 78) : color.new(#787B86, 82)
tintTk =
tkStackBull ? color.new(isBrand ? BRAND_GREEN : #2962FF, 78) :
tkStackBear ? color.new(isBrand ? BRAND_RED : #B71C1C, 78) : neutralBarColor
tintBoth =
hasKumo and aboveKumo and tkStackBull ? color.new(cBull, 78) :
hasKumo and belowKumo and tkStackBear ? color.new(cBear, 78) :
hasKumo and insideKumo ? color.new(#787B86, 82) :
not hasKumo ? neutralBarColor :
color.new(#5D6D7E, 80)
tintMixColored =
barPalette == 'Kumo' ? tintKumo :
barPalette == 'TK stack' ? tintTk :
tintBoth
tintMixMono =
barPalette == 'Kumo' ? tintKumoMono :
barPalette == 'TK stack' ? tintTkMono :
tintBothMono
tintMix = isMono ? tintMixMono : tintMixColored
showBarPaint = colorBars or isMono
barPaint = showBarPaint ? (isMono and not colorBars ? color.new(M_MID, 0) : tintMix) : na
barcolor(showBarPaint ? barPaint : na, title = 'Crypex candles')
bgcolor(
shadeVsKumo ? (
not hasKumo ? na :
aboveKumo ? effShadeAbove :
belowKumo ? effShadeBelow :
effShadeIn) : na,
title = 'Bg vs Kumo')
//=== 9a) Ichimoku analysis engine ================================================
// Future cloud: senkouA / senkouB at current bar are projected `shift` bars AHEAD.
// A > B now means the cloud that will appear in the future is bullish.
bool futureCloudBull = senkouA > senkouB
bool futureCloudBear = senkouA < senkouB
// Kijun flat: Donchian midpoint stays exactly the same when no new 26-bar H/L is made.
float kijunSlope5 = kijun - kijun[5]
bool kijunFlat = isFlat(kijun, flatBars + 1)
bool kijunRising = not kijunFlat and kijunSlope5 > 0
bool kijunFalling = not kijunFlat and kijunSlope5 < 0
// Senkou B flat: same logic at the lagged (chart-drawn) position
bool senkouBFlatNow = isFlat(senkouB, flatBars + 1)
bool senkouAFlatNow = isFlat(senkouA, flatBars + 1)
float senkBLag = senkouB[shift]
float senkBLag5 = senkouB[shift + 5]
bool senkouBFlat = not na(senkBLag) and senkouBFlatNow[shift]
// Tenkan distance from price (ATR-normalised) — high values signal overextension
float tenkanDistAtr = atr14 > 0 ? math.abs(close - tenkan) / atr14 : na
// Chikou reference candle (the one Chikou is drawn against) for scoring
float chRefHi = high[shift]
float chRefLo = low[shift]
float chikouScore = na(chRefHi) or na(chRefLo) ? 0.0 :
close > chRefHi ? 1.0 :
close < chRefLo ? -1.0 : 0.0
// Ichimoku composite score (theoretical range ≈ ±6.5)
// Price vs Kumo ±2 | TK stack ±1 | Price vs Kijun ±1 | Future cloud ±1 | Chikou ±1 | Kijun slope ±0.5
float ichiScore =
(aboveKumo ? 2.0 : belowKumo ? -2.0 : 0.0) +
(tkStackBull ? 1.0 : tkStackBear ? -1.0 : 0.0) +
(close > kijun ? 1.0 : close < kijun ? -1.0 : 0.0) +
(futureCloudBull ? 1.0 : futureCloudBear ? -1.0 : 0.0) +
chikouScore +
(kijunRising ? 0.5 : kijunFalling ? -0.5 : 0.0)
string ichiBias =
ichiScore >= 5.5 ? 'Strong Bullish' :
ichiScore >= 2.0 ? 'Bullish Bias' :
ichiScore <= -5.5 ? 'Strong Bearish' :
ichiScore <= -2.0 ? 'Bearish Bias' :
'Neutral / Range'
//=== 9) Status table =============================================================
var table t = na
// Derived labels
string regime =
not hasKumo ? 'Warm-up (no Kumo)' :
aboveKumo ? 'Above Kumo' :
belowKumo ? 'Below Kumo' : 'Inside Kumo'
string align =
hasKumo and kumoBull and aboveKumo and tkStackBull ? 'Strong bull stack' :
hasKumo and not kumoBull and belowKumo and tkStackBear ? 'Strong bear stack' :
hasKumo and aboveKumo and not tkStackBull ? 'Pullback risk (above cloud)' :
hasKumo and belowKumo and not tkStackBear ? 'Bounce risk (below cloud)' :
'Mixed / transit'
string _mtfTfLabel = showMtfCloud and mtfIsHigher ? ' (' + mtfTfStr + ')' : ''
string mtfReg = showMtfCloud and mtfTooFar ? 'HTF too far — pick a closer TF' : not showMtfCloud or not mtfIsHigher ? '—' : not mtfHas ? 'HTF warm-up' : mtfAbove ? 'Above Kumo' + _mtfTfLabel : mtfBelow ? 'Below Kumo' + _mtfTfLabel : 'In Kumo' + _mtfTfLabel
string mtfCloudTxt =
not showMtfCloud or not mtfIsHigher or not mtfHas ? '—' :
mtfCloudBull ? (isMono ? 'HTF light' : 'HTF green') :
(isMono ? 'HTF dark' : 'HTF red')
string cloudLbl =
hasKumo ? (kumoBull ? (isMono ? 'Light cloud (A>B)' : 'Green (A>B)') : (isMono ? 'Dark cloud (A<B)' : 'Red (A<B)')) : '—'
if showTable and barstate.islast
if na(t)
t := table.new(tablePos, 2, 19, border_width = 1, frame_color = color.new(color.gray, 40), border_color = color.new(color.gray, 55))
table.clear(t, 0, 0, 1, 18)
// ── semantic text colours ──────────────────────────────────────────────
color tBull = isMono ? M_LIGHT : cBull
color tBear = isMono ? M_DARK : cBear
color tWarn = isMono ? M_ACCENT : color.new(#FF9800, 0)
color tBase = color.white
// ── alternating bg ─────────────────────────────────────────────────────
color bg0 = color.new(color.black, 85)
color bg1 = color.new(color.black, 90)
// ── formatted strings ──────────────────────────────────────────────────
float kumoThickAtr = atr14 > 0 and hasKumo ? kumoThick / atr14 : na
string thickRating = na(kumoThickAtr) ? '' : kumoThickAtr > 1.5 ? ' · Thick' : kumoThickAtr < 0.5 ? ' · Thin' : ' · Normal'
string thickStr = not hasKumo ? '—' : str.tostring(kumoThick, format.mintick) + ' (' + str.tostring(math.round(thickTicks)) + ' t)' + thickRating
string distStr = not hasKumo or not showDistRows ? '—' : '↑ ' + str.tostring(math.round(dTopTicks)) + ' t / ↓ ' + str.tostring(math.round(dBotTicks)) + ' t'
string nearStr = not hasKumo or na(distNearestAtr) ? '—' : str.tostring(distNearest, format.mintick) + ' · ' + str.tostring(distNearestAtr, '#.##') + ' ATR'
string tkDistStr = na(tenkanDistAtr) ? '—' : str.tostring(tenkanDistAtr, '#.##') + ' ATR' + (tenkanDistAtr > 1.0 ? ' ⚠ overext' : tenkanDistAtr < 0.3 ? ' · near' : '')
string kijSlopeStr = kijunFlat ? 'Flat · equilibrium magnet' : kijunRising ? 'Rising' : kijunFalling ? 'Falling' : 'Sideways'
string senkBStr = na(senkBLag) ? '—' : senkouBFlat ? 'Flat S/R @ ' + str.tostring(senkBLag, format.mintick) : senkBLag > senkBLag5 ? 'Rising' : senkBLag < senkBLag5 ? 'Falling' : 'Sideways'
string futureTxt = futureCloudBull ? (isMono ? 'Light (A>B)' : 'Bullish (A>B)') : futureCloudBear ? (isMono ? 'Dark (A<B)' : 'Bearish (A<B)') : 'Flat / twisting'
string chikTxt = na(chRefHi) or na(chRefLo) ? 'Warm-up' : close > chRefHi ? 'Clear bullish' : close < chRefLo ? 'Clear bearish' : 'Trapped (neutral)'
string scoreStr = str.tostring(ichiScore, '#.#') + ' → ' + ichiBias
// ── per-row value colours ──────────────────────────────────────────────
color regimeCol = not hasKumo ? tBase : aboveKumo ? tBull : belowKumo ? tBear : tWarn
color cloudCol = not hasKumo ? tBase : kumoBull ? tBull : tBear
color futureCol = futureCloudBull ? tBull : futureCloudBear ? tBear : tBase
color alignCol = (aboveKumo and tkStackBull) ? tBull : (belowKumo and tkStackBear) ? tBear : tWarn
color tkMomCol = tkStackBull ? tBull : tkStackBear ? tBear : tBase
color kijSlopeCol = kijunFlat ? tWarn : kijunRising ? tBull : kijunFalling ? tBear : tBase
color senkBCol = na(senkBLag) ? tBase : senkouBFlat ? tWarn : senkBLag > senkBLag5 ? tBull : senkBLag < senkBLag5 ? tBear : tBase
color chikCol = chikouScore > 0 ? tBull : chikouScore < 0 ? tBear : tWarn
color tkDistCol = na(tenkanDistAtr) ? tBase : tenkanDistAtr > 1.0 ? tWarn : tBase
color biasCol = ichiScore >= 2.0 ? tBull : ichiScore <= -2.0 ? tBear : tWarn
// ── cells ──────────────────────────────────────────────────────────────
table.cell(t, 0, 0, 'Crypex', text_color = color.white, bgcolor = effTblHdrBg, text_size = tableSize)
table.cell(t, 1, 0, syminfo.ticker, text_color = color.white, bgcolor = effTblHdrBg, text_size = tableSize)
table.cell(t, 0, 1, 'Tenkan / Kijun', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 1, str.tostring(tenkan, format.mintick) + ' / ' + str.tostring(kijun, format.mintick), text_color = tBase, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 2, 'Regime', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 2, regime, text_color = regimeCol, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 3, 'Cloud (now)', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 3, cloudLbl, text_color = cloudCol, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 4, 'Future cloud', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 4, futureTxt, text_color = futureCol, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 5, 'Alignment', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 5, align, text_color = alignCol, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 6, 'TK momentum', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 6, tkStackBull ? 'Tenkan > Kijun' : tkStackBear ? 'Tenkan < Kijun' : 'Flat', text_color = tkMomCol, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 7, 'Kijun slope', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 7, kijSlopeStr, text_color = kijSlopeCol, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 8, 'Senkou B', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 8, senkBStr, text_color = senkBCol, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 9, 'Chikou', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 9, chikTxt, text_color = chikCol, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 10, 'Tenkan distance', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 10, tkDistStr, text_color = tkDistCol, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 11, 'Kumo thickness', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 11, showDistRows ? thickStr : '—', text_color = tBase, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 12, 'Distance (edge)', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 12, showDistRows ? distStr : '—', text_color = tBase, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 13, 'Nearest · ATR', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 13, showDistRows ? nearStr : '—', text_color = tBase, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 14, 'ATR(14)', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 14, showAtrRow ? str.tostring(atr14, format.mintick) : '—', text_color = tBase, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 15, 'Score · Bias', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 15, scoreStr, text_color = biasCol, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 16, 'HTF Kumo', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
table.cell(t, 1, 16, showMtfInTable ? mtfReg : '—', text_color = tBase, bgcolor = bg1, text_size = tableSize)
table.cell(t, 0, 17, 'HTF cloud', text_color = color.gray, bgcolor = bg0, text_size = tableSize)
table.cell(t, 1, 17, showMtfInTable ? mtfCloudTxt : '—', text_color = tBase, bgcolor = bg0, text_size = tableSize)
table.cell(t, 0, 18, 'Shift · Params', text_color = color.gray, bgcolor = bg1, text_size = tableSize)
string shiftTxt = (showMtfCloud and mtfIsHigher ? str.tostring(shift) + ' ch · ' + str.tostring(mtfForwardBars) + ' HTF shift' : str.tostring(shift) + ' ch') + ' | ' + str.tostring(conversionPeriods) + '/' + str.tostring(basePeriods) + '/' + str.tostring(laggingSpanBPeriods)
table.cell(t, 1, 18, shiftTxt, text_color = tBase, bgcolor = bg1, text_size = tableSize)
//=== 10) Ichimoku S/R levels =====================================================
// Short name for chart labels (strip star ratings)
srCleanNm(string raw) =>
string r = raw
r := str.replace_all(r, ' ★★★★', '')
r := str.replace_all(r, ' ★★★', '')
r := str.replace_all(r, ' ★★', '')
r := str.replace_all(r, ' ★', '')
r
// Strength tiers:
// ★★★★ Flat Senkou B / Flat Kijun (historical equilibrium magnets)
// ★★★ Kijun (dynamic)
// ★★ Kumo edges (dynamic)
//
// Visuals:
// Flat → thick solid line, full colour
// Dynamic → thin dashed line, 40% transparent colour
// HTF → dotted line, extra transparent, [H] tag
// Cluster → shaded box when ≥2 levels within 0.25 ATR
var line[] _srLines = array.new<line>()
var label[] _srLabels = array.new<label>()
var box[] _srBoxes = array.new<box>()
if showSRLines and barstate.islast and hasKumo and atr14 > 0
for _oldLine in _srLines
line.delete(_oldLine)
for _lb in _srLabels
label.delete(_lb)
for _bx in _srBoxes
box.delete(_bx)
array.clear(_srLines)
array.clear(_srLabels)
array.clear(_srBoxes)
float _clThr = atr14 * 0.25
// Cluster OFF: only merge levels at the same price, so nearby levels are no longer hidden.
float _mergeThr = srCluster ? _clThr : syminfo.mintick
// ── Raw level list ────────────────────────────────────────────────────
// price | name | isFlat | strength(1-4) | isHTF
float[] _lp = array.new_float()
string[] _ln = array.new_string()
bool[] _lf = array.new_bool()
int[] _ls = array.new_int()
bool[] _lh = array.new_bool()
// ── 1. Kijun — always the primary trendline S/R ───────────────────────
array.push(_lp, kijun)
array.push(_ln, kijunFlat ? 'Kijun ★★★★' : 'Kijun ★★★')
array.push(_lf, kijunFlat)
array.push(_ls, kijunFlat ? 4 : 3)
array.push(_lh, false)
// ── 2. Current cloud edges (each edge judged by the span that forms it) ──
bool _botIsB = senkouB[shift] <= senkouA[shift]
bool _botFlat = _botIsB ? senkouBFlat : senkouAFlatNow[shift]
bool _topFlat = _botIsB ? senkouAFlatNow[shift] : senkouBFlat
array.push(_lp, kumoBottom)
array.push(_ln, _botFlat ? 'Kumo bottom ★★★★' : 'Kumo bottom ★★')
array.push(_lf, _botFlat)
array.push(_ls, _botFlat ? 4 : 2)
array.push(_lh, false)
if kumoTop - kumoBottom > _clThr
array.push(_lp, kumoTop)
array.push(_ln, _topFlat ? 'Kumo top ★★★★' : 'Kumo top ★★')
array.push(_lf, _topFlat)
array.push(_ls, _topFlat ? 4 : 2)
array.push(_lh, false)
// ── 3. Historical flat Senkou B — the REAL historical S/R ──────────────
// Senkou B stays EXACTLY the same while no new 52-bar H/L is made. Scan the past
// 100 chart bars (step 5); a drawn Senkou B that was flat for flatBars bars there
// is a lasting S/R magnet. Clustering merges duplicates.
for _ni = 1 to 20
int _n = _ni * 5
float _sb = senkouB[shift + _n]
if not na(_sb) and senkouBFlatNow[shift + _n]
array.push(_lp, _sb)
array.push(_ln, 'Old Kumo B ★★★★')
array.push(_lf, true)
array.push(_ls, 4)
array.push(_lh, false)
// ── 4. Historical flat Kijun levels ───────────────────────────────────
for _ni = 1 to 12
int _n = _ni * 5
float _hkj = kijun[_n]
if not na(_hkj) and kijunFlat[_n] and math.abs(_hkj - kijun) > _clThr
array.push(_lp, _hkj)
array.push(_ln, 'Old Kijun ★★★★')
array.push(_lf, true)
array.push(_ls, 4)
array.push(_lh, false)
// ── 5. HTF: current edges + historical flat Senkou B ──────────────────
if showMtfCloud and mtfIsHigher and mtfHas
array.push(_lp, mtfBot)
array.push(_ln, mtfBotFlat ? 'HTF Kumo bottom ★★★★' : 'HTF Kumo bottom ★★')
array.push(_lf, mtfBotFlat)
array.push(_ls, mtfBotFlat ? 4 : 2)
array.push(_lh, true)
if mtfTop - mtfBot > _clThr
array.push(_lp, mtfTop)
array.push(_ln, mtfTopFlat ? 'HTF Kumo top ★★★★' : 'HTF Kumo top ★★')
array.push(_lf, mtfTopFlat)
array.push(_ls, mtfTopFlat ? 4 : 2)
array.push(_lh, true)
// Scan back in steps of ~5 HTF bars (flatness itself is measured on the HTF).
int _hStep = math.max(5, math.round(5 * mtfRatio))
for _ni = 1 to 20
int _n = _ni * _hStep
if _n > 4900
break
float _msb = mtfKumoB[_n]
if not na(_msb) and mtfKumoBFlat[_n]
array.push(_lp, _msb)
array.push(_ln, 'Old HTF Kumo B ★★★★')
array.push(_lf, true)
array.push(_ls, 4)
array.push(_lh, true)
int _sz = array.size(_lp)
int[] _idx = array.sort_indices(_lp, order.ascending)
// ── Cluster detection ─────────────────────────────────────────────────
float[] _cMin = array.new_float()
float[] _cMax = array.new_float()
string[] _cNm = array.new_string()
bool[] _cFl = array.new_bool()
int[] _cCt = array.new_int()
bool[] _cH = array.new_bool()
bool[] _vis = array.new_bool(_sz, false)
for _i = 0 to _sz - 1
int _oi = array.get(_idx, _i)
if not array.get(_vis, _oi)
float _pLo = array.get(_lp, _oi)
float _pHi = _pLo
string _nm = array.get(_ln, _oi)
bool _fl = array.get(_lf, _oi)
bool _hh = array.get(_lh, _oi)
int _ct = 1
array.set(_vis, _oi, true)
int _j = _i + 1
while _j < _sz
int _oj = array.get(_idx, _j)
float _np = array.get(_lp, _oj)
if _np - _pHi > _mergeThr
break
if not array.get(_vis, _oj)
_pHi := _np
// Same-name duplicates (e.g. several Old Kumo B hits) are listed once.
string _add = array.get(_ln, _oj)
if not str.contains(_nm, _add)
_nm := _nm + ' + ' + _add
if array.get(_lf, _oj)
_fl := true
if not array.get(_lh, _oj)
_hh := false
_ct += 1
array.set(_vis, _oj, true)
_j += 1
array.push(_cMin, _pLo)
array.push(_cMax, _pHi)
array.push(_cNm, _nm)
array.push(_cFl, _fl)
array.push(_cCt, _ct)
array.push(_cH, _hh)
// ── Count supports (below price) for nearest-first numbering ──────────
int _nCl = array.size(_cMin)
int _nS = 0
for _k = 0 to _nCl - 1
if (array.get(_cMin, _k) + array.get(_cMax, _k)) / 2 < close
_nS += 1
int _sCur = _nS
int _rCur = 1
// ── Draw ──────────────────────────────────────────────────────────────
for _k = 0 to _nCl - 1
float _lo = array.get(_cMin, _k)
float _hi = array.get(_cMax, _k)
float _mid = (_lo + _hi) / 2
string _nm = array.get(_cNm, _k)
bool _fl = array.get(_cFl, _k)
int _ct = array.get(_cCt, _k)
bool _isH = array.get(_cH, _k)
bool _isR = _mid >= close
// Rank 1 = nearest to price on each side.
int _rank = _isR ? _rCur : _sCur
color _base = _isR ? (isMono ? M_DARK : cBear) : (isMono ? M_LIGHT : cBull)
if _isR
_rCur += 1
else
_sCur -= 1
if _rank <= srMaxLevels
string _rn = (_isR ? 'R' : 'S') + str.tostring(_rank)
string _ht = _isH ? '[H]' : ''
bool _isZone = _ct > 1 and _hi > _lo
string _rng = _isZone ? str.tostring(_lo, format.mintick) + ' – ' + str.tostring(_hi, format.mintick) : str.tostring(_lo, format.mintick)
string _lbl = _rn + _ht + ' · ' + _rng + ' · ' + srCleanNm(_nm)
int _alpha = _isH ? 55 : (_fl ? 0 : 40)
color _col = color.new(_base, _alpha)
int _lw = _fl ? 2 : 1
int _x2 = bar_index + srExtend
color _bgFlat = color.new(_base, _fl ? 78 : 88)
if srCluster and _isZone
array.push(_srBoxes, box.new(bar_index, _hi, _x2, _lo, border_color = _col, bgcolor = _bgFlat, border_width = _lw))
array.push(_srLabels, label.new(_x2, _mid, _lbl, style = label.style_label_left, color = color.new(color.black, 100), textcolor = _col, size = tableSize))
else
string _lst = _isH ? line.style_dotted : (_fl ? line.style_solid : line.style_dashed)
array.push(_srLines, line.new(bar_index, _lo, _x2, _lo, color = _col, width = _lw, style = _lst))
array.push(_srLabels, label.new(_x2, _lo, _lbl, style = label.style_label_left, color = color.new(color.black, 100), textcolor = _col, size = tableSize))
// ── Kumo midpoint (outside the ranked system) ─────────────────────────
if showKumoMid
float _mMid = (kumoTop + kumoBottom) / 2
string _mRole = _mMid >= close ? 'R' : 'S'
array.push(_srLines, line.new(bar_index, _mMid, bar_index + srExtend, _mMid, color = effKumoMidCol, width = 1, style = line.style_dotted))
array.push(_srLabels, label.new(bar_index + srExtend, _mMid, _mRole + ' · ' + str.tostring(_mMid, format.mintick) + ' · Kumo mid', style = label.style_label_left, color = color.new(color.black, 100), textcolor = effKumoMidCol, size = tableSize))
//=== 11) Watermark ===============================================================
// "crypex" brand green · ".org" dark (white on dark charts).
// TradingView shows only one table per position, so all parts live in one table; text is aligned
// toward the neighbouring cell so the parts sit as close together as the cell padding allows.
var table wm = na
if showWatermark and barstate.islast
if na(wm)
wm := table.new(wmPos, 3, 1, border_width = 0, frame_width = 0)
string wmSize = wmSizeStr == 'small' ? size.small : wmSizeStr == 'normal' ? size.normal : wmSizeStr == 'large' ? size.large : size.huge
// Empty spacer cell keeps the text clear of the TradingView logo in the bottom-left corner.
float spacerW = wmPos == position.bottom_left ? wmGap : 0.0
bool bgLight = (color.r(chart.bg_color) * 299 + color.g(chart.bg_color) * 587 + color.b(chart.bg_color) * 114) / 1000 > 128
color orgCol = bgLight ? BRAND_DARK : color.white
table.cell(wm, 0, 0, '', width = spacerW, text_size = wmSize)
table.cell(wm, 1, 0, 'crypex', text_color = color.new(BRAND_GREEN, wmTransp), text_size = wmSize, text_halign = text.align_right, text_valign = text.align_bottom, text_formatting = text.format_bold)
table.cell(wm, 2, 0, '.org', text_color = color.new(orgCol, wmTransp), text_size = wmSize, text_halign = text.align_left, text_valign = text.align_bottom, text_formatting = text.format_bold)
//+------------------------------------------------------------------+
//| Crypex | Ichimoku Advanced (MT5) |
//| HTF Kumo + ranked S/R levels + status panel + alerts |
//| © Crypex.org — All rights reserved. | حق نشر محفوظ برای Crypex.org |
//| License: personal, non-commercial use only. Redistribution or |
//| resale without written permission is prohibited. |
//| مجوز: استفاده شخصی و غیرتجاری. بازنشر یا فروش مجدد بدون |
//| اجازهی کتبی ممنوع است. |
//| Educational tool, not investment advice. |
//+------------------------------------------------------------------+
#property copyright "Crypex.org"
#property link "https://crypex.org"
#property version "1.00"
#property description "Ichimoku with higher-timeframe Kumo, S/R levels and status panel"
#property indicator_chart_window
#property indicator_buffers 13
#property indicator_plots 10
#property indicator_label1 "Tenkan-sen"
#property indicator_type1 DRAW_LINE
#property indicator_label2 "Kijun-sen"
#property indicator_type2 DRAW_LINE
#property indicator_label3 "Chikou Span"
#property indicator_type3 DRAW_LINE
#property indicator_label4 "Kumo"
#property indicator_type4 DRAW_FILLING
#property indicator_label5 "HTF Kumo"
#property indicator_type5 DRAW_FILLING
#property indicator_label6 "HTF Kijun"
#property indicator_type6 DRAW_LINE
#property indicator_label7 "TK Cross Up"
#property indicator_type7 DRAW_ARROW
#property indicator_label8 "TK Cross Down"
#property indicator_type8 DRAW_ARROW
#property indicator_label9 "Kumo Breakout Up"
#property indicator_type9 DRAW_ARROW
#property indicator_label10 "Kumo Breakout Down"
#property indicator_type10 DRAW_ARROW
enum ENUM_PANEL_POS { POS_TOP_RIGHT, POS_TOP_LEFT, POS_BOTTOM_RIGHT, POS_BOTTOM_LEFT };
enum ENUM_CAL_MODE { CAL_AUTO, CAL_247, CAL_WEEKDAYS };
input group "Ichimoku"
input int InpTenkan = 9; // Tenkan-sen
input int InpKijun = 26; // Kijun-sen
input int InpSenkouB = 52; // Senkou Span B
input int InpDisp = 26; // Displacement (Senkou +, Chikou -)
input int InpFlatBars = 10; // Min flat bars (Kijun / Senkou B)
input group "Lines and Kumo"
input bool InpShowTenkan = true;
input bool InpShowKijun = true;
input bool InpShowChikou = true;
input bool InpShowKumo = true;
input color InpTenkanColor = C'11,163,28';
input color InpKijunColor = C'255,10,23';
input color InpChikouColor = C'120,123,134';
input color InpBullKumo = C'129,199,132';
input color InpBearKumo = C'239,154,154';
input group "Higher timeframe Kumo"
input bool InpShowHtf = true;
input bool InpHtfAuto = true; // Auto next timeframe
input ENUM_TIMEFRAMES InpHtfManual = PERIOD_H4; // Manual timeframe
input bool InpShowHtfKijun = true;
input ENUM_CAL_MODE InpCalendar = CAL_AUTO; // Market calendar (weekly/monthly HTF projection)
input double InpRatioOverride = 0.0; // Chart bars per HTF bar (0 = auto)
input int InpHtfBars = 3000; // HTF bars to load
input color InpHtfBullKumo = C'178,223,180';
input color InpHtfBearKumo = C'247,196,196';
input color InpHtfKijunColor = C'178,34,34';
input group "Markers and alerts"
input bool InpMarkTk = true; // TK cross arrows
input bool InpMarkKumoBo = false; // Kumo breakout arrows
input bool InpAlertPopup = true;
input bool InpAlertPush = false;
input bool InpAlertMail = false;
input group "Status panel"
input bool InpShowPanel = true;
input ENUM_PANEL_POS InpPanelPos = POS_TOP_RIGHT;
input int InpPanelFont = 8;
input color InpPanelBg = C'21,26,32';
input group "Support / Resistance levels"
input bool InpShowSR = true;
input int InpSrExtend = 20; // Extend (bars)
input bool InpSrCluster = true; // Merge nearby levels into zones
input int InpSrMax = 10; // Max levels per side
input color InpSupportColor = C'11,163,28';
input color InpResistanceColor = C'255,10,23';
input group "Watermark"
input bool InpShowWm = true; // Show crypex.org watermark
input int InpWmSize = 18;
input int InpWmGap = 60; // Gap from left (px), clears the chart logo
input int InpWmBottom = 14; // Gap from bottom (px)
const string PFX = "CRX_ICH_";
const color BRAND_GREEN = C'11,163,28';
double BufTenkan[], BufKijun[], BufChikou[], BufSpanA[], BufSpanB[];
double BufHtfA[], BufHtfB[], BufHtfKijun[];
double BufTkUp[], BufTkDn[], BufKbUp[], BufKbDn[], BufAtr[];
// higher-timeframe store (chronological, index 0 = oldest)
double hHi[], hLo[], hT[], hK[], hA[], hB[];
datetime hTm[];
int jMap[];
int hN = 0;
ENUM_TIMEFRAMES g_htf = PERIOD_CURRENT;
bool g_htfOn = false;
bool g_htfReady = false;
int g_fwd = 0;
double g_ratio = 1.0;
double g_tick = 0.0;
datetime g_lastBar = 0;
datetime g_lastAlertBar = 0;
uint g_lastPaint = 0;
uint g_lastHtfTry = 0;
bool g_firstDone = false;
//+------------------------------------------------------------------+
bool IsCryptoSymbol()
{
string s = _Symbol;
string keys[] = {"BTC", "ETH", "LTC", "XRP", "SOL", "DOGE", "ADA", "BNB", "TRX", "DOT"};
for(int i = 0; i < ArraySize(keys); i++)
if(StringFind(s, keys[i]) >= 0) return true;
return false;
}
ENUM_TIMEFRAMES AutoNextTf()
{
int s = PeriodSeconds(_Period);
if(s <= 60) return PERIOD_H1;
if(s <= 3600) return PERIOD_H4;
if(s <= 14400) return PERIOD_D1;
if(s <= 86400) return PERIOD_W1;
return PERIOD_MN1;
}
//+------------------------------------------------------------------+
int OnInit()
{
SetIndexBuffer(0, BufTenkan, INDICATOR_DATA);
SetIndexBuffer(1, BufKijun, INDICATOR_DATA);
SetIndexBuffer(2, BufChikou, INDICATOR_DATA);
SetIndexBuffer(3, BufSpanA, INDICATOR_DATA);
SetIndexBuffer(4, BufSpanB, INDICATOR_DATA);
SetIndexBuffer(5, BufHtfA, INDICATOR_DATA);
SetIndexBuffer(6, BufHtfB, INDICATOR_DATA);
SetIndexBuffer(7, BufHtfKijun, INDICATOR_DATA);
SetIndexBuffer(8, BufTkUp, INDICATOR_DATA);
SetIndexBuffer(9, BufTkDn, INDICATOR_DATA);
SetIndexBuffer(10, BufKbUp, INDICATOR_DATA);
SetIndexBuffer(11, BufKbDn, INDICATOR_DATA);
SetIndexBuffer(12, BufAtr, INDICATOR_CALCULATIONS);
g_tick = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
if(g_tick <= 0) g_tick = _Point;
// plots: 0 Tenkan, 1 Kijun, 2 Chikou, 3 Kumo, 4 HTF Kumo, 5 HTF Kijun, 6..9 arrows
PlotIndexSetInteger(0, PLOT_LINE_COLOR, InpTenkanColor);
PlotIndexSetInteger(1, PLOT_LINE_COLOR, InpKijunColor);
PlotIndexSetInteger(2, PLOT_LINE_COLOR, InpChikouColor);
PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 2);
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 1);
PlotIndexSetInteger(2, PLOT_SHIFT, -InpDisp);
PlotIndexSetInteger(3, PLOT_SHIFT, InpDisp);
PlotIndexSetInteger(3, PLOT_COLOR_INDEXES, 2);
PlotIndexSetInteger(3, PLOT_LINE_COLOR, 0, InpBullKumo);
PlotIndexSetInteger(3, PLOT_LINE_COLOR, 1, InpBearKumo);
PlotIndexSetInteger(4, PLOT_COLOR_INDEXES, 2);
PlotIndexSetInteger(4, PLOT_LINE_COLOR, 0, InpHtfBullKumo);
PlotIndexSetInteger(4, PLOT_LINE_COLOR, 1, InpHtfBearKumo);
PlotIndexSetInteger(5, PLOT_LINE_COLOR, InpHtfKijunColor);
PlotIndexSetInteger(5, PLOT_LINE_WIDTH, 1);
PlotIndexSetInteger(5, PLOT_LINE_STYLE, STYLE_DASH);
PlotIndexSetInteger(6, PLOT_ARROW, 233);
PlotIndexSetInteger(7, PLOT_ARROW, 234);
PlotIndexSetInteger(8, PLOT_ARROW, 159);
PlotIndexSetInteger(9, PLOT_ARROW, 159);
PlotIndexSetInteger(6, PLOT_LINE_COLOR, InpTenkanColor);
PlotIndexSetInteger(7, PLOT_LINE_COLOR, InpKijunColor);
PlotIndexSetInteger(8, PLOT_LINE_COLOR, InpTenkanColor);
PlotIndexSetInteger(9, PLOT_LINE_COLOR, InpKijunColor);
for(int p = 0; p < 10; p++) PlotIndexSetDouble(p, PLOT_EMPTY_VALUE, EMPTY_VALUE);
if(!InpShowTenkan) PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
if(!InpShowKijun) PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE);
if(!InpShowChikou) PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
if(!InpShowKumo) PlotIndexSetInteger(3, PLOT_DRAW_TYPE, DRAW_NONE);
// higher timeframe
g_htf = InpHtfAuto ? AutoNextTf() : InpHtfManual;
int chartSec = PeriodSeconds(_Period);
int htfSec = PeriodSeconds(g_htf);
g_htfOn = InpShowHtf && htfSec > chartSec && chartSec > 0;
bool weekdays = (InpCalendar == CAL_WEEKDAYS) || (InpCalendar == CAL_AUTO && !IsCryptoSymbol());
double weekFactor = (weekdays && htfSec >= 604800 && chartSec < 604800) ? 5.0 / 7.0 : 1.0;
g_ratio = g_htfOn ? (InpRatioOverride > 0 ? InpRatioOverride : (double)htfSec / chartSec * weekFactor) : 1.0;
g_fwd = g_htfOn ? (int)MathMax(0, MathRound((InpDisp - 1) * g_ratio)) : 0;
if(g_fwd > 4900) g_htfOn = false; // too far ahead for a chart this small
PlotIndexSetInteger(4, PLOT_SHIFT, g_fwd);
if(!g_htfOn)
{
PlotIndexSetInteger(4, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(5, PLOT_DRAW_TYPE, DRAW_NONE);
}
else if(!InpShowHtfKijun)
PlotIndexSetInteger(5, PLOT_DRAW_TYPE, DRAW_NONE);
if(!InpMarkTk)
{
PlotIndexSetInteger(6, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(7, PLOT_DRAW_TYPE, DRAW_NONE);
}
if(!InpMarkKumoBo)
{
PlotIndexSetInteger(8, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(9, PLOT_DRAW_TYPE, DRAW_NONE);
}
IndicatorSetString(INDICATOR_SHORTNAME, "Crypex Ichimoku");
IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PFX);
}
//+------------------------------------------------------------------+
//| helpers |
//+------------------------------------------------------------------+
double Donchian(const double &hi[], const double &lo[], const int idx, const int len)
{
if(idx - len + 1 < 0) return EMPTY_VALUE;
double h = hi[idx], l = lo[idx];
for(int k = 1; k < len; k++)
{
if(hi[idx - k] > h) h = hi[idx - k];
if(lo[idx - k] < l) l = lo[idx - k];
}
return (h + l) / 2.0;
}
// value stayed within one tick over the whole window (len+1 bars)
bool IsFlat(const double &arr[], const int idx, const int len)
{
if(idx - len < 0) return false;
double mx = -DBL_MAX, mn = DBL_MAX;
for(int k = 0; k <= len; k++)
{
double v = arr[idx - k];
if(v == EMPTY_VALUE) return false;
if(v > mx) mx = v;
if(v < mn) mn = v;
}
return (mx - mn) <= g_tick;
}
// flatness of the upper (top=true) or lower edge of a Kumo made of spans a and b
bool IsFlatEdge(const double &a[], const double &b[], const int idx, const int len, const bool top)
{
if(idx - len < 0) return false;
double mx = -DBL_MAX, mn = DBL_MAX;
for(int k = 0; k <= len; k++)
{
double va = a[idx - k], vb = b[idx - k];
if(va == EMPTY_VALUE || vb == EMPTY_VALUE) return false;
double v = top ? MathMax(va, vb) : MathMin(va, vb);
if(v > mx) mx = v;
if(v < mn) mn = v;
}
return (mx - mn) <= g_tick;
}
string Px(const double v)
{
return DoubleToString(v, _Digits);
}
//+------------------------------------------------------------------+
//| Build the higher timeframe Ichimoku and map it to chart bars |
//+------------------------------------------------------------------+
void BuildHtf(const int n, const datetime &time[])
{
g_htfReady = false;
int want = MathMin(iBars(_Symbol, g_htf), InpHtfBars);
int need = InpSenkouB + InpDisp + 5;
if(want < need) return;
ArrayResize(hHi, want); ArrayResize(hLo, want); ArrayResize(hTm, want);
if(CopyHigh(_Symbol, g_htf, 0, want, hHi) != want) return;
if(CopyLow(_Symbol, g_htf, 0, want, hLo) != want) return;
if(CopyTime(_Symbol, g_htf, 0, want, hTm) != want) return;
hN = want;
ArrayResize(hT, hN); ArrayResize(hK, hN); ArrayResize(hA, hN); ArrayResize(hB, hN);
for(int j = 0; j < hN; j++)
{
hT[j] = Donchian(hHi, hLo, j, InpTenkan);
hK[j] = Donchian(hHi, hLo, j, InpKijun);
hA[j] = (hT[j] != EMPTY_VALUE && hK[j] != EMPTY_VALUE) ? (hT[j] + hK[j]) / 2.0 : EMPTY_VALUE;
hB[j] = Donchian(hHi, hLo, j, InpSenkouB);
}
ArrayResize(jMap, n);
int j = 0;
for(int i = 0; i < n; i++)
{
while(j + 1 < hN && hTm[j + 1] <= time[i]) j++;
jMap[i] = (hTm[0] <= time[i]) ? j : -1;
int c = jMap[i] - 1; // last COMPLETED HTF bar: no repainting
if(c >= 0 && hA[c] != EMPTY_VALUE && hB[c] != EMPTY_VALUE)
{
BufHtfA[i] = hA[c];
BufHtfB[i] = hB[c];
BufHtfKijun[i] = (hK[c] != EMPTY_VALUE) ? hK[c] : EMPTY_VALUE;
}
else
{
BufHtfA[i] = EMPTY_VALUE;
BufHtfB[i] = EMPTY_VALUE;
BufHtfKijun[i] = EMPTY_VALUE;
}
}
g_htfReady = true;
}
//+------------------------------------------------------------------+
//| Chart-TF Ichimoku, markers |
//+------------------------------------------------------------------+
void ComputeChart(const int n, const int start, const double &high[], const double &low[], const double &close[])
{
for(int i = start; i < n; i++)
{
BufTenkan[i] = Donchian(high, low, i, InpTenkan);
BufKijun[i] = Donchian(high, low, i, InpKijun);
BufSpanA[i] = (BufTenkan[i] != EMPTY_VALUE && BufKijun[i] != EMPTY_VALUE) ? (BufTenkan[i] + BufKijun[i]) / 2.0 : EMPTY_VALUE;
BufSpanB[i] = Donchian(high, low, i, InpSenkouB);
BufChikou[i] = close[i];
double tr = high[i] - low[i];
if(i == 0)
BufAtr[i] = tr;
else
{
tr = MathMax(tr, MathMax(MathAbs(high[i] - close[i - 1]), MathAbs(low[i] - close[i - 1])));
BufAtr[i] = (BufAtr[i - 1] * 13.0 + tr) / 14.0;
}
BufTkUp[i] = EMPTY_VALUE; BufTkDn[i] = EMPTY_VALUE; BufKbUp[i] = EMPTY_VALUE; BufKbDn[i] = EMPTY_VALUE;
if(i < 1) continue;
if(BufTenkan[i] == EMPTY_VALUE || BufKijun[i] == EMPTY_VALUE || BufTenkan[i - 1] == EMPTY_VALUE || BufKijun[i - 1] == EMPTY_VALUE) continue;
if(BufTenkan[i - 1] <= BufKijun[i - 1] && BufTenkan[i] > BufKijun[i]) BufTkUp[i] = low[i] - BufAtr[i] * 0.5;
if(BufTenkan[i - 1] >= BufKijun[i - 1] && BufTenkan[i] < BufKijun[i]) BufTkDn[i] = high[i] + BufAtr[i] * 0.5;
int c0 = i - InpDisp, c1 = i - 1 - InpDisp;
if(c1 < 0 || BufSpanA[c0] == EMPTY_VALUE || BufSpanB[c0] == EMPTY_VALUE || BufSpanA[c1] == EMPTY_VALUE || BufSpanB[c1] == EMPTY_VALUE) continue;
double top0 = MathMax(BufSpanA[c0], BufSpanB[c0]), bot0 = MathMin(BufSpanA[c0], BufSpanB[c0]);
double top1 = MathMax(BufSpanA[c1], BufSpanB[c1]), bot1 = MathMin(BufSpanA[c1], BufSpanB[c1]);
if(close[i - 1] <= top1 && close[i] > top0) BufKbUp[i] = low[i] - BufAtr[i] * 0.9;
if(close[i - 1] >= bot1 && close[i] < bot0) BufKbDn[i] = high[i] + BufAtr[i] * 0.9;
}
}
//+------------------------------------------------------------------+
//| Alerts for the last closed bar |
//+------------------------------------------------------------------+
void Notify(const string msg)
{
string full = "Crypex Ichimoku " + _Symbol + " " + EnumToString(_Period) + ": " + msg;
if(InpAlertPopup) Alert(full);
if(InpAlertPush) SendNotification(full);
if(InpAlertMail) SendMail("Crypex Ichimoku", full);
}
void CheckAlerts(const int n, const datetime &time[], const double &close[])
{
int b = n - 2; // last closed bar
if(b < InpDisp + 3) return;
if(time[b] == g_lastAlertBar) return;
g_lastAlertBar = time[b];
if(!g_firstDone) return; // never alert for history on first load
double t0 = BufTenkan[b], k0 = BufKijun[b], t1 = BufTenkan[b - 1], k1 = BufKijun[b - 1];
if(t0 == EMPTY_VALUE || k0 == EMPTY_VALUE || t1 == EMPTY_VALUE || k1 == EMPTY_VALUE) return;
if(t1 <= k1 && t0 > k0) Notify("Tenkan crossed above Kijun");
if(t1 >= k1 && t0 < k0) Notify("Tenkan crossed below Kijun");
int c0 = b - InpDisp, c1 = b - 1 - InpDisp;
if(c1 >= 0 && BufSpanA[c0] != EMPTY_VALUE && BufSpanB[c0] != EMPTY_VALUE && BufSpanA[c1] != EMPTY_VALUE && BufSpanB[c1] != EMPTY_VALUE)
{
double top0 = MathMax(BufSpanA[c0], BufSpanB[c0]), bot0 = MathMin(BufSpanA[c0], BufSpanB[c0]);
double top1 = MathMax(BufSpanA[c1], BufSpanB[c1]), bot1 = MathMin(BufSpanA[c1], BufSpanB[c1]);
if(close[b - 1] <= top1 && close[b] > top0) Notify("Price closed above the Kumo");
if(close[b - 1] >= bot1 && close[b] < bot0) Notify("Price closed below the Kumo");
}
if(BufSpanA[b] != EMPTY_VALUE && BufSpanB[b] != EMPTY_VALUE && BufSpanA[b - 1] != EMPTY_VALUE && BufSpanB[b - 1] != EMPTY_VALUE)
{
if(BufSpanA[b - 1] <= BufSpanB[b - 1] && BufSpanA[b] > BufSpanB[b]) Notify("Kumo twist: future Kumo turns bullish");
if(BufSpanA[b - 1] >= BufSpanB[b - 1] && BufSpanA[b] < BufSpanB[b]) Notify("Kumo twist: future Kumo turns bearish");
}
}
//+------------------------------------------------------------------+
//| Text objects |
//+------------------------------------------------------------------+
void Label(const string name, const int corner, const int anchor, const int x, const int y,
const string text, const color clr, const int size, const string font = "Arial")
{
if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
ObjectSetString(0, name, OBJPROP_FONT, font);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, name, OBJPROP_ZORDER, 10);
}
void PaintWatermark()
{
if(!InpShowWm)
{
ObjectDelete(0, PFX + "WM1");
ObjectDelete(0, PFX + "WM2");
return;
}
long bg = ChartGetInteger(0, CHART_BACKGROUND_COLOR);
int r = (int)(bg & 0xFF), g = (int)((bg >> 8) & 0xFF), b = (int)((bg >> 16) & 0xFF);
bool light = (r * 299 + g * 587 + b * 114) / 1000 > 128;
color orgCol = light ? C'30,34,41' : clrWhite;
int wCrypex = (int)(InpWmSize * 5.2); // approximate pixel width of "crypex" in Arial Black
Label(PFX + "WM1", CORNER_LEFT_LOWER, ANCHOR_LEFT_LOWER, InpWmGap, InpWmBottom, "crypex", BRAND_GREEN, InpWmSize, "Arial Black");
Label(PFX + "WM2", CORNER_LEFT_LOWER, ANCHOR_LEFT_LOWER, InpWmGap + wCrypex, InpWmBottom, ".org", orgCol, InpWmSize, "Arial Black");
}
//+------------------------------------------------------------------+
//| Status panel |
//+------------------------------------------------------------------+
string g_rowKey[20];
string g_rowVal[20];
color g_rowClr[20];
int g_rows = 0;
void Row(const string k, const string v, const color c)
{
if(g_rows >= 20) return;
g_rowKey[g_rows] = k;
g_rowVal[g_rows] = v;
g_rowClr[g_rows] = c;
g_rows++;
}
void PaintPanel()
{
int fs = InpPanelFont;
int lineH = fs * 2 + 2;
int keyW = fs * 17, valW = fs * 27, pad = 8;
bool right = (InpPanelPos == POS_TOP_RIGHT || InpPanelPos == POS_BOTTOM_RIGHT);
bool lower = (InpPanelPos == POS_BOTTOM_RIGHT || InpPanelPos == POS_BOTTOM_LEFT);
int corner = InpPanelPos == POS_TOP_RIGHT ? CORNER_RIGHT_UPPER : InpPanelPos == POS_TOP_LEFT ? CORNER_LEFT_UPPER : InpPanelPos == POS_BOTTOM_RIGHT ? CORNER_RIGHT_LOWER : CORNER_LEFT_LOWER;
int totalH = g_rows * lineH + 10;
string bgName = PFX + "PBG";
if(ObjectFind(0, bgName) < 0) ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, bgName, OBJPROP_CORNER, corner);
ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, pad);
ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, pad);
ObjectSetInteger(0, bgName, OBJPROP_XSIZE, keyW + valW + 18);
ObjectSetInteger(0, bgName, OBJPROP_YSIZE, totalH);
ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, InpPanelBg);
ObjectSetInteger(0, bgName, OBJPROP_COLOR, C'70,76,86');
ObjectSetInteger(0, bgName, OBJPROP_BORDER_TYPE, 0);
ObjectSetInteger(0, bgName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, bgName, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, bgName, OBJPROP_ZORDER, 5);
for(int i = 0; i < 20; i++)
{
string nk = PFX + "PK" + IntegerToString(i);
string nv = PFX + "PV" + IntegerToString(i);
if(i >= g_rows)
{
ObjectDelete(0, nk);
ObjectDelete(0, nv);
continue;
}
int y = pad + 6 + (lower ? (g_rows - 1 - i) : i) * lineH;
int anchor = right ? (lower ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER) : (lower ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER);
if(lower) y = pad + 6 + (g_rows - 1 - i) * lineH;
int xVal = right ? pad + 9 : pad + 9 + keyW;
int xKey = right ? pad + 9 + valW : pad + 9;
Label(nk, corner, anchor, xKey, y, g_rowKey[i], i == 0 ? clrWhite : C'150,156,166', fs);
Label(nv, corner, anchor, xVal, y, g_rowVal[i], g_rowClr[i], fs, i == 0 ? "Arial Bold" : "Arial");
}
}
//+------------------------------------------------------------------+
//| Support / resistance levels |
//+------------------------------------------------------------------+
void AddLevel(double &lp[], string &ln[], bool &lf[], int &ls[], bool &lh[], int &cnt,
const double p, const string name, const bool flat, const int str, const bool htf)
{
ArrayResize(lp, cnt + 1); ArrayResize(ln, cnt + 1); ArrayResize(lf, cnt + 1); ArrayResize(ls, cnt + 1); ArrayResize(lh, cnt + 1);
lp[cnt] = p; ln[cnt] = name; lf[cnt] = flat; ls[cnt] = str; lh[cnt] = htf;
cnt++;
}
void DrawLevel(const string tag, const datetime t1, const datetime t2, const double lo, const double hi,
const bool zone, const color clr, const bool flat, const bool htf, const string text)
{
string nl = PFX + "SRL" + tag, nt = PFX + "SRT" + tag;
if(zone)
{
string nz = PFX + "SRZ" + tag;
ObjectCreate(0, nz, OBJ_RECTANGLE, 0, t1, hi, t2, lo);
ObjectSetInteger(0, nz, OBJPROP_COLOR, clr);
ObjectSetInteger(0, nz, OBJPROP_FILL, false);
ObjectSetInteger(0, nz, OBJPROP_WIDTH, flat ? 2 : 1);
ObjectSetInteger(0, nz, OBJPROP_STYLE, htf ? STYLE_DOT : (flat ? STYLE_SOLID : STYLE_DASH));
ObjectSetInteger(0, nz, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nz, OBJPROP_HIDDEN, true);
}
else
{
ObjectCreate(0, nl, OBJ_TREND, 0, t1, lo, t2, lo);
ObjectSetInteger(0, nl, OBJPROP_COLOR, clr);
ObjectSetInteger(0, nl, OBJPROP_WIDTH, flat ? 2 : 1);
ObjectSetInteger(0, nl, OBJPROP_STYLE, htf ? STYLE_DOT : (flat ? STYLE_SOLID : STYLE_DASH));
ObjectSetInteger(0, nl, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, nl, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nl, OBJPROP_HIDDEN, true);
}
ObjectCreate(0, nt, OBJ_TEXT, 0, t2, (lo + hi) / 2.0);
ObjectSetString(0, nt, OBJPROP_TEXT, " " + text);
ObjectSetInteger(0, nt, OBJPROP_COLOR, clr);
ObjectSetInteger(0, nt, OBJPROP_FONTSIZE, InpPanelFont);
ObjectSetInteger(0, nt, OBJPROP_ANCHOR, ANCHOR_LEFT);
ObjectSetInteger(0, nt, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nt, OBJPROP_HIDDEN, true);
}
void PaintLevels(const int n, const datetime &time[], const double &close[], const double kijunNow, const bool kijunFlat,
const double kumoTop, const double kumoBot, const bool topFlat, const bool botFlat, const bool kumoOk,
const bool senkouBFlat, const int jc)
{
ObjectsDeleteAll(0, PFX + "SR");
if(!InpShowSR || !kumoOk) return;
const int L = n - 1;
double atr = BufAtr[L];
if(atr <= 0) return;
double clThr = atr * 0.25;
double mergeThr = InpSrCluster ? clThr : g_tick;
double lp[]; string ln[]; bool lf[]; int ls[]; bool lh[];
int cnt = 0;
AddLevel(lp, ln, lf, ls, lh, cnt, kijunNow, "Kijun", kijunFlat, kijunFlat ? 4 : 3, false);
AddLevel(lp, ln, lf, ls, lh, cnt, kumoBot, "Kumo bottom", botFlat, botFlat ? 4 : 2, false);
if(kumoTop - kumoBot > clThr) AddLevel(lp, ln, lf, ls, lh, cnt, kumoTop, "Kumo top", topFlat, topFlat ? 4 : 2, false);
// historical flat Senkou B and flat Kijun: lasting S/R magnets
for(int ni = 1; ni <= 20; ni++)
{
int idx = L - InpDisp - ni * 5;
if(idx >= InpFlatBars && BufSpanB[idx] != EMPTY_VALUE && IsFlat(BufSpanB, idx, InpFlatBars))
AddLevel(lp, ln, lf, ls, lh, cnt, BufSpanB[idx], "Old Kumo B", true, 4, false);
}
for(int ni = 1; ni <= 12; ni++)
{
int idx = L - ni * 5;
if(idx >= InpFlatBars && BufKijun[idx] != EMPTY_VALUE && IsFlat(BufKijun, idx, InpFlatBars) && MathAbs(BufKijun[idx] - kijunNow) > clThr)
AddLevel(lp, ln, lf, ls, lh, cnt, BufKijun[idx], "Old Kijun", true, 4, false);
}
// higher timeframe levels
if(g_htfReady && jc - InpDisp > InpFlatBars)
{
int hi0 = jc - InpDisp;
if(hA[hi0] != EMPTY_VALUE && hB[hi0] != EMPTY_VALUE)
{
double mTop = MathMax(hA[hi0], hB[hi0]), mBot = MathMin(hA[hi0], hB[hi0]);
bool mTopFlat = IsFlatEdge(hA, hB, hi0, InpFlatBars, true);
bool mBotFlat = IsFlatEdge(hA, hB, hi0, InpFlatBars, false);
AddLevel(lp, ln, lf, ls, lh, cnt, mBot, "HTF Kumo bottom", mBotFlat, mBotFlat ? 4 : 2, true);
if(mTop - mBot > clThr) AddLevel(lp, ln, lf, ls, lh, cnt, mTop, "HTF Kumo top", mTopFlat, mTopFlat ? 4 : 2, true);
}
for(int ni = 1; ni <= 20; ni++)
{
int hidx = hi0 - ni * 5;
if(hidx < InpFlatBars) break;
if(hB[hidx] != EMPTY_VALUE && IsFlat(hB, hidx, InpFlatBars))
AddLevel(lp, ln, lf, ls, lh, cnt, hB[hidx], "Old HTF Kumo B", true, 4, true);
}
}
if(cnt == 0) return;
// sort ascending by price (insertion sort on indices)
int idxs[];
ArrayResize(idxs, cnt);
for(int i = 0; i < cnt; i++) idxs[i] = i;
for(int i = 1; i < cnt; i++)
{
int key = idxs[i];
int j = i - 1;
while(j >= 0 && lp[idxs[j]] > lp[key])
{
idxs[j + 1] = idxs[j];
j--;
}
idxs[j + 1] = key;
}
// cluster
double cMin[], cMax[]; string cNm[]; bool cFl[], cH[]; int cCt[];
int nCl = 0;
bool used[];
ArrayResize(used, cnt);
for(int i = 0; i < cnt; i++) used[i] = false;
for(int i = 0; i < cnt; i++)
{
int oi = idxs[i];
if(used[oi]) continue;
double pLo = lp[oi], pHi = lp[oi];
string nm = ln[oi];
bool fl = lf[oi], hh = lh[oi];
int ct = 1;
used[oi] = true;
for(int j = i + 1; j < cnt; j++)
{
int oj = idxs[j];
if(lp[oj] - pHi > mergeThr) break;
if(used[oj]) continue;
pHi = lp[oj];
if(StringFind(nm, ln[oj]) < 0) nm = nm + " + " + ln[oj];
if(lf[oj]) fl = true;
if(!lh[oj]) hh = false;
ct++;
used[oj] = true;
}
ArrayResize(cMin, nCl + 1); ArrayResize(cMax, nCl + 1); ArrayResize(cNm, nCl + 1);
ArrayResize(cFl, nCl + 1); ArrayResize(cH, nCl + 1); ArrayResize(cCt, nCl + 1);
cMin[nCl] = pLo; cMax[nCl] = pHi; cNm[nCl] = nm; cFl[nCl] = fl; cH[nCl] = hh; cCt[nCl] = ct;
nCl++;
}
int nS = 0;
for(int k = 0; k < nCl; k++)
if((cMin[k] + cMax[k]) / 2.0 < close[L]) nS++;
int sCur = nS, rCur = 1;
datetime t1 = time[L];
datetime t2 = time[L] + (datetime)(InpSrExtend * PeriodSeconds(_Period));
for(int k = 0; k < nCl; k++)
{
double mid = (cMin[k] + cMax[k]) / 2.0;
bool isR = mid >= close[L];
int rank = isR ? rCur : sCur;
if(isR) rCur++; else sCur--;
if(rank > InpSrMax) continue;
bool zone = cCt[k] > 1 && cMax[k] > cMin[k] && InpSrCluster;
string rng = (cCt[k] > 1 && cMax[k] > cMin[k]) ? Px(cMin[k]) + " - " + Px(cMax[k]) : Px(cMin[k]);
string txt = (isR ? "R" : "S") + IntegerToString(rank) + (cH[k] ? "[H]" : "") + " " + rng + " " + cNm[k];
DrawLevel(IntegerToString(k), t1, t2, cMin[k], cMax[k], zone, isR ? InpResistanceColor : InpSupportColor, cFl[k], cH[k], txt);
}
}
//+------------------------------------------------------------------+
//| Analysis of the last bar: status panel rows + S/R |
//+------------------------------------------------------------------+
void Analyze(const int n, const datetime &time[], const double &high[], const double &low[], const double &close[])
{
const int L = n - 1;
const int c0 = L - InpDisp;
g_rows = 0;
color tBull = C'8,153,129', tBear = C'242,54,69', tWarn = C'255,152,0', tBase = clrWhite;
double atr = BufAtr[L];
double tenkan = BufTenkan[L], kijun = BufKijun[L];
bool kumoOk = (c0 >= 0 && BufSpanA[c0] != EMPTY_VALUE && BufSpanB[c0] != EMPTY_VALUE && tenkan != EMPTY_VALUE && kijun != EMPTY_VALUE && atr > 0);
double kumoTop = 0, kumoBot = 0;
bool kumoBull = false, above = false, below = false, inside = false;
if(kumoOk)
{
kumoTop = MathMax(BufSpanA[c0], BufSpanB[c0]);
kumoBot = MathMin(BufSpanA[c0], BufSpanB[c0]);
kumoBull = BufSpanA[c0] > BufSpanB[c0];
above = close[L] > kumoTop;
below = close[L] < kumoBot;
inside = !above && !below;
}
Row("Crypex", _Symbol, clrWhite);
if(!kumoOk)
{
Row("Status", "Warm-up (not enough bars)", tBase);
if(InpShowPanel) PaintPanel();
PaintLevels(n, time, close, 0, false, 0, 0, false, false, false, false, 0);
return;
}
bool tkBull = tenkan > kijun, tkBear = tenkan < kijun;
bool futBull = BufSpanA[L] > BufSpanB[L], futBear = BufSpanA[L] < BufSpanB[L];
bool kijunFlat = IsFlat(BufKijun, L, InpFlatBars);
double slope5 = (L >= 5 && BufKijun[L - 5] != EMPTY_VALUE) ? kijun - BufKijun[L - 5] : 0;
bool kijunRise = !kijunFlat && slope5 > 0, kijunFall = !kijunFlat && slope5 < 0;
bool senkouBFlat = IsFlat(BufSpanB, c0, InpFlatBars);
bool aFlatNow = IsFlat(BufSpanA, c0, InpFlatBars);
double sbLag = BufSpanB[c0], sbLag5 = (c0 >= 5) ? BufSpanB[c0 - 5] : EMPTY_VALUE;
double tenkanDist = MathAbs(close[L] - tenkan) / atr;
double chHi = (L - InpDisp >= 0) ? high[L - InpDisp] : EMPTY_VALUE;
double chLo = (L - InpDisp >= 0) ? low[L - InpDisp] : EMPTY_VALUE;
double chScore = (chHi == EMPTY_VALUE) ? 0.0 : (close[L] > chHi ? 1.0 : (close[L] < chLo ? -1.0 : 0.0));
double score = (above ? 2.0 : (below ? -2.0 : 0.0)) + (tkBull ? 1.0 : (tkBear ? -1.0 : 0.0))
+ (close[L] > kijun ? 1.0 : (close[L] < kijun ? -1.0 : 0.0)) + (futBull ? 1.0 : (futBear ? -1.0 : 0.0))
+ chScore + (kijunRise ? 0.5 : (kijunFall ? -0.5 : 0.0));
string bias = score >= 5.5 ? "Strong Bullish" : score >= 2.0 ? "Bullish Bias" : score <= -5.5 ? "Strong Bearish" : score <= -2.0 ? "Bearish Bias" : "Neutral / Range";
Row("Tenkan / Kijun", Px(tenkan) + " / " + Px(kijun), tBase);
Row("Regime", above ? "Above Kumo" : (below ? "Below Kumo" : "Inside Kumo"), above ? tBull : (below ? tBear : tWarn));
Row("Cloud (now)", kumoBull ? "Green (A>B)" : "Red (A<B)", kumoBull ? tBull : tBear);
Row("Future cloud", futBull ? "Bullish (A>B)" : (futBear ? "Bearish (A<B)" : "Flat / twisting"), futBull ? tBull : (futBear ? tBear : tBase));
string align = (kumoBull && above && tkBull) ? "Strong bull stack" : (!kumoBull && below && tkBear) ? "Strong bear stack" :
(above && !tkBull) ? "Pullback risk (above cloud)" : (below && !tkBear) ? "Bounce risk (below cloud)" : "Mixed / transit";
Row("Alignment", align, (above && tkBull) ? tBull : ((below && tkBear) ? tBear : tWarn));
Row("TK momentum", tkBull ? "Tenkan > Kijun" : (tkBear ? "Tenkan < Kijun" : "Flat"), tkBull ? tBull : (tkBear ? tBear : tBase));
Row("Kijun slope", kijunFlat ? "Flat · equilibrium magnet" : (kijunRise ? "Rising" : (kijunFall ? "Falling" : "Sideways")), kijunFlat ? tWarn : (kijunRise ? tBull : (kijunFall ? tBear : tBase)));
string sbTxt = senkouBFlat ? "Flat S/R @ " + Px(sbLag) : ((sbLag5 != EMPTY_VALUE && sbLag > sbLag5) ? "Rising" : ((sbLag5 != EMPTY_VALUE && sbLag < sbLag5) ? "Falling" : "Sideways"));
Row("Senkou B", sbTxt, senkouBFlat ? tWarn : tBase);
Row("Chikou", chHi == EMPTY_VALUE ? "Warm-up" : (chScore > 0 ? "Clear bullish" : (chScore < 0 ? "Clear bearish" : "Trapped (neutral)")), chScore > 0 ? tBull : (chScore < 0 ? tBear : tWarn));
Row("Tenkan distance", DoubleToString(tenkanDist, 2) + " ATR" + (tenkanDist > 1.0 ? " ! overext" : (tenkanDist < 0.3 ? " · near" : "")), tenkanDist > 1.0 ? tWarn : tBase);
double thick = kumoTop - kumoBot;
double thickAtr = thick / atr;
Row("Kumo thickness", Px(thick) + (thickAtr > 1.5 ? " · Thick" : (thickAtr < 0.5 ? " · Thin" : " · Normal")), tBase);
double dTop = kumoTop - close[L], dBot = close[L] - kumoBot;
Row("Distance (edge)", "up " + DoubleToString(dTop / g_tick, 0) + " t / dn " + DoubleToString(dBot / g_tick, 0) + " t", tBase);
double nearest = MathMin(MathAbs(dTop), MathAbs(dBot));
Row("Nearest · ATR", Px(nearest) + " · " + DoubleToString(nearest / atr, 2) + " ATR", tBase);
Row("ATR(14)", Px(atr), tBase);
Row("Score · Bias", DoubleToString(score, 1) + " -> " + bias, score >= 2.0 ? tBull : (score <= -2.0 ? tBear : tWarn));
int jc = -1;
if(g_htfReady && ArraySize(jMap) == n) jc = jMap[L];
if(g_htfReady && jc - InpDisp >= 0 && hA[jc - InpDisp] != EMPTY_VALUE && hB[jc - InpDisp] != EMPTY_VALUE)
{
double mTop = MathMax(hA[jc - InpDisp], hB[jc - InpDisp]), mBot = MathMin(hA[jc - InpDisp], hB[jc - InpDisp]);
bool mBull = hA[jc - InpDisp] > hB[jc - InpDisp];
string tfName = EnumToString(g_htf);
StringReplace(tfName, "PERIOD_", "");
Row("HTF Kumo", (close[L] > mTop ? "Above Kumo (" : (close[L] < mBot ? "Below Kumo (" : "In Kumo (")) + tfName + ")", close[L] > mTop ? tBull : (close[L] < mBot ? tBear : tWarn));
Row("HTF cloud", mBull ? "HTF green" : "HTF red", mBull ? tBull : tBear);
}
else
{
Row("HTF Kumo", g_htfOn ? "HTF warm-up" : "-", tBase);
}
Row("Shift · Params", IntegerToString(InpDisp) + " ch" + (g_htfOn ? " · " + IntegerToString(g_fwd) + " HTF" : "") + " | " + IntegerToString(InpTenkan) + "/" + IntegerToString(InpKijun) + "/" + IntegerToString(InpSenkouB), tBase);
if(InpShowPanel) PaintPanel();
else ObjectsDeleteAll(0, PFX + "P");
bool botIsB = BufSpanB[c0] <= BufSpanA[c0];
bool botFlat = botIsB ? senkouBFlat : aFlatNow;
bool topFlat = botIsB ? aFlatNow : senkouBFlat;
PaintLevels(n, time, close, kijun, kijunFlat, kumoTop, kumoBot, topFlat, botFlat, true, senkouBFlat, jc);
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
const double &open[], const double &high[], const double &low[], const double &close[],
const long &tick_volume[], const long &volume[], const int &spread[])
{
const int n = rates_total;
const int need = MathMax(InpSenkouB, MathMax(InpKijun, InpTenkan)) + InpDisp + 5;
if(n < need) return 0;
int start = (prev_calculated > 2) ? prev_calculated - 2 : 0;
if(prev_calculated == 0)
for(int i = 0; i < n; i++)
{
BufHtfA[i] = EMPTY_VALUE;
BufHtfB[i] = EMPTY_VALUE;
BufHtfKijun[i] = EMPTY_VALUE;
}
ComputeChart(n, start, high, low, close);
uint now = GetTickCount();
bool newBar = (time[n - 1] != g_lastBar) || prev_calculated == 0; // full recalculation also rebuilds the HTF map
if(newBar)
{
g_lastBar = time[n - 1];
if(g_htfOn)
{
BuildHtf(n, time);
g_lastHtfTry = now;
}
CheckAlerts(n, time, close);
}
else if(g_htfOn && !g_htfReady && now - g_lastHtfTry > 5000)
{
BuildHtf(n, time); // HTF history was not loaded yet: retry every 5 s
g_lastHtfTry = now;
}
if(newBar || now - g_lastPaint > 1500 || prev_calculated == 0)
{
g_lastPaint = now;
Analyze(n, time, high, low, close);
PaintWatermark();
ChartRedraw();
}
g_firstDone = true;
return rates_total;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Crypex | Ichimoku Advanced (MT4) |
//| HTF Kumo + ranked S/R levels + status panel + alerts |
//| © Crypex.org — All rights reserved. | حق نشر محفوظ برای Crypex.org |
//| License: personal, non-commercial use only. Redistribution or |
//| resale without written permission is prohibited. |
//| مجوز: استفاده شخصی و غیرتجاری. بازنشر یا فروش مجدد بدون |
//| اجازهی کتبی ممنوع است. |
//| Educational tool, not investment advice. |
//+------------------------------------------------------------------+
#property copyright "Crypex.org"
#property link "https://crypex.org"
#property version "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 8
enum ENUM_PANEL_POS { POS_TOP_RIGHT, POS_TOP_LEFT, POS_BOTTOM_RIGHT, POS_BOTTOM_LEFT };
enum ENUM_CAL_MODE { CAL_AUTO, CAL_247, CAL_WEEKDAYS };
// ---- Ichimoku
input int InpTenkan = 9; // Tenkan-sen
input int InpKijun = 26; // Kijun-sen
input int InpSenkouB = 52; // Senkou Span B
input int InpDisp = 26; // Displacement (Senkou +, Chikou -)
input int InpFlatBars = 10; // Min flat bars (Kijun / Senkou B)
// ---- Lines and Kumo
input bool InpShowTenkan = true;
input bool InpShowKijun = true;
input bool InpShowChikou = true;
input bool InpShowKumo = true; // Senkou A/B lines
input bool InpFillKumo = true; // Fill the Kumo (chart objects)
input color InpTenkanColor = C'11,163,28';
input color InpKijunColor = C'255,10,23';
input color InpChikouColor = C'120,123,134';
input color InpSpanAColor = C'102,187,106';
input color InpSpanBColor = C'229,115,115';
input color InpBullKumo = C'129,199,132';
input color InpBearKumo = C'239,154,154';
input int InpCloudBars = 300; // Bars of Kumo fill (fewer = lighter chart)
// ---- Higher timeframe Kumo
input bool InpShowHtf = true;
input bool InpHtfAuto = true; // Auto next timeframe
input ENUM_TIMEFRAMES InpHtfManual = PERIOD_H4; // Manual timeframe
input bool InpShowHtfKijun = true;
input ENUM_CAL_MODE InpCalendar = CAL_AUTO; // Market calendar (weekly/monthly HTF projection)
input double InpRatioOverride = 0.0; // Chart bars per HTF bar (0 = auto)
input int InpHtfBars = 2000; // HTF bars to load
input color InpHtfBullKumo = C'178,223,180';
input color InpHtfBearKumo = C'247,196,196';
input color InpHtfKijunColor = C'178,34,34';
// ---- Markers and alerts
input bool InpMarkTk = true; // TK cross arrows
input bool InpMarkKumoBo = false; // Kumo breakout arrows
input bool InpAlertPopup = true;
input bool InpAlertPush = false;
input bool InpAlertMail = false;
// ---- Status panel
input bool InpShowPanel = true;
input ENUM_PANEL_POS InpPanelPos = POS_TOP_RIGHT;
input int InpPanelFont = 8;
input color InpPanelBg = C'21,26,32';
// ---- Support / Resistance levels
input bool InpShowSR = true;
input int InpSrExtend = 20; // Extend (bars)
input bool InpSrCluster = true; // Merge nearby levels into zones
input int InpSrMax = 10; // Max levels per side
input color InpSupportColor = C'11,163,28';
input color InpResistanceColor = C'255,10,23';
// ---- Watermark
input bool InpShowWm = true; // Show crypex.org watermark
input int InpWmSize = 18;
input int InpWmGap = 60; // Gap from left (px), clears the chart logo
input int InpWmBottom = 14; // Gap from bottom (px)
// ---- Calculation
input int InpMaxBars = 3000; // Bars used for calculation
const string PFX = "CRX_ICH_";
const color BRAND_GREEN = C'11,163,28';
// plotted buffers (MT4 buffers are series: index 0 = newest bar)
double BufTenkan[], BufKijun[], BufChikou[], BufSpanA[], BufSpanB[], BufHtfKijun[], BufTkUp[], BufTkDn[];
// working arrays, chronological (index 0 = oldest of the used range)
double cT[], cK[], cA[], cB[], cAtr[], cHi[], cLo[], cCl[];
double cTkUp[], cTkDn[], cKbUp[], cKbDn[], cHtfA[], cHtfB[], cHtfK[];
datetime cTm[];
int N = 0;
// higher-timeframe store (chronological)
double hHi[], hLo[], hT[], hK[], hA[], hB[];
datetime hTm[];
int jMap[];
int hN = 0;
int g_htf = 0;
bool g_htfOn = false;
bool g_htfReady = false;
int g_fwd = 0;
double g_ratio = 1.0;
double g_tick = 0.0;
datetime g_lastBar = 0;
datetime g_lastAlertBar = 0;
uint g_lastPaint = 0;
uint g_lastHtfTry = 0;
bool g_firstDone = false;
//+------------------------------------------------------------------+
bool IsCryptoSymbol()
{
string s = Symbol();
string keys[] = {"BTC", "ETH", "LTC", "XRP", "SOL", "DOGE", "ADA", "BNB", "TRX", "DOT"};
for(int i = 0; i < ArraySize(keys); i++)
if(StringFind(s, keys[i]) >= 0) return true;
return false;
}
int AutoNextTf()
{
int s = PeriodSeconds(_Period);
if(s <= 60) return PERIOD_H1;
if(s <= 3600) return PERIOD_H4;
if(s <= 14400) return PERIOD_D1;
if(s <= 86400) return PERIOD_W1;
return PERIOD_MN1;
}
//+------------------------------------------------------------------+
int OnInit()
{
IndicatorBuffers(8);
SetIndexBuffer(0, BufTenkan);
SetIndexBuffer(1, BufKijun);
SetIndexBuffer(2, BufChikou);
SetIndexBuffer(3, BufSpanA);
SetIndexBuffer(4, BufSpanB);
SetIndexBuffer(5, BufHtfKijun);
SetIndexBuffer(6, BufTkUp);
SetIndexBuffer(7, BufTkDn);
g_tick = MarketInfo(Symbol(), MODE_TICKSIZE);
if(g_tick <= 0) g_tick = Point;
SetIndexStyle(0, InpShowTenkan ? DRAW_LINE : DRAW_NONE, STYLE_SOLID, 2, InpTenkanColor);
SetIndexStyle(1, InpShowKijun ? DRAW_LINE : DRAW_NONE, STYLE_SOLID, 2, InpKijunColor);
SetIndexStyle(2, InpShowChikou ? DRAW_LINE : DRAW_NONE, STYLE_SOLID, 1, InpChikouColor);
SetIndexStyle(3, InpShowKumo ? DRAW_LINE : DRAW_NONE, STYLE_SOLID, 1, InpSpanAColor);
SetIndexStyle(4, InpShowKumo ? DRAW_LINE : DRAW_NONE, STYLE_SOLID, 1, InpSpanBColor);
SetIndexShift(2, -InpDisp);
SetIndexShift(3, InpDisp);
SetIndexShift(4, InpDisp);
g_htf = InpHtfAuto ? AutoNextTf() : (int)InpHtfManual;
int chartSec = PeriodSeconds(_Period);
int htfSec = PeriodSeconds(g_htf);
g_htfOn = InpShowHtf && htfSec > chartSec && chartSec > 0;
bool weekdays = (InpCalendar == CAL_WEEKDAYS) || (InpCalendar == CAL_AUTO && !IsCryptoSymbol());
double weekFactor = (weekdays && htfSec >= 604800 && chartSec < 604800) ? 5.0 / 7.0 : 1.0;
g_ratio = g_htfOn ? (InpRatioOverride > 0 ? InpRatioOverride : (double)htfSec / chartSec * weekFactor) : 1.0;
g_fwd = g_htfOn ? (int)MathMax(0, MathRound((InpDisp - 1) * g_ratio)) : 0;
if(g_fwd > 4900) g_htfOn = false;
SetIndexStyle(5, (g_htfOn && InpShowHtfKijun) ? DRAW_LINE : DRAW_NONE, STYLE_DASH, 1, InpHtfKijunColor);
SetIndexStyle(6, InpMarkTk ? DRAW_ARROW : DRAW_NONE, STYLE_SOLID, 1, InpTenkanColor);
SetIndexStyle(7, InpMarkTk ? DRAW_ARROW : DRAW_NONE, STYLE_SOLID, 1, InpKijunColor);
SetIndexArrow(6, 233);
SetIndexArrow(7, 234);
SetIndexLabel(0, "Tenkan-sen");
SetIndexLabel(1, "Kijun-sen");
SetIndexLabel(2, "Chikou Span");
SetIndexLabel(3, "Senkou Span A");
SetIndexLabel(4, "Senkou Span B");
SetIndexLabel(5, "HTF Kijun");
SetIndexLabel(6, "TK Cross Up");
SetIndexLabel(7, "TK Cross Down");
for(int p = 0; p < 8; p++) SetIndexEmptyValue(p, EMPTY_VALUE);
IndicatorShortName("Crypex Ichimoku");
IndicatorDigits(Digits);
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PFX);
}
//+------------------------------------------------------------------+
//| helpers |
//+------------------------------------------------------------------+
double Donchian(const double &hi[], const double &lo[], const int idx, const int len)
{
if(idx - len + 1 < 0) return EMPTY_VALUE;
double h = hi[idx], l = lo[idx];
for(int k = 1; k < len; k++)
{
if(hi[idx - k] > h) h = hi[idx - k];
if(lo[idx - k] < l) l = lo[idx - k];
}
return (h + l) / 2.0;
}
// value stayed within one tick over the whole window (len+1 bars)
bool IsFlat(const double &arr[], const int idx, const int len)
{
if(idx - len < 0) return false;
double mx = -DBL_MAX, mn = DBL_MAX;
for(int k = 0; k <= len; k++)
{
double v = arr[idx - k];
if(v == EMPTY_VALUE) return false;
if(v > mx) mx = v;
if(v < mn) mn = v;
}
return (mx - mn) <= g_tick;
}
// flatness of the upper (top=true) or lower edge of a Kumo made of spans a and b
bool IsFlatEdge(const double &a[], const double &b[], const int idx, const int len, const bool top)
{
if(idx - len < 0) return false;
double mx = -DBL_MAX, mn = DBL_MAX;
for(int k = 0; k <= len; k++)
{
double va = a[idx - k], vb = b[idx - k];
if(va == EMPTY_VALUE || vb == EMPTY_VALUE) return false;
double v = top ? MathMax(va, vb) : MathMin(va, vb);
if(v > mx) mx = v;
if(v < mn) mn = v;
}
return (mx - mn) <= g_tick;
}
string Px(const double v)
{
return DoubleToString(v, _Digits);
}
//+------------------------------------------------------------------+
//| Build the higher timeframe Ichimoku and map it to chart bars |
//+------------------------------------------------------------------+
void BuildHtf(const int n, const datetime &time[])
{
g_htfReady = false;
ArrayResize(cHtfA, n); ArrayResize(cHtfB, n); ArrayResize(cHtfK, n);
for(int i = 0; i < n; i++)
{
cHtfA[i] = EMPTY_VALUE;
cHtfB[i] = EMPTY_VALUE;
cHtfK[i] = EMPTY_VALUE;
}
int want = MathMin(iBars(Symbol(), g_htf), InpHtfBars);
int need = InpSenkouB + InpDisp + 5;
if(want < need) return;
ArrayResize(hHi, want); ArrayResize(hLo, want); ArrayResize(hTm, want);
for(int j = 0; j < want; j++)
{
int s = want - 1 - j;
hHi[j] = iHigh(Symbol(), g_htf, s);
hLo[j] = iLow(Symbol(), g_htf, s);
hTm[j] = iTime(Symbol(), g_htf, s);
if(hHi[j] <= 0 || hLo[j] <= 0 || hTm[j] <= 0) return; // history not loaded yet
}
hN = want;
ArrayResize(hT, hN); ArrayResize(hK, hN); ArrayResize(hA, hN); ArrayResize(hB, hN);
for(int j = 0; j < hN; j++)
{
hT[j] = Donchian(hHi, hLo, j, InpTenkan);
hK[j] = Donchian(hHi, hLo, j, InpKijun);
hA[j] = (hT[j] != EMPTY_VALUE && hK[j] != EMPTY_VALUE) ? (hT[j] + hK[j]) / 2.0 : EMPTY_VALUE;
hB[j] = Donchian(hHi, hLo, j, InpSenkouB);
}
ArrayResize(jMap, n);
int j = 0;
for(int i = 0; i < n; i++)
{
while(j + 1 < hN && hTm[j + 1] <= time[i]) j++;
jMap[i] = (hTm[0] <= time[i]) ? j : -1;
int c = jMap[i] - 1; // last COMPLETED HTF bar: no repainting
if(c >= 0 && hA[c] != EMPTY_VALUE && hB[c] != EMPTY_VALUE)
{
cHtfA[i] = hA[c];
cHtfB[i] = hB[c];
cHtfK[i] = hK[c];
}
}
g_htfReady = true;
}
//+------------------------------------------------------------------+
//| Chart-TF Ichimoku (chronological arrays), markers |
//+------------------------------------------------------------------+
void ComputeChart(const int n, const double &high[], const double &low[], const double &close[])
{
ArrayResize(cT, n); ArrayResize(cK, n); ArrayResize(cA, n); ArrayResize(cB, n); ArrayResize(cAtr, n);
ArrayResize(cTkUp, n); ArrayResize(cTkDn, n); ArrayResize(cKbUp, n); ArrayResize(cKbDn, n);
for(int i = 0; i < n; i++)
{
cT[i] = Donchian(high, low, i, InpTenkan);
cK[i] = Donchian(high, low, i, InpKijun);
cA[i] = (cT[i] != EMPTY_VALUE && cK[i] != EMPTY_VALUE) ? (cT[i] + cK[i]) / 2.0 : EMPTY_VALUE;
cB[i] = Donchian(high, low, i, InpSenkouB);
double tr = high[i] - low[i];
if(i == 0)
cAtr[i] = tr;
else
{
tr = MathMax(tr, MathMax(MathAbs(high[i] - close[i - 1]), MathAbs(low[i] - close[i - 1])));
cAtr[i] = (cAtr[i - 1] * 13.0 + tr) / 14.0;
}
cTkUp[i] = EMPTY_VALUE; cTkDn[i] = EMPTY_VALUE; cKbUp[i] = EMPTY_VALUE; cKbDn[i] = EMPTY_VALUE;
if(i < 1) continue;
if(cT[i] == EMPTY_VALUE || cK[i] == EMPTY_VALUE || cT[i - 1] == EMPTY_VALUE || cK[i - 1] == EMPTY_VALUE) continue;
if(cT[i - 1] <= cK[i - 1] && cT[i] > cK[i]) cTkUp[i] = low[i] - cAtr[i] * 0.5;
if(cT[i - 1] >= cK[i - 1] && cT[i] < cK[i]) cTkDn[i] = high[i] + cAtr[i] * 0.5;
int c0 = i - InpDisp, c1 = i - 1 - InpDisp;
if(c1 < 0 || cA[c0] == EMPTY_VALUE || cB[c0] == EMPTY_VALUE || cA[c1] == EMPTY_VALUE || cB[c1] == EMPTY_VALUE) continue;
double top0 = MathMax(cA[c0], cB[c0]), bot0 = MathMin(cA[c0], cB[c0]);
double top1 = MathMax(cA[c1], cB[c1]), bot1 = MathMin(cA[c1], cB[c1]);
if(close[i - 1] <= top1 && close[i] > top0) cKbUp[i] = low[i] - cAtr[i] * 0.9;
if(close[i - 1] >= bot1 && close[i] < bot0) cKbDn[i] = high[i] + cAtr[i] * 0.9;
}
}
// copy chronological results into the series buffers (shift = n-1-i)
void PushBuffers(const int n, const bool all)
{
bool htfLine = g_htfOn && g_htfReady && InpShowHtfKijun && ArraySize(cHtfK) == n;
for(int s = 0; s < n; s++)
{
int i = n - 1 - s;
BufTenkan[s] = cT[i];
BufKijun[s] = cK[i];
BufChikou[s] = cCl[i];
BufSpanA[s] = cA[i];
BufSpanB[s] = cB[i];
BufHtfKijun[s] = htfLine ? cHtfK[i] : EMPTY_VALUE;
BufTkUp[s] = cTkUp[i];
BufTkDn[s] = cTkDn[i];
}
if(all)
for(int s = n; s < Bars; s++)
{
BufTenkan[s] = EMPTY_VALUE; BufKijun[s] = EMPTY_VALUE; BufChikou[s] = EMPTY_VALUE;
BufSpanA[s] = EMPTY_VALUE; BufSpanB[s] = EMPTY_VALUE; BufHtfKijun[s] = EMPTY_VALUE;
BufTkUp[s] = EMPTY_VALUE; BufTkDn[s] = EMPTY_VALUE;
}
}
//+------------------------------------------------------------------+
//| Alerts for the last closed bar |
//+------------------------------------------------------------------+
void Notify(const string msg)
{
string full = "Crypex Ichimoku " + _Symbol + " " + EnumToString(_Period) + ": " + msg;
if(InpAlertPopup) Alert(full);
if(InpAlertPush) SendNotification(full);
if(InpAlertMail) SendMail("Crypex Ichimoku", full);
}
void CheckAlerts(const int n, const datetime &time[], const double &close[])
{
int b = n - 2; // last closed bar
if(b < InpDisp + 3) return;
if(time[b] == g_lastAlertBar) return;
g_lastAlertBar = time[b];
if(!g_firstDone) return; // never alert for history on first load
double t0 = cT[b], k0 = cK[b], t1 = cT[b - 1], k1 = cK[b - 1];
if(t0 == EMPTY_VALUE || k0 == EMPTY_VALUE || t1 == EMPTY_VALUE || k1 == EMPTY_VALUE) return;
if(t1 <= k1 && t0 > k0) Notify("Tenkan crossed above Kijun");
if(t1 >= k1 && t0 < k0) Notify("Tenkan crossed below Kijun");
int c0 = b - InpDisp, c1 = b - 1 - InpDisp;
if(c1 >= 0 && cA[c0] != EMPTY_VALUE && cB[c0] != EMPTY_VALUE && cA[c1] != EMPTY_VALUE && cB[c1] != EMPTY_VALUE)
{
double top0 = MathMax(cA[c0], cB[c0]), bot0 = MathMin(cA[c0], cB[c0]);
double top1 = MathMax(cA[c1], cB[c1]), bot1 = MathMin(cA[c1], cB[c1]);
if(close[b - 1] <= top1 && close[b] > top0) Notify("Price closed above the Kumo");
if(close[b - 1] >= bot1 && close[b] < bot0) Notify("Price closed below the Kumo");
}
if(cA[b] != EMPTY_VALUE && cB[b] != EMPTY_VALUE && cA[b - 1] != EMPTY_VALUE && cB[b - 1] != EMPTY_VALUE)
{
if(cA[b - 1] <= cB[b - 1] && cA[b] > cB[b]) Notify("Kumo twist: future Kumo turns bullish");
if(cA[b - 1] >= cB[b - 1] && cA[b] < cB[b]) Notify("Kumo twist: future Kumo turns bearish");
}
}
//+------------------------------------------------------------------+
//| Text objects |
//+------------------------------------------------------------------+
void Label(const string name, const int corner, const int anchor, const int x, const int y,
const string text, const color clr, const int size, const string font = "Arial")
{
if(ObjectFind(0, name) < 0) ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, size);
ObjectSetString(0, name, OBJPROP_FONT, font);
ObjectSetString(0, name, OBJPROP_TEXT, text);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, name, OBJPROP_ZORDER, 10);
}
void PaintWatermark()
{
if(!InpShowWm)
{
ObjectDelete(0, PFX + "WM1");
ObjectDelete(0, PFX + "WM2");
return;
}
long bg = ChartGetInteger(0, CHART_BACKGROUND_COLOR);
int r = (int)(bg & 0xFF), g = (int)((bg >> 8) & 0xFF), b = (int)((bg >> 16) & 0xFF);
bool light = (r * 299 + g * 587 + b * 114) / 1000 > 128;
color orgCol = light ? C'30,34,41' : clrWhite;
int wCrypex = (int)(InpWmSize * 5.2); // approximate pixel width of "crypex" in Arial Black
Label(PFX + "WM1", CORNER_LEFT_LOWER, ANCHOR_LEFT_LOWER, InpWmGap, InpWmBottom, "crypex", BRAND_GREEN, InpWmSize, "Arial Black");
Label(PFX + "WM2", CORNER_LEFT_LOWER, ANCHOR_LEFT_LOWER, InpWmGap + wCrypex, InpWmBottom, ".org", orgCol, InpWmSize, "Arial Black");
}
//+------------------------------------------------------------------+
//| Status panel |
//+------------------------------------------------------------------+
string g_rowKey[20];
string g_rowVal[20];
color g_rowClr[20];
int g_rows = 0;
void Row(const string k, const string v, const color c)
{
if(g_rows >= 20) return;
g_rowKey[g_rows] = k;
g_rowVal[g_rows] = v;
g_rowClr[g_rows] = c;
g_rows++;
}
void PaintPanel()
{
int fs = InpPanelFont;
int lineH = fs * 2 + 2;
int keyW = fs * 17, valW = fs * 27, pad = 8;
bool right = (InpPanelPos == POS_TOP_RIGHT || InpPanelPos == POS_BOTTOM_RIGHT);
bool lower = (InpPanelPos == POS_BOTTOM_RIGHT || InpPanelPos == POS_BOTTOM_LEFT);
int corner = InpPanelPos == POS_TOP_RIGHT ? CORNER_RIGHT_UPPER : InpPanelPos == POS_TOP_LEFT ? CORNER_LEFT_UPPER : InpPanelPos == POS_BOTTOM_RIGHT ? CORNER_RIGHT_LOWER : CORNER_LEFT_LOWER;
int totalH = g_rows * lineH + 10;
string bgName = PFX + "PBG";
if(ObjectFind(0, bgName) < 0) ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
ObjectSetInteger(0, bgName, OBJPROP_CORNER, corner);
ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, pad);
ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, pad);
ObjectSetInteger(0, bgName, OBJPROP_XSIZE, keyW + valW + 18);
ObjectSetInteger(0, bgName, OBJPROP_YSIZE, totalH);
ObjectSetInteger(0, bgName, OBJPROP_BGCOLOR, InpPanelBg);
ObjectSetInteger(0, bgName, OBJPROP_COLOR, C'70,76,86');
ObjectSetInteger(0, bgName, OBJPROP_BORDER_TYPE, 0);
ObjectSetInteger(0, bgName, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, bgName, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, bgName, OBJPROP_ZORDER, 5);
for(int i = 0; i < 20; i++)
{
string nk = PFX + "PK" + IntegerToString(i);
string nv = PFX + "PV" + IntegerToString(i);
if(i >= g_rows)
{
ObjectDelete(0, nk);
ObjectDelete(0, nv);
continue;
}
int y = pad + 6 + (lower ? (g_rows - 1 - i) : i) * lineH;
int anchor = right ? (lower ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER) : (lower ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER);
if(lower) y = pad + 6 + (g_rows - 1 - i) * lineH;
int xVal = right ? pad + 9 : pad + 9 + keyW;
int xKey = right ? pad + 9 + valW : pad + 9;
Label(nk, corner, anchor, xKey, y, g_rowKey[i], i == 0 ? clrWhite : C'150,156,166', fs);
Label(nv, corner, anchor, xVal, y, g_rowVal[i], g_rowClr[i], fs, i == 0 ? "Arial Bold" : "Arial");
}
}
//+------------------------------------------------------------------+
//| Support / resistance levels |
//+------------------------------------------------------------------+
void AddLevel(double &lp[], string &ln[], bool &lf[], int &ls[], bool &lh[], int &cnt,
const double p, const string name, const bool flat, const int str, const bool htf)
{
ArrayResize(lp, cnt + 1); ArrayResize(ln, cnt + 1); ArrayResize(lf, cnt + 1); ArrayResize(ls, cnt + 1); ArrayResize(lh, cnt + 1);
lp[cnt] = p; ln[cnt] = name; lf[cnt] = flat; ls[cnt] = str; lh[cnt] = htf;
cnt++;
}
void DrawLevel(const string tag, const datetime t1, const datetime t2, const double lo, const double hi,
const bool zone, const color clr, const bool flat, const bool htf, const string text)
{
string nl = PFX + "SRL" + tag, nt = PFX + "SRT" + tag;
if(zone)
{
string nz = PFX + "SRZ" + tag;
ObjectCreate(0, nz, OBJ_RECTANGLE, 0, t1, hi, t2, lo);
ObjectSetInteger(0, nz, OBJPROP_COLOR, clr);
ObjectSetInteger(0, nz, OBJPROP_FILL, false);
ObjectSetInteger(0, nz, OBJPROP_WIDTH, flat ? 2 : 1);
ObjectSetInteger(0, nz, OBJPROP_STYLE, htf ? STYLE_DOT : (flat ? STYLE_SOLID : STYLE_DASH));
ObjectSetInteger(0, nz, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nz, OBJPROP_HIDDEN, true);
}
else
{
ObjectCreate(0, nl, OBJ_TREND, 0, t1, lo, t2, lo);
ObjectSetInteger(0, nl, OBJPROP_COLOR, clr);
ObjectSetInteger(0, nl, OBJPROP_WIDTH, flat ? 2 : 1);
ObjectSetInteger(0, nl, OBJPROP_STYLE, htf ? STYLE_DOT : (flat ? STYLE_SOLID : STYLE_DASH));
ObjectSetInteger(0, nl, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, nl, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nl, OBJPROP_HIDDEN, true);
}
ObjectCreate(0, nt, OBJ_TEXT, 0, t2, (lo + hi) / 2.0);
ObjectSetString(0, nt, OBJPROP_TEXT, " " + text);
ObjectSetInteger(0, nt, OBJPROP_COLOR, clr);
ObjectSetInteger(0, nt, OBJPROP_FONTSIZE, InpPanelFont);
ObjectSetInteger(0, nt, OBJPROP_ANCHOR, ANCHOR_LEFT);
ObjectSetInteger(0, nt, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nt, OBJPROP_HIDDEN, true);
}
void PaintLevels(const int n, const datetime &time[], const double &close[], const double kijunNow, const bool kijunFlat,
const double kumoTop, const double kumoBot, const bool topFlat, const bool botFlat, const bool kumoOk,
const bool senkouBFlat, const int jc)
{
ObjectsDeleteAll(0, PFX + "SR");
if(!InpShowSR || !kumoOk) return;
const int L = n - 1;
double atr = cAtr[L];
if(atr <= 0) return;
double clThr = atr * 0.25;
double mergeThr = InpSrCluster ? clThr : g_tick;
double lp[]; string ln[]; bool lf[]; int ls[]; bool lh[];
int cnt = 0;
AddLevel(lp, ln, lf, ls, lh, cnt, kijunNow, "Kijun", kijunFlat, kijunFlat ? 4 : 3, false);
AddLevel(lp, ln, lf, ls, lh, cnt, kumoBot, "Kumo bottom", botFlat, botFlat ? 4 : 2, false);
if(kumoTop - kumoBot > clThr) AddLevel(lp, ln, lf, ls, lh, cnt, kumoTop, "Kumo top", topFlat, topFlat ? 4 : 2, false);
// historical flat Senkou B and flat Kijun: lasting S/R magnets
for(int ni = 1; ni <= 20; ni++)
{
int idx = L - InpDisp - ni * 5;
if(idx >= InpFlatBars && cB[idx] != EMPTY_VALUE && IsFlat(cB, idx, InpFlatBars))
AddLevel(lp, ln, lf, ls, lh, cnt, cB[idx], "Old Kumo B", true, 4, false);
}
for(int ni = 1; ni <= 12; ni++)
{
int idx = L - ni * 5;
if(idx >= InpFlatBars && cK[idx] != EMPTY_VALUE && IsFlat(cK, idx, InpFlatBars) && MathAbs(cK[idx] - kijunNow) > clThr)
AddLevel(lp, ln, lf, ls, lh, cnt, cK[idx], "Old Kijun", true, 4, false);
}
// higher timeframe levels
if(g_htfReady && jc - InpDisp > InpFlatBars)
{
int hi0 = jc - InpDisp;
if(hA[hi0] != EMPTY_VALUE && hB[hi0] != EMPTY_VALUE)
{
double mTop = MathMax(hA[hi0], hB[hi0]), mBot = MathMin(hA[hi0], hB[hi0]);
bool mTopFlat = IsFlatEdge(hA, hB, hi0, InpFlatBars, true);
bool mBotFlat = IsFlatEdge(hA, hB, hi0, InpFlatBars, false);
AddLevel(lp, ln, lf, ls, lh, cnt, mBot, "HTF Kumo bottom", mBotFlat, mBotFlat ? 4 : 2, true);
if(mTop - mBot > clThr) AddLevel(lp, ln, lf, ls, lh, cnt, mTop, "HTF Kumo top", mTopFlat, mTopFlat ? 4 : 2, true);
}
for(int ni = 1; ni <= 20; ni++)
{
int hidx = hi0 - ni * 5;
if(hidx < InpFlatBars) break;
if(hB[hidx] != EMPTY_VALUE && IsFlat(hB, hidx, InpFlatBars))
AddLevel(lp, ln, lf, ls, lh, cnt, hB[hidx], "Old HTF Kumo B", true, 4, true);
}
}
if(cnt == 0) return;
// sort ascending by price (insertion sort on indices)
int idxs[];
ArrayResize(idxs, cnt);
for(int i = 0; i < cnt; i++) idxs[i] = i;
for(int i = 1; i < cnt; i++)
{
int key = idxs[i];
int j = i - 1;
while(j >= 0 && lp[idxs[j]] > lp[key])
{
idxs[j + 1] = idxs[j];
j--;
}
idxs[j + 1] = key;
}
// cluster
double cMin[], cMax[]; string cNm[]; bool cFl[], cH[]; int cCt[];
int nCl = 0;
bool used[];
ArrayResize(used, cnt);
for(int i = 0; i < cnt; i++) used[i] = false;
for(int i = 0; i < cnt; i++)
{
int oi = idxs[i];
if(used[oi]) continue;
double pLo = lp[oi], pHi = lp[oi];
string nm = ln[oi];
bool fl = lf[oi], hh = lh[oi];
int ct = 1;
used[oi] = true;
for(int j = i + 1; j < cnt; j++)
{
int oj = idxs[j];
if(lp[oj] - pHi > mergeThr) break;
if(used[oj]) continue;
pHi = lp[oj];
if(StringFind(nm, ln[oj]) < 0) nm = nm + " + " + ln[oj];
if(lf[oj]) fl = true;
if(!lh[oj]) hh = false;
ct++;
used[oj] = true;
}
ArrayResize(cMin, nCl + 1); ArrayResize(cMax, nCl + 1); ArrayResize(cNm, nCl + 1);
ArrayResize(cFl, nCl + 1); ArrayResize(cH, nCl + 1); ArrayResize(cCt, nCl + 1);
cMin[nCl] = pLo; cMax[nCl] = pHi; cNm[nCl] = nm; cFl[nCl] = fl; cH[nCl] = hh; cCt[nCl] = ct;
nCl++;
}
int nS = 0;
for(int k = 0; k < nCl; k++)
if((cMin[k] + cMax[k]) / 2.0 < close[L]) nS++;
int sCur = nS, rCur = 1;
datetime t1 = time[L];
datetime t2 = time[L] + (datetime)(InpSrExtend * PeriodSeconds(_Period));
for(int k = 0; k < nCl; k++)
{
double mid = (cMin[k] + cMax[k]) / 2.0;
bool isR = mid >= close[L];
int rank = isR ? rCur : sCur;
if(isR) rCur++; else sCur--;
if(rank > InpSrMax) continue;
bool zone = cCt[k] > 1 && cMax[k] > cMin[k] && InpSrCluster;
string rng = (cCt[k] > 1 && cMax[k] > cMin[k]) ? Px(cMin[k]) + " - " + Px(cMax[k]) : Px(cMin[k]);
string txt = (isR ? "R" : "S") + IntegerToString(rank) + (cH[k] ? "[H]" : "") + " " + rng + " " + cNm[k];
DrawLevel(IntegerToString(k), t1, t2, cMin[k], cMax[k], zone, isR ? InpResistanceColor : InpSupportColor, cFl[k], cH[k], txt);
}
}
//+------------------------------------------------------------------+
//| Analysis of the last bar: status panel rows + S/R |
//+------------------------------------------------------------------+
void Analyze(const int n, const datetime &time[], const double &high[], const double &low[], const double &close[])
{
const int L = n - 1;
const int c0 = L - InpDisp;
g_rows = 0;
color tBull = C'8,153,129', tBear = C'242,54,69', tWarn = C'255,152,0', tBase = clrWhite;
double atr = cAtr[L];
double tenkan = cT[L], kijun = cK[L];
bool kumoOk = (c0 >= 0 && cA[c0] != EMPTY_VALUE && cB[c0] != EMPTY_VALUE && tenkan != EMPTY_VALUE && kijun != EMPTY_VALUE && atr > 0);
double kumoTop = 0, kumoBot = 0;
bool kumoBull = false, above = false, below = false, inside = false;
if(kumoOk)
{
kumoTop = MathMax(cA[c0], cB[c0]);
kumoBot = MathMin(cA[c0], cB[c0]);
kumoBull = cA[c0] > cB[c0];
above = close[L] > kumoTop;
below = close[L] < kumoBot;
inside = !above && !below;
}
Row("Crypex", _Symbol, clrWhite);
if(!kumoOk)
{
Row("Status", "Warm-up (not enough bars)", tBase);
if(InpShowPanel) PaintPanel();
PaintLevels(n, time, close, 0, false, 0, 0, false, false, false, false, 0);
return;
}
bool tkBull = tenkan > kijun, tkBear = tenkan < kijun;
bool futBull = cA[L] > cB[L], futBear = cA[L] < cB[L];
bool kijunFlat = IsFlat(cK, L, InpFlatBars);
double slope5 = (L >= 5 && cK[L - 5] != EMPTY_VALUE) ? kijun - cK[L - 5] : 0;
bool kijunRise = !kijunFlat && slope5 > 0, kijunFall = !kijunFlat && slope5 < 0;
bool senkouBFlat = IsFlat(cB, c0, InpFlatBars);
bool aFlatNow = IsFlat(cA, c0, InpFlatBars);
double sbLag = cB[c0], sbLag5 = (c0 >= 5) ? cB[c0 - 5] : EMPTY_VALUE;
double tenkanDist = MathAbs(close[L] - tenkan) / atr;
double chHi = (L - InpDisp >= 0) ? high[L - InpDisp] : EMPTY_VALUE;
double chLo = (L - InpDisp >= 0) ? low[L - InpDisp] : EMPTY_VALUE;
double chScore = (chHi == EMPTY_VALUE) ? 0.0 : (close[L] > chHi ? 1.0 : (close[L] < chLo ? -1.0 : 0.0));
double score = (above ? 2.0 : (below ? -2.0 : 0.0)) + (tkBull ? 1.0 : (tkBear ? -1.0 : 0.0))
+ (close[L] > kijun ? 1.0 : (close[L] < kijun ? -1.0 : 0.0)) + (futBull ? 1.0 : (futBear ? -1.0 : 0.0))
+ chScore + (kijunRise ? 0.5 : (kijunFall ? -0.5 : 0.0));
string bias = score >= 5.5 ? "Strong Bullish" : score >= 2.0 ? "Bullish Bias" : score <= -5.5 ? "Strong Bearish" : score <= -2.0 ? "Bearish Bias" : "Neutral / Range";
Row("Tenkan / Kijun", Px(tenkan) + " / " + Px(kijun), tBase);
Row("Regime", above ? "Above Kumo" : (below ? "Below Kumo" : "Inside Kumo"), above ? tBull : (below ? tBear : tWarn));
Row("Cloud (now)", kumoBull ? "Green (A>B)" : "Red (A<B)", kumoBull ? tBull : tBear);
Row("Future cloud", futBull ? "Bullish (A>B)" : (futBear ? "Bearish (A<B)" : "Flat / twisting"), futBull ? tBull : (futBear ? tBear : tBase));
string align = (kumoBull && above && tkBull) ? "Strong bull stack" : (!kumoBull && below && tkBear) ? "Strong bear stack" :
(above && !tkBull) ? "Pullback risk (above cloud)" : (below && !tkBear) ? "Bounce risk (below cloud)" : "Mixed / transit";
Row("Alignment", align, (above && tkBull) ? tBull : ((below && tkBear) ? tBear : tWarn));
Row("TK momentum", tkBull ? "Tenkan > Kijun" : (tkBear ? "Tenkan < Kijun" : "Flat"), tkBull ? tBull : (tkBear ? tBear : tBase));
Row("Kijun slope", kijunFlat ? "Flat · equilibrium magnet" : (kijunRise ? "Rising" : (kijunFall ? "Falling" : "Sideways")), kijunFlat ? tWarn : (kijunRise ? tBull : (kijunFall ? tBear : tBase)));
string sbTxt = senkouBFlat ? "Flat S/R @ " + Px(sbLag) : ((sbLag5 != EMPTY_VALUE && sbLag > sbLag5) ? "Rising" : ((sbLag5 != EMPTY_VALUE && sbLag < sbLag5) ? "Falling" : "Sideways"));
Row("Senkou B", sbTxt, senkouBFlat ? tWarn : tBase);
Row("Chikou", chHi == EMPTY_VALUE ? "Warm-up" : (chScore > 0 ? "Clear bullish" : (chScore < 0 ? "Clear bearish" : "Trapped (neutral)")), chScore > 0 ? tBull : (chScore < 0 ? tBear : tWarn));
Row("Tenkan distance", DoubleToString(tenkanDist, 2) + " ATR" + (tenkanDist > 1.0 ? " ! overext" : (tenkanDist < 0.3 ? " · near" : "")), tenkanDist > 1.0 ? tWarn : tBase);
double thick = kumoTop - kumoBot;
double thickAtr = thick / atr;
Row("Kumo thickness", Px(thick) + (thickAtr > 1.5 ? " · Thick" : (thickAtr < 0.5 ? " · Thin" : " · Normal")), tBase);
double dTop = kumoTop - close[L], dBot = close[L] - kumoBot;
Row("Distance (edge)", "up " + DoubleToString(dTop / g_tick, 0) + " t / dn " + DoubleToString(dBot / g_tick, 0) + " t", tBase);
double nearest = MathMin(MathAbs(dTop), MathAbs(dBot));
Row("Nearest · ATR", Px(nearest) + " · " + DoubleToString(nearest / atr, 2) + " ATR", tBase);
Row("ATR(14)", Px(atr), tBase);
Row("Score · Bias", DoubleToString(score, 1) + " -> " + bias, score >= 2.0 ? tBull : (score <= -2.0 ? tBear : tWarn));
int jc = -1;
if(g_htfReady && ArraySize(jMap) == n) jc = jMap[L];
if(g_htfReady && jc - InpDisp >= 0 && hA[jc - InpDisp] != EMPTY_VALUE && hB[jc - InpDisp] != EMPTY_VALUE)
{
double mTop = MathMax(hA[jc - InpDisp], hB[jc - InpDisp]), mBot = MathMin(hA[jc - InpDisp], hB[jc - InpDisp]);
bool mBull = hA[jc - InpDisp] > hB[jc - InpDisp];
string tfName = EnumToString((ENUM_TIMEFRAMES)g_htf);
StringReplace(tfName, "PERIOD_", "");
Row("HTF Kumo", (close[L] > mTop ? "Above Kumo (" : (close[L] < mBot ? "Below Kumo (" : "In Kumo (")) + tfName + ")", close[L] > mTop ? tBull : (close[L] < mBot ? tBear : tWarn));
Row("HTF cloud", mBull ? "HTF green" : "HTF red", mBull ? tBull : tBear);
}
else
{
Row("HTF Kumo", g_htfOn ? "HTF warm-up" : "-", tBase);
}
Row("Shift · Params", IntegerToString(InpDisp) + " ch" + (g_htfOn ? " · " + IntegerToString(g_fwd) + " HTF" : "") + " | " + IntegerToString(InpTenkan) + "/" + IntegerToString(InpKijun) + "/" + IntegerToString(InpSenkouB), tBase);
if(InpShowPanel) PaintPanel();
else ObjectsDeleteAll(0, PFX + "P");
bool botIsB = cB[c0] <= cA[c0];
bool botFlat = botIsB ? senkouBFlat : aFlatNow;
bool topFlat = botIsB ? aFlatNow : senkouBFlat;
PaintLevels(n, time, close, kijun, kijunFlat, kumoTop, kumoBot, topFlat, botFlat, true, senkouBFlat, jc);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Kumo fill (rectangles) and breakout arrows, as chart objects |
//+------------------------------------------------------------------+
datetime TimeAt(const int k, const int n)
{
if(k < 0) return cTm[0];
if(k <= n - 1) return cTm[k];
return cTm[n - 1] + (datetime)((k - (n - 1)) * PeriodSeconds(_Period));
}
void Rect(const string name, const datetime t1, const datetime t2, const double top, const double bottom, const color clr)
{
ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, top, t2, bottom);
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
ObjectSetInteger(0, name, OBJPROP_FILL, true);
ObjectSetInteger(0, name, OBJPROP_BACK, true);
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
}
void PaintCloud(const int n)
{
ObjectsDeleteAll(0, PFX + "K");
int first = MathMax(0, n - InpCloudBars);
if(InpFillKumo)
{
// chart-TF Kumo: one rectangle per bar (cloud at displayed bar i is made from bar i - disp)
for(int i = MathMax(first, InpDisp); i <= n - 1 + InpDisp; i++)
{
int r = i - InpDisp;
if(r < 0 || r > n - 1 || cA[r] == EMPTY_VALUE || cB[r] == EMPTY_VALUE) continue;
Rect(PFX + "KC" + IntegerToString(i), TimeAt(i, n), TimeAt(i + 1, n), MathMax(cA[r], cB[r]), MathMin(cA[r], cB[r]), cA[r] > cB[r] ? InpBullKumo : InpBearKumo);
}
// higher-timeframe Kumo: one rectangle per HTF step, projected g_fwd bars ahead
if(g_htfOn && g_htfReady && ArraySize(cHtfA) == n)
{
int runStart = -1;
for(int i = first; i <= n; i++)
{
bool ok = (i < n) && cHtfA[i] != EMPTY_VALUE && cHtfB[i] != EMPTY_VALUE;
bool same = ok && runStart >= 0 && cHtfA[i] == cHtfA[runStart] && cHtfB[i] == cHtfB[runStart];
if(runStart >= 0 && !same)
{
Rect(PFX + "KH" + IntegerToString(runStart), TimeAt(runStart + g_fwd, n), TimeAt(i + g_fwd, n),
MathMax(cHtfA[runStart], cHtfB[runStart]), MathMin(cHtfA[runStart], cHtfB[runStart]),
cHtfA[runStart] > cHtfB[runStart] ? InpHtfBullKumo : InpHtfBearKumo);
runStart = -1;
}
if(ok && runStart < 0) runStart = i;
}
}
}
if(InpMarkKumoBo)
for(int i = first; i < n; i++)
{
if(cKbUp[i] != EMPTY_VALUE)
{
string a = PFX + "KBU" + IntegerToString(i);
ObjectCreate(0, a, OBJ_ARROW, 0, cTm[i], cKbUp[i]);
ObjectSetInteger(0, a, OBJPROP_ARROWCODE, 159);
ObjectSetInteger(0, a, OBJPROP_COLOR, InpTenkanColor);
ObjectSetInteger(0, a, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, a, OBJPROP_HIDDEN, true);
}
if(cKbDn[i] != EMPTY_VALUE)
{
string a = PFX + "KBD" + IntegerToString(i);
ObjectCreate(0, a, OBJ_ARROW, 0, cTm[i], cKbDn[i]);
ObjectSetInteger(0, a, OBJPROP_ARROWCODE, 159);
ObjectSetInteger(0, a, OBJPROP_COLOR, InpKijunColor);
ObjectSetInteger(0, a, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, a, OBJPROP_HIDDEN, true);
}
}
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[],
const double &open[], const double &high[], const double &low[], const double &close[],
const long &tick_volume[], const long &volume[], const int &spread[])
{
int total = MathMin(rates_total, InpMaxBars);
int need = MathMax(InpSenkouB, MathMax(InpKijun, InpTenkan)) + InpDisp + 5;
if(total < need) return 0;
uint now = GetTickCount();
bool newBar = (Time[0] != g_lastBar) || prev_calculated == 0;
if(!newBar && now - g_lastPaint < 1500) return rates_total;
N = total;
ArrayResize(cHi, N); ArrayResize(cLo, N); ArrayResize(cCl, N); ArrayResize(cTm, N);
for(int i = 0; i < N; i++)
{
int s = N - 1 - i;
cHi[i] = High[s];
cLo[i] = Low[s];
cCl[i] = Close[s];
cTm[i] = Time[s];
}
ComputeChart(N, cHi, cLo, cCl);
if(newBar)
{
g_lastBar = Time[0];
if(g_htfOn)
{
BuildHtf(N, cTm);
g_lastHtfTry = now;
}
CheckAlerts(N, cTm, cCl);
}
else if(g_htfOn && !g_htfReady && now - g_lastHtfTry > 5000)
{
BuildHtf(N, cTm); // HTF history was not loaded yet: retry every 5 s
g_lastHtfTry = now;
}
PushBuffers(N, prev_calculated == 0);
if(newBar) PaintCloud(N);
g_lastPaint = now;
Analyze(N, cTm, cHi, cLo, cCl);
PaintWatermark();
ChartRedraw();
g_firstDone = true;
return rates_total;
}
//+------------------------------------------------------------------+
منطق کار
- خطوط و ابر طبق قرارداد TradingView: تنکان و کیجون (میانگین سقف و کف دونچین)، سنکو A و B بهاندازهی ۲۵ کندل جلوتر و چیکو اسپن ۲۵ کندل عقبتر.
- ابر تایمفریم بالاتر: یک تایمفریم بالاتر (بهصورت خودکار یا دستی) با همان پارامترها روی چارت میآید. فقط کندلهای تأییدشده خوانده میشود، پس روی کندل زنده تغییر نمیکند و ریپینت ندارد.
- حمایت و مقاومت: کیجون، لبههای ابر و سنکو B تخت (flat) گذشته با ستارهی قدرت رتبهبندی میشوند. سطوح نزدیک به هم (کمتر از ۰٫۲۵ ATR) به یک ناحیه ادغام میشوند و نزدیکترینها S1 و R1 نام میگیرند. سطوح تایمفریم بالاتر با برچسب [H] مشخصاند.
- امتیاز ترکیبی (حداکثر ±۶٫۵): جایگاه قیمت نسبت به ابر (±۲)، ترتیب تنکان و کیجون (±۱)، قیمت نسبت به کیجون (±۱)، رنگ ابر آینده (±۱)، چیکو اسپن (±۱) و شیب کیجون (±۰٫۵). امتیاز ۲ به بالا «تمایل صعودی» و ۵٫۵ به بالا «صعودی قوی» است (برای نزولی برعکس).
- نشانگرها (اختیاری): تقاطع تنکان و کیجون، تقاطع قیمت با کیجون یا تنکان، تویست ابر و شکست ابر. آلارم برای تقاطع TK، شکست ابر و تویست ابر آماده است.
- جدول وضعیت: جایگاه نسبت به ابر، رنگ ابر حال و آینده، شیب کیجون، وضعیت سنکو B، چیکو، ضخامت و فاصلهی ابر بر حسب تیک و ATR، و وضعیت ابر تایمفریم بالاتر.
- نسخهی متاتریدر ۵ و ۴: همهی بخشهای اصلی (ابر تایمفریم بالاتر، سطوح S/R، پنل وضعیت، نشانگرها، آلارم و واترمارک) را دارد. تفاوتها: ابر در MT5 با رسم پرشدهی خود پلتفرم و در MT4 با آبجکتهای مستطیلی (بهطور پیشفرض ۳۰۰ کندل آخر) رسم میشود؛ حالتهای رنگآمیزی کندل و پسزمینهی TradingView را ندارد؛ جابهجایی ابر طبق قرارداد کلاسیک متاتریدر ۲۶ کندل جلوتر است (در TradingView ۲۵).
تنظیمات مهم
- Tenkan / Kijun / Senkou B
- دورههای ایچیموکو؛ پیشفرض ۹ / ۲۶ / ۵۲
- Displacement
- جابهجایی ابر و چیکو؛ پیشفرض ۲۶ (طبق قرارداد TradingView ابر ۲۵ کندل جلو میرود)
- Show HTF Kumo / Auto next TF
- نمایش ابر تایمفریم بالاتر و انتخاب خودکار تایمفریم (۱ ساعته به ۴ ساعته، ۴ ساعته به روزانه، روزانه به هفتگی)
- Market calendar
- کریپتو ۲۴/۷ است و فارکس، طلا و سهام فقط ۵ روز در هفته. این گزینه موقعیت ابر هفتگی و ماهانه را درست جلو میبرد (پیشفرض: Auto)
- Min flat bars
- حداقل کندلهای متوالی برای اینکه کیجون یا سنکو B «تخت» حساب شود
- S/R levels / Cluster zones / Max levels
- نمایش سطوح، ادغام سطوح نزدیک و حداکثر تعداد سطح در هر سمت
- Style
- سه حالت ظاهر: Crypex، Colored و Monochrome
- Show crypex.org watermark
- واترمارک قابل خاموشکردن
- MT4/MT5: Kumo fill, Bars of Kumo fill
- (فقط MT4) پر کردن ابر و تعداد کندلهایی که ابر برایشان رسم میشود؛ کمتر یعنی چارت سبکتر
- MT4/MT5: Alerts
- هشدار پاپآپ، پوش و ایمیل برای تقاطع TK، شکست و تویست ابر
کجا بهتر کار میکند
چارتهای ۱ ساعته تا روزانه روی طلا، جفتارزهای اصلی و ارزهای دیجیتال پرنقدینگی؛ برای همراستایی تایمفریم (مثلاً ورود در ۱ ساعته در جهت ابر ۴ ساعته). ابر هفتگی و ماهانه روی کریپتو دقیق است و روی فارکس و طلا با تنظیم تقویم بازار تقریباً درست جلو میرود.
نقطهضعف
این یک اندیکاتور تحلیلی است، نه سیگنال معاملاتی؛ امتیاز و برچسبها فقط وضعیت ایچیموکو را خلاصه میکنند. در بازار رنج، تقاطعها و تویستها زیاد و کاذب میشوند. روی سهام با جلسهی کوتاه (غیر ۲۴ ساعته) برای تایمفریمهای درونروزی باید «تعداد کندل هر کندل HTF» را دستی وارد کنید. کد متاتریدر در MetaEditor کامپایل نشده و فقط با شبیهساز اجرا بررسی شده است؛ قبل از استفاده آن را کامپایل کنید و روی حساب دمو و چند تایمفریم تست کنید.
این کد آموزشی است و توصیهی سرمایهگذاری نیست. قبل از استفاده روی حساب واقعی، کامپایل و رفتار آن را روی حساب دمو و در بکتست بررسی کنید؛ نتیجهی گذشته تضمینکنندهی آینده نیست.
← همهی اسکریپتها