ThinkorSwim Indicators for Basic Fundamental Analysis

ugaotrader

Member
Share my template for "fundamentals" ,everything is better of course.

Data Labels

Code:
#Chart Label to Provide Trading  Information
#Adaption from Various Sources by zzz 15 Feb 2013

#Open, High, Low, Close for Day
input Show_Label = Yes;
def P = Round(close(period = AggregationPeriod.DAY)[1], 2);
def H = Round(high(period = AggregationPeriod.DAY), 2);
def L = Round(low(period = AggregationPeriod.DAY), 2);
def O = Round(open(period = AggregationPeriod.DAY), 2);

AddLabel(Show_Label, text = Concat("P ", P), if P < O then Color.RED else Color.GREEN);
AddLabel(Show_Label, text = Concat("O ", O), if P > O then Color.RED else Color.GREEN);
AddLabel(Show_Label, "Gap " + AbsValue(P - O), if P > O then Color.RED else if P == O then Color.YELLOW else Color.GREEN);
AddLabel(Show_Label, if P > O and H > P or P <= O and L < P then "Gap Fill" else "Gap Left " + (if P > O and H < P then AbsValue(P - H) else AbsValue(P - L)), if (P > O and H < P) then Color.RED else if P <= O and L > P then Color.GREEN else Color.WHITE);
AddLabel(Show_Label, text = Concat("H ", H), color = Color.WHITE);
AddLabel(Show_Label, text = Concat("L ", L), color = Color.WHITE);
AddLabel(Show_Label, "NC_Open " + Round((close - open(period = AggregationPeriod.DAY)), 2), if open(period = AggregationPeriod.DAY) > close then Color.RED else Color.GREEN);
AddLabel(Show_Label, "NC_Close " + Round((close - close(period = AggregationPeriod.DAY)[1]), 2), if close(period = AggregationPeriod.DAY)[1] > close then Color.RED else Color.GREEN);
AddLabel(Show_Label, Concat(" HH52 " , Highest(high(period = AggregationPeriod.DAY), 252)), Color.GREEN);
AddLabel(Show_Label, "%52W_H: " + AsPercent((close - Highest(high(period = AggregationPeriod.DAY), 252)) / Highest(high(period = AggregationPeriod.DAY), 252)), Color.RED);
AddLabel(Show_Label, Concat( " LL52 ", Lowest(low(period = AggregationPeriod.DAY), 252)), Color.RED);
AddLabel(Show_Label, "%52W_L: " + AsPercent((close - Lowest(low(period = AggregationPeriod.DAY), 252)) / Lowest(low(period = AggregationPeriod.DAY), 252)), Color.GREEN);

#ATR and Daily Range
input atrlength = 5;
def null1 = Double.NaN;
AddLabel(Show_Label, text = Concat("R ", H - L), if O > close then Color.RED else Color.GREEN);
AddLabel(Show_Label, Concat("ADR ",  Round(Average(high(period = AggregationPeriod.DAY) - low(period = AggregationPeriod.DAY), atrlength), 2)), if O > close then Color.RED else Color.GREEN);
AddLabel(Show_Label, text = if (H - L) - Round(Average(high(period = AggregationPeriod.DAY) - low(period = AggregationPeriod.DAY), atrlength), 2) == 0 then "ADR Filled" else "ADR Left " + (if (H - L) - Round(Average(high(period = AggregationPeriod.DAY) - low(period = AggregationPeriod.DAY), atrlength), 2) < 0 then  AbsValue((H - L) - Round(Average(high(period = AggregationPeriod.DAY) - low(period = AggregationPeriod.DAY), atrlength), 2)) else 0) , if (H - L) - Round(Average(high(period = AggregationPeriod.DAY) - low(period = AggregationPeriod.DAY), atrlength), 2) < 0 then Color.GREEN else Color.WHITE);
def ATR2 = Average(TrueRange(high(period = AggregationPeriod.DAY), close(period = AggregationPeriod.DAY), low(period = AggregationPeriod.DAY)), atrlength);
AddLabel(Show_Label, Concat("ATR: D ", Round(ATR2, 2) + Concat(" B " , Round(number = Average(TrueRange(high=high, close=close, low=low), length=atrlength)))), Color.YELLOW);

