API Flow-Percentage

This chapter will cover recreating daily percentage flows for some of EPFR’s macro strategies, by retrieving data via the API.

This chapter includes recreations for,

  • Flow-Percentage Country Strategy
  • Multi-Asset Region Strategy
  • Multi-Asset FI Strategy

API Flow-Percentage Country Strategy

The Flow-Percentage Country Strategy is an equity-based rotation strategy which ranks different countries across a customized universe of both developed and emerging markets. This approach uses the stated allocations of equity funds with a cross-border focus to calculate the percentage flow for each country.

More information about this strategy can be found under Flow-Percentage Country Strategy.

Download the R Script source for this strategy

The steps to recreating the flow-percentage country strategy are as follows,

  • Collect all equity country allocations.
  • Collect all cross-border equity fund flows.
  • Subset to funds reporting both allocations and fund flows.
  • Scale monthly country allocations against daily flow/AuM for each fund.
  • Divide the scaled flow by the scaled AuM to get the flow percentage for each country.

We will be recreating daily percentage flow per country across one allocation month.

Both country allocations and fund flows use the same function epfr.get.reportid to create the report which returns data. Full documentation for the parameters of epfr.get.reportid can be in the API documentation.

More about available filters, asset classes, and return value categories call the api dictionaries available here.

API Country Allocations

Step 1 is to retrieve country allocations for all available asset classes, across one month.

alloc_month <- "202505"
date <- flow_to_api_date(yyyymm.to.day(alloc_month))

all_acs <- as.list(epfr.get.dictionary.assetclasses("CA")$data %>%
  filter(grepl("Equity", name)) %>% pull(id) %>% as.list()) # ALL AVALIABLE ASSET CLASSES

report_ac <- epfr.get.reportid(dataset = "CA", 
                            from_date = date, 
                            to_date = date, 
                            asset_classes = all_acs, 
                            frequency =  "Monthly", 
                            level = 2, # FUND LEVEL
                            categories = list(85), # FUND ID
                            average_type = 1, 
                            allocation_ids = as.list(seq(1, 187)), # ALL COUNTRIES
                            assets = T, 
                            universal_filters = NULL,
                            equity_filters = NULL,
                            bond_filters = NULL,
                            alternative_filters = NULL)

After the report ID is created, wait for report’s completion, then return the result.

report_id_ac <- report_ac$data$reportId
wait_for_completion(report_id_ac, "CA", inital_time_offset = 20, time_offset = 10) 
report_result_ca <- epfr.get.reportoutput("CA", report_id_ac) 
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"

The resulting data frame will contain the allocation Date, FundID, Total Net Assets, and the funds weight towards individual countries (as a %), as well as it’s API asset class and any API filters (in this case “none”).

Date FundID Total Net Assets Australia Brazil China USA Asset Class Filters
5/31/2025 30470 53.1179 0.0000 0.0000 0.0000 0.0000 Europe Regional-Western Europe-CA-Equity None
5/31/2025 91358 681.5458 0.5058 0.0000 0.8725 67.2088 Global-Global-CA-Equity None
5/31/2025 850 522.9530 0.0000 8.0940 29.1253 0.0000 Global Emerging Markets-GEM-CA-Equity None
5/31/2025 98514 1167.1641 1.8673 0.0000 0.8029 2.5642 Global ex-US-Global-CA-Equity None
5/31/2025 13005 864.4328 0.4300 0.4800 2.9600 70.4200 Global-Global-CA-Equity None
5/31/2025 92366 406.3273 13.2687 0.0000 28.5633 0.0000 Asia ex-Japan Regional-Asia Ex-Japan-CA-Equity None
5/31/2025 271 841.3444 7.8766 0.0000 27.5127 2.5021 Asia ex-Japan Regional-Asia Ex-Japan-CA-Equity None
5/31/2025 92368 1585.2415 14.2611 0.0000 28.0676 0.0000 Asia ex-Japan Regional-Asia Ex-Japan-CA-Equity None
5/31/2025 40529 466.0487 13.9533 0.0000 23.9249 0.0000 Asia ex-Japan Regional-Asia Ex-Japan-CA-Equity None
5/31/2025 102060 75.6420 0.0000 4.7855 24.6677 0.0000 Global Emerging Markets-GEM-CA-Equity None

