working cva computation using quantlib

This commit is contained in:
local
2026-02-08 13:40:35 +00:00
parent d484f9c236
commit 8b5eb8797f
6 changed files with 1337 additions and 0 deletions

View File

@@ -0,0 +1,165 @@
import QuantLib as ql
# ============================================================
# Global defaults (override via parameters if needed)
# ============================================================
CALENDAR = ql.TARGET()
BUSINESS_CONVENTION = ql.ModifiedFollowing
DATE_GEN = ql.DateGeneration.Forward
FIXED_DAYCOUNT = ql.Thirty360(ql.Thirty360.European)
FLOAT_DAYCOUNT = ql.Actual360()
# ============================================================
# Yield curve factory
# ============================================================
def make_flat_curve(
evaluation_date: ql.Date,
rate: float,
day_count: ql.DayCounter = ql.Actual365Fixed()
) -> ql.YieldTermStructureHandle:
"""
Flat yield curve factory.
"""
ql.Settings.instance().evaluationDate = evaluation_date
curve = ql.FlatForward(evaluation_date, rate, day_count)
return ql.YieldTermStructureHandle(curve)
# ============================================================
# Index factory
# ============================================================
def make_euribor_6m(
curve_handle: ql.YieldTermStructureHandle
) -> ql.IborIndex:
"""
Euribor 6M index factory.
"""
return ql.Euribor6M(curve_handle)
# ============================================================
# Schedule factory
# ============================================================
def make_schedule(
start: ql.Date,
maturity: ql.Date,
tenor: ql.Period,
calendar: ql.Calendar = CALENDAR
) -> ql.Schedule:
"""
Generic schedule factory.
"""
return ql.Schedule(
start,
maturity,
tenor,
calendar,
BUSINESS_CONVENTION,
BUSINESS_CONVENTION,
DATE_GEN,
False
)
# ============================================================
# Swap factory
# ============================================================
def make_vanilla_swap(
evaluation_date: ql.Date,
curve_handle: ql.YieldTermStructureHandle,
notional: float,
fixed_rate: float,
maturity_years: int,
pay_fixed: bool = False,
fixed_leg_freq: ql.Period = ql.Annual,
float_leg_freq: ql.Period = ql.Semiannual
) -> ql.VanillaSwap:
"""
Vanilla fixed-for-float IRS factory.
pay_fixed = True -> payer swap
pay_fixed = False -> receiver swap
"""
ql.Settings.instance().evaluationDate = evaluation_date
index = make_euribor_6m(curve_handle)
start = CALENDAR.advance(evaluation_date, 2, ql.Days)
maturity = CALENDAR.advance(start, maturity_years, ql.Years)
fixed_schedule = make_schedule(
start, maturity, ql.Period(fixed_leg_freq)
)
float_schedule = make_schedule(
start, maturity, ql.Period(float_leg_freq)
)
swap_type = (
ql.VanillaSwap.Payer if pay_fixed else ql.VanillaSwap.Receiver
)
swap = ql.VanillaSwap(
swap_type,
notional,
fixed_schedule,
fixed_rate,
FIXED_DAYCOUNT,
float_schedule,
index,
0.0,
FLOAT_DAYCOUNT
)
swap.setPricingEngine(
ql.DiscountingSwapEngine(curve_handle)
)
return swap
# ============================================================
# Swaption helper factory (for HW calibration)
# ============================================================
def make_swaption_helpers(
swaption_data: list,
curve_handle: ql.YieldTermStructureHandle,
index: ql.IborIndex,
model: ql.HullWhite
) -> list:
"""
Create ATM swaption helpers.
swaption_data = [(expiry, tenor, vol), ...]
"""
helpers = []
for expiry, tenor, vol in swaption_data:
helper = ql.SwaptionHelper(
expiry,
tenor,
ql.QuoteHandle(ql.SimpleQuote(vol)),
index,
index.tenor(),
index.dayCounter(),
index.dayCounter(),
curve_handle
)
helper.setPricingEngine(
ql.JamshidianSwaptionEngine(model)
)
helpers.append(helper)
return helpers