可赎回债券的OAS Quantlib

2024-06-28 10:56:33 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图确定QuantLib中可赎回债券的OAS。然而,我的结果总是负面的

我想知道看涨期权计划中是否存在一些问题,因为根据赫尔-怀特模型对债券进行定价所获得的债券收益率似乎是合理的

考虑以下债券合同:

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.Compounded
frequency = ql.Annual

calendar = ql.UnitedStates()

a = 0.1
sigma = 0.1
grid_points = 100
face_amount = 100
mkt_price = 78

contract = {
    'IssueDate': ql.Date(30,6,2016),
    'MaturityDate': ql.Date(15,6,2023),
    'SettlementDays': 2,
    'FirstCouponDate': ql.Date(15,12,2016),
    'NextToLastCouponDate': ql.Date(15,12,2022),
    'RealValue': 0.0675,
    'FirstCallDate': ql.Date(15,6,2019),
    'OptionalityEndDate': ql.Date(15,6,2023),
    'OperatingCountry': 'US',
    'StrikeDate': [ql.Date(15,6,2019), ql.Date(15,6,2020), ql.Date(15,6,2021)],
    'OptionalityType': ['Call', 'Call', 'Call'],
    'NoticeDays': [30, 30, 30],
    'StrikePrice': [103.375, 101.688, 100.0]}

# Here is the zero curve:

times= np.array(['0 MO', '1 MO', '2 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(9,2,2017), ql.Date(9,3,2017), ql. Date(9,4,2017),
         ql.Date(9,5,2017),ql.Date(9,8,2017), ql.Date(9,2,2018),
         ql.Date(9,2,2019),ql.Date(9,2,2020), ql.Date(9,2,2022),
         ql.Date(9,2,2024), ql.Date(9,2,2027),ql.Date(9,2,2037),
         ql.Date(9,2,2047)]

rates = np.array([0.51 , 0.51 , 0.525, 0.54 , 0.64 ,
                   0.8  , 1.2  , 1.46 , 1.88 , 2.2  ,
                   2.4  , 2.74 , 3.02 ])

day_count = ql.ActualActual()
calc_date = ql.Date(9,2,2017)
ql.Settings.instance().evaluationDate = calc_date

issue_date = contract["IssueDate"]
maturity_date = contract["MaturityDate"]
tenor = ql.Period(ql.Semiannual)

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(),
                     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.CallabilityPrice(prices[time],
                                                 ql.CallabilityPrice.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)

这将产生-6.69的OAS值和0.121或12.1%的债券收益率(YTM)。是否有很大的不同,如果一个要考虑欧洲选择在罢工日期与美式期权,其中罢工是应付在优惠券付款日期??p>


Tags: datepricecalendarmoschedulehandlecontractql
2条回答

我会将预付罚款(电话罢工)设定得很高,这样打电话总是不经济的,然后观察/确认您的OAS为零。这至少会验证一些整体设置。如果它通过了测试,那么我会逐步使其中一个更经济,并尝试分别为欧洲期权定价(你可以在你的HW过程上使用Jamshidian引擎进行封闭形式,这是仿射的),然后看看债券dv01上期权的分解值是否足够接近你的OAS(假设后者为正). 虽然如果你有一个消极的美洲国家组织和一组美国的通话日期,它不太可能成为积极的欧洲通话时间表。但这些测试可能会提供一些见解

你们的价格差了100倍。2%的比率需要写为0.02,而不是2.0

相关问题 更多 >