R/backtesters.R

Defines functions backtestPortfolioGBM

Documented in backtestPortfolioGBM

#' Backtester for GBM Kelly-portfolio strategy
#'
#' @param stocks the data set of stocks
#' @param rolling boolean for rolling estimates
#' @param bankroll initial bankroll
#' @param rate risk-free rate
#' @param restraint max percentage of wealth to use
#' @param numDays number of days to trade for
#' @param sampleSize number of days for initial sample size in training data or rolling window size
#'
#' @description {Backtest the Kelly-portfolio on a set of stock prices. The vector of drifts and
#' covariance matrix are estimated using either the entire sample set or a rolling window of it.}
#' @return list
#' @export backtestPortfolioGBM
backtestPortfolioGBM <- function(stocks, rolling = TRUE, bankroll = 1500, rate = 0.0, restraint = 0.9, numDays = 30, sampleSize = 30)
{

  returns <- stockReturns(stocks, "log")
  # Number of days, assets
  n <- nrow(stocks)
  m <- ncol(stocks)
  if(sampleSize > n)
  {
    stop(paste("sampleSize must be less than ", n))
  }
  if(numDays > n-sampleSize)
  {
    stop(paste("numDays must be less than", n-sampleSize))
  }
  # Create the portfolio array
  portfolio <- matrix(0, nrow = numDays)
  portfolio[1] <- bankroll
  par <- findistr::fitGBMs(returns[1:(sampleSize), ])
  drift <- par$drift
  Sigma <- par$Sigma
  ww <- kellyfractions::kellyPortfolioGBM(drift, Sigma, rate, restraint)
  w <- (ww[-c(m+1)]) # remove cash component
  shares <- bankroll*w/stocks[sampleSize+1, ]
  cash <- ww[m+1]*bankroll
  # Populate it

  for(i in 2:numDays)
  {

    # Continuous time rebalancing:
    # New value
    portfolio[i] <- as.numeric((shares)%*%as.numeric(stocks[sampleSize+i, ])+cash*exp(rate/252))
    # Optimal fraction recomputed each step
    # use i:(...) for rolling window of data used in the estimation
    if(rolling)
    {
      par <- findistr::fitGBMs(returns[i:(sampleSize+i-1),])
      drift <- par$drift
      Sigma <- par$Sigma
      ww <- kellyfractions::kellyPortfolioGBM(drift, Sigma, rate, restraint)

    } else
    {
      par <- findistr::fitGBMs(returns[1:(sampleSize+i-1),])
      drift <- par$drift
      Sigma <- par$Sigma
      ww <- kellyfractions::kellyPortfolioGBM(drift, Sigma, rate, restraint)
    }
    w <- (ww[-c(m+1)]) # remove cash component
    shares <- portfolio[i]*w/stocks[sampleSize+i, ]
    cash <- ww[m+1]*portfolio[i]
    # print(shares[, shares>0])
  }
  # Time-stamps
  tts <- time(stocks[(sampleSize+1):(sampleSize+numDays),])
  portfolio <- xts::xts(x = portfolio, order.by = tts)
  profit <- as.numeric(portfolio[nrow(portfolio)])-as.numeric(portfolio[1])
  rr <- as.numeric(profit/portfolio[1])
  lg <- log(as.numeric(portfolio[nrow(portfolio)]))-log(as.numeric(portfolio[1]))
  results <- data.frame(pnl = profit, return = rr, log_growth = lg)
  print(plot(portfolio, type = "l"))
  output <- list(input = data.frame(bankroll, sampleSize, numDays, rate, restraint),
                 results = results)
  return(output)
}
shill1729/trader documentation built on Dec. 27, 2022, 10:55 p.m.