You look at ETHUSD - how many USD per one ETH. If S=K=20 you get (correctly, if vol is 130%) a price of 1.43.
However, if you swap it around to price the option as USDETH (how many ETH per USD), you need to change your strike and notional because Garman Kohlhagen (BS for FX) is is priced in terms of notional in ccy1 (ETH), and premium in ccy2 (USD). If you use USDETH, your notional is now in USD and your premium in ETH.
Using Julia, this looks like this:
function GK(S,K,ccy1,ccy2,σ)
d1 = (log(S/K) + (ccy2-ccy1+0.5*σ^2)*t)/(σ*sqrt(t))
d2 = d1 - σ*sqrt(t)
c = S*exp(-ccy1*t)*N(d1)-K*exp(-ccy2*t)*N(d2)
p = K*exp(-ccy2*t)*N(-d2) - S*exp(-ccy1*t)*N(-d1)
return c, p
end
S = K = 20
ccy1 = ccy2 = 0
σ = 1.3
t = 7/365.2425;
GK(S,K,ccy1,ccy2,σ) # ETHUSD in USD
GK(S,K,ccy1,ccy2,σ) ./S # ETHUSD in ETH
GK(1/S,1/K,ccy1,ccy2,σ).*K # USDETH in ETH

You can read some more details here.
Edit:
You are not pricing the same payoff. In Black Scholes, you have $S_t-K$ (or FP-K) as the original option but you price (FP-K)/FP (you do not divide by current spot), and to get the value of 0.05758 you would actually have a "final price" (FP) of something like 21.222 (for (FP−K)/FP to result in 0.05758). If you now compute 21.222 - 20 (S-K) you get 1.222 ; if you use your 0.05758 * 21.222 you get the same 1.222.
Your MC
def MCSIM(S,t,r,vol,M=1000000, inverted = False):
#np.random.seed(10)
dt = t
nudt = (r - 0.5*vol**2)*dt
volsdt = vol*np.sqrt(dt)
lnS = np.log(S)
Z = np.random.normal(size=M)
delta_lnSt = nudt + volsdt*Z
if not inverted:
lnSt = lnS + delta_lnSt
else:
lnSt = lnS - delta_lnSt
# compute Expectation and SE
ST = np.exp(lnSt)
return ST
(as copied from the chat with @Kurt G) gives this value, because you compute np.average([max((FP-K)/FP,0) for FP in MCSIM(S,t,r,σ,M=1000000, inverted = False)]). However, if you think this through, you only compute this value if FP > 20, else it is 0. Since all values smaller than 20 are excluded, this results in an average FP of ~21.43 (and FP-K will be the correct BS price). You can compute this like so np.average([FP if FP >= 20 else 20 for FP in MCSIM(S,t,r,σ,M=1000000, inverted = False)]).
However, the true expected spot np.average([FP for FP in MCSIM(S,t,r,σ,M=1000000, inverted = False)]) is ~20 in your MC run, given you ignore interest rates. I used a convoluted way with comprehensions to make this difference more explicit.
Put differently, you are interested in the ETH price of the option as of today - not as of the expiry date. Hence, you would need to divide by S instead of FP in np.average([max((FP-K)/S,0) for FP in MCSIM(S,t,r,σ,M=1000000, inverted = False)]), which gives 0.0717544967742682 (not using a seed).
So the idea is to calculate how much ETH I will earn in average and compare it to how much I will pay for the option in ETH. (When the option is caluclated using BS model divided by S, which is the price of the option in ETH terms). The issue is that these two values are different.
– lukas kiss Sep 27 '22 at 12:53