4.2 Strategy Implementation

The final step is implementing this strategy to create a signal. Let’s begin by reading in the flow and return files to our R studio workspace.

x <- mat.read(flow.file) # GET FLOW PERCENTAGE C:\\EPFR\\daily\\FloPctSector-US-daily.csv
y <- mat.read(ret.file)  # GET RETURN C:\\EPFR\\returns\\PsuedoReturns-Sector-US-daily.csv

Next, we will need to ensure that the data structure in \(x\) and \(y\) are completely aligned, having the same column names in the same order. Below we will subset the columns of \(x\) to use the same sectors, in the same order as \(y\).

x <- as.matrix(x[, dimnames(y)[[2]]]) # LINE UP INDICATORS WITH RETURNS 

* Note: subsetting can also be done when creating the flow and return files

4.2.1 Compounding Flows

Next, we set up a variable for our lookback period, which can also be called a flow window. This variable will be the window of time we use to create a trailing compounded daily percentage flow. The lookback period we choose for our demonstrations is 20 days.

lookback <- 20 # FLOW WINDOW (IN WEEKDAYS) - 20 day look back period

Again, using a function from the library('EPFR.r'), compound.flows() compounds our daily percentage flow over the trailing lookback period for each sector.

x <- compound.flows(x, lookback, F) # COMPOUND FLOWS
CDisc CStpls Engy Fins HCare Indls Tech Matls Telco Utes REst FinsExREst
20221201 -0.1509415 -0.2250062 0.6044772 -0.2808077 -0.0057585 -0.0289950 -0.1753918 -0.4577443 0.0143092 0.2198572 -1.1441277 0.0042669
20221202 -0.1478096 -0.1767291 0.3400230 -0.2732577 0.0510085 -0.0166465 -0.0866750 -0.3542438 0.0273871 0.2411600 -1.1013972 -0.0002700
20221205 -0.0422361 -0.0162768 0.4322582 -0.1392681 0.1139422 0.0972506 0.0000681 -0.3065147 0.0722724 0.2812595 -0.8822782 0.1048242
20221206 -0.0722140 -0.0336643 0.3658648 -0.1823607 0.0953741 0.0720908 -0.0102382 -0.3810076 0.0533155 0.1301528 -0.9054895 0.0549232
20221207 -0.1002727 -0.0618456 0.2749281 -0.2065582 0.1028432 0.0443100 -0.0173922 -0.4651538 0.0227681 0.0917185 -0.7905249 -0.0152572
20221208 -0.1393354 -0.1889118 0.2466263 -0.2722208 0.0550748 -0.0550250 -0.0694212 -0.7207375 -0.0138364 0.0941653 -0.8540915 -0.0811986

4.2.2 Total Return Index

We will now convert our percentage returns data \(y\) to total index returns indexed so that time moves forward. To do this we will use the function ret.to.idx() from library('EPFR.r'). We will also use the functions map.rname() to ensure the row names of the matrices line up with our flow file and ret.idx.gaps.fix() to replace any NA values. Please refer to the library documentation for the complete list of parameters for these functions (tip: ?ret.to.idx(),?ret.idx.gaps.fix() ).

y[is.na(y)] <- 0
y <- ret.to.idx(map.rname(y, dimnames(x)[[1]])) # CONVERT TO A TOTAL-RETURN INDEX
y <- ret.idx.gaps.fix(y)

4.2.3 Ranking Sectors

Next, we sort each of the sectors in our country or region into five equal bins based on their compounded percentage flow values for the selected holding period. To do this, we will use the function from library('EPFR.r'), called bbk(). This function will output a standardized backtest result.

The bbk() function requires our daily percentage flow data compounded over a desired period and the total return index data. Please refer to the library documentation for the complete list of parameters of this function (tip: ?bbk()).

The first parameter we add is the number of bins we want to use. For our case, we want to use 5 because our strategy is to go long the top fifth and short the bottom fifth.

nBin <- 5 # NUMBER OF BINS

Since EPFR data is published with a T+1 day lag and is released around 5:00 pm EST, we account for a T+2 day delay in our model. Users interested in more timely signals can also use the T+2 open prices for backtesting purposes. Alternatively, EPFR’s Premium Daily offering collects an earlier release of end-of-day data which includes a significant subset of its original fund-level flow information.

delay <- 2 # DELAY IN KNOWING DATA (IN WEEKDAYS) - data takes time to have

It is also important to note that this model will need to be re-balanced weekly. The day of the week the rebalancing occurs is at the user’s discretion. For this example we will set the day of the week to trade as Friday.

