"""Check fictional bond arithmetic and fixed official Treasury observations.

Python standard library only. Default: local CSV next to this script.
Optional --refresh: retrieve Treasury's 2010 XML feed and verify the snapshot.
No forecasts, strategy returns or causal estimates are produced.
"""

import argparse
import csv
import json
from decimal import Decimal
from pathlib import Path
from urllib.request import urlopen
from xml.etree import ElementTree


TREASURY_URL = (
    "https://home.treasury.gov/resource-center/data-chart-center/"
    "interest-rates/pages/xml?data=daily_treasury_yield_curve"
    "&field_tdr_date_value=2010"
)
SNAPSHOT = Path(__file__).with_name("qe-yield-observations.csv")
DATA_NAMESPACE = "{http://schemas.microsoft.com/ado/2007/08/dataservices}"


def load_snapshot():
    with SNAPSHOT.open(encoding="utf-8", newline="") as handle:
        rows = list(csv.DictReader(handle))
    assert [row["date"] for row in rows] == [
        "2010-11-02", "2010-11-03", "2010-11-04"
    ]
    for row in rows:
        spread = (Decimal(row["10_year_pct"]) - Decimal(row["2_year_pct"])) * 100
        assert spread == Decimal(row["spread_bps"])
    return rows


def refresh_and_compare(rows):
    with urlopen(TREASURY_URL, timeout=30) as response:
        root = ElementTree.fromstring(response.read())
    wanted = {row["date"]: row for row in rows}
    found = {}
    for properties in root.iter():
        day = properties.find(DATA_NAMESPACE + "NEW_DATE")
        if day is None or day.text[:10] not in wanted:
            continue
        date = day.text[:10]
        y2 = properties.find(DATA_NAMESPACE + "BC_2YEAR")
        y10 = properties.find(DATA_NAMESPACE + "BC_10YEAR")
        assert y2 is not None and y10 is not None, date
        assert Decimal(y2.text) == Decimal(wanted[date]["2_year_pct"]), date
        assert Decimal(y10.text) == Decimal(wanted[date]["10_year_pct"]), date
        found[date] = True
    assert set(found) == set(wanted), "Missing dates in official XML feed"


def calculate(rows):
    price_initial = 100 / (1.04 ** 10)
    price_lower_yield = 100 / (1.035 ** 10)
    price_change_pct = (price_lower_yield / price_initial - 1) * 100
    duration_approx_pct = -(10 / 1.04) * (0.035 - 0.04) * 100
    assert abs(price_initial - 67.55641688257987) < 1e-10
    assert abs(price_lower_yield - 70.89188137097722) < 1e-10
    assert abs(price_change_pct - 4.937302246498265) < 1e-10
    y10 = [Decimal(row["10_year_pct"]) for row in rows]
    spreads = [Decimal(row["spread_bps"]) for row in rows]
    changes = {
        "10_year_2010_11_02_to_03_bps": int((y10[1] - y10[0]) * 100),
        "10_year_2010_11_03_to_04_bps": int((y10[2] - y10[1]) * 100),
        "10_year_2010_11_02_to_04_bps": int((y10[2] - y10[0]) * 100),
        "spread_2010_11_02_to_04_bps": int(spreads[2] - spreads[0]),
    }
    assert list(changes.values()) == [4, -14, -10, -9]
    return {
        "checked_on": "2026-10-03",
        "toy_bond": {
            "description": "Fictional $100 payment in 10 years; annual compounding",
            "price_at_4_pct": round(price_initial, 10),
            "price_at_3_5_pct": round(price_lower_yield, 10),
            "instantaneous_price_change_pct": round(price_change_pct, 10),
            "duration_approx_price_change_pct": round(duration_approx_pct, 10),
        },
        "official_observations_source": TREASURY_URL,
        "observed_changes": changes,
        "interpretation": "Descriptive daily par yields; no causal effect or strategy return",
    }


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--refresh", action="store_true")
    args = parser.parse_args()
    rows = load_snapshot()
    if args.refresh:
        refresh_and_compare(rows)
    print(json.dumps(calculate(rows), indent=2))


if __name__ == "__main__":
    main()