Transform data such that there is a column of FundIDs and the rest of columns are the country allocations for each fund in the data frame.

curr_alloc_data <- report_result_ca %>%
  select(!c(Date, Fund, `Total Net Assets`, `Asset Class`, Filters)) %>%
  mutate(across(
    .cols = -c(FundID),
    .fns = ~ . / 100
  ))

print(curr_alloc_data %>% select(FundID, Australia, Brazil, China, USA) %>% top_n(10))
FundID Australia Brazil China USA
10959 0.000000 0 0.000000 0.766340
78120 0.000000 0 0.000000 0.768722
3701 0.000000 0 0.000000 0.819344
88963 0.008400 0 0.000000 0.876300
50111 0.013707 0 0.000585 0.805743
15342 0.000000 0 0.000000 0.762000
90008 0.000000 0 0.000000 0.982066
13663 0.004851 0 0.000000 0.844482
5168 0.004182 0 0.013083 0.784196
39696 0.000000 0 0.000356 0.765399

API Cross-Border Fund Flows

Next step is to retrieve the fund flows for all cross border funds which report allocations data. To do this query for all funds which have a cross border focus then filter the dataframe for FundIDs contained in the allocations dataframe curr_alloc_data.

First, we collect the start and end flow dates for the chosen allocation month.

flow_dates <- flowdate.ex.AllocMo(alloc_month)
start_date_api <- flow_to_api_date(flow_dates[1]) 
end_date_api <- flow_to_api_date(flow_dates[length(flow_dates)])

To get the cross border fund asset classes use the classif file, which is located in the sftp/ftp under Classifications/classif-GeoId.txt.

classif_geoid <- read in file from ftp located at Classifications/classif-GeoId.txt
xborder <- classif_geoid %>%
  filter(xBord == 1) %>%
  select(ApiEquityId) %>%
  pull()

Create the report id for the allocations data using the cross border ids. The report will return the FundIDs, flow USD and Net Assets Start for each fund over the time period.

report_ff <- epfr.get.reportid(dataset = "FF", 
                            from_date = start_date_api, 
                            to_date = end_date_api,  
                            asset_classes = xborder, 
                            frequency =  "Daily", 
                            level = 2, 
                            categories = list(53, 3, 13), #  FUND ID, FLOW USD, NET ASSETS START 
                            average_type = NULL, 
                            allocation_ids = NULL, 
                            assets = NULL, 
                            universal_filters = NULL, 
                            equity_filters = NULL, 
                            bond_filters = NULL, 
                            alternative_filters = NULL)

After creating the report wait for completion.

report_id_ff <- report_ff$data$reportId
wait_for_completion(report_id_ff, "FF", 30, 10)
## [1] "Inital wait for report to complete, delay 30"
## [1] "Report complete: 30 seconds taken"

Examine the top rows of the output.

Date Asset Class Filters Flow US$ mill Total Net Assets Start FundID
7/22/2025 Asia ex-Japan Regional-Asia Ex-Japan-FF-Equity None -0.0002 50.6880 93036
7/22/2025 Global-Global-FF-Equity None 0.0000 176.6451 77027
7/22/2025 Global-Global-FF-Equity None 0.0000 24.6424 50120
7/22/2025 Global-Global-FF-Equity None 0.0003 100.6267 77587
7/22/2025 Global-Global-FF-Equity None 0.0580 180.8213 76996
7/22/2025 Global-Global-FF-Equity None -0.2588 1324.8773 94879
7/22/2025 Global-Global-FF-Equity None 0.0000 1.3349 96822
7/22/2025 Global-Global-FF-Equity None -0.3366 70.8008 90098
7/22/2025 Global-Global-FF-Equity None 0.0015 0.9452 91045
7/22/2025 Global-Global-FF-Equity None 0.0000 12.0685 90121

