右上角状态面板
在“图表显示”中勾选“显示右上角状态面板”。面板汇总当前周期、高周期趋势与结构、EMA 方向、最近信号、待确认回踩和组合状态。可用“面板布局”选择完整或精简,并用“面板字号”调整阅读尺寸。
全周期结构突破与回踩确认工具:在当前图表周期识别结构信号,自动选择更高一级周期评定 A 顺势、B 中性、C 逆势;新增组合确认开关,并在历史复盘中分开呈现信号 K 线开盘与收盘确认时间。
作者/来源:Richman Labs
适用市场:加密货币、外汇、指数、股票、商品等具备稳定 OHLC 数据的流动性市场。
适用周期:支持非 Tick 的分钟、小时、日线、周线和月线。自动背景示例:5m→1H、15m→4H、1H→4H、4H→日线、日线→周线、周线→月线、月线→季度线;也可手动指定严格高于当前图表的周期。
参数:周期与结构:自动预设默认开启;自定义结构回看 8、大区间回看 72、准备区比例 0.35、准备有效期 30、突破缓冲 0.10 ATR。动量过滤:ATR 14、RSI 14、DMI 10、EMA 34、斜率回看 8。高周期评级:摆动确认 3、EMA 34、斜率回看 3。回踩确认:默认等待 12 根,触及容差 0.25 ATR、失效 0.50 ATR、恢复 0.10 ATR、确认实体 0.15 ATR。组合确认:总开关、参与评级、回踩后最多等待 K 线、突破回踩极值/结构位/实体 ATR 缓冲、2/3 与 3/3 标签和独立面板开关。历史复盘:最近 50 条原始或组合事件,可按序号或时间定位。
功能:先在当前周期识别高低位准备区与首次结构突破,再用 ATR、RSI、DMI、EMA 斜率和 K 线实体过滤。高一级周期通过已确认摆动高低点、价格相对 EMA 和 EMA 方向评定背景;A 表示顺高周期趋势,B 表示高周期中性、数据不足或无效,C 表示逆高周期趋势。突破后可等待结构位回踩并重新收强或收弱,回踩信号继承首次突破时的 A/B/C 评级,不会因后续背景变化重算。组合模块只在启用后记录 2/3 回踩与 3/3 后续确认事件;历史复盘可选择原始信号或组合事件,并分别展示该信号 K 线的开盘时间与收盘确认时间(UTC+8)。所有信号只在收盘后成立,高周期数据使用上一根已确认 K 线,避免未来数据泄露。
适用场景:适合多周期结构扫描、顺势突破筛选、突破后回踩确认、组合 2/3 与 3/3 状态研究、逆势转折研究、TradingView 警报和最近 50 条历史信号复盘。建议把 A 级作为顺势研究重点,B 级等待更多确认,C 级主要用于观察潜在转折;不同图表周期需要分别创建警报,不能把指标评级当作收益保证或自动交易指令。
更新时间:2026-11-02
状态:已发布
2026-11-02:更新至 v8.8。保留 v8.5 的 12 类基础信号与原始警报;组合层提供独立总开关、2/3 回踩与 3/3 后续确认,并在历史复盘中以 UTC+8 分开显示信号 K 线开盘与收盘确认时间;信号计算不变。
欢迎分享你对这个指标的使用体验。评论仅用于研究交流,不构成投资建议。
以下说明对应指标源码中的真实功能。请先把源码添加到 TradingView 的 K 线图,再在指标设置中调整这些选项;本站不提供替代性的行情或复盘图表。
在“图表显示”中勾选“显示右上角状态面板”。面板汇总当前周期、高周期趋势与结构、EMA 方向、最近信号、待确认回踩和组合状态。可用“面板布局”选择完整或精简,并用“面板字号”调整阅读尺寸。
在“历史信号回溯”中勾选“开启历史复盘模式”,再选择“序号回溯”或“时间定位”。序号 0 表示最新信号、1 表示上一条;时间定位会选取指定时间之前最近的信号。可选中“复盘时在所选 K 线上标记位置”,并选择回溯“原始信号”或“组合事件”。
在“组合确认 v8.7”中开启“启用组合识别(总开关)”后,指标才会记录 2/3 回踩和 3/3 后续确认。2/3 表示突破回踩完成,3/3 还需要后续价格行为与已确认的高周期方向共同确认。
所有信号和复盘结果均来自收盘后的指标计算,仅用于研究与方法复核,不构成投资建议或自动交易指令。
12 种基础信号由 3 个高周期评级(A 顺势、B 中性、C 逆势)× 2 个方向(多、空)× 2 个阶段(突破、回踩)组成。突破评级在信号发生时锁定;后续回踩沿用原突破评级。启用组合模块后,2/3 表示突破后的有效回踩已出现,3/3 还需要后续价格行为与已确认高周期方向共同确认。
在 TradingView 中选择当前执行周期,保持多周期自动预设开启;确认参考高周期严格大于当前周期,避免把无效背景误读为趋势结论。
A 级用于顺势研究,B 级等待背景更清晰,C 级只作逆势或转折观察;回踩继承原突破评级,因此先看来源突破再判断后续确认。
组合总开关开启后,2/3 是突破回踩完成后的等待状态,3/3 代表后续价格与高周期同向确认。历史复盘可在“原始信号 / 组合事件”之间切换,并用开盘时间和收盘确认时间核对当时环境。
正在显示 12 / 12 种组合
当前周期出现做多结构突破,且高一级周期处于上升状态。作为顺大方向的多头突破候选,优先观察收盘确认、突破质量和后续风险控制。
当前周期出现做空结构突破,且高一级周期处于下降状态。作为顺大方向的空头突破候选,优先观察收盘确认、突破质量和风险边界。
当前周期出现做多突破,但高一级周期中性、数据不足或背景无效。适合视为早期转强线索,需要更多结构、成交或后续回踩确认。
当前周期出现做空突破,但高一级周期中性、数据不足或背景无效。适合视为早期转弱线索,需要更多结构或后续回踩确认。
当前周期做多突破时,高一级周期仍为下降状态。属于逆势反弹或潜在转折观察信号,不应直接等同于高周期趋势反转。
当前周期做空突破时,高一级周期仍为上升状态。属于逆势回落或潜在转折观察信号,不应直接等同于高周期趋势反转。
此前的 A 级做多突破完成后,价格回踩突破结构位并重新收强。评级继承突破当时快照,可作为比首次突破更强调结构确认的顺势多头观察点。
此前的 A 级做空突破完成后,价格回踩突破结构位并重新收弱。评级继承突破当时快照,可作为强调结构确认的顺势空头观察点。
此前的 B 级做多突破完成后,回踩结构位并重新收强。用于确认中性背景下的多头结构是否获得延续,仍需关注高周期是否转为一致。
此前的 B 级做空突破完成后,回踩结构位并重新收弱。用于确认中性背景下的空头结构是否延续,仍需等待高周期方向进一步明确。
此前的 C 级做多突破完成后出现有效回踩确认。虽然比单次逆势突破多一层结构验证,但高周期冲突仍存在,主要用于转折研究和谨慎观察。
此前的 C 级做空突破完成后出现有效回踩确认。即使回踩成立,高周期仍与信号方向冲突,主要用于顶部转折研究和谨慎观察。
阅读顺序建议:先确认高周期背景,再看 A/B/C 评级与方向,最后区分首次突破、回踩、组合 2/3 和组合 3/3。2/3 不是最终确认,组合事件也不替代止损、仓位与失效条件;所有内容仅用于研究和复盘。
源码仅在作者确认可公开后展示,复制后请自行核对版本和适用范围。
//@version=6
indicator("Structure FLOW MTF v8.8 · 全周期+ABC+组合确认+历史复盘", shorttitle="Structure Flow v8.8 by richman", overlay=true, max_labels_count=500)
// v8.8:在v8.7上仅调整历史复盘时间:UTC+8分开显示信号K线开盘与收盘确认;信号计算不变。
// v8.7:在v8.6.1上调整右上角中文面板、三档字号,并为组合层增加独立总开关。
// 保留v8.5的12类信号逻辑与原始警报,组合只做额外识别,不影响基础信号。
// 回踩本身已有突破来源,不视为两个独立指标互相验证;第3级增加后续价格行为与已确认高周期趋势。
// 在当前图表周期计算突破、回踩;自动选择更高一级周期作为A/B/C趋势背景。
// 收盘信号才生效;更高周期使用[1]+lookahead_on读取上一根已经确认的数据。
// A=顺高一级趋势,B=高一级中性/无有效背景,C=逆高一级趋势。
// 回踩信号始终继承首次突破的A/B/C评级。
// 注意:表格不支持点击交互;通过「指标设置 -> 历史信号回溯」切换记录。
// ─── 1. 周期模式和结构参数 ───
useAutoParams = input.bool(true, "使用多周期自动预设参数", group="周期与结构", tooltip="1H使用v8.1原始参数;5m/15m更敏感,4H/日线/周线/月线自动调整结构长度。关闭后采用下方自定义参数。")
structureLenInput = input.int(8, "自定义:小结构突破回看(K线)", minval=4, maxval=50, group="周期与结构")
phaseLenInput = input.int(72, "自定义:大区间阶段回看(K线)", minval=20, maxval=240, group="周期与结构")
phaseZone = input.float(0.35, "高低位准备区域比例", minval=0.10, maxval=0.50, step=0.05, group="周期与结构")
setupExpiryInput = input.int(30, "自定义:准备状态有效期(K线)", minval=6, maxval=150, group="周期与结构")
breakATR = input.float(0.10, "突破缓冲(ATR倍数)", minval=0.0, maxval=1.0, step=0.05, group="周期与结构")
// ─── 2. 当前周期动量过滤 ───
atrLen = input.int(14, "ATR周期", minval=3, group="动量过滤")
rsiLen = input.int(14, "RSI周期", minval=3, group="动量过滤")
dmiLen = input.int(10, "DMI周期", minval=3, group="动量过滤")
emaLenInput = input.int(34, "自定义:EMA周期(不显示)", minval=10, group="动量过滤")
emaSlopeBarsInput = input.int(8, "自定义:EMA斜率比较K线数", minval=3, group="动量过滤")
weakRSI = input.float(45.0, "做多准备:RSI不高于", minval=10, maxval=50, group="动量过滤")
strongRSI = input.float(55.0, "做空准备:RSI不低于", minval=50, maxval=90, group="动量过滤")
longRSI = input.float(52.0, "突破做多:RSI至少", minval=50, maxval=80, group="动量过滤")
shortRSI = input.float(48.0, "突破做空:RSI至多", minval=20, maxval=50, group="动量过滤")
maxOppSlope = input.float(0.75, "允许逆向EMA斜率(ATR)", minval=0.0, step=0.25, group="动量过滤")
maxEmaDist = input.float(2.0, "距EMA最大逆向偏离(ATR)", minval=0.5, step=0.25, group="动量过滤")
minBodyATR = input.float(0.30, "突破K实体最小ATR", minval=0.0, step=0.05, group="动量过滤")
minBodyRatio = input.float(0.40, "突破K实体最小振幅比例", minval=0.0, maxval=1.0, step=0.05, group="动量过滤")
// ─── 3. 自动结构预设(全部按当前图表K线数量计数) ───
// 1H保留v8.1数值。日线/月线是不同交易节奏,不能按自然时间照搬1H参数。
int chartSeconds = timeframe.in_seconds()
bool intraday15 = timeframe.isintraday and chartSeconds <= 900
bool intraday60 = timeframe.isintraday and chartSeconds <= 3600
bool intraday240 = timeframe.isintraday and chartSeconds <= 14400
bool isMonth = timeframe.ismonthly
int structureLen = useAutoParams ? (isMonth ? 5 : timeframe.isweekly ? 6 : timeframe.isdaily ? 10 : intraday240 and not intraday60 ? 10 : 8) : structureLenInput
int phaseLen = useAutoParams ? (isMonth ? 24 : timeframe.isweekly ? 36 : timeframe.isdaily ? 52 : intraday240 and not intraday60 ? 48 : intraday15 ? (chartSeconds <= 300 ? 48 : 56) : 72) : phaseLenInput
int setupExpiry = useAutoParams ? (isMonth ? 12 : timeframe.isweekly ? 16 : timeframe.isdaily ? 24 : intraday240 and not intraday60 ? 36 : intraday15 ? (chartSeconds <= 300 ? 18 : 24) : 30) : setupExpiryInput
int emaLen = useAutoParams ? (isMonth ? 13 : timeframe.isweekly ? 21 : intraday240 and not intraday60 ? 55 : 34) : emaLenInput
int emaSlopeBars = useAutoParams ? (isMonth ? 3 : timeframe.isweekly ? 4 : intraday240 and not intraday60 ? 12 : 8) : emaSlopeBarsInput
// ─── 4. 高一级周期选择 ───
backgroundMode = input.string("自动", "高周期选择方式", options=["自动", "自定义"], group="高周期趋势评级")
manualHTF = input.timeframe("240", "自定义高周期", group="高周期趋势评级", tooltip="必须严格大于当前图表周期。否则只输出当前周期信号,ABC按B级归类,面板提示背景无效。")
htfPivotLen = input.int(3, "高周期摆动高低点确认根数", minval=2, maxval=10, group="高周期趋势评级")
htfEmaLen = input.int(34, "高周期EMA周期", minval=10, maxval=200, group="高周期趋势评级")
htfSlopeLen = input.int(3, "高周期EMA方向回看根数", minval=1, maxval=20, group="高周期趋势评级")
// TV周期格式:60=1小时,240=4小时,1D=日线,1W=周线,1M=月线,3M=季度线。
string autoHTF = timeframe.isseconds ? "15" : timeframe.isintraday ? (chartSeconds <= 60 ? "15" : chartSeconds <= 300 ? "60" : chartSeconds <= 900 ? "240" : chartSeconds <= 3600 ? "240" : chartSeconds <= 14400 ? "1D" : "1W") : timeframe.isdaily ? (timeframe.multiplier < 7 ? "1W" : timeframe.multiplier <= 30 ? "1M" : timeframe.multiplier <= 90 ? "3M" : "12M") : timeframe.isweekly ? (timeframe.multiplier <= 4 ? "1M" : timeframe.multiplier <= 12 ? "3M" : timeframe.multiplier <= 26 ? "6M" : "12M") : timeframe.ismonthly ? (timeframe.multiplier <= 1 ? "3M" : timeframe.multiplier <= 3 ? "6M" : "12M") : "1W"
string higherTF = backgroundMode == "自动" ? autoHTF : manualHTF
bool higherValid = not na(chartSeconds) and timeframe.in_seconds(higherTF) > chartSeconds
// 高周期的局部结构必须等确认摆动高低点,不代表实时预测未来高低点。
// 高周期计算摆动高低点和EMA,全部读取上一根已收盘高周期K线。
// 高点:1=HH,-1=LH,0=持平,9=摆动高点数据不足;低点:1=HL,-1=LL。
// EMA状态分别记录价格相对EMA的位置及EMA斜率,方便展示为什么判为中性。
f_htfSnapshot() =>
float swingH = ta.pivothigh(high, htfPivotLen, htfPivotLen)
float swingL = ta.pivotlow(low, htfPivotLen, htfPivotLen)
float h0 = ta.valuewhen(not na(swingH), swingH, 0)
float h1 = ta.valuewhen(not na(swingH), swingH, 1)
float l0 = ta.valuewhen(not na(swingL), swingL, 0)
float l1 = ta.valuewhen(not na(swingL), swingL, 1)
int highCode = na(h0) or na(h1) ? 9 : h0 > h1 ? 1 : h0 < h1 ? -1 : 0
int lowCode = na(l0) or na(l1) ? 9 : l0 > l1 ? 1 : l0 < l1 ? -1 : 0
float ema4 = ta.ema(close, htfEmaLen)
int priceCode = close > ema4 ? 1 : close < ema4 ? -1 : 0
int slopeCode = ema4 > ema4[htfSlopeLen] ? 1 : ema4 < ema4[htfSlopeLen] ? -1 : 0
int state = highCode == 1 and lowCode == 1 and priceCode == 1 and slopeCode == 1 ? 1 : highCode == -1 and lowCode == -1 and priceCode == -1 and slopeCode == -1 ? -1 : 0
// 对元组的每个元素分别[1],与lookahead_on搭配防止未来数据泄露。
[state[1], highCode[1], lowCode[1], priceCode[1], slopeCode[1]]
int htfState = 0
int htfHighState = 9
int htfLowState = 9
int htfPriceState = 0
int htfSlopeState = 0
if higherValid
[st, hs, ls, ps, es] = request.security(syminfo.tickerid, higherTF, f_htfSnapshot(), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_on)
htfState := nz(st, 0)
htfHighState := nz(hs, 9)
htfLowState := nz(ls, 9)
htfPriceState := nz(ps, 0)
htfSlopeState := nz(es, 0)
// ─── 5. 当前图表周期指标 ───
float atr = ta.atr(atrLen)
float ema = ta.ema(close, emaLen)
float rsi = ta.rsi(close, rsiLen)
[plusDI, minusDI, adxUnused] = ta.dmi(dmiLen, dmiLen)
float phaseHigh = ta.highest(high[1], phaseLen)
float phaseLow = ta.lowest(low[1], phaseLen)
float phaseRange = math.max(phaseHigh - phaseLow, syminfo.mintick)
float phasePos = (close - phaseLow) / phaseRange
bool longSetup = rsi <= weakRSI and phasePos <= phaseZone and close < ema
bool shortSetup = rsi >= strongRSI and phasePos >= 1.0 - phaseZone and close > ema
float prevHigh = ta.highest(high[1], structureLen)
float prevLow = ta.lowest(low[1], structureLen)
float prevHighBefore = ta.highest(high[2], structureLen)
float prevLowBefore = ta.lowest(low[2], structureLen)
bool firstUp = close > prevHigh + breakATR * atr and close[1] <= prevHighBefore + breakATR * atr[1]
bool firstDown = close < prevLow - breakATR * atr and close[1] >= prevLowBefore - breakATR * atr[1]
float body = math.abs(close - open)
float bodyRatio = body / math.max(high - low, syminfo.mintick)
bool strongCandle = body >= minBodyATR * atr and bodyRatio >= minBodyRatio
float emaSlopeATR = atr > 0 ? (ema - ema[emaSlopeBars]) / atr : na
bool longConfirmed = firstUp and close > open and strongCandle and rsi >= longRSI and plusDI > minusDI and emaSlopeATR >= -maxOppSlope and close > ema - maxEmaDist * atr
bool shortConfirmed = firstDown and close < open and strongCandle and rsi <= shortRSI and minusDI > plusDI and emaSlopeATR <= maxOppSlope and close < ema + maxEmaDist * atr
// ─── 6. 防止同向重复报警,但不影响突破后的回踩提醒 ───
minTurnGapInput = input.int(8, "自定义:突破最小间隔(K线)", minval=1, maxval=100, group="信号去重")
lockSide = input.bool(true, "限制同向连续突破", group="信号去重")
sameSideRearmInput = input.int(48, "自定义:同向突破重新开放间隔", minval=8, maxval=300, group="信号去重")
int minTurnGap = useAutoParams ? (isMonth ? 3 : timeframe.isweekly ? 5 : timeframe.isdaily ? 8 : intraday240 and not intraday60 ? 12 : 8) : minTurnGapInput
int sameSideRearmBars = useAutoParams ? (isMonth ? 12 : timeframe.isweekly ? 16 : timeframe.isdaily ? 24 : intraday240 and not intraday60 ? 48 : 48) : sameSideRearmInput
var int longSetupBar = na
var int shortSetupBar = na
var int lastBreakSide = 0
var int lastBreakBar = na
bool periodOK = (timeframe.isintraday or timeframe.isdaily or timeframe.isweekly or timeframe.ismonthly) and not timeframe.isticks
bool confirmed = barstate.isconfirmed and periodOK
if confirmed
if longSetup
longSetupBar := bar_index
if shortSetup
shortSetupBar := bar_index
bool longArmed = not na(longSetupBar) and bar_index > longSetupBar and bar_index - longSetupBar <= setupExpiry
bool shortArmed = not na(shortSetupBar) and bar_index > shortSetupBar and bar_index - shortSetupBar <= setupExpiry
bool turnGapOK = na(lastBreakBar) or bar_index - lastBreakBar >= minTurnGap
bool sideGapOK = na(lastBreakBar) or bar_index - lastBreakBar >= sameSideRearmBars
bool allowLong = not lockSide or lastBreakSide != 1 or sideGapOK
bool allowShort = not lockSide or lastBreakSide != -1 or sideGapOK
bool breakoutLong = confirmed and longArmed and longConfirmed and turnGapOK and allowLong
bool breakoutShort = confirmed and shortArmed and shortConfirmed and turnGapOK and allowShort and not breakoutLong
// 突破当时的A/B/C快照;回踩继承,绝不根据后来高周期变化重新评级。
int longGradeNow = htfState == 1 ? 1 : htfState == 0 ? 2 : 3
int shortGradeNow = htfState == -1 ? 1 : htfState == 0 ? 2 : 3
// ─── 7. 回踩状态机:突破价位 -> 触及 -> 收盘重新转强/弱 ───
retestExpiryInput = input.int(12, "自定义:突破后等待回踩最多K线数", minval=2, maxval=72, group="回踩确认")
int retestExpiry = useAutoParams ? (isMonth ? 6 : timeframe.isweekly ? 8 : timeframe.isdaily ? 10 : 12) : retestExpiryInput
touchATR = input.float(0.25, "回踩触及容差(ATR)", minval=0.0, maxval=1.5, step=0.05, group="回踩确认")
failATR = input.float(0.50, "收盘失效幅度(ATR)", minval=0.0, maxval=2.0, step=0.05, group="回踩确认")
recoverATR = input.float(0.10, "回踩收盘确认幅度(ATR)", minval=0.0, maxval=1.0, step=0.05, group="回踩确认")
retestBodyATR = input.float(0.15, "回踩确认实体最小ATR", minval=0.0, maxval=1.0, step=0.05, group="回踩确认")
retestLongRSI = input.float(50.0, "回踩多头RSI最低", minval=40, maxval=70, group="回踩确认")
retestShortRSI = input.float(50.0, "回踩空头RSI最高", minval=30, maxval=60, group="回踩确认")
var int longBreakBar = na
var float longBreakLevel = na
var bool longTouched = false
var int longBreakGrade = na
var int shortBreakBar = na
var float shortBreakLevel = na
var bool shortTouched = false
var int shortBreakGrade = na
bool retestLong = false
bool retestShort = false
int retestLongGrade = na
int retestShortGrade = na
// 临时保存回踩确认时的原始突破结构位(后续状态机清空等待任务前记录)。
float retestLongEventLevel = na
float retestShortEventLevel = na
if confirmed
// 先处理已有突破的回踩,再登记本K线新突破,杜绝突破当根直接算回踩。
if not na(longBreakBar)
bool longExpired = bar_index - longBreakBar > retestExpiry
bool longFailed = close < longBreakLevel - failATR * atr
if longExpired or longFailed
longBreakBar := na
longBreakLevel := na
longTouched := false
longBreakGrade := na
else if bar_index > longBreakBar
if low <= longBreakLevel + touchATR * atr and high >= longBreakLevel - touchATR * atr
longTouched := true
bool longRebound = longTouched and close >= longBreakLevel + recoverATR * atr and close > open and body >= retestBodyATR * atr and rsi >= retestLongRSI and plusDI >= minusDI and close > ema - maxEmaDist * atr
if longRebound
retestLong := true
retestLongGrade := longBreakGrade
retestLongEventLevel := longBreakLevel
longBreakBar := na
longBreakLevel := na
longTouched := false
longBreakGrade := na
if not na(shortBreakBar)
bool shortExpired = bar_index - shortBreakBar > retestExpiry
bool shortFailed = close > shortBreakLevel + failATR * atr
if shortExpired or shortFailed
shortBreakBar := na
shortBreakLevel := na
shortTouched := false
shortBreakGrade := na
else if bar_index > shortBreakBar
if high >= shortBreakLevel - touchATR * atr and low <= shortBreakLevel + touchATR * atr
shortTouched := true
bool shortRejection = shortTouched and close <= shortBreakLevel - recoverATR * atr and close < open and body >= retestBodyATR * atr and rsi <= retestShortRSI and minusDI >= plusDI and close < ema + maxEmaDist * atr
if shortRejection
retestShort := true
retestShortGrade := shortBreakGrade
retestShortEventLevel := shortBreakLevel
shortBreakBar := na
shortBreakLevel := na
shortTouched := false
shortBreakGrade := na
// 仅针对新的突破登记一次回踩任务;方向反转时作废旧任务。
if breakoutLong
lastBreakSide := 1
lastBreakBar := bar_index
longSetupBar := na
shortSetupBar := na
longBreakBar := bar_index
longBreakLevel := prevHigh
longTouched := false
longBreakGrade := longGradeNow
shortBreakBar := na
shortBreakLevel := na
shortTouched := false
shortBreakGrade := na
else if breakoutShort
lastBreakSide := -1
lastBreakBar := bar_index
longSetupBar := na
shortSetupBar := na
shortBreakBar := bar_index
shortBreakLevel := prevLow
shortTouched := false
shortBreakGrade := shortGradeNow
longBreakBar := na
longBreakLevel := na
longTouched := false
longBreakGrade := na
// ─── 8. A/B/C 分类:突破时评级;回踩继承原始突破等级 ───
bool aLongBreak = breakoutLong and longGradeNow == 1
bool bLongBreak = breakoutLong and longGradeNow == 2
bool cLongBreak = breakoutLong and longGradeNow == 3
bool aShortBreak = breakoutShort and shortGradeNow == 1
bool bShortBreak = breakoutShort and shortGradeNow == 2
bool cShortBreak = breakoutShort and shortGradeNow == 3
bool aLongRetest = retestLong and retestLongGrade == 1
bool bLongRetest = retestLong and retestLongGrade == 2
bool cLongRetest = retestLong and retestLongGrade == 3
bool aShortRetest = retestShort and retestShortGrade == 1
bool bShortRetest = retestShort and retestShortGrade == 2
bool cShortRetest = retestShort and retestShortGrade == 3
// ─── 8.25. 组合确认(可选模块,不改变原有12种信号) ───
// 第1级 = 原始突破;第2级 = 原始回踩(本来就由对应突破派生);
// 第3级 = 回踩之后新增一根已收盘K线,既突破回踩K线高/低点,
// 又守住最初结构价 + 高周期趋势同向(高周期使用v8.5原有已确认数据)。
// B级在突破时高周期中性,所以B组合达到第3级必须等高周期后来转向;可能较少出现。
// 2/3、3/3代表条件层级而非胜率、评级或买卖指令。
comboEnable = input.bool(true, "启用组合识别(总开关)", group="组合确认 v8.7", tooltip="关闭后不计算组合2/3与3/3,不绘制组合标记/结构线,也不触发组合警报;基础ABC突破/回踩信号及其警报完全保留。")
comboGradeFilter = input.string("仅B", "参与组合的原始评级", options=["仅B", "A+B", "全部ABC"], group="组合确认 v8.7", active=comboEnable)
comboWaitBars = input.int(32, "回踩后最多等待确认K线", minval=2, maxval=200, group="组合确认 v8.7", active=comboEnable)
comboCrossATR = input.float(0.05, "第3级:突破回踩K极值缓冲(ATR)", minval=0.0, maxval=2.0, step=0.05, group="组合确认 v8.7", active=comboEnable)
comboHoldATR = input.float(0.20, "第3级:守住原结构位缓冲(ATR)", minval=0.0, maxval=2.0, step=0.05, group="组合确认 v8.7", active=comboEnable)
comboMinBodyATR = input.float(0.10, "第3级:确认K实体最小(ATR)", minval=0.0, maxval=2.0, step=0.05, group="组合确认 v8.7", active=comboEnable)
comboShow2 = input.bool(true, "显示组合2/3标签(回踩当根)", group="组合确认 v8.7", active=comboEnable)
comboShow3 = input.bool(true, "显示确认3/3标签(随后新证据)", group="组合确认 v8.7", active=comboEnable)
comboShowLevel = input.bool(false, "显示等待期间的原突破结构线", group="组合确认 v8.7", active=comboEnable)
comboShowBoard = input.bool(true, "常规面板增加组合状态", group="组合确认 v8.7", active=comboEnable)
comboShowFailed = input.bool(false, "图表标记组合失效/超时", group="组合确认 v8.7", active=comboEnable)
f_comboGradeOK(int g) =>
comboGradeFilter == "全部ABC" or (comboGradeFilter == "A+B" and (g == 1 or g == 2)) or (comboGradeFilter == "仅B" and g == 2)
// 这里直接读取v8.5的回踩布尔值与继承评级,绝不重新计算新一套突破/回踩。
bool comboLong2 = comboEnable and confirmed and retestLong and f_comboGradeOK(retestLongGrade)
bool comboShort2 = comboEnable and confirmed and retestShort and f_comboGradeOK(retestShortGrade)
var int comboPendingSide = 0
var int comboPendingGrade = na
var int comboRetestBar = na
var float comboRetestHigh = na
var float comboRetestLow = na
var float comboBaseLevel = na
var int comboLastSide = 0
var int comboLastGrade = na
var int comboLastStage = 0
var int comboLastBar = na
var string comboLastFailReason = ""
bool comboFail = false
int comboFailSide = 0
int comboFailGrade = na
float comboFailLevel = na
string comboFailReason = ""
bool comboLong3 = false
bool comboShort3 = false
int comboConfirmGrade = na
float comboConfirmLevel = na
if confirmed and comboEnable
// 先检查先前的2/3事件,再登记本根新回踩;确保第3级必须晚于回踩K线。
if comboPendingSide != 0
bool comboExpired = na(comboRetestBar) or bar_index - comboRetestBar > comboWaitBars
bool comboInvalid = comboPendingSide == 1 ? close < comboBaseLevel - failATR * atr : close > comboBaseLevel + failATR * atr
bool comboNewBreak = breakoutLong or breakoutShort
if comboExpired or comboInvalid or comboNewBreak
comboFail := true
comboFailSide := comboPendingSide
comboFailGrade := comboPendingGrade
comboFailLevel := comboBaseLevel
comboFailReason := comboInvalid ? "结构失效" : comboNewBreak ? "新突破打断" : "等待超时"
comboLastSide := comboPendingSide
comboLastGrade := comboPendingGrade
comboLastStage := 4
comboLastBar := bar_index
comboLastFailReason := comboFailReason
comboPendingSide := 0
comboPendingGrade := na
comboRetestBar := na
comboRetestHigh := na
comboRetestLow := na
comboBaseLevel := na
else if bar_index > comboRetestBar
// 独立于原始回踩:后续收盘突破回踩K高低点、守住起始结构价、实体+EMA+DMI、已确认HTF同向。
bool freshLong = comboPendingSide == 1 and close > comboRetestHigh + comboCrossATR * atr and close >= comboBaseLevel + comboHoldATR * atr and close > open and body >= comboMinBodyATR * atr and close > ema and plusDI > minusDI
bool freshShort = comboPendingSide == -1 and close < comboRetestLow - comboCrossATR * atr and close <= comboBaseLevel - comboHoldATR * atr and close < open and body >= comboMinBodyATR * atr and close < ema and minusDI > plusDI
bool htfLongOK = higherValid and htfState == 1
bool htfShortOK = higherValid and htfState == -1
if (freshLong and htfLongOK) or (freshShort and htfShortOK)
comboLong3 := freshLong and htfLongOK
comboShort3 := freshShort and htfShortOK
comboConfirmGrade := comboPendingGrade
comboConfirmLevel := comboBaseLevel
comboLastSide := comboPendingSide
comboLastGrade := comboPendingGrade
comboLastStage := 3
comboLastBar := bar_index
comboLastFailReason := ""
comboPendingSide := 0
comboPendingGrade := na
comboRetestBar := na
comboRetestHigh := na
comboRetestLow := na
comboBaseLevel := na
if comboLong2 or comboShort2
comboPendingSide := comboLong2 ? 1 : -1
comboPendingGrade := comboLong2 ? retestLongGrade : retestShortGrade
comboRetestBar := bar_index
comboRetestHigh := high
comboRetestLow := low
comboBaseLevel := comboLong2 ? retestLongEventLevel : retestShortEventLevel
comboLastSide := comboPendingSide
comboLastGrade := comboPendingGrade
comboLastStage := 2
comboLastBar := bar_index
comboLastFailReason := ""
string comboLastGradeText = comboLastGrade == 1 ? "A" : comboLastGrade == 2 ? "B" : comboLastGrade == 3 ? "C" : "-"
string comboLastSideText = comboLastSide == 1 ? "多" : comboLastSide == -1 ? "空" : ""
string comboStateText = comboPendingSide != 0 ? (comboPendingGrade == 1 ? "A" : comboPendingGrade == 2 ? "B" : "C") + (comboPendingSide == 1 ? "多" : "空") + "组合2/3 · 等确认 " + str.tostring(bar_index - comboRetestBar) + "根" : na(comboLastBar) ? "暂无组合记录" : comboLastGradeText + comboLastSideText + (comboLastStage == 3 ? " · 已确认3/3" : comboLastStage == 4 ? " · 终止(" + comboLastFailReason + ")" : " · 曾出现2/3") + " · " + str.tostring(bar_index - comboLastBar) + "根前"
// ─── 8.5. 历史快照:信号触发的那根已收盘K线,保存当时的数据 ───
// 每次「突破」或「回踩」各计为一条事件;最多只留最新50条已加载历史记录。
// 注意:回踩等级继承首次突破;但下方的高周期背景数值记录回踩那根K线当时的已确认状态。
type SignalSnap
int eventTime // K线开盘时间,供历史标签定位
int eventCloseTime // K线收盘时间,供历史信号选择与展示
int eventBar
int grade
int side
int stage
float signalPrice
float structureLevel
int bgState
int highState
int lowState
int priceState
int slopeState
float rsiValue
float plusDiValue
float minusDiValue
float atrValue
float emaValue
float markerY
var array<SignalSnap> signalHistory = array.new<SignalSnap>()
// 组合历史单独维护,不改变原来的50条原始信号历史。
var array<SignalSnap> comboHistory = array.new<SignalSnap>()
// 最近一次本图表周期已确认的信号。即使隐藏标记,面板仍记录事件。
var int latestGrade = na
var int latestSide = 0 // 1=多,-1=空
var int latestStage = 0 // 1=突破,2=回踩
var int latestBar = na
if confirmed
if breakoutLong
latestGrade := longGradeNow
latestSide := 1
latestStage := 1
latestBar := bar_index
else if breakoutShort
latestGrade := shortGradeNow
latestSide := -1
latestStage := 1
latestBar := bar_index
else if retestLong
latestGrade := retestLongGrade
latestSide := 1
latestStage := 2
latestBar := bar_index
else if retestShort
latestGrade := retestShortGrade
latestSide := -1
latestStage := 2
latestBar := bar_index
// 新增复盘快照,不修改上方信号判定/去重/评级代码。
if confirmed
int eventGrade = na
int eventSide = 0
int eventStage = 0
float eventLevel = na
if breakoutLong
eventGrade := longGradeNow
eventSide := 1
eventStage := 1
eventLevel := prevHigh
else if breakoutShort
eventGrade := shortGradeNow
eventSide := -1
eventStage := 1
eventLevel := prevLow
else if retestLong
eventGrade := retestLongGrade
eventSide := 1
eventStage := 2
eventLevel := retestLongEventLevel
else if retestShort
eventGrade := retestShortGrade
eventSide := -1
eventStage := 2
eventLevel := retestShortEventLevel
if eventSide != 0
float markerPrice = eventSide == 1 ? low - nz(atr, 0.0) * 0.65 : high + nz(atr, 0.0) * 0.65
signalHistory.push(SignalSnap.new(time, time_close, bar_index, eventGrade, eventSide, eventStage, close, eventLevel, htfState, htfHighState, htfLowState, htfPriceState, htfSlopeState, rsi, plusDI, minusDI, atr, ema, markerPrice))
if signalHistory.size() > 50
signalHistory.shift()
// 与原始历史独立:2/3、3/3、失效事件均按发生时的已收盘K线分别入库,不回写历史。
if confirmed
if comboFail
float failY = comboFailSide == 1 ? low - nz(atr, 0.0) * 1.5 : high + nz(atr, 0.0) * 1.5
comboHistory.push(SignalSnap.new(time, time_close, bar_index, comboFailGrade, comboFailSide, 4, close, comboFailLevel, htfState, htfHighState, htfLowState, htfPriceState, htfSlopeState, rsi, plusDI, minusDI, atr, ema, failY))
if comboHistory.size() > 50
comboHistory.shift()
if comboLong2 or comboShort2 or comboLong3 or comboShort3
bool isLevel3 = comboLong3 or comboShort3
bool isLongCombo = comboLong2 or comboLong3
int cg = isLevel3 ? comboConfirmGrade : isLongCombo ? retestLongGrade : retestShortGrade
int cs = isLongCombo ? 1 : -1
int cstage = isLevel3 ? 3 : 2
float clevel = isLevel3 ? comboConfirmLevel : isLongCombo ? retestLongEventLevel : retestShortEventLevel
float cy = cs == 1 ? low - nz(atr, 0.0) * 1.5 : high + nz(atr, 0.0) * 1.5
comboHistory.push(SignalSnap.new(time, time_close, bar_index, cg, cs, cstage, close, clevel, htfState, htfHighState, htfLowState, htfPriceState, htfSlopeState, rsi, plusDI, minusDI, atr, ema, cy))
if comboHistory.size() > 50
comboHistory.shift()
// ─── 9. 当前周期K线图形:突破与回踩 ───
showLong = input.bool(true, "显示做多信号", group="图表显示")
showShort = input.bool(true, "显示做空信号", group="图表显示")
showBreak = input.bool(true, "显示突破预警(三角形)", group="图表显示")
showRetest = input.bool(true, "显示回踩确认(文字标签)", group="图表显示")
showBoard = input.bool(true, "显示右上角状态面板", group="图表显示")
color aBuy = color.rgb(22, 163, 74)
color aSell = color.rgb(220, 38, 38)
color bBuy = color.rgb(2, 132, 199)
color bSell = color.rgb(99, 102, 241)
color cWarn = color.rgb(217, 119, 6)
// 突破:三角形,便于第一时间看到行情启动;回踩:独立标签。
plotshape(showLong and showBreak and aLongBreak, title="A顺势多突破", style=shape.triangleup, location=location.belowbar, color=aBuy, text="A多\n突破", textcolor=aBuy, size=size.tiny)
plotshape(showLong and showBreak and bLongBreak, title="B中性多突破", style=shape.triangleup, location=location.belowbar, color=bBuy, text="B多\n突破", textcolor=bBuy, size=size.tiny)
plotshape(showLong and showBreak and cLongBreak, title="C逆势多突破", style=shape.triangleup, location=location.belowbar, color=cWarn, text="C多\n突破", textcolor=cWarn, size=size.tiny)
plotshape(showShort and showBreak and aShortBreak, title="A顺势空突破", style=shape.triangledown, location=location.abovebar, color=aSell, text="A空\n突破", textcolor=aSell, size=size.tiny)
plotshape(showShort and showBreak and bShortBreak, title="B中性空突破", style=shape.triangledown, location=location.abovebar, color=bSell, text="B空\n突破", textcolor=bSell, size=size.tiny)
plotshape(showShort and showBreak and cShortBreak, title="C逆势空突破", style=shape.triangledown, location=location.abovebar, color=cWarn, text="C空\n突破", textcolor=cWarn, size=size.tiny)
plotshape(showLong and showRetest and aLongRetest, title="A顺势多回踩", style=shape.labelup, location=location.belowbar, color=aBuy, text="A多\n回踩", textcolor=color.white, size=size.tiny)
plotshape(showLong and showRetest and bLongRetest, title="B中性多回踩", style=shape.labelup, location=location.belowbar, color=bBuy, text="B多\n回踩", textcolor=color.white, size=size.tiny)
plotshape(showLong and showRetest and cLongRetest, title="C逆势多回踩", style=shape.labelup, location=location.belowbar, color=cWarn, text="C多\n回踩", textcolor=color.white, size=size.tiny)
plotshape(showShort and showRetest and aShortRetest, title="A顺势空回踩", style=shape.labeldown, location=location.abovebar, color=aSell, text="A空\n回踩", textcolor=color.white, size=size.tiny)
plotshape(showShort and showRetest and bShortRetest, title="B中性空回踩", style=shape.labeldown, location=location.abovebar, color=bSell, text="B空\n回踩", textcolor=color.white, size=size.tiny)
plotshape(showShort and showRetest and cShortRetest, title="C逆势空回踩", style=shape.labeldown, location=location.abovebar, color=cWarn, text="C空\n回踩", textcolor=color.white, size=size.tiny)
// 组合新增标记不覆盖原始12类三角形/回踩标签。位置再向外移,避免遮住B多回踩。
if comboEnable and comboShow2 and (comboLong2 or comboShort2)
bool buy2 = comboLong2
string g2 = (buy2 ? retestLongGrade : retestShortGrade) == 1 ? "A" : (buy2 ? retestLongGrade : retestShortGrade) == 2 ? "B" : "C"
label.new(x=bar_index, y=buy2 ? low - nz(atr, 0.0) * 1.65 : high + nz(atr, 0.0) * 1.65, text="组合 2/3\n" + g2 + (buy2 ? "多" : "空"), style=buy2 ? label.style_label_up : label.style_label_down, color=color.rgb(14, 116, 144), textcolor=color.white, size=size.tiny)
if comboEnable and comboShow3 and (comboLong3 or comboShort3)
bool buy3 = comboLong3
string g3 = comboConfirmGrade == 1 ? "A" : comboConfirmGrade == 2 ? "B" : "C"
label.new(x=bar_index, y=buy3 ? low - nz(atr, 0.0) * 1.15 : high + nz(atr, 0.0) * 1.15, text="确认 3/3\n" + g3 + (buy3 ? "多" : "空"), style=buy3 ? label.style_label_up : label.style_label_down, color=color.rgb(22, 163, 74), textcolor=color.white, size=size.tiny)
if comboEnable and comboShowFailed and comboFail
label.new(x=bar_index, y=comboFailSide == 1 ? low - nz(atr, 0.0) * 1.4 : high + nz(atr, 0.0) * 1.4, text="组合终止\n" + comboFailReason, style=comboFailSide == 1 ? label.style_label_up : label.style_label_down, color=color.rgb(107, 114, 128), textcolor=color.white, size=size.tiny)
plot(comboEnable and comboShowLevel and comboPendingSide != 0 ? comboBaseLevel : na, title="组合等待期结构位", color=comboPendingSide == 1 ? color.new(color.green, 50) : color.new(color.red, 50), linewidth=1, style=plot.style_linebr)
// ─── 10. 清晰中文面板 + 可选组合状态 + 历史信号回溯 ───
boardMode = input.string("完整", "面板布局", options=["完整", "精简", "关闭"], group="图表显示", tooltip="完整=显示高周期结构和均线细节;精简=只显示趋势、最近信号和待回踩状态。")
boardFontChoice = input.string("中", "面板字号", options=["小", "中", "大"], group="图表显示", tooltip="小=11,中=14(默认,比旧版9明显大),大=18;复盘模式也同步生效。")
int boardSize = boardFontChoice == "大" ? 18 : boardFontChoice == "小" ? 11 : 14
// Pine的table不支持鼠标点击/按钮。本模块使用指标「输入」项选择历史快照。
reviewEnabled = input.bool(false, "开启历史复盘模式", group="历史信号回溯", tooltip="打开后面板显示选中历史信号的当时数据;关闭则显示实时中文趋势面板。")
reviewMode = input.string("序号回溯", "历史定位方式", options=["序号回溯", "时间定位"], group="历史信号回溯")
reviewOffset = input.int(0, "向前第几条(0=最新,1=上一条)", minval=0, maxval=49, group="历史信号回溯", active=reviewEnabled and reviewMode == "序号回溯")
reviewDate = input.time(timestamp("01 Jan 2026 00:00 +0000"), "定位时间(选该时间之前最新信号)", group="历史信号回溯", active=reviewEnabled and reviewMode == "时间定位")
showReviewMarker = input.bool(true, "复盘时在所选K线上标记位置", group="历史信号回溯", active=reviewEnabled)
reviewSource = input.string("原始信号", "回溯类型", options=["原始信号", "组合事件"], group="历史信号回溯", active=reviewEnabled, tooltip="原始信号仍保留自己的最近50条;组合事件只有启用组合总开关时才会记录。")
// 面板辅助说明:文字化表达,不参与信号或评级计算。
f_tfLabel(string tf) =>
tf == "1" ? "1m" : tf == "3" ? "3m" : tf == "5" ? "5m" : tf == "15" ? "15m" : tf == "30" ? "30m" : tf == "60" ? "1H" : tf == "120" ? "2H" : tf == "240" ? "4H" : tf == "360" ? "6H" : tf == "1D" or tf == "D" ? "日线" : tf == "1W" or tf == "W" ? "周线" : tf == "1M" or tf == "M" ? "月线" : tf == "3M" ? "季线" : tf == "6M" ? "半年" : tf == "12M" ? "年线" : tf
f_structureText(int hi, int lo) =>
hi == 1 and lo == 1 ? "高低点抬高 (HH/HL)" : hi == -1 and lo == -1 ? "高低点降低 (LH/LL)" : hi == -1 and lo == 1 ? "收敛整理 (LH/HL)" : hi == 1 and lo == -1 ? "扩张震荡 (HH/LL)" : hi == 9 or lo == 9 ? "结构数据不足" : (hi == 1 ? "高点抬高" : hi == -1 ? "高点降低" : "高点持平") + " / " + (lo == 1 ? "低点抬高" : lo == -1 ? "低点降低" : "低点持平")
f_emaText(int p, int sl) =>
string positionText = p == 1 ? "价格高于" : p == -1 ? "价格低于" : "价格贴近"
string slopeText = sl == 1 ? "均线向上" : sl == -1 ? "均线向下" : "均线走平"
positionText + "EMA" + str.tostring(htfEmaLen) + " · " + slopeText
f_htfText(int state, int p, int sl) =>
state == 1 ? "上涨趋势(多头)" : state == -1 ? "下跌趋势(空头)" : p == -1 and sl == -1 ? "中性 · 均线偏空" : p == 1 and sl == 1 ? "中性 · 均线偏多" : "中性 · 方向待确认"
string gradeTextLatest = latestGrade == 1 ? "A" : latestGrade == 2 ? "B" : latestGrade == 3 ? "C" : "-"
string lastSideText = latestSide == 1 ? "多" : latestSide == -1 ? "空" : ""
string lastStageText = latestStage == 1 ? "突破" : latestStage == 2 ? "回踩" : ""
string ageText = na(latestBar) ? "" : " · " + str.tostring(bar_index - latestBar) + "根前"
string lastSignalText = na(latestBar) ? "暂无历史信号" : gradeTextLatest + lastSideText + lastStageText + ageText
color lastSignalColor = latestGrade == 1 ? (latestSide == 1 ? aBuy : aSell) : latestGrade == 2 ? (latestSide == 1 ? bBuy : bSell) : latestGrade == 3 ? cWarn : color.silver
string htStateText = not higherValid ? "高周期无效·请调整" : f_htfText(htfState, htfPriceState, htfSlopeState)
string htStructureText = higherValid ? f_structureText(htfHighState, htfLowState) : "高周期无效"
string htEmaText = higherValid ? f_emaText(htfPriceState, htfSlopeState) : "请设置有效的更高周期"
color htTrendColor = not higherValid ? color.orange : htfState == 1 ? aBuy : htfState == -1 ? aSell : color.rgb(233, 187, 97)
color htEmaColor = not higherValid ? color.orange : htfPriceState == 1 and htfSlopeState == 1 ? color.rgb(62, 194, 145) : htfPriceState == -1 and htfSlopeState == -1 ? color.rgb(236, 115, 115) : color.silver
string waitingText = "无待回踩任务"
color waitingColor = color.silver
if not na(longBreakBar)
string g = longBreakGrade == 1 ? "A" : longBreakGrade == 2 ? "B" : "C"
waitingText := "等" + g + "多回踩 · " + str.tostring(longBreakLevel, format.mintick) + (longTouched ? " (已触及)" : "")
waitingColor := longBreakGrade == 1 ? aBuy : longBreakGrade == 2 ? bBuy : cWarn
else if not na(shortBreakBar)
string g = shortBreakGrade == 1 ? "A" : shortBreakGrade == 2 ? "B" : "C"
waitingText := "等" + g + "空回踩 · " + str.tostring(shortBreakLevel, format.mintick) + (shortTouched ? " (已触及)" : "")
waitingColor := shortBreakGrade == 1 ? aSell : shortBreakGrade == 2 ? bSell : cWarn
// 从最近50条快照选择一条。模式=序号:0最新1前一条;时间:在输入时间以前的最近一条。
int historyCount = reviewSource == "组合事件" ? comboHistory.size() : signalHistory.size()
SignalSnap selectedSnap = na
int selectedIndex = na
if historyCount > 0 and reviewEnabled
if reviewMode == "序号回溯"
if reviewOffset < historyCount
selectedIndex := historyCount - reviewOffset - 1
selectedSnap := reviewSource == "组合事件" ? comboHistory.get(selectedIndex) : signalHistory.get(selectedIndex)
else
for i = 0 to historyCount - 1
SignalSnap candidate = reviewSource == "组合事件" ? comboHistory.get(i) : signalHistory.get(i)
if candidate.eventCloseTime <= reviewDate
selectedIndex := i
selectedSnap := candidate
string selectedGrade = na(selectedSnap) ? "-" : selectedSnap.grade == 1 ? "A" : selectedSnap.grade == 2 ? "B" : "C"
string selectedSideText = na(selectedSnap) ? "" : selectedSnap.side == 1 ? "多" : "空"
string selectedStageText = na(selectedSnap) ? "" : selectedSnap.stage == 1 ? "突破" : selectedSnap.stage == 3 ? "确认3/3" : selectedSnap.stage == 4 ? "组合终止" : reviewSource == "组合事件" ? "组合2/3" : "回踩"
color selectedColor = na(selectedSnap) ? color.silver : selectedSnap.grade == 1 ? (selectedSnap.side == 1 ? aBuy : aSell) : selectedSnap.grade == 2 ? (selectedSnap.side == 1 ? bBuy : bSell) : cWarn
color boardBg = color.rgb(22, 25, 30)
color boardLabelColor = color.rgb(183, 191, 201)
// 固定两列13行(历史复盘新增1行开盘时间);每次最后一根K线清空再绘制,防止切换完整/精简及组合开关时出现残影。
var table board = table.new(position.top_right, 2, 13, border_width=0, frame_width=1, frame_color=color.new(color.gray, 65))
f_boardRow(table t, int r, string heading, string value, color valColor, int fontSz) =>
table.cell(t, 0, r, heading, bgcolor=boardBg, text_color=boardLabelColor, text_size=fontSz, text_halign=text.align_left)
table.cell(t, 1, r, value, bgcolor=boardBg, text_color=valColor, text_size=fontSz, text_halign=text.align_left)
if barstate.islast
table.clear(board, 0, 0, 1, 12)
if showBoard and boardMode != "关闭"
string thisTF = f_tfLabel(timeframe.period)
string bgTF = higherValid ? f_tfLabel(higherTF) : "无效"
if reviewEnabled
f_boardRow(board, 0, "历史复盘 · " + thisTF, "参考 " + bgTF + " · " + (reviewSource == "组合事件" ? "组合" : "原始"), color.white, boardSize)
if na(selectedSnap)
f_boardRow(board, 1, "历史记录", historyCount == 0 ? "当前类型暂无记录" : "已超出最近50条", color.orange, boardSize)
else
string idxText = "倒数第" + str.tostring(historyCount - selectedIndex) + "条 / 共" + str.tostring(historyCount) + "条"
string barOpenText = str.format_time(selectedSnap.eventTime, "yyyy-MM-dd HH:mm", "Asia/Shanghai")
string confirmCloseText = str.format_time(selectedSnap.eventCloseTime, "yyyy-MM-dd HH:mm", "Asia/Shanghai")
string bgText = f_htfText(selectedSnap.bgState, selectedSnap.priceState, selectedSnap.slopeState)
string structText = f_structureText(selectedSnap.highState, selectedSnap.lowState)
string emaStateText = f_emaText(selectedSnap.priceState, selectedSnap.slopeState)
string confirmText = selectedSnap.stage == 3 ? "后续价格+高周期确认" : selectedSnap.stage == 4 ? "条件未满足·任务终止" : selectedSnap.stage == 2 and reviewSource == "组合事件" ? "突破回踩完成·等下一步" : "已收盘时保存"
f_boardRow(board, 1, "历史序号", idxText, color.white, boardSize)
f_boardRow(board, 2, "当时信号", selectedGrade + selectedSideText + selectedStageText, selectedColor, boardSize)
// x轴定位属于K线开盘;任何原始突破/回踩信号,都在对应K线收盘后才确认。
f_boardRow(board, 3, "K线开盘 · UTC+8", barOpenText, color.silver, boardSize)
f_boardRow(board, 4, "收盘确认 · UTC+8", confirmCloseText, color.rgb(74, 222, 128), boardSize)
f_boardRow(board, 5, "收盘价 / 结构位", str.tostring(selectedSnap.signalPrice, format.mintick) + " / " + str.tostring(selectedSnap.structureLevel, format.mintick), color.white, boardSize)
f_boardRow(board, 6, "当时高周期", bgText, selectedSnap.bgState == 1 ? aBuy : selectedSnap.bgState == -1 ? aSell : color.rgb(233, 187, 97), boardSize)
f_boardRow(board, 7, "当时结构", structText, color.white, boardSize)
f_boardRow(board, 8, "当时均线", emaStateText, color.white, boardSize)
f_boardRow(board, 9, "RSI / DMI", str.tostring(selectedSnap.rsiValue, "#.0") + " / +" + str.tostring(selectedSnap.plusDiValue, "#.0") + " -" + str.tostring(selectedSnap.minusDiValue, "#.0"), color.white, boardSize)
f_boardRow(board, 10, "ATR", str.tostring(selectedSnap.atrValue, format.mintick), color.white, boardSize)
f_boardRow(board, 11, "当时EMA值", str.tostring(selectedSnap.emaValue, format.mintick), color.white, boardSize)
f_boardRow(board, 12, "快照说明", confirmText, color.silver, boardSize)
else
f_boardRow(board, 0, "FLOW " + thisTF, "参考高周期 " + bgTF, color.white, boardSize)
f_boardRow(board, 1, "最近信号", lastSignalText, lastSignalColor, boardSize)
f_boardRow(board, 2, bgTF + " 趋势", htStateText, htTrendColor, boardSize)
int rowN = 3
if boardMode == "完整"
f_boardRow(board, rowN, bgTF + " 结构", htStructureText, color.white, boardSize)
rowN += 1
f_boardRow(board, rowN, bgTF + " 均线", htEmaText, htEmaColor, boardSize)
rowN += 1
f_boardRow(board, rowN, "待确认回踩", waitingText, waitingColor, boardSize)
rowN += 1
if comboEnable and comboShowBoard
string readableCombo = comboStateText
color combColor = comboLastStage == 3 ? aBuy : comboLastStage == 4 ? color.silver : color.rgb(56, 189, 248)
f_boardRow(board, rowN, "组合状态", readableCombo, combColor, boardSize)
// 图表上只额外显示一个被选中事件的复盘标记。不影响已有12类信号标签。
var label reviewMarker = na
if barstate.islast
if not na(reviewMarker)
label.delete(reviewMarker)
reviewMarker := na
if reviewEnabled and showReviewMarker and not na(selectedSnap)
reviewMarker := label.new(x=selectedSnap.eventTime, y=selectedSnap.markerY, xloc=xloc.bar_time, yloc=yloc.price, text="复盘 " + selectedGrade + selectedSideText + selectedStageText, style=selectedSnap.side == 1 ? label.style_label_up : label.style_label_down, color=selectedColor, textcolor=color.white, size=size.tiny)
// ─── 11. 警报:独立于图形显示,适用任何时间级别 ───
alertcondition(breakoutLong, title="所有周期 · 所有做多突破", message="{{ticker}} {{interval}} 做多突破(ABC见图表)已收盘")
alertcondition(breakoutShort, title="所有周期 · 所有做空突破", message="{{ticker}} {{interval}} 做空突破(ABC见图表)已收盘")
alertcondition(retestLong, title="所有周期 · 所有做多回踩", message="{{ticker}} {{interval}} 做多回踩(ABC见图表)已收盘")
alertcondition(retestShort, title="所有周期 · 所有做空回踩", message="{{ticker}} {{interval}} 做空回踩(ABC见图表)已收盘")
alertcondition(aLongBreak, title="A 顺势多突破", message="{{ticker}} {{interval}} A顺势多突破;已收盘")
alertcondition(bLongBreak, title="B 中性多突破", message="{{ticker}} {{interval}} B中性多突破;已收盘")
alertcondition(cLongBreak, title="C 逆势多突破", message="{{ticker}} {{interval}} C逆势多突破;已收盘,仅观察")
alertcondition(aShortBreak, title="A 顺势空突破", message="{{ticker}} {{interval}} A顺势空突破;已收盘")
alertcondition(bShortBreak, title="B 中性空突破", message="{{ticker}} {{interval}} B中性空突破;已收盘")
alertcondition(cShortBreak, title="C 逆势空突破", message="{{ticker}} {{interval}} C逆势空突破;已收盘,仅观察")
alertcondition(aLongRetest, title="A 顺势多回踩", message="{{ticker}} {{interval}} A顺势多回踩;继承突破时评级")
alertcondition(bLongRetest, title="B 中性多回踩", message="{{ticker}} {{interval}} B中性多回踩;继承突破时评级")
alertcondition(cLongRetest, title="C 逆势多回踩", message="{{ticker}} {{interval}} C逆势多回踩;继承突破时评级")
alertcondition(aShortRetest, title="A 顺势空回踩", message="{{ticker}} {{interval}} A顺势空回踩;继承突破时评级")
alertcondition(bShortRetest, title="B 中性空回踩", message="{{ticker}} {{interval}} B中性空回踩;继承突破时评级")
alertcondition(cShortRetest, title="C 逆势空回踩", message="{{ticker}} {{interval}} C逆势空回踩;继承突破时评级")
// ─── 11.5. 组合警报:独立事件,不修改原v8.5的16项警报 ───
alertcondition(comboLong2, title="组合2/3 · 多突破后回踩", message="{{ticker}} {{interval}} 组合2/3 多:已回踩原突破结构位,等待后续关键位+HTF确认")
alertcondition(comboShort2, title="组合2/3 · 空突破后回踩", message="{{ticker}} {{interval}} 组合2/3 空:已回踩原突破结构位,等待后续关键位+HTF确认")
alertcondition(comboLong3, title="组合3/3 · 多头进一步确认", message="{{ticker}} {{interval}} 组合3/3 多:后续K确认回踩极值+原结构位,已确认高周期同向")
alertcondition(comboShort3, title="组合3/3 · 空头进一步确认", message="{{ticker}} {{interval}} 组合3/3 空:后续K确认回踩极值+原结构位,已确认高周期同向")
// ─── 12. 动态综合警报:在TV选择「Any alert() function call」只建一个警报 ───
// 消息内自动写明级别/方向/突破或回踩/收盘价格。
smartAlert = input.bool(true, "启用动态ABC综合警报", group="警报设置", tooltip="创建警报时选 Any alert() function call,消息自动包含当前图表周期和高一级周期背景。不同图表周期需要分别创建警报。")
if confirmed and smartAlert and (breakoutLong or breakoutShort or retestLong or retestShort)
bool signalLong = breakoutLong ? true : breakoutShort ? false : retestLong
int eventGrade = breakoutLong ? longGradeNow : breakoutShort ? shortGradeNow : retestLong ? retestLongGrade : retestShortGrade
string gradeText = eventGrade == 1 ? "A级·顺势" : eventGrade == 2 ? "B级·中性" : "C级·逆势"
string sideText = signalLong ? "做多" : "做空"
string stageText = breakoutLong or breakoutShort ? "结构突破预警" : "突破回踩确认"
string gradeHint = breakoutLong or breakoutShort ? "(突破时高周期评级)" : "(继承首次突破评级)"
alert(syminfo.ticker + " [" + timeframe.period + " / 背景" + (higherValid ? higherTF : "无") + "] " + gradeText + gradeHint + " " + sideText + " " + stageText + " | 收盘价: " + str.tostring(close, format.mintick), alert.freq_once_per_bar_close)
评论 (2)
感谢分享
不错,试试