doW <- 5 # DAY OF THE WEEK YOU WILL TRADE ON (5 = FRIDAYS)

Additionally, we also evaluate the returns for different holding periods. The user can input return the horizons that they are interested in here. For this example, we define a return horizon for weekly, fortnightly, monthly, bi-monthly, quarterly, and semi-annual rebalancing.

hz <- c(5, 10, 20, 45, 65, 130) # RETURN HORIZON (IN WEEKDAYS) - holding periods

Now that we have defined all of our inputs, to rank the sectors into quintiles by their 20-day percentage flow, we call the function bbk() for a one-week holding period.

z <- bbk(x, y, 1, hz[1], nBin, doW, T, 0, delay)

4.2.4 Model

Go long the top basket and short the bottom basket.

20-day flow percentage ranked into quintiles (computed only where forward returns are available)

z[["bins"]]
CStpls Engy Fins HCare Indls Tech Matls Telco Utes REst
20221230 4 1 2 3 2 5 5 3 1 4
20221223 4 1 3 2 2 5 5 3 1 4
20221216 4 1 3 2 2 4 5 3 1 5
20221209 4 1 3 1 2 4 5 3 2 5
20221202 4 1 2 3 2 4 5 3 1 5

Quintile returns over the equal-weight universe

z[["rets"]]
Q1 Q2 Q3 Q4 Q5 TxB uRet
20221230 -1.8566050 1.0887642 0.3708237 0.0497519 0.3472651 -2.2038701 2.2246404
20221223 -0.0418929 0.3402011 0.4276992 -0.3162858 -0.4097217 0.3678288 -0.1898268
20221216 2.1395331 0.2277807 0.0070763 -1.9778575 -0.3965326 2.5360658 0.2637821
20221209 1.9191695 0.7071599 -0.9190817 -0.9616110 -0.7456367 2.6648062 -1.5945934
20221202 -1.6735452 0.0697746 0.3735787 0.1790699 1.0511220 -2.7246672 -3.5737229

Def: TxB represents summary statistics for the long/short portfolio (top - bottom = Q1 - Q5 = overall portfolio returns)


4.2.5 Performance

Performance over all holding periods

fcn <- function(retW) {as.matrix(bbk(x, y, 1, retW, nBin, doW, T, 0, delay)$summ)} # DEFINE SUMMARY FUNCTION

