Skip to contents

This vignette demonstrates a simple workflow using downloaded fund files. For a comprehensive introduction using example data files, see vignette("importing-and-computing-differences"). For information on how to get data from fund and index providers, see fundsr’s GitHub wiki. More complex workflows are shipped without data under scripts/examples in the package directory (start with glob_funds.R or all_funds.R). The installation path can be discovered via system.file("scripts/examples", package = "fundsr").

Setup

Set up directories.

dirs <- c(
    data = file.path("data", "funds"),
    xlm = file.path("data", "xlm"),
    out = "output"
)
for (d in dirs) {
    dir.create(d, recursive = TRUE)
}

Populate the XLM directory with some monthly XLM reports.

base_url <- "https://www.cashmarket.deutsche-boerse.com/resource/blob/"
file_suffix <- "-ETF-ETP-Statistic.xlsx"
blob_paths <- c(
  "4844258/91ce589f5309cbbd1ad0c92b3e6cdbda/data/20251130",
  "4795286/394c4451af562507f9def3f39da62242/data/20251031",
  "4725636/2d62a1677b537d996aefc45df3ff21d3/data/20250930",
  "4674370/0de847380722e8e87bc821cb5313ba41/data/20250831"
)
xlm_urls <- paste0(base_url, blob_paths, file_suffix)
for (url in xlm_urls) {
    fname <- basename(url)
    dest_path <- file.path(dirs[["xlm"]], fname)
    if (!file.exists(dest_path)) {
        download.file(url, destfile = dest_path, mode = "wb", quiet = TRUE)
        Sys.sleep(stats::runif(1, 0.5, 1.0))
    }
}

Import all XLM files into a tibble.

if (!exists("xlm_data")) {
    xlm_data <- read_xlm_directory(dirs[["xlm"]])
}

Set package options.

fundsr_options(
    data_dir = dirs[["data"]],
    out_dir = dirs[["out"]],
    # internal_png = TRUE, # output PNGs without Inkscape
    # inkscape = "path/to/inkscape" # set only if auto-detection fails
)

# Helper to add urls to option fundsr.fund_urls
add_fund_urls(c(
    IUSQ = "https://www.ishares.com/uk/individual/en/products/251850/ishares-msci-acwi-ucits-etf/1535604580409.ajax?fileType=xls&fileName=iShares-MSCI-ACWI-UCITS-ETF-USD-Acc_fund&dataType=fund",
    SPYY = "https://www.ssga.com/ie/en_gb/institutional/library-content/products/fund-data/etfs/emea/navhist-emea-en-spyy-gy.xlsx"
))

Download and import

Populate funds directory (download URLs listed in option fundsr.fund_urls).

download_fund_data() # downloads as SPYY.xlsx, IUSQ.xls
#> Downloading 'IUSQ'
#> Downloading 'SPYY'

Register data loader calling vendor-specific wrappers around load_fund().

add_data_loader(function() {
    # this loads the downloaded SPYY.xlsx
    spdr("SPYY", benchmark = "ACWI")
    # loads IUSQ.xls, also retrieves ACWI (net) from the file
    ishs("IUSQ", benchmark = "ACWI", retrieve_benchmark = T)
})

Get fund and index data into a master table.

series <- build_all_series() %>%
    filter(date >= as_date("2012-12-29"))
#> Reading Excel: 'data/funds/SPYY.xlsx'
#> Reading Excel: 'data/funds/IUSQ.xls'

Check contents.

series %>% filter(date >= as_date("2015-04-03"))
#> # A tibble: 2,764 × 4
#>    date        iusq  ACWI  spyy
#>    <date>     <dbl> <dbl> <dbl>
#>  1 2015-04-06  38.2  153.   NA 
#>  2 2015-04-07  38.5  154.  101.
#>  3 2015-04-08  38.6  154.  101.
#>  4 2015-04-09  38.7  154.  101.
#>  5 2015-04-10  38.9  155.  102.
#>  6 2015-04-13  38.8  155.  102.
#>  7 2015-04-14  38.9  155.  102.
#>  8 2015-04-15  39.0  156.  102.
#>  9 2015-04-16  39.1  156.  102.
#> 10 2015-04-17  38.7  155.  101.
#> # ℹ 2,754 more rows
get_fund_index_map()
#>   spyy   iusq 
#> "ACWI" "ACWI"

Compute

Calculate CAGR and log-return differences with a 365-day rolling window.

