# QuantFinanceToGo, module 16: Monte Carlo Simulation. Exercise 3 (medium).
# Runs on its own with Python 3 and numpy. Fill in the lines marked TODO.
#
# 10,000 simulated paths of $10,000 (8 % a year, swings of 20 %), read after
# 1, 5, 10, 20 and 30 years.
# a) Write exact_percentile(t, u): after t years ln(W / W0) follows a bell
#    with the mean g t and the sd sigma sqrt(t), so the percentile at the
#    share u is W0 exp(g t + z sigma sqrt(t)), z = NormalDist().inv_cdf(u).
# b) Write exact_loss(t): the chance to end below W0, which is
#    NormalDist().cdf(-g t / (sigma sqrt(t))).
# c) Compare both with the simulated paths, and watch the ratio of the 90th
#    to the 10th percentile grow with the square root of time.
import math
from statistics import NormalDist
import numpy as np

start, years, paths, g, sigma = 10000, 30, 10000, 0.06, 0.2
rng = np.random.default_rng(3)                  # fixed seed
value = start * np.exp(rng.normal(g, sigma, (paths, years)).cumsum(axis=1))

def exact_percentile(t, u):
    # TODO: start * exp(g t + z sigma sqrt(t)) with z of the bell at u
    return None

def exact_loss(t):
    # TODO: the bell's share below -g t / (sigma sqrt(t))
    return None

if exact_percentile(1, 0.5) is None or exact_loss(1) is None:
    print("Exercise 3 is not solved yet. Fill in the lines marked TODO.")
else:
    print(" t   10th sim / exact      median sim / exact     loss sim / exact")
    for t in (1, 5, 10, 20, 30):
        lo, mid = np.percentile(value[:, t - 1], [10, 50])
        print(f"{t:>2}  {lo:>8,.0f} / {exact_percentile(t, 0.1):>8,.0f}"
              f"   {mid:>8,.0f} / {exact_percentile(t, 0.5):>8,.0f}"
              f"   {(value[:, t - 1] < start).mean():6.2%} / {exact_loss(t):6.2%}")
    for t in (1, 4, 9, 16, 30):
        hi, lo = np.percentile(value[:, t - 1], [90, 10])
        print(f"t = {t:>2}: 90th / 10th = {hi / lo:5.2f}   exact "
              f"{exact_percentile(t, 0.9) / exact_percentile(t, 0.1):5.2f}")