#Volume
def avgvol = Floor(Average(volume, 10));
AddLabel(Show_Label, "BV " + if volume > 0 then volume else 0, if volume < volume[1] then Color.RED else Color.GREEN);
AddLabel(Show_Label, "DV " + volume(period = AggregationPeriod.DAY), if volume(period = AggregationPeriod.DAY) < Average(volume(period = AggregationPeriod.DAY), 10) then Color.RED else Color.GREEN);

#Current Tick Reading
input u = 0;
input lo = -0;
def it = close ("$tick");
AddLabel(Show_Label, Concat("Tick ", it), if it > u then Color.GREEN else if it < lo then Color.LIGHT_RED else Color.WHITE);

Earning Trend

Code:
#Follow @KRose_TDA on twitter for free updates posted for this and other scripts
#Earnings trend study is used as part of the generating income with dividend stocks webcast presented Monday nights @ 7PM ET   https://events.thinkorswim.com/#/webcast
# I build custom studies like earnings trend as part of my Thursday night thinkscript webinar @ 5:30PM ET 
#     https://events.thinkorswim.com/#/webcast
#This study creates a line chart tracking the trend of a stock's quarterly EPS values(it doesn't work for ETFs).A red square appears when Earnings Per Share(EPS)is lower than the prior quarter's EPS, and a dark green square appears when EPS is higher than the previous quarter's EPS. The arrows represent a comparison of an analyst's estimated EPS for that quarter versus the actual EPS. If the arrow is colored Green, the company's actual EPS was greater than the analyst estimates. If the actual EPS was less than the analyst estimates, the arrow will be Red. The point of the arrow equals analyst estimate value.

declare lower;
declare Hide_on_intraday;

def EPS = if !isNaN(GetActualEarnings()) then GetActualEarnings() else EPS[1];

plot EPS_line_chart = GetActualEarnings();
EPS_line_chart.EnableApproximation();
EPS_line_chart.SetDefaultColor(color.black);

plot earnings_date = GetActualEarnings();
earnings_date.SetPaintingStrategy(PaintingStrategy.squares);

earnings_date.AssignValueColor(if EPS > EPS[1] then color.dark_green else color.red);
earnings_date.SetLineWeight(5);

plot EstEarning = GetEstimatedEarnings();
EstEarning.SetPaintingStrategy(PaintingStrategy.arrow_up );

EstEarning.AssignValueColor (if EstEarning < EPS then color.DARK_GREEN else color.DARK_RED);
EstEarning.SetLineWeight(5);

# end code

Squeeze Bars

Code:
#hint;A WatchList Column (WLC) that shows whether an equity is in a squeeze and if so how many bars it has been in a squeeze. Be sure to set the agg to the chart agg you want to view this on. This is very efficient code.

def Squeeze = if (reference BollingerBands()."UpperBand" - KeltnerChannels()."Upper_Band") < 0 then 1 else 0;
def count = compoundvalue(1, if Squeeze then count[1] + 1 else if !Squeeze then 0 else 0, 1);

#addlabel(1, "Bars into a squeeze = " + count, If squeeze then Color.LIGHT_RED else color.Current);
#if column width is a concern the label below shortens the word length and shows the count value

AddLabel(1, "SQZ= " + count, if Squeeze then Color.LIGHT_RED else Color.CURRENT);
# end

Chris' Enhanced Volume

Code:
#Chris' Enhanced Volume


###############
# Body
###############
input Audible_Alert = yes;
def Deviation_Length = 60;
def Deviate = 2;
def volumestdev = RelativeVolumeStDev(length = Deviation_Length);
def abovedev = volumestdev >= Deviate;
def belowdev = volumestdev <= Deviate;


