R/NumOutputs.R

Defines functions IdxAllSpanned ForwardPremia TermPremia Compute_EP rhoParas ExpectedComponent MatAdjusted YieldsFitAll ComputeGFEVDs ComputeFEVDs ComputeGIRFs ComputeIRFs BUnspannedAdapSep BUnspannedAdapJoint Y_ModImp Y_Fit TermPremiaDecomp FEVDandGFEVD IRFandGIRF YieldsFit VarianceExplained OutputConstruction NumOutputs

Documented in BUnspannedAdapJoint BUnspannedAdapSep Compute_EP ComputeFEVDs ComputeGFEVDs ComputeGIRFs ComputeIRFs ExpectedComponent FEVDandGFEVD ForwardPremia IdxAllSpanned IRFandGIRF MatAdjusted NumOutputs OutputConstruction rhoParas TermPremia TermPremiaDecomp VarianceExplained Y_Fit YieldsFit YieldsFitAll Y_ModImp

#' Constructs the model numerical outputs (model fit, IRFs, GIRFs, FEVDs, GFEVDs, and term premia)
#'
#' @param ModelType character. Model type to be estimated. Permissible choices: "JPS original", "JPS global", "GVAR single", "JPS multi", "GVAR multi", "JLL original", "JLL No DomUnit", "JLL joint Sigma".
#' @param ModelPara list. Point estimates of the model parameters. See outputs from \code{\link{Optimization}}
#' @param InputsForOutputs list. Inputs for generating IRFs, GIRFs, FEVDs, GFEVDs, and Term Premia.
#' @param FactorLabels list. Labels for all variables present in the model, as returned by \code{\link{LabFac}}.
#' @param Economies character vector. Names of the \code{C} economies included in the system.
#' @param Folder2save Folder path where the outputs will be stored. Default option saves the outputs in a temporary directory.
#' @param verbose Logical flag controlling function messaging. Default is TRUE.
#'
#' @examples
#' data("ParaSetEx")
#' data("InpForOutEx")
#' # Adjust inputs according to the loaded features
#' ModelType <- "JPS original"
#' Economy <- "Brazil"
#' FacLab <- LabFac(N = 1, DomVar = "Eco_Act", GlobalVar = "Gl_Eco_Act", Economy, ModelType)
#'
#' NumOut <- NumOutputs(ModelType, ParaSetEx, InpForOutEx, FacLab, Economy,
#'   Folder2save = NULL, verbose = FALSE
#' )
#'
#' @section Available methods:
#' - `autoplot(object, type)`
#'
#' @returns
#' An object of class 'ATSMNumOutputs' containing the following keys elements:
#' \enumerate{
#' \item Model parameter estimates
#' \item Model fit of bond yields
#' \item IRFs
#' \item FEVDs
#' \item GIRFs
#' \item GFEVDs
#' \item Bond yield decomposition
#' }
#'
#' @details
#' Both IRFs and FEVDs are computed using the Cholesky decomposition method. The risk factors are ordered as follows: (i) global unspanned factors, and (ii) domestic unspanned and spanned factors for each country. The order of countries follows the sequence defined in the \code{Economies} vector.
#'
#' @references
#' Pesaran, H. Hashem, and Shin, Yongcheol. "Generalized impulse response analysis in linear multivariate models." Economics letters 58.1 (1998): 17-29.
#' @export

NumOutputs <- function(ModelType, ModelPara, InputsForOutputs, FactorLabels, Economies, Folder2save = NULL,
                       verbose = TRUE) {
  if (verbose) message("2.2) Computing numerical outputs")

  AllNumOutputs <- list()
  AllNumOutputs <- OutputConstruction(ModelType, ModelPara, InputsForOutputs, FactorLabels, Economies, verbose)

  # Save relevant numerical outputs
  PEoutputs <- list(ModelPara, AllNumOutputs)
  names(PEoutputs) <- c("Model Parameters", "Numerical Outputs")

  FolderPath <- if (is.null(Folder2save)) tempdir() else Folder2save
  saveRDS(PEoutputs, paste(FolderPath, "/PEoutputs_", InputsForOutputs$"Label Outputs", ".rds", sep = ""))

  # Generate graphs, if previously selected
  GraphicalOutputs(
    ModelType, ModelPara, AllNumOutputs, InputsForOutputs, Economies, FactorLabels,
    FolderPath, verbose
  )

  # Create model output object
  attr(AllNumOutputs, "NumOuts") <- list(
    ModelType = ModelType,
    ModelPara = ModelPara,
    NumOut = AllNumOutputs,
    Inputs = InputsForOutputs,
    Economies = Economies,
    FactorLabels = FactorLabels
  )

  # Return the structured Outputs object
  return(structure(AllNumOutputs, class = "ATSMNumOutputs"))
}

######################################################################################################
######################################################################################################
#' Numerical outputs (variance explained, model fit, IRFs, GIRFs, FEVDs, GFEVDs, and risk premia decomposition)
#' for all models
#'
#' @param ModelType string-vector containing the label of the model to be estimated
#' @param ModelPara list of model parameter estimates (See the "Optimization" function)
#' @param InputsForOutputs list containing the desired horizon of analysis for the model fit, IRFs, GIRFs, FEVDs, GFEVDs,
#'                        and risk premia decomposition
#' @param FactorLabels string-list based which contains all the labels of all the variables present in the model
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#' @param verbose Logical flag controlling function messaging. Default is TRUE.
#'
#' @keywords internal

OutputConstruction <- function(ModelType, ModelPara, InputsForOutputs, FactorLabels, Economies, verbose = TRUE) {
  # Output summary
  # Total Variance Explained and Model fit
  if (verbose) message(" ** Total Variance explained")
  Total_Var_exp <- VarianceExplained(ModelType, ModelPara, FactorLabels, Economies)

  if (verbose) message(" ** Model Fit")
  ModFit <- YieldsFit(ModelType, ModelPara, FactorLabels, Economies)

  # IRF and GIRF
  if (verbose) message(" ** IRFs and GIRFs")
  IRFout <- IRFandGIRF(ModelType, ModelPara, InputsForOutputs[[ModelType]]$IRF$horiz, FactorLabels, Economies)

  # FEVD and GFEVD
  if (verbose) message(" ** FEVDs and GFEVDs")
  FEVDout <- FEVDandGFEVD(ModelType, ModelPara, InputsForOutputs[[ModelType]]$FEVD$horiz, FactorLabels, Economies)

  # Risk Premia Decomposition
  if (verbose) message(" ** Term Premia")
  TermPremia <- TermPremiaDecomp(ModelPara, FactorLabels, ModelType, InputsForOutputs, Economies)


  NumericalOutputs <- list(Total_Var_exp, ModFit, IRFout$IRFs, FEVDout$FEVDs, IRFout$GIRFs, FEVDout$GFEVDs, TermPremia)
  names(NumericalOutputs) <- c("PC var explained", "Fit", "IRF", "FEVD", "GIRF", "GFEVD", "TermPremiaDecomp")

  return(NumericalOutputs)
}

######################################################################################################
####################### 1) Total Variance Explained #########################################
######################################################################################################
#' Percentage explained by the spanned factors of the variations in the set of observed yields for all models
#'
#' @param ModelType string-vector containing the label of the model to be estimated
#' @param ModelPara List of model parameter estimates (see the "Optimization" function)
#' @param FactorLabels string-list based which contains all the labels of all the variables present in the model
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#'
#' @keywords internal

VarianceExplained <- function(ModelType, ModelPara, FactorLabels, Economies) {
  N <- length(FactorLabels$Spanned)
  Total_Var_exp <- list()

  # Models estimated individually
  if (any(ModelType == c("JPS original", "JPS global", "GVAR single"))) {
    for (i in 1:length(Economies)) {
      H <- eigen(stats::cov(t(ModelPara[[ModelType]][[Economies[i]]]$Inputs$Y)))$values
      percentages_explained <- cumsum(H) / sum(H)
      Total_Var_exp[[i]] <- percentages_explained[1:N]
    }
  } else {
    # Models estimated jointly
    J <- length(ModelPara[[ModelType]]$Inputs$mat)
    idx0 <- 0
    for (i in 1:length(Economies)) {
      idx1 <- idx0 + J
      H <- eigen(stats::cov(t(ModelPara[[ModelType]]$Inputs$Y[(idx0 + 1):idx1, ])))$values
      percentages_explained <- cumsum(H) / sum(H)
      Total_Var_exp[[i]] <- percentages_explained[1:N]
      idx0 <- idx1
    }
  }
  names(Total_Var_exp) <- Economies

  return(Total_Var_exp)
}

######################################################################################################
########################################### 2) Model Fit #############################################
######################################################################################################
#' Computes two measures of model fit for bond yields (all models)
#'
#' @param ModelType a string-vector containing the label of the model to be estimated
#' @param ModelPara List of model parameter estimates (See the "Optimization" function)
#' @param FactorLabels a string-list based which contains the labels of all the variables present in the model
#' @param Economies a string-vector containing the names of the economies which are part of the economic system
#'
#' @details
#' "Model-implied yields" is the measure of fit based exclusively on the risk-neutral parameters, whereas the
#' "Model-Fit" takes into account both the risk-neutral and the physical parameters.
#'
#' @references
#' See, for instance, Jotiskhatira, Le and Lundblad (2015). "Why do interest rates in different currencies co-move?" (Journal of Financial Economics)
#'
#' @keywords internal

