Introduction
This post we deploy our own R-shiny app.
- You can select a stock
- The app fetches the data, does some number crunching and plots the results
The source code is on github.
Bear in mind that I might not run the demo-server indefinitely, or that it might not be responsive, so first here’s a screenshot:
Here is the demo:
If you want to see this in a full page view here’s the link: https://arthought.shinyapps.io/15_stock_analysis/
Remark: This application might not be available as there is a fairly tight limit on the number of hours the free-plan shinyapps.io based apps will run. There is another server accessible using this link.
There are various options as to how you can use Shiny server:
- You can host your own server, instructions are here and then here
- You can sign up at https://www.shinyapps.io/
There are pros and cons to either, here’s a summary:
Own server | https://www.shinyapps.io/ | |
---|---|---|
Complexity | ✕ | ✓ |
Cost | ✓ | ✓ |
Scalability | ✓ | ✓ |
For this post we choose the free account option of https://www.shinyapps.io , which is limited to 5 Applications and 25 Active Hours (per month).
We then use an iframe to reference the shiny app in this post (as shown in the previous post).
Discussion of the code
In the typical shiny app there are two files, one representing the “frontend”/user interface called ui.R, and one representing the “backend” called server.R. The ui.R defines the user interface widgets, the server.R defines e.g. calculations or plots.
So let’s briefly discuss these two components in our app.
The “frontend”/user interface called ui.R
In the ui.R file we have the following:
- We download the current DAX components from a Wikipedia page, using the htmltab library : https://en.wikipedia.org/wiki/DAX
## Initialization library(htmltab) {## get dax components names tabledata.url <- "https://en.wikipedia.org/wiki/DAX" DAX.components = htmltab(doc = tabledata.url, which=3, rm_nodata_cols = F) DAX.components.Symbol = paste0( DAX.components[['Ticker symbol']], '.DE') names(DAX.components.Symbol) = paste0( DAX.components[['Company']], ' (', DAX.components[['Prime Standard industry group']] ,')' ) } # {## get dax components names # end: ## Initialization
- This list is fed into a selectInput box named DAX stock
selectInput("stock_id", "DAX Stock:", DAX.components.Symbol),
There is also a sliderInput
# Specification of range within an interval helpText("Specify Range (in % of data)"), sliderInput("range", "Range:", min = 0, max = 100, value = c(0,100))
- Once you, the user, select a stock this information is passed to server.R which defines that the data is downloaded from yahoo, analysed and shown in the plot; the server.R file is discussed below. However the way these plots are presented is defined in the ui.R. The definitions in the ui.R use a hierarchy of panels, in this case the hierarchy is mainPanel,tabsetPanel and tabPanel :
# Show a hierarchy of panels with plots mainPanel( tabsetPanel( tabPanel( "Timeseries", plotOutput("timeseries") ), tabPanel("Boxplot", plotOutput("boxplot") ), tabPanel("Histograms", plotOutput("histogram.stockdata"), plotOutput("histogram.GDAXI") ) ) )
The “backend” called server.R
In the server.R we have the following
- We download the data for the DAX index, using the getSymbols function from the quantmod package.
## Intialize DAX data AdjustedPrice = 6 .GDAXI = getSymbols('^GDAXI', warnings = FALSE, auto.assign = FALSE) GDAXI = na.fill(.GDAXI, fill = "extend")[,AdjustedPrice, drop=FALSE]
- The input “stock_id” from the selectInput box in the ui.R (=input$stock_id) is fed into what’s called a reactive expression.
The reactive expression is “informed” if and when any input has changed. When this happens the expression is recalculated, i.e. it reacts. In our case the reactive expression is the following function:# Return the requested dataset datasetInput <- reactive({ .stockdata = getSymbols(input$stock_id, warnings = FALSE, auto.assign = FALSE) stockdata = na.fill(.stockdata, fill = "extend")[, AdjustedPrice, drop=FALSE] stockdata = merge (GDAXI, stockdata) no_of_datapoints = dim(stockdata)[1] min = input$range[1]/100 * no_of_datapoints max = input$range[2]/100 * no_of_datapoints # AdjustedPrice out = stockdata[min:max,] return (out) })
When either input$stock_id or input$range are updated, the result of the datasetInput is updated wherever it is used, for instance in the plotting functions.
What this “datasetInput” function actually returns is a merge of the DAX and the stock’s data, restricted to the range defined by the slider, where the range is specified in terms of two percentage values of the total number of rows of the data table. - The server.R also has the definitions of the output plotting functions. These in turn make use of plotting functions from the package PerformanceAnalytics. The functions from PerformanceAnalytics that are used here are charts.PerformanceSummary, chart.Histogram and chart.Boxplot. As input they expect return type data. For charts.PerformanceSummary we use daily returns, for chart.Histogram and chart.Boxplot we use monthly returns.
# generate a plot output$timeseries <- renderPlot({ AdjustedPrice <- datasetInput() Returns <- AdjustedPrice/lag(AdjustedPrice, 1) -1 # daily returns charts.PerformanceSummary(Returns) }) # generate a plot output$histogram.GDAXI <- renderPlot({ AdjustedPrice <- datasetInput()[,1,drop=FALSE] Returns <- AdjustedPrice/lag(AdjustedPrice, 25) -1 # ~ monthly returns (because 1 month ~ 25 business days) chart.Histogram(Returns, methods = c( "add.density", "add.normal"), xlim = c(-0.4, 0.4)) }) # generate a plot output$histogram.stockdata <- renderPlot({ AdjustedPrice <- datasetInput()[,2,drop=FALSE] Returns <- AdjustedPrice/lag(AdjustedPrice, 25) -1 # ~ monthly returns (because 1 month ~ 25 business days) chart.Histogram(Returns, methods = c( "add.density", "add.normal"), xlim = c(-0.4, 0.4)) }) # generate a plot output$boxplot <- renderPlot({ AdjustedPrice <- datasetInput() Returns <- AdjustedPrice/lag(AdjustedPrice, 25) -1 # ~ monthly returns (because 1 month ~ 25 business days) chart.Boxplot(Returns) })
Please note that these plotting functions use the reactive function datasetInput(). Therefore the plots are automatically updated whenever the inputs input$stock_id or input$range, used in datasetInput(), have changed.
Success!