Multi-sided dice - random portfolio generator

Why Build a Random Portfolio Generator?

I don’t Like Benchmark Indexes

I am not a fan of benchmarking against widely available indexes.

Most anyone you ask will tell you that you should benchmark against an index because it is an objective measure of performance. It provides you with the “beta” that allows you to figure out if an investment manager delivers “alpha”.

Is this actually true? No. An index is a trading system in disguise. An index is created by establishing and implementing rules (inclusion, weighting, rebalancing, treatment of dividends and buy-backs, etc.). The index vendor constructed those rules much the same way as a systematic manager would design their trading strategy.

Don’t look too closely: You will find some of the decision-making that goes into an index is discretionary. Yep, there is a committee in the background picking stocks! Both the S&P 500 and the Dow 30 have an index selection committee that picks the firms that best represents the goals of the index. Not really objective.

What Else Can You Use?

If your task is to evaluate any kind of active manager’s performance, you are trying to determine if he or she has skill or luck. The best way to do that, in my opinion, is to compare the actual manager to a random version of him or herself.

Here’s what I mean. Let’s say a manager states the basics of his strategy as follows:

We select a concentrated portfolio of stocks from the S&P 500 universe. We are fully invested at all times, long only, and do not use leverage. Our portfolio holds 20 positions. We never open a position with more than 10%  or less than 2.5% of our capital.

Instead of using the manager’s skill or process, we build thousands of portfolios each month within the stated constraints and see how they perform versus our manager. If the manager significantly out-performs our random portfolios we know they add value, regardless how they do relative to the S&P 500 index itself.

Hedge Fund Selection

In my case, I put together portfolios of hedge fund and managed futures strategies. I can use the same process to evaluate my own approach to selecting managers: I compare the portfolios I create following my portfolio construction methodology with random portfolios drawn from the same universe. By choosing which aspects of the portfolio construction process are controlled or random, I can explore the impact of each aspect on the final portfolios I create.

Random Portfolio Generator

Using the random portfolio generator presented below, you can create thousands of random portfolios. You can represent pretty much any equity strategy (long only, short only, bear bias, long bias, market neutral, etc.) and any fund-of-fund portfolio construction technique.

This is a hedge fund hack that uses a Monte Carlo approach to evaluating a manager’s performance.

Basic Concepts

Objectives

I set out to create a tool that would build random portfolios for me. I wanted it to have the following capabilities:

  1. Create a fixed number of portfolios with a fixed number of assets in each portfolio as a matrix.
  2. Allow both long and short positions which combine to deliver a fixed net exposure.
  3. Control the maximum and minimum number of short positions in the portfolio.
  4. Control the maximum and minimum total short exposure in the portfolio.
  5. Set the minimum and maximum permissible size for individual long positions.
  6. Control the maximum and minimum size for individual short positions.
  7. Set the resolution of the portfolio positions (e.g. 1%, 0.1%, .01%, etc.).
  8. Provide optional reporting on the population of portfolios generated.

The portfolio-level parameters on the long side (total long book and count of long positions) are the complement of the same parameters on the short side. For example, if my net position is +150% and my short book is -30%, obviously my long book will be +180%. I chose to control the portfolio-level parameters on the short side rather than the long side as it feels more natural to me to do this. I think most people would tend to approach the problem in the order:

  1. What net exposure and total number of positions do I want?
  2. How much of that am I prepared to carry on the short side?

Additional Capabilities

Other capabilities I considered but discarded were:

  1. Select net exposure randomly subject to a maximum and minimum.
  2. Select the number of asset randomly subject to a maximum and minimum.

I discarded both these ideas because they would require much more intelligence in portfolio-level parameters (maximum total short book, maximum number of positions in short book, etc.). For example, 100 assets with 10 short may make sense, but 20 assets with 10 short may not. It is easy enough to run the random portfolio generator inside a script setting multiple random sets of these global parameters and having them influence the per-portfolio parameters. For example, you could generate 100 portfolios with 5 assets and up to 2 short positions, and another 100 with 10 assets and up to 4 short positions, etc.

Key Concept: Stars and Bars

Note 2019-05-28: Modified the implementation to simplify dividing up the range. Don’t need to explicitly use the bars at all, just randomly select the end point of each partition WITH replacement. Need to use replacement to ensure zero-sized partitions. The sample space then simply reduces to (0:N) in the notation below. This saves a couple of operations in the code. The stars and bars can be used to calculate total possible arrangements ((N + G)!/N!/G!). If this is less than the number of portfolios requested, it becomes more efficient to exhaustively derive the portfolios.

