
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Library General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU Library General
# Public License along with this library; if not, write to the
# Free Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA  02111-1307  USA


################################################################################
# FUNCTION:                   DESCRIPTION:
#  assetsMeanCov               Estimates mean and variance for a set of assets
# FUNCTION:                   DESCRIPTION:
#  .covMeanCov                 uses sample covariance estimation
#  .mveMeanCov                 uses "cov.mve" from [MASS]
#  .mcdMeanCov                 uses "cov.mcd" from [MASS]
#  .studentMeanCov             uses "cov.trob" from [MASS]
#  .MCDMeanCov                 requires "covMcd" from [robustbase]  
#  .OGKMeanCov                 requires "covOGK" from [robustbase] 
#  .nnveMeanCov                uses builtin from [covRobust]
#  .shrinkMeanCov              uses builtin from [corpcor]
#  .baggedMeanCov              uses builtin from [corpcor]
#  .arwMeanCov                 uses builtin from [mvoutlier]
#  .donostahMeanCov            uses builtin from [robust]
#  .bayesSteinMeanCov          copy from Alexios Ghalanos
#  .ledoitWolfMeanCov          uses builtin from [tawny]
#  .rmtMeanCov                 uses builtin from [tawny]
# FUNCTION:                   DESCRIPTION:
#  getCenterRob                Extracts the robust estimate for the center
#  getCovRob                   Extracts the robust estimate for the covariance
################################################################################


assetsMeanCov <- 
  function(x, 
           method = c("cov", "mve", "mcd", "MCD", "OGK", "nnve", "shrink", "bagged"), 
           check = TRUE, force = TRUE, baggedR = 100, sigmamu = scaleTau2, alpha = 1/2,
           ...)
  {   
    # A function implemented by Diethelm Wuertz
    
    # Description:
    #   Computes robust mean and covariance from multivariate time series
    
    # Arguments:
    #   x - a multivariate time series, a data frame, or any other
    #       rectangular object of assets which can be converted into
    #       a matrix by the function 'as.matrix'. Optional Dates are 
    #       rownames, instrument names are column names.
    #   method - Which method should be used to compute the covarinace?
    #       method = "cov"        sample covariance computation
    #       method = "mve"        uses "mve" from [MASS]
    #       method = "mcd"        uses "mcd" from [MASS]
    #       method = "MCD"        uses "MCD" from [robustbase]
    #       method = "OGK"        uses "OGK" from [robustbase]
    #       method = "nnve"       uses "nnve" from [covRobust]
    #       method = "shrink"     uses "shrinkage" from [corpcor] 
    #       method = "bagged"     uses "bagging" [corpcor]
    #   alpha - MCD: numeric parameter controlling the size of the subsets 
    #       over which the determinant is minimized, i.e., alpha*n observations 
    #       are used for computing the determinant. Allowed values are between 
    #       0.5 and 1 and the default is 0.5.
    #   sigma.mu - OGK: a function that computes univariate robust location 
    #       and scale estimates. By default it should return a single numeric 
    #       value containing the robust scale (standard deviation) estimate. 
    #       When mu.too is true, sigmamu() should return a numeric vector of 
    #       length 2 containing robust location and scale estimates. See 
    #       scaleTau2, s_Qn, s_Sn, s_mad or s_IQR for examples to be used as 
    #       sigmamu argument.
    
    # Note:
    #   The output of this function can be used for portfolio
    #   optimization.
    
    # Example:
    #   DJ = 100 * returns(as.timeSeries(data(DowJones30)))
    #   DJ = DJ[, c("CAT", "IBM", "GE", "JPM")]
    #   Sample Covariance:
    #       assetsMeanCov(DJ, "cov")
    #   MASS:
    #       assetsMeanCov(DJ, "mve")
    #       assetsMeanCov(DJ, "mcd")
    #   require(robustbase)
    #       assetsMeanCov(DJ, "MCD")
    #       assetsMeanCov(DJ, "OGK")
    #   require(covRobust)
    #       assetsMeanCov(DJ, "nnve")
    
    # FUNCTION:
    
    # Transform Input:
    x.mat <- as.matrix(x)
    
    # Do not use: method = match.arg(method)
    method <- method[1]
    N <- ncol(x)
    assetNames <- colnames(x.mat)
    
    # Attribute Control List:
    control <- c(method = method[1])
    user <- TRUE
    
    # Compute Classical Covariance:
    if (method == "cov") {
      # Classical Covariance Estimation:
      ans = list(center = colMeans(x.mat), cov = cov(x.mat))
      user = FALSE
    }
    
    # From R Package "robustbase":
    if (method == "MCD" | method == "Mcd") {
      ans <- robustbase::covMcd(x.mat, alpha = alpha, ...)
      mu = ans$center
      Sigma = ans$cov
      user = FALSE
    }   
    if (method == "OGK" | method == "Ogk") {
      ans <- robustbase::covOGK(x.mat, sigmamu = sigmamu, ...)
      user = FALSE
    }
    
    # [MASS] mve and mcd Routines:
    if (method == "mve") {
      ans = MASS::cov.rob(x = x.mat, method = "mve", ...)
      user = FALSE
    }
    if (method == "mcd") {
      ans = MASS::cov.rob(x = x.mat, method = "mcd", ...) 
      user = FALSE
    }    
    
    # [corpcor] Shrinkage and Bagging Routines 
    if (method == "shrink") {
      fit = .cov.shrink(x = x.mat, ...)
      ans = list(center = colMeans(x.mat), cov = fit)
      user = FALSE
    } 
    if (method == "bagged") {
      fit = .cov.bagged(x = x.mat, R = baggedR, ...)
      ans = list(center = colMeans(x.mat), cov = fit)
      control = c(control, R = as.character(baggedR))
      user = FALSE
    }
    
    # Nearest Neighbour Variance Estimation:
    if (method == "nnve") {
      fit = .cov.nnve(datamat = x.mat, ...)
      ans = list(center = colMeans(x.mat), cov = fit$cov)
      user = FALSE
    }
    
    # User specified estimator:
    if(user) {
      fun = match.fun(method[1])
      ans = fun(x.mat, ...)
    }
    
    # Result:
    mu = center = ans$center
    Sigma = cov = ans$cov
    
    # Add Size to Control List:
    control = c(control, size = as.character(N))
    
    # Add Names for Covariance Matrix to Control List:
    names(mu) = assetNames
    colnames(Sigma) = rownames(Sigma) = colNames = assetNames
    
    # Check Positive Definiteness:
    if (check) {
      result = isPositiveDefinite(Sigma)
      if(result) {
        control = c(control, posdef = "TRUE")
      } else {
        control = c(control, posdef = "FALSE")
      }
    }
    
    # Check Positive Definiteness:
    control = c(control, forced = "FALSE")
    if (force) {
      control = c(control, forced = "TRUE")
      if (!result) Sigma = makePositiveDefinite(Sigma)       
    }
    
    # Result:
    ans = list(center = mu, cov = Sigma, mu = mu, Sigma = Sigma)
    attr(ans, "control") = control
    
    # Return Value:
    ans
  }


