简体   繁体   English

Heston校准Quantlib Python中的最大曲线时间误差

[英]max curve time error in Heston calibration quantlib python

I am running a compiled from source SWIG python 1.16 version of QuantLib. 我正在运行来自QuantLib的源SWIG python 1.16版本的编译文件。

I have been trying to calibrate a heston model following this example . 我一直在尝试根据此示例校准heston模型。 I am only using the QL calibration at the moment to test it out before trying others. 目前,我仅使用QL校准对其进行测试,然后再尝试其他方法。 I need time dependent parameters so I am using PiecewiseTimeDependentHestonModel . 我需要时间相关的参数,所以我正在使用PiecewiseTimeDependentHestonModel

Here is the relevant portion of my code. 这是我的代码的相关部分。

Helper functions : 辅助功能:

def tenor2date(s, base_date=None,ql=False):
    # returns a date from a tenor and a base date
    if base_date is None:
        base_date = datetime.today()
    num = float(s[:-1])
    period = s[-1].upper()
    if period == "Y":
        return_date = base_date + relativedelta(years=num)
    elif period == "M":
        return_date = base_date + relativedelta(months=num)
    elif period == "W":
        return_date = base_date + relativedelta(weeks=num)
    elif period == "D":
        return_date = base_date + relativedelta(days=num)
    else:
        return_date = base_date
    if ql:
        return Date(return_date.strftime("%F"),"yyyy-mm-dd")
    else:
        return return_date

def setup_model(yield_ts, dividend_ts, spot, times,init_condition=(0.02, 0.2, 0.5, 0.1, 0.01)):
    theta, kappa, sigma, rho, v0 = init_condition

    model = ql.PiecewiseTimeDependentHestonModel(yield_ts, dividend_ts, ql.QuoteHandle(ql.SimpleQuote(spot)), v0, ql.Parameter(), ql.Parameter(),
                                                   ql.Parameter(), ql.Parameter(), ql.TimeGrid(times))
    engine = ql.AnalyticPTDHestonEngine(model)
    return model, engine

def setup_helpers(engine, vol_surface, ref_date, spot, yield_ts, dividend_ts):
    heston_helpers = []
    grid_data = []
    for tenor in vol_surface:
        expiry_date = tenor2date(tenor, datetime(ref_date.year(), ref_date.month(), ref_date.dayOfMonth()), True)
        t = (expiry_date - ref_date)
        print(f"{tenor} : {t / 365}")
        p = ql.Period(t, ql.Days)
        for strike, vol in zip(vol_surface[tenor]["strikes"], vol_surface[tenor]["volatilities"]):
            print((strike, vol))
            helper = ql.HestonModelHelper(p, calendar, spot, strike, ql.QuoteHandle(ql.SimpleQuote(vol / 100)), yield_ts, dividend_ts)
            helper.setPricingEngine(engine)
            heston_helpers.append(helper)
            grid_data.append((expiry_date, strike))
    return heston_helpers, grid_data

Market data : 市场数据:

vol_surface = {'12M': {'strikes': [1.0030154025220293, 0.9840808634190958, 0.9589657270688433, 0.9408279805370683, 0.9174122318462831, 0.8963792435025802, 0.8787138822765832, 0.8538712672800733, 0.8355036501980958], 'volatilities': [6.7175, 6.5, 6.24375, 6.145, 6.195, 6.425, 6.72125, 7.21, 7.5625], 'forward': 0.919323}, '1M': {'strikes': [0.9369864196692815, 0.9324482223892986, 0.9261255003380027, 0.9213195223581382, 0.9150244003650484, 0.9088253068972495, 0.9038936313900919, 0.897245676067657, 0.8924388848562849], 'volatilities': [6.3475, 6.23375, 6.1075, 6.06, 6.09, 6.215, 6.3725, 6.63125, 6.8225], 'forward': 0.915169}, '1W': {'strikes': [0.9258809998009043, 0.9236526412979602, 0.920487656155217, 0.9180490618315417, 0.9148370595017086, 0.9116231311263782, 0.9090950947170667, 0.9057357691404444, 0.9033397443834199], 'volatilities': [6.7175, 6.63375, 6.53625, 6.5025, 6.53, 6.6425, 6.77875, 6.99625, 7.1525], 'forward': 0.914875}, '2M': {'strikes': [0.9456173410343232, 0.9392447942175677, 0.9304717860942596, 0.9238709412876663, 0.9152350197527926, 0.9068086964842931, 0.9000335970840222, 0.8908167643473346, 0.884110721680849], 'volatilities': [6.1575, 6.02625, 5.8825, 5.8325, 5.87, 6.0175, 6.1975, 6.48875, 6.7025], 'forward': 0.915506}, '3M': {'strikes': [0.9533543407827232, 0.945357456067501, 0.9343646071178692, 0.9261489737826977, 0.9154251386183144, 0.9050707394248945, 0.8966770979707913, 0.8851907303568785, 0.876803402158318], 'volatilities': [6.23, 6.09125, 5.93, 5.8725, 5.915, 6.0775, 6.28, 6.60375, 6.84], 'forward': 0.915841}, '4M': {'strikes': [0.9603950279333742, 0.9509237742916833, 0.9379657828957041, 0.928295643018581, 0.9156834006905108, 0.9036539552069216, 0.8938804229269658, 0.8804999196762403, 0.870730837142799], 'volatilities': [6.3175, 6.17125, 6.005, 5.94375, 5.985, 6.15125, 6.36, 6.69375, 6.9375], 'forward': 0.916255}, '6M': {'strikes': [0.9719887962018352, 0.9599837798239937, 0.943700651576822, 0.9316544554849711, 0.9159768970939797, 0.9013018796367052, 0.8892904835162911, 0.8727031923006017, 0.8605425787295339], 'volatilities': [6.3925, 6.22875, 6.04125, 5.9725, 6.01, 6.1875, 6.41375, 6.78625, 7.0575], 'forward': 0.916851}, '9M': {'strikes': [0.9879332225745909, 0.9724112749400833, 0.951642771321364, 0.936450663789222, 0.9167103888580063, 0.8985852649047051, 0.8835274087791912, 0.8625837214139542, 0.8472311260811375], 'volatilities': [6.54, 6.34875, 6.1325, 6.055, 6.11, 6.32, 6.5875, 7.01625, 7.32], 'forward': 0.918086}}
spotDates = [ql.Date(1,7,2019), ql.Date(8,7,2019), ql.Date(1,8,2019), ql.Date(1,9,2019), ql.Date(1,10,2019), ql.Date(1,11,2019), ql.Date(1,1,2020), ql.Date(1,4,2020), ql.Date(1,7,2020)]
spotRates = [0.9148, 0.914875, 0.915169, 0.915506, 0.915841, 0.916255, 0.916851, 0.918086, 0.919323]
udl_value = 0.9148
todaysDate = ql.Date("2019-07-01","yyyy-mm-dd")
settlementDate = ql.Date("2019-07-03","yyyy-mm-dd")