The key concept behind a random portfolio is the stars and bars approach to dividing up a range. If you are not familiar with this approach it works as follows:

Let’s say I have 10 units of something I wish to divide up into 3 groups. To divide something into 3 groups I need 2 dividers. The stars are what I want to divide and the bars are the dividers. Here are some possible arrangements:

****|*|***** = (4, 1, 5)

**||******** = (2, 0, 8)

I can denote the positions in the above representations as a vector (1:12). This is a sample space. I can randomly sample 2 items from the sample space without replacement and these are the random positions of my bars. I have an implicit bar at position 0, and one at position 13. Let’s say my random selections are 7 and 3. I can create a vector: (0, 3, 7, 13) – note I have sorted the random selection.

I can now create a portfolio by subtraction: vector[-1] – vector[-4] = (3, 4, 6). Notice they sum to 13 – I have 3 (the number of groups) too many. Subtracting 1 off each value I get: (2, 3, 5) which sums to 10.

We can generalize to N units and G groups and create our vector: V = c(0, sort(sample(1:(N + G – 1), G – 1)), N + G) where “sample” takes (G – 1) random samples without replacement from the vector (1, 2, …, N + G – 1).

Then our portfolio is simply (V[-1] – V[-length(V)]) – 1.

Separating Short From Long

The approach I adopt in the random portfolio generator is to complete the short side first, if there is one, and then complete the long side.

I choose the number of short positions at random from the minimum to the maximum inclusive. I make adjustments to the minimum and maximum total short exposure to ensure feasible portfolios. Then I set the total short exposure at random from between the limits. Finally, I use the stars and bars approach to build the portfolio. Individual positions are tweaked to ensure all of the individual positions are within the maximum and minimum permitted. See the two sections below about positions that are too small or too large.

Note that some of the ranges entered by the user may get truncated due to the number of positions taken. For example, if the minimum individual short position is -1%, and the minimum total short position -10% and there are 5 short positions, the minimum short position has to be -2%.

Once the short side is completed, the long book is done in a similar way. The total long book is not random as it is the net position adjusted for the total short position. For example, if the net position is set to 100%, for a given portfolio the short position is -20%, then the long position must be 120%.

Finally, I construct the full portfolio by randomly assigning the long and short positions to the available assets. e.g. my short portfolio is (-8, -2, -10) and my long portfolio is (+20, +26, +14, +5, +18, +28, +9), maybe my final portfolio is (-8, +20, +26, -2, -10, +14, +5, +18, +28, +9).

Dealing with Resolution

In order to use the stars and bars method, we need integer values. Therefore, we have to specify a resolution which enables us to divide up our net position into a fixed number of units: 120% with a resolution of 0.1% = 1200 units. The random portfolio generator converts everything to units at the beginning, does all the manipulation in “units”, and converts it back to percent before returning the portfolios at the end.

Preventing Positions That Are Too Small

We specify minimum position size in the inputs using minShortPos and minLongPos. If we have, say, 10 positions and a minimum of 2% for each we can reserve 20% of our assets to meet this requirement. We can reduce the stars in the stars and bars approach by 20%. After we have partitioned the remaining stars, we add 2% back to each. We are guaranteed never to have a position less than the required minimum of 2%.

Fixing Positions That Are Too Large

We can end up with individual positions that exceed the upper limit long or short because the bars are placed randomly. The random portfolio generator handles this by capping those positions at the maximum permissible and randomly distributing the excess around all the other positions. This leads to a truncated distribution of individual positions. i.e. if the cap is, say 5% on the long side, there can be a large number of 5% positions – it is all the positions of 5+% combined into the 5% bar.

An alternative approach would be to randomly re-size the over-large bars, and distribute the excess around the remaining positions. The risk in doing this is that you may create an endless process as new excess positions are created and redistributed back to the other positions. The process may never end. Another approach might be to simply re-size excessively large positions, but this introduces a bias in the overall size of the book – large books become less likely. I chose my approach because I think it mimics what a portfolio manager would do: he would cap the position and re-allocate to smaller positions.

The Code

NOTE: Replaced code 2019-05-28 with slightly more efficient version.

Here is the code in R to generate random portfolios. I have tried to add in sufficient comments to make it understandable. Any questions, just ask. If you use the code and would like to change it in any way, please make a suggestion in the comments. Similarly, if you find ways to break it, let me know – I will try to improve it.

