Skip to content

Commit 7a11a20

Browse files
cgraham-rsdotNomad
andauthored
Add quarto-stock-report-r (#112)
* Add quarto-stock-report-r * Use example category --------- Co-authored-by: Jordan Jensen <[email protected]>
1 parent 25e84f2 commit 7a11a20

File tree

8 files changed

+7683
-0
lines changed

8 files changed

+7683
-0
lines changed

.github/workflows/extensions.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ jobs:
4040
stock-dashboard-python: extensions/stock-dashboard-python/**
4141
top-5-income-share-bokeh: extensions/top-5-income-share-bokeh/**
4242
landing-page: extensions/landing-page/**
43+
quarto-stock-report-r: extensions/quarto-stock-report-r/**
4344
stock-api-fastapi: extensions/stock-api-fastapi/**
4445
quarto-presentation: extensions/quarto-presentation/**
4546
connectwidgets-example: extensions/connectwidgets-example/**
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/.quarto/
2+
data.csv
3+
index.html
4+
index_files
5+
.output_metadata.json
6+
/email-preview/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Quarto Stock Report using R
2+
3+
## About this example
4+
5+
This stock report is generated using Quarto and R. It is an example of how you might automate regular updates using a data source. The report also generates a custom email, sharing the results directly to stakeholders.
6+
7+
8+
## Learn more
9+
10+
* [Quarto](https://quarto.org)
11+
12+
## Requirements
13+
14+
* Quarto version 1.4 or higher
15+
* R version 4.4 or higher
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
project:
2+
title: Stock Report

extensions/quarto-stock-report-r/gspc.csv

Lines changed: 4616 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
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

Comments
 (0)