We will only need the Date, FundID, Flow, and Total Net Assets Start to calculate the flow percentage, all other columns can be discarded.

report_result_ff <- epfr.get.reportoutput("FF", report_id_ff) 

curr_flow_data <- report_result_ff %>%
  select(c(Date, FundID,`Flow US$ mill`, `Total Net Assets Start`))

print(curr_flow_data %>% top_n(10))
Date FundID Flow US$ mill Total Net Assets Start
7/22/2025 14460 0.5472 164576.2
7/18/2025 14460 0.7913 163995.9
7/15/2025 14460 1.1877 164019.1
7/14/2025 14460 0.3411 163990.0
7/11/2025 14460 0.6604 165373.9
7/10/2025 14460 0.5022 165113.8
7/7/2025 14460 84.7347 164311.4
7/4/2025 14460 0.0000 164311.4
7/3/2025 14460 1.1156 164137.9
7/1/2025 14460 1.2995 163790.9

Calculating Flow-Percentage Country

To calculate the percentage flow to a given country, for a single day we use the formula \(\frac{100 \times \sum{(Flow \times Country Allocation)}}{\sum{(AssetsStart\times Country Allocation)}}\). We will start by summing Total Fund Flows and Total Net Assets Start scaled by Country Allocations, separately.

# SCALE NET ASSETS START
net_asset_start <- curr_alloc_data %>%
  mutate(FundID = as.character(FundID)) %>%
  inner_join(curr_flow_data, by="FundID") %>%
  mutate(across(
    .cols = -c(Date, FundID, `Total Net Assets Start`, `Flow US$ mill`),
    .fns = ~.* `Total Net Assets Start`)) %>%
  select(!c(FundID, `Total Net Assets Start`, `Flow US$ mill`)) %>% 
  group_by(Date) %>%
  summarise(across(everything(), sum))

# SCALE FLOW
flow <- curr_alloc_data %>%
  mutate(FundID = as.character(FundID)) %>%
  inner_join(curr_flow_data, by="FundID") %>%
  mutate(across(
    .cols = -c(Date, FundID, `Total Net Assets Start`, `Flow US$ mill`),
    .fns = ~.* `Flow US$ mill`)) %>%
  select(!c(FundID, `Total Net Assets Start`, `Flow US$ mill`)) %>% 
  group_by(Date) %>%
  summarise(across(everything(), sum))

We then calculate the weighted Total Fund Flow as a percentage over the weighted Total Net Assets Start, to get the Flow-Percentage for each country for a given Flow Date.

# CALCULATE FLOW PERCENTAGE
flowpct <- net_asset_start %>%
  inner_join(flow, by="Date", suffix = c("_NAS", "_flow")) %>%
  mutate(across(
    .cols = ends_with("_flow"),
    .fns = ~. / get(sub("_flow$", "_NAS", cur_column())) * 100,
    .names = "{sub('_flow$', '', .col)}_flowpct")) %>%
  select(Date, ends_with("_flowpct")) %>%
  mutate(Date = chrdate_to_flowdate_vec(Date)) %>%
  arrange(Date) %>%
  tibble::column_to_rownames(var = "Date")

names(flowpct) <- sub("_flowpct$", "", names(flowpct))

print(flowpct %>% select(Australia, Brazil, China, USA) %>% top_n(10))
Flow Date Australia Brazil China USA
20250624 0.0281460 0.0106213 0.0154277 0.0301169
20250627 0.0707934 0.0562816 0.0651659 0.0248924
20250701 0.0489678 0.0847893 0.0717443 0.0721819
20250703 0.0388208 0.1395051 0.1612564 0.1207161
20250709 0.0745372 0.0352085 0.0383067 0.0153098
20250711 0.0600016 0.0091183 0.0220054 0.1102320
20250714 0.0280433 -0.0441572 -0.0221710 0.0231083
20250715 0.0454806 0.0269412 0.0539303 0.0207744
20250718 0.0370245 0.0363192 0.0768154 0.0484770
20250721 0.0336125 0.0130824 0.0592443 0.0280501

