# Calendar Quality Score
# Hybrid TOS Version
# Use on UNDERLYING chart, not option chart
declare lower;
input frontIV = 25.0;
input backIV = 22.0;
input backIVRank = 35.0;
input strikePrice = 6000.0;
input frontDTE = 7;
input backDTE = 21;
input ivLookback = 252;
input atrLength = 14;
input bbLength = 20;
input adxLength = 14;
input eventRisk =
{default No_Event, Event_After_Front_Expiration, Event_During_Trade};
input showLabels = yes;
# ----------------------------
# Manual IV Structure
# ----------------------------
def termSpread = frontIV - backIV;
def termScore =
if termSpread >= 5 then 40
else if termSpread >= 3 then 32
else if termSpread >= 1 then 24
else if termSpread >= 0 then 12
else 0;
def backIVScore =
if backIVRank < 30 then 25
else if backIVRank < 50 then 20
else if backIVRank < 70 then 10
else 0;
# ----------------------------
# Expected Move / Strike Placement
# ----------------------------
def expectedMove = close * (backIV / 100) * Sqrt(backDTE / 365);
def strikeDistance = AbsValue(close - strikePrice);
def distPctEM = if expectedMove != 0 then strikeDistance / expectedMove else 999;
def strikeScore =
if distPctEM <= 0.10 then 20
else if distPctEM <= 0.25 then 15
else if distPctEM <= 0.50 then 8
else 0;
# ----------------------------
# Event Risk
# ----------------------------
def eventScore =
if eventRisk == eventRisk.No_Event then 15
else if eventRisk == eventRisk.Event_After_Front_Expiration then 8
else 0;
# ----------------------------
# Regime / Realized Volatility
# ----------------------------
def atr = Average(TrueRange(high, close, low), atrLength);
def atrPct = atr / close * 100;
def bbMid = Average(close, bbLength);
def bbDev = StDev(close, bbLength);
def bbUpper = bbMid + 2 * bbDev;
def bbLower = bbMid - 2 * bbDev;
def bbWidth = if bbMid != 0 then (bbUpper - bbLower) / bbMid * 100 else 0;
def bbWidthAvg = Average(bbWidth, 50);
def compression = bbWidth < bbWidthAvg;
def adx = ADX(adxLength);
def ema21 = ExpAverage(close, 21);
def ema50 = ExpAverage(close, 50);
def controlledTrend =
close > ema21 and ema21 > ema50 and adx >= 15 and adx <= 28;
def expansionRisk =
adx > 30 and bbWidth > bbWidthAvg;
def regimeBonus =
if compression then 10
else if controlledTrend then 7
else if expansionRisk then 0
else 5;
# ----------------------------
# Final Score
# ----------------------------
def baseScore = termScore + backIVScore + strikeScore + eventScore;
def finalScore = baseScore + regimeBonus;
plot CalendarScore = finalScore;
CalendarScore.SetLineWeight(3);
CalendarScore.AssignValueColor(
if finalScore >= 90 then Color.GREEN
else if finalScore >= 80 then Color.LIGHT_GREEN
else if finalScore >= 70 then Color.YELLOW
else if finalScore >= 60 then Color.ORANGE
else Color.RED
);
plot PassLine = 60;
PassLine.SetDefaultColor(Color.GRAY);
PassLine.SetStyle(Curve.SHORT_DASH);
plot GoodLine = 80;
GoodLine.SetDefaultColor(Color.DARK_GREEN);
GoodLine.SetStyle(Curve.SHORT_DASH);
# ----------------------------
# Labels
# ----------------------------
def gradeAPlus = finalScore >= 90;
def gradeA = finalScore >= 80 and finalScore < 90;
def gradeB = finalScore >= 70 and finalScore < 80;
def gradeC = finalScore >= 60 and finalScore < 70;
AddLabel(showLabels,
"CALENDAR SCORE: " + Round(finalScore, 0),
if finalScore >= 80 then Color.GREEN
else if finalScore >= 70 then Color.YELLOW
else if finalScore >= 60 then Color.ORANGE
else Color.RED
);
AddLabel(showLabels,
"GRADE: " +
if gradeAPlus then "A+"
else if gradeA then "A"
else if gradeB then "B"
else if gradeC then "C"
else "PASS",
if finalScore >= 80 then Color.GREEN
else if finalScore >= 70 then Color.YELLOW
else if finalScore >= 60 then Color.ORANGE
else Color.RED
);
AddLabel(showLabels,
"TERM SPREAD: " + Round(termSpread, 2) + " pts",
if termSpread > 3 then Color.GREEN
else if termSpread >= 0 then Color.YELLOW
else Color.RED
);
AddLabel(showLabels,
"BACK IV RANK: " + Round(backIVRank, 0),
if backIVRank < 50 then Color.GREEN
else if backIVRank < 70 then Color.YELLOW
else Color.RED
);
AddLabel(showLabels,
"EXPECTED MOVE: " + Round(expectedMove, 2),
Color.CYAN
);
AddLabel(showLabels,
"STRIKE DIST: " + Round(strikeDistance, 2) +
" / " + Round(distPctEM * 100, 0) + "% EM",
if distPctEM <= 0.25 then Color.GREEN
else if distPctEM <= 0.50 then Color.YELLOW
else Color.RED
);
AddLabel(showLabels,
"REGIME: " +
if compression then "COMPRESSION"
else if controlledTrend then "CONTROLLED TREND"
else if expansionRisk then "EXPANSION RISK"
else "BALANCED",
if compression or controlledTrend then Color.GREEN
else if expansionRisk then Color.RED
else Color.YELLOW
);
AddLabel(showLabels,
"ACTION: " +
if finalScore >= 80 then "CALENDAR OK"
else if finalScore >= 70 then "SMALL / CAUTION"
else "PASS",
if finalScore >= 80 then Color.GREEN
else if finalScore >= 70 then Color.YELLOW
else Color.RED
);