Note: Sometimes, with edge-cases you can get the random portfolio generator to fail. The code strays into a non-feasible area, but I have not yet figured out how to trap the problem!

# Random Portfolio Generator - using stars and bars to divide up allocations into n random parts
# Ian Rayner May 2019

MakePorts <- function(count=100, assets=5, resolution=1, report=F, 
                      netPosition=100, minLongPos=10, maxLongPos=40, 
                      minShortCount=0, minShortPos=0, minShortTotal=0, 
                      maxShortCount=0, maxShortPos=0, maxShortTotal=0){
  # count = number of portfolios
  # assets = number of positions in each portfolio
  # resolution = minimum increment in % for each position
  # report = print portfolio diagnostic report
  # netPosition = % overall exposure of portfolio (indirectly sets min. and maxx. long positions)
  # minLongPos = minimum size of a position given a position is long
  # maxLongPos = maximum size of a position given a position is long
  # minShortCount = smallest number of short positions
  # minShortPos = minimum size of a position given a position is short (negative)
  # minShortTotal = smallest total short position (negative)
  # maxShortCount = largest number of short positions
  # maxShortPos = maximum size of a position given a position is short (negative)
  # maxShortTotal = largest total short position (negative)
  
  if (assets <= 1) stop("Need at least 2 assets")
  if (maxShortCount >= assets | minShortCount >= assets) stop(paste("Total assets:", assets, "Max short assets:", maxShortCount, "Min short assets", minShortCount))
  if ((assets - maxShortCount) * maxLongPos < netPosition - maxShortTotal) stop(paste("maxLongPos too small, or maxShortCount, maxShortTotal, and netPosition too large."))
  if ((assets - minShortCount) * minLongPos > netPosition - minShortTotal) stop(paste("minLongPos and netPosition too large, or minShortCount and minShortTotal too small."))
  if (maxLongPos < minLongPos) stop("maxLongPos < minLongPos")
  
  # Scale values
  netPosition <- netPosition / resolution
  minLongPos <- minLongPos / resolution
  maxLongPos <- maxLongPos / resolution
  minShortPos <- minShortPos / resolution
  maxShortPos <- maxShortPos / resolution
  minShortTotal <- minShortTotal / resolution
  maxShortTotal <- maxShortTotal / resolution
  
  # Re-set values as appropriate
  if (minShortPos >= 0) message(paste0("Re-setting minShortPos from ", resolution * minShortPos, " to -", resolution))
  minShortPos <- min(minShortPos, -1)
  if (floor(minShortTotal / minShortPos) < maxShortCount) message(paste0("Re-setting maxShortCount from ", maxShortCount, " to ", floor(minShortTotal / minShortPos)))
  maxShortCount <- min(floor(minShortTotal / minShortPos), maxShortCount)
  
  portfolios <- matrix(0, ncol=assets, nrow=count)
  if (maxShortPos < 0 & maxShortTotal < 0) {
    if (maxShortCount < minShortCount) stop("maxShortCount < minShortCount")
    if (maxShortPos > minShortPos) stop("maxShortPos > minShortPos")
    if (maxShortTotal > minShortTotal) stop("maxShortTotal > minShortTotal")
    maxShortTotal <- max(maxShortTotal, maxShortPos * maxShortCount)
    
    for (i in 1:count){
      randLongShort <- rep(F, assets)
      # Set short part of portfolio first
      shortCount <- ifelse(minShortCount == maxShortCount, minShortCount, sample(minShortCount:maxShortCount, 1))
      
      if (shortCount > 0){
        randLongShort[sample(assets, shortCount)] <- T
        if (shortCount == 1){
          shortPort <- sample(min(-1, minShortPos, minShortTotal):maxShortPos, 1)
        }else{
          maxShortTotalLocal <- max(shortCount * maxShortPos, maxShortTotal) # given number of positions, largest possible total
          smallestShort <- min(minShortTotal - maxShortPos * (shortCount - 1), minShortPos) # constrain minimum short position
          minShortTotalLocal <- min(minShortTotal, smallestShort * shortCount) # constrain minimum total position
          sampleSpace <- sample((minShortTotalLocal:maxShortTotalLocal) - shortCount * smallestShort, 1) # reduce sample space to allow for smallest short
          partStops <- sort(sample(0:sampleSpace, shortCount - 1, replace=T)) # randomly select for end of each partition
          shortPort <- c(sampleSpace, partStops) - c(partStops, 0) + smallestShort # figure differences and add back minimum short position
          if (sum(shortPort) > minShortTotal) browser()
          
          underPositions <- shortPort >= maxShortPos # Now correct for over-sort positions
          while(!all(underPositions)){
            underPositions <- shortPort > maxShortPos
            shortExcess <- sum(pmin(shortPort - maxShortPos, 0)) # total all the overages
            underCount <- sum(underPositions) # count the number of positions to allocate overage to
            excessPartStops <- sort(sample(0:shortExcess, underCount - 1, replace=T)) # randomly divide up the excess
            deltas <- c(shortExcess, excessPartStops) - c(excessPartStops, 0) # figure differences
            shortPort <- pmax(shortPort, maxShortPos) # cap the too large positions at the max
            shortPort[underPositions] <- shortPort[underPositions] + deltas
            underPositions <- shortPort >= maxShortPos # check for any new excess positions
          }
          if (sum(shortPort) > minShortTotal) browser()
        }
        portfolios[i, randLongShort] <- shortPort
      }
    }
  }else{message("Assuming no short positions based on maxShortPos and maxShortTotal")}
  
  for (i in 1:count){
    longShort <- portfolios[i, ] >= 0
    if (sum(!longShort) < minShortCount) {
      longShort[sample(which(longShort), minShortCount - sum(!longShort))] <- F}
    longCount <- sum(longShort)
    longTotal <- netPosition - sum(portfolios[i, ])
    if (longCount == 1){
      longPort <- longTotal   
    }else{
      smallestLong <- max(longTotal - maxLongPos * (longCount - 1), minLongPos)
      sampleSpace <- longTotal - longCount * smallestLong
      partStops <- sort(sample(0:sampleSpace, longCount - 1, replace=T))
      longPort <- c(partStops, sampleSpace) - c(0, partStops) + smallestLong

      overPositions <- longPort <= maxLongPos
      while(!all(overPositions)){
        overPositions <- longPort < maxLongPos
        longExcess <- sum(pmax(longPort - maxLongPos, 0)) # total all the overages
        overCount <- sum(overPositions) # count the number of positions to allocate overage to
        excessPartStops <- sort(sample(0:longExcess, overCount - 1, replace=T)) # randomly divide up the excess
        deltas <- c(excessPartStops, longExcess) - c(0, excessPartStops) # figure differences
        longPort <- pmin(longPort, maxLongPos) # cap the too large positions at the max
        longPort[overPositions] <- longPort[overPositions] + deltas # adjusted positions
        overPositions <- longPort <= maxLongPos # check for any new excess positions
      }
    }
    portfolios[i, longShort] <- longPort
  }

  if (report){
    hist(rowSums(portfolios) * resolution, main="Net Portfolio Position", xlab="Size", col="grey")
    dev.new()
    hist(portfolios * resolution, main="Individual Positions", xlab="Size", col="grey")
    message("Summary of portfolio net positions: ")
    print(summary(rowSums(portfolios)))
    message(paste0("Total flat positions: ", sum(apply(portfolios, 1, function(vect) sum(vect == 0))), " out of ", count * assets, " total positions"))
    message("Smallest single long position: ", min(apply(portfolios,1,function(vect) min(vect[vect >= 0]))) * resolution)
    message("Largest single long position: ", max(portfolios) * resolution)
    message("Fewest long positions in any portfolio: ", min(apply(portfolios,1,function(vect) sum(vect >= 0)))) 
    message("Least long portfolio: ", min(apply(portfolios,1,function(vect) sum(vect[vect >= 0]))) * resolution)  
    message("Most long positions in any portfolio: ", max(apply(portfolios,1,function(vect) sum(vect >= 0)))) 
    message("Most long portfolio: ", max(apply(portfolios,1,function(vect) sum(vect[vect >= 0]))) * resolution)  
    if (maxShortPos < 0 & maxShortTotal < 0) {
      # long histograms are only relevent if there are short positions
      dev.new()
      hist(apply(portfolios,1,function(vect) sum(vect >= 0)), col="grey", 
           main="Portfolio Long Books", xlab="Number of Long Positions")
      dev.new()
      hist(apply(portfolios,1,function(vect) sum(vect[vect >= 0])) * resolution, col="grey", 
           main="Portfolio Long Books", xlab="Total Long Positions")
      dev.new()
      hist(apply(portfolios,1,function(vect) sum(vect < 0)), col="grey", 
           main="Portfolio Short Books", xlab="Number of Short Positions")
      dev.new()
      hist(apply(portfolios,1,function(vect) sum(vect[vect < 0])) * resolution, col="grey", 
           main="Portfolio Short Books", xlab="Total Short Positions")
      message("Fewest short positions in any portfolio: ", min(apply(portfolios,1,function(vect) sum(vect < 0)))) 
      message("Smallest single short position: ", max(apply(portfolios,1,function(vect) max(vect[vect < 0]))) * resolution)
      message("Least short portfolio: ", max(apply(portfolios,1,function(vect) sum(vect[vect < 0]))) * resolution)  
      message("Most short positions in any portfolio: ", max(apply(portfolios,1,function(vect) sum(vect < 0)))) 
      message("Largest single short position: ", min(portfolios) * resolution)
      message("Most short portfolio: ", min(apply(portfolios,1,function(vect) sum(vect[vect < 0]))) * resolution)  
    }
  }

  return(portfolios * resolution)
}