###############
# Volume Bars
###############
plot volumereplace = volume;
volumereplace.SetPaintingStrategy(paintingStrategy = PaintingStrategy.HISTOGRAM);
def increase = volume > volume[1];
def devincrease = increase and abovedev;
def decrease = volume < volume[1];
def devdecrease = decrease and abovedev;
volumereplace.DefineColor("Increase", Color.DARK_GREEN);
volumereplace.DefineColor("DevIncrease", Color.GREEN);
volumereplace.DefineColor("Decrease", Color.DARK_RED);
volumereplace.DefineColor("DevDecrease", Color.LIGHT_RED);
volumereplace.AssignValueColor(
if devincrease then volumereplace.Color("DevIncrease")
else
if increase then volumereplace.Color("Increase")
else
if devdecrease then volumereplace.Color("DevDecrease")
else
volumereplace.Color("Decrease"));


###############
# Alerts
###############
Alert(devincrease and Audible_Alert, "Support/Resistance Detected", Alert.BAR, Sound.Bell);
Alert(devdecrease and Audible_Alert, "Support/Resistance Detected", Alert.BAR, Sound.Bell);

Vix Labels

Code:
# ------------------------
# Script para Embajadores Shark Trading System
# Gustavo Moyano
# V.1.0 Año 2018
# Creado y Documentado Edwin Londoño
# web: http://www.stsinnova.com/
# Correos:
#  [email protected]
#
# Mostrar en el grafico Diferentes indices
#
# Indicadores_Mercado
# V01.0.2018
# -----------------------------------------------------
#--------------------
declare upper;
input Temporalidad = AggregationPeriod.day;
Input Indicador=  {"SPY", "QQQ", "DIA", default "VIX"};
input Color_del_recuadro = {"magenta", "green", "pink", default "cyan", "orange", "red", "blue", "gray", "violet"};

def Indicador_ = close(Indicador,period = Temporalidad);

AddLabel(yes, Indicador +": " + Indicador_, GetColor(Color_del_recuadro));

Earnings Reaction

Code:
# Script displays earnings and securities reaction to the announcement
#Developed and Coded By Eric Rasmussen
# [email protected]

# Finding the Earnings Dates
def isBefore = HasEarnings(EarningTime.BEFORE_MARKET);
def isAfter = HasEarnings(EarningTime.AFTER_MARKET);

# Earnings Move Up or Down
def afterCalc = AbsValue(Round(close[-1] - close));
def beforeCalc = AbsValue(Round(close - close[1]));

def afterAbs = if isAfter then Round(AbsValue(((close[-1] - close) / close) * 100)) else 0;
def beforeAbs =  if isBefore then Round(AbsValue(((close - close[1]) / close) * 100)) else 0;

def earningsPrice = if isAfter then afterCalc else if isBefore then beforeCalc else 0;

# Price Percent Calculations
def afterPercent = if isAfter then Round(((close[-1] - close) / close) * 100) else 0;
def beforePercent = if isBefore then Round(((close - close[1]) / close) * 100) else 0;

def earningsPercent = if isAfter then afterPercent else if isBefore then beforePercent else 0;

def absPercent = if isAfter then afterAbs else if isBefore then beforeAbs else 0;

# Records Number of Earnings Periods
def aMove = if HasEarnings(EarningTime.AFTER_MARKET) then TotalSum(earningsPrice) else aMove[1];

def bMove = if HasEarnings(EarningTime.BEFORE_MARKET) then TotalSum(earningsPrice) else bMove[1];

def earningsPeriods = if HasEarnings() then 1 else 0;

# Average Earnings Move calculation
def periodSum = Sum(earningsPeriods, length = 252);
def Sum = Sum(earningsPrice, length = 252);
def averageMove = Sum / periodSum;
def percentSum = Sum(absPercent, length = 252);
def percentAvg = percentSum / periodSum;
def avgMove = if !IsNaN(averageMove) then averageMove else avgMove[1];
def percentAvgMove = if !IsNaN(averageMove) then percentAvg else percentAvgMove[1];

# Actual Earnings and Estimates Info
def AECont = if HasEarnings() then GetActualEarnings() else AECont[1];
def EECont = if HasEarnings() then GetEstimatedEarnings() else EECont[1];

def actualEarnings = if !IsNaN(AECont) then AECont else actualEarnings[1];
def estimatedEarnings = if !IsNaN(EECont) then EECont else estimatedEarnings[1];

