# ---
# 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]
# # Yield Measures And YAS
#
# This notebook recreates a compact sell-side bond worksheet: we start from one representative fixed-rate bond, compare several yield measures, and then inspect the YAS-style invoice and risk outputs across a range of clean prices.

# %%
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.analytics import current_yield, simple_yield
from fuggers_py.analytics.spreads import BenchmarkSpec, GovernmentCurve
from fuggers_py.analytics.yas import YASCalculator
from fuggers_py.bonds import FixedBondBuilder, Tenor, YieldCalculationRules
from fuggers_py.core import Currency, Date, Frequency, Price
from fuggers_py.curves import DiscountCurveBuilder

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

apply_research_style()

# %% [markdown]
# ## Representative bond and benchmark setup
#
# The bond is a plain-vanilla USD corporate with semi-annual coupons. We pair it with a flat discount curve and a simple Treasury benchmark strip so the YAS analysis remains deterministic.

# %%
issue = Date.from_ymd(2015, 6, 15)
settlement = Date.from_ymd(2020, 6, 15)
bond = (
    FixedBondBuilder.new()
    .with_issue_date(issue)
    .with_maturity_date(Date.from_ymd(2025, 6, 15))
    .with_coupon_rate(Decimal('0.075'))
    .with_frequency(Frequency.SEMI_ANNUAL)
    .with_currency(Currency.USD)
    .with_rules(YieldCalculationRules.us_corporate())
    .build()
)
curve = (
    DiscountCurveBuilder(reference_date=settlement)
    .add_zero_rate(1.0, Decimal('0.0300'))
    .add_zero_rate(10.0, Decimal('0.0300'))
    .build()
)
government_curve = GovernmentCurve.us_treasury(settlement)
government_curve.add_benchmark(Tenor.parse('2Y'), Decimal('0.0200'))
government_curve.add_benchmark(Tenor.parse('5Y'), Decimal('0.0300'))
government_curve.add_benchmark(Tenor.parse('10Y'), Decimal('0.0400'))
yas = YASCalculator(curve=curve, government_curve=government_curve, benchmark=BenchmarkSpec.nearest())

price_scenarios = [Decimal('98.00'), Decimal('100.00'), Decimal('102.00'), Decimal('105.00'), Decimal('110.503')]
scenario_rows = []
for clean_price in price_scenarios:
    analysis = yas.calculate(bond, Price.new(clean_price, bond.currency()), settlement)
    scenario_rows.append(
        {
            'clean_price': clean_price,
            'ytm': analysis.ytm,
            'street_yield': analysis.street_yield,
            'true_yield': analysis.true_yield,
            'current_yield': analysis.current_yield,
            'simple_yield': analysis.simple_yield,
            'dirty_price': analysis.invoice.dirty_price,
            'accrued_interest': analysis.invoice.accrued_interest,
            'settlement_amount': analysis.invoice.settlement_amount,
        }
    )
scenario_table = pd.DataFrame(scenario_rows)
base_analysis = yas.calculate(bond, Price.new(Decimal('105.00'), bond.currency()), settlement)

# %% [markdown]
# At `105.00`, the bond trades above par, so the current yield remains above the solved yield-to-maturity while the settlement invoice is driven mostly by clean price rather than accrued.

# %%
yield_summary = pd.DataFrame(
    [
        {'metric': 'Current yield', 'value': current_yield(Decimal('0.075'), Decimal('105.00'))},
        {'metric': 'Simple yield', 'value': simple_yield(Decimal('7.5'), Decimal('105.0'), Decimal('100.0'), Decimal('5.0'))},
        {'metric': 'Street yield', 'value': base_analysis.street_yield / Decimal('100')},
        {'metric': 'True yield', 'value': base_analysis.true_yield / Decimal('100')},
        {'metric': 'Yield to maturity', 'value': base_analysis.ytm / Decimal('100')},
    ]
)

display_table(yield_summary, percent_columns=['value'])

# %%
display_table(
    scenario_table,
    percent_columns=['ytm', 'street_yield', 'true_yield', 'current_yield', 'simple_yield'],
    float_columns=['clean_price', 'dirty_price', 'accrued_interest', 'settlement_amount'],
)

# %% [markdown]
# ## Yield-versus-price behavior
#
# All standard yield measures fall as the clean price rises, but they do not move one-for-one. The simple and current-yield measures remain shortcut diagnostics; the YAS-style street and true yields are the better desk references.

# %%
figure, axes = plt.subplots(1, 2, figsize=(12, 4.8))
axes[0].plot(scenario_table['clean_price'], scenario_table['ytm'], label='YTM', linewidth=2)
axes[0].plot(scenario_table['clean_price'], scenario_table['street_yield'], label='Street yield', linewidth=2)
axes[0].plot(scenario_table['clean_price'], scenario_table['current_yield'], label='Current yield', linewidth=2)
axes[0].plot(scenario_table['clean_price'], scenario_table['simple_yield'], label='Simple yield', linewidth=2)
axes[0].set_title('Yield Measures Versus Clean Price')
axes[0].set_xlabel('Clean price')
axes[0].set_ylabel('Yield')
percent_axis(axes[0])
axes[0].legend(title='')

axes[1].bar(scenario_table['clean_price'].astype(str), scenario_table['dirty_price'], label='Dirty price')
axes[1].plot(scenario_table['clean_price'].astype(str), scenario_table['settlement_amount'], color='#8c2f39', marker='o', label='Settlement amount')
axes[1].plot(scenario_table['clean_price'].astype(str), scenario_table['accrued_interest'], color='#e09f3e', marker='o', label='Accrued interest')
axes[1].set_title('Invoice Components Across Prices')
axes[1].set_xlabel('Clean price scenario')
axes[1].set_ylabel('Amount per 100')
axes[1].legend(title='')
plt.tight_layout()
figure

# %% [markdown]
# ## Practitioner takeaways
#
# - Current yield is useful for fast income screening, but it ignores pull-to-par and settlement mechanics.
# - Street and true yield are the desk measures to trust for relative-value work because they solve the full bond cashflow equation.
# - The YAS invoice makes accrued and settlement proceeds explicit, which matters as bonds move away from standard coupon dates.
# - Premium bonds can show attractive coupon carry while still offering lower solved yields than their current-yield headline implies.
