Anony Mole (and others), yes I have some VBA functions I am happy to share (remember me when you make a fortune trading). Here are the ones somewhat related to this problem. Note these function use an ARITHMETIC average return, not a GEOMETRIC. If you need an explanation of the difference or why the arithmetic is used here, let me know.
Option Explicit
'Modified 3/25/98
Function AA_Wealth(vProb As Single, vReturn As Single, vRisk As Single, vTime As Single, Optional vAmount)
'Calculates the wealth for which there is only a vProb chance of doing worse
'than over vTime years given a mean of vMean and a standard deviation of
'vSD. Specify inputs in percentage formats (e.g. 8%=8, not 0.08).
Dim vMean As Single
Dim vSD As Single
Dim vLNER As Single, vLNSD As Single, vLNVAR As Single
If IsMissing(vAmount) Then vAmount = 1
vMean = 1 + vReturn / 100
vSD = vRisk / 100
vLNSD = Sqr(Log(1 + (vSD / vMean) ^ 2)) 'var
vLNER = Log(vMean) - vLNSD ^ 2 / 2
AA_Wealth = vAmount * Exp(Application.NormInv(1 - vProb, vLNER * vTime, vLNSD * Sqr(vTime)))
End Function
Function AA_Return(vProb As Single, vMean As Single, vSD As Single, vTime As Single)
'Calculates the return for which there is only a vProb chance of doing worse
'than over vTime years given a mean of vMean and a standard deviation of
'vSD. Periods over 1 year are annualized. Specify inputs in percentage formats (e.g. 8%=8, not 0.08).
Dim x
x = AA_Wealth(vProb, vMean, vSD, vTime, 1)
If vTime < 1 Then
AA_Return = (x - 1) * 100
Else
AA_Return = (x ^ (1 / vTime) - 1) * 100
End If
End Function
Function AA_Prob(vPReturn As Single, vReturn As Single, vRisk As Single, vTime As Single)
'Returns the probability of doing at least as well as vReturn over vTime
'=NORMDIST(LN((vReturn/100+1)^vTime),vLNER*T$13,vLNSD*SQRT(vTime),TRUE)
Dim vMean As Single
Dim vSD As Single
vMean = 1 + vReturn / 100
vSD = vRisk / 100
Dim vLNER As Single, vLNSD As Single, vLNVAR As Single
vLNSD = Sqr(Log(1 + (vSD / vMean) ^ 2)) 'var
vLNER = Log(vMean) - vLNSD ^ 2 / 2
AA_Prob = 1 - Application.WorksheetFunction.NormDist(Log((vPReturn / 100 + 1) ^ vTime), vLNER * vTime, vLNSD * Sqr(vTime), True)
End Function
Function AA_Rand(vReturn As Single, vRisk As Single, vTime As Single, Optional vSeed)
'Returns a lognormally distributed random variable with a mean of vMean and a standard deviation of
'vSD. Periods over 1 year are annualized. Specify inputs in percentage formats (e.g. 8%=8, not 0.08).
Application.Volatile
If IsMissing(vSeed) Or IsNull(vSeed) Then vSeed = 1
Dim vLNER As Single, vLNSD As Single, vLNVAR As Single, dblRnd As Double
Dim vMean As Single
Dim vSD As Single
vMean = 1 + vReturn / 100
vSD = vRisk / 100
vLNSD = Sqr(Log(1 + (vSD / vMean) ^ 2)) 'var
vLNER = Log(vMean) - vLNSD ^ 2 / 2
Do
dblRnd = Rnd(vSeed)
Loop Until (dblRnd > 0 And dblRnd < 1)
AA_Rand = 100 * (Exp(Application.WorksheetFunction.NormInv(dblRnd, vLNER * vTime, vLNSD * Sqr(vTime)) / vTime) - 1)
If vTime >= 1 Then Exit Function
AA_Rand = 100 * ((1 + AA_Rand / 100) ^ vTime - 1)
End Function
I use AA_Rand for do Monte Carlo simulations in Excel. To help me learn Python, I've put a couple of these into Python below:
import random
import array
import math
import numpy as np
from scipy import stats
various functions related to asset allocation and the generation of random returns
def aa_wealth(prob, ror, risk, t=1.0, pv=1.0):
#Calculates the wealth for which there is only a vProb chance of doing worse
#than over vTime years given a mean of vMean and a standard deviation of
#vSD. Specify inputs in percentage formats (e.g. 8%=8, not 0.08).
vmean=1.0+ror/100.0
vrisk=risk/100
vlnsd=math.sqrt(math.log(1+(vrisk/vmean)**2))
vlner=math.log(vmean)-vrisk**2/2
return pv*math.exp(vlner*t+normsinv(1-prob)*vlnsd*math.sqrt(t))
def aa_return(prob, ror, risk,t,n=1):
#Calculates the return for which there is only a vProb chance of doing worse
#than over vTime years given a mean of vMean and a standard deviation of
#vSD. Periods over 1 year are annualized. Specify inputs in percentage formats (e.g. 8%=8, not 0.08).
x=aa_wealth(prob,ror,risk,t,1.0)
if t1:
result=np.exp((vlner*t+np.random.standard_normal(n)*vlnsd*np.sqrt(t))/t)**t-1
else:
result=np.exp((vlner*t+np.random.standard_normal(n)*vlnsd*np.sqrt(t))/t)-1
return result
(the indentation was lost in the copy/paste process).
I could also send the add-in or a macro enabled workbook. My email is my first name, rex, @ last name which is macey with a .US domain (in case there are robots lurking). I'm traveling this week but will try to get it out. I have other functions as well including option pricing (and greeks) and some performance measurement. Caveat: use at your own risk.