Save result locally.

dir <- "path to your save directory"
mat.write(flowpct, paste0(dir, "FlowCtryPct_", alloc_month ,".csv"))

API Multi-Asset Region Strategy

EPFR’s Multi-Asset Strategy, rotates between asset classes within equity and fixed-income universes. The following code recreates the equity portion of the Multi-Asset strategy using the API. To learn more about this strategy you can read Multi-Asset Strategy section of this notebook.

Download the R Script source for this strategy

For this strategy we calculate aggregate flows into 7 different equity regions. Most of these regions are a single asset class where the API would aggregate flow percentage for us, but pacific ex Japan is made up of 5 asset classes meaning flow percentage will be aggregated using the net assets start and flow USD.

Category Asset Class - Equity API ID
AsiaXJP asia ex japan FF121000
EurXGB europe ex uk FF114004
Japan japan FF111003
LatAm latain america FF124000
UK uk FF114017
USA usa FF113002
PacXJP australia FF111001
PacXJP hong kong FF111002
PacXJP pacific regional FF111004
PacXJP singapore FF111005
PacXJP new zeland FF111006
dataset <- "FF"  
from_date <- "01-01-2025"  
to_date <- "07-28-2025" # TODAYS DATE

asset_classes <- list("FF121000", # ASIA EX JAPAN
                      "FF114004", # EUROPE EX GB
                      "FF111003", # JAPAN
                      "FF124000", # LATAM
                      "FF114017", # UK
                      "FF113002", # USA
                      "FF111001", # AUSTRALIA
                      "FF111002", # HONG KONG
                      "FF111004", # PACIFIC REGIONAL
                      "FF111005", # SINGAPORE
                      "FF111006"  # NEW ZELAND

)

frequency <- "Daily"  
level  <- 0  # AGGREGATE
categories <- list(13, 3) # ASSETS START, FLOW USD
assets <- "true"  
universal_filters <- list("U100005") # ACTIVE FUNDS
equity_filters <- NULL

The next steps for calling data from the API are to create the report, wait for completion, and then return the data.

report <- epfr.get.reportid(dataset, from_date, to_date, asset_classes, frequency, level,
                            categories, NULL, NULL, assets, universal_filters, equity_filters, NULL, NULL)

report_id <- report$data$reportId
wait_for_completion(report_id, "FF", 30, 30)
result <- epfr.get.reportoutput(dataset, report_id)

print(result %>% top_n(10))
## [1] "Inital wait for report to complete, delay 30"
## [1] "Report Incomplete time taken: 60"
## [1] "Report Incomplete time taken: 90"
## [1] "Report Incomplete time taken: 120"
## [1] "Report Incomplete time taken: 150"
## [1] "Report complete: 150 seconds taken"
## Selecting by Total Net Assets Start
Date Asset Class Filters Flow US$ mill Total Net Assets Start
7/28/2025 USA-North America-FF-Equity Active -33.4447 4548326
7/25/2025 USA-North America-FF-Equity Active -2316.4397 4534417
7/24/2025 USA-North America-FF-Equity Active -63.6056 4529274
7/23/2025 USA-North America-FF-Equity Active -522.0354 4502067
7/22/2025 USA-North America-FF-Equity Active 1142.6047 4492392
7/21/2025 USA-North America-FF-Equity Active -1197.8905 4496470
7/18/2025 USA-North America-FF-Equity Active 520.7728 4495671
7/11/2025 USA-North America-FF-Equity Active -1033.4003 4500242
7/10/2025 USA-North America-FF-Equity Active -1202.9866 4492208
7/7/2025 USA-North America-FF-Equity Active 109.0544 4487428

After getting the report output calculate the percent flow to each of the 7 equity regions, as \(\frac{100 \times \sum{Flow}}{\sum{AssetsStart}}\). Complete methodology is outlined in the Multi-Asset Strategy section of this notebook.

