Time-Series-dygraph

knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
suppressPackageStartupMessages(require(dplyr))
suppressPackageStartupMessages(require(tidyquant))
suppressPackageStartupMessages(require(FinanceGraphs))
knitr::opts_chunk$set(fig.width = 7)
options(datatable.print.keys=FALSE, datatable.print.class=FALSE)

Interactive Time Series Visualization with Dygraphs

Visualizing financial time series is critical to communicating ideas and deciding how to trade or invest. While ggplot2 is the gold standard for static graphs, Dygraphs is an excellent package to mix interactivity with flexibility and visual aesthetics, and is well suited to Financial time series in particular.

That flexibility is great, but can take some time (and code) to set up and customize well. This package is designed to reduce the time spent going from data to finished and enhanced visualization. Rather than emphasize pipes and complicated coding, this package offers just a few flexible functions and a simplified parameter-based approach to graph customization. Aesthetic details have sensible defaults, but are almost always customizable. The package is designed to be as agnostic as possible about input data, and also seeks to manage other common data which may add useful information to the graph.

Data vs Time plots with fgts_dygraph

fgts_dygraph() is a flexible wrapper around dygraphs building blocks. Internally, it makes extensive use of data.table functionality, both for flexibility and speed. Key concepts useful to use this function are

Simple Examples

Most financial time series are just prices of various assets. Most of the examples will use a simple prepared data set of prices. Simply plotting these prices can be done with just a few parameters.

head(eqtypx,1)
fgts_dygraph(eqtypx, title="Stock Prices", ylab="Adjusted Close")

Note two important features of this graph. First, the series is smoothed by taking moving averages specified by the number in the lower left hand column. These values are determined by the length of the input series, can be changed using the roller parameter. Second, there is a nice date range selector below the graph. This can be interactively used to focus on parts of the graph, and double clicking within the graph always resets the view to the broadest possible one.

Let's enhance the graph with a few more parameters that can be added to the fgts_dygraph function call:

fgts_dygraph(eqtypx, title="Stock Prices, Date focused", roller=3,dtstartfrac=0.6,splitcols=TRUE)

| Parameter call | Feature | |:------|:-----------------------------| |roller=3|Lessen the smoothing by only averaging every 3 prices | |dtstartfrac=0.6|Start the graph 60 percent of the way from input series beginning to the end | |splitcols=TRUE|Split the first series found (leftmost if wide, first if narrow) onto a second axis. A list of series names can also be given|"

To highlight or hide series and focus in on a particular date range, use:

fgts_dygraph(eqtypx, title="Stock Prices, highlighted", roller=1,
             dtwindow="-3y::",hidecols="TLT",hilightcols="IBM",hilightwidth=4, hilightstyle="dashed")

|Parameter call|Feature| |:------|:--------------------| |dtwindow="-3y::"|focus the date range selector to 1 year prior to Sys.Date()| |hidecols="TLT"|Hide one or more series. Can either separate by ; or give a list.| |hilightcols="IBM|Highlight a series by thickening its line or changing the line style| |hilightwidth=4|New width for columns named inhilightcols| |hilightstyle="dashed"|New line style for columns named inhilightcols|

Horizontal annotations and rebasing

Many times, you may want to show relative changes of series with widely varying values. Dygraphs has a nice function dyRebase for doing so, but there are times you would like to rebase to given date. For example to make all the series start at 120 as of the beginning of 2025, use the rebase parameter.

There are also ways to add horizontal lines to highlight current (or last in the dataset) values or labels. These can be useful when you prefer more static graphs than interactive ones, especially when legends are turned off.

|Parameter call|Feature| |:------|:--------------------| |rebase="2025-01-01,120"|Divide all series by their values a of 1/1/2025 and scale to 120.| |annotations="last,linelabel"|Put vertical lines highlighting last values in each series | |annotations="range,100,120"|Put a highlighted range betwewen 100 and 120 percent of value on 1/1/2025| |events="pt,2025-02-01,QQQ,Feb1"|Add a note a particular date and series| |dylegend="never" |Don't replicate information in the legend| |bg_opts="grid,none;norange"|Hide the grid and the date range selector|

