# ---
# 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]
# # Fixed-Rate Bonds: Pricing And Risk
#
# We build two representative fixed-rate bonds, inspect their cashflows, and compare price, yield, duration, convexity, and DV01.

# %%
from __future__ import annotations

from decimal import Decimal

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import pandas as pd

from fuggers_py.bonds import BondPricer, FixedBondBuilder, RiskMetrics, YieldCalculationRules
from fuggers_py.core import Compounding, Currency, Date, Frequency, Price, Yield

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

apply_research_style()

# %%
settlement = Date.from_ymd(2026, 1, 15)
short_bond = (
    FixedBondBuilder.new()
    .with_issue_date(Date.from_ymd(2024, 1, 15))
    .with_maturity_date(Date.from_ymd(2029, 1, 15))
    .with_coupon_rate(Decimal('0.0425'))
    .with_frequency(Frequency.SEMI_ANNUAL)
    .with_currency(Currency.USD)
    .with_rules(YieldCalculationRules.us_corporate())
    .build()
)
long_bond = (
    FixedBondBuilder.new()
    .with_issue_date(Date.from_ymd(2024, 1, 15))
    .with_maturity_date(Date.from_ymd(2034, 1, 15))
    .with_coupon_rate(Decimal('0.0525'))
    .with_frequency(Frequency.SEMI_ANNUAL)
    .with_currency(Currency.USD)
    .with_rules(YieldCalculationRules.us_corporate())
    .build()
)

term_sheet = pd.DataFrame(
    [
        {'bond': 'Short 2029', 'maturity': short_bond.maturity_date(), 'coupon_rate': short_bond.coupon_rate(), 'frequency': str(short_bond.frequency())},
        {'bond': 'Long 2034', 'maturity': long_bond.maturity_date(), 'coupon_rate': long_bond.coupon_rate(), 'frequency': str(long_bond.frequency())},
    ]
)
display_table(term_sheet, date_columns=['maturity'], percent_columns=['coupon_rate'])

# %%
pricer = BondPricer()
base_yield = Yield.new(Decimal('0.0480'), Compounding.SEMI_ANNUAL)
price_result = pricer.price_from_yield(long_bond, base_yield, settlement)
yield_roundtrip = pricer.yield_from_price(long_bond, price_result.clean, settlement)
risk = RiskMetrics.from_bond(long_bond, base_yield, settlement)

cashflows = pd.DataFrame(
    [
        {'payment_date': cashflow.date, 'flow_type': str(cashflow.flow_type), 'amount': cashflow.amount}
        for cashflow in long_bond.cash_flows()[:8]
    ]
)
display_table(cashflows, date_columns=['payment_date'], float_columns=['amount'])

# %% [markdown]
# The clean-to-dirty bridge is explicit: price from yield, recover the yield from price, and inspect the coupon ladder that produces the risk profile.

# %%
yield_scenarios = [Decimal('0.035'), Decimal('0.040'), Decimal('0.045'), Decimal('0.050'), Decimal('0.055'), Decimal('0.060')]
price_yield = pd.DataFrame(
    {
        'yield_rate': yield_scenarios,
        'clean_price': [pricer.price_from_yield(long_bond, Yield.new(rate, Compounding.SEMI_ANNUAL), settlement).clean.as_percentage() for rate in yield_scenarios],
    }
)
risk_summary = pd.DataFrame(
    [
        {'metric': 'clean_price', 'value': price_result.clean.as_percentage()},
        {'metric': 'dirty_price', 'value': price_result.dirty.as_percentage()},
        {'metric': 'accrued_interest', 'value': price_result.accrued},
        {'metric': 'yield_roundtrip', 'value': yield_roundtrip.ytm.value()},
        {'metric': 'modified_duration', 'value': risk.modified_duration},
        {'metric': 'convexity', 'value': risk.convexity},
        {'metric': 'dv01', 'value': risk.dv01},
    ]
)
risk_summary

# %%
figure, axes = plt.subplots(1, 2, figsize=(12, 4.6))
axes[0].bar(cashflows['payment_date'].astype(str), cashflows['amount'])
axes[0].set_title('Long Bond Cashflow Ladder')
axes[0].set_xlabel('Payment date')
axes[0].set_ylabel('Amount per 100 par')
for tick in axes[0].get_xticklabels():
    tick.set_rotation(25)
    tick.set_ha('right')

axes[1].plot([100 * float(rate) for rate in price_yield['yield_rate']], price_yield['clean_price'], marker='o', linewidth=2)
axes[1].scatter([100 * float(base_yield.value())], [price_result.clean.as_percentage()], color='#b7410e', s=40, zorder=3)
axes[1].set_title('Price-Yield Relationship')
axes[1].set_xlabel('Yield (%)')
axes[1].set_ylabel('Clean price')
plt.tight_layout()
figure
