[Returnanalytics-commits] r2496 - in pkg/PortfolioAnalytics: . R man sandbox

noreply at r-forge.r-project.org noreply at r-forge.r-project.org
Thu Jul 4 00:49:48 CEST 2013


Author: rossbennett34
Date: 2013-07-04 00:49:48 +0200 (Thu, 04 Jul 2013)
New Revision: 2496

Added:
   pkg/PortfolioAnalytics/man/group_fail.Rd
   pkg/PortfolioAnalytics/sandbox/testing_rp_transform.R
Modified:
   pkg/PortfolioAnalytics/NAMESPACE
   pkg/PortfolioAnalytics/R/constraint_fn_map.R
   pkg/PortfolioAnalytics/man/rp_transform.Rd
Log:
Updating rp_transform to include group constraints. Added testing script and updated documentation.

Modified: pkg/PortfolioAnalytics/NAMESPACE
===================================================================
--- pkg/PortfolioAnalytics/NAMESPACE	2013-07-03 10:42:15 UTC (rev 2495)
+++ pkg/PortfolioAnalytics/NAMESPACE	2013-07-03 22:49:48 UTC (rev 2496)
@@ -27,6 +27,7 @@
 export(generatesequence)
 export(get.constraints)
 export(group_constraint)
+export(group_fail)
 export(is.constraint)
 export(is.objective)
 export(is.portfolio)

Modified: pkg/PortfolioAnalytics/R/constraint_fn_map.R
===================================================================
--- pkg/PortfolioAnalytics/R/constraint_fn_map.R	2013-07-03 10:42:15 UTC (rev 2495)
+++ pkg/PortfolioAnalytics/R/constraint_fn_map.R	2013-07-03 22:49:48 UTC (rev 2496)
@@ -196,11 +196,11 @@
   return(weights)
 }
 
-#' Transform a weights vector to min_sum/max_sum leverage and min/max box constraints using logic from randomize_portfolio
+#' Transform a weights vector to satisfy leverage, box, and group constraints using logic from \code{randomize_portfolio}
 #' 
 #' This function uses a block of code from \code{\link{randomize_portfolio}} 
 #' to transform the weight vector if either the weight_sum (leverage) 
-#' constraints or box constraints are violated.
+#' constraints, box constraints, or group constraints are violated.
 #' The resulting weights vector might be quite different from the original weights vector.
 #' 
 #' @param w weights vector to be transformed
@@ -208,18 +208,28 @@
 #' @param max_sum maximum sum of all asset weights, default 1.01
 #' @param min numeric or named vector specifying minimum weight box constraints
 #' @param max numeric or named vector specifying maximum weight box constraints
+#' @param groups vector specifying the groups of the assets
+#' @param cLO numeric or vector specifying minimum weight group constraints
+#' @param cUP numeric or vector specifying minimum weight group constraints
 #' @param max_permutations integer: maximum number of iterations to try for a valid portfolio, default 200
 #' @return named weighting vector
 #' @author Peter Carl, Brian G. Peterson, Ross Bennett (based on an idea by Pat Burns)
 #' @export