fgts_dygraph(eqtypx, title="Stock Prices, rebased",hidecols="TLT;EEM", dtstartfrac=0.7,
             rebase="2025-01-01,120",events="pt,2025-02-01,QQQ,Feb3",
             annotations="last,linelabel;range,100,120",
             dylegend = "never", bg_opts="grid,none")

Grouping series together

Many time series are closely related, as in confidence intervals around a forecast or ranges for a given time periods. Dygraphs allows you to plot series with areas shaded between lower and upper bounds. The ability to do so can be used to enhance graphs in many ways. Some I've used in addition to adding confidence bounds include

fgts_dygraph() plots together series which have suffixes in (.lo,.hi) with their undecorated counterparts, keeping their assigned colors. An example showing Colombia's currency value relative to peers is:

toplot <- reerdta[REGION=="LATAM",.(cop=sum(value*(variable=="COL")),
               peers=mean(value),peers.lo=min(value),peers.hi=max(value)),by=.(date)]
head(toplot,2)

Those series (peers, peers.lo, and peers.hi) are combined to get a graph which shows how one series has moved with a geographically similar set of peers.

fgts_dygraph(toplot,title="COP REER vs Latam peers",ylab="Price",
             roller=1,hilightcols="cop",hilightwidth=4,annotations="last,linevalue")

Another example is showing periods where a series is significantly correleted to another (e.g. the broader market). The series eqtyrtn in the package has the rolling 66 day p.value of regressing returns of TLT against QQQ:

toplot <- eqtypx |> left_join(select(eqtyrtn,date,p_TLT_QQQ), by="date") |>
                    filter(!is.na(p_TLT_QQQ)) |>
                    transmute(date,TLT, TLT.lo=TLT * case_when( p_TLT_QQQ<0.04 ~ 0.9,.default=1) )
fgts_dygraph(toplot,title="TLT with significant relationship to QQQ",ylab="Price",xlab="shaded areas significant",roller=1)

Events and Event handlers

fgts_dygraph() has many ways of integrating date-based information such as events or regimes with the original data. Both "pre-canned" events and events which draw in other information (via event_ds) can be displayed. Events are show in two ways:

One liners using events parameter

Events can be determined from several sources, and can be combined together.

smalldta <- eqtypx |> filter(date>=as.Date("2023-01-01")) |> select(date,EEM,TLT)
fgts_dygraph(smalldta,title="With Precanned Events",ylab="Price",rebase=",100",roller=1,
                    events="doi,regm;doi,fedmoves")

Internally, a persistent data set is kept that includes dates (beginning and/or end), text labels (as eventid) and formatting information. In some cases the formatting is inferred from the label (e.g. ending in "+" makes it green), but each point in customizable.

fg_get_dates_of_interest("fedmoves|regm") |> group_by(category) |> slice_tail(n=2)

New data can be added easily. We can add new events (e.g a FOMC move in 2026) by using the fg_update_dates_of_interest() function.

newdoi <-data.frame(category="fedmoves",eventid="F:-50",DT_ENTRY=as.Date("6/16/2026",format="%m/%d/%Y"))
fg_update_dates_of_interest(newdoi)
fg_get_dates_of_interest("fedmoves") |> dplyr::slice_tail(n=2) |> as.data.frame()

For example, to add monthly option expiration dates and a note for Christmas, add

fgts_dygraph(smalldta,title="With Seasonals",rebase=",100",roller=1,dylegend="never",dtstartfrac=0.7,
             events="seasonal,optexp,mo|qtr;dt,XMAS,2025-12-25")
fgts_dygraph(smalldta,title="With Turning Points",rebase=",100",roller=1,events="tp,7")

Events, Annotations, and forecasts can all be added with separate data frames.

Events can also be added with data.frames that include all the details of the annotation, such as the text, dates, and colors. Those can be created by the user, but the package includes several "helpers" to take (more) raw data and add relevant formatting details.

