Files
OpenBB/openbb_platform/providers/cftc/openbb_cftc/utils/cds.py
Danglewood c66935d51e [V5] openbb-cftc: Refactor for V5 + Add OIS, Forward Curve, and FX Trade Analysis (#7615)
* refactor openbb-cftc for v5

* add SHIBOR to fixings

---------

Co-authored-by: deeleeramone <>
2026-09-23 12:08:51 -07:00

96 lines
2.6 KiB
Python

"""CDS index print extraction from DTCC PPD slice records."""
from collections.abc import Iterable
from datetime import date as dateType
def tenor_at_execution(maturity: dateType, executed: dateType) -> str | None:
"""Snap the years from execution to maturity onto a standard CDS index tenor."""
from openbb_cftc.utils.constants import (
CDS_STANDARD_TENORS,
CDS_TENOR_TOLERANCE_YEARS,
DAYS_PER_YEAR,
)
years = (maturity - executed).days / DAYS_PER_YEAR
if years <= 0:
return None
nearest = min(CDS_STANDARD_TENORS, key=lambda tenor: abs(years - tenor))
if abs(years - nearest) <= CDS_TENOR_TOLERANCE_YEARS:
return f"{nearest:g}Y"
return f"{max(round(years), 1)}Y"
def is_index_print(record: dict) -> bool:
"""Return whether a credits record is a CDS index print."""
from openbb_cftc.utils.constants import CDS_INDEX_FISNS
return (record.get("UPI FISN") or "").strip() in CDS_INDEX_FISNS
def curate_print(record: dict, dissemination_date: dateType) -> dict | None:
"""Add a print's dissemination date and tenor, or None when it cannot be dated."""
from openbb_cftc.utils.curve import parse_date
maturity = parse_date(record.get("Expiration Date"))
executed = parse_date(record.get("Execution Timestamp"))
if maturity is None or executed is None:
return None
row = dict(record)
row["dissemination_date"] = dissemination_date
row["tenor"] = tenor_at_execution(maturity, executed)
return row
def extract_prints(
records: Iterable[dict],
dissemination_date: dateType,
index: str | None = None,
tenor: str | None = None,
min_notional: float | None = None,
limit: int | None = None,
) -> list[dict]:
"""Select the CDS index prints matching a query and curate each into a row."""
from openbb_cftc.utils.curve import parse_notional
rows: list[dict] = []
for record in records:
if not is_index_print(record):
continue
if (
index
and (record.get("UPI Underlier Name") or "").strip().upper()
!= index.strip().upper()
):
continue
row = curate_print(record, dissemination_date)
if row is None:
continue
if tenor and (row["tenor"] or "").upper() != tenor.strip().upper():
continue
if min_notional is not None:
notional, _ = parse_notional(record.get("Notional amount-Leg 1"))
if notional is None or notional < min_notional:
continue
rows.append(row)
if limit and len(rows) >= limit:
break
return rows