YieldsFit <- function(ModelType, ModelPara, FactorLabels, Economies) {
  # Extract dimensions
  N <- length(FactorLabels$Spanned)
  G <- length(FactorLabels$Global)
  M <- length(FactorLabels$Domestic) - N
  C <- length(Economies)
  Output <- list()

  # Helper function to compute spanned factors
  get_spanned_factors <- function(Z, LabelSpannedCS, AllLabels) {
    IdxSpanned <- match(LabelSpannedCS, AllLabels)
    return(Z[IdxSpanned, ])
  }

  # Helper function to compute model fit
  compute_model_fit <- function(Afull, Bspanned, P, J, T, YieldData) {
    return(Y_Fit(Afull, Bspanned, P, J, T, dimnames(YieldData)))
  }

  # Helper function to compute model-implied yields
  compute_model_implied_yields <- function(Afull, Bfull, K0Z, K1Z, Z, J, T, YieldData) {
    return(Y_ModImp(Afull, Bfull, K0Z, K1Z, Z, J, T, dimnames(YieldData)))
  }

  # I) Models estimated individually
  if (ModelType %in% c("JPS original", "JPS global", "GVAR single")) {
    mat <- ModelPara[[ModelType]][[Economies[1]]]$Inputs$mat
    J <- length(mat)

    for (i in 1:C) {
      # Extract necessary data
      InfoSet <- ModelPara[[ModelType]][[Economies[i]]]
      YieldData <- InfoSet$Inputs$Y
      Z <- InfoSet$Inputs$AllFactors
      T_dim <- ncol(Z)
      Afull <- InfoSet$ModEst$Q$Load$P$A
      Bspanned <- InfoSet$ModEst$Q$Load$P$B
      K0Z <- InfoSet$ModEst$P$K0Z
      K1Z <- InfoSet$ModEst$P$K1Z

      # Determine AllLabels and LabelSpannedCS
      if (ModelType == "JPS original") {
        AllLabels <- c(FactorLabels$Global, FactorLabels$Tables[[Economies[i]]])
      } else {
        AllLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
      }
      LabelSpannedCS <- c(FactorLabels$Tables[[Economies[i]]][-(1:M)])

      # Get spanned factors
      P <- get_spanned_factors(Z, LabelSpannedCS, AllLabels)

      # Compute model fit and implied yields
      Yieldfit <- compute_model_fit(Afull, Bspanned, P, J, T_dim, YieldData)
      Bfull <- BUnspannedAdapSep(G, M, ModelPara[[ModelType]], Economies, Economies[i], ModelType)
      dimnames(Bfull) <- list(rownames(YieldData), rownames(Z))
      YieldModelImplied <- compute_model_implied_yields(Afull, Bfull, K0Z, K1Z, Z, J, T_dim, YieldData)

      # Store results
      Output[[ModelType]][[Economies[i]]] <- list(
        "Yield Fit" = Yieldfit,
        "Yield Model Implied" = YieldModelImplied
      )
    }
  } else {
    # II) Models estimated jointly
    InfoSet <- ModelPara[[ModelType]]
    Z <- InfoSet$Inputs$AllFactors
    YieldData <- InfoSet$Inputs$Y
    mat <- InfoSet$Inputs$mat
    Afull <- InfoSet$ModEst$Q$Load$P$A
    Bspanned <- InfoSet$ModEst$Q$Load$P$B
    K0Z <- InfoSet$ModEst$P$K0Z
    K1Z <- InfoSet$ModEst$P$K1Z
    J <- length(mat)
    T_dim <- ncol(Z)

    # Compute IdxSpanned
    IdxSpanned <- c()
    idxSpa0 <- G + M
    for (j in 1:C) {
      idxSpa1 <- idxSpa0 + N
      IdxSpanned <- c(IdxSpanned, (idxSpa0 + 1):idxSpa1)
      idxSpa0 <- idxSpa1 + M
    }

    # Get spanned factors
    P <- Z[IdxSpanned, ]

    # Compute model fit and implied yields
    Yieldfit <- compute_model_fit(Afull, Bspanned, P, C * J, T_dim, YieldData)
    Bfull <- BUnspannedAdapJoint(G, M, N, C, J, Bspanned)
    dimnames(Bfull) <- list(rownames(YieldData), rownames(Z))
    YieldModelImplied <- compute_model_implied_yields(Afull, Bfull, K0Z, K1Z, Z, C * J, T_dim, YieldData)

    Output <- list(
      "Yield Fit" = Yieldfit,
      "Yield Model Implied" = YieldModelImplied
    )
  }

  return(Output)
}

######################################################################################################
########################################### 3) IRFs and GIRFs ########################################
######################################################################################################
#' IRFs and GIRFs for all models
#'
#' @param ModelType string-vector containing the label of the model to be estimated
#' @param ModelPara list of model parameter estimates (See the "Optimization" function)
#' @param IRFhoriz single numerical vector containing the desired horizon of analysis for the IRFs
#' @param FactorLabels string-list based which contains the labels of all the variables present in the model
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#'
#' @details
#' The Structural shocks from the IRFs are identified via Cholesky decomposition
#'
#' @keywords internal

IRFandGIRF <- function(ModelType, ModelPara, IRFhoriz, FactorLabels, Economies) {
  # Pre-allocation
  C <- length(Economies)
  G <- length(FactorLabels$Global)
  N <- length(FactorLabels$Spanned)
  M <- length(FactorLabels$Domestic) - N
  IRFoutputs <- list()
  GIRFoutputs <- list()

  # Helper function to compute IRFs and GIRFs for a single country
  compute_single_country <- function(ModelType, Para_Set, Econ, Economies) {
    J <- length(Para_Set[[Econ]]$Inputs$mat)
    K <- nrow(Para_Set[[Econ]]$Inputs$AllFactors)
    YieldsLabel <- rownames(Para_Set[[Econ]]$Inputs$Y) # Yield labels

    # Summarize inputs for the IRFs
    SIGMA <- Para_Set[[Econ]]$ModEst$P$SSZ # KxK (variance-covariance matrix)
    K1Z <- Para_Set[[Econ]]$ModEst$P$K1Z # KxK (feedback matrix)
    B <- BUnspannedAdapSep(G, M, Para_Set, Economies, Econ, ModelType)

    # Compute IRFs
    IRFs <- ComputeIRFs(SIGMA, K1Z, B, FactorLabels, K, J, IRFhoriz, YieldsLabel, ModelType, Econ)
    IRFoutputs[[ModelType]][[Econ]] <- IRFs # Store Country specific IRFs

    # Compute GIRFs
    G0.y <- Para_Set[[Econ]]$ModEst$P$Gy.0
    GIRFs <- ComputeGIRFs(SIGMA, K1Z, B, G0.y, FactorLabels, K, J, IRFhoriz, YieldsLabel, ModelType, Econ)
    GIRFoutputs[[ModelType]][[Econ]] <- GIRFs # Store Country specific GIRFs

    return(list(IRFs = IRFs, GIRFs = GIRFs))
  }

  # Helper function to compute IRFs and GIRFs for joint country models
  compute_joint_country <- function(ModelType, Para_Set, FactorLabels, Economies) {
    JLL_Label <- c("JLL original", "JLL No DomUnit", "JLL joint Sigma")
    J <- length(Para_Set$Inputs$mat)
    K <- nrow(Para_Set$Inputs$AllFactors)
    YieldsLabel <- rownames(Para_Set$Inputs$Y) # Yield labels

    # Summarize inputs for the IRFs
    if (ModelType %in% JLL_Label) {
      SIGMA <- Para_Set$ModEst$P$JLLoutcomes$Sigmas$Sigma_Y # For JLL models, we selected the cholesky factor, which won't be computed inside "ComputeIRFs"
    } else {
      SIGMA <- Para_Set$ModEst$P$SSZ # KxK (variance-covariance matrix)
    }
    K1Z <- Para_Set$ModEst$P$K1Z # KxK (feedback matrix)
    BSpanned <- Para_Set$ModEst$Q$Load$P$B
    B <- BUnspannedAdapJoint(G, M, N, C, J, BSpanned)

    # Compute IRFs
    IRFoutputs[[ModelType]] <- ComputeIRFs(SIGMA, K1Z, B, FactorLabels, K, C * J, IRFhoriz, YieldsLabel, ModelType)

    # Compute GIRFs
    G0.y <- Para_Set$ModEst$P$Gy.0
    if (ModelType %in% JLL_Label) {
      SIGMA <- ModelPara[[ModelType]]$ModEst$P$JLLoutcomes$Sigmas$VarCov_NonOrtho
    }
    GIRFs <- ComputeGIRFs(SIGMA, K1Z, B, G0.y, FactorLabels, K, C * J, IRFhoriz, YieldsLabel, ModelType)
    GIRFoutputs[[ModelType]] <- GIRFs # Store Country specific GIRFs

    # Compute orthogonalized IRFs and GIRFs for JLL-based models
    if (ModelType %in% JLL_Label) {
      K1Ze <- Para_Set$ModEst$P$JLLoutcomes$k1_e # KxK (feedback matrix)
      PI <- Para_Set$ModEst$P$JLLoutcomes$PI
      Se <- Para_Set$ModEst$P$JLLoutcomes$Sigmas$Sigma_Ye
      SIGMA_e <- Para_Set$ModEst$P$JLLoutcomes$Sigmas$VarCov_Ortho

      # Compute IRFs orthogonalized
      IRFOrtho <- list()
      IRFOrtho[[ModelType]] <- ComputeIRFs(Se, K1Ze, B, FactorLabels, K, C * J, IRFhoriz, YieldsLabel, ModelType,
        PI = PI, Mode = "Ortho"
      )

      # Gather Outputs
      IRFoutputs[[ModelType]]$Factors <- list(NonOrtho = IRFoutputs[[ModelType]]$Factors, Ortho = IRFOrtho[[ModelType]]$Factors)
      IRFoutputs[[ModelType]]$Yields <- list(NonOrtho = IRFoutputs[[ModelType]]$Yields, Ortho = IRFOrtho[[ModelType]]$Yields)

      # Compute GIRFs orthogonalized
      GIRFsOrtho <- list()
      GIRFsOrtho[[ModelType]] <- ComputeGIRFs(SIGMA_e, K1Ze, B, G0.y, FactorLabels, K, C * J, IRFhoriz, YieldsLabel,
        ModelType,
        PI = PI, Mode = "Ortho"
      )

      # Gather Outputs
      GIRFoutputs[[ModelType]]$Factors <- list(NonOrtho = GIRFoutputs[[ModelType]]$Factors, Ortho = GIRFsOrtho[[ModelType]]$Factors)
      GIRFoutputs[[ModelType]]$Yields <- list(NonOrtho = GIRFoutputs[[ModelType]]$Yields, Ortho = GIRFsOrtho[[ModelType]]$Yields)
    }

    return(list(IRFoutputs = IRFoutputs, GIRFoutputs = GIRFoutputs))
  }


  # 1) SINGLE COUNTRY MODELS
  Para_Set <- ModelPara[[ModelType]]
  if (ModelType %in% c("JPS original", "JPS global", "GVAR single")) {
    for (i in 1:C) {
      IRFset_CS <- compute_single_country(ModelType, Para_Set, Economies[i], Economies)
      IRFoutputs[[ModelType]][[Economies[i]]] <- IRFset_CS$IRFs
      GIRFoutputs[[ModelType]][[Economies[i]]] <- IRFset_CS$GIRFs
    }
  } else {
    # 2) JOINT COUNTRY MODELS
    IRFset <- compute_joint_country(ModelType, Para_Set, FactorLabels, Economies)
    IRFoutputs <- IRFset$IRFoutputs
    GIRFoutputs <- IRFset$GIRFoutputs
  }

  Out <- list(IRFs = IRFoutputs, GIRFs = GIRFoutputs)

  return(Out)
}

