[Returnanalytics-commits] r2478 - pkg/PortfolioAnalytics/R
noreply at r-forge.r-project.org
noreply at r-forge.r-project.org
Mon Jul 1 00:13:27 CEST 2013
Author: rossbennett34
Date: 2013-07-01 00:13:27 +0200 (Mon, 01 Jul 2013)
New Revision: 2478
Added:
pkg/PortfolioAnalytics/R/constraint_fn_map.R
Removed:
pkg/PortfolioAnalytics/R/constraint_fnMap.R
Log:
adding functions to transform weight for box_constraint, group_constraint, and weight_sum_constraint. changed naming of function to be more consistent
Deleted: pkg/PortfolioAnalytics/R/constraint_fnMap.R
===================================================================
--- pkg/PortfolioAnalytics/R/constraint_fnMap.R 2013-06-30 18:38:16 UTC (rev 2477)
+++ pkg/PortfolioAnalytics/R/constraint_fnMap.R 2013-06-30 22:13:27 UTC (rev 2478)
@@ -1,102 +0,0 @@
-#' Constraint mapping function
-#'
-#' The purpose of the mapping function is to transform a weights vector
-#' that does not meet all the constraints into a weights vector that
-#' does meet the constraints, if one exists, hopefully with a minimum
-#' of transformation.
-
-#' I think our first step should be to test each constraint type, in
-#' some sort of hierarchy, starting with box constraints (almost all
-#' solvers support box constraints, of course), since some of the other
-#' transformations will violate the box constraints, and we'll need to
-#' transform back again.
-#'
-#' @param weights vector of weights
-#' @param portfolio object of class portfolio
-#' @author Ross Bennett
-#' @export
-constraint_fnMap <- function(weights, portfolio) {
-
- if (!is.portfolio(portfolio)) {
- stop("Portfolio passed in is not of class portfolio")
- }
-
- for(constraint in portfolio$constraints) {
- # Check for enabled constraints
- if(constraint$enabled){
-
- ## box constraint
- if(inherits(constraint, "box_constraint")){
- # TODO
- } # box constraint
-
- ## weight_sum constraint
- if(inherits(constraint, "weight_sum_constraint")){
- min_sum <- constraint$min_sum
- max_sum <- constraint$max_sum
- print(min_sum)
- print(max_sum)
- # normalize to max_sum
- if(sum(weights) > max_sum) { weights <- (max_sum / sum(weights)) * weights }
- # normalize to min_sum
- if(sum(weights) < min_sum) { weights <- (min_sum / sum(weights)) * weights }
- } # weight_sum constraint
-
- ## group constraint
- if(inherits(constraint, "group_constraint")){
- groups <- constraint$groups
- cLO <- constraint$cLO
- cUP <- constraint$cUP
- print(groups)
- print(cLO)
- print(cUP)
- n.groups <- length(groups)
- k <- 1
- l <- 0
- for(i in 1:n.groups){
- j <- groups[i]
- tmp.w <- weights[k:(l+j)]
- # normalize weights for a given group that sum to less than specified group min
- grp.min <- cLO[i]
- if(sum(tmp.w) < grp.min) {
- weights[k:(l+j)] <- (grp.min / sum(tmp.w)) * tmp.w
- }
- # normalize weights for a given group that sum to greater than specified group max
- grp.max <- cUP[i]
- if(sum(tmp.w) > grp.max) {
- weights[k:(l+j)] <- (grp.max / sum(tmp.w)) * tmp.w
- }
- k <- k + j
- l <- k - 1
- }
- # Normalizing the weights inside the groups changes the sum of the weights.
- # Should normalizing the sum of weights take place here or somewhere else?
- # Re-normalizing the weights will get us *close* to satisfying the group constraints.
- # Maybe then add a penalty in constrained objective for violation of group constraints?
- } # group constraint
-
- # Turnover constraints
- # TODO
-
- # Diversification constraints
- # TODO
- }
- }
- return(weights)
-}
-
-# library(PortfolioAnalytics)
-# data(edhec)
-# ret <- edhec[, 1:4]
-# funds <- colnames(ret)
-#
-# pspec <- portfolio.spec(assets=funds)
-# pspec <- add.constraint(portfolio=pspec, type="weight_sum", min_sum=0.99, max_sum=1.01, enabled=TRUE)
-# pspec <- add.constraint(portfolio=pspec, type="box", enabled=TRUE)
-# pspec <- add.constraint(portfolio=pspec, type="group", groups=c(2,2), group_min=c(0.1, 0.2), group_max=c(0.3, 0.8), enabled=TRUE)
-#
-# weights <- c(0.15, 0.2, 0.15, 0.5)
-# sum(weights)
-#
-# (w <- constraint_fnMap(weights, pspec))
-# sum(w)
Added: pkg/PortfolioAnalytics/R/constraint_fn_map.R
===================================================================
--- pkg/PortfolioAnalytics/R/constraint_fn_map.R (rev 0)
+++ pkg/PortfolioAnalytics/R/constraint_fn_map.R 2013-06-30 22:13:27 UTC (rev 2478)
@@ -0,0 +1,181 @@
+#' Constraint mapping function
+#'
+#' The purpose of the mapping function is to transform a weights vector
+#' that does not meet all the constraints into a weights vector that
+#' does meet the constraints, if one exists, hopefully with a minimum
+#' of transformation.
+
+#' I think our first step should be to test each constraint type, in
+#' some sort of hierarchy, starting with box constraints (almost all
+#' solvers support box constraints, of course), since some of the other
+#' transformations will violate the box constraints, and we'll need to
+#' transform back again.
+#'
+#' @param weights vector of weights
+#' @param portfolio object of class portfolio
+#' @author Ross Bennett
+#' @export
+constraint_fn_map <- function(weights, portfolio) {
+
+ if (!is.portfolio(portfolio)) {
+ stop("Portfolio passed in is not of class portfolio")
+ }
+
+ # This is in a loop so the order of transformation depends on how the constraints are added by the user.
+ # Maybe take this out of a loop because the order of transformation is important
+ for(constraint in portfolio$constraints) {
+ # Check for enabled constraints
+ if(constraint$enabled){
+
+ ## box constraint
+ if(inherits(constraint, "box_constraint")){
+ min <- constraint$min
+ max <- constraint$max
+
+ w <- txfrm_box_constraint(weights=weights, min=min, max=max)
+
+ # The transformation will likely change the sum of weights and violate min_sum or max_sum
+ # Should we normalize here by transforming the entire weights vector?
+ # Normalizing by transforming the entire weights may violate min and max, but will get us *close*
+ } # end box_constraint transformation
+
+ ## weight_sum constraint
+ if(inherits(constraint, "weight_sum_constraint")){
+ min_sum <- constraint$min_sum
+ max_sum <- constraint$max_sum
+ # print(min_sum)
+ # print(max_sum)
+
+ w <- txfrm_weight_sum_constraint(weights=weights, min_sum=min_sum, max_sum=max_sum)
+
+ } # end weight_sum constraint transformation
+
+ ## group constraint
+ if(inherits(constraint, "group_constraint")){
+ groups <- constraint$groups
+ cLO <- constraint$cLO
+ cUP <- constraint$cUP
+ # print(groups)
+ # print(cLO)
+ # print(cUP)
+
+ w <- txfrm_group_constraint(weights=weights, groups=groups, cLO=cLO, cUP=cUP)
+
+ # Normalizing the weights inside the groups changes the sum of the weights.
+ # Should normalizing the sum of weights take place here or somewhere else?
+ # Re-normalizing the weights will get us *close* to satisfying the group constraints.
+ # Maybe then add a penalty in constrained objective for violation of group constraints?
+ } # end group_constraint transformation
+
+ # Turnover constraints
+ # TODO
+
+ # Diversification constraints
+ # TODO
+ }
+ }
+ return(w)
+}
+
+#' Transform weights that violate min or max box constraints
+#'
+#' This is a helper function called inside constraint_fnMap to transform the weights vector to satisfy box constraints.
+#'
+#' @param weights vector of weights
+#' @param min vector of minimum asset weights from box constraints
+#' @param max vector of maximum asset weights from box constraints
+#' @author Ross Bennett
+#' @export
+txfrm_box_constraint <- function(weights, min, max) {
+ # 1. Check if any elements of the weights vector violate min or max
+ # 2. If min or max is violated, then set those weights equal to their respective min or max values
+ # The length of the weights vector must be equal to the length of min and max vectors so that an element-by-element comparison is done
+ if(any(weights < min) | any(weights > max)){
+ # get the index of elements in the weights vector that violate min
+ idx.min <- which(weights < min)
+ # set those elements in the weights vector equal to their respective min
+ weights[idx.min] = min[idx.min]
+ # print(weights)
+ # get the index of elements in the weights vector that violate max
+ idx.max <- which(weights > max)
+ # set those elements in the weights vector equal to their respective max
+ weights[idx.max] = max[idx.max]
+ # print(weights)
+ # The transformation will likely change the sum of weights and violate min_sum or max_sum
+ # Should we normalize here by transforming the entire weights vector?
+ # Normalizing by transforming the entire weights may violate min and max, but will get us *close*
+ # if(sum(weights) < min_sum) weights <- min_sum / sum(weights) * weights
+ # if(sum(weights) > max_sum) weights <- max_sum / sum(weights) * weights
+ }
+ return(weights)
+}
+
+#' Transform weights that violate group constraints
+#'
+#' This is a helper function called inside constraint_fnMap to transform the weights vector to satisfy group constraints.
+#'
+#' @param weights vector of weights
+#' @param groups vector of groups
+#' @param cLO vector of minimum group weights from group constraints
+#' @param cUP vector of maximum group weights from group constraints
+#' @author Ross Bennett
+#' @export
+txfrm_group_constraint <- function(weights, groups, cLO, cUP){
+ n.groups <- length(groups)
+ k <- 1
+ l <- 0
+ for(i in 1:n.groups){
+ j <- groups[i]
+ tmp.w <- weights[k:(l+j)]
+ # normalize weights for a given group that sum to less than specified group min
+ grp.min <- cLO[i]
+ if(sum(tmp.w) < grp.min) {
+ weights[k:(l+j)] <- (grp.min / sum(tmp.w)) * tmp.w
+ }
+ # normalize weights for a given group that sum to greater than specified group max
+ grp.max <- cUP[i]
+ if(sum(tmp.w) > grp.max) {
+ weights[k:(l+j)] <- (grp.max / sum(tmp.w)) * tmp.w
+ }
+ k <- k + j
+ l <- k - 1
+ }
+ # Normalizing the weights inside the groups changes the sum of the weights.
+ # Should normalizing the sum of weights take place here or somewhere else?
+ # Re-normalizing the weights will get us *close* to satisfying the group constraints.
+ # Maybe then add a penalty in constrained objective for violation of group constraints?
+ return(weights)
+}
+
+#' Transform weights that violate weight_sum constraints
+#'
+#' This is a helper function called inside constraint_fnMap to transform the weights vector to satisfy weight_sum constraints.
+#'
+#' @param weights vector of weights
+#' @param min_sum minimum sum of asset weights
+#' @param max_sum maximum sum of asset weights
+#' @author Ross Bennett
+#' @export
+txfrm_weight_sum_constraint <- function(weights, min_sum, max_sum){
+ # normalize to max_sum
+ if(sum(weights) > max_sum) { weights <- (max_sum / sum(weights)) * weights }
+ # normalize to min_sum
+ if(sum(weights) < min_sum) { weights <- (min_sum / sum(weights)) * weights }
+ return(weights)
+}
+
+# library(PortfolioAnalytics)
+# data(edhec)
+# ret <- edhec[, 1:4]
+# funds <- colnames(ret)
+#
+# pspec <- portfolio.spec(assets=funds)
+# pspec <- add.constraint(portfolio=pspec, type="weight_sum", min_sum=0.99, max_sum=1.01, enabled=TRUE)
+# pspec <- add.constraint(portfolio=pspec, type="box", enabled=TRUE)
+# pspec <- add.constraint(portfolio=pspec, type="group", groups=c(2,2), group_min=c(0.1, 0.2), group_max=c(0.3, 0.8), enabled=TRUE)
+#
+# weights <- c(0.15, 0.2, 0.15, 0.5)
+# sum(weights)
+#
+# (w <- constraint_fn_map(weights, pspec))
+# sum(w)
More information about the Returnanalytics-commits
mailing list