Я пытаюсь оценить форвардный своп, используя кривую начальной загрузки в среде QuantLib. Для моей даты оценки от 4 апреля 2019 г. начальная загрузка кривой работает, как и ожидалось. Я также могу легко оценить стартовый своп на 10–10 лет. Проблема возникает, когда я пытаюсь оценить форвардный своп на 15-5 лет. Предположим, что мое расчетное время t+2 (2019-04-08), и я нахожу дату начала форвардного свопа, используя дату расчета и объект календаря. выходной, поэтому в качестве даты начала используется следующий рабочий день. В нашем случае 08.04.2034 — это суббота, поэтому мы получаем дату начала свопа 10.04.2034. Затем возникает эта ошибка:
невозможно рассчитать форвардный курс между 11 апреля 2034 г. и 11 апреля 2034 г.: неверное время (0) с использованием счетчика "Факт/360 дней"
К этому подходили в комментариях здесь C++ Quantlib Vanilla Swap: установка будущих дат фиксации и передача плавающей ноги, но я не нашел официального вопроса, касающегося этой «проблемы».
Пытаясь понять проблему, я считаю, что генерация обратной даты может быть частью проблемы, поскольку кажется, что она создает заглушку. Дата начала обмена с использованием обратной генерации — 11 апреля 2034 года, а дата начала обмена, которую я указываю, — 10 апреля 2034 года. Это отображается как в моем расписании (фиксированное, так и плавающее).
Продолжая свое исследование, я искал «заглушку» здесь: https://leanpub.com/quantlibpythoncookbook/read и нашел то, что я думаю, является частью ответа. Конструктор Schedule позволяет указать короткие/длинные передние/задние заглушки, но даже если я укажу firstDate как 11 апреля 2034 года, будет выдана та же ошибка. Вот полный код, воспроизводящий ошибку. Как видите, оба моих расписания включают 10 апреля 2034 г. И 11 апреля 2034 г., что, как мне кажется, и является причиной моей проблемы. Я до сих пор не понимаю, почему и как это решить.
import QuantLib as ql
# my quotes
nodes=(
(ql.Date( 4, 4, 2019 ), 1.0),
(ql.Date( 8, 4, 2020 ), 0.9744804179560926),
(ql.Date( 8, 4, 2021 ), 0.9523386108738999),
(ql.Date( 8, 4, 2022 ), 0.9315169815568433),
(ql.Date( 11, 4, 2023 ), 0.910405285996171),
(ql.Date( 8, 4, 2024 ), 0.8892891964251837),
(ql.Date( 8, 4, 2025 ), 0.8676501405451038),
(ql.Date( 8, 4, 2026 ), 0.8457795884699698),
(ql.Date( 8, 4, 2027 ), 0.8237398951999767),
(ql.Date( 10, 4, 2028 ), 0.801457566049863),
(ql.Date( 9, 4, 2029 ), 0.7795144954869505),
(ql.Date( 8, 4, 2031 ), 0.7362944371445531),
(ql.Date( 11, 4, 2034 ), 0.6755019523836218),
(ql.Date( 12, 4, 2039 ), 0.5864073271433347),
(ql.Date( 8, 4, 2044 ), 0.5120023623536163),
(ql.Date( 8, 4, 2049 ), 0.4479312303231183),
(ql.Date( 8, 4, 2059 ), 0.34859916237300465),
(ql.Date( 8, 4, 2069 ), 0.2788046487083811))
node_dates, node_rates = zip(*nodes)
# Construct the discount curve
curve = ql.DiscountCurve(node_dates, node_rates, ql.Actual360(), ql.UnitedStates())
termStruct = ql.RelinkableYieldTermStructureHandle()
termStruct.linkTo(curve)
curve_date = ql.Date(4,4,2019) # the curve date
settlement = ql.Period(2,
ql.Days)
settle_date = ql.UnitedStates().advance(curve_date,
settlement) # the settlement date, assume t+2 settlement
fwdstart = ql.UnitedStates().advance(settle_date,
ql.Period(15,ql.Years)) # forward start date of swap
fwdend = ql.UnitedStates().advance(fwdstart,
ql.Period(5,ql.Years)) # forwrad end date of swap
fixedSchedule = ql.Schedule( fwdstart, # forward start
fwdend, # forward end
ql.Period('6M'), # period tenor
ql.UnitedStates(), # calendar
ql.ModifiedFollowing, # convention
ql.ModifiedFollowing, # termination date convention
ql.DateGeneration.Backward, # date generation
True # EoM
)
print('\n' + 10*'*' + ' Fixed Schedule ' + 10*'*')
for d in fixedSchedule:
print(d)
print(40*'*')
floatingSchedule = ql.Schedule( fwdstart, # forward start
fwdend, # forward end
ql.Period('3M'), # period tenor
ql.UnitedStates(), # calendar
ql.ModifiedFollowing, # convention
ql.ModifiedFollowing, # termination date convention
ql.DateGeneration.Backward, # date generation
True # EoM
)
print('\n' + 10*'*' + ' Floating Schedule ' + 10*'*')
for d in floatingSchedule:
print(d)
print(40*'*')
forwardswap = ql.VanillaSwap( type=ql.VanillaSwap.Receiver, # direction
nominal=1E8, # notional
fixedSchedule=fixedSchedule, # fixed schedule
fixedRate=0.023, # fixed rate
fixedDayCount=ql.Actual360(), # fixed leg basis
floatSchedule=floatingSchedule, # floating schedule
index=ql.USDLibor(ql.Period('3M')),
spread=0.0, # spread
floatingDayCount=ql.Thirty360() # float leg basis
)
swap_engine = ql.DiscountingSwapEngine(termStruct)
forwardswap.setPricingEngine(swap_engine)