#====================================================================================================================
# EPS LINE GRAPH
#--------------------------------------------------------------------------------------------------------------------
# Original concept by: @JohnMuchow
# Refined / expanded version
# By: Richard Campbell / AKA Tricky Rick
#
# WHAT THIS STUDY DOES
# - Plots actual reported EPS history in a lower study
# - Marks each actual EPS report with a color-coded square
# - Optionally adds no marker, arrows, bubbles, or arrows & bubbles for actual EPS events
# - Plots estimated EPS history and connects estimate points with a dashed line
# - Optionally colors main-chart price bars on actual EPS event bars
# - Displays current actual EPS, prior actual EPS, dollar delta, and percent delta in labels
# - Displays EPS Beat / Missed / Flat using actual EPS vs the estimate tied to that report snapshot
# - Displays current estimated EPS, previous estimated EPS, dollar delta, and percent delta in labels
# - Marks stock split / reverse split events with vertical lines
#
# COLOR LOGIC
# - Actual EPS Green = current actual EPS improved vs prior actual EPS
# - Actual EPS Red = current actual EPS declined vs prior actual EPS
# - Actual EPS Gray = flat or no valid prior comparison
# - Estimated EPS Green = current estimate is above current actual EPS reference
# - Estimated EPS Red = current estimate is below current actual EPS reference
# - Estimated EPS Gray = flat or no valid comparison
# - EPS Beat label Green = actual EPS came in above estimate
# - EPS Missed label Red = actual EPS came in below estimate
# - EPS Flat label Gray = actual EPS matched estimate
#====================================================================================================================
declare lower;
#====================================================================================================================
# INPUTS
#====================================================================================================================
input epsDisplay = {None, Arrows, Bubbles, default "Arrows & Bubbles"};
input showEstEPS = yes;
input colorEpsOnPriceBars = no;
input showZeroLine = yes;
input showEPSLabel = yes;
input bubbleOffset = 0.10;
input arrowOffset = 0.10;
#====================================================================================================================
# GLOBAL COLORS
#====================================================================================================================
DefineGlobalColor("EPSLine", CreateColor(0,127,191));
DefineGlobalColor("EPSUp", CreateColor(50, 205, 50));
DefineGlobalColor("EPSDown", CreateColor(220, 20, 60));
DefineGlobalColor("EPSFlat", Color.GRAY);
DefineGlobalColor("ZeroLine", CreateColor(134,1,17));
DefineGlobalColor("EPSPriceUp", Color.CYAN);
DefineGlobalColor("EPSPriceDown", Color.MAGENTA);
DefineGlobalColor("EPSPriceFlat", Color.GRAY);
DefineGlobalColor("LabelSpacer", Color.BLACK);
#====================================================================================================================
# CORE EPS DATA
#====================================================================================================================
def e = GetActualEarnings();
def hasEvent = !IsNaN(e);
def earnings = CompoundValue(1, if IsNaN(e) then earnings[1] else e, e);
def prevEPS = CompoundValue(1,
if hasEvent then earnings[1] else prevEPS[1],
Double.NaN
);
def isUp = hasEvent and !IsNaN(prevEPS) and e > prevEPS;
def isDown = hasEvent and !IsNaN(prevEPS) and e < prevEPS;
def trendState = CompoundValue(1,
if hasEvent then
if IsNaN(prevEPS) then 0
else if e > prevEPS then 1
else if e < prevEPS then -1
else 0
else trendState[1],
0
);
def epsDelta =
if !IsNaN(prevEPS) and !IsNaN(earnings)
then earnings - prevEPS
else Double.NaN;
def epsPctDelta =
if !IsNaN(prevEPS) and prevEPS != 0 and !IsNaN(earnings)
then ((earnings - prevEPS) / AbsValue(prevEPS)) * 100
else Double.NaN;
#====================================================================================================================
# ESTIMATED EPS DATA
#====================================================================================================================
def est = GetEstimatedEarnings();
def estEarnings = CompoundValue(1, if IsNaN(est) then estEarnings[1] else est, est);
def prevEstEPS = CompoundValue(1,
if !IsNaN(est) then estEarnings[1] else prevEstEPS[1],
Double.NaN
);
def estDeltaPrev =
if !IsNaN(estEarnings) and !IsNaN(prevEstEPS)
then estEarnings - prevEstEPS
else Double.NaN;
def estPctDeltaPrev =
if !IsNaN(estEarnings) and !IsNaN(prevEstEPS) and prevEstEPS != 0
then ((estEarnings - prevEstEPS) / AbsValue(prevEstEPS)) * 100
else Double.NaN;
#====================================================================================================================
# EARNINGs SURPISE EPS DATA
#====================================================================================================================
# EPS surprise vs carried estimate snapshot at the actual earnings report bar
def epsSurpriseDeltaRaw =
if hasEvent and !IsNaN(estEarnings)
then e - estEarnings
else Double.NaN;
def epsSurprisePctRaw =
if hasEvent and !IsNaN(estEarnings) and estEarnings != 0
then ((e - estEarnings) / AbsValue(estEarnings)) * 100
else Double.NaN;
def epsSurpriseDelta = CompoundValue(1,
if hasEvent and !IsNaN(estEarnings)
then epsSurpriseDeltaRaw
else epsSurpriseDelta[1],
Double.NaN
);
def epsSurprisePct = CompoundValue(1,
if hasEvent and !IsNaN(estEarnings) and estEarnings != 0
then epsSurprisePctRaw
else epsSurprisePct[1],
Double.NaN
);
def epsSurpriseState = CompoundValue(1,
if hasEvent and !IsNaN(estEarnings) then
if epsSurpriseDeltaRaw > 0 then 1
else if epsSurpriseDeltaRaw < 0 then -1
else 0
else epsSurpriseState[1],
0
);
#====================================================================================================================
# DISPLAY-MODE HELPERS
#====================================================================================================================
def showEPSArrows =
epsDisplay == epsDisplay.Arrows
or epsDisplay == epsDisplay."Arrows & Bubbles";
def showEPSArrowAndBubble =
epsDisplay == epsDisplay."Arrows & Bubbles";
#====================================================================================================================
# MAIN CHART PRICE-BAR REFERENCE
#====================================================================================================================
AssignPriceColor(
if colorEpsOnPriceBars and hasEvent then
if isUp then GlobalColor("EPSPriceUp")
else if isDown then GlobalColor("EPSPriceDown")
else GlobalColor("EPSPriceFlat")
else Color.CURRENT
);
#====================================================================================================================
# EPS EVENT SQUARE
#====================================================================================================================
plot EPS = if hasEvent then e else Double.NaN;
EPS.SetPaintingStrategy(PaintingStrategy.SQUARES);
EPS.SetLineWeight(5);
EPS.AssignValueColor(
if isUp then GlobalColor("EPSUp")
else if isDown then GlobalColor("EPSDown")
else GlobalColor("EPSFlat")
);
EPS.HideBubble();
#====================================================================================================================
# OPTIONAL ARROWS
#====================================================================================================================
plot EPSUpArrow =
if showEPSArrows and isUp
then e + arrowOffset
else Double.NaN;
EPSUpArrow.SetPaintingStrategy(PaintingStrategy.ARROW_UP);
EPSUpArrow.SetLineWeight(3);
EPSUpArrow.AssignValueColor(GlobalColor("EPSUp"));
EPSUpArrow.HideBubble();
EPSUpArrow.HideTitle();
plot EPSDownArrow =
if showEPSArrows and isDown
then e - arrowOffset
else Double.NaN;
EPSDownArrow.SetPaintingStrategy(PaintingStrategy.ARROW_DOWN);
EPSDownArrow.SetLineWeight(3);
EPSDownArrow.AssignValueColor(GlobalColor("EPSDown"));
EPSDownArrow.HideBubble();
EPSDownArrow.HideTitle();
#====================================================================================================================
# OPTIONAL BUBBLES
#====================================================================================================================
AddChartBubble(
epsDisplay == epsDisplay.Bubbles and hasEvent,
if isDown then e - bubbleOffset else e + bubbleOffset,
(if e < 0 then "-$" else "$") + AsText(Round(AbsValue(e), 2)),
if isUp then GlobalColor("EPSUp")
else if isDown then GlobalColor("EPSDown")
else GlobalColor("EPSFlat"),
if isDown then no else yes
);
AddChartBubble(
showEPSArrowAndBubble and hasEvent,
if isUp then e - bubbleOffset else e + bubbleOffset,
(if e < 0 then "-$" else "$") + AsText(Round(AbsValue(e), 2)),
if isUp then GlobalColor("EPSUp")
else if isDown then GlobalColor("EPSDown")
else GlobalColor("EPSFlat"),
if isUp then no else yes
);
#====================================================================================================================
# EPS LINE
#====================================================================================================================
plot EPSLine = e;
EPSLine.EnableApproximation();
EPSLine.SetPaintingStrategy(PaintingStrategy.POINTS);
EPSLine.SetDefaultColor(GlobalColor("EPSLine"));
EPSLine.SetLineWeight(2);
EPSLine.HideBubble();
EPSLine.HideTitle();
#====================================================================================================================
# ZERO LINE
#====================================================================================================================
plot ZeroLine = if showZeroLine then 0 else Double.NaN;
ZeroLine.SetDefaultColor(GlobalColor("ZeroLine"));
ZeroLine.SetLineWeight(1);
ZeroLine.SetStyle(Curve.MEDIUM_DASH);
ZeroLine.HideBubble();
ZeroLine.HideTitle();
#====================================================================================================================
# ESTIMATED EPS
#====================================================================================================================
plot EstimatedEPS =
if showEstEPS then est
else Double.NaN;
EstimatedEPS.SetPaintingStrategy(PaintingStrategy.POINTS);
EstimatedEPS.SetLineWeight(3);
EstimatedEPS.AssignValueColor(
if !IsNaN(earnings) and !IsNaN(est) then
if est > earnings then GlobalColor("EPSUp")
else if est < earnings then GlobalColor("EPSDown")
else GlobalColor("EPSFlat")
else GlobalColor("EPSFlat")
);
EstimatedEPS.HideBubble();
plot EstimatedEPSLine =
if showEstEPS then est
else Double.NaN;
EstimatedEPSLine.SetStyle(Curve.SHORT_DASH);
EstimatedEPSLine.EnableApproximation();
EstimatedEPSLine.SetLineWeight(1);
EstimatedEPSLine.SetDefaultColor(Color.GRAY);
EstimatedEPSLine.HideBubble();
EstimatedEPSLine.HideTitle();
#====================================================================================================================
# SPLIT EVENT VERTICAL LINES
#====================================================================================================================
def splitNum = GetSplitNumerator();
def splitDen = GetSplitDenominator();
def hasSplit = !IsNaN(splitDen);
def isRegularSplit = hasSplit and splitNum > splitDen;
def isReverseSplit = hasSplit and splitNum < splitDen;
AddVerticalLine(
hasSplit,
if isRegularSplit then "Split " + AsText(splitNum, "%1$.0f") + ":" + AsText(splitDen, "%1$.0f")
else if isReverseSplit then "Reverse " + AsText(splitNum, "%1$.0f") + ":" + AsText(splitDen, "%1$.0f")
else "Split " + AsText(splitNum, "%1$.0f") + ":" + AsText(splitDen, "%1$.0f"),
if isRegularSplit then GlobalColor("EPSUp")
else if isReverseSplit then GlobalColor("EPSDown")
else GlobalColor("EPSFlat"),
Curve.SHORT_DASH
);
#====================================================================================================================
# LABELS
#====================================================================================================================
AddLabel(showEPSLabel, " EPS Line Graph ", Color.LIGHT_GREEN);
AddLabel(showEPSLabel, " ", GlobalColor("LabelSpacer"));
AddLabel(showEPSLabel and !IsNaN(epsSurpriseDelta),
" EPS " +
(if epsSurpriseState == 1 then "Beat: "
else if epsSurpriseState == -1 then "Missed: "
else "Flat: ") +
(if epsSurpriseDelta < 0 then "-$" else "+$") + AsText(Round(AbsValue(epsSurpriseDelta), 2)) +
" | " +
(if !IsNaN(epsSurprisePct)
then AsText(Round(epsSurprisePct, 1)) + " % "
else "N/A"),
if epsSurpriseState == 1 then GlobalColor("EPSUp")
else if epsSurpriseState == -1 then GlobalColor("EPSDown")
else GlobalColor("EPSFlat")
);
AddLabel(showEPSLabel, " ", GlobalColor("LabelSpacer"));
AddLabel(showEPSLabel and !IsNaN(earnings),
" EPS: " + (if earnings < 0 then "-$" else "$") + AsText(Round(AbsValue(earnings), 2)) +
(if !IsNaN(prevEPS)
then " Prev: " + (if prevEPS < 0 then "-$" else "$") + AsText(Round(AbsValue(prevEPS), 2)) + " "
else ""),
if trendState == 1 then GlobalColor("EPSUp")
else if trendState == -1 then GlobalColor("EPSDown")
else GlobalColor("EPSFlat")
);
AddLabel(showEPSLabel and !IsNaN(earnings) and !IsNaN(prevEPS),
" Δ: " + (if epsDelta < 0 then "-$" else "$") + AsText(Round(AbsValue(epsDelta), 2)) +
" or " +
(if !IsNaN(epsPctDelta)
then AsText(Round(epsPctDelta, 1)) + " % "
else "N/A"),
Color.LIGHT_GRAY
);
AddLabel(showEPSLabel and showEstEPS, " ", GlobalColor("LabelSpacer"));
AddLabel(showEPSLabel and showEstEPS and !IsNaN(estEarnings) and !IsNaN(earnings),
" Estimated EPS: " + (if estEarnings < 0 then "-$" else "$") + AsText(Round(AbsValue(estEarnings), 2)) +
(if !IsNaN(prevEstEPS)
then " Prev Est: " + (if prevEstEPS < 0 then "-$" else "$") + AsText(Round(AbsValue(prevEstEPS), 2)) + " "
else ""),
if estEarnings > earnings then GlobalColor("EPSUp")
else if estEarnings < earnings then GlobalColor("EPSDown")
else GlobalColor("EPSFlat")
);
AddLabel(showEPSLabel and showEstEPS and !IsNaN(estEarnings) and !IsNaN(prevEstEPS),
" Δ: " + (if estDeltaPrev < 0 then "-$" else "$") + AsText(Round(AbsValue(estDeltaPrev), 2)) +
" or " +
(if !IsNaN(estPctDeltaPrev)
then AsText(Round(estPctDeltaPrev, 1)) + " % "
else "N/A "),
Color.LIGHT_GRAY
);
AddLabel(showEPSLabel and colorEpsOnPriceBars, " ", GlobalColor("LabelSpacer"));
AddLabel(showEPSLabel and colorEpsOnPriceBars, " EPS Candle Reference is Active ", Color.CYAN);
def hasAnyEPSInLoadedBars = HighestAll(if hasEvent then 1 else 0) == 1;
AddLabel(showEPSLabel and !hasAnyEPSInLoadedBars,
" No EPS data found in loaded chart history ",
Color.YELLOW
);