ea-quant/pine/ema_ribbon_state_signal_indicator.pine
2026-05-24 22:54:53 +08:00

958 lines
68 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//@version=6
indicator(
title="EMA Ribbon State Signal",
shorttitle="EMA Ribbon State",
overlay=true,
max_labels_count=500,
max_boxes_count=500)
plot(na, title="脚本锚点", display=display.none)
emaFastLen = input.int(5, "EMA 快线", minval=1)
emaMidLen = input.int(15, "EMA 中线", minval=1)
emaSlowLen = input.int(30, "EMA 慢线", minval=1)
emaBiasLen = input.int(144, "EMA 多空分界线", minval=1)
atrLen = input.int(14, "ATR 长度", minval=1)
signalProfile = input.string("均衡", "信号风格", options=["激进", "均衡", "保守"])
mainSignalMode = input.string("只做趋势回踩", "主信号模式", options=["只做趋势回踩", "启动+趋势回踩"])
cooldownBars = input.int(10, "同向信号冷却 K 数", minval=0, maxval=100)
globalCooldownBars = input.int(6, "任意信号冷却 K 数", minval=0, maxval=100)
useLateTrendFilter = input.bool(true, "启用趋势末段风险过滤")
maxMainSignalsPerTrend = input.int(6, "单段趋势最多主信号次数", minval=0, maxval=20)
maxTrendBars = input.int(0, "趋势最长有效 K 数", minval=0, maxval=300)
useAdxFilter = input.bool(true, "启用 ADX 趋势过滤")
adxLen = input.int(14, "ADX 长度", minval=1)
adxSmoothing = input.int(14, "ADX 平滑", minval=1)
minAdx = input.float(18.0, "ADX 最小值", minval=0.0, step=0.5)
allowRisingAdxLaunch = input.bool(true, "启动允许 ADX 上升替代阈值")
requireDiDirection = input.bool(true, "要求 DI 方向一致")
useBiasSide = input.bool(true, "要求价格在 EMA144 同侧")
requireRibbonBiasSide = input.bool(true, "要求均线带在 EMA144 同侧")
allowRibbonCrossLaunch = input.bool(true, "启动允许均线带刚穿越 EMA144")
useBiasSlope = input.bool(false, "要求 EMA144 同向斜率")
biasSlopeLookback = input.int(10, "EMA144 斜率回看 K 数", minval=1, maxval=100)
avoidOppositeHardTrend = input.bool(true, "禁止逆 144 + 均线排列")
avoidChopZone = input.bool(true, "避开价格均线缠绕区")
chopLookback = input.int(10, "缠绕检测回看 K 数", minval=3, maxval=80)
chopMaxCrosses = input.int(4, "缠绕最大允许穿均线次数", minval=0, maxval=30)
chopRibbonAtr = input.float(0.35, "缠绕均线带过窄 / ATR", minval=0.0, step=0.05)
chopBiasDistanceAtr = input.float(0.45, "缠绕离 EMA144 过近 / ATR", minval=0.0, step=0.05)
chopBiasBandAtr = input.float(0.80, "144 横盘带宽 / ATR", minval=0.0, step=0.05)
chopExitConfirmBars = input.int(0, "脱离缠绕后确认 K 数", minval=0, maxval=20)
chopRequireCleanClose = input.bool(true, "信号 K 必须收在均线带外")
compressionLookback = input.int(18, "启动前压缩回看 K 数", minval=3, maxval=120)
compressionAtr = input.float(0.55, "启动前最大均线带宽 / ATR", minval=0.0, step=0.05)
launchBreakLookback = input.int(4, "启动突破结构回看 K 数", minval=2, maxval=80)
launchFreshBars = input.int(8, "启动后有效 K 数", minval=1, maxval=50)
minLaunchBodyAtr = input.float(0.08, "启动确认实体 / ATR", minval=0.0, step=0.01)
launchEscapeDistanceAtr = input.float(0.12, "启动脱离均线带距离 / ATR", minval=0.0, step=0.01)
launchMinRibbonAtr = input.float(0.20, "启动后最小均线带宽 / ATR", minval=0.0, step=0.01)
launchChopBodyAtr = input.float(0.18, "缠绕逃逸实体 / ATR", minval=0.0, step=0.01)
launchBiasEscapeAtr = input.float(0.12, "缠绕逃逸离 EMA144 / ATR", minval=0.0, step=0.01)
prelaunchMode = input.string("辅助标记", "预启动信号模式", options=["关闭", "辅助标记", "并入主信号"])
prelaunchCooldownBars = input.int(12, "预启动信号冷却 K 数", minval=0, maxval=100)
prelaunchMaxRibbonAtr = input.float(0.75, "预启动最大均线带宽 / ATR", minval=0.0, step=0.05)
prelaunchBreakBufferAtr = input.float(0.18, "预启动贴近突破位 / ATR", minval=0.0, step=0.01)
prelaunchBiasNearAtr = input.float(0.35, "预启动允许贴近 EMA144 / ATR", minval=0.0, step=0.05)
prelaunchMinBodyAtr = input.float(0.04, "预启动最小实体 / ATR", minval=0.0, step=0.01)
prelaunchMaxBiasCrosses = input.int(1, "预启动最大 EMA144 反复穿越", minval=0, maxval=10)
prelaunchMaxRibbonCrosses = input.int(3, "预启动最大短均线反复穿越", minval=0, maxval=30)
prelaunchMinBreakCloseAtr = input.float(0.04, "预启动离突破位最小差距 / ATR", minval=0.0, step=0.01)
pullbackLookback = input.int(5, "中继回踩有效 K 数", minval=1, maxval=30)
trendMatureBars = input.int(4, "趋势形成确认 K 数", minval=0, maxval=50)
trendAwayAtr = input.float(0.25, "趋势离 EMA144 最小距离 / ATR", minval=0.0, step=0.05)
minTrendRibbonAtr = input.float(0.35, "趋势均线带最小发散 / ATR", minval=0.0, step=0.05)
minTrendGapAtr = input.float(0.04, "EMA 间最小间距 / ATR", minval=0.0, step=0.01)
trendExpansionLookback = input.int(8, "趋势发散确认回看 K 数", minval=1, maxval=50)
requireRecentBiasBreakout = input.bool(false, "要求近期突破 EMA144")
biasBreakoutRetestBars = input.int(40, "突破 EMA144 后回踩有效 K 数", minval=1, maxval=200)
useBreakoutLevelRetest = input.bool(false, "要求回踩突破结构位")
breakoutLevelLookback = input.int(24, "突破结构位回看 K 数", minval=3, maxval=200)
breakoutLevelRetestAtr = input.float(0.60, "突破结构位回踩容差 / ATR", minval=0.0, step=0.05)
minBreakoutBodyAtr = input.float(0.20, "突破 144 最小实体 / ATR", minval=0.0, step=0.05)
useBiasRetestPullback = input.bool(true, "回踩/反抽允许测试 EMA144")
biasRetestAtr = input.float(0.30, "EMA144 回踩/反抽容差 / ATR", minval=0.0, step=0.05)
minContinuationBodyAtr = input.float(0.05, "中继确认实体 / ATR", minval=0.0, step=0.01)
maxContinuationDistanceAtr = input.float(1.30, "中继确认离 EMA5 最远 / ATR", minval=0.1, step=0.05)
maxContinuationDistanceMidAtr = input.float(0.75, "中继确认离 EMA15 最远 / ATR", minval=0.1, step=0.05)
entryRangeLookback = input.int(20, "入场位置回看 K 数", minval=5, maxval=120)
maxLongEntryRangePosition = input.float(0.68, "做多确认最高区间位置", minval=0.1, maxval=1.0, step=0.05)
minShortEntryRangePosition = input.float(0.32, "做空确认最低区间位置", minval=0.0, maxval=0.9, step=0.05)
extremeLookback = input.int(14, "避免局部高低点回看 K 数", minval=3, maxval=100)
maxLongRangePosition = input.float(0.82, "做多最高允许区间位置", minval=0.1, maxval=1.0, step=0.05)
minShortRangePosition = input.float(0.18, "做空最低允许区间位置", minval=0.0, maxval=0.9, step=0.05)
useStructureZoneFilter = input.bool(true, "启用供需区空间过滤")
showStructureZones = input.bool(true, "显示供需区 Box")
showHtfStructureZones = input.bool(true, "显示高周期供需区 Box")
useHtfStructureFilter = input.bool(true, "高周期供需区参与信号过滤")
htfStructureTf1 = input.timeframe("60", "高周期供需区一")
htfStructureTf2 = input.timeframe("240", "高周期供需区二")
useHtfZoneHardBlock = input.bool(true, "高周期供需区硬拦截")
htfZoneBlockAtr = input.float(1.20, "高周期供需区禁入缓冲 / ATR", minval=0.0, step=0.05)
htfZoneTouchBlockBars = input.int(8, "触碰高周期区后禁反向 K 数", minval=0, maxval=50)
useZoneReactionBlock = input.bool(true, "供需区反应拦截")
zoneReactionBlockBars = input.int(10, "供需区反应后禁反向 K 数", minval=0, maxval=80)
minZoneReactionBodyAtr = input.float(0.20, "供需区反应实体 / ATR", minval=0.0, step=0.05)
zoneImpulseAtr = input.float(2.20, "供需区前置冲击 / ATR", minval=0.0, step=0.10)
structureLookback = input.int(120, "供需区回看 K 数", minval=20, maxval=500)
structurePivotLeft = input.int(6, "供需区摆点左侧 K 数", minval=1, maxval=20)
structurePivotRight = input.int(6, "供需区摆点右侧 K 数", minval=1, maxval=20)
minStructureReactionAtr = input.float(1.20, "供需区最小反应 / ATR", minval=0.0, step=0.05)
structureZoneAtr = input.float(0.60, "供需区厚度 / ATR", minval=0.0, step=0.05)
minRoomToZoneAtr = input.float(0.80, "信号到供需区最小空间 / ATR", minval=0.0, step=0.05)
maxStructureBoxes = input.int(3, "每边最多显示供需区数量", minval=1, maxval=20)
maxHtfStructureBoxes = input.int(2, "高周期每边最多显示数量", minval=1, maxval=10)
maxZoneTests = input.int(3, "供需区弱化测试次数", minval=1, maxval=10)
useMtfTrendFilter = input.bool(true, "启用多周期趋势过滤")
mtfFilterTf1 = input.timeframe("15", "方向过滤周期一")
mtfFilterTf2 = input.timeframe("30", "方向过滤周期二")
mtfRiskTf = input.timeframe("60", "风险分级周期")
mtfStrongScore = input.int(2, "多周期强趋势分数", minval=1, maxval=4)
mtfRiskMode = input.string("只标记风险", "风险周期强反向处理", options=["只标记风险", "拦截强反向"])
showMtfRiskMarks = input.bool(true, "显示逆大周期风险标记")
showAuxMarks = input.bool(true, "显示信号来源小标记")
showStateTable = input.bool(true, "显示状态面板")
showBarColor = input.bool(false, "K 线按均线带状态染色")
emaFast = ta.ema(close, emaFastLen)
emaMid = ta.ema(close, emaMidLen)
emaSlow = ta.ema(close, emaSlowLen)
emaBias = ta.ema(close, emaBiasLen)
atr = ta.atr(atrLen)
[diPlus, diMinus, adx] = ta.dmi(adxLen, adxSmoothing)
body = math.abs(close - open)
ribbonWidth = math.abs(emaFast - emaSlow)
fastMidSpread = math.abs(emaFast - emaMid)
midSlowSpread = math.abs(emaMid - emaSlow)
ribbonWidthAtr = atr > syminfo.mintick ? ribbonWidth / atr : 0.0
ribbonHigh = math.max(math.max(emaFast, emaMid), emaSlow)
ribbonLow = math.min(math.min(emaFast, emaMid), emaSlow)
bullishEngulfing = close > open and close[1] < open[1] and close >= open[1] and open <= close[1]
bearishEngulfing = close < open and close[1] > open[1] and close <= open[1] and open >= close[1]
bullOrder = emaFast > emaMid and emaMid > emaSlow
bearOrder = emaFast < emaMid and emaMid < emaSlow
bullSlopeCount = (emaFast > emaFast[1] ? 1 : 0) + (emaMid > emaMid[1] ? 1 : 0) + (emaSlow > emaSlow[1] ? 1 : 0)
bearSlopeCount = (emaFast < emaFast[1] ? 1 : 0) + (emaMid < emaMid[1] ? 1 : 0) + (emaSlow < emaSlow[1] ? 1 : 0)
ribbonExpanding = ribbonWidth > ribbonWidth[1] and fastMidSpread >= fastMidSpread[1]
ribbonSeparated = ribbonWidthAtr >= minTrendRibbonAtr and fastMidSpread >= atr * minTrendGapAtr and midSlowSpread >= atr * minTrendGapAtr
ribbonExpansionNow = ribbonSeparated and ribbonWidth > ribbonWidth[1] and fastMidSpread >= fastMidSpread[1] and midSlowSpread >= midSlowSpread[1]
ribbonExpansionRecent = ta.highest(ribbonExpansionNow ? 1.0 : 0.0, trendExpansionLookback) > 0
bullRibbonExpanding = ribbonSeparated and ribbonExpansionRecent
bearRibbonExpanding = ribbonSeparated and ribbonExpansionRecent
profileSlopeNeed = signalProfile == "激进" ? 1 : signalProfile == "均衡" ? 2 : 3
profileBreakNeed = signalProfile != "激进"
biasBullOk = (not useBiasSide or close > emaBias) and (not requireRibbonBiasSide or ribbonLow > emaBias)
biasBearOk = (not useBiasSide or close < emaBias) and (not requireRibbonBiasSide or ribbonHigh < emaBias)
biasBullLaunchOk = (not useBiasSide or close > emaBias) and (not requireRibbonBiasSide or ribbonLow > emaBias or (allowRibbonCrossLaunch and close > emaBias + atr * launchBiasEscapeAtr and emaFast > emaBias and emaMid > emaBias))
biasBearLaunchOk = (not useBiasSide or close < emaBias) and (not requireRibbonBiasSide or ribbonHigh < emaBias or (allowRibbonCrossLaunch and close < emaBias - atr * launchBiasEscapeAtr and emaFast < emaBias and emaMid < emaBias))
biasSlopeBullOk = not useBiasSlope or emaBias >= emaBias[biasSlopeLookback]
biasSlopeBearOk = not useBiasSlope or emaBias <= emaBias[biasSlopeLookback]
adxStrong = adx >= minAdx
adxRising = adx > adx[1] and adx[1] > adx[2]
adxLaunchOk = not useAdxFilter or adxStrong or (allowRisingAdxLaunch and adxRising)
adxContinuationOk = not useAdxFilter or adxStrong
diBullOk = not requireDiDirection or diPlus > diMinus
diBearOk = not requireDiDirection or diMinus > diPlus
f_mtfTrendScore() =>
mtfFast = ta.ema(close, emaFastLen)
mtfMid = ta.ema(close, emaMidLen)
mtfSlow = ta.ema(close, emaSlowLen)
mtfBias = ta.ema(close, emaBiasLen)
priceScore = close > mtfBias ? 1 : close < mtfBias ? -1 : 0
orderScore = mtfFast > mtfMid and mtfMid > mtfSlow ? 1 : mtfFast < mtfMid and mtfMid < mtfSlow ? -1 : 0
fastSlopeScore = mtfFast > mtfFast[1] and mtfMid >= mtfMid[1] ? 1 : mtfFast < mtfFast[1] and mtfMid <= mtfMid[1] ? -1 : 0
biasSlopeScore = mtfBias >= mtfBias[biasSlopeLookback] ? 1 : mtfBias <= mtfBias[biasSlopeLookback] ? -1 : 0
priceScore + orderScore + fastSlopeScore + biasSlopeScore
mtfScore1 = request.security(syminfo.tickerid, mtfFilterTf1, f_mtfTrendScore(), barmerge.gaps_off, barmerge.lookahead_off)
mtfScore2 = request.security(syminfo.tickerid, mtfFilterTf2, f_mtfTrendScore(), barmerge.gaps_off, barmerge.lookahead_off)
mtfRiskScore = request.security(syminfo.tickerid, mtfRiskTf, f_mtfTrendScore(), barmerge.gaps_off, barmerge.lookahead_off)
mtfLongFilterOk = not useMtfTrendFilter or (mtfScore1 > -mtfStrongScore and mtfScore2 > -mtfStrongScore)
mtfShortFilterOk = not useMtfTrendFilter or (mtfScore1 < mtfStrongScore and mtfScore2 < mtfStrongScore)
mtfLongRisk = useMtfTrendFilter and mtfRiskScore <= -mtfStrongScore
mtfShortRisk = useMtfTrendFilter and mtfRiskScore >= mtfStrongScore
mtfRiskBlocks = mtfRiskMode == "拦截强反向"
mtfLongOk = mtfLongFilterOk and (not mtfRiskBlocks or not mtfLongRisk)
mtfShortOk = mtfShortFilterOk and (not mtfRiskBlocks or not mtfShortRisk)
hardBullContext = close > emaBias and bullOrder
hardBearContext = close < emaBias and bearOrder
longDirectionOk = not avoidOppositeHardTrend or not hardBearContext
shortDirectionOk = not avoidOppositeHardTrend or not hardBullContext
crossFastCount = math.sum(ta.cross(close, emaFast) ? 1.0 : 0.0, chopLookback)
crossMidCount = math.sum(ta.cross(close, emaMid) ? 1.0 : 0.0, chopLookback)
crossSlowCount = math.sum(ta.cross(close, emaSlow) ? 1.0 : 0.0, chopLookback)
priceCrossRibbonCount = crossFastCount + crossMidCount + crossSlowCount
priceInsideRibbon = close <= ribbonHigh and close >= ribbonLow
ribbonInsideBar = high >= ribbonHigh and low <= ribbonLow
nearBias = math.abs(close - emaBias) <= atr * chopBiasDistanceAtr
biasRangeOverlap = ta.highest(high, chopLookback) >= emaBias and ta.lowest(low, chopLookback) <= emaBias
biasBandRange = ta.highest(high, chopLookback) - ta.lowest(low, chopLookback)
biasSideFlips = math.sum(ta.cross(close, emaBias) ? 1.0 : 0.0, chopLookback)
ribbonStraddlesBias = ribbonLow <= emaBias and ribbonHigh >= emaBias
biasChopZone = biasRangeOverlap and biasBandRange <= atr * chopBiasBandAtr
narrowRibbon = ribbonWidthAtr <= chopRibbonAtr and not ribbonExpanding
chopZone = avoidChopZone and (priceCrossRibbonCount > chopMaxCrosses or priceInsideRibbon or ribbonInsideBar or nearBias or biasChopZone or biasSideFlips > 0 or ribbonStraddlesBias or narrowRibbon)
barsSinceChop = nz(ta.barssince(chopZone), 100000)
chopExitConfirmed = not avoidChopZone or barsSinceChop >= chopExitConfirmBars
longCleanClose = not chopRequireCleanClose or close > ribbonHigh
shortCleanClose = not chopRequireCleanClose or close < ribbonLow
launchBaseEscapeOk = not avoidChopZone or ((not priceInsideRibbon) and (not ribbonInsideBar) and (not narrowRibbon))
launchChopContext = avoidChopZone and (priceCrossRibbonCount > chopMaxCrosses or nearBias or biasChopZone or biasSideFlips > 0 or ribbonStraddlesBias or narrowRibbon)
longLaunchDistanceOk = close - ribbonHigh >= atr * launchEscapeDistanceAtr
shortLaunchDistanceOk = ribbonLow - close >= atr * launchEscapeDistanceAtr
launchRibbonOpenOk = ribbonWidthAtr >= launchMinRibbonAtr
longBiasEscapeOk = not launchChopContext or not useBiasSide or close > emaBias + atr * launchBiasEscapeAtr
shortBiasEscapeOk = not launchChopContext or not useBiasSide or close < emaBias - atr * launchBiasEscapeAtr
longLaunchEscapeOk = launchBaseEscapeOk and longLaunchDistanceOk and launchRibbonOpenOk and (not launchChopContext or body >= atr * launchChopBodyAtr) and longBiasEscapeOk
shortLaunchEscapeOk = launchBaseEscapeOk and shortLaunchDistanceOk and launchRibbonOpenOk and (not launchChopContext or body >= atr * launchChopBodyAtr) and shortBiasEscapeOk
continuationZoneOk = not chopZone
localRangeHigh = ta.highest(high, extremeLookback)
localRangeLow = ta.lowest(low, extremeLookback)
localRange = localRangeHigh - localRangeLow
rangePosition = localRange > syminfo.mintick ? (close - localRangeLow) / localRange : 0.5
longPositionOk = rangePosition <= maxLongRangePosition
shortPositionOk = rangePosition >= minShortRangePosition
entryRangeHigh = ta.highest(high, entryRangeLookback)
entryRangeLow = ta.lowest(low, entryRangeLookback)
entryRange = entryRangeHigh - entryRangeLow
entryRangePosition = entryRange > syminfo.mintick ? (close - entryRangeLow) / entryRange : 0.5
longEntryPositionOk = entryRangePosition <= maxLongEntryRangePosition
shortEntryPositionOk = entryRangePosition >= minShortEntryRangePosition
pivotDemand = ta.pivotlow(low, structurePivotLeft, structurePivotRight)
pivotSupply = ta.pivothigh(high, structurePivotLeft, structurePivotRight)
var float[] demandZoneBottoms = array.new_float()
var float[] demandZoneTops = array.new_float()
var int[] demandZoneBars = array.new_int()
var box[] demandZoneBoxes = array.new_box()
var label[] demandZoneLabels = array.new_label()
var int[] demandZoneTests = array.new_int()
var bool[] demandZoneWasInside = array.new_bool()
var float[] supplyZoneTops = array.new_float()
var float[] supplyZoneBottoms = array.new_float()
var int[] supplyZoneBars = array.new_int()
var box[] supplyZoneBoxes = array.new_box()
var label[] supplyZoneLabels = array.new_label()
var int[] supplyZoneTests = array.new_int()
var bool[] supplyZoneWasInside = array.new_bool()
chartTfText = timeframe.isminutes ? str.tostring(timeframe.multiplier) + "m" : timeframe.period
f_tfText(tf) =>
tf == "60" ? "1H" : tf == "240" ? "4H" : tf == "D" ? "1D" : tf
f_htfDemandBottom() =>
htfPivotDemand = ta.pivotlow(low, structurePivotLeft, structurePivotRight)
htfDemandValid = not na(htfPivotDemand) and high >= htfPivotDemand + ta.atr(atrLen)[structurePivotRight] * minStructureReactionAtr
htfDemandValid ? htfPivotDemand : na
f_htfDemandTop() =>
htfPivotDemand = ta.pivotlow(low, structurePivotLeft, structurePivotRight)
htfDemandValid = not na(htfPivotDemand) and high >= htfPivotDemand + ta.atr(atrLen)[structurePivotRight] * minStructureReactionAtr
htfDemandValid ? htfPivotDemand + ta.atr(atrLen)[structurePivotRight] * structureZoneAtr : na
f_htfDemandTime() =>
htfPivotDemand = ta.pivotlow(low, structurePivotLeft, structurePivotRight)
htfDemandValid = not na(htfPivotDemand) and high >= htfPivotDemand + ta.atr(atrLen)[structurePivotRight] * minStructureReactionAtr
htfDemandValid ? time[structurePivotRight] : na
f_htfSupplyTop() =>
htfPivotSupply = ta.pivothigh(high, structurePivotLeft, structurePivotRight)
htfSupplyValid = not na(htfPivotSupply) and low <= htfPivotSupply - ta.atr(atrLen)[structurePivotRight] * minStructureReactionAtr
htfSupplyValid ? htfPivotSupply : na
f_htfSupplyBottom() =>
htfPivotSupply = ta.pivothigh(high, structurePivotLeft, structurePivotRight)
htfSupplyValid = not na(htfPivotSupply) and low <= htfPivotSupply - ta.atr(atrLen)[structurePivotRight] * minStructureReactionAtr
htfSupplyValid ? htfPivotSupply - ta.atr(atrLen)[structurePivotRight] * structureZoneAtr : na
f_htfSupplyTime() =>
htfPivotSupply = ta.pivothigh(high, structurePivotLeft, structurePivotRight)
htfSupplyValid = not na(htfPivotSupply) and low <= htfPivotSupply - ta.atr(atrLen)[structurePivotRight] * minStructureReactionAtr
htfSupplyValid ? time[structurePivotRight] : na
var float[] htfDemandZoneBottoms = array.new_float()
var float[] htfDemandZoneTops = array.new_float()
var int[] htfDemandZoneTimes = array.new_int()
var box[] htfDemandZoneBoxes = array.new_box()
var label[] htfDemandZoneLabels = array.new_label()
var int[] htfDemandZoneTests = array.new_int()
var bool[] htfDemandZoneWasInside = array.new_bool()
var string[] htfDemandZoneTfTexts = array.new_string()
var float[] htfSupplyZoneTops = array.new_float()
var float[] htfSupplyZoneBottoms = array.new_float()
var int[] htfSupplyZoneTimes = array.new_int()
var box[] htfSupplyZoneBoxes = array.new_box()
var label[] htfSupplyZoneLabels = array.new_label()
var int[] htfSupplyZoneTests = array.new_int()
var bool[] htfSupplyZoneWasInside = array.new_bool()
var string[] htfSupplyZoneTfTexts = array.new_string()
var int lastHtfDemandTime1 = na
var int lastHtfSupplyTime1 = na
var int lastHtfDemandTime2 = na
var int lastHtfSupplyTime2 = na
validDemandPivot = not na(pivotDemand) and high >= pivotDemand + atr[structurePivotRight] * minStructureReactionAtr
validSupplyPivot = not na(pivotSupply) and low <= pivotSupply - atr[structurePivotRight] * minStructureReactionAtr
htfDemandBottom1 = request.security(syminfo.tickerid, htfStructureTf1, f_htfDemandBottom(), barmerge.gaps_off, barmerge.lookahead_off)
htfDemandTop1 = request.security(syminfo.tickerid, htfStructureTf1, f_htfDemandTop(), barmerge.gaps_off, barmerge.lookahead_off)
htfDemandTime1 = request.security(syminfo.tickerid, htfStructureTf1, f_htfDemandTime(), barmerge.gaps_off, barmerge.lookahead_off)
htfSupplyTop1 = request.security(syminfo.tickerid, htfStructureTf1, f_htfSupplyTop(), barmerge.gaps_off, barmerge.lookahead_off)
htfSupplyBottom1 = request.security(syminfo.tickerid, htfStructureTf1, f_htfSupplyBottom(), barmerge.gaps_off, barmerge.lookahead_off)
htfSupplyTime1 = request.security(syminfo.tickerid, htfStructureTf1, f_htfSupplyTime(), barmerge.gaps_off, barmerge.lookahead_off)
htfDemandBottom2 = request.security(syminfo.tickerid, htfStructureTf2, f_htfDemandBottom(), barmerge.gaps_off, barmerge.lookahead_off)
htfDemandTop2 = request.security(syminfo.tickerid, htfStructureTf2, f_htfDemandTop(), barmerge.gaps_off, barmerge.lookahead_off)
htfDemandTime2 = request.security(syminfo.tickerid, htfStructureTf2, f_htfDemandTime(), barmerge.gaps_off, barmerge.lookahead_off)
htfSupplyTop2 = request.security(syminfo.tickerid, htfStructureTf2, f_htfSupplyTop(), barmerge.gaps_off, barmerge.lookahead_off)
htfSupplyBottom2 = request.security(syminfo.tickerid, htfStructureTf2, f_htfSupplyBottom(), barmerge.gaps_off, barmerge.lookahead_off)
htfSupplyTime2 = request.security(syminfo.tickerid, htfStructureTf2, f_htfSupplyTime(), barmerge.gaps_off, barmerge.lookahead_off)
newHtfDemand1 = not na(htfDemandTime1) and (na(lastHtfDemandTime1) or htfDemandTime1 != lastHtfDemandTime1)
newHtfSupply1 = not na(htfSupplyTime1) and (na(lastHtfSupplyTime1) or htfSupplyTime1 != lastHtfSupplyTime1)
newHtfDemand2 = not na(htfDemandTime2) and (na(lastHtfDemandTime2) or htfDemandTime2 != lastHtfDemandTime2)
newHtfSupply2 = not na(htfSupplyTime2) and (na(lastHtfSupplyTime2) or htfSupplyTime2 != lastHtfSupplyTime2)
if newHtfDemand1
lastHtfDemandTime1 := htfDemandTime1
htfDemandBox = showHtfStructureZones ? box.new(left=htfDemandTime1, top=htfDemandTop1, right=time, bottom=htfDemandBottom1, xloc=xloc.bar_time, bgcolor=color.new(color.green, 82), border_color=color.new(color.green, 10), border_width=2) : na
htfDemandLabel = showHtfStructureZones ? label.new(x=time, y=(htfDemandTop1 + htfDemandBottom1) / 2.0, text=f_tfText(htfStructureTf1) + " 需求 x0", xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.new(color.green, 60), textcolor=color.white, size=size.small) : na
array.push(htfDemandZoneBottoms, htfDemandBottom1)
array.push(htfDemandZoneTops, htfDemandTop1)
array.push(htfDemandZoneTimes, htfDemandTime1)
array.push(htfDemandZoneBoxes, htfDemandBox)
array.push(htfDemandZoneLabels, htfDemandLabel)
array.push(htfDemandZoneTests, 0)
array.push(htfDemandZoneWasInside, false)
array.push(htfDemandZoneTfTexts, f_tfText(htfStructureTf1))
if newHtfSupply1
lastHtfSupplyTime1 := htfSupplyTime1
htfSupplyBox = showHtfStructureZones ? box.new(left=htfSupplyTime1, top=htfSupplyTop1, right=time, bottom=htfSupplyBottom1, xloc=xloc.bar_time, bgcolor=color.new(color.maroon, 82), border_color=color.new(color.red, 10), border_width=2) : na
htfSupplyLabel = showHtfStructureZones ? label.new(x=time, y=(htfSupplyTop1 + htfSupplyBottom1) / 2.0, text=f_tfText(htfStructureTf1) + " 供应 x0", xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.new(color.red, 60), textcolor=color.white, size=size.small) : na
array.push(htfSupplyZoneTops, htfSupplyTop1)
array.push(htfSupplyZoneBottoms, htfSupplyBottom1)
array.push(htfSupplyZoneTimes, htfSupplyTime1)
array.push(htfSupplyZoneBoxes, htfSupplyBox)
array.push(htfSupplyZoneLabels, htfSupplyLabel)
array.push(htfSupplyZoneTests, 0)
array.push(htfSupplyZoneWasInside, false)
array.push(htfSupplyZoneTfTexts, f_tfText(htfStructureTf1))
if newHtfDemand2
lastHtfDemandTime2 := htfDemandTime2
htfDemandBox = showHtfStructureZones ? box.new(left=htfDemandTime2, top=htfDemandTop2, right=time, bottom=htfDemandBottom2, xloc=xloc.bar_time, bgcolor=color.new(color.teal, 80), border_color=color.new(color.teal, 5), border_width=2) : na
htfDemandLabel = showHtfStructureZones ? label.new(x=time, y=(htfDemandTop2 + htfDemandBottom2) / 2.0, text=f_tfText(htfStructureTf2) + " 需求 x0", xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.new(color.teal, 55), textcolor=color.white, size=size.small) : na
array.push(htfDemandZoneBottoms, htfDemandBottom2)
array.push(htfDemandZoneTops, htfDemandTop2)
array.push(htfDemandZoneTimes, htfDemandTime2)
array.push(htfDemandZoneBoxes, htfDemandBox)
array.push(htfDemandZoneLabels, htfDemandLabel)
array.push(htfDemandZoneTests, 0)
array.push(htfDemandZoneWasInside, false)
array.push(htfDemandZoneTfTexts, f_tfText(htfStructureTf2))
if newHtfSupply2
lastHtfSupplyTime2 := htfSupplyTime2
htfSupplyBox = showHtfStructureZones ? box.new(left=htfSupplyTime2, top=htfSupplyTop2, right=time, bottom=htfSupplyBottom2, xloc=xloc.bar_time, bgcolor=color.new(color.purple, 80), border_color=color.new(color.purple, 5), border_width=2) : na
htfSupplyLabel = showHtfStructureZones ? label.new(x=time, y=(htfSupplyTop2 + htfSupplyBottom2) / 2.0, text=f_tfText(htfStructureTf2) + " 供应 x0", xloc=xloc.bar_time, yloc=yloc.price, style=label.style_label_left, color=color.new(color.purple, 55), textcolor=color.white, size=size.small) : na
array.push(htfSupplyZoneTops, htfSupplyTop2)
array.push(htfSupplyZoneBottoms, htfSupplyBottom2)
array.push(htfSupplyZoneTimes, htfSupplyTime2)
array.push(htfSupplyZoneBoxes, htfSupplyBox)
array.push(htfSupplyZoneLabels, htfSupplyLabel)
array.push(htfSupplyZoneTests, 0)
array.push(htfSupplyZoneWasInside, false)
array.push(htfSupplyZoneTfTexts, f_tfText(htfStructureTf2))
while array.size(htfDemandZoneTimes) > maxHtfStructureBoxes
oldHtfDemandBox = array.shift(htfDemandZoneBoxes)
oldHtfDemandLabel = array.shift(htfDemandZoneLabels)
array.shift(htfDemandZoneBottoms)
array.shift(htfDemandZoneTops)
array.shift(htfDemandZoneTimes)
array.shift(htfDemandZoneTests)
array.shift(htfDemandZoneWasInside)
array.shift(htfDemandZoneTfTexts)
if not na(oldHtfDemandBox)
box.delete(oldHtfDemandBox)
if not na(oldHtfDemandLabel)
label.delete(oldHtfDemandLabel)
while array.size(htfSupplyZoneTimes) > maxHtfStructureBoxes
oldHtfSupplyBox = array.shift(htfSupplyZoneBoxes)
oldHtfSupplyLabel = array.shift(htfSupplyZoneLabels)
array.shift(htfSupplyZoneTops)
array.shift(htfSupplyZoneBottoms)
array.shift(htfSupplyZoneTimes)
array.shift(htfSupplyZoneTests)
array.shift(htfSupplyZoneWasInside)
array.shift(htfSupplyZoneTfTexts)
if not na(oldHtfSupplyBox)
box.delete(oldHtfSupplyBox)
if not na(oldHtfSupplyLabel)
label.delete(oldHtfSupplyLabel)
if array.size(htfDemandZoneTimes) > 0
htfDemandIndex = array.size(htfDemandZoneTimes) - 1
while htfDemandIndex >= 0
htfDemandBottomLevel = array.get(htfDemandZoneBottoms, htfDemandIndex)
htfDemandTopLevel = array.get(htfDemandZoneTops, htfDemandIndex)
htfDemandBroken = close < htfDemandBottomLevel
htfDemandInside = low <= htfDemandTopLevel and high >= htfDemandBottomLevel
htfDemandWasInside = array.get(htfDemandZoneWasInside, htfDemandIndex)
if htfDemandBroken
brokenHtfDemandBox = array.get(htfDemandZoneBoxes, htfDemandIndex)
brokenHtfDemandLabel = array.get(htfDemandZoneLabels, htfDemandIndex)
if not na(brokenHtfDemandBox)
box.delete(brokenHtfDemandBox)
if not na(brokenHtfDemandLabel)
label.delete(brokenHtfDemandLabel)
array.remove(htfDemandZoneBottoms, htfDemandIndex)
array.remove(htfDemandZoneTops, htfDemandIndex)
array.remove(htfDemandZoneTimes, htfDemandIndex)
array.remove(htfDemandZoneBoxes, htfDemandIndex)
array.remove(htfDemandZoneLabels, htfDemandIndex)
array.remove(htfDemandZoneTests, htfDemandIndex)
array.remove(htfDemandZoneWasInside, htfDemandIndex)
array.remove(htfDemandZoneTfTexts, htfDemandIndex)
else
if htfDemandInside and not htfDemandWasInside
array.set(htfDemandZoneTests, htfDemandIndex, array.get(htfDemandZoneTests, htfDemandIndex) + 1)
array.set(htfDemandZoneWasInside, htfDemandIndex, htfDemandInside)
htfDemandTests = array.get(htfDemandZoneTests, htfDemandIndex)
htfDemandTfText = array.get(htfDemandZoneTfTexts, htfDemandIndex)
htfDemandWeak = htfDemandTests >= maxZoneTests
htfDemandBoxToStyle = array.get(htfDemandZoneBoxes, htfDemandIndex)
htfDemandLabelToStyle = array.get(htfDemandZoneLabels, htfDemandIndex)
if not na(htfDemandBoxToStyle)
box.set_bgcolor(htfDemandBoxToStyle, htfDemandWeak ? color.new(color.gray, 90) : color.new(color.green, 82))
box.set_border_color(htfDemandBoxToStyle, htfDemandWeak ? color.new(color.gray, 35) : color.new(color.green, 10))
box.set_right(htfDemandBoxToStyle, time)
if not na(htfDemandLabelToStyle)
label.set_x(htfDemandLabelToStyle, time)
label.set_y(htfDemandLabelToStyle, (htfDemandTopLevel + htfDemandBottomLevel) / 2.0)
label.set_text(htfDemandLabelToStyle, htfDemandTfText + " 需求 x" + str.tostring(htfDemandTests))
label.set_color(htfDemandLabelToStyle, htfDemandWeak ? color.new(color.gray, 70) : color.new(color.green, 55))
htfDemandIndex := htfDemandIndex - 1
if array.size(htfSupplyZoneTimes) > 0
htfSupplyIndex = array.size(htfSupplyZoneTimes) - 1
while htfSupplyIndex >= 0
htfSupplyTopLevel = array.get(htfSupplyZoneTops, htfSupplyIndex)
htfSupplyBottomLevel = array.get(htfSupplyZoneBottoms, htfSupplyIndex)
htfSupplyBroken = close > htfSupplyTopLevel
htfSupplyInside = high >= htfSupplyBottomLevel and low <= htfSupplyTopLevel
htfSupplyWasInside = array.get(htfSupplyZoneWasInside, htfSupplyIndex)
if htfSupplyBroken
brokenHtfSupplyBox = array.get(htfSupplyZoneBoxes, htfSupplyIndex)
brokenHtfSupplyLabel = array.get(htfSupplyZoneLabels, htfSupplyIndex)
if not na(brokenHtfSupplyBox)
box.delete(brokenHtfSupplyBox)
if not na(brokenHtfSupplyLabel)
label.delete(brokenHtfSupplyLabel)
array.remove(htfSupplyZoneTops, htfSupplyIndex)
array.remove(htfSupplyZoneBottoms, htfSupplyIndex)
array.remove(htfSupplyZoneTimes, htfSupplyIndex)
array.remove(htfSupplyZoneBoxes, htfSupplyIndex)
array.remove(htfSupplyZoneLabels, htfSupplyIndex)
array.remove(htfSupplyZoneTests, htfSupplyIndex)
array.remove(htfSupplyZoneWasInside, htfSupplyIndex)
array.remove(htfSupplyZoneTfTexts, htfSupplyIndex)
else
if htfSupplyInside and not htfSupplyWasInside
array.set(htfSupplyZoneTests, htfSupplyIndex, array.get(htfSupplyZoneTests, htfSupplyIndex) + 1)
array.set(htfSupplyZoneWasInside, htfSupplyIndex, htfSupplyInside)
htfSupplyTests = array.get(htfSupplyZoneTests, htfSupplyIndex)
htfSupplyTfText = array.get(htfSupplyZoneTfTexts, htfSupplyIndex)
htfSupplyWeak = htfSupplyTests >= maxZoneTests
htfSupplyBoxToStyle = array.get(htfSupplyZoneBoxes, htfSupplyIndex)
htfSupplyLabelToStyle = array.get(htfSupplyZoneLabels, htfSupplyIndex)
if not na(htfSupplyBoxToStyle)
box.set_bgcolor(htfSupplyBoxToStyle, htfSupplyWeak ? color.new(color.gray, 90) : color.new(color.red, 82))
box.set_border_color(htfSupplyBoxToStyle, htfSupplyWeak ? color.new(color.gray, 35) : color.new(color.red, 10))
box.set_right(htfSupplyBoxToStyle, time)
if not na(htfSupplyLabelToStyle)
label.set_x(htfSupplyLabelToStyle, time)
label.set_y(htfSupplyLabelToStyle, (htfSupplyTopLevel + htfSupplyBottomLevel) / 2.0)
label.set_text(htfSupplyLabelToStyle, htfSupplyTfText + " 供应 x" + str.tostring(htfSupplyTests))
label.set_color(htfSupplyLabelToStyle, htfSupplyWeak ? color.new(color.gray, 70) : color.new(color.red, 55))
htfSupplyIndex := htfSupplyIndex - 1
if validDemandPivot
demandLeft = bar_index - structurePivotRight
demandBottom = pivotDemand
demandTop = pivotDemand + atr[structurePivotRight] * structureZoneAtr
demandBox = showStructureZones ? box.new(left=demandLeft, top=demandTop, right=bar_index, bottom=demandBottom, xloc=xloc.bar_index, bgcolor=color.new(color.lime, 88), border_color=color.new(color.lime, 25), border_width=1) : na
demandLabel = showStructureZones ? label.new(x=bar_index, y=(demandTop + demandBottom) / 2.0, text=chartTfText + " 需求 x0", xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, color=color.new(color.lime, 75), textcolor=color.white, size=size.tiny) : na
array.push(demandZoneBottoms, demandBottom)
array.push(demandZoneTops, demandTop)
array.push(demandZoneBars, demandLeft)
array.push(demandZoneBoxes, demandBox)
array.push(demandZoneLabels, demandLabel)
array.push(demandZoneTests, 0)
array.push(demandZoneWasInside, false)
if validSupplyPivot
supplyLeft = bar_index - structurePivotRight
supplyTop = pivotSupply
supplyBottom = pivotSupply - atr[structurePivotRight] * structureZoneAtr
supplyBox = showStructureZones ? box.new(left=supplyLeft, top=supplyTop, right=bar_index, bottom=supplyBottom, xloc=xloc.bar_index, bgcolor=color.new(color.red, 88), border_color=color.new(color.red, 25), border_width=1) : na
supplyLabel = showStructureZones ? label.new(x=bar_index, y=(supplyTop + supplyBottom) / 2.0, text=chartTfText + " 供应 x0", xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, color=color.new(color.red, 75), textcolor=color.white, size=size.tiny) : na
array.push(supplyZoneTops, supplyTop)
array.push(supplyZoneBottoms, supplyBottom)
array.push(supplyZoneBars, supplyLeft)
array.push(supplyZoneBoxes, supplyBox)
array.push(supplyZoneLabels, supplyLabel)
array.push(supplyZoneTests, 0)
array.push(supplyZoneWasInside, false)
while array.size(demandZoneBars) > 0
oldestDemandBar = array.get(demandZoneBars, 0)
if bar_index - oldestDemandBar > structureLookback
array.shift(demandZoneBottoms)
array.shift(demandZoneTops)
array.shift(demandZoneBars)
oldDemandBox = array.shift(demandZoneBoxes)
oldDemandLabel = array.shift(demandZoneLabels)
array.shift(demandZoneTests)
array.shift(demandZoneWasInside)
if not na(oldDemandBox)
box.delete(oldDemandBox)
if not na(oldDemandLabel)
label.delete(oldDemandLabel)
else
break
while array.size(supplyZoneBars) > 0
oldestSupplyBar = array.get(supplyZoneBars, 0)
if bar_index - oldestSupplyBar > structureLookback
array.shift(supplyZoneTops)
array.shift(supplyZoneBottoms)
array.shift(supplyZoneBars)
oldSupplyBox = array.shift(supplyZoneBoxes)
oldSupplyLabel = array.shift(supplyZoneLabels)
array.shift(supplyZoneTests)
array.shift(supplyZoneWasInside)
if not na(oldSupplyBox)
box.delete(oldSupplyBox)
if not na(oldSupplyLabel)
label.delete(oldSupplyLabel)
else
break
while array.size(demandZoneBars) > maxStructureBoxes
array.shift(demandZoneBottoms)
array.shift(demandZoneTops)
array.shift(demandZoneBars)
oldDemandBox = array.shift(demandZoneBoxes)
oldDemandLabel = array.shift(demandZoneLabels)
array.shift(demandZoneTests)
array.shift(demandZoneWasInside)
if not na(oldDemandBox)
box.delete(oldDemandBox)
if not na(oldDemandLabel)
label.delete(oldDemandLabel)
while array.size(supplyZoneBars) > maxStructureBoxes
array.shift(supplyZoneTops)
array.shift(supplyZoneBottoms)
array.shift(supplyZoneBars)
oldSupplyBox = array.shift(supplyZoneBoxes)
oldSupplyLabel = array.shift(supplyZoneLabels)
array.shift(supplyZoneTests)
array.shift(supplyZoneWasInside)
if not na(oldSupplyBox)
box.delete(oldSupplyBox)
if not na(oldSupplyLabel)
label.delete(oldSupplyLabel)
if array.size(demandZoneBars) > 0
demandIndex = array.size(demandZoneBars) - 1
while demandIndex >= 0
demandBottomLevel = array.get(demandZoneBottoms, demandIndex)
demandTopLevel = array.get(demandZoneTops, demandIndex)
demandBroken = close < demandBottomLevel
demandInside = low <= demandTopLevel and high >= demandBottomLevel
demandWasInside = array.get(demandZoneWasInside, demandIndex)
if demandBroken
brokenDemandBox = array.get(demandZoneBoxes, demandIndex)
brokenDemandLabel = array.get(demandZoneLabels, demandIndex)
if not na(brokenDemandBox)
box.delete(brokenDemandBox)
if not na(brokenDemandLabel)
label.delete(brokenDemandLabel)
array.remove(demandZoneBottoms, demandIndex)
array.remove(demandZoneTops, demandIndex)
array.remove(demandZoneBars, demandIndex)
array.remove(demandZoneBoxes, demandIndex)
array.remove(demandZoneLabels, demandIndex)
array.remove(demandZoneTests, demandIndex)
array.remove(demandZoneWasInside, demandIndex)
else
if demandInside and not demandWasInside
array.set(demandZoneTests, demandIndex, array.get(demandZoneTests, demandIndex) + 1)
array.set(demandZoneWasInside, demandIndex, demandInside)
demandTests = array.get(demandZoneTests, demandIndex)
demandBoxToStyle = array.get(demandZoneBoxes, demandIndex)
if not na(demandBoxToStyle)
demandWeak = demandTests >= maxZoneTests
box.set_bgcolor(demandBoxToStyle, demandWeak ? color.new(color.gray, 92) : color.new(color.lime, 88))
box.set_border_color(demandBoxToStyle, demandWeak ? color.new(color.gray, 45) : color.new(color.lime, 25))
demandLabelToStyle = array.get(demandZoneLabels, demandIndex)
if not na(demandLabelToStyle)
demandWeak = demandTests >= maxZoneTests
label.set_text(demandLabelToStyle, chartTfText + " 需求 x" + str.tostring(demandTests))
label.set_y(demandLabelToStyle, (demandTopLevel + demandBottomLevel) / 2.0)
label.set_color(demandLabelToStyle, demandWeak ? color.new(color.gray, 75) : color.new(color.lime, 75))
demandIndex := demandIndex - 1
if array.size(supplyZoneBars) > 0
supplyIndex = array.size(supplyZoneBars) - 1
while supplyIndex >= 0
supplyTopLevel = array.get(supplyZoneTops, supplyIndex)
supplyBottomLevel = array.get(supplyZoneBottoms, supplyIndex)
supplyBroken = close > supplyTopLevel
supplyInside = high >= supplyBottomLevel and low <= supplyTopLevel
supplyWasInside = array.get(supplyZoneWasInside, supplyIndex)
if supplyBroken
brokenSupplyBox = array.get(supplyZoneBoxes, supplyIndex)
brokenSupplyLabel = array.get(supplyZoneLabels, supplyIndex)
if not na(brokenSupplyBox)
box.delete(brokenSupplyBox)
if not na(brokenSupplyLabel)
label.delete(brokenSupplyLabel)
array.remove(supplyZoneTops, supplyIndex)
array.remove(supplyZoneBottoms, supplyIndex)
array.remove(supplyZoneBars, supplyIndex)
array.remove(supplyZoneBoxes, supplyIndex)
array.remove(supplyZoneLabels, supplyIndex)
array.remove(supplyZoneTests, supplyIndex)
array.remove(supplyZoneWasInside, supplyIndex)
else
if supplyInside and not supplyWasInside
array.set(supplyZoneTests, supplyIndex, array.get(supplyZoneTests, supplyIndex) + 1)
array.set(supplyZoneWasInside, supplyIndex, supplyInside)
supplyTests = array.get(supplyZoneTests, supplyIndex)
supplyBoxToStyle = array.get(supplyZoneBoxes, supplyIndex)
if not na(supplyBoxToStyle)
supplyWeak = supplyTests >= maxZoneTests
box.set_bgcolor(supplyBoxToStyle, supplyWeak ? color.new(color.gray, 92) : color.new(color.red, 88))
box.set_border_color(supplyBoxToStyle, supplyWeak ? color.new(color.gray, 45) : color.new(color.red, 25))
supplyLabelToStyle = array.get(supplyZoneLabels, supplyIndex)
if not na(supplyLabelToStyle)
supplyWeak = supplyTests >= maxZoneTests
label.set_text(supplyLabelToStyle, chartTfText + " 供应 x" + str.tostring(supplyTests))
label.set_y(supplyLabelToStyle, (supplyTopLevel + supplyBottomLevel) / 2.0)
label.set_color(supplyLabelToStyle, supplyWeak ? color.new(color.gray, 75) : color.new(color.red, 75))
supplyIndex := supplyIndex - 1
if array.size(demandZoneBoxes) > 0
for zoneIndex = 0 to array.size(demandZoneBoxes) - 1
activeDemandBox = array.get(demandZoneBoxes, zoneIndex)
if not na(activeDemandBox)
box.set_right(activeDemandBox, bar_index)
activeDemandLabel = array.get(demandZoneLabels, zoneIndex)
if not na(activeDemandLabel)
label.set_x(activeDemandLabel, bar_index)
if array.size(supplyZoneBoxes) > 0
for zoneIndex = 0 to array.size(supplyZoneBoxes) - 1
activeSupplyBox = array.get(supplyZoneBoxes, zoneIndex)
if not na(activeSupplyBox)
box.set_right(activeSupplyBox, bar_index)
activeSupplyLabel = array.get(supplyZoneLabels, zoneIndex)
if not na(activeSupplyLabel)
label.set_x(activeSupplyLabel, bar_index)
float nearestDemandTop = na
float nearestDemandBottom = na
bool currentDemandTouch = false
if array.size(demandZoneTops) > 0
for zoneIndex = 0 to array.size(demandZoneTops) - 1
demandTopLevel = array.get(demandZoneTops, zoneIndex)
demandBottomLevel = array.get(demandZoneBottoms, zoneIndex)
demandTouched = low <= demandTopLevel + atr * htfZoneBlockAtr and high >= demandBottomLevel - atr * htfZoneBlockAtr
currentDemandTouch := currentDemandTouch or demandTouched
demandZoneStrong = array.get(demandZoneTests, zoneIndex) < maxZoneTests
if demandZoneStrong
if demandBottomLevel <= close and (na(nearestDemandTop) or demandTopLevel > nearestDemandTop)
nearestDemandTop := demandTopLevel
nearestDemandBottom := demandBottomLevel
float nearestSupplyBottom = na
float nearestSupplyTop = na
bool currentSupplyTouch = false
if array.size(supplyZoneBottoms) > 0
for zoneIndex = 0 to array.size(supplyZoneBottoms) - 1
supplyBottomLevel = array.get(supplyZoneBottoms, zoneIndex)
supplyTopLevel = array.get(supplyZoneTops, zoneIndex)
supplyTouched = high >= supplyBottomLevel - atr * htfZoneBlockAtr and low <= supplyTopLevel + atr * htfZoneBlockAtr
currentSupplyTouch := currentSupplyTouch or supplyTouched
supplyZoneStrong = array.get(supplyZoneTests, zoneIndex) < maxZoneTests
if supplyZoneStrong
if supplyTopLevel >= close and (na(nearestSupplyBottom) or supplyBottomLevel < nearestSupplyBottom)
nearestSupplyBottom := supplyBottomLevel
nearestSupplyTop := supplyTopLevel
roomToDemand = na(nearestDemandTop) ? na : close - nearestDemandTop
roomToSupply = na(nearestSupplyBottom) ? na : nearestSupplyBottom - close
float nearestHtfDemandTop = na
float nearestHtfDemandBottom = na
bool currentHtfDemandTouch = false
if array.size(htfDemandZoneTops) > 0
for zoneIndex = 0 to array.size(htfDemandZoneTops) - 1
htfDemandTopLevel = array.get(htfDemandZoneTops, zoneIndex)
htfDemandBottomLevel = array.get(htfDemandZoneBottoms, zoneIndex)
htfDemandTouched = low <= htfDemandTopLevel + atr * htfZoneBlockAtr and high >= htfDemandBottomLevel - atr * htfZoneBlockAtr
currentHtfDemandTouch := currentHtfDemandTouch or htfDemandTouched
htfDemandStrong = array.get(htfDemandZoneTests, zoneIndex) < maxZoneTests
if htfDemandStrong
if htfDemandBottomLevel <= close and (na(nearestHtfDemandTop) or htfDemandTopLevel > nearestHtfDemandTop)
nearestHtfDemandTop := htfDemandTopLevel
nearestHtfDemandBottom := htfDemandBottomLevel
float nearestHtfSupplyBottom = na
float nearestHtfSupplyTop = na
bool currentHtfSupplyTouch = false
if array.size(htfSupplyZoneBottoms) > 0
for zoneIndex = 0 to array.size(htfSupplyZoneBottoms) - 1
htfSupplyBottomLevel = array.get(htfSupplyZoneBottoms, zoneIndex)
htfSupplyTopLevel = array.get(htfSupplyZoneTops, zoneIndex)
htfSupplyTouched = high >= htfSupplyBottomLevel - atr * htfZoneBlockAtr and low <= htfSupplyTopLevel + atr * htfZoneBlockAtr
currentHtfSupplyTouch := currentHtfSupplyTouch or htfSupplyTouched
htfSupplyStrong = array.get(htfSupplyZoneTests, zoneIndex) < maxZoneTests
if htfSupplyStrong
if htfSupplyTopLevel >= close and (na(nearestHtfSupplyBottom) or htfSupplyBottomLevel < nearestHtfSupplyBottom)
nearestHtfSupplyBottom := htfSupplyBottomLevel
nearestHtfSupplyTop := htfSupplyTopLevel
roomToHtfDemand = na(nearestHtfDemandTop) ? na : close - nearestHtfDemandTop
roomToHtfSupply = na(nearestHtfSupplyBottom) ? na : nearestHtfSupplyBottom - close
shortNearHtfDemand = useHtfStructureFilter and useHtfZoneHardBlock and currentHtfDemandTouch
longNearHtfSupply = useHtfStructureFilter and useHtfZoneHardBlock and currentHtfSupplyTouch
shortHtfDemandBlock = useHtfStructureFilter and useHtfZoneHardBlock and (shortNearHtfDemand or nz(ta.barssince(shortNearHtfDemand), 100000) <= htfZoneTouchBlockBars)
longHtfSupplyBlock = useHtfStructureFilter and useHtfZoneHardBlock and (longNearHtfSupply or nz(ta.barssince(longNearHtfSupply), 100000) <= htfZoneTouchBlockBars)
downImpulseIntoZone = ta.highest(high[1], 5) - low >= atr * zoneImpulseAtr
upImpulseIntoZone = high - ta.lowest(low[1], 5) >= atr * zoneImpulseAtr
developingDemandReaction = downImpulseIntoZone and low <= ta.lowest(low, extremeLookback) + atr * 0.20
developingSupplyReaction = upImpulseIntoZone and high >= ta.highest(high, extremeLookback) - atr * 0.20
demandReaction = useZoneReactionBlock and (currentDemandTouch or currentHtfDemandTouch or developingDemandReaction) and close > open and body >= atr * minZoneReactionBodyAtr and (bullishEngulfing or close > high[1] or downImpulseIntoZone)
supplyReaction = useZoneReactionBlock and (currentSupplyTouch or currentHtfSupplyTouch or developingSupplyReaction) and close < open and body >= atr * minZoneReactionBodyAtr and (bearishEngulfing or close < low[1] or upImpulseIntoZone)
shortZoneReactionBlock = useZoneReactionBlock and (demandReaction or nz(ta.barssince(demandReaction), 100000) <= zoneReactionBlockBars)
longZoneReactionBlock = useZoneReactionBlock and (supplyReaction or nz(ta.barssince(supplyReaction), 100000) <= zoneReactionBlockBars)
shortLocalStructureRoomOk = not useStructureZoneFilter or na(nearestDemandTop) or roomToDemand >= atr * minRoomToZoneAtr
longLocalStructureRoomOk = not useStructureZoneFilter or na(nearestSupplyBottom) or roomToSupply >= atr * minRoomToZoneAtr
shortHtfStructureRoomOk = not useHtfStructureFilter or ((na(nearestHtfDemandTop) or roomToHtfDemand >= atr * minRoomToZoneAtr) and not shortHtfDemandBlock)
longHtfStructureRoomOk = not useHtfStructureFilter or ((na(nearestHtfSupplyBottom) or roomToHtfSupply >= atr * minRoomToZoneAtr) and not longHtfSupplyBlock)
shortStructureRoomOk = shortLocalStructureRoomOk and shortHtfStructureRoomOk and not shortZoneReactionBlock
longStructureRoomOk = longLocalStructureRoomOk and longHtfStructureRoomOk and not longZoneReactionBlock
simpleBullTrend = biasBullOk and biasSlopeBullOk and bullOrder and bullSlopeCount >= profileSlopeNeed and ribbonSeparated and close > emaBias + atr * trendAwayAtr
simpleBearTrend = biasBearOk and biasSlopeBearOk and bearOrder and bearSlopeCount >= profileSlopeNeed and ribbonSeparated and close < emaBias - atr * trendAwayAtr
simpleBullPullback = simpleBullTrend and low <= emaMid and low >= emaSlow - atr * biasRetestAtr
simpleBearPullback = simpleBearTrend and high >= emaMid and high <= emaSlow + atr * biasRetestAtr
simpleBullPullbackRecent = nz(ta.barssince(simpleBullPullback), 100000) <= pullbackLookback
simpleBearPullbackRecent = nz(ta.barssince(simpleBearPullback), 100000) <= pullbackLookback
simpleLongConfirm = close > emaFast and close > emaMid and close > open and close - emaMid <= atr * maxContinuationDistanceMidAtr and entryRangePosition <= maxLongEntryRangePosition
simpleShortConfirm = close < emaFast and close < emaMid and close < open and emaMid - close <= atr * maxContinuationDistanceMidAtr and entryRangePosition >= minShortEntryRangePosition
simpleLongSetup = mtfLongOk and simpleBullTrend and simpleBullPullbackRecent and simpleLongConfirm and not chopZone and adxContinuationOk and diBullOk
simpleShortSetup = mtfShortOk and simpleBearTrend and simpleBearPullbackRecent and simpleShortConfirm and not chopZone and adxContinuationOk and diBearOk
launchWasCompressed = ta.lowest(ribbonWidthAtr, compressionLookback) <= compressionAtr
prelaunchStillCompressed = launchWasCompressed and ribbonWidthAtr <= prelaunchMaxRibbonAtr
breaksShortHigh = close > ta.highest(high[1], launchBreakLookback)
breaksShortLow = close < ta.lowest(low[1], launchBreakLookback)
prelaunchHigh = ta.highest(high[1], launchBreakLookback)
prelaunchLow = ta.lowest(low[1], launchBreakLookback)
longNearBreak = high >= prelaunchHigh - atr * prelaunchBreakBufferAtr and close <= prelaunchHigh - atr * prelaunchMinBreakCloseAtr
shortNearBreak = low <= prelaunchLow + atr * prelaunchBreakBufferAtr and close >= prelaunchLow + atr * prelaunchMinBreakCloseAtr
launchBodyOk = body >= atr * minLaunchBodyAtr
prelaunchBodyOk = body >= atr * prelaunchMinBodyAtr
continuationBodyOk = body >= atr * minContinuationBodyAtr
continuationDistanceOk = math.abs(close - emaFast) <= atr * maxContinuationDistanceAtr
longContinuationDistanceOk = continuationDistanceOk and close - emaMid <= atr * maxContinuationDistanceMidAtr
shortContinuationDistanceOk = continuationDistanceOk and emaMid - close <= atr * maxContinuationDistanceMidAtr
prelaunchNoMatureTrend = not simpleBullTrend and not simpleBearTrend
prelaunchCleanCompression = prelaunchStillCompressed and priceCrossRibbonCount <= prelaunchMaxRibbonCrosses and biasSideFlips <= prelaunchMaxBiasCrosses
longPrelaunchDirectionOk = emaFast >= emaMid and emaMid >= emaSlow and emaFast > emaFast[1] and emaMid >= emaMid[1] and close >= emaBias - atr * prelaunchBiasNearAtr
shortPrelaunchDirectionOk = emaFast <= emaMid and emaMid <= emaSlow and emaFast < emaFast[1] and emaMid <= emaMid[1] and close <= emaBias + atr * prelaunchBiasNearAtr
longPrelaunchRaw = prelaunchMode != "关闭" and mtfLongOk and prelaunchNoMatureTrend and prelaunchCleanCompression and longStructureRoomOk and longDirectionOk and longNearBreak and close > emaMid and close > open and prelaunchBodyOk and bullSlopeCount >= profileSlopeNeed and longPrelaunchDirectionOk and diBullOk and longPositionOk
shortPrelaunchRaw = prelaunchMode != "关闭" and mtfShortOk and prelaunchNoMatureTrend and prelaunchCleanCompression and shortStructureRoomOk and shortDirectionOk and shortNearBreak and close < emaMid and close < open and prelaunchBodyOk and bearSlopeCount >= profileSlopeNeed and shortPrelaunchDirectionOk and diBearOk and shortPositionOk
bullLaunchRaw = mtfLongOk and adxLaunchOk and diBullOk and longStructureRoomOk and longLaunchEscapeOk and biasBullLaunchOk and biasSlopeBullOk and longDirectionOk and launchWasCompressed and bullOrder and bullSlopeCount >= profileSlopeNeed and bullRibbonExpanding and close > emaFast and close > open and launchBodyOk and longPositionOk and longCleanClose and (not profileBreakNeed or breaksShortHigh)
bearLaunchRaw = mtfShortOk and adxLaunchOk and diBearOk and shortStructureRoomOk and shortLaunchEscapeOk and biasBearLaunchOk and biasSlopeBearOk and shortDirectionOk and launchWasCompressed and bearOrder and bearSlopeCount >= profileSlopeNeed and bearRibbonExpanding and close < emaFast and close < open and launchBodyOk and shortPositionOk and shortCleanClose and (not profileBreakNeed or breaksShortLow)
bullLaunchStart = bullLaunchRaw and not bullLaunchRaw[1]
bearLaunchStart = bearLaunchRaw and not bearLaunchRaw[1]
bullLaunchFresh = nz(ta.barssince(bullLaunchStart), 100000) <= launchFreshBars
bearLaunchFresh = nz(ta.barssince(bearLaunchStart), 100000) <= launchFreshBars
bullTrendContext = biasBullOk and biasSlopeBullOk and longDirectionOk and bullOrder and bullSlopeCount >= profileSlopeNeed
bearTrendContext = biasBearOk and biasSlopeBearOk and shortDirectionOk and bearOrder and bearSlopeCount >= profileSlopeNeed
bullTrendBars = bullTrendContext ? nz(ta.barssince(not bullTrendContext), 100000) : 0
bearTrendBars = bearTrendContext ? nz(ta.barssince(not bearTrendContext), 100000) : 0
var float lastBullBreakoutLevel = na
var float lastBearBreakoutLevel = na
bullBreakoutBodyOk = close > open and body >= atr * minBreakoutBodyAtr
bearBreakoutBodyOk = close < open and body >= atr * minBreakoutBodyAtr
bullPriceBreakout = ta.crossover(close, emaBias) and close > ta.highest(high[1], breakoutLevelLookback) and bullBreakoutBodyOk
bearPriceBreakout = ta.crossunder(close, emaBias) and close < ta.lowest(low[1], breakoutLevelLookback) and bearBreakoutBodyOk
bullBiasBreakout = bullPriceBreakout or ta.crossover(emaSlow, emaBias)
bearBiasBreakout = bearPriceBreakout or ta.crossunder(emaSlow, emaBias)
if bullPriceBreakout
lastBullBreakoutLevel := ta.highest(high[1], breakoutLevelLookback)
if bearPriceBreakout
lastBearBreakoutLevel := ta.lowest(low[1], breakoutLevelLookback)
bullBiasBreakoutFresh = not requireRecentBiasBreakout or nz(ta.barssince(bullBiasBreakout), 100000) <= biasBreakoutRetestBars
bearBiasBreakoutFresh = not requireRecentBiasBreakout or nz(ta.barssince(bearBiasBreakout), 100000) <= biasBreakoutRetestBars
bullTrendMature = bullTrendContext and bullTrendBars >= trendMatureBars and bullRibbonExpanding and bullBiasBreakoutFresh and (not useBiasSide or close > emaBias + atr * trendAwayAtr)
bearTrendMature = bearTrendContext and bearTrendBars >= trendMatureBars and bearRibbonExpanding and bearBiasBreakoutFresh and (not useBiasSide or close < emaBias - atr * trendAwayAtr)
longTrendAgeOk = not useLateTrendFilter or maxTrendBars == 0 or bullTrendBars <= maxTrendBars
shortTrendAgeOk = not useLateTrendFilter or maxTrendBars == 0 or bearTrendBars <= maxTrendBars
bullPullbackToRibbon = bullTrendMature and low <= emaFast and low >= emaSlow - atr * biasRetestAtr
bearPullbackToRibbon = bearTrendMature and high >= emaFast and high <= emaSlow + atr * biasRetestAtr
bullPullbackToBias = bullTrendMature and useBiasRetestPullback and low <= emaBias + atr * biasRetestAtr
bearPullbackToBias = bearTrendMature and useBiasRetestPullback and high >= emaBias - atr * biasRetestAtr
bullPullbackToBreakoutLevel = bullTrendMature and not na(lastBullBreakoutLevel) and low <= lastBullBreakoutLevel + atr * breakoutLevelRetestAtr and close >= lastBullBreakoutLevel - atr * breakoutLevelRetestAtr
bearPullbackToBreakoutLevel = bearTrendMature and not na(lastBearBreakoutLevel) and high >= lastBearBreakoutLevel - atr * breakoutLevelRetestAtr and close <= lastBearBreakoutLevel + atr * breakoutLevelRetestAtr
bullPullbackEvent = (useBreakoutLevelRetest ? bullPullbackToBreakoutLevel : bullPullbackToRibbon) or bullPullbackToBias
bearPullbackEvent = (useBreakoutLevelRetest ? bearPullbackToBreakoutLevel : bearPullbackToRibbon) or bearPullbackToBias
bullPullbackRecent = nz(ta.barssince(bullPullbackEvent), 100000) <= pullbackLookback
bearPullbackRecent = nz(ta.barssince(bearPullbackEvent), 100000) <= pullbackLookback
bullBreakoutLevelConfirmOk = not useBreakoutLevelRetest or na(lastBullBreakoutLevel) or close <= lastBullBreakoutLevel + atr * maxContinuationDistanceAtr
bearBreakoutLevelConfirmOk = not useBreakoutLevelRetest or na(lastBearBreakoutLevel) or close >= lastBearBreakoutLevel - atr * maxContinuationDistanceAtr
bullContinuationRaw = mtfLongOk and adxContinuationOk and diBullOk and continuationZoneOk and chopExitConfirmed and longStructureRoomOk and longTrendAgeOk and bullTrendMature and bullPullbackRecent and bullBreakoutLevelConfirmOk and close > emaFast and close > emaMid and close > open and continuationBodyOk and longContinuationDistanceOk and longPositionOk and longEntryPositionOk and longCleanClose
bearContinuationRaw = mtfShortOk and adxContinuationOk and diBearOk and continuationZoneOk and chopExitConfirmed and shortStructureRoomOk and shortTrendAgeOk and bearTrendMature and bearPullbackRecent and bearBreakoutLevelConfirmOk and close < emaFast and close < emaMid and close < open and continuationBodyOk and shortContinuationDistanceOk and shortPositionOk and shortEntryPositionOk and shortCleanClose
var int lastLongSignalBar = na
var int lastShortSignalBar = na
var int lastAnySignalBar = na
var int lastLongPrelaunchBar = na
var int lastShortPrelaunchBar = na
var int longTrendSignalCount = 0
var int shortTrendSignalCount = 0
if close < emaBias or bearTrendContext
longTrendSignalCount := 0
if close > emaBias or bullTrendContext
shortTrendSignalCount := 0
canLongSignal = na(lastLongSignalBar) or bar_index - lastLongSignalBar > cooldownBars
canShortSignal = na(lastShortSignalBar) or bar_index - lastShortSignalBar > cooldownBars
canAnySignal = na(lastAnySignalBar) or bar_index - lastAnySignalBar > globalCooldownBars
canLongPrelaunch = na(lastLongPrelaunchBar) or bar_index - lastLongPrelaunchBar > prelaunchCooldownBars
canShortPrelaunch = na(lastShortPrelaunchBar) or bar_index - lastShortPrelaunchBar > prelaunchCooldownBars
canLongTrendSignal = not useLateTrendFilter or maxMainSignalsPerTrend == 0 or longTrendSignalCount < maxMainSignalsPerTrend
canShortTrendSignal = not useLateTrendFilter or maxMainSignalsPerTrend == 0 or shortTrendSignalCount < maxMainSignalsPerTrend
longPrelaunchSignal = barstate.isconfirmed and longPrelaunchRaw and not longPrelaunchRaw[1] and not bullLaunchRaw and canLongPrelaunch and canAnySignal
shortPrelaunchSignal = barstate.isconfirmed and shortPrelaunchRaw and not shortPrelaunchRaw[1] and not bearLaunchRaw and canShortPrelaunch and canAnySignal
longLaunchSignal = barstate.isconfirmed and bullLaunchStart and canLongSignal and canAnySignal
shortLaunchSignal = barstate.isconfirmed and bearLaunchStart and canShortSignal and canAnySignal
longContinuationSignal = barstate.isconfirmed and simpleLongSetup and not simpleLongSetup[1] and canLongSignal and canAnySignal and canLongTrendSignal
shortContinuationSignal = barstate.isconfirmed and simpleShortSetup and not simpleShortSetup[1] and canShortSignal and canAnySignal and canShortTrendSignal
useLaunchAsMain = mainSignalMode == "启动+趋势回踩"
usePrelaunchAsMain = prelaunchMode == "并入主信号"
mainLongPrelaunchSignal = usePrelaunchAsMain and longPrelaunchSignal and canLongTrendSignal
mainShortPrelaunchSignal = usePrelaunchAsMain and shortPrelaunchSignal and canShortTrendSignal
mainLongLaunchSignal = useLaunchAsMain and longLaunchSignal and canLongTrendSignal
mainShortLaunchSignal = useLaunchAsMain and shortLaunchSignal and canShortTrendSignal
longSignal = mainLongPrelaunchSignal or mainLongLaunchSignal or longContinuationSignal
shortSignal = mainShortPrelaunchSignal or mainShortLaunchSignal or shortContinuationSignal
if longPrelaunchSignal
lastLongPrelaunchBar := bar_index
if shortPrelaunchSignal
lastShortPrelaunchBar := bar_index
if longSignal
lastLongSignalBar := bar_index
longTrendSignalCount := longTrendSignalCount + 1
if shortSignal
lastShortSignalBar := bar_index
shortTrendSignalCount := shortTrendSignalCount + 1
if longSignal or shortSignal
lastAnySignalBar := bar_index
plot(emaFast, "EMA5", color=color.new(color.teal, 0), linewidth=1)
plot(emaMid, "EMA15", color=color.new(color.orange, 0), linewidth=1)
plot(emaSlow, "EMA30", color=color.new(color.yellow, 0), linewidth=1)
plot(emaBias, "EMA144", color=color.new(color.red, 0), linewidth=2)
barcolor(showBarColor and bullTrendContext ? color.new(color.green, 0) : showBarColor and bearTrendContext ? color.new(color.red, 0) : na)
plotshape(longSignal, title="做多信号", style=shape.labelup, location=location.belowbar, color=color.new(color.lime, 0), textcolor=color.black, size=size.normal, text="做多")
plotshape(shortSignal, title="做空信号", style=shape.labeldown, location=location.abovebar, color=color.new(color.red, 0), textcolor=color.white, size=size.normal, text="做空")
plotshape(prelaunchMode != "关闭" and longPrelaunchSignal, title="预启动做多", style=shape.labelup, location=location.belowbar, color=color.new(color.aqua, 0), textcolor=color.black, size=size.tiny, text="预多")
plotshape(prelaunchMode != "关闭" and shortPrelaunchSignal, title="预启动做空", style=shape.labeldown, location=location.abovebar, color=color.new(color.fuchsia, 0), textcolor=color.white, size=size.tiny, text="预空")
plotshape(showMtfRiskMarks and mtfLongRisk and (longPrelaunchSignal or longSignal), title="风险:逆风险周期做多", style=shape.diamond, location=location.belowbar, color=color.new(color.orange, 0), size=size.tiny, text="逆1H")
plotshape(showMtfRiskMarks and mtfShortRisk and (shortPrelaunchSignal or shortSignal), title="风险:逆风险周期做空", style=shape.diamond, location=location.abovebar, color=color.new(color.orange, 0), size=size.tiny, text="逆1H")
plotshape(showAuxMarks and longLaunchSignal, title="辅助:均线带启动做多", style=shape.arrowup, location=location.belowbar, color=color.new(color.lime, 45), size=size.tiny, text="")
plotshape(showAuxMarks and shortLaunchSignal, title="辅助:均线带启动做空", style=shape.arrowdown, location=location.abovebar, color=color.new(color.red, 45), size=size.tiny, text="")
plotshape(showAuxMarks and simpleLongSetup, title="辅助:干净回踩做多", style=shape.circle, location=location.belowbar, color=color.new(color.aqua, 45), size=size.tiny, text="")
plotshape(showAuxMarks and simpleShortSetup, title="辅助:干净反抽做空", style=shape.circle, location=location.abovebar, color=color.new(color.orange, 45), size=size.tiny, text="")
var table stateTable = table.new(position.top_right, 2, 6, border_width=1)
if showStateTable and barstate.islast
directionText = simpleBullTrend ? "趋势多" : simpleBearTrend ? "趋势空" : close > emaBias ? "偏多" : close < emaBias ? "偏空" : "中性"
trendText = chopZone ? "缠绕禁区" : simpleBullTrend or simpleBearTrend ? "均线发散" : ribbonSeparated ? "排列未确认" : "无趋势"
structureText = longPrelaunchSignal ? "预启动多" : shortPrelaunchSignal ? "预启动空" : simpleBullPullback ? "回踩均线带" : simpleBearPullback ? "反抽均线带" : simpleBullPullbackRecent or simpleBearPullbackRecent ? "等待确认" : "等待回踩"
positionText = entryRangePosition > maxLongEntryRangePosition ? "确认偏高" : entryRangePosition < minShortEntryRangePosition ? "确认偏低" : "位置适中"
mtfText = not useMtfTrendFilter ? "关闭" : not mtfLongFilterOk and close >= emaBias ? "15/30反空" : not mtfShortFilterOk and close < emaBias ? "15/30反多" : mtfLongRisk and close >= emaBias ? "逆1H空" : mtfShortRisk and close < emaBias ? "逆1H多" : "顺/中性"
signalText = longSignal ? "做多" : shortSignal ? "做空" : longPrelaunchSignal ? "预多" : shortPrelaunchSignal ? "预空" : chopZone ? "等待脱离" : "等待"
signalColor = longSignal ? color.new(color.green, 0) : shortSignal ? color.new(color.red, 0) : longPrelaunchSignal ? color.new(color.aqua, 0) : shortPrelaunchSignal ? color.new(color.fuchsia, 0) : color.new(color.gray, 0)
directionColor = simpleBullTrend or close > emaBias ? color.new(color.green, 15) : simpleBearTrend or close < emaBias ? color.new(color.red, 15) : color.new(color.gray, 20)
trendColor = chopZone ? color.new(color.orange, 20) : simpleBullTrend ? color.new(color.green, 15) : simpleBearTrend ? color.new(color.red, 15) : color.new(color.gray, 20)
structureColor = longPrelaunchSignal ? color.new(color.aqua, 10) : shortPrelaunchSignal ? color.new(color.fuchsia, 10) : simpleBullPullbackRecent or simpleBearPullbackRecent ? color.new(color.orange, 15) : color.new(color.gray, 20)
mtfCurrentRisk = close >= emaBias ? (not mtfLongFilterOk or mtfLongRisk) : (not mtfShortFilterOk or mtfShortRisk)
mtfColor = not useMtfTrendFilter ? color.new(color.gray, 20) : mtfCurrentRisk ? color.new(color.orange, 15) : color.new(color.green, 25)
table.cell(stateTable, 0, 0, "方向", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(stateTable, 1, 0, directionText, text_color=color.white, bgcolor=directionColor)
table.cell(stateTable, 0, 1, "趋势", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(stateTable, 1, 1, trendText, text_color=color.white, bgcolor=trendColor)
table.cell(stateTable, 0, 2, "结构", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(stateTable, 1, 2, structureText, text_color=color.white, bgcolor=structureColor)
table.cell(stateTable, 0, 3, "位置", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(stateTable, 1, 3, positionText, text_color=color.white, bgcolor=color.new(color.black, 70))
table.cell(stateTable, 0, 4, "多周期", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(stateTable, 1, 4, mtfText, text_color=color.white, bgcolor=mtfColor)
table.cell(stateTable, 0, 5, "信号", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(stateTable, 1, 5, signalText, text_color=color.white, bgcolor=signalColor)
alertcondition(longLaunchSignal, title="均线带启动做多", message="EMA5/15/30 从压缩转为多头排列并向上突破短线结构,出现做多信号")
alertcondition(shortLaunchSignal, title="均线带启动做空", message="EMA5/15/30 从压缩转为空头排列并向下跌破短线结构,出现做空信号")
alertcondition(longPrelaunchSignal, title="预启动做多", message="EMA5/15/30 仍处于压缩/初开阶段,价格贴近上方短结构突破位,出现趋势启动前做多预警")
alertcondition(shortPrelaunchSignal, title="预启动做空", message="EMA5/15/30 仍处于压缩/初开阶段,价格贴近下方短结构突破位,出现趋势启动前做空预警")
alertcondition(longContinuationSignal, title="均线带中继做多", message="多头均线带中回踩 EMA15/30 后重新收回 EMA5出现做多信号")
alertcondition(shortContinuationSignal, title="均线带中继做空", message="空头均线带中反抽 EMA15/30 后重新跌回 EMA5出现做空信号")
alertcondition(longSignal, title="均线带综合做多", message="EMA 均线带出现做多信号")
alertcondition(shortSignal, title="均线带综合做空", message="EMA 均线带出现做空信号")