The inputs to the random portfolio generator are as follows:

  • count = number of portfolios
  • assets = number of positions in each portfolio
  • resolution = minimum increment in % for each position
  • report = print portfolio diagnostic report
  • netPosition = % overall exposure of portfolio (indirectly sets min. and max. long book.)
  • minLongPos = % minimum size of a position given a position is long
  • maxLongPos = % maximum size of a position given a position is long
  • minShortCount = smallest number of short positions
  • minShortPos = % minimum size of a position given a position is short (negative)
  • minShortTotal = % smallest total short position (negative)
  • maxShortCount = largest number of short positions
  • maxShortPos = % maximum size of a position given a position is short (negative)
  • maxShortTotal = % largest total short position (negative)

Sample Results

To give you an idea of the kind of results you can generate, I have put some of the reporting output below. The first example is a randomly generated set of 100 market-neutral portfolios, the second is a set of 130/30 style portfolios. The final set is for a set of portfolios, each of 12 positions, long-only to represent a random fund of hedge funds.

To get the R function to produce the reports, simply set “report” to “T” in the function call.

Market Neutral

Inputs for a Market Neutral Portfolio

The inputs to the function call I chose were as follows:

    • Number of portfolios to generate, count = 1000
    • Number of positions, or assets, in the portfolio, assets = 100
    • Position resolution, resolution = 0.1%
    • Net position for a market neutral portfolio, netPosition = 0%
    • Smallest long position, minLongPos = 0.1%
    • Largest long position, maxLongPos = 5%
    • Fewest short positions in the portfolio, minShortCount = 30
    • Smallest, or least short position, minShortPos = -0.1%
    • Smallest short book in a portfolio, minShortTotal = -50%
    • Most short positions in the portfolio, maxShortCount = 70
    • Largest, or most short position, maxShortPos = -5%
    • Largest short book in a portfolio, minShortTotal = -100%