def EarningsBeat =  !IsNaN(GetActualEarnings()) and HasEarnings() and actualEarnings > estimatedEarnings;
def EarningsMiss =  !IsNaN(GetActualEarnings()) and HasEarnings() and actualEarnings < estimatedEarnings;

# Earnings Move Calculations
def aCalc =  Round(close[-1] - close);
def bCalc = Round(close - close[1]);

def ePrice = if isAfter then aCalc else if isBefore then bCalc else ePrice[1];

def earningsNaN = if HasEarnings() and IsNaN(GetEstimatedEarnings()) then 1 else earningsNaN[1];
def earningsSum = TotalSum(HasEarnings()) - earningsNaN;
def earningsMove =  if HasEarnings() then ePrice else earningsMove[1];

def uM = HasEarnings() and earningsMove > 0;
def dM = HasEarnings() and earningsMove < 0;

def beatRatio =  Round((TotalSum(EarningsBeat) / earningsSum) * 100);

# Look And Feel
 
# Average Move Label (Percentage)
input percentLabel = yes;
AddLabel(percentLabel, Concat(Concat("Average Earnings Move: ", Round(percentAvgMove, 2)), "%"), if earningsPrice[1] > avgMove[1] then Color.MAGENTA else Color.LIME);

# Percentage of Earnings Beats Label
input beatRatioLabel = yes;
AddLabel(beatRatioLabel, Concat(Concat("Earnings Beats: ", beatRatio), "%"), color = if beatRatio >= 80 then Color.GREEN else Color.RED);

# Bubbles Showing Percent Moves After Earnings
input percentBubbles = yes;

AddChartBubble("price location" = high, text = Concat(earningsPercent, "%"), "time condition" =  if percentBubbles == yes then earningsPercent else 0, color = if isAfter and close[-1] > close then Color.MAGENTA else if isAfter and close[-1] < close then Color.LIME else if isBefore and close > close[1] then Color.MAGENTA else if isBefore and close < close[1] then Color.LIME else Color.BLACK);

# Date Line for Earnings

input text = no;

AddVerticalLine(HasEarnings() and EarningsBeat and uM, color = Color.CYAN, stroke = Curve.FIRM, text = if text == yes then concat(if isBefore then "(Before) " else "(After) ", concat("$", concat(EECont, concat(" Actual",concat("  $", concat(AECont, " Estimated")))))) else "");

AddVerticalLine(HasEarnings() and EarningsBeat and dM, color = Color.MAGENTA, stroke = Curve.FIRM, text = if text == yes then concat(if isBefore then "(Before) " else "(After) ", concat("$", concat(EECont, concat(" Actual",concat("  $", concat(AECont, " Estimated")))))) else "");

AddVerticalLine(HasEarnings() and EarningsMiss and uM, color = Color.LIME, stroke = Curve.FIRM, text = if text == yes then concat(if isBefore then "(Before) " else "(After) ", concat("$", concat(EECont, concat(" Actual",concat("  $", concat(AECont, " Estimated")))))) else "");

AddVerticalLine(HasEarnings() and EarningsMiss and dM, color = Color.RED, stroke = Curve.FIRM, text = if text == yes then concat(if isBefore then "(Before) " else "(After) ", concat("$", concat(EECont, concat(" Actual",concat("  $", concat(AECont, " Estimated")))))) else "");

Earnings Statistic

Code:
# Archive Name: Earnings Statistics Paris

# Archive Section: Scripts-Earnings

# Suggested Tos Name: EarningsStatistics_Paris

# Archive Date: 5.07.2018

# Archive Notes: 

# Earnings Statisques

# Paris

# 5.6.2018

 

# This study displays the following earnings related info/stats:

#

# ++ Vertical lines that mark each earnings cycle and earnings date

# ++ Bubble that measures the gap and percentage moves post earnings

# ++ Total number of gaps ups versus gap downs

# ++ Average gap ups versus gap downs

# ++ Average percentage moves up versus percentage moves down

# ++ If the next earnings cycle happens within the next "ExpansionBars", 