#########################################################################################################
################################### 4) FEVD and GFEVD ###################################################
#########################################################################################################
#' FEVDs and GFEVDs for all models
#'
#' @param ModelType string-vector containing the label of the model to be estimated
#' @param ModelPara list of model parameter estimates (see the "Optimization" function)
#' @param FEVDhoriz single numerical vector containing the desired horizon of analysis for the FEVDs and GFEVDs
#' @param FactorLabels string-list based which contains all the labels of all the variables present in the model
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#'
#' @details
#' Structural shocks are identified via Cholesky decomposition
#'
#' @keywords internal

FEVDandGFEVD <- function(ModelType, ModelPara, FEVDhoriz, FactorLabels, Economies) {
  # Pre-allocation
  C <- length(Economies)
  G <- length(FactorLabels$Global)
  N <- length(FactorLabels$Spanned)
  M <- length(FactorLabels$Domestic) - N
  FEVDoutputs <- list()
  GFEVDoutputs <- list()

  # Helper function to compute FEVDs and GFEVDs for a single country
  compute_single_country <- function(ModelType, Para_Set, Econ, Economies) {
    K <- nrow(Para_Set[[Econ]]$Inputs$AllFactors)
    J <- length(Para_Set[[Econ]]$Inputs$mat)
    YieldsLabel <- rownames(Para_Set[[Econ]]$Inputs$Y) # Yield labels

    # Summarize inputs for the FEVDs
    SIGMA <- Para_Set[[Econ]]$ModEst$P$SSZ
    K1Z <- Para_Set[[Econ]]$ModEst$P$K1Z
    G0 <- Para_Set[[Econ]]$ModEst$P$Gy.0
    B <- BUnspannedAdapSep(G, M, Para_Set, Economies, Econ, ModelType)

    # Compute FEVDs
    FEVDs <- ComputeFEVDs(SIGMA, K1Z, G0, B, FactorLabels, K, J, FEVDhoriz, YieldsLabel, ModelType, Econ)

    # Compute GFEVDs
    GFEVDs <- ComputeGFEVDs(SIGMA, K1Z, G0, B, FactorLabels, K, J, FEVDhoriz, YieldsLabel, ModelType, Econ)

    # Return results
    return(list(FEVDs = FEVDs, GFEVDs = GFEVDs))
  }

  # Helper function to compute FEVDs and GFEVDs for joint country models
  compute_joint_country <- function(ModelType, ParaSet, FactorLabels, Economies) {
    J <- length(ParaSet$Inputs$mat)
    K <- nrow(ParaSet$Inputs$AllFactors)
    YieldsLabel <- rownames(ParaSet$Inputs$Y) # Yield labels

    # Summarize inputs for the FEVD
    if (any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
      Chol_Fac_JLL <- ParaSet$ModEst$P$JLLoutcomes$Sigmas$Sigma_Y
    }

    K1Z <- ParaSet$ModEst$P$K1Z # KxK (feedback matrix)
    SIGMA <- ParaSet$ModEst$P$SSZ # KxK (variance-covariance matrix)
    BSpanned <- ParaSet$ModEst$Q$Load$P$B
    B <- BUnspannedAdapJoint(G, M, N, C, J, BSpanned)
    G0 <- ParaSet$ModEst$P$Gy.0

    # Compute FEVD
    FEVDoutputs[[ModelType]] <- ComputeFEVDs(SIGMA, K1Z, G0, B, FactorLabels, K, C * J, FEVDhoriz, YieldsLabel,
      ModelType,
      CholFac_JLL = Chol_Fac_JLL
    )

    # Compute GFEVD
    GFEVDoutputs[[ModelType]] <- ComputeGFEVDs(
      SIGMA, K1Z, G0, B, FactorLabels, K, C * J, FEVDhoriz, YieldsLabel,
      ModelType
    )

    # Compute orthogonalized FEVDs and GFEVDs for JLL-based models
    if (any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
      K1Ze <- ParaSet$ModEst$P$JLLoutcomes$k1_e # KxK (feedback matrix)
      PI <- ParaSet$ModEst$P$JLLoutcomes$PI
      SIGMA_Ortho <- ParaSet$ModEst$P$JLLoutcomes$Sigmas$VarCov_Ortho
      Chol_Fac_JLL_Ortho <- ParaSet$ModEst$P$JLLoutcomes$Sigmas$Sigma_Ye

      # Compute FEVDs orthogonalized
      FEVDOrtho <- list()
      FEVDOrtho[[ModelType]] <- ComputeFEVDs(SIGMA_Ortho, K1Ze, G0, B, FactorLabels, K, C * J, FEVDhoriz, YieldsLabel,
        ModelType,
        CholFac_JLL = Chol_Fac_JLL_Ortho, PI = PI, Mode = "Ortho"
      )

      # Gather Outputs
      FEVDoutputs[[ModelType]]$Factors <- list(NonOrtho = FEVDoutputs[[ModelType]]$Factors, Ortho = FEVDOrtho[[ModelType]]$Factors)
      FEVDoutputs[[ModelType]]$Yields <- list(NonOrtho = FEVDoutputs[[ModelType]]$Yields, Ortho = FEVDOrtho[[ModelType]]$Yields)

      # Compute GFEVDs orthogonalized
      GFEVDsOrtho <- list()
      GFEVDsOrtho[[ModelType]] <- ComputeGFEVDs(SIGMA_Ortho, K1Ze, G0, B, FactorLabels, K, C * J, FEVDhoriz, YieldsLabel,
        ModelType,
        PI = PI, Mode = "Ortho"
      )

      # Gather Outputs
      GFEVDoutputs[[ModelType]]$Factors <- list(NonOrtho = GFEVDoutputs[[ModelType]]$Factors, Ortho = GFEVDsOrtho[[ModelType]]$Factors)
      GFEVDoutputs[[ModelType]]$Yields <- list(NonOrtho = GFEVDoutputs[[ModelType]]$Yields, Ortho = GFEVDsOrtho[[ModelType]]$Yields)
    }

    # Return results
    return(list(FEVDoutputs = FEVDoutputs, GFEVDoutputs = GFEVDoutputs))
  }

  # 1) SINGLE COUNTRY MODELS
  Para_Set <- ModelPara[[ModelType]]
  if (any(ModelType == c("JPS original", "JPS global", "GVAR single"))) {
    for (i in 1:C) {
      FEVDset_CS <- compute_single_country(ModelType, Para_Set, Economies[i], Economies)
      FEVDoutputs[[ModelType]][[Economies[i]]] <- FEVDset_CS$FEVDs
      GFEVDoutputs[[ModelType]][[Economies[i]]] <- FEVDset_CS$GFEVDs
    }
  } else {
    # 2) JOINT COUNTRY MODELS
    FEVDset <- compute_joint_country(ModelType, Para_Set, FactorLabels, Economies)
    FEVDoutputs <- FEVDset$FEVDoutputs
    GFEVDoutputs <- FEVDset$GFEVDoutputs
  }

  Out <- list(FEVDs = FEVDoutputs, GFEVDs = GFEVDoutputs)
  return(Out)
}

######################################################################################################
####################### 5) Risk Premia Decomposition #################################################
######################################################################################################
#' Decomposition of yields into the average of expected future short-term interest rate and risk premia for all models
#'
#' @param ModelPara list of model parameter estimates (see the "Optimization" function)
#' @param FactorLabels  string-list based which contains all the labels of all the variables present in the model
#' @param ModelType string-vector containing the label of the model to be estimated
#' @param InputsForOutputs list containing the desired horizon of analysis for the model fit, IRFs, GIRFs, FEVDs, GFEVDs,
#'                        and risk premia decomposition
#' @param Economies  string-vector containing the names of the economies which are part of the economic system
#'
#'
#'
#' @keywords internal

TermPremiaDecomp <- function(ModelPara, FactorLabels, ModelType, InputsForOutputs, Economies) {
  WishFP <- InputsForOutputs$ForwardPremia

  # 1) Compute the country-specific expected components
  EP_list <- ExpectedComponent(ModelPara, InputsForOutputs, ModelType, Economies, FactorLabels, WishFP)

  # 2) Compute Term Premium
  OutputTP <- TermPremia(ModelPara, EP_list$avexp, ModelType, Economies)

  # 3) Forward Premia (if desired)
  if (WishFP) {
    OutputFP <- ForwardPremia(ModelPara, EP_list$avexpFP, ModelType, FactorLabels, InputsForOutputs, Economies)
  } else {
    OutputFP <- NA
  }

  Output <- list(RiskPremia = OutputTP, ForwardPremia = OutputFP)

  return(Output)
}

