958 lines
68 KiB
Plaintext
958 lines
68 KiB
Plaintext
//@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 均线带出现做空信号")
|