The number of short positions was chosen to be symmetrical around 50, or half the total positions in the portfolio. The size of the short book was chosen to give a wide spread of total exposure. Obviously, if the total assets are set to 100, and the overall book is neutral, you can figure out the complementary values on the long side.

Reporting

The function reports back all the information above but derived from the actual portfolios so you can confirm you got what you expected. It also tells me the total number of flat positions. In this case, there were none as expected due to the min long and min short parameters.

The charts give a good visual overview of the portfolios. In the first chart, you can clearly see the effect of truncating positions to the max and min permitted in the tall bars at the left and right. Without the truncation, the values would show long tails. You can see that the ranges conform to the input values. You can also see that the long and short books are complementary as you would expect in market neutral portfolios.

Distribution of all individual positions in a set of randomly generated market neutral portfolios.
Distribution of number of long positions in a set of randomly generated market neutral portfolios.
Distribution of number of short positions in a set of randomly generated market neutral portfolios.
Distribution of the size of the long books in a set of randomly generated market neutral portfolios.
Distribution of the size of the short books in a set of randomly generated market neutral portfolios.

130 Long 30 Short Portfolio

Inputs for a 130/30 Portfolio

The inputs to the function call I chose were the same as above except as follows:

    • Net position for a market neutral portfolio, netPosition = 100%
    • Smallest long position, minLongPos = 1%
    • Fewest short positions in the portfolio, minShortCount = 15
    • Smallest, or least short position, minShortPos = -1%
    • Smallest short book in a portfolio, minShortTotal = -30%
    • Most short positions in the portfolio, maxShortCount = 35
    • Largest short book in a portfolio, minShortTotal = -30%

