Эмитент: Citigroup, дата первого расчета (05.07.2024), срок погашения (05.07.2029)
Купон (начисляется и выплачивается единовременно на дату отзыва или срок погашения): 5,75%
График досрочного погашения:
05.07.25 по 105,75%
5.07.26 по 111,5%
05.07.27 по 117,25%
05.07.28 при 123,00%
Использование Bloomberg OAS и YAS, похоже, не дает правильных результатов, поскольку цена опциона немного низкая.
Я просто начал пытаться использовать QuantLib для определения цены опциона и изменил код из другого вопроса на форуме для своих целей, но возвращенный OAS отрицательный, и я не уверен, как вернуть цену опциона сам по себе. Я включил код ниже. Мы будем очень признательны за любую помощь.
Примечание. Нулевая кривая взята из кривой BVAL STRIPS.
Код: Выделить всё
import QuantLib as ql
import numpy as np
import pandas as pd
bf = ql.BondFunctions
qd = ql.DateParser.parseFormatted
# Conventions
accrual_convention = ql.Unadjusted
Rule = ql.DateGeneration.Backward
endofMonth = False
firstDate = None
#OAS
compounding = ql.Simple
frequency = ql.Once
calendar = ql.UnitedStates(ql.UnitedStates.NYSE)
a = 0.03
sigma = 0.27
grid_points = 100
face_amount = 100
mkt_price = 100
contract = {
'IssueDate': ql.Date(3,7,2024),
'MaturityDate': ql.Date(5,7,2029),
'SettlementDays': 2,
'RealValue': 0.0575,
'FirstCallDate': ql.Date(5,7,2025),
'OptionalityEndDate': ql.Date(5,7,2029),
'OperatingCountry': 'US',
'StrikeDate': [ql.Date(5,7,2025), ql.Date(5, 7,2026), ql.Date(5,7,2027), ql.Date(5,7,2028), ql.Date(5,7,2029)],
'OptionalityType': ['Call', 'Call', 'Call', 'Call', 'Call'],
'NoticeDays': [5, 5, 5, 5],
'StrikePrice': [105.75, 111.5, 117.25, 123, 128.75]}
# Here is the zero curve:
times= np.array([ '0 MO', '3 MO', '6 MO', '1 YR', '2 YR', '3 YR',
'5 YR', '7 YR', '10 YR', '20 YR', '30 YR'], dtype=object)
dates = [
ql.Date(17,7, 2024), ql.Date(17,10,2024),ql.Date(17,1,2025), ql.Date(17,7,2025),
ql.Date(17,7,2026),ql.Date(17,7,2027), ql.Date(17,7,2029),
ql.Date(17,7,2031), ql.Date(17,7,2034),ql.Date(17,7,2044),
ql.Date(17,7,2054)]
rates = np.array([ 0.0533, 0.05699 , 0.05630 ,
0.05395 , 0.04865 , 0.04638 , 0.04528 , 0.04615 ,
0.04796 , 0.05055 , 0.04951 ])
day_count = ql.ActualActual(ql.ActualActual.ISDA)
calc_date = ql.Date(16,7,2024)
ql.Settings.instance().evaluationDate = calc_date
issue_date = contract["IssueDate"]
maturity_date = contract["MaturityDate"]
tenor = ql.Period(ql.Annual)
coupon = contract["RealValue"]
settlement_days = contract["SettlementDays"]
# Determine Schedule
schedule = ql.Schedule(issue_date,
maturity_date,
tenor,
calendar,
accrual_convention,
accrual_convention,
Rule,
endofMonth)
# Initiate Zero Curve
curve = ql.ZeroCurve(dates,
rates,
ql.ActualActual(ql.ActualActual.ISDA),
calendar,
ql.Linear())
curve.enableExtrapolation()
ts_handle = ql.YieldTermStructureHandle(curve)
def get_call_schedule(df, period=ql.Period(ql.Annual)):
dates = df["StrikeDate"]
prices = df["StrikePrice"]
callability_schedule = ql.CallabilitySchedule()
null_calendar = ql.NullCalendar()
call_date = df["StrikeDate"][0]
for time in range(len(df["StrikeDate"])):
callability_price = ql.BondPrice(prices[time],
ql.BondPrice.Clean)
callability_schedule.append(ql.Callability(callability_price,
ql.Callability.Call,
dates[time]))
call_date = null_calendar.advance(call_date, period)
return callability_schedule
callability_schedule = get_call_schedule(contract)
bond = ql.CallableFixedRateBond(settlement_days,
face_amount,
schedule,
[coupon],
day_count,
ql.Following,
face_amount,
calc_date,
callability_schedule)
def value_bond(a, s, ts_handle, grid_points, bond):
model = ql.HullWhite(ts_handle, a, s)
engine = ql.TreeCallableFixedRateBondEngine(model, grid_points)
bond.setPricingEngine(engine)
return bond
bondprice = value_bond(a, sigma, ts_handle, grid_points, bond)
OAS = bondprice.OAS(mkt_price,
ts_handle,
day_count, compounding, frequency)
bond_yield = bondprice.bondYield(mkt_price,
day_count,
compounding,
frequency)
print(OAS)
print(bond_yield)
print(bondprice)
Подробнее здесь: https://stackoverflow.com/questions/787 ... lable-note