and the script itself: 和脚本本身:

ql.Settings.instance().evaluationDate = todaysDate

dayCounter = ql.Actual365Fixed()
interpolation = ql.Linear()
compounding = ql.Compounded
compoundingFrequency = ql.Annual
times = [(x - spotDates[0]) / 365 for x in spotDates][1:]
discountFactors = [-log(x / spotRates[0]) / (times[i]) for i, x in enumerate(spotRates[1:])]
fwdCurve = ql.ZeroCurve(spotDates, [0] + discountFactors, dayCounter, calendar, interpolation, compounding, compoundingFrequency)
fwdCurveHandle = ql.YieldTermStructureHandle(fwdCurve)

dividendCurveHandle = ql.YieldTermStructureHandle(ql.FlatForward(settlementDate, 0, dayCounter))

hestonModel, hestonEngine = setup_model(fwdCurveHandle, dividendCurveHandle, udl_value, times)
heston_helpers, grid_data = setup_helpers(hestonEngine, vol_surface, todaysDate, udl_value, fwdCurveHandle, dividendCurveHandle)
lm = ql.LevenbergMarquardt(1e-8, 1e-8, 1e-8)
hestonModel.calibrate(heston_helpers, lm, ql.EndCriteria(500, 300, 1.0e-8, 1.0e-8, 1.0e-8))

When I run the last line I get the following error message : 当我运行最后一行时,出现以下错误消息:

RuntimeError: time (1.42466) is past max curve time (1.00274) RuntimeError:时间(1.42466)超过最大曲线时间(1.00274)

I do not understand how it can try to price things beyond 1Y as both the helpers and the forwards curve are defined on the same set of dates. 我不理解它如何尝试对超过1年的价格进行定价,因为助手和远期曲线都在同一组日期上定义。

In case it helps someone, posting here the answer I got from the quantlb mailing : 万一它对某人有帮助,在这里发布我从Quantlb邮件中得到的答案:

specifying the maturity in days 指定天数的到期日

    t = (expiry_date - ref_date)
    print(f"{tenor} : {t / 365}")
    p = ql.Period(t, ql.Days)

might have an counterintuitive effect here as the specified calendar is used to calculate the real expiry date. 由于指定的日历用于计算实际到期日期,因此可能会产生反常的影响。 If the calendar is eg ql.UnitedStates then this takes weekends and holidays into consideration, 如果日历是例如ql.UnitedStates,则这将考虑到周末和假日,

ql.UnitedStates().advance(ql.Date(1,1,2019),ql.Period(365, ql.Days)) => Date(12,6,2020) ql.UnitedStates().advance(ql.Date(1,1,2019),ql.Period(365, ql.Days)) => Date(12,6,2020)

whereas ql.NullCalendar().advance(ql.Date(1,1,2019),ql.Period(365, ql.Days)) => Date(1,1,2020) ql.NullCalendar().advance(ql.Date(1,1,2019),ql.Period(365, ql.Days)) => Date(1,1,2020)

hence I guess the interest rate curve is not long enough and throws the error message. 因此,我认为利率曲线不够长,并会引发错误消息。

So the fix is to make sure to use ql.NullCalendar() accross. 因此,解决方法是确保使用ql.NullCalendar()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM