市场冲击消化能力评判
这个是前段时间左侧做空市场的时候用的一个小东西,最近用不上了,拿出来分享。本体是对之前的那个盘中市场各种冲击分析的一个升级。美盘开盘时间用。
准确度判断的话,主要用在分母作用大过于分子的时候,在市场风险偏好扩大和企业盈利利好冲破估值的时候用不太上。
//@version=6
// 版权所有, 仅限授权学员本人使用, 未经许可不得转售或二次分发.
// 免责声明: 本工具仅供信息参考与教学演示, 不构成任何投资建议或买卖邀约.
// 所有读数为历史数据的统计整理, 不预测未来. 据此操作, 风险自负.
//==================================================
// 1. 输入
//==================================================
grpCore = "核心设置"
calcTf = input.timeframe("D", "计算周期", group=grpCore)
lb = input.int(1, "冲击观察窗(根)", minval=1, maxval=10, group=grpCore, tooltip="每次冲击量的是几根日K的变化. 大于1时相邻观察窗互相重叠, 破裂次数会重复计, 建议保持1.")
zLen = input.int(120, "z分母窗口(日)", minval=60, maxval=500, group=grpCore, tooltip="冲击和损伤都换算成零心 z: 当日变动直接除以最近这么多天的标准差, 不减均值. 不减均值是为了防止趋势期的均值漂移把顺势小动误标成冲击.")
vixPctLen = input.int(252, "波动水位分位窗口(日)", minval=100, group=grpCore)
grpTh = "门槛(初值, 未标定, 见头部 SA-1)"
zExam = input.float(1.0, "冲击认定线 z", minval=0.5, step=0.1, group=grpTh, tooltip="某条腿的变动达到这个稀有度才认定为一次冲击. 全栈稀有度词汇: 1.0 起算有意义.")
zBig = input.float(1.5, "强冲击线 z", minval=1.0, step=0.1, group=grpTh, tooltip="冲击达到这个稀有度算强冲击. 全栈稀有度词汇: 1.5 以上算异常.")
qStrain = input.float(0.5, "加剧线(损伤比)", minval=0.1, step=0.1, group=grpTh, tooltip="损伤比 = 股指损伤z 除以 冲击z的绝对值. 低于这条线判为接受冲击.")
qFail = input.float(1.0, "破裂线(损伤比)", minval=0.5, step=0.1, group=grpTh, tooltip="损伤比达到这条线判为破裂: 损伤不小于冲击本身, 市场在放大冲击而不是吸收.")
enter1 = input.int(3, "脆弱累积进场(20日破裂次数)", minval=1, group=grpTh)
enter2 = input.int(5, "高度脆弱进场(20日破裂次数)", minval=2, group=grpTh)
grpDur = "久期常量(需随最便宜可交割券定期核对)"
durZN = input.float(6.4, "ZN 久期", minval=3.0, step=0.1, group=grpDur)
durUB = input.float(22.0, "UB 久期", minval=15.0, step=0.5, group=grpDur)
grpSym = "符号"
symZN = input.symbol("CBOT:ZN1!", "10年期国债期货", group=grpSym)
symUB = input.symbol("CBOT:UB1!", "超长期国债期货", group=grpSym)
symDXY = input.symbol("TVC:DXY", "美元指数", group=grpSym)
symES = input.symbol("CME_MINI:ES1!", "ES 期货", group=grpSym)
symNQ = input.symbol("CME_MINI:NQ1!", "NQ 期货", group=grpSym)
symBTC = input.symbol("BITSTAMP:BTCUSD", "比特币", group=grpSym)
symGC = input.symbol("COMEX:GC1!", "黄金期货", group=grpSym)
symHYG = input.symbol("AMEX:HYG", "高收益债 HYG", group=grpSym)
symQual = input.symbol("AMEX:HYG/AMEX:LQD", "质量差(高收益比投资级)", group=grpSym)
symKRE = input.symbol("AMEX:KRE", "区域银行 KRE", group=grpSym)
symVIX = input.symbol("CBOE:VIX", "VIX", group=grpSym)
symMOVE = input.symbol("TVC:MOVE", "MOVE", group=grpSym)
//==================================================
// 2. 周期守卫(家族标准, 按时长比较)
//==================================================
tfOk = timeframe.in_seconds(timeframe.period) == timeframe.in_seconds(calcTf)
//==================================================
// 3. 数据请求: 统计全部在各符号自己的日线语境内算
// gaps_on: 该符号今天没出K线就整组返回缺数, 该腿弃权
// z 为零心口径: 变动除以标准差, 不减均值 (v2.1)
//==================================================
f_chgZ() =>
float chg = ta.roc(close, lb)
float sd = ta.stdev(chg, zLen)
float z = not na(chg) and not na(sd) and sd > 0 ? chg / sd : na
[chg, z]
f_bpZ(float dur) =>
float chgP = ta.roc(close, lb)
float bp = -chgP / dur * 100.0
float sd = ta.stdev(bp, zLen)
float z = not na(bp) and not na(sd) and sd > 0 ? bp / sd : na
[bp, z]
f_vix() =>
float chg = ta.roc(close, lb)
float sd = ta.stdev(chg, zLen)
float z = not na(chg) and not na(sd) and sd > 0 ? chg / sd : na
float pct = ta.percentrank(close, vixPctLen)
[chg, z, pct]
[znBp, znZ] = request.security(symZN, calcTf, f_bpZ(durZN), gaps=barmerge.gaps_on)
[ubBp, ubZ] = request.security(symUB, calcTf, f_bpZ(durUB), gaps=barmerge.gaps_on)
[dxyChg, dxyZ] = request.security(symDXY, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[esChg, esZ] = request.security(symES, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[nqChg, nqZ] = request.security(symNQ, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[btcChg, btcZ] = request.security(symBTC, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[gcChg, gcZ] = request.security(symGC, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[hygChg, hygZ] = request.security(symHYG, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[qualChg, qualZ] = request.security(symQual, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[kreChg, kreZ] = request.security(symKRE, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
[vixChg, vixZ, vixPct] = request.security(symVIX, calcTf, f_vix(), gaps=barmerge.gaps_on)
[moveChg, moveZ] = request.security(symMOVE, calcTf, f_chgZ(), gaps=barmerge.gaps_on)
//==================================================
// 4. 损伤量表: 股指伤得多重(零心z, 正=在跌)
// 某条腿缺数就用剩下的腿, 全缺才判没法评
//==================================================
float eqDmgZ = na(esZ) and na(nqZ) ? na : na(esZ) ? -nqZ : na(nqZ) ? -esZ : -(esZ + nqZ) / 2.0
//==================================================
// 5. 四条冲击腿: 判定挨打并记扛没扛住
// 判定代码: 0=无冲击 1=接受冲击 2=冲击加剧 3=破裂
// 利率与美元双边(急升急跌都认定冲击); 信用与波动单边 (v2.1)
//==================================================
f_grade(bool hit, float shockZ, float dmgZ) =>
int code = 0
float q = na
if hit
if na(dmgZ)
code := 0
else
q := dmgZ / math.abs(shockZ)
code := q >= qFail ? 3 : q >= qStrain ? 2 : 1
[code, q]
// 利率腿: ZN 与 UB 经久期换算 bp 当量取零心 z, 用绝对值更大的那条; 双边
bool rateAbstain = na(znZ) and na(ubZ)
float rateZ = rateAbstain ? na : na(ubZ) ? znZ : na(znZ) ? ubZ : (math.abs(ubZ) > math.abs(znZ) ? ubZ : znZ)
float rateBp = rateAbstain ? na : na(ubZ) ? znBp : na(znZ) ? ubBp : (math.abs(ubZ) > math.abs(znZ) ? ubBp : znBp)
bool rateLongEnd = not rateAbstain and not na(ubZ) and (na(znZ) or math.abs(ubZ) > math.abs(znZ))
bool rateHit = not rateAbstain and math.abs(rateZ) >= zExam
[rateCode, rateQ] = f_grade(rateHit, rateZ, eqDmgZ)
// 美元腿: 双边
bool dxyAbstain = na(dxyZ)
bool dxyHit = not dxyAbstain and math.abs(dxyZ) >= zExam
[dxyCode, dxyQ] = f_grade(dxyHit, dxyZ, eqDmgZ)
// 信用腿: HYG 自身被砸是冲击源; 单边, 信用大涨是顺风不认定冲击
bool credAbstain = na(hygZ)
bool credHit = not credAbstain and -hygZ >= zExam
[credCode, credQ] = f_grade(credHit, hygZ, eqDmgZ)
// 波动腿: VIX 上冲认定冲击; 单边, 波动大跌是顺风
bool volAbstain = na(vixZ)
bool volHit = not volAbstain and vixZ >= zExam
[volCode, volQ] = f_grade(volHit, vixZ, eqDmgZ)
//==================================================
// 6. 当日结论与20日记录
//==================================================
int dayCode = math.max(math.max(rateCode, dxyCode), math.max(credCode, volCode))
bool hitToday = dayCode >= 1
bool failToday = dayCode == 3
bool strainToday = dayCode == 2
bool bigHitToday = (rateHit and math.abs(rateZ) >= zBig) or (dxyHit and math.abs(dxyZ) >= zBig) or (credHit and -hygZ >= zBig) or (volHit and vixZ >= zBig)
bool bigFailToday = failToday and bigHitToday
float hit20 = math.sum(hitToday ? 1.0 : 0.0, 20)
float fail20 = math.sum(failToday ? 1.0 : 0.0, 20)
float strain20 = math.sum(strainToday ? 1.0 : 0.0, 20)
float rateFail20 = math.sum(rateCode == 3 ? 1.0 : 0.0, 20)
float dxyFail20 = math.sum(dxyCode == 3 ? 1.0 : 0.0, 20)
float credFail20 = math.sum(credCode == 3 ? 1.0 : 0.0, 20)
float volFail20 = math.sum(volCode == 3 ? 1.0 : 0.0, 20)
//==================================================
// 7. 大状态: 进出各有门槛, 不逐根翻牌
//==================================================
var int fragLevel = 0
int prevLevel = nz(fragLevel[1], 0)
fragLevel := fail20 >= enter2 ? 2 :
prevLevel == 2 and fail20 >= enter2 - 1 ? 2 :
fail20 >= enter1 ? 1 :
prevLevel >= 1 and fail20 >= enter1 - 1 ? 1 : 0
//==================================================
// 8. 指纹与旁证(不进状态机, 只显示和报警)
//==================================================
bool marginFingerprint = not na(gcZ) and not na(dxyZ) and not na(eqDmgZ) and gcZ <= -1.0 and dxyZ >= 0.5 and eqDmgZ >= 1.0
bool sellAmerica = rateHit and rateZ > 0 and dxyHit and dxyZ < 0 and not na(eqDmgZ) and eqDmgZ >= 1.0
bool btcCanary = not na(btcZ) and not na(eqDmgZ) and btcZ <= -1.0 and eqDmgZ < 0.5
//==================================================
// 8b. 当日冲击评分(0-100): 主观数值评比, 不定义状态 (v2.2)
//==================================================
int legsReg = (rateHit ? 1 : 0) + (dxyHit ? 1 : 0) + (credHit ? 1 : 0) + (volHit ? 1 : 0)
float maxAbsZ = na
maxAbsZ := rateHit ? math.abs(rateZ) : maxAbsZ
maxAbsZ := dxyHit and (na(maxAbsZ) or math.abs(dxyZ) > maxAbsZ) ? math.abs(dxyZ) : maxAbsZ
maxAbsZ := credHit and (na(maxAbsZ) or -hygZ > maxAbsZ) ? -hygZ : maxAbsZ
maxAbsZ := volHit and (na(maxAbsZ) or vixZ > maxAbsZ) ? vixZ : maxAbsZ
float qWorst = na
qWorst := rateHit and not na(rateQ) ? rateQ : qWorst
qWorst := dxyHit and not na(dxyQ) and (na(qWorst) or dxyQ > qWorst) ? dxyQ : qWorst
qWorst := credHit and not na(credQ) and (na(qWorst) or credQ > qWorst) ? credQ : qWorst
qWorst := volHit and not na(volQ) and (na(qWorst) or volQ > qWorst) ? volQ : qWorst
float ptsStrength = na(maxAbsZ) ? 0.0 : math.min(maxAbsZ, 2.5) / 2.5 * 40.0
float ptsQuality = na(qWorst) ? 0.0 : math.min(math.max(qWorst, 0.0), 1.5) / 1.5 * 40.0
float ptsBreadth = math.min(math.max(legsReg - 1, 0) * 7.0, 14.0)
float ptsSide = (marginFingerprint ? 5.0 : 0.0) + (sellAmerica ? 5.0 : 0.0)
int shockScore = hitToday ? int(math.round(math.min(ptsStrength + ptsQuality + ptsBreadth + ptsSide, 100.0))) : 0
//==================================================
// 9. 文字
//==================================================
f_codeText(int c) =>
c == 3 ? "破裂" : c == 2 ? "冲击加剧" : c == 1 ? "接受冲击" : "无冲击"
f_codeColor(int c) =>
c == 3 ? color.red : c == 2 ? color.orange : c == 1 ? color.green : color.gray
string liveMark = barstate.islast and not barstate.isconfirmed ? " (盘中)" : ""
string dayText = hitToday ? (bigHitToday ? "强冲击 · " : "") + f_codeText(dayCode) + " · " + str.tostring(shockScore, "#") + "分" : "无冲击"
color dayColor = hitToday ? f_codeColor(dayCode) : color.gray
string stateText = fragLevel == 2 ? "高度脆弱" : fragLevel == 1 ? "脆弱累积" : "吸收正常"
color stateColor = fragLevel == 2 ? color.red : fragLevel == 1 ? color.yellow : color.green
// 主软肋: 今天破裂的腿优先, 否则看20日谁破裂最多
f_weakLink() =>
string s = "无"
if rateCode == 3
s := "利率"
else if credCode == 3
s := "信用"
else if dxyCode == 3
s := "美元"
else if volCode == 3
s := "波动"
else
float m = math.max(math.max(rateFail20, credFail20), math.max(dxyFail20, volFail20))
if m > 0
s := rateFail20 == m ? "利率(20日)" : credFail20 == m ? "信用(20日)" : dxyFail20 == m ? "美元(20日)" : "波动(20日)"
s
string weakLink = f_weakLink()
string doText = fragLevel == 2 ? "降杠杆, 停止逢跌加仓, 等一次干净的接受冲击再谈修复" :
fragLevel == 1 ? "新仓减半, 只留高确信, 盯紧信用腿和银行腿" :
"按各机指令正常执行, 冲击日照常守低吸纪律"
string dontText = fragLevel == 2 ? "别假设逢跌买入还管用" :
fragLevel == 1 ? "别追高贝塔反弹, 别把单日反弹当修复" :
"别把单独一天的国债下跌过度解读"
string exitText = fragLevel == 2 ? "解除: 20日破裂次数回落到 " + str.tostring(enter2 - 2) + " 次以下" :
fragLevel == 1 ? "解除: 20日破裂次数回落到 " + str.tostring(enter1 - 2) + " 次以下" :
"升级: 20日破裂次数达到 " + str.tostring(enter1) + " 次"
//==================================================
// 10. 腿显示文字
//==================================================
f_legText(bool abstain, bool hit, string dirStr, float zSigned, float q, int code, string extra) =>
abstain ? "缺数弃权" :
not hit ? "无冲击 (z " + str.tostring(zSigned, "#.0") + ")" :
dirStr + " · z " + str.tostring(zSigned, "#.0") + extra + " · 损伤比 " + str.tostring(q, "#.0") + " · " + f_codeText(code)
string rateDir = rateHit ? (rateZ > 0 ? "利率急升" : "利率急跌") : ""
string dxyDir = dxyHit ? (dxyZ > 0 ? "美元急升" : "美元急跌") : ""
//==================================================
// 11. 悬浮窗文字
//==================================================
tipHead = "记账制: 每天先看市场有没有受到冲击, 某条腿的变动稀有度(z)过线才认定为冲击;\n认定冲击后再评吸收, 损伤比 = 股指损伤z 除以 冲击z的绝对值;\n无冲击的日子老实写无冲击, 因为无冲击的日子什么都证明不了.\n丑话: 所有门槛是初值, 未经回放标定(见源码头部 SA-1), 标定完成前把本表当参考, 不当指令."
tipState = "大状态看的是20日记录, 不逐日翻牌:\n20日内破裂达 " + str.tostring(enter1) + " 次进脆弱累积, 达 " + str.tostring(enter2) + " 次进高度脆弱;\n离场门槛各比进场低一档, 防止在门槛附近来回跳."
tipDay = "当日结论取四条腿里最差的判定, 后缀是当日冲击评分.\n接受冲击: 损伤远小于冲击(损伤比低于" + str.tostring(qStrain, "#.0") + "), 损伤比为负说明冲击当天不跌反涨;\n冲击加剧: 损伤逼近冲击(损伤比" + str.tostring(qStrain, "#.0") + "到" + str.tostring(qFail, "#.0") + ");\n破裂: 损伤不小于冲击(损伤比达" + str.tostring(qFail, "#.0") + "), 市场在放大而不是吸收.\n评分0到100, 主观数值评比, 只用来跨日比较严重程度, 不定义状态:\n冲击强度最多40(最强腿|z|按2.5封顶线性) + 吸收质量最多40(最差损伤比按1.5封顶线性, 为负记0)\n+ 多腿齐发最多14(每多一条腿加7) + 指纹最多10(保证金抛售与股债汇三杀各5).\n配方为人工设定, 未标定, 已并入 SA-1.\n带盘中标记时为实时预览, 收盘定稿."
tipRecord = "20日滚动记录: 受了几次冲击, 几次加剧, 几次破裂.\n窗口按图表K线根数数, 所以要求挂在日线上, 建议挂 ES 或 SPX."
tipWeak = "主软肋: 今天正在破裂的腿优先报告;\n今天没有破裂的, 报20日内破裂最多的腿(带20日字样).\n同分时按 利率 > 信用 > 美元 > 波动 的次序."
tipDo = "行动与解除条件成对出现, 家规.\n" + exitText
tipRate = "利率腿, 双边. ZN 和 UB 的价格变动先除以久期换算成收益率 bp 当量, 再各自取零心 z, 用绝对值更大的那条当冲击, 括号里标腹部或长端.\n急升是紧缩冲击在压资产; 急跌多半是增长恐慌或有东西在断裂, 一样要看股指的吸收质量:\n急跌当天股指无恙, 说明债市的动静是仓位问题; 急跌当天股指同跌, 恐慌得到确认.\n久期常量在设置里, 需随最便宜可交割券定期核对."
tipDxy = "美元腿, 双边. 美元指数变动取零心 z.\n急升是全球融资收紧在压资产; 急跌单独出现多半是顺风(宽松预期),\n但急跌配上股指受伤要警惕, 配上利率急升就是下面的股债汇三杀指纹.\n金价的日常回落不掺进这条腿, 金的问题看保证金抛售指纹."
tipCred = "信用腿, 单边. HYG 自身变动取零心 z, HYG 被砸才认定冲击; 信用大涨是顺风, 不构成冲击.\n旁边的质量差是 HYG 比 LQD 的比价变动 z: 负值越深, 垃圾债相对投资级越弱, 信用内部在分层.\n质量差只做背景, 不进判定."
tipVol = "波动腿, 单边. VIX 变动取零心 z, VIX 上冲才认定冲击; 波动大跌是顺风.\n水位是 VIX 现值在过去一年里的分位: 高水位下的冲击和低水位下的冲击, 含义不同, 自己掂量.\n水位只做背景, 不进判定."
tipDmg = "损伤量表: ES 和 NQ 各自的当日变动零心 z 取平均再取负, 正值代表在跌、跌得越稀有数值越大.\n一条缺数就用另一条, 两条全缺当天无法判定.\n四条冲击腿共用这一把尺."
tipMargin = "保证金抛售指纹: 金跌(z低于-1) + 股跌(损伤z高于1) + 美元走强(z高于0.5) 同时出现.\n连避险资产都在被卖换现金, 这是被动去杠杆的指纹, 不是普通的risk-off.\n只显示和报警, 不进状态机."
tipSellUs = "股债汇三杀: 利率急升(国债被卖) + 美元急跌 + 股指受伤(损伤z高于1) 同时出现.\n三个本该互相对冲的资产一起跌, 是资金整体离开美元资产的指纹, 比单腿冲击严重一个量级.\n只显示和报警, 不进状态机."
tipBtc = "BTC金丝雀: BTC 按自身波动零心 z 明显转弱(z低于-1), 而股指基本没事(损伤z低于0.5).\n全天候交易的高贝塔资产先失血, 有时是周末和盘外风险偏好变化的前哨.\n只显示和报警, 不进状态机."
tipKre = "银行腿 KRE 当日变动零心 z. 资金面和银行压力的老证人, 信用腿破裂时先看它有没有同步受创."
tipMove = "债券波动 MOVE 当日变动零心 z. 债市的恐慌表. 该数据源缺数是常事, 缺数时此行显示弃权, 不影响任何计算."
//==================================================
// 12. 面板
//==================================================
var table t = table.new(position.top_right, 2, 18, border_width=1)
if barstate.islast
if not tfOk
table.clear(t, 0, 0, 1, 17)
table.cell(t, 0, 0, "图表周期与计算周期不一致\n本机按日K根数计窗口, 当前读数不可信\n请把图表切换到日线再看", text_color=color.white, bgcolor=color.new(color.red, 10))
else
table.cell(t, 0, 0, "总状态", text_color=color.white, bgcolor=color.new(stateColor, 30), tooltip=tipState)
table.cell(t, 1, 0, stateText + liveMark, text_color=color.white, bgcolor=color.new(stateColor, 55), tooltip=tipState)
table.cell(t, 0, 1, "今日结论", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipDay)
table.cell(t, 1, 1, dayText + liveMark, text_color=color.white, bgcolor=color.new(dayColor, 55), tooltip=tipDay)
table.cell(t, 0, 2, "主软肋", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipWeak)
table.cell(t, 1, 2, weakLink, text_color=color.white, bgcolor=color.new(color.black, 30), tooltip=tipWeak)
table.cell(t, 0, 3, "20日记录", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipRecord)
table.cell(t, 1, 3, "冲击 " + str.tostring(hit20, "#") + " · 加剧 " + str.tostring(strain20, "#") + " · 破裂 " + str.tostring(fail20, "#"), text_color=color.white, bgcolor=color.new(color.black, 30), tooltip=tipRecord)
table.cell(t, 0, 4, "做", text_color=color.white, bgcolor=color.new(color.green, 60), tooltip=tipDo)
table.cell(t, 1, 4, doText, text_color=color.white, bgcolor=color.new(color.green, 80), tooltip=tipDo)
table.cell(t, 0, 5, "别做", text_color=color.white, bgcolor=color.new(color.red, 60), tooltip=tipDo)
table.cell(t, 1, 5, dontText, text_color=color.white, bgcolor=color.new(color.red, 80), tooltip=tipDo)
table.merge_cells(t, 0, 6, 1, 6)
table.cell(t, 0, 6, "四条冲击腿 · 今日", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipHead)
table.cell(t, 0, 7, "利率腿", text_color=color.white, bgcolor=color.new(f_codeColor(rateCode), 60), tooltip=tipRate)
table.cell(t, 1, 7, f_legText(rateAbstain, rateHit, rateDir, rateZ, rateQ, rateCode, rateAbstain ? "" : " (" + (rateBp >= 0 ? "+" : "") + str.tostring(rateBp, "#.0") + "bp " + (rateLongEnd ? "长端" : "腹部") + ")"), text_color=color.white, bgcolor=color.new(f_codeColor(rateCode), 75), tooltip=tipRate)
table.cell(t, 0, 8, "美元腿", text_color=color.white, bgcolor=color.new(f_codeColor(dxyCode), 60), tooltip=tipDxy)
table.cell(t, 1, 8, f_legText(dxyAbstain, dxyHit, dxyDir, dxyZ, dxyQ, dxyCode, dxyAbstain ? "" : " (" + str.tostring(dxyChg, "#.00") + "%)"), text_color=color.white, bgcolor=color.new(f_codeColor(dxyCode), 75), tooltip=tipDxy)
table.cell(t, 0, 9, "信用腿", text_color=color.white, bgcolor=color.new(f_codeColor(credCode), 60), tooltip=tipCred)
table.cell(t, 1, 9, f_legText(credAbstain, credHit, "信用承压", hygZ, credQ, credCode, "") + (na(qualZ) ? "" : " · 质量差 z " + str.tostring(qualZ, "#.0")), text_color=color.white, bgcolor=color.new(f_codeColor(credCode), 75), tooltip=tipCred)
table.cell(t, 0, 10, "波动腿", text_color=color.white, bgcolor=color.new(f_codeColor(volCode), 60), tooltip=tipVol)
table.cell(t, 1, 10, f_legText(volAbstain, volHit, "波动骤升", vixZ, volQ, volCode, "") + (na(vixPct) ? "" : " · 水位 " + str.tostring(vixPct, "#") + "分位"), text_color=color.white, bgcolor=color.new(f_codeColor(volCode), 75), tooltip=tipVol)
table.merge_cells(t, 0, 11, 1, 11)
table.cell(t, 0, 11, "损伤与旁证", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipHead)
table.cell(t, 0, 12, "股指损伤", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipDmg)
table.cell(t, 1, 12, na(eqDmgZ) ? "缺数, 今日无法判定" : "损伤 z " + str.tostring(eqDmgZ, "#.0") + " (ES " + (na(esZ) ? "弃权" : str.tostring(esZ, "#.0")) + " / NQ " + (na(nqZ) ? "弃权" : str.tostring(nqZ, "#.0")) + ")", text_color=color.white, bgcolor=color.new(color.black, 30), tooltip=tipDmg)
table.cell(t, 0, 13, "保证金抛售指纹", text_color=color.white, bgcolor=color.new(marginFingerprint ? color.red : color.black, marginFingerprint ? 40 : 0), tooltip=tipMargin)
table.cell(t, 1, 13, marginFingerprint ? "出现: 金 z " + str.tostring(gcZ, "#.0") + " · 美元 z " + str.tostring(dxyZ, "#.0") : "未出现", text_color=color.white, bgcolor=color.new(marginFingerprint ? color.red : color.black, marginFingerprint ? 65 : 30), tooltip=tipMargin)
table.cell(t, 0, 14, "股债汇三杀", text_color=color.white, bgcolor=color.new(sellAmerica ? color.red : color.black, sellAmerica ? 40 : 0), tooltip=tipSellUs)
table.cell(t, 1, 14, sellAmerica ? "出现: 利率急升 + 美元急跌 + 股指受伤" : "未出现", text_color=color.white, bgcolor=color.new(sellAmerica ? color.red : color.black, sellAmerica ? 65 : 30), tooltip=tipSellUs)
table.cell(t, 0, 15, "BTC金丝雀", text_color=color.white, bgcolor=color.new(btcCanary ? color.orange : color.black, btcCanary ? 40 : 0), tooltip=tipBtc)
table.cell(t, 1, 15, na(btcZ) ? "缺数弃权" : btcCanary ? "报警: BTC z " + str.tostring(btcZ, "#.0") + " 而股指无恙" : "平静 (z " + str.tostring(btcZ, "#.0") + ")", text_color=color.white, bgcolor=color.new(btcCanary ? color.orange : color.black, btcCanary ? 65 : 30), tooltip=tipBtc)
table.cell(t, 0, 16, "银行腿 KRE", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipKre)
table.cell(t, 1, 16, na(kreZ) ? "缺数弃权" : "z " + str.tostring(kreZ, "#.0"), text_color=color.white, bgcolor=color.new(color.black, 30), tooltip=tipKre)
table.cell(t, 0, 17, "债券波动 MOVE", text_color=color.white, bgcolor=color.new(color.black, 0), tooltip=tipMove)
table.cell(t, 1, 17, na(moveZ) ? "缺数弃权" : "z " + str.tostring(moveZ, "#.0"), text_color=color.white, bgcolor=color.new(color.black, 30), tooltip=tipMove)
//==================================================
// 13. 画线: 有界的账目计数, 不画无界比值
//==================================================
plot(tfOk and hitToday ? shockScore : na, "当日冲击评分", color=color.new(dayColor, 25), linewidth=1, style=plot.style_columns)
plot(tfOk ? fail20 : na, "20日破裂次数", color=stateColor, linewidth=2, style=plot.style_stepline)
plot(tfOk ? strain20 : na, "20日加剧次数", color=color.new(color.orange, 60), linewidth=1, style=plot.style_stepline)
hline(enter1, "脆弱累积进场线", color=color.yellow)
hline(enter2, "高度脆弱进场线", color=color.red)
//==================================================
// 14. 警报(全部经周期守卫门控; 使用时一律设成K线收盘触发)
//==================================================
alertcondition(tfOk and fragLevel == 1 and nz(fragLevel[1]) < 1, "进入脆弱累积", "冲击吸收: 20日破裂次数达到进场线, 进入脆弱累积。")
alertcondition(tfOk and fragLevel == 2 and nz(fragLevel[1]) < 2, "进入高度脆弱", "冲击吸收: 进入高度脆弱, 降杠杆, 停止逢跌加仓。")
alertcondition(tfOk and fragLevel == 0 and nz(fragLevel[1]) > 0, "回到吸收正常", "冲击吸收: 记录修复, 回到吸收正常。")
alertcondition(tfOk and failToday and not failToday[1], "今日破裂", "冲击吸收: 今日判定破裂, 看主软肋是哪条腿。")
alertcondition(tfOk and bigFailToday and not bigFailToday[1], "强冲击破裂", "冲击吸收: 强冲击(z达1.5)判定破裂, 高优先级。")
alertcondition(tfOk and marginFingerprint and not marginFingerprint[1], "保证金抛售指纹", "冲击吸收: 金股同跌加美元走强, 被动去杠杆指纹出现。")
alertcondition(tfOk and sellAmerica and not sellAmerica[1], "股债汇三杀", "冲击吸收: 利率急升加美元急跌加股指受伤, 资金离开美元资产的指纹出现。")
alertcondition(tfOk and btcCanary and not btcCanary[1], "BTC金丝雀报警", "冲击吸收: BTC领先转弱而股指未动, 留意盘外风险偏好。")