######################################################################################################
######################################## AUXILIARY FUNCTIONS #########################################
######################################################################################################
#' Model-implied yields (cross-section)
#'
#' @param ALoad  A loadings
#' @param BLoad B loadings
#' @param Spa_TS time series of spanned factors
#' @param MatLength length of the vector of maturities
#' @param TDim Time-series dimension
#' @param YieldLab Label of yields
#'
#' @keywords internal

Y_Fit <- function(ALoad, BLoad, Spa_TS, MatLength, TDim, YieldLab) {
  Yieldfit <- matrix(NA, nrow = MatLength, ncol = TDim)
  if (is.null(dim(Spa_TS))) {
    for (h in 1:TDim) {
      Yieldfit[, h] <- ALoad + BLoad %*% Spa_TS[h]
    }
  } else {
    for (h in 1:TDim) {
      Yieldfit[, h] <- ALoad + BLoad %*% Spa_TS[, h]
    }
  }
  dimnames(Yieldfit) <- YieldLab

  return(Yieldfit)
}

##############################################################
#' Model-implied yields (P-dynamics)
#'
#' @param ALoad  A loadings
#' @param BLoad B loadings
#' @param K0Z intercept from the P-dynamics
#' @param K1Z feedback matrix from the P-dynamics
#' @param PdynFact time series of the risk-factors spanned factors
#' @param MatLength length of the vector of maturities
#' @param TDim Time-series dimension
#' @param YieldLab Label of yields
#'
#'
#' @keywords internal

Y_ModImp <- function(ALoad, BLoad, K0Z, K1Z, PdynFact, MatLength, TDim, YieldLab) {
  YieldModelImplied <- matrix(NA, nrow = MatLength, ncol = TDim)
  for (h in 2:TDim) { #  first observation is discarded
    YieldModelImplied[, h] <- ALoad + BLoad %*% (K0Z + K1Z %*% PdynFact[, h - 1])
  }
  dimnames(YieldModelImplied) <- YieldLab

  return(YieldModelImplied)
}

#####################################################################################################
#' Transform B_spanned into B_unspanned for jointQ models
#'
#' @param G number of global unspanned factors
#' @param M number of domestic unspanned factors
#' @param N number of domestic spanned factors
#' @param C number of economies of the economic system
#' @param J number of country-specific observed bond yields
#' @param BSpanned B that accomodates only the map to the spanned factors only
#'
#' @keywords internal

BUnspannedAdapJoint <- function(G, M, N, C, J, BSpanned) {
  K <- C * (N + M) + G
  CJ <- C * J

  BUnspanned <- matrix(0, nrow = CJ, ncol = K)

  idxA <- 0
  idxB <- G + M
  idxC <- 0

  for (i in 1:C) {
    idxAA <- idxA + J
    idxBB <- idxB + N
    idxCC <- idxC + N
    BUnspanned[(idxA + 1):idxAA, (idxB + 1):idxBB] <- BSpanned[(idxA + 1):idxAA, (idxC + 1):idxCC]
    idxA <- idxAA
    idxB <- idxBB + M
    idxC <- idxCC
  }

  return(BUnspanned)
}

#################################################################################################
#' Transform B_spanned into B_unspanned for sepQ models
#'
#' @param G number of global unspanned factors
#' @param M number of domestic unspanned factors per country
#' @param ModelPara_Short list of model parameter estimates (See the "Optimization" function)
#' @param Economies complet set of economies of the economic system
#' @param Economy  specific economy under study
#' @param ModelType a string-vector containing the label of the model to be estimated
#'
#' @keywords internal

BUnspannedAdapSep <- function(G, M, ModelPara_Short, Economies, Economy, ModelType) {
  i <- match(Economy, Economies)
  C <- length(Economies)
  N <- ModelPara_Short[[Economy]]$Inputs$N
  J <- length(ModelPara_Short[[Economy]]$Inputs$mat)


  if (ModelType == "JPS original") {
    K <- N + M + G
    BUnspanned <- matrix(0, nrow = J, ncol = K)
    BSpanned <- ModelPara_Short[[Economies[i]]]$ModEst$Q$Load$P$B
    BUnspanned[, (K - N + 1):K] <- BSpanned
  } else if (ModelType %in% c("JPS global", "GVAR single")) {
    K <- C * (N + M) + G
    BUnspanned <- matrix(0, nrow = J, ncol = K)
    BSpanned <- ModelPara_Short[[Economies[i]]]$ModEst$Q$Load$P$B

    IDX <- list()
    idx0 <- G + M
    for (h in 1:C) {
      idx1 <- idx0 + N
      IDX[[h]] <- (idx0 + 1):idx1
      idx0 <- idx1 + M
    }

    BUnspanned[, IDX[[i]]] <- BSpanned
  }

  return(BUnspanned)
}
#####################################################################################################
#' Compute IRFs of all models
#'
#' @param SIGMA Variance-covariance matrix
#' @param K1Z Loading As
#' @param BLoad Loading Bs
#' @param FactorLabels List containing the label of factors
#' @param FacDim Dimension of the P-dynamics
#' @param MatLength Length of the maturity vector
#' @param IRFhoriz Horizon of the analysis
#' @param YieldsLabel Label of bond yields
#' @param ModelType Desired model type
#' @param Economy specific economy under study
#' @param PI matrix PI for JLL-based models
#' @param Mode allows for the orthogonalized version in the case of JLL-based models
#'
#' @keywords internal

ComputeIRFs <- function(SIGMA, K1Z, BLoad, FactorLabels, FacDim, MatLength, IRFhoriz, YieldsLabel, ModelType,
                        Economy = NULL, PI = NULL, Mode = FALSE) {
  # 1) Initialization of IRFs of interest
  tempFactors <- array(0, c(FacDim, FacDim, IRFhoriz))
  tempYields <- array(0, c(MatLength, FacDim, IRFhoriz))


  # 2) Compute the IRFs
  if (Mode == "Ortho" & any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
    AdjTerm <- PI
  } else {
    AdjTerm <- diag(FacDim)
  }

  # Choleski term
  if (any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
    S <- SIGMA
  } else {
    S <- t(chol(SIGMA))
  }

  # Shock at t=0:
  tempFactors[, , 1] <- S
  tempYields[, , 1] <- BLoad %*% AdjTerm %*% S
  # Shock at t=1:
  for (r in 2:IRFhoriz) {
    if (r == 2) {
      A1h <- K1Z
    } else {
      A1h <- A1h %*% K1Z
    }
    tempFactors[, , r] <- A1h %*% S # IRF (t+h) = A1^h*S
    tempYields[, , r] <- BLoad %*% AdjTerm %*% A1h %*% S
  }
  IRFRiskFactors <- aperm(tempFactors, c(3, 1, 2))
  IRFYields <- aperm(tempYields, c(3, 1, 2))

  Horiz <- t(t(0:(IRFhoriz - 1))) # Add a column for horizon of interest

  # 3) Adjust the variable labels
  # Factor Labels
  if (ModelType == "JPS original") {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables[[Economy]])
  } else if (any(ModelType == c("JPS global", "GVAR single"))) {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  } else {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  }

  dimnames(IRFRiskFactors) <- list(Horiz, AllFactorsLabels, AllFactorsLabels)
  dimnames(IRFYields) <- list(Horiz, YieldsLabel, AllFactorsLabels)

  Out <- list(Factors = IRFRiskFactors, Yields = IRFYields)

  return(Out)
}

##########################################################################################################
#' Compute GIRFs for all models
#'
#' @param Sigma.y Variance-covariance matrix
#' @param F1 Feedback matrix
#' @param BLoad Loading Bs
#' @param G0.y matrix of contemporaneous terms
#' @param FactorLabels List containing the labels of the factors
#' @param FacDim Dimension of the P-dynamics
#' @param MatLength Length of the maturity vector
#' @param GIRFhoriz Horizon of the analysis
#' @param YieldsLabel Label o yields
#' @param ModelType desired Model type
#' @param Economy Economy under study
#' @param PI matrix PI for JLL-based models
#' @param Mode allows for the orthogonalized version in the case of JLL-based models
#'
#' #' @references
#' \itemize{
#' \item This function is partially based on the version of the "irf" function from
#' Smith, L.V. and A. Galesi (2014). GVAR Toolbox 2.0, available at https://sites.google.com/site/gvarmodelling/gvar-toolbox.
#'
#' \item Pesaran and Shin, 1998. "Generalized impulse response analysis in linear multivariate models" (Economics Letters)
#' }
#'
#' @keywords internal

