How to Build a Dividend Growth Investing Dashboard in Google Sheets

Update 4/28/2020:

I added a similar post, but using Python instead of Google Sheets to build the dashboard:

This is awesome!

I’ll soon be leaving my job and won’t have Microsoft Office paid for, so I’m migrating everything over to Google Sheets (and Docs, Slides, etc).

It’s been incredible. I believe you can do all this with Excel, but Google Sheets has been integrated with the internet from the start. I fairly quickly automated most of my personal portfolio. I can now check things on my phone that used to take me 15 minutes on a computer.

In the process, I made a Dividend Dashboard.

If you want to skip the tutorial and open/copy the sheet, here you go: Dividend Dozer’s Dividend Dashboard. Just log into your Google account and Menu >> Make a Copy. Then put your own values into the green cells and give it time to load.

I used to get my favorite DGI metrics from multiple sites, but now I have it all in front of me.

  • Dividend growth rate (over a number of years)
  • Current Yield
  • Payout Ratio
  • PE Ratio (and also that of the S&P 500)
  • Consecutive years of dividend increases
  • Share appreciation (over a number of years)

I’ll explain all my formulas if you’d like to follow along and make this for yourself. Buckle up – this will be a long ride.

Start off with a blank Google Sheets document. From now on, when I refer to “L6”, that means column L, row 6.

Enter a dividend growth ticker (I’ll use JNJ for this example)

Enter a number of years to look back (I’ll use 15, but you should use at least 5 until you get comfortable with the spreadsheet).

After the sheet is built, we will only need to enter a Ticker and a Number of Years to Look Back. Our automated spreadsheet will do the rest.

=query(importhtml(concatenate(“”,B3,”/dividend-history”),”table”,0),”select Col3, Col6 LIMIT 500″)

Change L6 color to gray (to represent an equation being present).

Format all of Column L to be in currency ($).

It’s weird to start off in L6, but this will leave room for our dashboard later.

If you haven’t automated much in spreadsheets, prepared to be amazed. We are importing Nasdaq’s record of dividend payments into our spreadsheet. The concatenate function inserts our B3 ticker symbol into a url structure, and Google sheets does the rest of the work with the importhtml and query functions.

importhtml imports the entire table, and the query function selects only the columns we want (cash amount and payment date).

It’s basically magic. There’s going to be a lot more of this.

Next, we want to detect when there is a dividend increase, and calculate that increase.

Hard-enter a column title, “% Increase.”


Drag N7 down all the way to N506 and shade these cells gray to indicate formulas being present. Also, make all of Column N a percentage format ($).

*Why N506? Because our dividend import limits itself to 500 rows of data. Even a monthly dividend payer will only produce 240 rows in 20 years, so I am confident 500 is enough.

What is this doing? It’s just taking the most recent dividend payment and comparing it against the second-most recent. If there is no dividend raise, the cell shows as nothing. Likewise, if it is the oldest record, the cell will produce an error, but iferror also creates an output of nothing. If there is an increase (or decrease), the cell will show the % change.

Nice work. We now have an unfiltered dividend history import. I like to surround this entire table with a border to keep things organized. I also hard-entered “Unfiltered” in L5 to title to this table.

On to the second table (of three)…

If we want to see only two years of data, but are importing 5 years, we will need to create a filter of the first table. First, create the headers:

=concatenate(“Dividend data filtered for last “,round((max(Q:Q)min(Q:Q))/365.25,1),” years”)

Hard enter, “Cash Amount.”

Hard enter, “Payment Date.”


Hard enter, “Historic Yield”

Shade cells with formulas gray if you’d like to keep using my format.

The second table title is a long concatenate formula. Remember, concatenate means to combine, and we are just combining words and cells values.

round((max(Q:Q)-min(Q:Q))/365.25,1) generates the number of years in the title. It subtracts the newest date from the oldest date to come up with a number of years of data. Then, it rounds it off to the nearest decimal, which is why the “1” is in the formula.

Next, create the formulas for the body of this table:


*Notice we leave nothing between the two commas. This will come in handy later when we look for the oldest dividend payment (furthest non-blank cell down).



=iferror(P7*$J$3/(index(V:V, match(int(Q7),W:W,1),0)),)