#    the earnings date is displayed, otherwise it is not displayed

 

declare hide_on_intraday;

 

input ExpansionBars = 11;

input BubbleOffset = 2;

 

def bn = BarNumber();

def Month = GetMonth();

def Day = GetDayOfMonth(GetYYYYMMDD());

 

def isBefore = HasEarnings(EarningTime.BEFORE_MARKET);

def isAfter  = HasEarnings(EarningTime.AFTER_MARKET);

def isToday  = isBefore or isAfter or (HasEarnings() and !isBefore and !isAfter);

 

def EarningsBN  = if HasEarnings(EarningTime.BEFORE_MARKET) then bn[1] 

else if HasEarnings(EarningTime.AFTER_MARKET) then bn 

else EarningsBN[1]; 

def DayAfterBN = if bn - EarningsBN == 1 then bn else Double.NaN;

 

AddVerticalLine(isToday, "        " + Month + "/" + Day + " Earnings", Color.ORANGE, Curve.SHORT_DASH);

 

def EGap = fold y = 0 to 1 

while DayAfterBN do 

if (bn - DayAfterBN) <= 1 then open - close[1]

else Double.NaN;

 

def EPct = fold z = 0 to 1 

while DayAfterBN do 

if (bn - DayAfterBN) <= 1 then (open - close[1]) / close[1]

else Double.NaN;

 

def gapUp = EGap > 0;

def gapDn = EGap < 0;

 

def gapUpTotal = if EGap > 0 then gapUpTotal[1] + EGap else gapUpTotal[1];

def gapDnTotal = if EGap < 0 then gapDnTotal[1] + EGap else gapDnTotal[1];

def pctUpTotal = if EGap > 0 then pctUpTotal[1] + EPct else pctUpTotal[1];

def pctDnTotal = if EGap < 0 then pctDnTotal[1] + EPct else pctDnTotal[1];

 

AddChartBubble(gapUp, high[1] + TickSize() * bubbleOffset, "GapUp: $" + EGap + "\nPctUp: " + AsPercent(EPct), Color.LIGHT_Green);

AddChartBubble(gapDn, low[1] - TickSize() * bubbleOffset, "GapDn: $" + EGap + "\nPctDn: " + AsPercent(EPct), Color.PINK);

 

AddLabel(1, "# Earnings Gap Up: " + TotalSum(gapUp), Color.LIGHT_GREEN);

AddLabel(1, "# Earnings Gap Down: " + TotalSum(gapDn), Color.PINK);

AddLabel(1, "Average Gap Up: " + Round(gapUpTotal/TotalSum(gapUp),2), Color.LIGHT_GREEN);

AddLabel(1, "Average Gap Down: " + Round(gapDnTotal/TotalSum(gapDn),2), Color.PINK);

AddLabel(1, "Average Pct Up: " + AsPercent(pctUpTotal/TotalSum(gapUp)), Color.LIGHT_GREEN);

AddLabel(1, "Average Pct Down: " + AsPercent(pctDnTotal/TotalSum(gapDn)), Color.PINK);

 

###--- Process next earnings date

 

def Earnings = AbsValue(GetEventOffset(Events.Earnings, 0));

def DTE = if isNaN(Earnings) then 0 else Earnings;

def NextEarningsBN = if !isNaN(Earnings) then bn + Earnings else Double.NaN;

def EarningsBNX = bn == HighestAll(NextEarningsBN);

def NextEarningsMth = HighestAll(if EarningsBNX then Month else Double.NaN);

def NextEarningsDay = HighestAll(if EarningsBNX then Day   else Double.NaN);

 

AddLabel(DTE < ExpansionBars and DTE > 0, "Upcoming Earnings Date: " + NextEarningsMth + "/" + NextEarningsDay, Color.YELLOW);

 

# END STUDY

Dividends Return

Code:
#BEST
#Total Return created by [email protected]
#This study shows the total Return: Div + Price Appreciation
#On the chart for the chart period showing

#declare lower;
def close1 = First(close);