ComputeGIRFs <- function(Sigma.y, F1, BLoad, G0.y, FactorLabels, FacDim, MatLength, GIRFhoriz, YieldsLabel,
                         ModelType, Economy = NULL, PI = NULL, Mode = FALSE) {
  # 1) Dynamic multiplier:
  Ry.h <- array(NA, c(FacDim, FacDim, GIRFhoriz))
  Ry.h[, , 1] <- diag(FacDim) # dynamic multiplier at t=0

  for (w in 2:GIRFhoriz) {
    Ry.h[, , w] <- F1 %*% Ry.h[, , w - 1]
  }

  # 2) Build the vector containing the one unit-shock for each variable of the system
  ey.j <- diag(FacDim)

  # 3) GIRFs:
  if (Mode == "Ortho" & any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
    AdjTerm <- PI
  } else {
    AdjTerm <- diag(FacDim)
  }

  # 3.1) Factors
  AllResponsesToAllShocksFactors <- array(NA, c(FacDim, GIRFhoriz, FacDim))
  AllResponsesToShockOfOneVariableFactors <- matrix(NA, ncol = GIRFhoriz, nrow = FacDim)
  for (g in 1:FacDim) {
    for (w in 1:GIRFhoriz) {
      numFactors <- AdjTerm %*% (Ry.h[, , w] %*% solve(G0.y) %*% Sigma.y %*% ey.j[, g]) # numerator from equation at the bottom of the page 22 (PS, 1998)
      demFactors <- 1 / sqrt((t(ey.j[, g]) %*% Sigma.y %*% ey.j[, g])) # denominator from equation at the bottom of the page 22 (PS, 1998)
      AllResponsesToShockOfOneVariableFactors[, w] <- numFactors * drop(demFactors)
    }
    AllResponsesToAllShocksFactors[, , g] <- AllResponsesToShockOfOneVariableFactors
  }

  GIRFFactors <- aperm(AllResponsesToAllShocksFactors, c(2, 1, 3))

  # 3.2) Yields
  AllResponsesToAllShocksYields <- array(NA, c(MatLength, GIRFhoriz, FacDim))
  AllResponsesToShockOfOneVariableYields <- matrix(NA, ncol = GIRFhoriz, nrow = MatLength)
  for (g in 1:FacDim) {
    for (w in 1:GIRFhoriz) {
      numYields <- BLoad %*% AdjTerm %*% (Ry.h[, , w] %*% solve(G0.y) %*% Sigma.y %*% ey.j[, g]) # numerator from equation at the bottom of the page 22 (PS, 1998)
      demYields <- 1 / sqrt((t(ey.j[, g]) %*% Sigma.y %*% ey.j[, g])) # denominator from equation at the bottom of the page 22 (PS, 1998)
      AllResponsesToShockOfOneVariableYields[, w] <- numYields * drop(demYields)
    }
    AllResponsesToAllShocksYields[, , g] <- AllResponsesToShockOfOneVariableYields
  }

  GIRFYields <- aperm(AllResponsesToAllShocksYields, c(2, 1, 3))

  # 4) Prepare labels for the output
  if (ModelType == "JPS original") {
    labelsGIRF <- c(FactorLabels$Global, FactorLabels$Tables[[Economy]])
  } else if (any(ModelType == c("JPS global", "GVAR single"))) {
    labelsGIRF <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  } else {
    labelsGIRF <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  }

  # 4.1) Add columns containing the horizons
  Horiz <- t(t(0:(GIRFhoriz - 1))) # Add a column for horizon of interest

  # 4.2) Labels
  dimnames(GIRFFactors) <- list(Horiz, labelsGIRF, labelsGIRF)
  dimnames(GIRFYields) <- list(Horiz, YieldsLabel, labelsGIRF)

  GIRFoutputs <- list(Factors = GIRFFactors, Yields = GIRFYields)

  return(GIRFoutputs)
}

#######################################################################################################
#' Compute FEVDs for all models
#'
#' @param SIGMA Variance-covariance matrix
#' @param K1Z Loading As
#' @param G0 contemporaneous terms
#' @param BLoad Loading Bs
#' @param FactorLabels List containing the label of factors
#' @param FacDim Dimension of the P-dynamics
#' @param MatLength Length of the maturity vector
#' @param FEVDhoriz Horizon of the analysis
#' @param YieldsLabel Label of bond yields
#' @param ModelType Desired model type
#' @param Economy specific economy under study
#' @param CholFac_JLL Cholesky factorization term from JLL models
#' @param PI matrix PI for JLL-based models
#' @param Mode allows for the orthogonalized version in the case of JLL-based models
#'
#' @references
#' \itemize{
#' \item This function is a modified and extended version of the "fevd" function from
#' Smith, L.V. and A. Galesi (2014). GVAR Toolbox 2.0, available at https://sites.google.com/site/gvarmodelling/gvar-toolbox.
#'
#' \item Pesaran and Shin, 1998. "Generalized impulse response analysis in linear multivariate models" (Economics Letters)
#' }
#'
#' @keywords internal

ComputeFEVDs <- function(SIGMA, K1Z, G0, BLoad, FactorLabels, FacDim, MatLength, FEVDhoriz, YieldsLabel,
                         ModelType, Economy = NULL, CholFac_JLL = NULL, PI = NULL, Mode = FALSE) {
  # 1) Dynamic multipliers
  Ry.h <- array(NA, c(FacDim, FacDim, FEVDhoriz))
  Ry.h[, , 1] <- diag(FacDim) # dynamic multiplier at t=0

  for (l in 2:FEVDhoriz) {
    Ry.h[, , l] <- K1Z %*% Ry.h[, , l - 1]
  }

  # 2) Initialization
  vslct <- diag(FacDim)
  eslct <- diag(FacDim)

  # 2.1) Minor preliminary work
  invG <- diag(nrow(G0)) / G0
  invG[!is.finite(invG)] <- 0
  invGSigmau <- solve(G0) %*% SIGMA

  # Choleski term
  if (any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
    P <- CholFac_JLL
  } else {
    P <- t(chol(invGSigmau))
  }

  # Adjustment term
  if (Mode == "Ortho" & any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
    AdjTerm <- PI
  } else {
    AdjTerm <- diag(FacDim)
  }

  scale <- 1

  # 3) FEVD
  # 3.1) Factors
  FEVDresFactors <- array(NA, c(nrow = FacDim, ncol = FacDim, FEVDhoriz))
  num <- matrix(0, nrow = FacDim, ncol = FacDim)
  den <- rep(0, times = FacDim)

  for (l in 1:FEVDhoriz) {
    acc1 <- (eslct %*% Ry.h[, , l] %*% P %*% vslct)^2
    num <- num + acc1
    acc2 <- diag(eslct %*% Ry.h[, , l] %*% invGSigmau %*% t(invG) %*% t(Ry.h[, , l]) %*% eslct)
    den <- den + acc2
    FEVDresFactors[, , l] <- scale * num / den
  }

  FEVDFactors <- aperm(FEVDresFactors, c(3, 2, 1))

  # 3.2) Yields
  eslctCJ <- diag(MatLength)
  vslctCJ <- diag(FacDim)

  FEVDresYields <- array(NA, c(nrow = MatLength, ncol = FacDim, FEVDhoriz))
  num <- matrix(0, nrow = MatLength, ncol = FacDim)
  den <- matrix(0, nrow = MatLength, ncol = FacDim)

  for (l in 1:FEVDhoriz) {
    acc1 <- (eslctCJ %*% BLoad %*% AdjTerm %*% Ry.h[, , l] %*% P %*% vslctCJ)^2
    num <- num + acc1
    acc2 <- diag(eslctCJ %*% BLoad %*% AdjTerm %*% Ry.h[, , l] %*% invGSigmau %*% t(invG) %*% t(Ry.h[, , l]) %*% t(AdjTerm) %*% t(BLoad) %*% eslctCJ)
    den <- den + acc2
    FEVDresYields[, , l] <- scale * num / den
  }

  FEVDYields <- aperm(FEVDresYields, c(3, 2, 1))

  # 4) Prepare labels
  if (ModelType == "JPS original") {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables[[Economy]])
  } else if (any(ModelType == c("JPS global", "GVAR single"))) {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  } else {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  }

  # 5) Export outputs
  Horiz <- 1:(FEVDhoriz) # # We don't subtract 1, because there is no contemporaneous effect for the FORECAST error

  dimnames(FEVDFactors) <- list(Horiz, AllFactorsLabels, AllFactorsLabels)
  dimnames(FEVDYields) <- list(Horiz, AllFactorsLabels, YieldsLabel)

  Out <- list(Factors = FEVDFactors, Yields = FEVDYields)

  return(Out)
}

########################################################################################################
#' Compute GFEVDs for all models
#'
#' @param SIGMA Variance-covariance matrix
#' @param K1Z Loading As
#' @param G0 contemporaneous terms
#' @param BLoad Loading Bs
#' @param FactorLabels List containing the label of factors
#' @param FacDim Dimension of the P-dynamics
#' @param MatLength Length of the maturity vector
#' @param GFEVDhoriz Horizon of the analysis
#' @param YieldsLabel Label of bond yields
#' @param ModelType Desired model type
#' @param Economy specific economy under study
#' @param PI matrix PI for JLL-based models
#' @param Mode allows for the orthogonalized version in the case of JLL-based models
#'
#' @references
#' \itemize{
#' \item This function is a modified and extended version of the "fevd" function from
#' Smith, L.V. and A. Galesi (2014). GVAR Toolbox 2.0, available at https://sites.google.com/site/gvarmodelling/gvar-toolbox.
#'
#' \item Pesaran and Shin, 1998. "Generalized impulse response analysis in linear multivariate models" (Economics Letters)
#' }
#'
#' @keywords internal

