# QuantFinanceToGo, module 16: Monte Carlo Simulation. Exercise 4 (medium).
# Runs on its own with Python 3 and numpy. Fill in the lines marked TODO.
#
# How often does a 95 % interval hold the true value? In the model of step 1,
# ln(W / W0) after 30 years follows a bell with the mean 1.8 and the
# sd 0.2 sqrt(30), so one draw per path is enough. The exact average final
# value is 10,000 e^(0.08 * 30).
# a) Write interval(values): the average plus and minus 1.96 standard
#    errors, with the standard error s / sqrt(M) and s computed with M - 1.
# b) Repeat a simulation of M paths 1,000 times, for M = 100 and 1,000, and
#    count how often the interval holds the exact average of the final value
#    and how often the interval of ln(W / W0) holds the exact 1.8.
import math
import numpy as np

rng = np.random.default_rng(4)                  # fixed seed
start, mean_log, sd_log = 10000, 1.8, 0.2 * math.sqrt(30)
exact_final = start * math.exp(0.08 * 30)

def interval(values):
    # TODO: return (low, high), the average -/+ 1.96 standard errors
    return None

if interval(np.array([1.0, 2.0, 3.0])) is None:
    print("Exercise 4 is not solved yet. Fill in the lines marked TODO.")
else:
    for m in (100, 1000):
        hit_final = hit_log = 0
        for _ in range(1000):
            logs = rng.normal(mean_log, sd_log, m)
            low, high = interval(start * np.exp(logs))
            hit_final += low <= exact_final <= high
            low, high = interval(logs)
            hit_log += low <= mean_log <= high
        print(f"M = {m:>5,}: the interval of the final value holds it in "
              f"{hit_final / 1000:.1%}, the interval of the log in "
              f"{hit_log / 1000:.1%}")