rec TotalDiv = totalDiv[1] + if IsNaN(GetDividend()) then 0 else  GetDividend();
def PriceReturn = round((close - close1)/(close1)*100,2);
def DividendReturn = if totaldiv > 0 then round(TotalDiv / close1 * 100,2)else 0 ;

#addlabel(yes,"close1 " + close1 + " Close " + close );

addlabel (yes,"Price Return= "+ PriceReturn +"% " + " Dividend Return= " + DividendReturn +"% " + "Total % Return= " + round(Pricereturn + DividendReturn,2));

Entire collection

https://tos.mx/AV4Tz3k
 
Last edited by a moderator:

MoneyMaker123

New member
VIP
Hello everyone. New VIP member here. Not sure if this is the correct forum to ask this question. My focus is on long term investments so I'm looking to add key fundamentals data such operating margin, revenue growth etc to the chart as labels so I can look at them all in one place. Anyone has any code created to pull the fundamentals data?
 

MerryDay

Administrative
Staff member
Staff
VIP
@MoneyMaker123 I have one. Before I post it, I want to have a printed definition for each fundamental. It's slow going trying to explain that a high P/E ratio could be way overvalued (bad) or a really hot stock (good). I have a label for each of TOS's fundamentals so it's slow-going but I should be finished by the weekend.

If you can't wait, you can make your own. You can find a list of them in the scanner. Click on Add filter and then scroll down to Fundamental. They are listed in studies also.

YbOPUTd.png
 

MoneyMaker123

New member
VIP
@MerryDay Thank you for the quick response. I can certainly wait till next week to get my hands on the fundamentals script. At the same time, I like to learn how to this "scanner" that you mentioned. Is that a Study? How can I import it? Thanks much!
 

MoneyMaker123

New member
VIP
@MerryDay I was able to access the scanner and I see the Fundamentals as a filter criteria so thank you! In my case, I want to add these data points to my chart as labels so you mentioned these Fundamentals are available as Studies also but I can't find them when I look at the list of Studies under Edit Studies. Maybe I'm not looking at the right place? Can you help please?
 

BenTen

Administrative
Staff member
Staff
VIP
@MoneyMaker123 I believe those fundamental data are only available under the Scan and Watchlist section. Those data are not available as separate studies.
 

number9

New member
VIP
Of the four main scans I use daily to day and swing trade (stochastic crossover, Wolfe Wave, Advanced Market Moves, Buy the Dip), they all share in common a few basic stock and study filters (price, average volume, RSI, MACD). One type of filter I’ve never incorporated is Fundamentals. Does anyone have any Fundamental filter(s) they find essential or like to use? Any other must haves that are not the typical ones?
 

ChuckRosselli

New member
Okay. Here are 4 screenshots to show the following:
1. Overall Graham Number scan - I only scan in the S&P 500 because too many stocks cause the scanner to timeout.
The scan looks for companies with current price LESS THAN their Graham Number - IE. perhaps a good buy according to Benjamin Graham.

JSc6aye.jpg



2. The specific Graham Number scanner code - This is for the last custom condition of the scan.

wn6qW1P.jpg



3. The chart style "look & feel" that prints out a stock's Graham Number - Aggregation must be set to 2 years or more with 1 week bars or the Graham Number will be shown as 0. (Why? I have no idea).

1VGBTDO.jpg



4. The specific Graham Number study code to print out the stock chart labels.

Z2XJdfX.jpg



Note that I do not display the Graham Number of the found stocks in the Graham Number scan columns. I felt that understanding whether the stock is a good buy can only be understood in the context of the stock's chart with its Graham Number. Having a scan column listing Graham Number would be more work and would probably not be helpful. However, simply bring up a stock's chart using the described style (with the Graham Number chart study) and the Graham Number will be shown in a label at the bottom next to the stock's current price.

Let me know if you have questions.

Chuck
 
Last edited by a moderator:

Gellidus

New member
VIP
So here is my redone post with the images embedded directly in the post...


Okay. Here are 4 screenshots to show the following:
1. Overall Graham Number scan - I only scan in the S&P 500 because too many stocks cause the scanner to timeout.
The scan looks for companies with current price LESS THAN their Graham Number - IE. perhaps a good buy according to Benjamin Graham.