ComputeGFEVDs <- function(SIGMA, K1Z, G0, BLoad, FactorLabels, FacDim, MatLength, GFEVDhoriz, YieldsLabel,
                          ModelType, Economy, PI = NULL, Mode = FALSE) {
  # 1) Dynamic multipliers
  Ry.h <- array(NA, c(FacDim, FacDim, GFEVDhoriz))
  Ry.h[, , 1] <- diag(FacDim) # dynamic multiplier at t=0

  for (l in 2:GFEVDhoriz) {
    Ry.h[, , l] <- K1Z %*% Ry.h[, , l - 1]
  }

  # 2) Initialization/ Minor preliminary work
  GFEVDresFac <- array(NA, c(nrow = FacDim, ncol = GFEVDhoriz, FacDim))
  vslct <- diag(FacDim)
  eslct <- diag(FacDim)

  invG <- diag(nrow(G0)) / G0
  invG[!is.finite(invG)] <- 0
  invGSigmau <- solve(G0) %*% SIGMA

  scale <- 1 / diag(SIGMA)

  # Adjustment term
  if (Mode == "Ortho" & any(ModelType == c("JLL original", "JLL No DomUnit", "JLL joint Sigma"))) {
    AdjTerm <- PI
  } else {
    AdjTerm <- diag(FacDim)
  }


  # 3) GFEVD
  # 3.1) Factors
  for (h in 1:FacDim) {
    n <- 1
    num <- matrix(0, nrow = FacDim, ncol = GFEVDhoriz)
    den <- matrix(0, nrow = FacDim, ncol = GFEVDhoriz)
    while (n <= GFEVDhoriz) {
      for (l in 1:n) {
        acc1 <- t((eslct[, h] %*% AdjTerm %*% Ry.h[, , l] %*% invGSigmau %*% vslct)^2) # Contribution of all j variables to explain i
        num[, n] <- num[, n] + acc1
        acc2 <- eslct[, h] %*% AdjTerm %*% Ry.h[, , l] %*% invGSigmau %*% t(invG) %*% t(Ry.h[, , l]) %*% eslct[, h]
        den[, n] <- den[, n] + matrix(1, nrow = FacDim) %*% acc2
      }
      GFEVDresFac[, n, h] <- t(t(scale * num[, n])) / den[, n]
      n <- n + 1
    }
  }

  GFEVDFactors <- aperm(GFEVDresFac, c(2, 1, 3)) # Non-normalized GFEVD (i.e. rows need not sum up to 1)

  #  Normalization of the GFEVD for the factors
  # (Make sure that the sum of the errors equal to one in each period)
  DEM <- array(NA, c(nrow = GFEVDhoriz, ncol = 1, FacDim))
  GFEVDFactorsNormalized <- array(NA, c(nrow = GFEVDhoriz, ncol = FacDim, FacDim))

  for (h in 1:FacDim) {
    for (n in 1:GFEVDhoriz) {
      DEM[n, 1, h] <- sum(GFEVDFactors[n, , h])
      GFEVDFactorsNormalized[n, , h] <- GFEVDFactors[n, , h] / DEM[n, , h]
    }
  }

  # 3.2) Yields
  # Initialization
  GFEVDresYie <- array(NA, c(nrow = MatLength, ncol = FacDim, GFEVDhoriz))
  vslctYie <- diag(FacDim)
  eslctYie <- diag(MatLength)

  num <- matrix(0, nrow = MatLength, ncol = FacDim)
  den <- matrix(0, nrow = MatLength, ncol = FacDim)

  for (l in 1:GFEVDhoriz) {
    acc1 <- (eslctYie %*% BLoad %*% AdjTerm %*% Ry.h[, , l] %*% invGSigmau %*% vslctYie)^2
    num <- num + acc1
    acc2 <- diag(eslctYie %*% BLoad %*% AdjTerm %*% Ry.h[, , l] %*% invGSigmau %*% t(invG) %*% t(Ry.h[, , l]) %*% t(AdjTerm) %*% t(BLoad) %*% eslctYie)
    den <- den + acc2
    for (q in 1:FacDim) {
      GFEVDresYie[, q, l] <- scale[q] * (num / den)[, q] # note: unlike the GFEVD of the factors, note that the "scale" variable is now at the acc1
    }
  }

  GFEVDYields <- aperm(GFEVDresYie, c(3, 2, 1)) # Non-normalized GFEVD (i.e. rows need not sum up to 1)

  #  Normalization of the GFEVD for the factors
  # (Make sure that the sum of the errors equal to one in each period)
  DEM <- array(NA, c(nrow = GFEVDhoriz, ncol = 1, MatLength))
  GFEVDYieldsNormalized <- array(NA, c(nrow = GFEVDhoriz, ncol = FacDim, MatLength))

  for (h in 1:MatLength) {
    for (n in 1:GFEVDhoriz) {
      DEM[n, 1, h] <- sum(GFEVDYields[n, , h])
      GFEVDYieldsNormalized[n, , h] <- GFEVDYields[n, , h] / DEM[n, , h]
    }
  }

  # 4) Prepare labels
  if (ModelType == "JPS original") {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables[[Economy]])
  } else if (any(ModelType == c("JPS global", "GVAR single"))) {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  } else {
    AllFactorsLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
  }

  # 5) Outputs to export
  Horiz <- 1:(GFEVDhoriz) # # We don't subtract 1, because there is no contemporaneous effect for the FORECAST error

  dimnames(GFEVDFactorsNormalized) <- list(Horiz, AllFactorsLabels, AllFactorsLabels)
  dimnames(GFEVDYieldsNormalized) <- list(Horiz, AllFactorsLabels, YieldsLabel)

  Out <- list(Factors = GFEVDFactorsNormalized, Yields = GFEVDYieldsNormalized)

  return(Out)
}

########################################################################################################
#' Fit yields for all maturities of interest
#'
#' @param MatInt numerical vector containing the fit maturities of interest
#' @param ModelPara List of model parameter estimates (See the "Optimization" function)
#' @param FactorLabels a string-list based which contains all the labels of all the variables present in the model
#' @param ModelType a string-vector containing the label of the model to be estimated
#' @param Economies a string-vector containing the names of the economies which are part of the economic system
#' @param YLab Label of yields ("Months" or "Yields")
#'
#' @keywords internal

YieldsFitAll <- function(MatInt, ModelPara, FactorLabels, ModelType, Economies, YLab) {
  # 0) Auxliary functions
  # a) Function to compute fitted yields
  compute_fitted_yields <- function(MatInt, MatAll, K1XQ, ModelType, r0, SSX, X, T_dim, dt, Economies,
                                    Time_Labels, Yield_Labels) {
    LoadingsLat <- Get__BnXAnX(MatAll, K1XQ, ModelType, r0, SSX, Economies)
    AnXAll <- LoadingsLat$AnX / dt
    BnXAll <- LoadingsLat$BnX / dt

    if (ModelType %in% Joint_Lab) {
      C <- length(Economies)
      FitLat <- matrix(NA, nrow = C * length(MatInt), ncol = T_dim)
    } else {
      FitLat <- matrix(NA, nrow = length(MatInt), ncol = T_dim)
    }

    IdxMatInt <- sort(unlist(lapply(MatInt, function(h) seq(h, length(AnXAll), by = max(MatAll)))))
    AnXInt <- AnXAll[IdxMatInt]
    BnXInt <- BnXAll[IdxMatInt, ]
    FitLat <- matrix(AnXInt, nrow = nrow(FitLat), ncol = T_dim) + BnXInt %*% X

    if (ModelType %in% Joint_Lab) {
      colnames(FitLat) <- Time_Labels
    } else {
      dimnames(FitLat) <- list(Yield_Labels, Time_Labels)
    }

    return(FitLat)
  }

  # b) Function to compute time series of the latent factors
  compute_X <- function(ZZ, Wpca, BnX, AnX, T_dim) {
    PP <- ZZ
    X <- matrix(NA, nrow = nrow(PP), ncol = T_dim)
    for (t in 1:T_dim) {
      X[, t] <- solve(Wpca %*% BnX, tol = 1e-50) %*% (PP[, t] - Wpca %*% AnX)
    }
    return(X)
  }

  # 1) Preliminar work
  Joint_Lab <- c("JPS multi", "GVAR multi", "JLL original", "JLL No DomUnit", "JLL joint Sigma")

  C <- length(Economies)
  Mod_ParaSet <- ModelPara[[ModelType]]
  N <- if (ModelType %in% Joint_Lab) Mod_ParaSet$Inputs$N else Mod_ParaSet[[Economies[1]]]$Inputs$N
  dt <- if (ModelType %in% Joint_Lab) Mod_ParaSet$Inputs$dt else Mod_ParaSet[[Economies[1]]]$Inputs$dt
  mat <- if (ModelType %in% Joint_Lab) Mod_ParaSet$Inputs$mat else Mod_ParaSet[[Economies[1]]]$Inputs$mat
  J <- length(mat)
  M <- length(FactorLabels$Domestic) - N
  G <- length(FactorLabels$Global)

  T_dim <- if (ModelType %in% Joint_Lab) {
    ncol(Mod_ParaSet$Inputs$AllFactors)
  } else {
    ncol(Mod_ParaSet[[Economies[1]]]$Inputs$AllFactors)
  }

  # 2) Compute results for joint or separate models
  # a) JointQ models
  if (ModelType %in% Joint_Lab) {
    BnX <- Mod_ParaSet$ModEst$Q$Load$X$B
    AnX <- Mod_ParaSet$ModEst$Q$Load$X$A
    K1XQ <- Mod_ParaSet$ModEst$Q$K1XQ
    SSX <- Mod_ParaSet$ModEst$Q$Load$X$SS
    r0 <- Mod_ParaSet$ModEst$Q$r0
    Wpca <- Mod_ParaSet$Inputs$Wpca
    ZZ <- Mod_ParaSet$Inputs$AllFactors

    b <- IdxSpanned(G, M, N, C)
    PP <- ZZ[b, ]
    X <- compute_X(PP, Wpca, BnX, AnX, T_dim)

    MatAll <- 1:max(mat / dt)
    Time_Labels <- colnames(Mod_ParaSet$Inputs$AllFactors)
    Yield_Labels <- paste(MatInt, YLab, sep = "")
    FittedYieldsPerMat <- compute_fitted_yields(
      MatInt, MatAll, K1XQ, ModelType, r0, SSX, X, T_dim, dt, Economies,
      Time_Labels, Yield_Labels
    )

    h <- length(MatInt)

    FittedYieldsCS <- lapply(1:C, function(i) {
      start_row <- (i - 1) * h + 1
      end_row <- i * h
      FittedYieldsPerMat[start_row:end_row, , drop = FALSE]
    })

    names(FittedYieldsCS) <- Economies

    return(FittedYieldsCS)
  } else {
    # b) SepQ models
    FittedYieldsPerMat <- list()

    for (i in 1:C) {
      BnX <- Mod_ParaSet[[Economies[i]]]$ModEst$Q$Load$X$B
      AnX <- Mod_ParaSet[[Economies[i]]]$ModEst$Q$Load$X$A
      K1XQ <- Mod_ParaSet[[Economies[i]]]$ModEst$Q$K1XQ
      SSX <- Mod_ParaSet[[Economies[i]]]$ModEst$Q$Load$X$SS
      r0 <- Mod_ParaSet[[Economies[i]]]$ModEst$Q$r0
      Wpca <- Mod_ParaSet[[Economies[i]]]$Inputs$Wpca
      ZZ <- Mod_ParaSet[[Economies[i]]]$Inputs$AllFactors

      if (ModelType == "JPS original") {
        AllLabels <- c(FactorLabels$Global, FactorLabels$Tables[[Economies[i]]])
      } else {
        AllLabels <- c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
      }

      LabelSpannedCS <- c(FactorLabels$Tables[[Economies[i]]][-(1:M)])
      b <- match(LabelSpannedCS, AllLabels)
      PP <- ZZ[b, ]

      X <- compute_X(PP, Wpca, BnX, AnX, T_dim)
      MatAll <- 1:max(mat / dt)
      col_names <- colnames(Mod_ParaSet[[Economies[1]]]$Inputs$AllFactors)
      row_names <- paste(MatInt, YLab, sep = "")
      FittedYieldsPerMat[[i]] <- compute_fitted_yields(MatInt, MatAll, K1XQ, ModelType, r0, SSX, X, T_dim, dt, Economies, col_names, row_names)
    }

    names(FittedYieldsPerMat) <- Economies
    return(FittedYieldsPerMat)
  }
}

