|
| 1 | +--- |
| 2 | +title: Stock Report |
| 3 | +date: today |
| 4 | +date-format: long |
| 5 | +published-title: produced |
| 6 | +format: email |
| 7 | +attachments: |
| 8 | + - data.csv |
| 9 | +email-preview: true |
| 10 | +--- |
| 11 | + |
| 12 | +## Report for `^GSPC` |
| 13 | + |
| 14 | +```{r, echo=TRUE, message=FALSE} |
| 15 | +library(DT) |
| 16 | +library(formattable) |
| 17 | +library(ggthemes) |
| 18 | +library(quantmod) |
| 19 | +library(dplyr) |
| 20 | +library(plotly) |
| 21 | +library(xts) |
| 22 | +library(dotty) |
| 23 | +``` |
| 24 | + |
| 25 | + |
| 26 | +```{r} |
| 27 | +#| eval: false |
| 28 | +#| echo: false |
| 29 | +
|
| 30 | +# set eval: TRUE fetch updated data |
| 31 | +
|
| 32 | +prices <- round(getSymbols("^GSPC", auto.assign = FALSE, src = "yahoo"), 2) |
| 33 | +close <- Cl(xts::last(prices)) |
| 34 | +open <- Op(xts::last(prices)) |
| 35 | +
|
| 36 | +prices_df <- prices %>% |
| 37 | + as_tibble() %>% |
| 38 | + mutate( |
| 39 | + Date = index(prices), |
| 40 | + Direction = ifelse(GSPC.Close >= GSPC.Open, 'Increasing', 'Decreasing') |
| 41 | + ) |
| 42 | +
|
| 43 | +readr::write_csv(prices_df, "gspc.csv") |
| 44 | +``` |
| 45 | + |
| 46 | + |
| 47 | +```{r} |
| 48 | +prices_df <- readr::read_csv("gspc.csv") |
| 49 | +recent <- slice_max(prices_df, order_by = Date, n = 90) |
| 50 | +yest <- prices_df |> slice_max(order_by = Date, n = 1) |> select(close = GSPC.Close, open = GSPC.Open) |
| 51 | +.[close, open] <- yest |
| 52 | +``` |
| 53 | + |
| 54 | +The stock closed `r ifelse(close>open,'up','down')` at `r close` dollars per share yesterday. |
| 55 | + |
| 56 | +### Price History |
| 57 | + |
| 58 | +The chart below is made with the `quantmod` and `plotly` R packages. An API returns all of the price history based on the stock tick symbol "GSPC.". |
| 59 | + |
| 60 | + |
| 61 | +```{r build_plot, echo=TRUE, warning=FALSE, message=FALSE} |
| 62 | +fig1 <- plot_ly(prices_df) %>% |
| 63 | + add_trace( |
| 64 | + type = "candlestick", x = ~Date, name = "GSPC", |
| 65 | + open = ~GSPC.Open, close = ~GSPC.Close, |
| 66 | + high = ~GSPC.High, low = ~GSPC.Low, |
| 67 | + increasing = list(line = list(color = '#17BECF')), |
| 68 | + decreasing = list(line = list(color = '#7F7F7F')) |
| 69 | + ) %>% |
| 70 | + layout(yaxis = list(title = "Price")) |
| 71 | +
|
| 72 | +fig2 <- plot_ly(prices_df) %>% |
| 73 | + add_bars( |
| 74 | + x = ~Date, y = ~GSPC.Volume, name = "GSPC Volume", |
| 75 | + color = ~Direction, colors = c('#17BECF', '#7F7F7F') |
| 76 | + ) %>% |
| 77 | + layout(yaxis = list(title = "Volume"), xaxis = list(title = "")) |
| 78 | +
|
| 79 | +subplot( |
| 80 | + fig1, fig2, heights = c(0.7, 0.2), nrows = 2, |
| 81 | + shareX = TRUE, titleY = TRUE |
| 82 | +) %>% |
| 83 | + layout( |
| 84 | + hovermode = "x", margin = list(t = 80), |
| 85 | + title = paste("Tesla from", min(prices_df$Date), "to", max(prices_df$Date)), |
| 86 | + xaxis = list( |
| 87 | + rangeselector = list( |
| 88 | + x = 0, y = 1, xanchor = 'left', yanchor = "top", |
| 89 | + visible = TRUE, font = list(size = 9), |
| 90 | + buttons = list( |
| 91 | + list(count = 1, label = 'RESET', step = 'all'), |
| 92 | + list(count = 1, label = '1 YR', step = 'year', stepmode = 'backward'), |
| 93 | + list(count = 3, label = '3 MO', step = 'month', stepmode = 'backward'), |
| 94 | + list(count = 1, label = '1 MO', step = 'month', stepmode = 'backward') |
| 95 | + ) |
| 96 | + ) |
| 97 | + ), |
| 98 | + legend = list( |
| 99 | + x = 1, y = 1, xanchor = 'right', |
| 100 | + orientation = 'h', font = list(size = 10) |
| 101 | + ) |
| 102 | + ) |
| 103 | +``` |
| 104 | + |
| 105 | +### Raw Data |
| 106 | + |
| 107 | +The table below displays the daily price data for the stock. A concise, interactive table is created with the `DT` package. |
| 108 | + |
| 109 | + |
| 110 | +```{r show_data, echo=TRUE} |
| 111 | +recent %>% |
| 112 | + mutate(GSPC.Volume = GSPC.Volume / 1000000) %>% |
| 113 | + datatable() %>% |
| 114 | + formatCurrency(c("GSPC.Open", "GSPC.High", "GSPC.Low", "GSPC.Close"), digits = 2) %>% |
| 115 | + formatRound("GSPC.Volume", digits = 0) |
| 116 | +``` |
| 117 | + |
| 118 | +## Legacy Information |
| 119 | + |
| 120 | +This report also produces a CSV file containing recent price data, which may |
| 121 | +be used in other analysis. |
| 122 | + |
| 123 | +```{r write_csv, echo = FALSE, include = FALSE} |
| 124 | +write.csv(recent, file = "data.csv") |
| 125 | +``` |
| 126 | + |
| 127 | +[Link to CSV](data.csv) |
| 128 | + |
| 129 | +## Email |
| 130 | + |
| 131 | +This report also produces an email that is sent to key stakeholders with summary |
| 132 | +information. |
| 133 | + |
| 134 | +::: {.email} |
| 135 | + |
| 136 | +```{r compute_subject, warning = FALSE, message = FALSE, echo = FALSE} |
| 137 | +# Calculate the total change |
| 138 | +
|
| 139 | +diff <- prices_df |> |
| 140 | + slice_max(order_by = Date, n = 2) |> |
| 141 | + mutate(chg = GSPC.Close - lag(GSPC.Close, order_by = Date)) |> |
| 142 | + pull(chg) |
| 143 | +subject <- sprintf("GSPC is %s today by $%g!", |
| 144 | + ifelse(diff[1] > 0,"up", "down"), |
| 145 | + abs(diff[1])) |
| 146 | +
|
| 147 | +# ideally, we would always compute the email content but |
| 148 | +# suppress its scheduled sending only when (abs(diff) > 0.5) |
| 149 | +``` |
| 150 | + |
| 151 | +::: {.subject} |
| 152 | +`r#subject` |
| 153 | +::: |
| 154 | + |
| 155 | +# Stock Update |
| 156 | + |
| 157 | +Hello Team, |
| 158 | + |
| 159 | +Here are the latest stock prices for **GSPC** as of `r Sys.Date()`: |
| 160 | + |
| 161 | +```{r, echo = FALSE} |
| 162 | +price_new <- prices_df |> |
| 163 | + slice_tail(n = 10) |> |
| 164 | + select(Date, open = GSPC.Open, close = GSPC.Close) |> |
| 165 | + mutate(change = close - open) |
| 166 | +
|
| 167 | +# include a table with the stock prices |
| 168 | +format_table( |
| 169 | + x = as.data.frame(tail(price_new)), |
| 170 | + list( |
| 171 | + change = formatter( |
| 172 | + "span", |
| 173 | + style = x ~ ifelse( |
| 174 | + x > 0, |
| 175 | + formattable::style(color = "green"), |
| 176 | + formattable::style(color = "red"))) |
| 177 | + ) |
| 178 | + ) |
| 179 | +``` |
| 180 | + |
| 181 | +The historical trend is shown below: |
| 182 | + |
| 183 | +```{r, echo = FALSE, message = FALSE} |
| 184 | +# include a plot with the price data |
| 185 | +ggplot(recent) + |
| 186 | + aes(Date, GSPC.Adjusted) + |
| 187 | + geom_smooth() + |
| 188 | + theme_fivethirtyeight() + |
| 189 | + labs( |
| 190 | + title = sprintf("%s Price Adjusted", "GSPC") |
| 191 | + ) |
| 192 | +``` |
| 193 | + |
| 194 | +Let me know if you have any questions. |
| 195 | + |
| 196 | +Best, |
| 197 | + |
| 198 | +Team Lead |
| 199 | + |
| 200 | +::: |
0 commit comments