# ---
# jupyter:
#   jupytext:
#     formats: ipynb,py:percent
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.19.1
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# %% [markdown]
# # Risk analytics examples
#
# This notebook demonstrates:
#
# - Effective duration vs key-rate duration decomposition
# - Spread duration for a fixed-rate bond
# - A small tenor-by-duration table

# %% [markdown]
# ## Setup

# %%
from __future__ import annotations

import sys
from decimal import Decimal
from pathlib import Path

root = Path.cwd()
if (root / "src").exists():
    sys.path.insert(0, str(root / "src"))
elif (root.parent / "src").exists():
    sys.path.insert(0, str(root.parent / "src"))

from fuggers_py.analytics.pricing import BondPricer  # noqa: E402
from fuggers_py.analytics.risk.duration import (  # noqa: E402
    KeyRateDurationCalculator,
    STANDARD_KEY_RATE_TENORS,
    spread_duration,
)
from fuggers_py.bonds.instruments import FixedBond  # noqa: E402
from fuggers_py.core.types import Compounding, Date, Frequency  # noqa: E402
from fuggers_py.curves import ZeroCurveBuilder  # noqa: E402
from fuggers_py.curves.bumping import ParallelBump  # noqa: E402

# %% [markdown]
# ## Build a curve and bond

# %%
ref = Date.from_ymd(2024, 1, 1)
curve = (
    ZeroCurveBuilder(reference_date=ref, compounding=Compounding.CONTINUOUS)
    .add_rate(ref.add_days(365), Decimal("0.02"))
    .add_rate(ref.add_days(365 * 5), Decimal("0.03"))
    .add_rate(ref.add_days(365 * 10), Decimal("0.035"))
    .add_rate(ref.add_days(365 * 30), Decimal("0.04"))
    .build()
)

bond = FixedBond.new(
    issue_date=ref,
    maturity_date=ref.add_days(365 * 10),
    coupon_rate=Decimal("0.04"),
    frequency=Frequency.SEMI_ANNUAL,
)

# %% [markdown]
# ## Effective duration from curve bumps

# %%
pricer = BondPricer()
base_price = pricer.price_from_curve(bond, curve, ref).dirty.as_percentage()

bump = 1e-4
price_up = pricer.price_from_curve(bond, ParallelBump(bump).apply(curve), ref).dirty.as_percentage()
price_down = pricer.price_from_curve(bond, ParallelBump(-bump).apply(curve), ref).dirty.as_percentage()

if base_price != 0:
    effective_duration = (price_down - price_up) / (Decimal(2) * base_price * Decimal(str(bump)))
else:
    effective_duration = Decimal(0)

print("effective duration:", effective_duration)

# %% [markdown]
# ## Key-rate duration decomposition

# %%
kr_calc = KeyRateDurationCalculator(bump=bump)
krd = kr_calc.calculate(bond, curve, ref, tenors=list(STANDARD_KEY_RATE_TENORS))

kr_table = [(item.tenor, item.duration) for item in krd.items]
kr_sum = sum((item.duration for item in krd.items), Decimal(0))

print("sum key-rate duration:", kr_sum)

# %% [markdown]
# ## Spread duration

# %%
spread_dur = spread_duration(bond, None, ref, curve=curve, spread=Decimal("0.0"), bump=bump)
print("spread duration:", spread_dur)

# %% [markdown]
# ## Tenor-by-duration table

# %%
for tenor, dur in kr_table:
    print(f"{str(tenor):>4}: {dur:.6f}")