#################################################################################################
#' Adjust vector of maturities
#'
#' @param mat vector of maturities (J x 1)
#' @param UnitYields Available options: "Month" and "Year"
#'
#' @keywords internal

MatAdjusted <- function(mat, UnitYields) {
  if (UnitYields == "Month") {
    k <- 12
  } else if (UnitYields == "Year") {
    k <- 1
  }
  matAdjUnit <- mat * k

  return(matAdjUnit)
}
########################################################################################################
#' Get the expected component of all models
#'
#' @param ModelPara list of model parameter estimates
#' @param InputsForOutputs list containing the desired horizon of analysis for the model fit, IRFs, GIRFs, FEVDs, GFEVDs,
#'                        and risk premia decomposition
#' @param ModelType desired model type
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#' @param FactorLabels string-list based which contains all the labels of all the variables present in the model
#' @param WishFP If users wants to compute the forward premia. Default is FALSE.
#'
#' @keywords internal

ExpectedComponent <- function(ModelPara, InputsForOutputs, ModelType, Economies, FactorLabels, WishFP = FALSE) {
  # 0) Preliminary work
  N <- length(FactorLabels$Spanned)
  UnitYields <- InputsForOutputs$UnitMatYields

  if (any(ModelType == c("JPS original", "JPS global", "GVAR single"))) {
    mat <- ModelPara[[ModelType]][[1]]$Inputs$mat
  } else {
    mat <- ModelPara[[ModelType]]$Inputs$mat
  }

  matAdjUnit <- MatAdjusted(mat, UnitYields)

  if (WishFP) {
    matMIN <- InputsForOutputs[[ModelType]]$ForwardPremia$Limits[1]
    matMAX <- InputsForOutputs[[ModelType]]$ForwardPremia$Limits[2]
  }

  # 1) Compute risk-neutral parameters
  rhos <- rhoParas(ModelPara, N, ModelType, Economies)

  # 2) Compute the expectation component
  # Pure expected component
  avexp <- Compute_EP(ModelPara, ModelType, UnitYields, matAdjUnit, N, rhos, Economies, FactorLabels)

  # expected component related to the forward premia (if desired)
  if (WishFP) {
    avexpFP <- Compute_EP(
      ModelPara, ModelType, UnitYields, matAdjUnit, N, rhos, Economies, FactorLabels,
      WishFP, matMIN, matMAX
    )
  } else {
    avexpFP <- list()
  }

  return(list(avexp = avexp, avexpFP = avexpFP))
}

#################################################################################################
#' Compute  risk-neutral intercept and slope
#'
#' @param ModelPara list of model parameter estimates
#' @param N number of country-specific spanned factors
#' @param ModelType desired model type
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#'
#' @keywords internal

rhoParas <- function(ModelPara, N, ModelType, Economies) {
  # Compute the intercept and slope coefficients of the short rate expressed as a function of the spanned factors
  # By definition: r_t = r0 + rho1_X* X_t
  # But X_t = (W*BX)^(-1)(P- WAx)
  # so r_t = rho0_PP + rho1_PP*P_t
  # where (i) rho0_PP = r0 - rho1_X*(W*BX)^(-1)W*AX and (ii) rho1_PP = rho1_X (W*BX)^(-1)

  # 1) Models estimated for countries SEPARETLY
  if (any(ModelType == c("JPS original", "JPS global", "GVAR single"))) {
    rho0_PP <- vector(mode = "list", length = length(Economies))
    rho1_PP <- vector(mode = "list", length = length(Economies))
    names(rho0_PP) <- Economies
    names(rho1_PP) <- Economies

    dt <- ModelPara[[ModelType]][[Economies[1]]]$Inputs$dt

    for (i in 1:length(Economies)) {
      Para_Set <- ModelPara[[ModelType]][[Economies[i]]]
      BnX <- Para_Set$ModEst$Q$Load$X$B
      AnX <- Para_Set$ModEst$Q$Load$X$A
      Wpca <- Para_Set$Inputs$Wpca
      r0 <- Para_Set$ModEst$Q$r0

      # Compute rhos
      rho1_X <- rep(1, N)
      rho0_PP[[i]] <- as.numeric((r0 - rho1_X %*% solve(Wpca %*% BnX, tol = 1e-50) %*% Wpca %*% AnX) / dt)
      rho1_PP[[i]] <- (rho1_X %*% solve(Wpca %*% BnX, tol = 1e-50)) / dt
    }
  } else {
    # 2) Models estimated for countries JOINTLY
    Para_Set <- ModelPara[[ModelType]]
    dt <- Para_Set$Inputs$dt
    BnX <- Para_Set$ModEst$Q$Load$X$B
    AnX <- Para_Set$ModEst$Q$Load$X$A
    Wpca <- Para_Set$Inputs$Wpca
    r0 <- Para_Set$ModEst$Q$r0

    # Compute rhos
    rho1_X_CS <- rep(1, N)
    rho1_X <- matrix(0, nrow = length(Economies), ncol = N * length(Economies))

    idx0 <- 0
    for (j in 1:length(Economies)) {
      idx1 <- idx0 + N
      rho1_X[j, (idx0 + 1):idx1] <- rho1_X_CS
      idx0 <- idx1
    }

    rho0_PP <- (r0 - rho1_X %*% solve(Wpca %*% BnX, tol = 1e-50) %*% Wpca %*% AnX) / dt
    rho1_PP <- (rho1_X %*% solve(Wpca %*% BnX, tol = 1e-50)) / dt
  }

  return(list(rho0_PP = rho0_PP, rho1_PP = rho1_PP))
}

###################################################################################################
#' Compute the expected component for all models
#'
#' @param ModelPara list of model parameter estimates
#' @param ModelType Desired model type
#' @param UnitYields Available options: "Month" and "Year"
#' @param matAdjUnit Adjusted vector of matutities
#' @param N number of country-specific spanned factors
#' @param rhoList List of risk-neutral parameters
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#' @param FactorLabels List of factor labels
#' @param WishFP If users wants to compute the forward premia. Default is FALSE.
#' @param matMIN For the forward premia, the shortest maturity of the remium of interest
#' @param matMAX For the forward premia, the longest maturity of the remium of interest
#'
#' @keywords internal

Compute_EP <- function(ModelPara, ModelType, UnitYields, matAdjUnit, N, rhoList, Economies, FactorLabels,
                       WishFP = FALSE, matMIN = FALSE, matMAX = FALSE) {
  # 0) Preliminary work
  SepQ_Lab <- c("JPS original", "JPS global", "GVAR single")
  M <- length(FactorLabels$Domestic) - N

  dt <- if (ModelType %in% SepQ_Lab) {
    ModelPara[[ModelType]][[Economies[1]]]$Inputs$dt
  } else {
    ModelPara[[ModelType]]$Inputs$dt
  }

  k <- if (UnitYields == "Month") 12 else 1
  YLab <- if (UnitYields == "Month") "M" else "Y"

  if (WishFP) { # Forward premia features
    EP_Lab <- "FP_"
    ExpecCompLength <- 1
    matMINAdj <- round((matMIN / k) / dt)
    matMAXAdj <- round((matMAX / k) / dt)
  } else {
    EP_Lab <- "RP_"
    ExpecCompLength <- round((matAdjUnit / k) / dt)
  }

  # 1) Compute the expected component

  ParaSet <- ModelPara[[ModelType]]
  # jointQ models
  if (!(ModelType %in% SepQ_Lab)) {
    K0Z <- ParaSet$ModEst$P$K0Z
    K1Z <- ParaSet$ModEst$P$K1Z
    ZZ <- ParaSet$Inputs$AllFactors
  }

  avexp <- list()
  for (i in 1:length(Economies)) {
    econ <- Economies[i]
    # a) Pre-allocation
    if (ModelType %in% SepQ_Lab) { # SepQ models
      K0Z <- ParaSet[[Economies[i]]]$ModEst$P$K0Z
      K1Z <- ParaSet[[Economies[i]]]$ModEst$P$K1Z
      ZZ <- ParaSet[[Economies[i]]]$Inputs$AllFactors
    }

    # b) Extract spanned factors from the list of unspanned factors
    IdxSpanned <- if (ModelType %in% SepQ_Lab) {
      all_labels <- if (ModelType == "JPS original") {
        c(FactorLabels$Global, FactorLabels$Tables[[econ]])
      } else {
        c(FactorLabels$Global, FactorLabels$Tables$AllCountries)
      }

      match(FactorLabels$Tables[[econ]][-(1:M)], all_labels)
    } else {
      IdxAllSpanned(ModelType, FactorLabels, Economies)
    }

    # c) Get the expected component
    avexpCS <- matrix(0, ncol(ZZ), length(ExpecCompLength))
    dimnames(avexpCS) <- list(colnames(ZZ), paste(EP_Lab, ExpecCompLength, YLab, sep = ""))

    for (h in 1:length(ExpecCompLength)) { # per bond maturity
      for (t in 1:ncol(ZZ)) { # Per point in time

        # Initialization
        if (WishFP) {
          g <- matrix(NA, nrow(K0Z), matMAXAdj)
        } else {
          g <- matrix(NA, nrow(K0Z), ExpecCompLength[h])
        }
        rownames(g) <- rownames(ZZ)

        # Fitted P-dynamics
        g[, 1] <- ZZ[, t]
        if (WishFP) {
          loopLim <- matMAXAdj
        } else {
          loopLim <- ExpecCompLength[h]
        }
        for (j in 2:loopLim) {
          g[, j] <- K0Z + K1Z %*% g[, j - 1]
        }

        # Adjust `g_lim` dynamically
        g_lim <- if (WishFP) {
          round((matMIN / k) / dt):round((matMAX / k) / dt)
        } else {
          seq_len(ncol(g))
        }

        g <- g[IdxSpanned, g_lim] # extract relevant variables

        # Adjustment term
        if (ModelType %in% SepQ_Lab) {
          MaxExpec <- pmax(rhoList$rho0_PP[[i]] + (rhoList$rho1_PP[[i]] %*% g), 0)
        } else {
          MaxExpec <- pmax(rhoList$rho0_PP[i] + (rhoList$rho1_PP[i, ] %*% g), 0)
        }

        avexpCS[t, h] <- mean(MaxExpec)
      }
    }

    avexp[[Economies[i]]] <- avexpCS * 100
  }

  return(avexp)
}