################################################################################


.covMeanCov <- 
  function(x, ...)
  {
    # Description:
    #   Uses sample covariance estimation
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans = list(center = colMeans(x.mat), cov = cov(x.mat))
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames 
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.mveMeanCov <- 
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans <- MASS::cov.rob(x = x.mat, method = "mve")
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.mcdMeanCov <- 
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans <- MASS::cov.rob(x = x.mat, method = "mcd") 
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.studentMeanCov <-
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans <- MASS::cov.trob(x, ...)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:  
    ans
  }


# ------------------------------------------------------------------------------


.MCDMeanCov <- 
  function(x, alpha = 1/2, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans <- robustbase::covMcd(x.mat, alpha = alpha, ...)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.OGKMeanCov <- 
  function(x, sigmamu = scaleTau2, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans <- robustbase::covOGK(x.mat, sigmamu = sigmamu, ...)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans    
  }


# ------------------------------------------------------------------------------


.nnveMeanCov <- 
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    fit = .cov.nnve(datamat = x.mat, ...)
    
    ans = list(center = colMeans(x.mat), cov = fit$cov) 
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.shrinkMeanCov <- 
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Note:                                              
    #   Based on a function borrowed from package corpcor
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    fit = .cov.shrink(x = x.mat, ...)
    
    ans = list(center = colMeans(x.mat), cov = fit)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.baggedMeanCov <- 
  function(x, baggedR = 100, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Note:                                                
    #   Based on a function borrowed from package corpcor
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    fit = .cov.bagged(x = x.mat, R = baggedR, ...)
    
    ans = list(center = colMeans(x.mat), cov = fit)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.arwMeanCov <- 
  function(x, ...)
  {
    # Description:
    #   Adaptive reweighted estimator for multivariate location and scatter
    #   with hard-rejection weights and delta = chi2inv(1-d,p)
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Note:
    #   Based on a function borrowed from package mvoutlier
    
    # FUNCTION:
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    fit = .cov.arw(x = x.mat, center = colMeans(x.mat), cov = cov(x),, ...)
    
    ans = list(center = fit$center, cov = fit$cov)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.donostahMeanCov <- 
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Note:
    #   Based on a function borrowed from package robust
    
    # Settings:
    x.mat = as.matrix(x)
    N = ncol(x)
    assetNames = colnames(x)
    
    ans = .cov.donostah(x = x.mat)
    names(ans$center) = assetNames
    rownames(ans$cov) = colnames(ans$cov) = assetNames
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.bayesSteinMeanCov <- 
  function(x, ...)
  {
    # Description:
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Note:
    #   Based on a function written by Alexios Ghalanos
    
    # Bayes Stein estimator
    # Alexios Ghalanos 2008
    # alexios at 4dscape.com
    # This function encapsulates an example of shrinking the returns 
    #   and covariance using Bayes-Stein shrinkage as described in 
    #   Jorion, 1986.
    
    # Settings:
    data <- getDataPart(x)
    mu <- as.matrix(apply(data,2, FUN = function(x) mean(x)))
    S <- cov(data)
    k <- dim(data)[2]
    n <- dim(data)[1]
    one <- as.matrix(rep(1, k))
    a <- solve(S, one)
    
    # Constant non informative prior
    mu.prior <- one * as.numeric(t(mu) %*% a/t(one) %*% a)
    S.inv <- solve(S)
    d <- t(mu-mu.prior) %*% S.inv %*% (mu-mu.prior)
    d <- as.numeric(d)
    lambda <- (k+2) / d
    w <- lambda / (n+lambda)
    mu.pred <- (1-w) * mu + w * mu.prior
    
    wc1 <- 1 / (n+lambda)
    wc2 <- lambda*(n-1) / (n*(n+lambda)*(n-k-2))
    wc2 <- wc2 / as.numeric(t(one) %*% a)
    V.post <- wc1 * S + wc2 * one %*% t(one)
    V.pred <- S + V.post
    sigma.post <- sqrt(diag(V.post))
    sigma.pred <- sqrt(diag(V.pred))
    
    result <- list(
      mu = mu, mu.prior = mu.prior, mu.predict = mu.pred, 
      V = S, V.post = V.post, V.pred = V.pred, Sigma = sqrt(diag(S)), 
      Sigma.post = sigma.post, Sigma.predict = sigma.pred)
    
    ans = list(center = result$mu.pred[,1], cov = result$V.pred)
    names(ans$center) = colnames(x)
    rownames(ans$cov) = colnames(ans$cov) = colnames(x)
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.ledoitWolfMeanCov <-
  function(x, ...)
  {
    # Description:
    #   Perform shrinkage on a sample covariance towards a biased covariance
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Details:
    #   This performs a covariance shrinkage estimation as specified in 
    #   Ledoit and Wolf. Using within the larger framework only requires 
    #   using the getCorFilter.Shrinkage function, which handles the work 
    #   of constructing a shrinkage estimate of the covariance matrix of 
    #   returns (and consequently its corresponding correlation matrix).
    
    # Note:
    #   Based on a function borrowed from package tawny
    #   Author: Brian Lee Yung Rowe
    
    # Settings:
    data = getDataPart(x)
    center = colMeans(data)
    cov = .cov.shrink.tawny(data, ...)
    
    ans = list(center = center, cov = cov)
    names(ans$center) = colnames(x)
    rownames(ans$cov) = colnames(ans$cov) = colnames(x)
    
    # Return Value:
    ans
  }


# ------------------------------------------------------------------------------


.rmtMeanCov <-
  function(x, ...)
  {
    # Description:
    #   Perform Random Matrix Theory on correlation matrix
    
    # Arguments:
    #   x - an object of class timeSeries
    
    # Author: 
    #   tawnyBrian Lee Yung Rowe
    
    # Note:
    #   Based on a function borrowed from package tawny
    #   Author: Brian Lee Yung Rowe
    
    # FUNCTION:
    
    # Settings:
    data = getDataPart(x)
    center = colMeans(data)
    cor = .filter.RMT(data, trace = FALSE, doplot = FALSE)
    
    g = colSds(data)
    N = length(g)
    cov = 0*cor
    for (i in 1:N)
      for (j in i:N)
        cov[i,j] = cov[j,i] = g[i]*cor[i,j]*g[j]
    
    ans = list(center = center, cov = cov)
    names(ans$center) = colnames(x)
    rownames(ans$cov) = colnames(ans$cov) = colnames(x)
    
    # Return Value:
    ans
  }


################################################################################


getCenterRob <-
  function(object)
  {
    # Return Value:
    object$center
  }


# ------------------------------------------------------------------------------


getCovRob <-
  function(object)
  {
    # Return Value:
    object$cov
  }


################################################################################

