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.