###################################################################################################
#' Compute the term premia
#'
#' @param ModelPara list of model parameter estimates
#' @param avexp list containing the country-specific pure expected component
#' @param ModelType desired model type
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#'
#' @keywords internal

TermPremia <- function(ModelPara, avexp, ModelType, Economies) {
  # 0) Preliminary work
  YieldData <- list()
  TermPremium <- list()

  SepQ_Lab <- c("JPS original", "JPS global", "GVAR single")

  if (!ModelType %in% SepQ_Lab) {
    mat <- ModelPara[[ModelType]]$Inputs$mat
    J <- length(mat)
  }

  # 1) Compute the term premia
  for (i in 1:length(Economies)) {
    if (ModelType %in% SepQ_Lab) { # SepQ models
      Y <- ModelPara[[ModelType]][[Economies[i]]]$Inputs$Y
      YieldData[[Economies[i]]] <- t(Y) * 100
    } else { # jointQ models
      IdxRP <- (1:J) + J * (i - 1)
      Y <- ModelPara[[ModelType]]$Inputs$Y
      YieldData[[Economies[i]]] <- t(Y[IdxRP, ] * 100)
    }
    TermPremium[[Economies[i]]] <- YieldData[[Economies[i]]] - avexp[[Economies[i]]]
  }

  Output <- list(TermPremium, avexp)
  names(Output) <- c("Term Premia", "Expected Component")

  return(Output)
}
###############################################################################################
#' Compute the forward premia for all models
#'
#' @param ModelPara list of model parameter estimates
#' @param avexpFP list containing the country-specific expected component of the forward period
#' @param ModelType desired model type
#' @param FactorLabels List of factor labels
#' @param InputsForOutputs list containing the desired horizon of analysis for the model fit, IRFs, GIRFs, FEVDs, GFEVDs,
#'                        and risk premia decomposition
#' @param Economies string-vector containing the names of the economies which are part of the economic system
#'
#' @keywords internal

ForwardPremia <- function(ModelPara, avexpFP, ModelType, FactorLabels, InputsForOutputs, Economies) {
  # 0) Preliminary work: redefine necessary inputs
  SepQ_Lab <- c("JPS original", "JPS global", "GVAR single")

  if (ModelType %in% SepQ_Lab) {
    mat <- ModelPara[[ModelType]][[1]]$Inputs$mat
  } else {
    mat <- ModelPara[[ModelType]]$Inputs$mat
  }

  J <- length(mat)

  # Yield labels
  UnitYields <- InputsForOutputs$UnitMatYields
  if (UnitYields == "Month") {
    YLab <- "M"
  } else {
    YLab <- "Y"
  }

  # Inputs from the forward premia specification
  matAdjUnit <- MatAdjusted(mat, UnitYields)

  matMIN <- InputsForOutputs[[ModelType]]$ForwardPremia$Limits[1]
  matMAX <- InputsForOutputs[[ModelType]]$ForwardPremia$Limits[2]
  IDXMatMIN <- match(matMIN, matAdjUnit)
  IDXMatMAX <- match(matMAX, matAdjUnit)
  IDXMatBoth <- c(IDXMatMIN, IDXMatMAX)

  FR <- list()
  ForwardPremium <- list()

  # CASE 1: If any of the data of the specific maturity were not used in the estimation, then compute the fitted value
  if (anyNA(IDXMatBoth)) {
    MatBoth <- c(matMIN, matMAX)
    IdxNA <- which(is.na(IDXMatBoth))

    # a) Missing maturity (model-implied)
    MissingMat <- MatBoth[IdxNA] # Maturity not available
    YieldMissing <- YieldsFitAll(MissingMat, ModelPara, FactorLabels, ModelType, Economies, YLab)

    for (i in 1:length(Economies)) {
      if (ModelType %in% SepQ_Lab) {
        Y <- ModelPara[[ModelType]][[Economies[i]]]$Inputs$Y
      } else {
        Y <- ModelPara[[ModelType]]$Inputs$Y
      }

      # b) If both maturities are missing
      if (length(MissingMat) == 2) {
        YieldMIN <- t(t(YieldMissing[[Economies[i]]][1, ])) * 100
        YieldMAX <- t(t(YieldMissing[[Economies[i]]][2, ])) * 100
      } else {
        # c) If only one maturity is missing
        # Available maturity
        IdxNotMissing0 <- IDXMatBoth[!is.na(IDXMatBoth)]
        if (ModelType %in% SepQ_Lab) {
          IdxNotMissingCS <- IdxNotMissing0
        } else {
          IdxNotMissingCS <- IdxNotMissing0 + J * (i - 1)
        }

        YieldNotMissing <- t(t(Y[IdxNotMissingCS, ]))

        if (MissingMat == 1) {
          YieldMIN <- YieldNotMissing * 100
          YieldMAX <- t(YieldMissing[[Economies[i]]]) * 100
        } else {
          YieldMIN <- t(YieldMissing[[Economies[i]]]) * 100
          YieldMAX <- YieldNotMissing * 100
        }
      }

      FR[[Economies[i]]] <- (matMAX * YieldMAX - matMIN * YieldMIN) / (matMAX - matMIN) # Fitted forward rate
      ForwardPremium[[Economies[i]]] <- FR[[Economies[i]]] - avexpFP[[Economies[i]]] # Forward Premia
      colnames(ForwardPremium[[Economies[i]]]) <- paste("FP_", matMIN, "-", matMAX, YLab, sep = "")
      colnames(FR[[Economies[i]]]) <- paste("Mat", matMIN, "-", matMAX, YLab, sep = "")
    }
  } else {
    # CASE 2: when all data is available
    YieldData <- list()

    for (i in 1:length(Economies)) {
      # SepQ models
      if (ModelType %in% SepQ_Lab) {
        Y <- ModelPara[[ModelType]][[Economies[i]]]$Inputs$Y
        YieldData[[Economies[i]]] <- t(Y) * 100

        YieldMIN <- t(t(YieldData[[Economies[i]]][, IDXMatMIN]))
        YieldMAX <- t(t(YieldData[[Economies[i]]][, IDXMatMAX]))
      } else {
        # jointQ models
        Y <- ModelPara[[ModelType]]$Inputs$Y

        IdxMinCS <- IDXMatMIN + J * (i - 1)
        IdxMaxCS <- IDXMatMAX + J * (i - 1)
        YieldMIN <- t(t(Y[IdxMinCS, ] * 100))
        YieldMAX <- t(t(Y[IdxMaxCS, ] * 100))
      }

      FR[[Economies[i]]] <- (matMAX * YieldMAX - matMIN * YieldMIN) / (matMAX - matMIN) # Fitted forward rate
      ForwardPremium[[Economies[i]]] <- FR[[Economies[i]]] - avexpFP[[Economies[i]]] # Forward Premia
      colnames(ForwardPremium[[Economies[i]]]) <- paste("FP_", matMIN, "-", matMAX, YLab, sep = "")
      colnames(FR[[Economies[i]]]) <- paste("Mat", matMIN, "-", matMAX, YLab, sep = "")
    }
  }

  OutputFP <- list(ForwardPremium, avexpFP, FR)
  names(OutputFP) <- c("Forward Premia", "Expected Component", "Forward Rate")

  return(OutputFP)
}

################################################################################################################
#' Find the indexes of the spanned factors
#'
#' @param ModelType string-vector containing the label of the model to be estimated
#' @param FactorLabels string-list based which contains the labels of all the variables present in the model
#' @param Economies  string-vector containing the names of the economies which are part of the economic system
#'
#' @keywords internal

IdxAllSpanned <- function(ModelType, FactorLabels, Economies) {
  G <- length(FactorLabels$Global)
  N <- length(FactorLabels$Spanned)
  M <- length(FactorLabels$Domestic) - N
  C <- length(Economies)

  IdxSpanned <- c()

  if (ModelType == "JPS original") {
    IdxSpanned <- (G + M + 1):(G + M + N)
  } else {
    idxSpa0 <- G + M
    for (j in 1:C) {
      idxSpa1 <- idxSpa0 + N

      if (j == 1) {
        IdxSpanned <- (idxSpa0 + 1):idxSpa1
      } else {
        IdxSpanned <- c(IdxSpanned, (idxSpa0 + 1):idxSpa1)
      }
      idxSpa0 <- idxSpa1 + M
    }
  }

  return(IdxSpanned)
}

Try the MultiATSM package in your browser

Any scripts or data that you put into this service are public.

MultiATSM documentation built on Nov. 5, 2025, 7:01 p.m.