# ---
# 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]
# # Curve Construction And Value Conversions
#
# This notebook builds one USD curve through two public construction paths, then translates the curve into zero-rate, discount-factor, forward-rate, and credit-survival views.

# %%
from __future__ import annotations

import math
from decimal import Decimal

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import pandas as pd

from fuggers_py.core import Compounding, Date
from fuggers_py.curves import CreditCurve, CurveBuilder, CurveFamily, DiscountCurveBuilder, ValueConverter, ZeroCurveBuilder

from examples._shared.plotting import apply_research_style, percent_axis
from examples._shared.tables import display_table

apply_research_style()

# %%
reference_date = Date.from_ymd(2026, 1, 2)
market_zeros = {
    0.5: Decimal('0.0430'),
    1.0: Decimal('0.0418'),
    2.0: Decimal('0.0398'),
    5.0: Decimal('0.0382'),
    10.0: Decimal('0.0389'),
}

direct_curve = ZeroCurveBuilder(reference_date=reference_date)
for tenor, rate in market_zeros.items():
    direct_curve = direct_curve.add_rate(reference_date.add_days(int(365 * tenor)), rate)
direct_curve = direct_curve.build()

builder_curve = CurveBuilder.zero(reference_date)
for tenor, rate in market_zeros.items():
    builder_curve = builder_curve.add_pillar(float(tenor), rate)
builder_curve = builder_curve.build()

credit_curve = CurveBuilder.credit(reference_date, family=CurveFamily.HAZARD_RATE)
for tenor, hazard in {1.0: Decimal('0.010'), 3.0: Decimal('0.012'), 5.0: Decimal('0.014'), 10.0: Decimal('0.017')}.items():
    credit_curve = credit_curve.add_pillar(float(tenor), hazard)
credit_curve = credit_curve.build()

# %% [markdown]
# ## Zero, Discount, and Forward Views

# %%
curve_grid = [0.5, 1.0, 2.0, 3.0, 5.0, 7.0, 10.0]
records = []
for tenor in curve_grid:
    date = reference_date.add_days(int(365 * tenor))
    zero = float(direct_curve.zero_rate(date).value())
    discount = float(direct_curve.discount_factor(date))
    next_tenor = tenor + 0.5
    next_date = reference_date.add_days(int(365 * next_tenor))
    next_discount = float(direct_curve.discount_factor(next_date))
    forward = ValueConverter.forward_rate_from_dfs(discount, next_discount, tenor, next_tenor, Compounding.CONTINUOUS)
    records.append(
        {
            'tenor_years': tenor,
            'zero_rate': zero,
            'discount_factor': discount,
            'forward_rate_next_6m': forward,
            'builder_zero_rate': float(builder_curve.zero_rate(date).value()),
        }
    )
curve_table = pd.DataFrame(records)

display_table(
    curve_table,
    percent_columns=['zero_rate', 'forward_rate_next_6m', 'builder_zero_rate'],
    float_columns=['tenor_years', 'discount_factor'],
)

# %% [markdown]
# The direct builder is the cleanest route when the market input is already a zero strip. The generic curve builder is useful when the curve has to be assembled more explicitly inside a larger workflow.

# %%
figure, axes = plt.subplots(1, 2, figsize=(12, 4.8))
axes[0].plot(curve_table['tenor_years'], 100 * curve_table['zero_rate'], label='Zero rate', linewidth=2)
axes[0].plot(curve_table['tenor_years'], 100 * curve_table['forward_rate_next_6m'], label='6M forward', linewidth=2)
axes[0].set_title('Rate Views On The USD Curve')
axes[0].set_xlabel('Tenor (years)')
axes[0].set_ylabel('Rate')
percent_axis(axes[0])
axes[0].legend(title='')

axes[1].plot(curve_table['tenor_years'], curve_table['discount_factor'], label='Discount factor', linewidth=2, color='#b7410e')
axes[1].set_title('Discount-Factor View')
axes[1].set_xlabel('Tenor (years)')
axes[1].set_ylabel('Discount factor')
plt.tight_layout()
figure

# %% [markdown]
# ## Credit Survival Interpretation

# %%
credit_grid = [1.0, 2.0, 3.0, 5.0, 7.0, 10.0]
credit_table = pd.DataFrame(
    {
        'tenor_years': credit_grid,
        'survival_probability': [float(credit_curve.survival_probability(reference_date.add_days(int(365 * tenor)))) for tenor in credit_grid],
    }
)
credit_table['implied_hazard_rate'] = credit_table.apply(
    lambda row: ValueConverter.implied_hazard_rate(row['survival_probability'], row['tenor_years']),
    axis=1,
)

display_table(credit_table, percent_columns=['implied_hazard_rate'], float_columns=['tenor_years', 'survival_probability'])

# %%
figure, axis = plt.subplots(figsize=(10.5, 4.6))
axis.plot(credit_table['tenor_years'], 100 * credit_table['implied_hazard_rate'], linewidth=2, label='Implied hazard rate')
axis.plot(credit_table['tenor_years'], 100 * credit_table['survival_probability'], linewidth=2, label='Survival probability (%)')
axis.set_title('Credit Curve Interpretation')
axis.set_xlabel('Tenor (years)')
axis.set_ylabel('Level')
axis.legend(title='')
plt.tight_layout()
figure