sapply(split(hz, hz), fcn, simplify = "array") # WRITE SUMMARIES
Q1 Q2 Q3 Q4 Q5 TxB uRet
Weekly
AnnMn 7.7 -2.3 -1.9 -0.3 -3.3 11.0 7.8
AnnSd 10.7 7.9 7.6 8.3 9.7 16.8 20.2
Sharpe 72.1 -28.7 -25.2 -3.2 -33.5 65.1 38.7
HitRate 7.1 -2.6 -0.2 -0.3 -4.6 6.0 7.1
Beta 0.0 0.0 0.0 0.0 0.0 0.0 1.0
Alpha 7.5 -2.3 -1.8 0.0 -3.4 10.8 0.0
DrawDn -21.1 -55.7 -54.1 -33.6 -83.8 -33.7 -76.6
DDnBeg 20220610 20081107 20081024 20070622 20100702 20220610 20071012
DDnN 34 743 718 77 557 34 73
AnnTo 1114 2248 2549 2308 1178 2292 0
Fortnightly
AnnMn 6.3 -2.2 -0.6 -0.5 -3.0 9.3 7.5
AnnSd 10.7 7.7 7.6 7.8 8.8 16.1 19.3
Sharpe 58.5 -28.7 -9.1 -6.0 -33.1 56.9 39.2
HitRate 4.9 -0.5 0.0 -0.5 -4.2 6.8 10.0
Beta 0.0 0.0 0.0 0.0 0.0 0.0 1.0
Alpha 6.0 -1.9 -0.8 -0.2 -3.1 9.1 0.0
DrawDn -20.2 -55.7 -30.4 -30.1 -83.1 -36.4 -72.9
DDnBeg 20215418 20081062 20135464 20070723 20100418 20210308 20071008
DDnN 19 341 51 196 306 35 36
AnnTo 847 1460 1605 1453 872 1720 0
Monthly
AnnMn 5.6 -1.5 -0.9 -1.1 -2.2 7.8 7.5
AnnSd 10.2 7.3 7.3 8.1 8.5 15.1 19.1
Sharpe 55.4 -20.2 -12.2 -14.1 -26.0 52.3 39.4
HitRate 9.2 -1.8 -0.6 -2.6 -4.4 7.5 12.5
Beta 0.0 0.0 0.0 0.0 0.0 0.0 1.0
Alpha 5.5 -1.2 -0.9 -0.8 -2.5 8.0 0.0
DrawDn -19.8 -42.2 -39.3 -41.3 -74.1 -33.4 -68.4
DDnBeg 20198012 20127988 20073296 20070838 20097993 20178008 20070911
DDnN 15 110 136 150 153 17 20
AnnTo 626 881 894 909 646 1272 0
Bi-Monthly
AnnMn 3.7 -0.5 0.7 -1.5 -2.4 6.1 7.1
AnnSd 10.1 7.5 7.1 7.5 8.7 15.2 18.1
Sharpe 36.6 -7.7 9.9 -21.2 -29.4 40.5 39.8
HitRate 8.6 2.0 5.6 -3.3 -6.0 7.5 16.3
Beta 0.0 -0.1 0.0 0.0 0.1 0.0 1.0
Alpha 3.4 0.1 0.7 -1.2 -3.0 6.3 0.0
DrawDn -20.7 -32.7 -26.3 -41.6 -75.9 -30.1 -60.9
DDnBeg 20113939 20112865 20117285 20085108 20102873 20081688 20070870
DDnN 16 29 37 60 62 7 9
AnnTo 312 415 414 410 322 634 0
Quarterly
AnnMn 3.5 -0.5 0.7 -1.8 -1.9 5.5 7.1
AnnSd 9.3 7.7 7.3 7.4 8.5 13.9 17.4
Sharpe 38.9 -5.8 9.4 -24.6 -25.0 39.5 41.3
HitRate 7.0 2.5 5.1 -5.0 -6.3 8.2 17.9
Beta 0.0 0.0 0.0 -0.1 0.1 0.0 1.0
Alpha 3.3 -0.2 0.7 -1.4 -2.3 5.6 0.0
DrawDn -21.2 -35.1 -25.9 -39.1 -69.5 -38.4 -56.8
DDnBeg 20146599 20109802 20112855 20093701 20102121 20123790 20071638
DDnN 8 16 15 42 44 5 6
AnnTo 232 294 293 288 238 470 0
Semi-Annual
AnnMn 3.0 -0.5 0.5 -0.9 -2.2 5.2 7.2
AnnSd 8.8 7.2 7.2 7.7 9.1 14.2 18.1
Sharpe 35.2 -5.8 8.6 -12.3 -26.5 38.2 40.4
HitRate 8.7 -1.4 5.8 -2.9 -9.6 11.6 19.9
Beta 0.0 0.0 0.0 -0.1 0.1 0.0 1.0
Alpha 2.7 -0.3 0.5 -0.3 -2.6 5.4 0.0
DrawDn -23.0 -32.9 -25.2 -34.6 -61.5 -37.0 -48.7
DDnBeg 20128651 20117665 20127621 20112646 20105746 20112554 20071216
DDnN 8 13 10 14 22 5 3
AnnTo 126 148 145 152 128 254 0

Annualized mean one-week returns

bbk(x, y, 1, hz[1], nBin, doW, T, 0, delay)$annSumm # DISPLAY CALENDAR-YEAR RETURNS 
Q1 Q2 Q3 Q4 Q5 TxB uRet nPrds
2007 4.6 8.7 -4.9 -21.2 12.8 -8.2 -1.0 31
2008 14.4 -1.3 1.7 -19.4 4.6 9.8 -46.5 52
2009 14.9 -5.7 -21.4 16.2 -3.9 18.8 34.6 52
2010 8.7 -6.7 -6.5 3.3 1.2 7.5 17.2 53
2011 9.7 3.7 -3.4 -2.0 -8.0 17.6 2.2 52
2012 3.4 2.0 4.4 2.1 -11.9 15.3 13.7 52
2013 4.6 3.8 -0.2 3.1 -11.3 15.9 24.4 52
2014 5.4 -3.7 6.3 0.7 -8.7 14.1 13.2 52
2015 6.8 -8.1 -2.9 11.3 -7.1 13.9 -4.5 52
2016 -2.4 -3.9 4.9 -1.3 2.8 -5.2 11.3 53
2017 0.2 3.9 1.4 -0.7 -4.8 5.0 11.5 52
2018 11.6 -3.9 2.1 -4.8 -5.1 16.7 -9.6 52
2019 2.9 3.8 5.7 -0.2 -12.2 15.1 23.7 52
2020 38.7 -4.0 -24.0 -2.5 -8.3 47.0 13.1 52
2021 -8.6 -11.7 0.1 8.9 11.2 -19.8 23.5 53
2022 16.4 -6.9 5.0 -9.7 -4.8 21.2 -12.7 52