Event helpers

Event helpers are small functions which convert any time based data into the correct annotations. The currently implemented helpers are

|Function|Description| |:---|:------------| |fg_addbreakouts()|Statistically identify breakout points (also available via events) | |fg_findTurningPoints()|Statistically identify turning points (also available via events) | |fg_cut_to_events()|"Cut" a univariate series into colored bands, with two different colors for positive and negative values | |fg_signal_to_events()|Map a long/short signal to events | |fg_tq_divs()|Add dividend events from tidyquant dividend data | |fg_av_earnings()|Add earnings events from alphavantagepf earnings data | |fg_ratingsEvents()|Add colored ranges based on analyst credit ratings |

The first two are described in the function documentation. The next two are designed to map exogenous univariate series to colored regions.

toplot <- rbind(eqtypx_melt |> filter(variable=="QQQ"), 
                consumer_sent |> transmute(date,variable=symbol,value=price))
fgts_dygraph(toplot,title="With consumer sentiment",splitcols="UMCSENT",stepcols="UMCSENT",roller=5,
             event_ds = fg_cut_to_events(consumer_sent,center="zscore"))
suppressPackageStartupMessages(require(data.table))
ma_signal<-eqtypx[,.(date,sig=cut(frollmean(EEM,5)-frollmean(EEM,20),
                     c(-10,-0.5,0.5,10),labels=c("long","flat","short")),EEM)]
tail(ma_signal,3)
colormap <- fg_get_aes("tradesignal")[,.(sig=variable,value)]
colormap
fgts_dygraph(eqtypx[,.(date,EEM)],dtstartfrac=0.6,roller=1,title="5/20 MA positions",
             event_ds=fg_signal_to_events(ma_signal,colormap))
suppressPackageStartupMessages(require(tidyquant))
all_events <- rbindlist(list(fg_tq_divs(c("IBM")),earnings_ibm |> fg_av_earnings() ))
fgts_dygraph(eqtypx[,.(date,QQQ,IBM)],title="With earnings and divs",dtstartfrac=0.8,event_ds=all_events)

Note that mutiple event sets can be used with rbind, and the color of the event annotations matches that of the original series.

head(ratings_db,3)
fgts_dygraph(nomfxdta |> filter(variable=="COP"),title="COP with Ratings",
         event_ds=fg_ratingsEvents("COLOM",ratings_db,agency="S.P"))

Forecasts

Time series plotted in fgts_dygraph can also be extended beyond the current day. Forecasts come in many output forms, but fortunately, there are broom like objects now which can standardize the outputs of many forecasting models. forecast in particular can produce standardized forecasts from multiple models, including ets(), auto.arima(), tbats and a host of others.

The forecasts from forecast require quite a bit of post-processing to recover dates, but fortunately the function sw_sweep() from Sweep produced an actual data.frame. This can be forwaded to another (included) helper fg_sweep(). An example to predict QQQ with ets() is shown below. First, we show the format we expect the forecasts to be in.

suppressPackageStartupMessages(require(timetk))
suppressPackageStartupMessages(require(forecast))
suppressPackageStartupMessages(require(sweep))
fcst_eqtypx <- tk_ts(eqtypx[,.(date,QQQ)]) |> ets() |> forecast::forecast(h=60) |> sweep::sw_sweep(timetk_idx=TRUE)
head( fcst_in <- fg_sweep(fcst_eqtypx) ,3)
fgts_dygraph(eqtypx[,.(date,IBM,QQQ)],title="Rebased With Forecasts",roller=1,dtstartfrac=0.6,rebase="2024-01-01,100",forecast_ds=fcst_in)

Note that

Customization: Colors

The visual aesthetics of fgts_dygraphs are designed to have sensible defaults, but still somewhat customizable, and more importantly mostly out of the way of the function call. Colors in particular are stored internally in persistent data.frames To see the default line colors and shading, use fg_get_aes(<category>) as seen below, or run fg_get_aes() to get a chart with the actual colors used. Some colors have variables associated with them, which are arbitrary ordering values or (in some cases) values mapped to greps on the text labels. [\^1]