nd <- 365
diffs <- roll_diffs(series, nd, get_fund_index_map())
#> Roll diffs spyy -> ACWI
#> Roll diffs iusq -> ACWI
diffs$cagr %>% slice_tail(n = 3)
#>         date        spyy         iusq
#> 1 2026-01-07 0.004357993 0.0007516283
#> 2 2026-01-08 0.004395124 0.0007814405
#> 3 2026-01-09          NA           NA
diffs$log %>% slice_tail(n = 3)
#>         date        spyy         iusq
#> 1 2026-01-07 0.003521613 0.0006082626
#> 2 2026-01-08 0.003554415 0.0006328890
#> 3 2026-01-09          NA           NA

Plot specifications

no_filter <- NULL
zoom_filter <- function(x) {x %>% filter(date >= as_date("2022-01-01"))}
acwi_funds <- c("spyy", "iusq")
gg_par <- scale_color_manual(values = c("iusq" = "red", "spyy" = "blue"),
                             labels = toupper)

plot_spec <- tribble(
    ~plot_id, ~title, ~data_filter,
    ~gg_params, ~width,  ~height,
    ~funds,

    "ACWI", "SPYY & IUSQ", no_filter,
    gg_par, 14, 9,
    acwi_funds,

    "ACWIz",
    c(en = "SPYY & IUSQ: recent years",
      bg = "SPYY & IUSQ: последни години"),
    zoom_filter,
    gg_par, 14, 9,
    acwi_funds
)

Plot

Run the plots! This outputs SVG files and queues plots for optional PNG export using Inkscape (see blow). It may also output lower-quality PNGs (if option fundsr.internal_png is TRUE).

p <- run_plots(diffs, nd, plot_spec, xlm_data)
#> plot_roll_diffs: 365d rolling CAGR differences vs net benchmark: SPYY & IUSQ
#> plot_xlms: spyy, iusq
#> plot_roll_diffs: 365d rolling log-return differences vs net benchmark: SPYY & IUSQ
#> plot_roll_diffs: 365d rolling CAGR differences vs net benchmark: SPYY & IUSQ: recent years
#> plot_roll_diffs: 365d rolling log-return differences vs net benchmark: SPYY & IUSQ: recent years

Output

p[["ACWI"]]

p[["ACWI_L"]]

p[["ACWIz_L"]]

p[["xlm_ACWI"]]

Corresponding SVG files should be in the output directory.

Plot in another language

Sys.setlocale("LC_MESSAGES", "bg_BG.UTF-8") # needed on some systems
#> [1] "bg_BG.UTF-8"
Sys.setLanguage("bg")
plot_spec_bg <- plot_spec %>%
    mutate(plot_id = paste0(plot_id, "_bg"))
bg_p <- run_plots(diffs, nd, plot_spec_bg, xlm_data)
#> plot_roll_diffs: 365-дневни плъзгащи се CAGR разлики спрямо нетен индекс: SPYY & IUSQ
#> plot_xlms: spyy, iusq
#> plot_roll_diffs: 365-дневни плъзгащи се разлики в log доходност спрямо нетен индекс: SPYY & IUSQ
#> plot_roll_diffs: 365-дневни плъзгащи се CAGR разлики спрямо нетен индекс: SPYY & IUSQ: последни години
#> plot_roll_diffs: 365-дневни плъзгащи се разлики в log доходност спрямо нетен индекс: SPYY & IUSQ: последни години
bg_p[["ACWIz_bg_L"]]

bg_p[["xlm_ACWI_bg"]]

Optional high-quality PNG export

Process queued files via Inkscape (converting SVG to PNG).

export_pngs()
#> Executing "C:/Program Files/Inkscape/bin/inkscape.exe" --actions="export-background:white;file-open:output/ACWI.svg;export-filename:output/ACWI.png;export-width:1300;export-do;file-close;file-open:output/xlm_ACWI.svg;export-filename:output/xlm_ACWI.png;export-width:1300;export-do;file-close;file-open:output/ACWI_L.svg;export-filename:output/ACWI_L.png;export-width:1300;export-do;file-close;file-open:output/ACWIz.svg;export-filename:output/ACWIz.png;export-width:1300;export-do;file-close;file-open:output/ACWIz_L.svg;export-filename:output/ACWIz_L.png;export-width:1300;export-do;file-close;file-open:output/ACWI_bg.svg;export-filename:output/ACWI_bg.png;export-width:1300;export-do;file-close;file-open:output/xlm_ACWI_bg.svg;export-filename:output/xlm_ACWI_bg.png;export-width:1300;export-do;file-close;file-open:output/ACWI_bg_L.svg;export-filename:output/ACWI_bg_L.png;export-width:1300;export-do;file-close;file-open:output/ACWIz_bg.svg;export-filename:output/ACWIz_bg.png;export-width:1300;export-do;file-close;file-open:output/ACWIz_bg_L.svg;export-filename:output/ACWIz_bg_L.png;export-width:1300;export-do;file-close"
#> [1] 0