I chose 1% and -1% as the smallest individual position sizes just to make the histograms more interesting! This time I have set minShortTotal and maxShortTotal to the same value, -30%. Naturally, this forces the long side to +130%. Thus, every portfolio will be 130% long and 30% short.

I chose 15 and 35 as the min and max short position count as they are symmetrical about 25% (30% / 130% ~ 25%). This will also illustrate an interesting aspect of the random portfolio generator.

Distribution of all individual positions in a set of randomly generated 130/30 portfolios.
Distribution of number of long positions in a set of randomly generated 130/30 portfolios.
Distribution of the size of the long books in a set of randomly generated 130/30 portfolios.

Reporting

The generator reports back that the value of 35 short positions was re-set to 30. If the smallest short position is -1% and the maximum total short is -30%, then you can only ever carry a maximum of 30 positions. The function adjusts to this reality.

The more interesting histograms are presented nearby. Notice the big gap in the distribution of individual positions. This is because we told the generator that all short positions were to be -1% or more short, and all long positions were to be +1% or more.

If you are observant, your spidey senses should be set off by the large bars that don’t seem to fit in the position count histograms. This is an effect you get with histograms of integer values: the first bar includes BOTH 15 and 16 positions on the short side and 70 and 71 positions on the long side. The range of each bar except the first is closed on the right and open on the left. The first bar is closed both sides.

The same effect is causing the first bar on the long side of the individual positions histogram to look unusually short – it contains only the positions that are exactly 1%!

If you check the earlier example, you can see the effect there too. It is less obvious because the bars cover 5 values each – they are about 20% taller than you would naively expect.

Fund of Hedge Fund Portfolios

Inputs for a FoHF Portfolio

The inputs to the function call I chose were as follows:

    • Number of positions, assets = 12
    • Net position, netPosition = 100%
    • Resolution of positions, resolution = 1%
    • Smallest long position, minLongPos = 6%
    • Largest long position, maxLongPos = 25%
    • All short-related parameters set to zero.

Reporting

The individual positions histogram is presented nearby. It looks as you would expect, with many more small positions than large. Remember the first bar contains both 6 and 7% positions. There are a couple of 25% positions, showing the effectiveness of the “stars and bars” approach at providing full coverage of the range of possibilities.

Distribution of all individual positions in a set of randomly generated hedge fund portfolios.

Conclusion

Let me know if you have any questions or suggestions concerning the random portfolio generator. If you would like me to email you a copy, use the contact form or request it via LinkedIn. I would especially appreciate it if you would let me know if you find an issue with the script.

New Commodity Pool Launches

Please provide your name and email address so we can send you our quarterly compilation of new commodity pools registered with NFA.

We hate SPAM and promise to keep your email address safe.

Thank you. Your file will be available after you confirm your subscription. Check your in-box!

Biggest Hedge Funds By $AUM

Please provide your name and email address so we can send you our quarterly compilation of biggest hedge funds by $AUM as reported on the SEC's Form ADV.

We hate SPAM and promise to keep your email address safe.

Thank you. Your file will be available after you confirm your subscription. Check your in-box!

Free Hedge Fund ODD Toolkit

Note this is US Letter size. If you want A4, use the other button!

We hate SPAM and promise to keep your email address safe.

Hedge Fund Operational Due Diligence Toolkit

Thank you. Your file will be available after you confirm your subscription. Check your in-box!

Free Hedge Fund ODD Toolkit

Note this is A4 size. If you want US Letter, use the other button!

We hate SPAM and promise to keep your email address safe.

Hedge Fund Operational Due Diligence Toolkit

Thank you. Your file will be available after you confirm your subscription. Check your in-box!

Free Hedge Fund ODD Toolkit

Note this is US Letter size. If you want A4, use the other button!

We hate SPAM and promise to keep your email address safe.

Thank you. Your file will be available after you confirm your subscription. Check your in-box!

Free Hedge Fund ODD Toolkit

Note this is A4 size. If you want US Letter, use the other button!

We hate SPAM and promise to keep your email address safe.

Thank you. Your file will be available after you confirm your subscription. Check your in-box!

Subscribe:

Don't miss our next hedge fund article!

We hate SPAM and promise to keep your email address safe.

Thank you! Check your email for confirmation message.

Share This

Share

If you found this post informative, please share it!