Drag these four formulas down to row 506 and shade all these cells gray.

Format Column P as currency ($). Format Columns R and S as percentages (%).

What did we just do? We are now filtering the first table to only show what data between today and the number of years back that we entered in C3. This will help us build our charts later on.

We also added a new column, “Historic Yield.” For now, it is blank. In a minute, we are going to populate a third table to import historic closing prices for our ticker. The historic yield column is going to match dividend payments with the stock price on that day in history, and tell us what the yield was throughout the duration of dividend payments.

To help further understand the formulas,

  • today() is a handy Google Sheets function that produces today’s date.
  • today()-365.25*(number of years to look back) creates a range of years.
  • index(match()) lets Google sheets look for a date and return a stock price for that date. We will need to create a few more data imports for these to display.

Hopefully, your second table looks like this:

On to our third table (keep going, this is an easy one).

Start with the headers:

Hard-enter “Price History.”

=googlefinance(B3, “price”, min(Q:Q), today(), “Daily”)

Hard enter, “Close.”

Hard-enter “Date (integer)”

googlefinance() is a super cool function that looks for a ticker symbol (B3, or JNJ in our case). Then, it looks for a start and end date to import daily or weekly closing prices. We use min(Q:Q) to find the date of the earliest dividend payment.

Now, enter the column formulas:

Scroll to the very bottom of the sheet and add rows until you have 13,000 rows. This will allow for 50 years of closing price data, if we ever have that much to look at.

=if(U7=“”,, int(U7))

Drag the formula in W7 down to W13000 and shade these in as gray.

Create a border if you like.

What did we just do here? googlefinance() imports dates with times attached. Sheets stores dates and times as something like 41527.755. The 41527 indicates a date, and the .755 indicates a time in that day. We want to match historic dividend data with historic closing prices, so making the date into an integer (whole number) eliminates the time portion and allows for an exact match later on.

We can now hide the columns with tables (L-W). We will check boxes in the charts later to tell Sheets to keep using hidden data.

Now, to manually bring in the Dividend Champions spreadsheet.

First, create a new tab in your Google Sheet. Name it “Dividend Streaks (Updated 1/30/2019)” or whatever the current date is. You will have to manually update this as often as you like to keep the dividend streaks current.

If you haven’t downloaded the US Dividend Champions Spreadsheet, it is one of the most effective dividend growth screeners out there. Originally maintained by David Fish, it is now maintained by Justin Law (thank you, Justin). You can find the link to the US Dividend Champions, Justin Law, and other non-US champions at

Download the champions sheet, go to the “All CCC” tab, and select all. Copy and paste into Cell D16 of your google sheet. This will leave a bit of room to the left and to the top. I paste the link to the champions sheet to the left in Cell A15, and enter tickers above that might not be in the sheet, for instance VTI. You can hand-enter other tickers if necessary.

If you want Canadian or UK DGI stocks, add them so the tickers are in Column E, the streak is in Column H, and the payouts/year are in Column O.

Congrats! We are ready to start making our Dividend Dashboard.

Alright, let’s switch back to our first tab. I renamed it “Div Graph” at this point to keep things clear. Now for the fun part, the Dividend Dashboard!

Hard-enter “Ticker.”

Hard-enter “Years to Look Back.”

Hard-enter “Current Share Price”

=concatenate(“Compound Annual Dividend Growth Rate”, char(10),”(Last “,round((max(Q:Q)-min(Q:Q))/365.25,0),” years)”)

Hard-enter “Current Yield.”

Hard-enter “Payout Ratio (Forward).”

Hard-enter “PE Ratio (vs SP500).”

Hard-enter “Consecutive Dividend Increases (Years).”

Hard-enter “Dividends per Year”

Shade these any color you like, and put borders around each cell.

The only formula here is in column E, and uses a concatenate function to auto-generate how many years of data we are looking at. The “char(10)” acts as a return.

On to the numeric dashboard:

We already have our Ticker and Years to Look Back populated with JNJ and 15. The rest of these are going to be some fun formulas.



Our first formula here is familiar. Instead of giving googlefinance() a date range, we just get the current price for the ticker we are feeding it in B3.

Our second formula looks like a mess. The underlying
compound annual growth rate (CAGR) equation is actually not so bad:

I’ll let you dig more into this one if you like, but we are breaking it down into:

  • Ending Value: Most recent dividend payment value (imported as cell P7)
  • Beginning Value: Oldest dividend payment value in Column P (last non-blank cell)
  • # Years: Difference between oldest and newest payment date

Pretty cool! If you’re still with me, I think you are a true spreadsheet lover.



You’ll have to trust me on these two. Our yield column still needs another piece. For now, we are calling googlefinance(B3,”eps”) to get earnings per share. The payout ratio is (Dividends per share / Earnings Per Share). We are missing J3, dividend payments per year, but not for long.

=concatenate(B3,”: “,googlefinance(B3,”pe”),char(10),”sp500: “,query(IMPORTXML(“”,”//div[@id=’current’]”), “select Col2 LIMIT 1”))

=iferror(index(‘Dividend Streaks (Updated 1/30/2019)’!H:H, match(B3,’Dividend Streaks (Updated 1/30/2019)’!E:E,0),0),”Not Listed”)

=iferror(index(‘Dividend Streaks (Updated 1/30/2019)’!O:O, match(B3,’Dividend Streaks (Updated 1/30/2019)’!E:E,0),0),”Not Listed”)

The H3 equation is interesting. We are pulling the current PE ratio for our ticker from Google, then also pulling the overall S&P 500 PE ratio from using IMPORTXML(). This makes it easy to compare our stock’s PE ratio with that of the overall market.

The I3 equation matches our ticker with tickers on our Dividend Streaks tab, and returns the number of years of consecutive increases.

The J3 equation does the same thing as the I3 equation, but returns the number of dividend payments per year. Voila! You’ll see that a few other cells needed this Dividends Per Year figure and now all should be populating. Not only that, but if you scroll over to Table 2 (filtered dividend data), you’ll see that our historic yields are now being calculated thanks to cell J3.

We are actually getting really close now.

We have a few things left to do.

Drag from B4 to J29 and color these cells dark gray.



=concatenate(text((index(V:V, match(MAX(U:U),U:U,1),0)/index(V:V, match(min(U:U),U:U,1),0))^(1/((max(U:U)-min(U:U))/365.25))-1,”+0.00%;-0.00%”),” annual growth over last “,round((max(U:U)-min(U:U))/365.25,1), ” year(s)”)

Change the color of the cells we just added in Row 8 so they are easier to see.

Align-center cell F8.

C8 and I8 act as titles for a chart we will add later. These fetch the date range for Price History from Table 3.

Starting to take shape! Two more cells to add at the bottom.

=concatenate(“Generated: “,text(today(),”mm/dd/yyyy”))

Hard-enter, “*Accuracy and completeness of any stock information is not guaranteed.”

Change these cell text colors to the same as those in Row 8.

Align-right cell B29.

B29 now shows what date the dashboard was created, and J29 reads off a disclaimer. This disclaimer should be taken seriously, as my formulas could be off, etc.

Now we have a blank canvas. We’ll create two charts and be done.

Menu >> Insert >> Chart >> Setup:

*Chart type = Area Chart

*Stacking = None

*Data Range = U6:V13000

*Remove X-Axis

*Check box for “Include hidden/filtered data”

Chart >> Customize >> Chart Style:

*Background Color: None; Check the Maximize box

Chart >> Chart & axis titles:

*Delete Title text for chart title, horizontal axis title, and vertical axis title

Chart >> Gridlines:

*Major gridline count: None

On to the last step:

Menu >> Insert >> Chart >> Setup:

*Chart type: Combo chart

*Stacking: none

*Data Range: Q6:Q201,P6:P201,R6:R201,S6:S201

*Edit x-axis range: Q6:Q201

*Series: Remove Payment Date

*Check the “Use row 6 as headers” box

*Check box for “Include hidden/filtered data”

Chart >> Customize >> Chart style:

*Change background color to match your background cells

Chart >> Customize >> Horizontal axis:

*Check the box for “Reverse axis order”

*Select 60° for “slant labels”

Chart >> Customize >> Series:

*Drop down and select Cash Amount. Select the “data labels” box. Position: Inside end. Font size: 10. Text color: yellow.

*Drop down and select % Increase. Color: white. Axis: right axis. Line thickness: 0px. Point size: 7px. Select the “data labels” box. Position: below. Data label font size: 14.

*Drop down and select Historic Yield. Axis: right axis.

Chart >> Customize >> Chart and axis titles:

*Drop down select “chart title,”: Recent Dividend Payments and Dates (With % Increase)

*Drop down select “vertical axis title,”: Dividend Payment

*Drop down select “right vertical axis title,”: Historic Yield & Dividend Increase %

Resize as necessary!

If you’ve gone through this whole guide, I hope you had as much fun as I did.

Google Sheets lets us make some pretty cool tools. Not only do I have all my information in one place now, but I also can see historic yield trends, dividend growth trends, and share price trends.

I recognize a few shortcomings. The Nasdaq data often only goes back 5 years (which is usually enough for me). It’s not *fully* automated – we’ll still have to update the dividend streaks tab, and input any ETFs or other symbols that don’t come on the sheets from

Another shortcoming is that it just doesn’t look that great on my iPhone. Some of the chart formats don’t transfer, so I’ll mostly use it on my computer.

Please let me know if you like/dislike this tool, and also if you find a way to break it. While creating this blog post, I found a few formula errors and corrected them. I’m looking for a way to add Free Cash Flow, so that may be a future addition. Until then, enjoy! If you’d like to use this dashboard for your own blog, I’d appreciate a link back to this post.

Here’s the link to the Dividend Dashboard Google Sheet if you want to just save a copy for yourself and skip all the work.

5 thoughts on “How to Build a Dividend Growth Investing Dashboard in Google Sheets

  1. Dear Dozer:

    I am a novice at scraping formulas in GoogleSheets, but have migrated there after Excel has totally failed me. (Stock property in Excel–no dividend data? really?)

    My goal is (for purposes of this question) to have a column of tickers, with the next 2 columns giving the yield and dollar amount of the dividend for each ticker

    I was muddling my way through your spreadsheet (which is a masterwork, BTW), in an effort to just get a formula that returns current yield and current dollar amount of last dividend. I notice that you link to Cell S7 in the stats line of your graph, but I think I would have go through your entire div history exercise, and then only come up with yield on the single ticker in your graph.

    So I tried this:

    =index((transpose(query(ImportXML(“”&$A1&””,”//div[@class=’column span-1-of-2′]/div[@class=’table-table fontS14px’]/div[@class=’table-row’]/div[@class=’table-cell’]”),”SELECT * LIMIT 50 OFFSET 0″,0))),1,(MATCH(“dividend Yield”,(transpose(query(ImportXML(“”&$A1&””,”//div[@class=’column span-1-of-2′]/div[@class=’table-table fontS14px’]/div[@class=’table-row’]/div[@class=’table-cell’]”),”SELECT * LIMIT 50 OFFSET 0″,0))),0))+1)

    A1 has the stock ticker, and for this illustration, i put in fixed text in the match function (“Dividend yield”), but in my sheet it refers to the column header. ( Using multiple column headers and dragging to the right, [using a fixed row ref as in $RC], i can return any function in the Nasdaq table by entering it in the column header.

    I think what I have done is return an array of all the data for the ticker, searched within the (horizontal) array for title of the data field in question, and return the cell one to the right to get the actual data. I had to do this because the number of data points in each ticker is not the same; if i used a fixed column # to return data, sometimes i get the yield and sometimes another data point.

    Would love your feedback on this. I borrowed a lot of the design from Database Guy in a reddit post at



    1. Robert, thanks for taking the time to work through this! I’m not forgetting about you – I’m just a little busy with school work these days. I’m should be able to come up with an answer, but can’t promise when.

  2. I absolutely love this who Google sheets product. Do you know if Google has changed their algorithm again. I inserted your formula into into L6 and there is a #ERROR! message. Continuing on through the tutorial, all of the steps have the same #ERROR! message. I would love to learn to master this and any help would be much appreciated.

    1. I had the same #ERROR! message. I solved it replacing all the “,” outside the ” ” with “;” so this is what actually works for me:
      =query(importhtml(concatenate(“”;upper(B3);”/dividend”);”table”;0);”select Col6, Col3 LIMIT 500″)

Comments are closed.