A re-balance strategy consists in a portfolio \(\Pi\), with \(\bar{n}\)-assets whose relative weights \(w\) need to be constant and equal to an ideal allocation \(w^{\star}\). Let’s consider the following situation:
the portfolio \(\Pi\) is self-financing: a capital is invested at time \(t_{now}\) and the profit and losses (\(P\&L\)) are generated by the returns of the instruments inside the portfolio.
The assets available are in dollars, but the investment capital is expressed in euros (€). In this way we simulate the situation of an European investor that have to deal with the currency risk coming from the fact that the assets are not quoted in the domestic currency (euros).
We consider also the transaction costs, but not (eventual) capital gain taxes. Moreover, in order to avoid to re-balance too often, we set an additional parameter \(\epsilon \in [0,1)\) to contro it. In this way the rebalance will occur only if the weight of an asset, i.e. \(w\), exceed the following bounds with respect to the ideal weight \(w^{\star}\): \[w^{\star} (1-\epsilon ) \leq w \leq w^{\star} (1+\epsilon) \; \; \Rightarrow \; \; \text{no rebalance}\]
1.1 Implementation in R
Show the inputs
require("dplyr") # for data manipulation require("ggplot2") # for plots require("readr") # for importing data (or simply read.csv)# risky assets dataset <- readr::read_csv("dataset.csv", show_col_types =FALSE)# initial date for the strategyt_now <-as.Date("2018-01-01")# end date for the strategyt_hor <-as.Date("2023-05-01") # capital invested at time t_now initial_capital <-10000# ideal weights of risky assetsw_risky =c(BTC =0.1, SP500 =0.6, GOLD =0.1, USDEUR =0.2)# implied risk-free investments (liquid cash in EUR)w_riskfree =max(1-sum(w_risky), 0)# tolerance to avoid a re-balancing a small quantity w_tolerance =0.10# Transaction fee tx_fee <-0.1/100# filter risk_asset dataset to be in [t_now, t_hor] dataset <- dataset[dataset$date >= t_now & dataset$date <= t_hor,]# select the assets risky_asset <- dataset[,colnames(dataset) %in%c("date", names(w_risky))]# update t_now with the most recent date if t_now is an holidayt_now <-min(risky_asset$date)# select the prices at time t_now price_now <-unlist(risky_asset[risky_asset$date == t_now,][,-1])
We consider a capital of 10000€ invested in 2018-01-02 up to 2023-05-01. We consider a portfolio composed by Bitcoin (10%), the S&P500 index (60%), Gold spot (10%) and the remaining part liquid in USD (20%). The rebalances will occur in the following bounds:
Asset
\(w^{\star} (1-\epsilon )\)
\(w^{\star}\)
\(w^{\star} (1+\epsilon )\)
Value
BITCOIN
9%
10%
11%
1000€
S&P500
54%
60%
66%
6000€
GOLD
9%
10%
11%
1000€
USD
18%
20%
22%
2000€
Show the initialization
# initialize risky investments dataset df_risky <- dplyr::tibble( date = t_now, # risky asset symbols symbol =colnames(risky_asset)[-1], # risky asset prices at t_now price = price_now,# risky asset weights weights = w_risky,# risky asset total value at t_now value = initial_capital*weights,# risky asset holdings amount at t_now holdings = value/price )# initialize risk-free investments datasetdf_riskfree <- dplyr::tibble(date = t_now, # risky asset symbols symbol ="EUR", # riskfree asset prices at t_now price =1,# risky asset weights weights = w_riskfree,# risky asset total value at t_now value = initial_capital*weights,# risky asset holdings amount at t_now holdings = value/price )# initialize the portfoliodf_portfolio <- dplyr::tibble(date = t_now, # time now symbol ="Portfolio", capital = initial_capital, # initial capitalrebalance =FALSE, # if TRUE has happened a rebalance change =0) # return
Show the re-balance
fees <-0# initialize a list to store each rebalance strategy <-list()strategy[[1]] <-list()strategy[[1]]$risky <- df_riskystrategy[[1]]$riskfree <- df_riskfreestrategy[[1]]$portfolio <- df_portfoliofor(i in2:nrow(risky_asset)){# start from the previous rebalance strat <- strategy[[i-1]] # update the date in all the lists strat$risky$date <- risky_asset$date[i] strat$riskfree$date <- risky_asset$date[i] strat$portfolio$date <- risky_asset$date[i]# update prices for risky assets strat$risky$price <-unlist(risky_asset[i,][,-1])# Update weights and portfolio value # compute value of risky assets given holdings at previous time strat$risky$value <- strat$risky$holdings*strat$risky$price# update value of total capital strat$portfolio$capital <- strat$riskfree$value +sum(strat$risky$value)# compute actual weights of risky assets strat$risky$weights <- strat$risky$value/strat$portfolio$capital# compute target value of risky assets given ideal allocation target_risky_value <- strat$portfolio$capital*w_risky# Re-balance # h_reb positive imply BUY while h_reb negative imply SELL h_reb <- (target_risky_value - strat$risky$value)/strat$risky$price# check tolerance bounds: re-balance only if weights exceed bounds w_actual <- strat$risky$weights w_star_up <- w_risky*(1+ w_tolerance) w_star_dw <- w_risky*(1- w_tolerance) index_tolerance <- w_actual <= w_star_dw | w_actual >= w_star_up# set to zero the quantity for non-rebalancing assets h_reb[!index_tolerance] =0# check if no rebalances occurs if(sum(index_tolerance !=0)){# add TRUE if re-belance happens strat$portfolio$rebalance <-TRUE } else {# add FALSE if re-belance does not happen strat$portfolio$rebalance <-FALSE }# update holdings strat$risky$holdings <- strat$risky$holdings + h_reb fees <-abs(h_reb*strat$risky$price*tx_fee)# update value of the risky assets given new weights strat$risky$value <- strat$risky$holdings*strat$risky$price - fees# update capital in EUR h_reb positive means buy so we have sign reverted strat$riskfree$value <- strat$riskfree$value -sum(h_reb*strat$risky$price)# update portfolio value strat$portfolio$capital <-sum(strat$risky$value) + strat$riskfree$value# check that new weights are equal to ideal allocation strat$risky$weights <- strat$risky$value/strat$portfolio$capital# drow-down: compute portfolio percentage change with respect to initial capital strat$portfolio$change <- (strat$portfolio$capital - initial_capital)/initial_capital# add to strategy list strategy[[i]] <- strat}# data-set with the re-balancing strategy portfolio <- purrr::map_df(strategy, ~.x$portfolio)