result_multiAsset_Rgn_daily <- result %>%
  mutate(Ctry = recode(`Asset Class`,
                  "Asia Ex-Japan-FF-Equity" = "AsiaXJP",
                  "Europe ex-UK Regional-Western Europe-FF-Equity" = "EurXGB",
                  "Japan-Asia Pacific-FF-Equity" = "Japan",
                  "LatAm-FF-Equity" = "LatAm",
                  "United Kingdom-Western Europe-FF-Equity" = "UK",
                  "USA-North America-FF-Equity" = "USA",
                  "Australia-Asia Pacific-FF-Equity" = "PacXJP",
                  "Hong Kong Special Administrative Region of China-Asia Pacific-FF-Equity" = "PacXJP",
                  "Singapore-Asia Pacific-FF-Equity" = "PacXJP",
                  "Pacific Regional-Asia Pacific-FF-Equity" = "PacXJP",
                  "New Zealand-Asia Pacific-FF-Equity" = "PacXJP",
                  .default = `Asset Class`) 
  ) %>%
  group_by(Date, Ctry) %>% 
  summarise(`Flow US$ mill` = sum(`Flow US$ mill`), 
            `Total Net Assets Start` = sum(`Total Net Assets Start`)) %>%
  mutate(floPct = 100 *`Flow US$ mill` / `Total Net Assets Start`) %>%
  select(Date, Ctry, floPct) %>% 
  pivot_wider(names_from = Ctry, values_from = floPct) %>% 
  mutate(Date = as.Date(Date, format = "%m/%d/%Y")) %>%
  arrange(Date)

Examine the top rows of the output.

print(result_multiAsset_Rgn_daily %>% arrange(desc(Date)) %>% top_n(10))
Date FundID Flow US$ mill Total Net Assets Start
7/7/2025 14460 84.7347 164311.4
7/4/2025 14460 0.0000 164311.4
7/3/2025 14460 1.1156 164137.9
7/22/2025 14460 0.5472 164576.2
7/18/2025 14460 0.7913 163995.9
7/15/2025 14460 1.1877 164019.1
7/14/2025 14460 0.3411 163990.0
7/11/2025 14460 0.6604 165373.9
7/10/2025 14460 0.5022 165113.8
7/1/2025 14460 1.2995 163790.9

Save result locally.

dir <- "path to your save directory"
write.csv(result_multiAsset_Rgn_daily, paste0(dir, "MultiAsset-Rgn-daily-API.csv"), row.names = FALSE)

API Multi-Asset FI Strategy

EPFR’s Multi-Asset Strategy, rotates between asset classes within equity and fixed-income universes. The following code recreates the fixed-income portion of the Multi-Asset strategy using the API. To learn more about this strategy you can read Multi-Asset Strategy section of this notebook.

Download the R Script source for this strategy

For this strategy we calculate aggregate flows into 9 fixed-income asset classes, which are listed below. We will need to use multiple filters to achieve these desired asset classes using the API. (Note that when a filter is applied to an asset class it will be applied over all data in the API call, so for this reason there are multiple API reports created.)

Each category is defined in a list with 3 elements: category name, asset class, and bond filter.

Category Asset Class API ID1 Bond Filter API ID2
GLOBEM global EMs bond FF223002
HYIELD all bonds FF200000 high yield B100013
FLOATS all bonds FF200000 bank loan B100016
USTRIN usa bonds FF213002 intermediate term B100009
USTRLT usa bonds FF213002 long term B100008
USTRST usa bonds FF213002 short term B100010
CASH all money market FF400000
USMUNI usa bonds FF213002 municipal B100019
GLOFIX global bond FF212000
dataset <- "FF"
from_date <- "01-01-2025"
to_date <- "07-28-2025"  # TODAY'S DATE
frequency <- "Daily"
level  <- 0
categories <- list(13, 3) # ASSETS START, FLOW USD
assets <- "true"

filters <- list(list("GLOBEM", "FF223002", NULL), 
                list("HYIELD", "FF200000", "B100013"), 
                list("FLOATS", "FF200000", "B100016"), 
                list("USTRIN", "FF213002", "B100009"), 
                list("USTRLT", "FF213002", "B100008"), 
                list("USTRST", "FF213002", "B100010"), 
                list("CASH", "FF400000", NULL), 
                list("USMUNI", "FF213002", "B100019"), 
                list("GLOFIX", "FF212000", NULL))