mycolors <-fg_get_aes("lines",n_max=4)
mycolors

[\^1] In particular, the included events in the set regm have text labels that look like (e.g) "TariffT-". The "-" at the end of the text is mapped to the marketregines,- color.

There are two ways to change colors. To persistently change them use fg_update_aes() which requires a data.frame in the same format as that obtained with fg_get_aes(). To use graduated colors instead of the defaults chosen by the package:

suppressPackageStartupMessages(require(RColorBrewer))
newcolors <- mycolors |> mutate(value = rev(RColorBrewer::brewer.pal(8,"GnBu"))[1:4])
fg_update_aes(newcolors, persist=TRUE )
fg_get_aes("lines",n_max=4)

which gives, with annotations

fgts_dygraph(eqtypx, title="Stock Prices, new colors", annotations="last,label",rebase=",100",bg_opts="grid,none")

To just change series colors temporarily in a session (and not save them for future use, you can use fg_update_line_colors() as in the next example

fg_update_line_colors(c("gray30","gray30","red","gray30"))
fgts_dygraph(eqtypx, title="Stock Prices, line colors", annotations="last,label",rebase=",100",bg_opts="grid,none")

To reset the colors back to their defaults, run

fg_reset_to_default_state("color")
fg_get_aesstring("lines",n_max=2)

Customization: Adding Dates of Interest

Events added with the events="doi,<category>" parameter can managed persistently across invocations of the package by using fg_update_dates_of_interest(). For example, suppose the FOMC cuts rates 50bps in the future. The event can be added with

newdoi <-data.frame(category="fedmoves",eventid="F:-50",DT_ENTRY=as.Date("6/16/2026",format="%m/%d/%Y"))
fg_update_dates_of_interest(newdoi)
tail(fg_get_dates_of_interest("fedmoves"),2)  |> as.data.frame()

Entire new categories can be added. Here are a few examples:

Recession Indicators

Adding recession indicators from FRED. FRED has a monthly probability of recession indicator. Mapping that indicator to a yes/no series using a 7pct threhold, we can add recession events using the event helper fg_signal_to_events():

recindic <- recession_indic |> transmute(date,isrec=(price>7))
colormap <- data.frame(isrec=c(FALSE,TRUE),color=c("white","pink"),eventid=c("","Recession"))
newevents <- fg_signal_to_events( recindic, colormap |> mutate(category="FREDPREC"))
fg_update_dates_of_interest(newevents)
fgts_dygraph(eqtypx, title="Stock Prices, w/ Recession Events", events="doi,FREDPREC",rebase=",100",bg_opts="grid,none")

Central Bank events

We can add other central bank moves, e.g. Brazil's COPOM decisions BCB, by downloaded the data and using

filepath <- system.file("extdata", "selic_historical_rates.csv", package = "FinanceGraphs")
selic<-data.table::fread(filepath,skip=1,select=c(2,5),col.names=c("date","tgt"))
selic<-selic[,let(DT_ENTRY=as.Date(date,format="%m/%d/%Y"))][order(DT_ENTRY)][,move:=c(0,diff(tgt,1))]
newevents <- selic[,.(category="COPOM",eventid=format(tgt,digits=2), DT_ENTRY,color=c("blue","grey","red")[sign(move)+2])]
fg_update_dates_of_interest(newevents[!color=="grey"]) # Only add real moves
tail(newevents,2)

and use it easily with just a text argument to events.

fgts_dygraph(filter(nomfxdta,variable=="BRL"),title="BRL w/ COPOM",dtstartfrac=0.6,events="doi,COPOM")

Cleaning up any persistent event additions can be accomplished with

fg_reset_to_default_state("all")


Try the FinanceGraphs package in your browser

Any scripts or data that you put into this service are public.

FinanceGraphs documentation built on June 22, 2026, 5:08 p.m.