| Title: | Portfolio Choice: Estimation, Construction, and Evaluation |
|---|---|
| Description: | Providing quantitative tools for input estimation, portfolio construction, and performance evaluation. |
| Authors: | Pavlos Pantatosakis [aut, cre], Georgios Skoulakis [aut] |
| Maintainer: | Pavlos Pantatosakis <[email protected]> |
| License: | GPL-3 |
| Version: | 1.0.0 |
| Built: | 2026-05-26 07:47:22 UTC |
| Source: | https://github.com/cran/WeightCraft |
This function calculates an exponentially weighted moving average vector, and an exponentially weighted moving covariance matrix for a given numeric matrix or data.frame. It is designed for applications where recent observations receive higher weights than past ones.
EWMA(x, lambda, center = TRUE)EWMA(x, lambda, center = TRUE)
x |
A numeric matrix or data.frame. Rows represent time periods, columns represent variables. |
lambda |
A single numeric value in the interval (0, 1] representing the decay factor. When lambda = 1, all observations receive equal weight. |
center |
Logical; if |
The weight for each time is calculated as:
When lambda = 1, all weights equal and the function returns the
equal-weighted mean and covariance.
The weighted mean vector is:
When center = FALSE, is set to zero and the covariance matrix
is computed around zero instead.
The weighted covariance matrix is:
A list with three elements:
weights: A numeric vector of length containing the exponential weights,
ordered from oldest (smallest weight) to most recent (largest weight).
EWMA_mu: A numeric vector of length containing the weighted mean of
each column of x. If center = FALSE, this is a zero vector.
EWMA_sigma: A numeric matrix containing the weighted
covariance matrix.
Pavlos Pantatosakis [email protected], Georgios Skoulakis [email protected]
J.P.Morgan/Reuters (Longerstaey, J., & Spencer, M., 1996). Riskmetrics—Technical Document.
Pafka, S., Potters, M., & Kondor, I. (2004). Exponential weighting and random-matrix-theory-based filtering of financial covariance matrices for portfolio optimization. arXiv preprint cond-mat/0402573.
y <- matrix(rnorm(600, 0, 1), ncol = 3) colnames(y) <- c("X1", "X2", "X3") example1 <- EWMA(y, 0.97) print(example1$EWMA_mu) # Weighted Mean print(example1$EWMA_sigma) # Weighted Covariance Matrix # Weights sum to 1 and decrease exponentially over time. sum(example1$weights) ; ts.plot(example1$weights) #Equal Weight example2 <- EWMA(y, 1) print(example2$EWMA_mu) # Mean print(example2$EWMA_sigma) # Covariance Matrix sum(example2$weights) ; ts.plot(example2$weights)y <- matrix(rnorm(600, 0, 1), ncol = 3) colnames(y) <- c("X1", "X2", "X3") example1 <- EWMA(y, 0.97) print(example1$EWMA_mu) # Weighted Mean print(example1$EWMA_sigma) # Weighted Covariance Matrix # Weights sum to 1 and decrease exponentially over time. sum(example1$weights) ; ts.plot(example1$weights) #Equal Weight example2 <- EWMA(y, 1) print(example2$EWMA_mu) # Mean print(example2$EWMA_sigma) # Covariance Matrix sum(example2$weights) ; ts.plot(example2$weights)
This function computes various performance measures for one or more return series. It is designed for evaluating and comparing different investment strategies.
PerformanceMeasures(R, rf, risk_aversion = 3, FREQ = "Monthly")PerformanceMeasures(R, rf, risk_aversion = 3, FREQ = "Monthly")
R |
A numeric matrix or data.frame with the returns of each strategy. Rows represent time periods, and columns represent different strategies or assets. |
rf |
A numeric scalar or a vector representing the risk-free rate.
If a vector, its length must match the number of rows in |
risk_aversion |
A single positive numeric value representing the
coefficient of risk aversion ( |
FREQ |
The frequency of the data. Can be a character string: 'Annual', 'Monthly', 'Weekly', or 'Daily', or a numeric value (1 for Annual, 12 for Monthly, 52 for Weekly, and 252 for Daily). Default is 'Monthly'. |
The function computes annualized metrics based on the specified frequency .
Let be the return and be the risk-free rate at time .
The performance measures are defined as follows:
Certainty Equivalent Return (CER):
where and .
Compound Annual Growth Rate (CAGR):
Risk Premium (Annualized Excess Returns):
Annualized Volatility:
where .
Sharpe Ratio:
Sortino Ratio:
where .
Maximum Drawdown (MaxDD):
where is the cumulative wealth series, and .
Terminal Wealth:
A data.frame where each row corresponds to a strategy. The columns CER, CAGR, Premium, Volatility, and MaxDD are returned as percentages (multiplied by 100).
Pavlos Pantatosakis [email protected], Georgios Skoulakis [email protected]
set.seed(123) # Time-varying risk-free rate, monthly data, default risk aversion = 3 example1 <- matrix(rnorm(120, 0.01, 0.04), ncol = 2) colnames(example1) <- c("Strategy_A", "Strategy_B") rf_vec <- rnorm(60, 0.005, 0.001) perf1 <- PerformanceMeasures(example1, rf = rf_vec, FREQ = "Monthly") print(perf1) # Constant risk-free rate, annual data, risk aversion = 4 example2 <- matrix(rnorm(100, 0.10, 0.15), ncol = 2) colnames(example2) <- c("Strategy_C", "Strategy_D") perf2 <- PerformanceMeasures(example2, rf = 0.02, risk_aversion = 4, FREQ = "Annual") print(perf2)set.seed(123) # Time-varying risk-free rate, monthly data, default risk aversion = 3 example1 <- matrix(rnorm(120, 0.01, 0.04), ncol = 2) colnames(example1) <- c("Strategy_A", "Strategy_B") rf_vec <- rnorm(60, 0.005, 0.001) perf1 <- PerformanceMeasures(example1, rf = rf_vec, FREQ = "Monthly") print(perf1) # Constant risk-free rate, annual data, risk aversion = 4 example2 <- matrix(rnorm(100, 0.10, 0.15), ncol = 2) colnames(example2) <- c("Strategy_C", "Strategy_D") perf2 <- PerformanceMeasures(example2, rf = 0.02, risk_aversion = 4, FREQ = "Annual") print(perf2)
This function performs a Weighted Least Squares regression with optional sign constraints on the slope coefficients. It is useful for Return-Based Style Analysis (RBSA) where factor exposures are required to be non-negative or non-positive.
solve_cwls(y, X, w, slope_sign_constraint, intercept = TRUE)solve_cwls(y, X, w, slope_sign_constraint, intercept = TRUE)
y |
A numeric vector of the dependent variable. |
X |
A numeric matrix or data.frame of independent variables. |
w |
A numeric vector of non-negative weights for the observations. |
slope_sign_constraint |
A numeric vector of length |
intercept |
Logical; if |
The function minimizes the weighted sum of squared residuals:
subject to the sign constraints specified in slope_sign_constraint.
If intercept = TRUE, an intercept column is prepended to the design matrix.
The intercept is always unconstrained. If intercept = FALSE, the design matrix
is used as supplied and constraints are applied to all columns as indexed.
The optimization is solved via Quadratic Programming:
where is a diagonal matrix of weights.
A list containing:
coefficients: Vector of estimated constrained coefficients.
fitted: The fitted values ().
residuals: The residuals ().
weighted_r_squared: The weighted R-squared of the fit.
obj_value: The value of the quadratic objective function at the solution.
Pavlos Pantatosakis [email protected], Georgios Skoulakis [email protected]
# Simulate fund returns and 3 factor benchmarks set.seed(123) x_factors <- matrix(rnorm(300), ncol = 3) colnames(x_factors) <- c("Market", "Value", "Size") y_fund <- 0.5 * x_factors[,1] + 0.2 * x_factors[,2] + rnorm(100, 0, 0.1) # Define weights (e.g., more weight on recent data) obs_weights <- seq(0.1, 1, length.out = 100) # Constraint: all slopes must be non-negative constraints <- c(1, 1, 1) fit <- solve_cwls(y_fund, x_factors, obs_weights, constraints) print(fit$coefficients) # Unconstrained slope signs reduces to base R lm(). fit_uncon <- solve_cwls(y_fund, x_factors, obs_weights, rep(0, 3)) all.equal(as.numeric(fit_uncon$coefficients), as.numeric(coef(lm(y_fund ~ x_factors, weights = obs_weights)))) # Unconstrained slope signs with equal weights reduces to Ordinary Least Squares (OLS). fit_ols <- solve_cwls(y_fund, x_factors, rep(1,100), rep(0,3)) fit_ols$coefficients coef(lm(y_fund ~ x_factors))# Simulate fund returns and 3 factor benchmarks set.seed(123) x_factors <- matrix(rnorm(300), ncol = 3) colnames(x_factors) <- c("Market", "Value", "Size") y_fund <- 0.5 * x_factors[,1] + 0.2 * x_factors[,2] + rnorm(100, 0, 0.1) # Define weights (e.g., more weight on recent data) obs_weights <- seq(0.1, 1, length.out = 100) # Constraint: all slopes must be non-negative constraints <- c(1, 1, 1) fit <- solve_cwls(y_fund, x_factors, obs_weights, constraints) print(fit$coefficients) # Unconstrained slope signs reduces to base R lm(). fit_uncon <- solve_cwls(y_fund, x_factors, obs_weights, rep(0, 3)) all.equal(as.numeric(fit_uncon$coefficients), as.numeric(coef(lm(y_fund ~ x_factors, weights = obs_weights)))) # Unconstrained slope signs with equal weights reduces to Ordinary Least Squares (OLS). fit_ols <- solve_cwls(y_fund, x_factors, rep(1,100), rep(0,3)) fit_ols$coefficients coef(lm(y_fund ~ x_factors))
Solves a mean-variance optimization problem to find the optimal risky asset weights that maximize a quadratic utility function, subject to individual asset bounds and risk-free asset allocation constraints. The risk-free asset absorbs any wealth not invested in risky assets, with its weight determined implicitly as the residual of the risky weights.
solve_mean_variance( mu, Sigma, risky_lb, risky_ub, rf_lb, rf_ub, risk_aversion = 3 )solve_mean_variance( mu, Sigma, risky_lb, risky_ub, rf_lb, rf_ub, risk_aversion = 3 )
mu |
A numeric vector of length |
Sigma |
A symmetric positive definite |
risky_lb |
A numeric vector of length |
risky_ub |
A numeric vector of length |
rf_lb |
A single numeric value specifying the minimum weight of the
risk-free asset. Must satisfy |
rf_ub |
A single numeric value specifying the maximum weight of the risk-free asset. |
risk_aversion |
A single positive numeric value representing the
investor's risk aversion coefficient ( |
Utility Function
The investor maximizes a mean-variance utility defined over the full
portfolio, including the risk-free asset. Let be the
vector of risky weights. The risk-free weight is the
implicit residual:
The full portfolio utility is:
Expanding the risk-free term:
Since is supplied as excess returns
and is a constant that does not affect the optimizer solution,
the effective objective passed to the solver is:
QP Mapping
Internally, the problem is mapped to the quadprog::solve.QP
minimization framework:
where and .
Constraints
Asset-specific bounds:
Risk-free lower bound:
Risk-free upper bound:
A list containing:
weights: Full weight vector for all assets, including
the risk-free asset.
risky_weights: Vector of optimal risky asset weights
.
rf_weight: Scalar weight allocated to the risk-free asset:
.
expected_return: Expected excess return of the
risky portfolio .
variance: Portfolio variance .
volatility: Portfolio volatility .
Pavlos Pantatosakis [email protected], Georgios Skoulakis [email protected]
# 1. Define Inputs (3 Assets: Stocks, Bonds, Real Estate) # Note: mu must be excess returns (raw return - rf), computed upstream example_mu <- c(Stocks = 0.08, Bonds = 0.04, RealEstate = 0.06) example_Sigma <- matrix(c(0.040, 0.005, 0.015, 0.005, 0.010, 0.002, 0.015, 0.002, 0.025), nrow = 3, byrow = TRUE) colnames(example_Sigma) <- rownames(example_Sigma) <- names(example_mu) # 2. Set Constraints # No short-selling (lb = 0), max 60% in any single risky asset (ub = 0.6) example_lb <- rep(0, 3) example_ub <- rep(0.6, 3) # Risk-free asset must be between 10% and 30% of total wealth example_rf_low <- 0.10 example_rf_high <- 0.30 # 3. Solve for gamma = 3 example_result <- solve_mean_variance(example_mu, example_Sigma, example_lb, example_ub, example_rf_low, example_rf_high, risk_aversion = 3) # 4. View Results print(example_result$weights) cat("Portfolio Volatility:", round(example_result$volatility, 4), "\n")# 1. Define Inputs (3 Assets: Stocks, Bonds, Real Estate) # Note: mu must be excess returns (raw return - rf), computed upstream example_mu <- c(Stocks = 0.08, Bonds = 0.04, RealEstate = 0.06) example_Sigma <- matrix(c(0.040, 0.005, 0.015, 0.005, 0.010, 0.002, 0.015, 0.002, 0.025), nrow = 3, byrow = TRUE) colnames(example_Sigma) <- rownames(example_Sigma) <- names(example_mu) # 2. Set Constraints # No short-selling (lb = 0), max 60% in any single risky asset (ub = 0.6) example_lb <- rep(0, 3) example_ub <- rep(0.6, 3) # Risk-free asset must be between 10% and 30% of total wealth example_rf_low <- 0.10 example_rf_high <- 0.30 # 3. Solve for gamma = 3 example_result <- solve_mean_variance(example_mu, example_Sigma, example_lb, example_ub, example_rf_low, example_rf_high, risk_aversion = 3) # 4. View Results print(example_result$weights) cat("Portfolio Volatility:", round(example_result$volatility, 4), "\n")
The WeightCraft package provides functions for quantitative portfolio management, covering return and risk estimation, mean-variance optimization, and performance evaluation.
| Package: | WeightCraft |
| Type: | Package |
| Version: | 1.0.0 |
| Date: | 2026-03-22 |
| License: | GPL-3 |
Pavlos Pantatosakis [email protected]
Pavlos Pantatosakis [email protected], Georgios Skoulakis [email protected]