12rp_transform2 <- function(weights,3min_sum,4max_sum,5min_box,6max_box,7groups=NULL,8cLO=NULL,9cUP=NULL,10max_pos=NULL,11group_pos=NULL,12max_pos_long=NULL,13max_pos_short=NULL,14leverage=NULL,15weight_seq=NULL,16max_permutations=200){17tmp_w <- weights1819# Set some reasonable default values20# Maybe I should leave these as NULL values and incorporate that into the21# checks22#if(is.null(min_sum)) min_sum <- 0.9923#if(is.null(max_sum)) max_sum <- 1.0124#if(is.null(min_box)) min_box <- rep(-Inf, length(tmp_w))25#if(is.null(max_box)) max_box <- rep(Inf, length(tmp_w))26if(is.null(max_pos)) max_pos <- length(tmp_w)27#if(is.null(max_poslong)) max_pos_long <- length(tmp_w)28#if(is.null(max_pos_short)) max_pos_short <- length(tmp_w)29#if(is.null(leverage)) leverage <- Inf3031# Generate a weight sequence, we should check for portfolio$weight_seq32if(is.null(weight_seq))33weight_seq <- generatesequence(min=min(min_box), max=max(max_box), by=0.002)3435# make sure there is a 0 in weight_seq if we have a position limit constraint36if((!is.null(max_pos) | !is.null(group_pos) | !is.null(max_pos_long) | !is.null(max_pos_short)) & !is.element(0, weight_seq)) weight_seq <- c(0, weight_seq)3738# Tolerance for "non-zero" definition for position limit constraints39tolerance <- .Machine$double.eps^0.54041# initialize the outer while loop42permutations <- 14344# while we have not reached max_permutations and the following constraints45# are violated:46# - min_sum47# - max_sum48# - leverage49# - max_pos, max_pos_long, max_pos_short50# - group5152# Do we want to check all constraints in here?53# Box constraints should be satisfied by construction so we should not need54# to check those here55while (( min_sum_fail(tmp_w, min_sum) |56max_sum_fail(tmp_w, max_sum) |57leverage_fail(tmp_w, leverage) |58pos_limit_fail(tmp_w, max_pos, max_pos_long, max_pos_short) |59any(group_fail(tmp_w, groups, cLO, cUP)) ) &60(permutations < max_permutations)) {6162# cat("permutation #:", permutations, "\n")63permutations <- permutations+16465# Reset tmp_w to original weights vector66# I'm not sure we want to do this here because it puts us back to where we67# started, but it seems to help with the position limit constraint68# tmp_w <- weights6970# Reset the random index based on the maximum position constraint71# This basically allows us to generate a portfolio of max_pos assets72# with the given constraints and then add assets with zero weight73random_index <- sample(1:length(tmp_w), max_pos)7475# Get the index values that are not in random_index and set them equal to 076full_index <- 1:length(tmp_w)77not_index <- setdiff(full_index, random_index)78tmp_w[not_index] <- 07980# min_sum violation81if(min_sum_fail(tmp_w, min_sum)){82tmp_w <- rp_increase(weights=tmp_w,83min_sum=min_sum,84max_box=max_box,85weight_seq=weight_seq)86}8788# max_sum violation89if(max_sum_fail(tmp_w, max_sum)){90tmp_w <- rp_decrease(weights=tmp_w,91max_sum=max_sum,92min_box=min_box,93weight_seq=weight_seq)94}959697# leverage violation98if(leverage_fail(tmp_w, leverage)){99tmp_w <- rp_decrease_leverage(weights=tmp_w,100max_box=max_box,101min_box=min_box,102leverage=leverage,103weight_seq=weight_seq)104}105106# position limit violation107if(pos_limit_fail(tmp_w, max_pos, max_pos_long, max_pos_short)){108tmp_w <- rp_position_limit(weights=tmp_w,109min_box=min_box,110max_box=max_box,111max_pos=max_pos,112max_pos_long=max_pos_long,113max_pos_short=max_pos_short,114weight_seq=weight_seq)115}116117# group violation118if(any(group_fail(tmp_w, groups, cLO, cUP, group_pos))){119n_groups <- length(groups)120for(j in 1:n_groups){121# index of the weights vector belonging to the jth group122j_idx <- groups[[j]]123# weights of the jth group124tmp_group_w <- tmp_w[j_idx]125126# May be easier to just make a recursive call and treat each group127# as a portfolio of weight vectors128tmp_w[j_idx] <- rp_transform2(weights=tmp_group_w,129min_sum=cLO[j],130max_sum=cUP[j],131min_box=min_box[j_idx],132max_box=max_box[j_idx],133group_pos=group_pos[j])134135# treat this as if min_sum were violated136# if(sum(tmp_group_w) < cLO[j]){137# tmp_w[j_idx] <- rp_increase(weights=tmp_group_w,138# min_sum=cLO[j],139# max_box=max_box[j_idx],140# weight_seq=weight_seq)141# }142143# treat this as if max_sum were violated144# if(sum(tmp_group_w) > cUP[j]){145# tmp_w[j_idx] <- rp_decrease(weights=tmp_group_w,146# max_sum=cUP[j],147# min_box=min_box[j_idx],148# weight_seq=weight_seq)149# }150}151} # end group violation loop152} # end final walk towards the edges153portfolio <- tmp_w154155colnames(portfolio) <- colnames(weights)156157# checks for infeasible portfolio158# Stop execution and return an error if an infeasible portfolio is created159# This will be useful in fn_map so that we can catch the error and take160# action (try again with more permutations, relax constraints, different161# method to normalize, etc.)162if (sum(portfolio) < min_sum | sum(portfolio) > max_sum){163portfolio <- weights164stop("Infeasible portfolio created, perhaps increase max_permutations and/or adjust your parameters.")165}166return(portfolio)167}168169rp_increase <- function(weights, min_sum, max_box, weight_seq){170# randomly permute and increase a random portfolio element if the sum of171# the weights is less than min_sum while respecting box constraints172173if(sum(weights) >= min_sum) return(weights)174175tmp_w <- weights176n_weights <- length(weights)177# random_index <- sample(1:length(weights), max_pos)178random_index <- sample(1:n_weights, n_weights)179i <- 1180while (sum(tmp_w) < min_sum & i <= n_weights) {181# print("min_sum violation loop")182183cur_index <- random_index[i]184cur_val <- tmp_w[cur_index]185tmp_seq <- weight_seq[(weight_seq > cur_val) & (weight_seq <= max_box[cur_index])]186n_tmp_seq <- length(tmp_seq)187if(n_tmp_seq > 1){188# randomly sample one of the larger weights189tmp_w[cur_index] <- tmp_seq[sample.int(n=n_tmp_seq, size=1L, replace=FALSE, prob=NULL)]190} else if(n_tmp_seq == 1){191tmp_w[cur_index] <- tmp_seq192}193i <- i + 1 # increment our counter194} # end increase loop195return(tmp_w)196}197198rp_decrease <- function(weights, max_sum, min_box, weight_seq){199# randomly permute and decrease a random portfolio element if the sum of200# the weights is greater than max_sum while respecting box constraints201202if(sum(weights) <= max_sum) return(weights)203204tmp_w <- weights205n_weights <- length(weights)206# random_index <- sample(1:length(weights), max_pos)207random_index <- sample(1:n_weights, n_weights)208209i <- 1210while (sum(tmp_w) > max_sum & i <= n_weights) {211# print("max_sum violation loop")212213cur_index <- random_index[i]214cur_val <- tmp_w[cur_index]215tmp_seq <- weight_seq[(weight_seq < cur_val) & (weight_seq >= min_box[cur_index])]216n_tmp_seq <- length(tmp_seq)217if(n_tmp_seq > 1){218tmp_w[cur_index] <- tmp_seq[sample.int(n=n_tmp_seq, size=1L, replace=FALSE, prob=NULL)]219} else if(n_tmp_seq == 1){220tmp_w[cur_index] <- tmp_seq221}222i <- i + 1 # increment our counter223} # end decrease loop224return(tmp_w)225}226227rp_decrease_leverage <- function(weights, max_box, min_box, leverage, weight_seq){228# randomly permute and increae decrease a random portfolio element229# according to leverage exposure while respecting box constraints230231tmp_w <- weights232n_weights <- length(weights)233# random_index <- sample(1:length(weights), max_pos)234random_index <- sample(1:n_weights, n_weights)235236# set counter to 1 for leverage violation loop237i <- 1238while (sum(abs(tmp_w)) > leverage & i <= length(tmp_w)) {239#print("leverage violation loop")240241cur_index <- random_index[i]242cur_val <- tmp_w[cur_index]243244tmp_seq <- NULL245# check the sign of the current value246if(cur_val < 0){247# if the current value is negative, we want to increase to lower248# sum(abs(weights)) while respecting uppper bound box constraint249tmp_seq <- weight_seq[(weight_seq > cur_val) & (weight_seq <= max_box[cur_index])]250} else if(cur_val > 0){251# if the current value is positive, we want to decrease to lower252# sum(abs(weights)) while respecting lower bound box constraint253tmp_seq <- weight_seq[(weight_seq < cur_val) & (weight_seq >= min_box[cur_index])]254}255# tmp_seq can be NULL if cur_val is zero256if(!is.null(tmp_seq)){257n_tmp_seq <- length(tmp_seq)258259if(n_tmp_seq > 1) {260# randomly sample one of the weights261tmp_w[cur_index] <- tmp_seq[sample.int(n=n_tmp_seq, size=1L, replace=FALSE, prob=NULL)]262} else if(n_tmp_seq == 1){263tmp_w[cur_index] <- tmp_seq264}265}266i <- i + 1 # increment our counter267} # end leverage violation loop268return(tmp_w)269}270271rp_position_limit <- function(weights, max_pos=NULL, max_pos_long=NULL, max_pos_short=NULL, min_box, max_box, weight_seq){272tmp_w <- weights273n_weights <- length(weights)274# random_index <- sample(1:length(weights), max_pos)275random_index <- sample(1:n_weights, n_weights)276277tolerance <- .Machine$double.eps^0.5278279# set counter to 1 for position limit violation loop280i <- 1281while ( pos_limit_fail(tmp_w, max_pos, max_pos_long, max_pos_short) & i <= length(tmp_w)) {282#print("position limit violation loop")283284cur_index <- random_index[i]285cur_val <- tmp_w[cur_index]286287if(!is.null(max_pos_long)){288# Check if max_pos_long is violated289# If max_pos_long is violated, we we grab a positive weight and set it290# to be between min_box and 0291if(sum(tmp_w > tolerance) > max_pos_long){292if(cur_val > tolerance){293# subset such that min_box_i <= weight_i <= 0294tmp_seq <- weight_seq[(weight_seq <= 0) & (weight_seq >= min_box[cur_index])]295n_tmp_seq <- length(tmp_seq)296if(n_tmp_seq > 1){297tmp_w[cur_index] <- tmp_seq[sample.int(n=n_tmp_seq, size=1L, replace=FALSE, prob=NULL)]298} else if(n_tmp_seq == 1){299tmp_w[cur_index] <- tmp_seq300}301}302} # end max_pos_long violation loop303}304305if(!is.null(max_pos_short)){306# Check if max_pos_short is violated307# If max_pos_short is violated, we grab a negative weight and set it308# to be between 0 and max_box309if(sum(tmp_w < tolerance) > max_pos_short){310if(cur_val < tolerance){311# subset such that 0 <= weight_i <= max_box_i312tmp_seq <- weight_seq[(weight_seq >= 0) & (weight_seq <= max_box[cur_index])]313n_tmp_seq <- length(tmp_seq)314if(n_tmp_seq > 1){315tmp_w[cur_index] <- tmp_seq[sample.int(n=n_tmp_seq, size=1L, replace=FALSE, prob=NULL)]316} else if(n_tmp_seq == 1){317tmp_w[cur_index] <- tmp_seq318}319}320} # end max_pos_short violation loop321}322i <- i + 1 # increment our counter323} # end position limit violation loop324return(tmp_w)325}326327328329