OPEN-SOURCE SCRIPT
Forward P/E: Price-Reactive and Reported [Pro]

How to Interpret the Forward P/E: Price-Reactive and Reported [Pro] Indicator and Table:
What this indicator does
This indicator compares two versions of forward P/E:
Term
Meaning
Reactive
Daily price-adjusted forward P/E using the latest anchored forward EPS
Reported
TradingView’s reported forward P/E from its financial data source
The chart itself plots the Reactive Forward P/E, while the table shows both Reactive / Reported valuation context.
The main question it answers is:
Has the stock’s valuation moved too far compared with its own history?
Best default settings
Setting
Suggested
Financial Period Mode
Auto FY then FQ
Historical Lookback
3 Years or 5 Years
Require Full Lookback History
Off
Minimum Valid Bars
63 or 126
Smoothing Type
EMA
Smoothing Length
10
Signal Length
20
Percentile / Z-Score Source
Raw Price-Reactive P/E
Show Reported Line
Off
Show Reactive Adaptive Bands
On
Fill Extreme Zones
On
For newer stocks, use 6 Months, 1 Year, or 2 Years.
Main table readings
Reactive / Reported P/E
Example:
42.5x / 38.9x
This means:
Side
Meaning
Reactive P/E
Price-adjusted forward P/E based on current price
Reported P/E
TradingView’s reported forward P/E
If Reactive P/E is much higher than Reported P/E, the stock price may have run up since the last reported valuation update.
Smoothed Reactive P/E
This is the smoother plotted value.
Use it to read the trend, not the exact current valuation.
Reading
Best use
Reactive P/E
Current valuation estimate
Smoothed Reactive P/E
Cleaner visual trend
Anchored Forward EPS
This is the estimated forward EPS base used to calculate the Reactive P/E.
Reactive P/E = Current Price / Anchored Forward EPS
If the anchored EPS is stale, the Reactive P/E may still move daily with price, but the earnings base may be outdated.
Reactive / Reported Rank
This is one of the most important rows.
Example:
91% / 78%
Side
Meaning
Reactive Rank
Where the price-adjusted P/E ranks versus its own history
Reported Rank
Where the reported P/E ranks versus its own history
Interpretation:
Percentile Rank
Meaning
95%+
Extremely expensive
90–95%
Expensive / stretched
75–90%
Above-normal valuation
25–75%
Normal valuation range
10–25%
Below-normal valuation
5–10%
Historically cheap
Below 5%
Deep valuation discount
A major warning setup is:
Reactive Rank above 90%
Reported Rank much lower
That can mean price has moved ahead of the last reported valuation update.
Reactive / Reported Median
Example:
31.0x / 29.5x
This shows the stock’s historical “normal” P/E level for each method.
Use this to see whether the current valuation is above or below its own normal range.
Reactive / Reported Premium
Example:
+37% / +28%
This means valuation is above its historical median.
Reading
Meaning
+50%
Very rich versus normal
+20%
Above normal
0%
Near median valuation
-20%
Below normal
-40%
Potentially cheap
This is one of the easiest rows to use.
Reactive / Reported P95
This shows each method’s historical expensive zone.
Example:
52x / 48x
If the Reactive P/E is near or above its P95, the stock is historically expensive based on current price movement.
Reactive / Reported P05
This shows each method’s historical cheap zone.
Example:
18x / 16x
If the Reactive P/E is near or below P05, the stock is historically cheap versus its own valuation history.
Reactive / Reported Yield
This converts forward P/E into earnings yield.
Forward Earnings Yield = 100 / Forward P/E
Examples:
P/E
Earnings Yield
10x
10.0%
20x
5.0%
40x
2.5%
80x
1.25%
Lower yield means richer valuation.
Reactive Room to P95
This shows how much room remains before the Reactive P/E reaches its historical expensive zone.
Reading
Meaning
Positive
Still below historical expensive extreme
Near zero
Near historical expensive extreme
Negative
Already above historical expensive extreme
Example:
Reactive Room to P95: -3.5x
The stock is already above its historical 95th percentile valuation level.
Reactive Room above P05
This shows how far the Reactive P/E is above its historical cheap zone.
Reading
Meaning
Large positive
Not cheap
Near zero
Near historical cheap zone
Negative
Below historical cheap extreme
EPS / Multiple Trend
This shows whether the anchored EPS base and valuation multiple are rising, falling, or stable.
Reading
Meaning
Rising / Expansion
EPS anchor rising and multiple rising
Rising / Compression
EPS anchor rising while valuation gets cheaper
Falling / Expansion
EPS anchor falling while valuation rises — riskier
Falling / Compression
EPS anchor falling and multiple falling
Stable / Stable
Little change
Most concerning:
Falling / Expansion
That means investors are paying a higher multiple while the earnings base is worsening.
Driver
This explains what is mostly driving the valuation move.
Driver
Meaning
Price-Led Multiple Expansion
Price is pushing valuation higher
Price-Led Multiple Compression
Price decline is compressing valuation
EPS Anchor Improved
Earnings base improved
EPS Anchor Deteriorated
Earnings base worsened
Stable
No major change
For overextension, watch for:
Price-Led Multiple Expansion
especially when Reactive Rank is above 90%.
PEG / Fwd P/S
These are supplemental checks.
Metric
Use
PEG
P/E adjusted for growth
Forward P/S
Useful when earnings are unstable or distorted
A high P/E is less concerning if growth is strong and PEG is reasonable. A high P/E with a high PEG is more concerning.
Data Quality
This row tells you whether the indicator has enough usable history.
Message
Meaning
Full reactive history
Most reliable
Limited reactive history
Usable, but be cautious
Too little reactive history
Percentiles are weak
No valid reported P/E
TradingView does not have usable forward P/E
Bars since anchor
How long since the reported P/E last updated
If “bars since anchor” is very high, the Reactive P/E is still useful for price movement, but the earnings base may be stale.
Status readings
Reactive Status
This is the main status because the chart is price-reactive.
Status
Meaning
Extreme Expensive
Reactive Rank 95%+
Expensive / Stretched
Reactive Rank 90–95%
Above Normal Valuation
Reactive Rank 75–90%
Normal Valuation Range
Reactive Rank 25–75%
Below Normal Valuation
Reactive Rank 10–25%
Historically Cheap
Reactive Rank 5–10%
Deep Valuation Discount
Reactive Rank below 5%
Limited / Building History
Not enough data yet
Reported Status
This is the slower TradingView-reported valuation status.
Use it as confirmation.
Best warning setup:
Reactive Status = Expensive / Stretched
Reported Status = Normal Valuation Range
That can mean the stock price has moved ahead of the last reported valuation update.
How to use it practically
Avoid chasing
Be cautious when you see:
Reactive Rank above 90%
Reactive Premium strongly positive
Reactive Room to P95 near zero or negative
Driver = Price-Led Multiple Expansion
That means valuation is stretched mainly because price has moved up.
Look for possible value
Look closer when you see:
Reactive Rank below 10%
Reactive Premium negative
Reactive Room above P05 near zero or negative
Reported Status also cheap
That means the stock may be cheap versus its own history.
Best long setup
The healthier setup is often:
Reactive Rank 25–75%
EPS trend Rising
Multiple Stable or Compressing
Price trend improving
That means the stock may not be overextended, and the earnings base may be improving.
Main takeaway
Use this indicator as a stock-specific valuation stretch tool.
The most important rows are:
Reactive / Reported P/E
Reactive / Reported Rank
Reactive / Reported Premium
Reactive Room to P95
Reactive Status
Reported Status
Data Quality
The key idea:
Reactive = what valuation looks like today after price movement.
Reported = what TradingView’s financial data currently reports.
Here is the source code:
//version=6
indicator("Price-Reactive Forward P/E Extremes [Combined]", shorttitle="PRFPE+", overlay=false, max_bars_back=3000)
//====================================================
// INPUTS
//====================================================
grp1 = "Forward Valuation Settings"
financialMode = input.string("Auto FY then FQ", "Financial Period Mode", options=["Auto FY then FQ", "Auto FQ then FY", "FY", "FQ"], group=grp1)
minValidPE = input.float(0.1, "Minimum Valid Forward P/E", minval=0.0, step=0.1, group=grp1)
maxValidPE = input.float(500.0, "Maximum Valid Forward P/E", minval=1.0, step=5.0, group=grp1)
grp2 = "Historical Lookback Settings"
lookbackChoice = input.string("5 Years", "Historical Lookback", options=["6 Months", "1 Year", "2 Years", "3 Years", "4 Years", "5 Years"], group=grp2)
requireFullHistory = input.bool(false, "Require Full Lookback History?", group=grp2)
minValidBars = input.int(63, "Minimum Valid Bars in Limited-History Mode", minval=20, maxval=1260, group=grp2)
usePriorHistoryOnly = input.bool(true, "Rank Current Reading vs Prior History Only?", group=grp2)
grp3 = "Smoothing Settings"
smoothType = input.string("EMA", "Price-Reactive P/E Smoothing Type", options=["None", "SMA", "EMA", "RMA"], group=grp3)
smoothLen = input.int(10, "Smoothing Length", minval=1, group=grp3)
signalLength = input.int(20, "Signal Line Length", minval=1, group=grp3)
rankSource = input.string("Raw Price-Reactive P/E", "Percentile / Z-Score Source", options=["Raw Price-Reactive P/E", "Smoothed Price-Reactive P/E"], group=grp3)
grp4 = "EPS / Multiple Trend Settings"
epsTrendLen = input.int(63, "EPS / Multiple Trend Lookback", minval=1, group=grp4)
trendThreshold = input.float(2.5, "Trend Threshold %", minval=0.0, step=0.5, group=grp4)
grp5 = "Visuals & UI"
showHistogram = input.bool(false, "Show Price-Reactive P/E Histogram?", group=grp5)
showReportedLine = input.bool(false, "Show Reported Forward P/E Step Line?", group=grp5)
showAdaptiveBands = input.bool(true, "Show Reactive Adaptive Percentile Bands?", group=grp5)
showZoneFills = input.bool(true, "Fill Extreme Zones?", group=grp5)
showFixedBands = input.bool(false, "Show Fixed Forward P/E Bands?", group=grp5)
showTable = input.bool(true, "Show Current Reading Table?", group=grp5)
showBgHighlight = input.bool(false, "Highlight Extreme Background?", group=grp5)
fixedUpper1 = input.float(30.0, "Fixed Upper Band 1", step=0.5, group=grp5)
fixedUpper2 = input.float(45.0, "Fixed Upper Band 2", step=0.5, group=grp5)
fixedLower1 = input.float(15.0, "Fixed Lower Band 1", step=0.5, group=grp5)
fixedLower2 = input.float(10.0, "Fixed Lower Band 2", step=0.5, group=grp5)
//====================================================
// LOOKBACK CONVERSION
//====================================================
historyBars = lookbackChoice == "6 Months" ? 126 : lookbackChoice == "1 Year" ? 252 : lookbackChoice == "2 Years" ? 504 : lookbackChoice == "3 Years" ? 756 : lookbackChoice == "4 Years" ? 1008 : 1260
requiredBars = requireFullHistory ? historyBars : minValidBars
//====================================================
// REPORTED FINANCIAL DATA
//====================================================
peFY = request.financial(syminfo.tickerid, "PRICE_EARNINGS_FORWARD", "FY", barmerge.gaps_off, true)
peFQ = request.financial(syminfo.tickerid, "PRICE_EARNINGS_FORWARD", "FQ", barmerge.gaps_off, true)
pegFY = request.financial(syminfo.tickerid, "PEG_RATIO", "FY", barmerge.gaps_off, true)
pegFQ = request.financial(syminfo.tickerid, "PEG_RATIO", "FQ", barmerge.gaps_off, true)
psFY = request.financial(syminfo.tickerid, "PRICE_SALES_FORWARD", "FY", barmerge.gaps_off, true)
psFQ = request.financial(syminfo.tickerid, "PRICE_SALES_FORWARD", "FQ", barmerge.gaps_off, true)
f_valid_pe(_v) =>
not na(_v) and _v >= minValidPE and _v <= maxValidPE ? _v : na
validPEFY = f_valid_pe(peFY)
validPEFQ = f_valid_pe(peFQ)
float reportedPE = na
string effectivePeriod = "n/a"
if financialMode == "FY"
reportedPE := validPEFY
effectivePeriod := "FY"
else if financialMode == "FQ"
reportedPE := validPEFQ
effectivePeriod := "FQ"
else if financialMode == "Auto FY then FQ"
if not na(validPEFY)
reportedPE := validPEFY
effectivePeriod := "FY"
else
reportedPE := validPEFQ
effectivePeriod := "FQ"
else
if not na(validPEFQ)
reportedPE := validPEFQ
effectivePeriod := "FQ"
else
reportedPE := validPEFY
effectivePeriod := "FY"
pegRatioData = effectivePeriod == "FY" ? pegFY : effectivePeriod == "FQ" ? pegFQ : na
forwardPSData = effectivePeriod == "FY" ? psFY : effectivePeriod == "FQ" ? psFQ : na
//====================================================
// PRICE-REACTIVE FORWARD P/E
//====================================================
reportedUpdated = not na(reportedPE) and (na(reportedPE[1]) or reportedPE != reportedPE[1])
var float anchoredForwardEPS = na
if reportedUpdated and reportedPE > 0.0
anchoredForwardEPS := close / reportedPE
else
anchoredForwardEPS := anchoredForwardEPS[1]
priceReactivePE = not na(anchoredForwardEPS) and anchoredForwardEPS > 0.0 ? close / anchoredForwardEPS : na
barsSinceReportedUpdate = ta.barssince(reportedUpdated)
//====================================================
// SMOOTHING
//====================================================
f_smooth(_src, _len) =>
if smoothType == "None" or _len <= 1
_src
else if smoothType == "SMA"
ta.sma(_src, _len)
else if smoothType == "EMA"
ta.ema(_src, _len)
else
ta.rma(_src, _len)
smoothedReactivePE = f_smooth(priceReactivePE, smoothLen)
rankReactivePE = rankSource == "Raw Price-Reactive P/E" ? priceReactivePE : smoothedReactivePE
signalLine = f_smooth(smoothedReactivePE, signalLength)
//====================================================
// HISTORICAL STATS FUNCTIONS
//====================================================
f_array_percentile(_arr, _pct) =>
int _n = array.size(_arr)
float _result = na
if _n > 0
float _pos = (_pct / 100.0) * (_n - 1)
int _lo = int(math.floor(_pos))
int _hi = int(math.ceil(_pos))
float _weight = _pos - _lo
float _loVal = array.get(_arr, _lo)
float _hiVal = array.get(_arr, _hi)
_result := _loVal + ((_hiVal - _loVal) * _weight)
_result
f_hist_stats(_series, _x, _len, _priorOnly) =>
array<float> _values = array.new_float(0)
float _sum = 0.0
float _sumSq = 0.0
int _start = _priorOnly ? 1 : 0
int _finish = _priorOnly ? _len : _len - 1
for i = _start to _finish
_v = _series
if not na(_v)
array.push(_values, _v)
_sum += _v
_sumSq += _v * _v
int _n = array.size(_values)
float _p95 = na
float _p90 = na
float _p75 = na
float _p50 = na
float _p25 = na
float _p10 = na
float _p05 = na
float _rank = na
float _mean = na
float _stdev = na
if _n > 0
array.sort(_values, order.ascending)
_p95 := f_array_percentile(_values, 95)
_p90 := f_array_percentile(_values, 90)
_p75 := f_array_percentile(_values, 75)
_p50 := f_array_percentile(_values, 50)
_p25 := f_array_percentile(_values, 25)
_p10 := f_array_percentile(_values, 10)
_p05 := f_array_percentile(_values, 5)
if not na(_x)
float _countBelowOrEqual = 0.0
for j = 0 to _n - 1
if array.get(_values, j) <= _x
_countBelowOrEqual += 1.0
_rank := (_countBelowOrEqual / _n) * 100.0
_mean := _sum / _n
if _n > 1
float _variance = (_sumSq - ((_sum * _sum) / _n)) / (_n - 1)
_stdev := _variance >= 0.0 ? math.sqrt(_variance) : na
[_p95, _p90, _p75, _p50, _p25, _p10, _p05, _rank, _mean, _stdev, _n * 1.0]
//====================================================
// REACTIVE HISTORICAL STATS
//====================================================
[rP95Raw, rP90Raw, rP75Raw, rP50Raw, rP25Raw, rP10Raw, rP05Raw, rRankRaw, rAvgRaw, rStdevRaw, reactiveHistoryCount] = f_hist_stats(rankReactivePE, rankReactivePE, historyBars, usePriorHistoryOnly)
hasEnoughReactiveHistory = not na(priceReactivePE) and reactiveHistoryCount >= requiredBars
rP95 = hasEnoughReactiveHistory ? rP95Raw : na
rP90 = hasEnoughReactiveHistory ? rP90Raw : na
rP75 = hasEnoughReactiveHistory ? rP75Raw : na
rP50 = hasEnoughReactiveHistory ? rP50Raw : na
rP25 = hasEnoughReactiveHistory ? rP25Raw : na
rP10 = hasEnoughReactiveHistory ? rP10Raw : na
rP05 = hasEnoughReactiveHistory ? rP05Raw : na
reactivePercentRank = hasEnoughReactiveHistory ? rRankRaw : na
reactiveZScore = hasEnoughReactiveHistory and not na(rStdevRaw) and rStdevRaw != 0.0 ? (rankReactivePE - rAvgRaw) / rStdevRaw : na
//====================================================
// REPORTED HISTORICAL STATS
//====================================================
[fP95Raw, fP90Raw, fP75Raw, fP50Raw, fP25Raw, fP10Raw, fP05Raw, fRankRaw, fAvgRaw, fStdevRaw, reportedHistoryCount] = f_hist_stats(reportedPE, reportedPE, historyBars, usePriorHistoryOnly)
hasEnoughReportedHistory = not na(reportedPE) and reportedHistoryCount >= requiredBars
reportedP95 = hasEnoughReportedHistory ? fP95Raw : na
reportedP90 = hasEnoughReportedHistory ? fP90Raw : na
reportedP75 = hasEnoughReportedHistory ? fP75Raw : na
reportedP50 = hasEnoughReportedHistory ? fP50Raw : na
reportedP25 = hasEnoughReportedHistory ? fP25Raw : na
reportedP10 = hasEnoughReportedHistory ? fP10Raw : na
reportedP05 = hasEnoughReportedHistory ? fP05Raw : na
reportedPercentRank = hasEnoughReportedHistory ? fRankRaw : na
reportedZScore = hasEnoughReportedHistory and not na(fStdevRaw) and fStdevRaw != 0.0 ? (reportedPE - fAvgRaw) / fStdevRaw : na
//====================================================
// VALUATION CONTEXT
//====================================================
reactivePremiumToMedian = not na(rP50) and rP50 != 0.0 and not na(rankReactivePE) ? ((rankReactivePE - rP50) / rP50) * 100.0 : na
reportedPremiumToMedian = not na(reportedP50) and reportedP50 != 0.0 and not na(reportedPE) ? ((reportedPE - reportedP50) / reportedP50) * 100.0 : na
reactiveRoomToP95 = not na(rP95) and not na(rankReactivePE) ? rP95 - rankReactivePE : na
reportedRoomToP95 = not na(reportedP95) and not na(reportedPE) ? reportedP95 - reportedPE : na
reactiveRoomAboveP05 = not na(rP05) and not na(rankReactivePE) ? rankReactivePE - rP05 : na
reportedRoomAboveP05 = not na(reportedP05) and not na(reportedPE) ? reportedPE - reportedP05 : na
reactiveEarningsYield = not na(priceReactivePE) and priceReactivePE > 0.0 ? 100.0 / priceReactivePE : na
reportedEarningsYield = not na(reportedPE) and reportedPE > 0.0 ? 100.0 / reportedPE : na
anchoredEPSChangePct = not na(anchoredForwardEPS[epsTrendLen]) and anchoredForwardEPS[epsTrendLen] != 0.0 ? ((anchoredForwardEPS - anchoredForwardEPS[epsTrendLen]) / math.abs(anchoredForwardEPS[epsTrendLen])) * 100.0 : na
reactivePEChangePct = not na(rankReactivePE[epsTrendLen]) and rankReactivePE[epsTrendLen] != 0.0 ? ((rankReactivePE - rankReactivePE[epsTrendLen]) / math.abs(rankReactivePE[epsTrendLen])) * 100.0 : na
priceChangePct = not na(close[epsTrendLen]) and close[epsTrendLen] != 0.0 ? ((close - close[epsTrendLen]) / close[epsTrendLen]) * 100.0 : na
//====================================================
// DATA QUALITY
//====================================================
string reactiveDataQualityText = "n/a"
if na(reportedPE)
reactiveDataQualityText := "No valid reported P/E"
else if na(anchoredForwardEPS)
reactiveDataQualityText := "No anchored EPS yet"
else if reactiveHistoryCount < minValidBars
reactiveDataQualityText := "Too little reactive history: " + str.tostring(reactiveHistoryCount, "#") + " bars"
else if requireFullHistory and reactiveHistoryCount < historyBars
reactiveDataQualityText := "Need full reactive lookback: " + str.tostring(reactiveHistoryCount, "#") + "/" + str.tostring(historyBars)
else if reactiveHistoryCount < historyBars
reactiveDataQualityText := "Limited reactive history: " + str.tostring(reactiveHistoryCount, "#") + "/" + str.tostring(historyBars)
else
reactiveDataQualityText := "Full reactive history: " + str.tostring(reactiveHistoryCount, "#") + "/" + str.tostring(historyBars)
string reportedDataQualityText = "n/a"
if na(reportedPE)
reportedDataQualityText := "No valid reported P/E"
else if reportedHistoryCount < minValidBars
reportedDataQualityText := "Too little reported history: " + str.tostring(reportedHistoryCount, "#") + " bars"
else if requireFullHistory and reportedHistoryCount < historyBars
reportedDataQualityText := "Need full reported lookback: " + str.tostring(reportedHistoryCount, "#") + "/" + str.tostring(historyBars)
else if reportedHistoryCount < historyBars
reportedDataQualityText := "Limited reported history: " + str.tostring(reportedHistoryCount, "#") + "/" + str.tostring(historyBars)
else
reportedDataQualityText := "Full reported history: " + str.tostring(reportedHistoryCount, "#") + "/" + str.tostring(historyBars)
//====================================================
// STATUS LOGIC
//====================================================
f_status(_hasEnough, _rank, _valueIsValid) =>
string _status = "Neutral"
if not _valueIsValid
_status := "No Forward P/E Data"
else if not _hasEnough
_status := "Limited / Building History"
else if _rank >= 95
_status := "Extreme Expensive"
else if _rank >= 90
_status := "Expensive / Stretched"
else if _rank >= 75
_status := "Above Normal Valuation"
else if _rank <= 5
_status := "Deep Valuation Discount"
else if _rank <= 10
_status := "Historically Cheap"
else if _rank <= 25
_status := "Below Normal Valuation"
else
_status := "Normal Valuation Range"
_status
reactiveStatusText = f_status(hasEnoughReactiveHistory, reactivePercentRank, not na(priceReactivePE))
reportedStatusText = f_status(hasEnoughReportedHistory, reportedPercentRank, not na(reportedPE))
isReady = hasEnoughReactiveHistory and not na(reactivePercentRank)
isExtremeExpensive = isReady and reactivePercentRank >= 95
isExpensive = isReady and reactivePercentRank >= 90 and reactivePercentRank < 95
isAboveNormal = isReady and reactivePercentRank >= 75 and reactivePercentRank < 90
isDeepDiscount = isReady and reactivePercentRank <= 5
isCheap = isReady and reactivePercentRank > 5 and reactivePercentRank <= 10
isBelowNormal = isReady and reactivePercentRank > 10 and reactivePercentRank <= 25
string epsTrendText = "n/a"
if not na(anchoredEPSChangePct)
if anchoredEPSChangePct > trendThreshold
epsTrendText := "Rising"
else if anchoredEPSChangePct < -trendThreshold
epsTrendText := "Falling"
else
epsTrendText := "Flat"
string multipleTrendText = "n/a"
if not na(reactivePEChangePct)
if reactivePEChangePct > trendThreshold
multipleTrendText := "Expansion"
else if reactivePEChangePct < -trendThreshold
multipleTrendText := "Compression"
else
multipleTrendText := "Stable"
string driverText = "n/a"
if not na(priceChangePct) and not na(reactivePEChangePct)
if priceChangePct > trendThreshold and reactivePEChangePct > trendThreshold
driverText := "Price-Led Multiple Expansion"
else if priceChangePct < -trendThreshold and reactivePEChangePct < -trendThreshold
driverText := "Price-Led Multiple Compression"
else if priceChangePct > trendThreshold and reactivePEChangePct < -trendThreshold
driverText := "EPS Anchor Improved"
else if priceChangePct < -trendThreshold and reactivePEChangePct > trendThreshold
driverText := "EPS Anchor Deteriorated"
else
driverText := "Stable"
color dynamicColor = color.gray
if isReady
if reactivePercentRank > 50
dynamicColor := color.from_gradient(reactivePercentRank, 50, 100, color.new(color.gray, 45), color.new(color.red, 0))
else
dynamicColor := color.from_gradient(reactivePercentRank, 0, 50, color.new(color.blue, 0), color.new(color.gray, 45))
color statusColor = color.gray
if isReady
if isExtremeExpensive
statusColor := color.red
else if isExpensive
statusColor := color.orange
else if isAboveNormal
statusColor := color.yellow
else if isDeepDiscount
statusColor := color.blue
else if isCheap
statusColor := color.aqua
else if isBelowNormal
statusColor := color.teal
else
statusColor := color.gray
//====================================================
// PLOTS — PRICE-REACTIVE ONLY
//====================================================
plot(showHistogram ? smoothedReactivePE : na, title="Smoothed Price-Reactive Forward P/E Histogram", style=plot.style_columns, color=color.new(dynamicColor, 45))
plot(showReportedLine ? reportedPE : na, title="Reported Forward P/E Step Line", color=color.new(color.white, 65), linewidth=1, style=plot.style_stepline)
plot(smoothedReactivePE, title="Price-Reactive Forward P/E", color=dynamicColor, linewidth=3)
plot(signalLine, title="Price-Reactive Forward P/E Signal Line", color=color.new(color.yellow, 0), linewidth=2)
p95Plot = plot(showAdaptiveBands ? rP95 : na, title="Reactive 95th Percentile", color=color.new(color.red, 20), linewidth=2)
p90Plot = plot(showAdaptiveBands ? rP90 : na, title="Reactive 90th Percentile", color=color.new(color.red, 65), linewidth=1)
p75Plot = plot(showAdaptiveBands ? rP75 : na, title="Reactive 75th Percentile", color=color.new(color.gray, 75), linewidth=1)
p50Plot = plot(showAdaptiveBands ? rP50 : na, title="Reactive 50th Percentile / Median", color=color.new(color.gray, 20), linewidth=2)
p25Plot = plot(showAdaptiveBands ? rP25 : na, title="Reactive 25th Percentile", color=color.new(color.gray, 75), linewidth=1)
p10Plot = plot(showAdaptiveBands ? rP10 : na, title="Reactive 10th Percentile", color=color.new(color.blue, 65), linewidth=1)
p05Plot = plot(showAdaptiveBands ? rP05 : na, title="Reactive 5th Percentile", color=color.new(color.blue, 20), linewidth=2)
fill(p95Plot, p90Plot, color=showAdaptiveBands and showZoneFills ? color.new(color.red, 88) : na, title="Reactive Expensive Zone Fill")
fill(p10Plot, p05Plot, color=showAdaptiveBands and showZoneFills ? color.new(color.blue, 88) : na, title="Reactive Cheap Zone Fill")
fill(p75Plot, p25Plot, color=showAdaptiveBands and showZoneFills ? color.new(color.gray, 94) : na, title="Reactive Normal Zone Fill")
plot(showFixedBands ? fixedUpper1 : na, title="Fixed Upper Band 1", color=color.new(color.orange, 45), linewidth=1)
plot(showFixedBands ? fixedUpper2 : na, title="Fixed Upper Band 2", color=color.new(color.red, 45), linewidth=1)
plot(showFixedBands ? fixedLower1 : na, title="Fixed Lower Band 1", color=color.new(color.aqua, 45), linewidth=1)
plot(showFixedBands ? fixedLower2 : na, title="Fixed Lower Band 2", color=color.new(color.blue, 45), linewidth=1)
bgcolor(showBgHighlight and isExtremeExpensive ? color.new(color.red, 90) : showBgHighlight and isDeepDiscount ? color.new(color.blue, 90) : na)
//====================================================
// TABLE
//====================================================
f_x(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#.##") + "x"
f_pct(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#.##") + "%"
f_num(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#.##")
f_price(_value) =>
na(_value) ? "n/a" : str.tostring(_value, format.mintick)
f_bars(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#")
var table infoTable = table.new(position.top_right, 2, 18, border_width=1, border_color=color.new(color.gray, 75))
if showTable and barstate.islast
table.cell(infoTable, 0, 0, "Metric", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(infoTable, 1, 0, "Current", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(infoTable, 0, 1, "Reactive / Reported P/E", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 1, f_x(priceReactivePE) + " / " + f_x(reportedPE), text_color=color.white, bgcolor=color.new(dynamicColor, 25))
table.cell(infoTable, 0, 2, "Smoothed Reactive P/E", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 2, f_x(smoothedReactivePE), text_color=color.white, bgcolor=color.new(dynamicColor, 25))
table.cell(infoTable, 0, 3, "Anchored Forward EPS", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 3, f_price(anchoredForwardEPS), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 4, "Reactive / Reported Rank", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 4, f_pct(reactivePercentRank) + " / " + f_pct(reportedPercentRank), text_color=color.white, bgcolor=color.new(dynamicColor, 25))
table.cell(infoTable, 0, 5, "Reactive / Reported Median", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 5, f_x(rP50) + " / " + f_x(reportedP50), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 6, "Reactive / Reported Premium", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 6, f_pct(reactivePremiumToMedian) + " / " + f_pct(reportedPremiumToMedian), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 7, "Reactive / Reported P95", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 7, f_x(rP95) + " / " + f_x(reportedP95), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 8, "Reactive / Reported P05", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 8, f_x(rP05) + " / " + f_x(reportedP05), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 9, "Reactive / Reported Yield", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 9, f_pct(reactiveEarningsYield) + " / " + f_pct(reportedEarningsYield), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 10, "Reactive Room to P95", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 10, f_x(reactiveRoomToP95), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 11, "Reactive Room above P05", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 11, f_x(reactiveRoomAboveP05), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 12, "EPS / Multiple Trend", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 12, epsTrendText + " / " + multipleTrendText, text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 13, "Driver", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 13, driverText, text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 14, "PEG / Fwd P/S", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 14, f_x(pegRatioData) + " / " + f_x(forwardPSData), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 15, "Data Quality", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 15, reactiveDataQualityText + " / " + effectivePeriod + " / " + f_bars(barsSinceReportedUpdate) + " bars since anchor", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 16, "Reactive Status", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 16, reactiveStatusText, text_color=color.white, bgcolor=color.new(statusColor, 20))
table.cell(infoTable, 0, 17, "Reported Status", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 17, reportedStatusText, text_color=color.white, bgcolor=color.new(color.black, 50))
if not showTable and barstate.islast
table.clear(infoTable, 0, 0, 1, 17)
//====================================================
// ALERTS — PRICE-REACTIVE ONLY
//====================================================
crossOver95 = ta.crossover(reactivePercentRank, 95)
crossUnder95 = ta.crossunder(reactivePercentRank, 95)
crossOver90 = ta.crossover(reactivePercentRank, 90)
crossUnder90 = ta.crossunder(reactivePercentRank, 90)
crossUnder5 = ta.crossunder(reactivePercentRank, 5)
crossOver5 = ta.crossover(reactivePercentRank, 5)
crossUnder10 = ta.crossunder(reactivePercentRank, 10)
crossOver10 = ta.crossover(reactivePercentRank, 10)
crossOverMedian = ta.crossover(rankReactivePE, rP50)
crossUnderMedian = ta.crossunder(rankReactivePE, rP50)
peCrossAboveSignal = ta.crossover(smoothedReactivePE, signalLine)
peCrossBelowSignal = ta.crossunder(smoothedReactivePE, signalLine)
enteredExtremeExpensive = isReady and crossOver95
exitedExtremeExpensive = isReady and crossUnder95
enteredExpensive = isReady and crossOver90
exitedExpensive = isReady and crossUnder90
enteredDeepDiscount = isReady and crossUnder5
exitedDeepDiscount = isReady and crossOver5
enteredCheap = isReady and crossUnder10
exitedCheap = isReady and crossOver10
expensiveMultipleCompression = isReady and reactivePercentRank >= 90 and peCrossBelowSignal
cheapReRating = isReady and reactivePercentRank <= 10 and peCrossAboveSignal
alertcondition(enteredExtremeExpensive, title="Entered Extreme Expensive Zone", message="Price-reactive forward P/E entered its 95th percentile extreme expensive zone.")
alertcondition(exitedExtremeExpensive, title="Exited Extreme Expensive Zone", message="Price-reactive forward P/E exited its 95th percentile extreme expensive zone.")
alertcondition(enteredExpensive, title="Entered Expensive Zone", message="Price-reactive forward P/E entered its 90th percentile expensive zone.")
alertcondition(exitedExpensive, title="Exited Expensive Zone", message="Price-reactive forward P/E exited its 90th percentile expensive zone.")
alertcondition(enteredDeepDiscount, title="Entered Deep Discount Zone", message="Price-reactive forward P/E entered its 5th percentile deep discount zone.")
alertcondition(exitedDeepDiscount, title="Exited Deep Discount Zone", message="Price-reactive forward P/E exited its 5th percentile deep discount zone.")
alertcondition(enteredCheap, title="Entered Historically Cheap Zone", message="Price-reactive forward P/E entered its 10th percentile historically cheap zone.")
alertcondition(exitedCheap, title="Exited Historically Cheap Zone", message="Price-reactive forward P/E exited its 10th percentile historically cheap zone.")
alertcondition(crossOverMedian, title="Reactive P/E Crossed Above Median", message="Price-reactive forward P/E crossed above its historical median.")
alertcondition(crossUnderMedian, title="Reactive P/E Crossed Below Median", message="Price-reactive forward P/E crossed below its historical median.")
alertcondition(expensiveMultipleCompression, title="Expensive Multiple Compression Warning", message="Price-reactive forward P/E is historically expensive and crossed below its signal line.")
alertcondition(cheapReRating, title="Cheap Re-Rating Warning", message="Price-reactive forward P/E is historically cheap and crossed above its signal line.")
What this indicator does
This indicator compares two versions of forward P/E:
Term
Meaning
Reactive
Daily price-adjusted forward P/E using the latest anchored forward EPS
Reported
TradingView’s reported forward P/E from its financial data source
The chart itself plots the Reactive Forward P/E, while the table shows both Reactive / Reported valuation context.
The main question it answers is:
Has the stock’s valuation moved too far compared with its own history?
Best default settings
Setting
Suggested
Financial Period Mode
Auto FY then FQ
Historical Lookback
3 Years or 5 Years
Require Full Lookback History
Off
Minimum Valid Bars
63 or 126
Smoothing Type
EMA
Smoothing Length
10
Signal Length
20
Percentile / Z-Score Source
Raw Price-Reactive P/E
Show Reported Line
Off
Show Reactive Adaptive Bands
On
Fill Extreme Zones
On
For newer stocks, use 6 Months, 1 Year, or 2 Years.
Main table readings
Reactive / Reported P/E
Example:
42.5x / 38.9x
This means:
Side
Meaning
Reactive P/E
Price-adjusted forward P/E based on current price
Reported P/E
TradingView’s reported forward P/E
If Reactive P/E is much higher than Reported P/E, the stock price may have run up since the last reported valuation update.
Smoothed Reactive P/E
This is the smoother plotted value.
Use it to read the trend, not the exact current valuation.
Reading
Best use
Reactive P/E
Current valuation estimate
Smoothed Reactive P/E
Cleaner visual trend
Anchored Forward EPS
This is the estimated forward EPS base used to calculate the Reactive P/E.
Reactive P/E = Current Price / Anchored Forward EPS
If the anchored EPS is stale, the Reactive P/E may still move daily with price, but the earnings base may be outdated.
Reactive / Reported Rank
This is one of the most important rows.
Example:
91% / 78%
Side
Meaning
Reactive Rank
Where the price-adjusted P/E ranks versus its own history
Reported Rank
Where the reported P/E ranks versus its own history
Interpretation:
Percentile Rank
Meaning
95%+
Extremely expensive
90–95%
Expensive / stretched
75–90%
Above-normal valuation
25–75%
Normal valuation range
10–25%
Below-normal valuation
5–10%
Historically cheap
Below 5%
Deep valuation discount
A major warning setup is:
Reactive Rank above 90%
Reported Rank much lower
That can mean price has moved ahead of the last reported valuation update.
Reactive / Reported Median
Example:
31.0x / 29.5x
This shows the stock’s historical “normal” P/E level for each method.
Use this to see whether the current valuation is above or below its own normal range.
Reactive / Reported Premium
Example:
+37% / +28%
This means valuation is above its historical median.
Reading
Meaning
+50%
Very rich versus normal
+20%
Above normal
0%
Near median valuation
-20%
Below normal
-40%
Potentially cheap
This is one of the easiest rows to use.
Reactive / Reported P95
This shows each method’s historical expensive zone.
Example:
52x / 48x
If the Reactive P/E is near or above its P95, the stock is historically expensive based on current price movement.
Reactive / Reported P05
This shows each method’s historical cheap zone.
Example:
18x / 16x
If the Reactive P/E is near or below P05, the stock is historically cheap versus its own valuation history.
Reactive / Reported Yield
This converts forward P/E into earnings yield.
Forward Earnings Yield = 100 / Forward P/E
Examples:
P/E
Earnings Yield
10x
10.0%
20x
5.0%
40x
2.5%
80x
1.25%
Lower yield means richer valuation.
Reactive Room to P95
This shows how much room remains before the Reactive P/E reaches its historical expensive zone.
Reading
Meaning
Positive
Still below historical expensive extreme
Near zero
Near historical expensive extreme
Negative
Already above historical expensive extreme
Example:
Reactive Room to P95: -3.5x
The stock is already above its historical 95th percentile valuation level.
Reactive Room above P05
This shows how far the Reactive P/E is above its historical cheap zone.
Reading
Meaning
Large positive
Not cheap
Near zero
Near historical cheap zone
Negative
Below historical cheap extreme
EPS / Multiple Trend
This shows whether the anchored EPS base and valuation multiple are rising, falling, or stable.
Reading
Meaning
Rising / Expansion
EPS anchor rising and multiple rising
Rising / Compression
EPS anchor rising while valuation gets cheaper
Falling / Expansion
EPS anchor falling while valuation rises — riskier
Falling / Compression
EPS anchor falling and multiple falling
Stable / Stable
Little change
Most concerning:
Falling / Expansion
That means investors are paying a higher multiple while the earnings base is worsening.
Driver
This explains what is mostly driving the valuation move.
Driver
Meaning
Price-Led Multiple Expansion
Price is pushing valuation higher
Price-Led Multiple Compression
Price decline is compressing valuation
EPS Anchor Improved
Earnings base improved
EPS Anchor Deteriorated
Earnings base worsened
Stable
No major change
For overextension, watch for:
Price-Led Multiple Expansion
especially when Reactive Rank is above 90%.
PEG / Fwd P/S
These are supplemental checks.
Metric
Use
PEG
P/E adjusted for growth
Forward P/S
Useful when earnings are unstable or distorted
A high P/E is less concerning if growth is strong and PEG is reasonable. A high P/E with a high PEG is more concerning.
Data Quality
This row tells you whether the indicator has enough usable history.
Message
Meaning
Full reactive history
Most reliable
Limited reactive history
Usable, but be cautious
Too little reactive history
Percentiles are weak
No valid reported P/E
TradingView does not have usable forward P/E
Bars since anchor
How long since the reported P/E last updated
If “bars since anchor” is very high, the Reactive P/E is still useful for price movement, but the earnings base may be stale.
Status readings
Reactive Status
This is the main status because the chart is price-reactive.
Status
Meaning
Extreme Expensive
Reactive Rank 95%+
Expensive / Stretched
Reactive Rank 90–95%
Above Normal Valuation
Reactive Rank 75–90%
Normal Valuation Range
Reactive Rank 25–75%
Below Normal Valuation
Reactive Rank 10–25%
Historically Cheap
Reactive Rank 5–10%
Deep Valuation Discount
Reactive Rank below 5%
Limited / Building History
Not enough data yet
Reported Status
This is the slower TradingView-reported valuation status.
Use it as confirmation.
Best warning setup:
Reactive Status = Expensive / Stretched
Reported Status = Normal Valuation Range
That can mean the stock price has moved ahead of the last reported valuation update.
How to use it practically
Avoid chasing
Be cautious when you see:
Reactive Rank above 90%
Reactive Premium strongly positive
Reactive Room to P95 near zero or negative
Driver = Price-Led Multiple Expansion
That means valuation is stretched mainly because price has moved up.
Look for possible value
Look closer when you see:
Reactive Rank below 10%
Reactive Premium negative
Reactive Room above P05 near zero or negative
Reported Status also cheap
That means the stock may be cheap versus its own history.
Best long setup
The healthier setup is often:
Reactive Rank 25–75%
EPS trend Rising
Multiple Stable or Compressing
Price trend improving
That means the stock may not be overextended, and the earnings base may be improving.
Main takeaway
Use this indicator as a stock-specific valuation stretch tool.
The most important rows are:
Reactive / Reported P/E
Reactive / Reported Rank
Reactive / Reported Premium
Reactive Room to P95
Reactive Status
Reported Status
Data Quality
The key idea:
Reactive = what valuation looks like today after price movement.
Reported = what TradingView’s financial data currently reports.
Here is the source code:
//version=6
indicator("Price-Reactive Forward P/E Extremes [Combined]", shorttitle="PRFPE+", overlay=false, max_bars_back=3000)
//====================================================
// INPUTS
//====================================================
grp1 = "Forward Valuation Settings"
financialMode = input.string("Auto FY then FQ", "Financial Period Mode", options=["Auto FY then FQ", "Auto FQ then FY", "FY", "FQ"], group=grp1)
minValidPE = input.float(0.1, "Minimum Valid Forward P/E", minval=0.0, step=0.1, group=grp1)
maxValidPE = input.float(500.0, "Maximum Valid Forward P/E", minval=1.0, step=5.0, group=grp1)
grp2 = "Historical Lookback Settings"
lookbackChoice = input.string("5 Years", "Historical Lookback", options=["6 Months", "1 Year", "2 Years", "3 Years", "4 Years", "5 Years"], group=grp2)
requireFullHistory = input.bool(false, "Require Full Lookback History?", group=grp2)
minValidBars = input.int(63, "Minimum Valid Bars in Limited-History Mode", minval=20, maxval=1260, group=grp2)
usePriorHistoryOnly = input.bool(true, "Rank Current Reading vs Prior History Only?", group=grp2)
grp3 = "Smoothing Settings"
smoothType = input.string("EMA", "Price-Reactive P/E Smoothing Type", options=["None", "SMA", "EMA", "RMA"], group=grp3)
smoothLen = input.int(10, "Smoothing Length", minval=1, group=grp3)
signalLength = input.int(20, "Signal Line Length", minval=1, group=grp3)
rankSource = input.string("Raw Price-Reactive P/E", "Percentile / Z-Score Source", options=["Raw Price-Reactive P/E", "Smoothed Price-Reactive P/E"], group=grp3)
grp4 = "EPS / Multiple Trend Settings"
epsTrendLen = input.int(63, "EPS / Multiple Trend Lookback", minval=1, group=grp4)
trendThreshold = input.float(2.5, "Trend Threshold %", minval=0.0, step=0.5, group=grp4)
grp5 = "Visuals & UI"
showHistogram = input.bool(false, "Show Price-Reactive P/E Histogram?", group=grp5)
showReportedLine = input.bool(false, "Show Reported Forward P/E Step Line?", group=grp5)
showAdaptiveBands = input.bool(true, "Show Reactive Adaptive Percentile Bands?", group=grp5)
showZoneFills = input.bool(true, "Fill Extreme Zones?", group=grp5)
showFixedBands = input.bool(false, "Show Fixed Forward P/E Bands?", group=grp5)
showTable = input.bool(true, "Show Current Reading Table?", group=grp5)
showBgHighlight = input.bool(false, "Highlight Extreme Background?", group=grp5)
fixedUpper1 = input.float(30.0, "Fixed Upper Band 1", step=0.5, group=grp5)
fixedUpper2 = input.float(45.0, "Fixed Upper Band 2", step=0.5, group=grp5)
fixedLower1 = input.float(15.0, "Fixed Lower Band 1", step=0.5, group=grp5)
fixedLower2 = input.float(10.0, "Fixed Lower Band 2", step=0.5, group=grp5)
//====================================================
// LOOKBACK CONVERSION
//====================================================
historyBars = lookbackChoice == "6 Months" ? 126 : lookbackChoice == "1 Year" ? 252 : lookbackChoice == "2 Years" ? 504 : lookbackChoice == "3 Years" ? 756 : lookbackChoice == "4 Years" ? 1008 : 1260
requiredBars = requireFullHistory ? historyBars : minValidBars
//====================================================
// REPORTED FINANCIAL DATA
//====================================================
peFY = request.financial(syminfo.tickerid, "PRICE_EARNINGS_FORWARD", "FY", barmerge.gaps_off, true)
peFQ = request.financial(syminfo.tickerid, "PRICE_EARNINGS_FORWARD", "FQ", barmerge.gaps_off, true)
pegFY = request.financial(syminfo.tickerid, "PEG_RATIO", "FY", barmerge.gaps_off, true)
pegFQ = request.financial(syminfo.tickerid, "PEG_RATIO", "FQ", barmerge.gaps_off, true)
psFY = request.financial(syminfo.tickerid, "PRICE_SALES_FORWARD", "FY", barmerge.gaps_off, true)
psFQ = request.financial(syminfo.tickerid, "PRICE_SALES_FORWARD", "FQ", barmerge.gaps_off, true)
f_valid_pe(_v) =>
not na(_v) and _v >= minValidPE and _v <= maxValidPE ? _v : na
validPEFY = f_valid_pe(peFY)
validPEFQ = f_valid_pe(peFQ)
float reportedPE = na
string effectivePeriod = "n/a"
if financialMode == "FY"
reportedPE := validPEFY
effectivePeriod := "FY"
else if financialMode == "FQ"
reportedPE := validPEFQ
effectivePeriod := "FQ"
else if financialMode == "Auto FY then FQ"
if not na(validPEFY)
reportedPE := validPEFY
effectivePeriod := "FY"
else
reportedPE := validPEFQ
effectivePeriod := "FQ"
else
if not na(validPEFQ)
reportedPE := validPEFQ
effectivePeriod := "FQ"
else
reportedPE := validPEFY
effectivePeriod := "FY"
pegRatioData = effectivePeriod == "FY" ? pegFY : effectivePeriod == "FQ" ? pegFQ : na
forwardPSData = effectivePeriod == "FY" ? psFY : effectivePeriod == "FQ" ? psFQ : na
//====================================================
// PRICE-REACTIVE FORWARD P/E
//====================================================
reportedUpdated = not na(reportedPE) and (na(reportedPE[1]) or reportedPE != reportedPE[1])
var float anchoredForwardEPS = na
if reportedUpdated and reportedPE > 0.0
anchoredForwardEPS := close / reportedPE
else
anchoredForwardEPS := anchoredForwardEPS[1]
priceReactivePE = not na(anchoredForwardEPS) and anchoredForwardEPS > 0.0 ? close / anchoredForwardEPS : na
barsSinceReportedUpdate = ta.barssince(reportedUpdated)
//====================================================
// SMOOTHING
//====================================================
f_smooth(_src, _len) =>
if smoothType == "None" or _len <= 1
_src
else if smoothType == "SMA"
ta.sma(_src, _len)
else if smoothType == "EMA"
ta.ema(_src, _len)
else
ta.rma(_src, _len)
smoothedReactivePE = f_smooth(priceReactivePE, smoothLen)
rankReactivePE = rankSource == "Raw Price-Reactive P/E" ? priceReactivePE : smoothedReactivePE
signalLine = f_smooth(smoothedReactivePE, signalLength)
//====================================================
// HISTORICAL STATS FUNCTIONS
//====================================================
f_array_percentile(_arr, _pct) =>
int _n = array.size(_arr)
float _result = na
if _n > 0
float _pos = (_pct / 100.0) * (_n - 1)
int _lo = int(math.floor(_pos))
int _hi = int(math.ceil(_pos))
float _weight = _pos - _lo
float _loVal = array.get(_arr, _lo)
float _hiVal = array.get(_arr, _hi)
_result := _loVal + ((_hiVal - _loVal) * _weight)
_result
f_hist_stats(_series, _x, _len, _priorOnly) =>
array<float> _values = array.new_float(0)
float _sum = 0.0
float _sumSq = 0.0
int _start = _priorOnly ? 1 : 0
int _finish = _priorOnly ? _len : _len - 1
for i = _start to _finish
_v = _series
if not na(_v)
array.push(_values, _v)
_sum += _v
_sumSq += _v * _v
int _n = array.size(_values)
float _p95 = na
float _p90 = na
float _p75 = na
float _p50 = na
float _p25 = na
float _p10 = na
float _p05 = na
float _rank = na
float _mean = na
float _stdev = na
if _n > 0
array.sort(_values, order.ascending)
_p95 := f_array_percentile(_values, 95)
_p90 := f_array_percentile(_values, 90)
_p75 := f_array_percentile(_values, 75)
_p50 := f_array_percentile(_values, 50)
_p25 := f_array_percentile(_values, 25)
_p10 := f_array_percentile(_values, 10)
_p05 := f_array_percentile(_values, 5)
if not na(_x)
float _countBelowOrEqual = 0.0
for j = 0 to _n - 1
if array.get(_values, j) <= _x
_countBelowOrEqual += 1.0
_rank := (_countBelowOrEqual / _n) * 100.0
_mean := _sum / _n
if _n > 1
float _variance = (_sumSq - ((_sum * _sum) / _n)) / (_n - 1)
_stdev := _variance >= 0.0 ? math.sqrt(_variance) : na
[_p95, _p90, _p75, _p50, _p25, _p10, _p05, _rank, _mean, _stdev, _n * 1.0]
//====================================================
// REACTIVE HISTORICAL STATS
//====================================================
[rP95Raw, rP90Raw, rP75Raw, rP50Raw, rP25Raw, rP10Raw, rP05Raw, rRankRaw, rAvgRaw, rStdevRaw, reactiveHistoryCount] = f_hist_stats(rankReactivePE, rankReactivePE, historyBars, usePriorHistoryOnly)
hasEnoughReactiveHistory = not na(priceReactivePE) and reactiveHistoryCount >= requiredBars
rP95 = hasEnoughReactiveHistory ? rP95Raw : na
rP90 = hasEnoughReactiveHistory ? rP90Raw : na
rP75 = hasEnoughReactiveHistory ? rP75Raw : na
rP50 = hasEnoughReactiveHistory ? rP50Raw : na
rP25 = hasEnoughReactiveHistory ? rP25Raw : na
rP10 = hasEnoughReactiveHistory ? rP10Raw : na
rP05 = hasEnoughReactiveHistory ? rP05Raw : na
reactivePercentRank = hasEnoughReactiveHistory ? rRankRaw : na
reactiveZScore = hasEnoughReactiveHistory and not na(rStdevRaw) and rStdevRaw != 0.0 ? (rankReactivePE - rAvgRaw) / rStdevRaw : na
//====================================================
// REPORTED HISTORICAL STATS
//====================================================
[fP95Raw, fP90Raw, fP75Raw, fP50Raw, fP25Raw, fP10Raw, fP05Raw, fRankRaw, fAvgRaw, fStdevRaw, reportedHistoryCount] = f_hist_stats(reportedPE, reportedPE, historyBars, usePriorHistoryOnly)
hasEnoughReportedHistory = not na(reportedPE) and reportedHistoryCount >= requiredBars
reportedP95 = hasEnoughReportedHistory ? fP95Raw : na
reportedP90 = hasEnoughReportedHistory ? fP90Raw : na
reportedP75 = hasEnoughReportedHistory ? fP75Raw : na
reportedP50 = hasEnoughReportedHistory ? fP50Raw : na
reportedP25 = hasEnoughReportedHistory ? fP25Raw : na
reportedP10 = hasEnoughReportedHistory ? fP10Raw : na
reportedP05 = hasEnoughReportedHistory ? fP05Raw : na
reportedPercentRank = hasEnoughReportedHistory ? fRankRaw : na
reportedZScore = hasEnoughReportedHistory and not na(fStdevRaw) and fStdevRaw != 0.0 ? (reportedPE - fAvgRaw) / fStdevRaw : na
//====================================================
// VALUATION CONTEXT
//====================================================
reactivePremiumToMedian = not na(rP50) and rP50 != 0.0 and not na(rankReactivePE) ? ((rankReactivePE - rP50) / rP50) * 100.0 : na
reportedPremiumToMedian = not na(reportedP50) and reportedP50 != 0.0 and not na(reportedPE) ? ((reportedPE - reportedP50) / reportedP50) * 100.0 : na
reactiveRoomToP95 = not na(rP95) and not na(rankReactivePE) ? rP95 - rankReactivePE : na
reportedRoomToP95 = not na(reportedP95) and not na(reportedPE) ? reportedP95 - reportedPE : na
reactiveRoomAboveP05 = not na(rP05) and not na(rankReactivePE) ? rankReactivePE - rP05 : na
reportedRoomAboveP05 = not na(reportedP05) and not na(reportedPE) ? reportedPE - reportedP05 : na
reactiveEarningsYield = not na(priceReactivePE) and priceReactivePE > 0.0 ? 100.0 / priceReactivePE : na
reportedEarningsYield = not na(reportedPE) and reportedPE > 0.0 ? 100.0 / reportedPE : na
anchoredEPSChangePct = not na(anchoredForwardEPS[epsTrendLen]) and anchoredForwardEPS[epsTrendLen] != 0.0 ? ((anchoredForwardEPS - anchoredForwardEPS[epsTrendLen]) / math.abs(anchoredForwardEPS[epsTrendLen])) * 100.0 : na
reactivePEChangePct = not na(rankReactivePE[epsTrendLen]) and rankReactivePE[epsTrendLen] != 0.0 ? ((rankReactivePE - rankReactivePE[epsTrendLen]) / math.abs(rankReactivePE[epsTrendLen])) * 100.0 : na
priceChangePct = not na(close[epsTrendLen]) and close[epsTrendLen] != 0.0 ? ((close - close[epsTrendLen]) / close[epsTrendLen]) * 100.0 : na
//====================================================
// DATA QUALITY
//====================================================
string reactiveDataQualityText = "n/a"
if na(reportedPE)
reactiveDataQualityText := "No valid reported P/E"
else if na(anchoredForwardEPS)
reactiveDataQualityText := "No anchored EPS yet"
else if reactiveHistoryCount < minValidBars
reactiveDataQualityText := "Too little reactive history: " + str.tostring(reactiveHistoryCount, "#") + " bars"
else if requireFullHistory and reactiveHistoryCount < historyBars
reactiveDataQualityText := "Need full reactive lookback: " + str.tostring(reactiveHistoryCount, "#") + "/" + str.tostring(historyBars)
else if reactiveHistoryCount < historyBars
reactiveDataQualityText := "Limited reactive history: " + str.tostring(reactiveHistoryCount, "#") + "/" + str.tostring(historyBars)
else
reactiveDataQualityText := "Full reactive history: " + str.tostring(reactiveHistoryCount, "#") + "/" + str.tostring(historyBars)
string reportedDataQualityText = "n/a"
if na(reportedPE)
reportedDataQualityText := "No valid reported P/E"
else if reportedHistoryCount < minValidBars
reportedDataQualityText := "Too little reported history: " + str.tostring(reportedHistoryCount, "#") + " bars"
else if requireFullHistory and reportedHistoryCount < historyBars
reportedDataQualityText := "Need full reported lookback: " + str.tostring(reportedHistoryCount, "#") + "/" + str.tostring(historyBars)
else if reportedHistoryCount < historyBars
reportedDataQualityText := "Limited reported history: " + str.tostring(reportedHistoryCount, "#") + "/" + str.tostring(historyBars)
else
reportedDataQualityText := "Full reported history: " + str.tostring(reportedHistoryCount, "#") + "/" + str.tostring(historyBars)
//====================================================
// STATUS LOGIC
//====================================================
f_status(_hasEnough, _rank, _valueIsValid) =>
string _status = "Neutral"
if not _valueIsValid
_status := "No Forward P/E Data"
else if not _hasEnough
_status := "Limited / Building History"
else if _rank >= 95
_status := "Extreme Expensive"
else if _rank >= 90
_status := "Expensive / Stretched"
else if _rank >= 75
_status := "Above Normal Valuation"
else if _rank <= 5
_status := "Deep Valuation Discount"
else if _rank <= 10
_status := "Historically Cheap"
else if _rank <= 25
_status := "Below Normal Valuation"
else
_status := "Normal Valuation Range"
_status
reactiveStatusText = f_status(hasEnoughReactiveHistory, reactivePercentRank, not na(priceReactivePE))
reportedStatusText = f_status(hasEnoughReportedHistory, reportedPercentRank, not na(reportedPE))
isReady = hasEnoughReactiveHistory and not na(reactivePercentRank)
isExtremeExpensive = isReady and reactivePercentRank >= 95
isExpensive = isReady and reactivePercentRank >= 90 and reactivePercentRank < 95
isAboveNormal = isReady and reactivePercentRank >= 75 and reactivePercentRank < 90
isDeepDiscount = isReady and reactivePercentRank <= 5
isCheap = isReady and reactivePercentRank > 5 and reactivePercentRank <= 10
isBelowNormal = isReady and reactivePercentRank > 10 and reactivePercentRank <= 25
string epsTrendText = "n/a"
if not na(anchoredEPSChangePct)
if anchoredEPSChangePct > trendThreshold
epsTrendText := "Rising"
else if anchoredEPSChangePct < -trendThreshold
epsTrendText := "Falling"
else
epsTrendText := "Flat"
string multipleTrendText = "n/a"
if not na(reactivePEChangePct)
if reactivePEChangePct > trendThreshold
multipleTrendText := "Expansion"
else if reactivePEChangePct < -trendThreshold
multipleTrendText := "Compression"
else
multipleTrendText := "Stable"
string driverText = "n/a"
if not na(priceChangePct) and not na(reactivePEChangePct)
if priceChangePct > trendThreshold and reactivePEChangePct > trendThreshold
driverText := "Price-Led Multiple Expansion"
else if priceChangePct < -trendThreshold and reactivePEChangePct < -trendThreshold
driverText := "Price-Led Multiple Compression"
else if priceChangePct > trendThreshold and reactivePEChangePct < -trendThreshold
driverText := "EPS Anchor Improved"
else if priceChangePct < -trendThreshold and reactivePEChangePct > trendThreshold
driverText := "EPS Anchor Deteriorated"
else
driverText := "Stable"
color dynamicColor = color.gray
if isReady
if reactivePercentRank > 50
dynamicColor := color.from_gradient(reactivePercentRank, 50, 100, color.new(color.gray, 45), color.new(color.red, 0))
else
dynamicColor := color.from_gradient(reactivePercentRank, 0, 50, color.new(color.blue, 0), color.new(color.gray, 45))
color statusColor = color.gray
if isReady
if isExtremeExpensive
statusColor := color.red
else if isExpensive
statusColor := color.orange
else if isAboveNormal
statusColor := color.yellow
else if isDeepDiscount
statusColor := color.blue
else if isCheap
statusColor := color.aqua
else if isBelowNormal
statusColor := color.teal
else
statusColor := color.gray
//====================================================
// PLOTS — PRICE-REACTIVE ONLY
//====================================================
plot(showHistogram ? smoothedReactivePE : na, title="Smoothed Price-Reactive Forward P/E Histogram", style=plot.style_columns, color=color.new(dynamicColor, 45))
plot(showReportedLine ? reportedPE : na, title="Reported Forward P/E Step Line", color=color.new(color.white, 65), linewidth=1, style=plot.style_stepline)
plot(smoothedReactivePE, title="Price-Reactive Forward P/E", color=dynamicColor, linewidth=3)
plot(signalLine, title="Price-Reactive Forward P/E Signal Line", color=color.new(color.yellow, 0), linewidth=2)
p95Plot = plot(showAdaptiveBands ? rP95 : na, title="Reactive 95th Percentile", color=color.new(color.red, 20), linewidth=2)
p90Plot = plot(showAdaptiveBands ? rP90 : na, title="Reactive 90th Percentile", color=color.new(color.red, 65), linewidth=1)
p75Plot = plot(showAdaptiveBands ? rP75 : na, title="Reactive 75th Percentile", color=color.new(color.gray, 75), linewidth=1)
p50Plot = plot(showAdaptiveBands ? rP50 : na, title="Reactive 50th Percentile / Median", color=color.new(color.gray, 20), linewidth=2)
p25Plot = plot(showAdaptiveBands ? rP25 : na, title="Reactive 25th Percentile", color=color.new(color.gray, 75), linewidth=1)
p10Plot = plot(showAdaptiveBands ? rP10 : na, title="Reactive 10th Percentile", color=color.new(color.blue, 65), linewidth=1)
p05Plot = plot(showAdaptiveBands ? rP05 : na, title="Reactive 5th Percentile", color=color.new(color.blue, 20), linewidth=2)
fill(p95Plot, p90Plot, color=showAdaptiveBands and showZoneFills ? color.new(color.red, 88) : na, title="Reactive Expensive Zone Fill")
fill(p10Plot, p05Plot, color=showAdaptiveBands and showZoneFills ? color.new(color.blue, 88) : na, title="Reactive Cheap Zone Fill")
fill(p75Plot, p25Plot, color=showAdaptiveBands and showZoneFills ? color.new(color.gray, 94) : na, title="Reactive Normal Zone Fill")
plot(showFixedBands ? fixedUpper1 : na, title="Fixed Upper Band 1", color=color.new(color.orange, 45), linewidth=1)
plot(showFixedBands ? fixedUpper2 : na, title="Fixed Upper Band 2", color=color.new(color.red, 45), linewidth=1)
plot(showFixedBands ? fixedLower1 : na, title="Fixed Lower Band 1", color=color.new(color.aqua, 45), linewidth=1)
plot(showFixedBands ? fixedLower2 : na, title="Fixed Lower Band 2", color=color.new(color.blue, 45), linewidth=1)
bgcolor(showBgHighlight and isExtremeExpensive ? color.new(color.red, 90) : showBgHighlight and isDeepDiscount ? color.new(color.blue, 90) : na)
//====================================================
// TABLE
//====================================================
f_x(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#.##") + "x"
f_pct(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#.##") + "%"
f_num(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#.##")
f_price(_value) =>
na(_value) ? "n/a" : str.tostring(_value, format.mintick)
f_bars(_value) =>
na(_value) ? "n/a" : str.tostring(_value, "#")
var table infoTable = table.new(position.top_right, 2, 18, border_width=1, border_color=color.new(color.gray, 75))
if showTable and barstate.islast
table.cell(infoTable, 0, 0, "Metric", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(infoTable, 1, 0, "Current", text_color=color.white, bgcolor=color.new(color.black, 0))
table.cell(infoTable, 0, 1, "Reactive / Reported P/E", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 1, f_x(priceReactivePE) + " / " + f_x(reportedPE), text_color=color.white, bgcolor=color.new(dynamicColor, 25))
table.cell(infoTable, 0, 2, "Smoothed Reactive P/E", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 2, f_x(smoothedReactivePE), text_color=color.white, bgcolor=color.new(dynamicColor, 25))
table.cell(infoTable, 0, 3, "Anchored Forward EPS", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 3, f_price(anchoredForwardEPS), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 4, "Reactive / Reported Rank", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 4, f_pct(reactivePercentRank) + " / " + f_pct(reportedPercentRank), text_color=color.white, bgcolor=color.new(dynamicColor, 25))
table.cell(infoTable, 0, 5, "Reactive / Reported Median", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 5, f_x(rP50) + " / " + f_x(reportedP50), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 6, "Reactive / Reported Premium", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 6, f_pct(reactivePremiumToMedian) + " / " + f_pct(reportedPremiumToMedian), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 7, "Reactive / Reported P95", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 7, f_x(rP95) + " / " + f_x(reportedP95), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 8, "Reactive / Reported P05", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 8, f_x(rP05) + " / " + f_x(reportedP05), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 9, "Reactive / Reported Yield", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 9, f_pct(reactiveEarningsYield) + " / " + f_pct(reportedEarningsYield), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 10, "Reactive Room to P95", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 10, f_x(reactiveRoomToP95), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 11, "Reactive Room above P05", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 11, f_x(reactiveRoomAboveP05), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 12, "EPS / Multiple Trend", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 12, epsTrendText + " / " + multipleTrendText, text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 13, "Driver", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 13, driverText, text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 14, "PEG / Fwd P/S", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 14, f_x(pegRatioData) + " / " + f_x(forwardPSData), text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 15, "Data Quality", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 15, reactiveDataQualityText + " / " + effectivePeriod + " / " + f_bars(barsSinceReportedUpdate) + " bars since anchor", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 0, 16, "Reactive Status", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 16, reactiveStatusText, text_color=color.white, bgcolor=color.new(statusColor, 20))
table.cell(infoTable, 0, 17, "Reported Status", text_color=color.white, bgcolor=color.new(color.black, 50))
table.cell(infoTable, 1, 17, reportedStatusText, text_color=color.white, bgcolor=color.new(color.black, 50))
if not showTable and barstate.islast
table.clear(infoTable, 0, 0, 1, 17)
//====================================================
// ALERTS — PRICE-REACTIVE ONLY
//====================================================
crossOver95 = ta.crossover(reactivePercentRank, 95)
crossUnder95 = ta.crossunder(reactivePercentRank, 95)
crossOver90 = ta.crossover(reactivePercentRank, 90)
crossUnder90 = ta.crossunder(reactivePercentRank, 90)
crossUnder5 = ta.crossunder(reactivePercentRank, 5)
crossOver5 = ta.crossover(reactivePercentRank, 5)
crossUnder10 = ta.crossunder(reactivePercentRank, 10)
crossOver10 = ta.crossover(reactivePercentRank, 10)
crossOverMedian = ta.crossover(rankReactivePE, rP50)
crossUnderMedian = ta.crossunder(rankReactivePE, rP50)
peCrossAboveSignal = ta.crossover(smoothedReactivePE, signalLine)
peCrossBelowSignal = ta.crossunder(smoothedReactivePE, signalLine)
enteredExtremeExpensive = isReady and crossOver95
exitedExtremeExpensive = isReady and crossUnder95
enteredExpensive = isReady and crossOver90
exitedExpensive = isReady and crossUnder90
enteredDeepDiscount = isReady and crossUnder5
exitedDeepDiscount = isReady and crossOver5
enteredCheap = isReady and crossUnder10
exitedCheap = isReady and crossOver10
expensiveMultipleCompression = isReady and reactivePercentRank >= 90 and peCrossBelowSignal
cheapReRating = isReady and reactivePercentRank <= 10 and peCrossAboveSignal
alertcondition(enteredExtremeExpensive, title="Entered Extreme Expensive Zone", message="Price-reactive forward P/E entered its 95th percentile extreme expensive zone.")
alertcondition(exitedExtremeExpensive, title="Exited Extreme Expensive Zone", message="Price-reactive forward P/E exited its 95th percentile extreme expensive zone.")
alertcondition(enteredExpensive, title="Entered Expensive Zone", message="Price-reactive forward P/E entered its 90th percentile expensive zone.")
alertcondition(exitedExpensive, title="Exited Expensive Zone", message="Price-reactive forward P/E exited its 90th percentile expensive zone.")
alertcondition(enteredDeepDiscount, title="Entered Deep Discount Zone", message="Price-reactive forward P/E entered its 5th percentile deep discount zone.")
alertcondition(exitedDeepDiscount, title="Exited Deep Discount Zone", message="Price-reactive forward P/E exited its 5th percentile deep discount zone.")
alertcondition(enteredCheap, title="Entered Historically Cheap Zone", message="Price-reactive forward P/E entered its 10th percentile historically cheap zone.")
alertcondition(exitedCheap, title="Exited Historically Cheap Zone", message="Price-reactive forward P/E exited its 10th percentile historically cheap zone.")
alertcondition(crossOverMedian, title="Reactive P/E Crossed Above Median", message="Price-reactive forward P/E crossed above its historical median.")
alertcondition(crossUnderMedian, title="Reactive P/E Crossed Below Median", message="Price-reactive forward P/E crossed below its historical median.")
alertcondition(expensiveMultipleCompression, title="Expensive Multiple Compression Warning", message="Price-reactive forward P/E is historically expensive and crossed below its signal line.")
alertcondition(cheapReRating, title="Cheap Re-Rating Warning", message="Price-reactive forward P/E is historically cheap and crossed above its signal line.")
オープンソーススクリプト
TradingViewの精神に則り、このスクリプトの作者はコードをオープンソースとして公開してくれました。トレーダーが内容を確認・検証できるようにという配慮です。作者に拍手を送りましょう!無料で利用できますが、コードの再公開はハウスルールに従う必要があります。
免責事項
これらの情報および投稿は、TradingViewが提供または承認する金融、投資、取引、またはその他の種類の助言もしくは推奨であることを意図したものではなく、またこれらに該当するものでもありません。詳細は利用規約をご覧ください。
オープンソーススクリプト
TradingViewの精神に則り、このスクリプトの作者はコードをオープンソースとして公開してくれました。トレーダーが内容を確認・検証できるようにという配慮です。作者に拍手を送りましょう!無料で利用できますが、コードの再公開はハウスルールに従う必要があります。
免責事項
これらの情報および投稿は、TradingViewが提供または承認する金融、投資、取引、またはその他の種類の助言もしくは推奨であることを意図したものではなく、またこれらに該当するものでもありません。詳細は利用規約をご覧ください。