-rp_transform <- function(w, min_sum=0.99, max_sum=1.01, min, max, max_permutations=200){
+rp_transform <- function(w, min_sum=0.99, max_sum=1.01, min, max, groups, cLO, cUP, max_permutations=200){
   # Uses logic from randomize_portfolio to "normalize" a weights vector to 
-  # satisfy min_sum and max_sum while account for min and max box constraints
+  # satisfy min_sum and max_sum while accounting for box and group constraints
   # Modified from randomize_portfolio to trigger the while loops if any weights 
   # violate min or max box constraints. A weights vector would not be transformed
   # in randomize_portfolio if min_sum and max_sum were satisfied, but the
   # min/max constraints were violated.
   
+  # return w if all constraints are satisfied
+  if((sum(w) >= min_sum & sum(w) <= max_sum) & 
+       (all(w >= min) & all(w <= max)) & 
+       (all(!group_fail(weights, groups, cLO, cUP)))){
+    return(w)
+  }
+  
   # generate a sequence of weights based on min/max box constraints
   weight_seq <- generatesequence(min=min(min), max=max(max), by=0.005)
   
@@ -229,8 +239,8 @@
   # create a temporary weights vector that will be modified in the while loops
   tmp_w <- w
   
-  # while portfolio is outside min_sum/max_sum or min/max and we have not reached max_permutations
-  while ((sum(tmp_w) <= min_sum | sum(tmp_w) >= max_sum | any(tmp_w < min) | any(tmp_w > max)) & permutations <= max_permutations) {
+  # while portfolio is outside min_sum/max_sum or min/max or group constraints and we have not reached max_permutations
+  while ((sum(tmp_w) <= min_sum | sum(tmp_w) >= max_sum | any(tmp_w < min) | any(tmp_w > max) | any(group_fail(tmp_w, groups, cLO, cUP))) & permutations <= max_permutations) {
     permutations = permutations + 1
     # check our box constraints on total portfolio weight
     # reduce(increase) total portfolio size till you get a match
@@ -240,8 +250,8 @@
     
     random_index <- sample(1:length(tmp_w), length(tmp_w))
     i = 1
-    # while sum of weights is less than min_sum or min/max box constraint is violated
-    while ((sum(tmp_w) <= min_sum | any(tmp_w < min) | any(tmp_w > max)) & i <= length(tmp_w)) {
+    # while sum of weights is less than min_sum or min/max box or group constraint is violated
+    while ((sum(tmp_w) <= min_sum | any(tmp_w < min) | any(tmp_w > max) | any(group_fail(tmp_w, groups, cLO, cUP))) & i <= length(tmp_w)) {
       # randomly permute and increase a random portfolio element
       cur_index <- random_index[i]
       cur_val <- tmp_w[cur_index]
@@ -256,8 +266,11 @@
       }
       i=i+1 # increment our counter
     } # end increase loop
-    # while sum of weights is greater than max_sum or min/max box constraint is violated
-    while ((sum(tmp_w) >= max_sum | any(tmp_w < min) | any(tmp_w > max)) & i <= length(tmp_w)) {
+    # need to reset i here otherwise the decreasing loop will be ignored
+    # group_fail does not test for direction of violation, just that group constraints were violated
+    i = 1 
+    # while sum of weights is greater than max_sum or min/max box or group constraint is violated
+    while ((sum(tmp_w) >= max_sum | any(tmp_w < min) | any(tmp_w > max) | any(group_fail(tmp_w, groups, cLO, cUP))) & i <= length(tmp_w)) {
       # randomly permute and decrease a random portfolio element
       cur_index <- random_index[i]
       cur_val <- tmp_w[cur_index]
@@ -294,6 +307,40 @@
   return(portfolio)
 }
 
+#' Test if group constraints have been violated
+#' 
+#' The function loops through each group and tests if cLO or cUP have been violated
+#' for the given group. This is a helper function for \code{\link{rp_transform}}.
+#' 
+#' @param weights weights vector to test
+#' @param groups vector specifying the groups of the assets
+#' @param cLO numeric or vector specifying minimum weight group constraints
+#' @param cUP numeric or vector specifying minimum weight group constraints
+#' @return logical vector: TRUE if group constraints are violated for a given group
+#' @author Ross Bennett
+#' @export
+group_fail <- function(weights, groups, cLO, cUP){
+  # return FALSE if groups, cLO, or cUP is NULL
+  if(is.null(groups) | is.null(cLO) | is.null(cUP)) return(FALSE)
+  
+  n.groups <- length(groups)
+  group_fail <- vector(mode="logical", length=n.groups)
+  k <- 1
+  l <- 0
+  for(i in 1:n.groups){
+    j <- groups[i]
+    tmp.w <- weights[k:(l+j)]
+    grp.min <- cLO[i]
+    grp.max <- cUP[i]
+    # return TRUE if grp.min or grp.max is violated
+    group_fail[i] <- ( sum(tmp.w) < grp.min | sum(tmp.w) > grp.max )
+    k <- k + j
+    l <- k - 1
+  }
+  # returns logical vector of groups. TRUE if either cLO or cUP is violated
+  return(group_fail)
+}
+
 # test
 # w <- c(0.1, 0.25, 0.3, 0.15, 0.05, 0.15)
 # min <- rep(0.1, length(w))

Added: pkg/PortfolioAnalytics/man/group_fail.Rd
===================================================================
--- pkg/PortfolioAnalytics/man/group_fail.Rd	                        (rev 0)
+++ pkg/PortfolioAnalytics/man/group_fail.Rd	2013-07-03 22:49:48 UTC (rev 2496)
@@ -0,0 +1,30 @@
+\name{group_fail}
+\alias{group_fail}
+\title{Test if group constraints have been violated}
+\usage{
+  group_fail(weights, groups, cLO, cUP)
+}
+\arguments{
+  \item{weights}{weights vector to test}
+
+  \item{groups}{vector specifying the groups of the assets}
+
+  \item{cLO}{numeric or vector specifying minimum weight
+  group constraints}
+
+  \item{cUP}{numeric or vector specifying minimum weight
+  group constraints}
+}
+\value{
+  logical vector: TRUE if group constraints are violated
+  for a given group
+}
+\description{
+  The function loops through each group and tests if cLO or
+  cUP have been violated for the given group. This is a
+  helper function for \code{\link{rp_transform}}.
+}
+\author{
+  Ross Bennett
+}
+

Modified: pkg/PortfolioAnalytics/man/rp_transform.Rd
===================================================================
--- pkg/PortfolioAnalytics/man/rp_transform.Rd	2013-07-03 10:42:15 UTC (rev 2495)
+++ pkg/PortfolioAnalytics/man/rp_transform.Rd	2013-07-03 22:49:48 UTC (rev 2496)
@@ -1,9 +1,9 @@
 \name{rp_transform}
 \alias{rp_transform}
-\title{Transform a weights vector to min_sum/max_sum leverage and min/max box constraints using logic from randomize_portfolio}
+\title{Transform a weights vector to satisfy leverage, box, and group constraints using logic from \code{randomize_portfolio}}
 \usage{
   rp_transform(w, min_sum = 0.99, max_sum = 1.01, min, max,
-    max_permutations = 200)
+    groups, cLO, cUP, max_permutations = 200)
 }
 \arguments{
   \item{w}{weights vector to be transformed}
@@ -20,6 +20,14 @@
   \item{max}{numeric or named vector specifying maximum
   weight box constraints}
 
+  \item{groups}{vector specifying the groups of the assets}
+
+  \item{cLO}{numeric or vector specifying minimum weight
+  group constraints}
+
+  \item{cUP}{numeric or vector specifying minimum weight
+  group constraints}
+
   \item{max_permutations}{integer: maximum number of
   iterations to try for a valid portfolio, default 200}
 }
@@ -29,10 +37,10 @@
 \description{
   This function uses a block of code from
   \code{\link{randomize_portfolio}} to transform the weight
-  vector if either the weight_sum (leverage) constraints or
-  box constraints are violated. The resulting weights
-  vector might be quite different from the original weights
-  vector.
+  vector if either the weight_sum (leverage) constraints,
+  box constraints, or group constraints are violated. The
+  resulting weights vector might be quite different from
+  the original weights vector.
 }
 \author{
   Peter Carl, Brian G. Peterson, Ross Bennett (based on an

Added: pkg/PortfolioAnalytics/sandbox/testing_rp_transform.R
===================================================================
--- pkg/PortfolioAnalytics/sandbox/testing_rp_transform.R	                        (rev 0)
+++ pkg/PortfolioAnalytics/sandbox/testing_rp_transform.R	2013-07-03 22:49:48 UTC (rev 2496)
@@ -0,0 +1,96 @@
+library(PortfolioAnalytics)
+
+# Testing to see how rp_transform handles group constraints
+
+##### EX1 #####
+# first group exceeds cUP
+weights <- c(0.15, 0.35, 0.50)
+sum(weights)
+
+groups <- c(2, 1)
+cLO <- c(0.1, 0.10)
+cUP <- c(0.45, 0.8)
+min_sum <- 0.99
+max_sum <- 1.01
+min <- rep(0.05, length(weights))
+max <- rep(0.65, length(weights))
+
+group_fail(weights, groups, cLO, cUP)
+
+w <- rp_transform(weights, min_sum, max_sum, min, max, groups, cLO, cUP, 200)
+w
+group_fail(w, groups, cLO, cUP)
+
+##### EX2 #####
+# The assets are grouped into 3 groups of 2
+# The sum of the weights for the first group assets must be between 0.05 and 0.35
+# The sum of the weights for the second group of assets must be between 0.10 and 0.45
+# The sum of the weights for the last group of assets must be between 0.05 and 0.25
+
+# first group exceeds cUP
+weights <- c(0.15, 0.30, 0.15, 0.25, 0.05, 0.10)
+sum(weights)
+
+groups <- c(2, 2, 2)
+cLO <- c(0.05, 0.10, 0.05)
+cUP <- c(0.4, 0.45, 0.35)
+min_sum <- 0.99
+max_sum <- 1.01
+min <- rep(0.05, length(weights))
+max <- rep(0.65, length(weights))
+
+
+group_fail(weights, groups, cLO, cUP)
+
+# groups is NULL and box and leverage constraints are satisfied so this should
+# just return the original weights vector
+w <- rp_transform(weights, min_sum, max_sum, min, max, groups=NULL, cLO, cUP, 500)
+w
+
+# The first group exceeds cUP so the weights vector should be modified
+w <- rp_transform(weights, min_sum, max_sum, min, max, groups, cLO, cUP, 1000)
+w
+group_fail(w, groups, cLO, cUP)
+
+##### Ex3 #####
+# The second group is below cLO and the third weight is below min
+weights <- c(0.15, 0.25, 0.08, 0.2, 0.22, 0.10)
+sum(weights)
+
+groups <- c(2, 1, 3)
+cLO <- c(0.05, 0.10, 0.05)
+cUP <- c(0.4, 0.45, 0.65)
+min_sum <- 0.99
+max_sum <- 1.01
+min <- rep(0.1, length(weights))
+max <- rep(0.65, length(weights))
+
+
+group_fail(weights, groups, cLO, cUP)
+
+w <- rp_transform(weights, min_sum, max_sum, min, max, groups, cLO, cUP, 500)
+w
+group_fail(w, groups, cLO, cUP)
+
+##### Ex4 #####
+# The second group is above cUP and the fourth group is below cLO
+weights <- c(0.06, 0.1, 0.07, 0.2, 0.22, 0.10, 0.05, 0.08, 0.05, 0.04, 0.03)
+sum(weights[1:2])
+sum(weights[3:6])
+sum(weights[7:10])
+sum(weights[10:11])
+sum(weights)
+
+groups <- c(2, 4, 3, 2)
+cLO <- c(0.05, 0.10, 0.05, 0.08)
+cUP <- c(0.4, 0.55, 0.65, 0.45)
+min_sum <- 0.99
+max_sum <- 1.01
+min <- rep(0.05, length(weights))
+max <- rep(0.65, length(weights))
+
+group_fail(weights, groups, cLO, cUP)
+
+# Note that this was typically not working with max_permutations=200
+# Relax constraints or increase max_permutations
+rp_transform(weights, min_sum, max_sum, min, max, groups, cLO, cUP, 1000)



More information about the Returnanalytics-commits mailing list