The next steps for calling data from the API are to create the report, wait for completion, and then return the data. We will do this for all 9 fixed-income asset classes.

result_multiAsset_FI_daily <- NULL

for (i in 1:length(filters)) {

  print(paste0("Calling data for: ", filters[[i]][[1]]))

  report <- epfr.get.reportid(dataset,
                              from_date,
                              to_date,
                              list(filters[[i]][[2]]), # ASSET CLASS
                              frequency,
                              level,
                              categories,
                              NULL,
                              NULL,
                              assets,
                              NULL,
                              NULL,
                              if (is.null(filters[[i]][[3]])) NULL else list(filters[[i]][[3]]),
                              NULL) # BOND FILTER

  report_id <- report$data$reportId
  wait_for_completion(report_id, dataset, 20, 30)
  result_catagory <- epfr.get.reportoutput(dataset = "FF",  report_id = toString(report_id))

  restult_catagory_clean <-  result_catagory %>%
    mutate(FloPct = 100 *`Flow US$ mill` / `Total Net Assets Start`,
           Date = as.Date(Date, format = "%m/%d/%Y")) %>%
    select(Date, FloPct) %>%
    rename(!!filters[[i]][[1]] := FloPct)

  if (is.null(result_multiAsset_FI_daily)) {
    result_multiAsset_FI_daily <- restult_catagory_clean
  } else {
    result_multiAsset_FI_daily <- result_multiAsset_FI_daily %>% inner_join(restult_catagory_clean, by = "Date")
  }

}
## [1] "Calling data for: GLOBEM"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: HYIELD"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: FLOATS"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: USTRIN"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: USTRLT"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: USTRST"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: CASH"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: USMUNI"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"
## [1] "Calling data for: GLOFIX"
## [1] "Inital wait for report to complete, delay 20"
## [1] "Report complete: 20 seconds taken"

Check top rows of output.

print(result_multiAsset_FI_daily %>% arrange(desc(Date)) %>% top_n(10))
Date GLOBEM HYIELD FLOATS USTRIN USTRLT USTRST CASH USMUNI GLOFIX
2019-06-05 -0.0279737 0.0953499 -0.1986875 0.0177541 0.0094597 0.1615458 0.0729610 0.0417629 0.4192535
2014-05-26 0.0521502 -0.0093754 -0.0000483 0.0024525 -0.0004529 -0.0018943 -0.1878645 -0.0011076 0.3851974
2010-04-27 0.3376269 0.1029392 0.2648442 0.0030902 0.1746803 0.0602334 0.0982793 0.0080934 0.3946408
2010-04-16 0.4127297 0.0711022 0.4498816 -0.0104679 0.0531455 0.0845487 -0.1359191 0.0055590 0.3806760
2010-03-03 0.2413263 0.0803211 0.2213384 0.0867212 0.0254124 0.2971411 0.0944945 0.0471712 0.4351618
2010-01-20 0.3366571 0.0616013 0.2117595 0.0333624 0.0255387 0.2164246 -0.0404757 0.0590163 0.3594302
2009-10-27 0.3782404 0.0570280 0.0604020 0.0131985 -0.0056886 0.2609019 0.1491588 0.0774289 0.3631106
2009-10-19 0.0045374 0.0594008 -0.1519629 0.1209147 0.0139186 0.3042336 0.0739953 0.0844787 0.4147324
2009-07-16 0.2589983 0.0386666 0.1033427 0.1252718 -0.0324792 0.2348748 0.2249244 0.0721367 0.3610647
2009-07-06 0.0130407 0.0321640 0.3548821 -0.2521641 0.0004447 0.3632362 0.2354842 0.0784644 0.5482938

Save result locally.

dir <- "path to your save directory"
write.csv(result_multiAsset_FI_daily, paste0(dir, "result_multiAsset_FI_daily.csv"), row.names = FALSE)