JSc6aye.jpg



2. The specific Graham Number scanner code - This is for the last custom condition of the scan.

wn6qW1P.jpg



3. The chart style "look & feel" that prints out a stock's Graham Number - Aggregation must be set to 2 years or more with 1 week bars or the Graham Number will be shown as 0. (Why? I have no idea).

1VGBTDO.jpg



4. The specific Graham Number study code to print out the stock chart labels.

Z2XJdfX.jpg



Note that I do not display the Graham Number of the found stocks in the Graham Number scan columns. I felt that understanding whether the stock is a good buy can only be understood in the context of the stock's chart with its Graham Number. Having a scan column listing Graham Number would be more work and would probably not be helpful. However, simply bring up a stock's chart using the described style (with the Graham Number chart study) and the Graham Number will be shown in a label at the bottom next to the stock's current price.

Let me know if you have questions.

Chuck
Thanks Chuck for sharing.

May I know if Graham Number is similar to Fair Price of the stock value?

Also your Season column seems interesting and may have their own stories to tell.....
 

ChuckRosselli

New member
Gellidus,

Yes, I think Graham Number is Benjamin Graham's idea of the Fair Price of a share of stock.

Benjamin Graham actually defined 2 metrics help determine if a stock was a good value or not.

The first, was Graham Number (which this post thread has been discussing):

Graham number - fair value of stock
= SQRT( 22.5 x EPS x BVPS )
EPS - Earnings per Share (for last year)
BVPS - Book Value per Share (as of most recent quarter)

(If I am calculating EPS and BVPS from other numbers, I only use shares of common stock & not preferred stock).

The other:

Graham Intrinsic Value (also called "Benjamin Graham Formula") - stock's value including time growth
= (EPS x (8.5 + (2 x 5 yr growth rate)) x 4.40 / (20 yr AAA corporate bond rate)
EPS - Earnings per Share (common + preferred)
8.5 - Typical PE ratio for a non-growth company
4.4 - Yield of corporate AAA bonds in 1962 (when model introduced)
Current 20 year AAA corporate bond rate - Available at: https://fred.stlouisfed.org/series/HQMCB20YR

(I calculate Graham Intrinsic Value both without and with dividends. To calculate Graham Intrinsic Value "Total Return" (with dividends), I add the yearly dividend yield to the yearly predicted growth rate).

You can look up both "Graham Number" and "Benjamin Graham Formula" on Wikipedia which gives a good summary of both.

I hope this helps.

Chuck
 

trader1618

New member
VIP
@ugaotrader and @BenTen I think there is a flaw in the calculation for the earnings, I was trying to follow the code but couldn't really get to the bottom for the change. Any input to fix will be appreciated. the chart currently has 7 completed earnings and 1 upcoming it calculates the upcoming earnings as a down by default and adds to the total, instead of just showing stats for 7 earnings only. Also I am not sure how this will affect the other dependent
calculations either.

0OA3QOL.png
 
Last edited:

ChuckRosselli

New member
Itrader1618, I see what you mean.

The chart with Graham Number study gives a total TTM (Trailing Twelve Months (IE. the last 4 quarters of EPS) ) of EPS of $6.71.
oaFKMjL.jpg


I took a look at the TOS Fundamentals page for MSFT:
niXk8Mb.jpg

The quarterly EPS listed there, when added together, equals $6.19 (which is wrong).
1.51 + 1.40 + 1.46 + 1.82 = 6.19

However, the TD Ameritrade web site Fundamentals page for MSFT is:
WwHrFML.jpg

The EPS-TTM returned by the chart study and the TD Ameritrade site is $6.71. This is correct.
1.40 + 1.46 + 1.82 + 2.03 = 6.71

The 1st value skips the last MSFT EPS from Q1 of 2021 (of 2.03) but includes the EPS from Q1 of 2020 (of 1.51) - so it's off by one quarter.

The study seems to work but I now mistrust the TOS Analyze=>Fundamentals page.

Is that what you see?

Chuck
 

Similar threads

Top