Thus far we have looked at individual components to a simulation, heavily focusing on critical basic elements such as pseudorandom number generation. While these concepts are extremely important as the basic building blocks for a simulation, we should review in more detail a few additional elementary concepts, namely iterations and sampling.
Iterations are the number of times the simulation runs. One complete cycle of the simulation is one iteration. When a low number of iterations is used there can be a large variance in the error from the simulation. In general a large number of iterations should be used to reduce such variance. What number is the right number of iterations? This is definitely project dependent and by no means definitive, but in finance you will often see Monte Carlo simulations running a minimum of 10,000 to 20,000 iterations. In some cases there have been models that we have worked on with up to 1,000,000 iterations assumed.
In certain situations we may only be able to sample from a population to infer results. We have already seen this with the coin toss example, where we know that the probability of getting a heads or tails is ½ or .5. We ran a simulation of tossing a coin and came up with probability results that were close to what is expected. The more samples that are included in a simulation, the closer we can estimate the actual probability. In summary, increasing the sampling gives more accurate results, while increasing the number of iterations decreases the variance on the results.
To help piece together all of the basic elements, we are going to run a more sophisticated simulation. This simulation is done using both the Excel sheet and VBA code. While simulations in finance can be done on the sheet, it is important to understand code-based solutions because as we move on to more advanced concepts through this book there are sections that will simply be far too inefficient to be done on the sheet. If you are struggling with any of the code, refer to the completed version of the Model Builder available on the website.
B3: “Number of Sides” B4: “Iterations” B5: “Sample Size” E3: “Simulated Probability” E4: “Error of Probability” E5: “Actual Probability”
C3: 6 C4: 1000 C5: 50
C3: nSides C4: iterations C5: numOfRolls F3: expectedProb F4: errOfProb
Dim iNSides As Integer Dim dProb As Double, adSimProb() As Double Dim lIterations As Long, lNumOfRolls As Long, lI As Long, lJ As Long, alResult() As Long Dim scratch As Range
Notice that we have created variables and arrays (denoted by the () after the name).
If Not (CheckInputs) Then Exit Sub End If
Application.ScreenUpdating = False
Range(Range(“BinStart”), Range(“BinStart”).Offset(150, 3)).Clear
iNSides = Range(“nSides”).Value lIterations = Range(“iterations”).Value lNumOfRolls = Range(“numOfRolls”).Value
ReDim alResult(1 To lIterations) ReDim adSimProb(1 To lIterations)
dProb = 1 / iNSides
For lI = 1 To lIterations For lJ = 1 To lNumOfRolls
If (Rnd <= dProb) Then alResult(lI) = alResult(lI) + 1 End If Next lJ
adSimProb(lI) = alResult(lI) / lNumOfRolls Next lI
Range(“expectedProb”).Value = MyMean(adSimProb) Range(“errOfProb”).Value = Sqr(MyVar(adSimProb))
Call CopySimResultsToScratch(adSimProb, lIterations) Call SetupHistogramBins
These two subroutines export the iteration results to a scratch sheet so we can create a histogram. The second subroutine listed (SetupHistogramBins) creates the bins for the histogram. This can be done on the sheet or in code depending on the functionality that the user desires.
Range(“nSides”).Select Application.ScreenUpdating = True End Sub
Private Sub CopySimResultsToScratch(adSimProb() As Double, lIterations As Long) Dim scratch As Range Dim lI As Long
Worksheets(“scratch”).Range(Range(“start”), Range(“start”) .End(xlDown)).Clear Set scratch = Worksheets(“scratch”).Range(Range(“start”), Range(“start”).Offset(lIterations-1)) scratch = Application.WorksheetFunction.Transpose(adSimProb) End Sub
The interesting part to this subroutine is that it accepts the number of iterations and the probability that we are trying to return for each iteration as a parameter. The subroutine then clears the export area, sets a range based on the number of iterations and writes out the probability array to that range.
Private Sub SetupHistogramBins() Dim i As Integer Dim binRng As Range Worksheets(“scratch”).Range(“$B$1:$B$101”).ClearContents Set binRng = Worksheets(“scratch”).Range(“bins”) For i = 0 To 100 binRng.Offset(i).Value = i * 0.01 Next i End Sub
The key to this subroutine is that it makes 100 bins at increments of .01 in the For Next loop. The .01 hardcoded figure could be replaced by a variable that is assigned from the Excel sheet inputs section if a user wanted flexibility in changing this.
Private Function CheckInputs() As Boolean ' Check to make sure that the number of dice sides is greater than 0. If Not (Range(“nSides”).Value > 0) Then MsgBox Prompt:=“Please enter a nonzero positive value for N Sides” CheckInputs = False Exit Function End If
' Check to make sure that the number of iterations sides is greater than 0. If Not (Range(“iterations”).Value > 0) Then MsgBox Prompt:=“Please enter a nonzero positive value for the number iterations” CheckInputs = False Exit Function End If ' Check to make sure that the number of rolls is greater than 0. If Not (Range(“numOfRolls”).Value > 0) Then MsgBox Prompt:=“Please enter a nonzero positive value for number of rolls per iteration” CheckInputs = False Exit Function End If CheckInputs = True End Function
The first calculation function is MyMean:
Function MyMean(ByRef avInput() As Double) As Double Dim dSum As Double Dim lCnt As Long Dim lJ As Long dSum = 0 lCnt = 0 For lJ = LBound(avInput) To UBound(avInput) If IsNumeric(avInput(lJ)) Then dSum = dSum + avInput(lJ) lCnt = lCnt + 1 End If Next lJ If lCnt = 0 Then Exit Function End If MyMean = dSum / lCnt End Function
This function is essentially an average function that takes the simulation probabilities from the main subroutine and averages them out by summing them and dividing by the count. The interesting part of this function is the use of LBound and UBound to define the number of loops for the For Next loop. This technique is useful because when we are running simulations we are going to want to be efficient in our looping.
The final function is MyVar, which is essentially the same as MyMean, but it calculates the variance:
Function MyVar(ByRef avInput() As Double) As Double Dim dMean As Double Dim lJ As Long Dim lCnt As Long dMean = MyMean(avInput) MyVar = 0 lCnt = 0 For lJ = LBound(avInput) To UBound(avInput) If IsNumeric(avInput(lJ)) Then MyVar = MyVar + (avInput(lJ)-dMean) ^ 2 lCnt = lCnt + 1 End If Next lJ If lCnt = 0 Then Exit Function End If MyVar = MyVar / lCnt End Function
The main difference between MyVar and MyMean function is the calculation that is done in the For Next Loop. In MyVar the variance is being captured, while in MyMean the mean is recorded.
While this Model Builder is simple, it is a good example of the basic inputs and outputs seen in many simulations. This Model Builder also exemplifies a few thoughts we should have in efficiency:
The other important take away from this Model Builder is an initial understanding of the statistical foundations of simulations. Take some time to run the subroutine by changing the inputs and looking at the resulting histogram. The first change you should try is lowering the number of iterations. Change it from 1000 to 50 and run the simulation. Looking at the histogram chart you will see the distribution is very choppy. With lower iterations there is higher variance in the results and what should be a smooth normal distribution is all over the place. Using data parsed by confidence intervals from such a result could be misleading.
Alternatively, reset the iterations to 1000 and this time lower the sample size to an extreme level such as 5. Notice now that the distribution looks off because of a small sample, but also look at the error (range errOfProb on sheet). The difference in the error is much higher with a smaller sample size. This similarly will lead to misleading results.