# forward + displacement

I I am trying to price a cap/floor using Quantlib in Python. the initial code from from this website: http://gouthamanbalaraman.com/blog/interest-rate-cap-floor-valuation-quantlib-python.html

Here is my code to price a floor on EURIBOR1M.

When I print the results , I get the following error:

RuntimeError: forward + displacement (-0.437337 + 0) must be positive

And when I launch the computation for a floor I get this error:

start_date = ql.Date(4, 2, 2019)
end_date = ql.Date(31, 8 , 2023)
period = ql.Period(1, ql.Months)
calendar = ql.TARGET()
buss_convention = ql.ModifiedFollowing
rule = ql.DateGeneration.Forward
end_of_month = False

schedule = ql.Schedule(start_date, end_date, period,
calendar, buss_convention, buss_convention,
rule, end_of_month)

ibor_index = ql.Euribor(ql.Period(1, ql.Months), ts_handle)
ibor_leg = ql.IborLeg([2074408.03], schedule, ibor_index)

strike = 0.0
floor = ql.Floor(ibor_leg, [strike])

vols = ql.QuoteHandle(ql.SimpleQuote(0.547295))
engine2 = ql.BlackCapFloorEngine(ts_handle, ovs_handle)
floor.setPricingEngine(engine2)
print (floor.NPV())


RuntimeError: forward + displacement (-0.368 + 0) must be positive

 import QuantLib as ql
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

calc_date = ql.Date(22, 2, 2019)
ql.Settings.instance().evaluationDate = calc_date

dates = [ql.Date(22,2,2019), ql.Date(22,4,2019),
ql.Date(23,5,2019), ql.Date(24,6,2019),
ql.Date(24,7,2019), ql.Date(22,8,2019),
ql.Date(23,9,2019), ql.Date(23,10,2019),
ql.Date(22,11,2019), ql.Date(23,12,2019),
ql.Date(23,1,2020), ql.Date(24,2,2020),
ql.Date(22,5,2020), ql.Date(24,8,2020),
ql.Date(23,11,2020), ql.Date(22,2,2021),
ql.Date(24,2,2022), ql.Date(23,2,2023),
ql.Date(22,2,2024), ql.Date(24,2,2025),
ql.Date(23,2,2026), ql.Date(22,2,2027),
ql.Date(24,2,2028), ql.Date(22,2,2029),
ql.Date(24,2,2031), ql.Date(23,2,2034),
ql.Date(24,2,2039), ql.Date(22,2,2044),
ql.Date(22,2,2049),
]

yields = [-0.36800000,-0.36500000,
-0.36520000,-0.36430000,
-0.36350000,-0.36200000,
-0.36190000,-0.36150000,
-0.36000000,-0.35890000,
-0.35680000,-0.35410000,
-0.34600000,-0.33650000,
-0.32000000,-0.30490000,
-0.22600000,-0.13200000,
-0.02640000,0.08200000,
0.19150000,0.30100000,
0.40100000,0.49900000,
0.68700000,0.89380000,
1.06480000,1.13200000,
1.14900000]

day_count = ql.ActualActual()
calendar = ql.TARGET()
interpolation = ql.Linear()
compounding = ql.Compounded
compounding_frequency = ql.Annual

term_structure = ql.ZeroCurve(dates, yields, day_count, calendar,
interpolation, compounding, compounding_frequency)
ts_handle = ql.YieldTermStructureHandle(term_structure)
ts_handle.referenceDate()

strikes = [0.01,0.02, 0.025, 0.03, 0.04, 0.05]
expiries = [ql.Period(i, ql.Years) for i in range(1,6)]
vols = ql.Matrix(len(expiries), len(strikes))
data = [
[90.635, 96.25, 73.745, 67.215, 61.17],#vector of vols by strike
[66.665, 70.58, 60.48,  55.18,  49.6],
[61.225, 64.64, 56.595, 55.18, 49.6],
[57.25,  60.33, 53.5695, 49.045, 43.695],
[51.645, 54.29, 49.065,  45.015, 39.915],
[47.755,50.13,  45.8,    42.04,  37.21]
]

for i in range(vols.rows()):
for j in range(vols.columns()):
vols[i][j] = data[j][i]/100.0

calendar = ql.UnitedStates()
bdc = ql.ModifiedFollowing
daycount = ql.Actual365Fixed()
settlement_days = 2
capfloor_vol = ql.CapFloorTermVolSurface(settlement_days, calendar, bdc, expiries, strikes, vols, daycount)

optionlet_surf = ql.OptionletStripper1(capfloor_vol, ibor_index)
ovs_handle = ql.OptionletVolatilityStructureHandle(
)
tenors = np.arange(0,10,0.25)

tenors = np.arange(0,5,0.25)
strike = 0.015
capfloor_vols = [capfloor_vol.volatility(t, strike) for t in tenors]
opionlet_vols = [ovs_handle.volatility(t, strike) for t in tenors]

plt.plot(tenors, capfloor_vols, "--", label="CapFloor Vols")
plt.plot(tenors, opionlet_vols,"-", label="Optionlet Vols")
plt.legend(bbox_to_anchor=(0.5, 0.25))*


From Github, It looks like forward + displacement being > 0 is a requirement of the pricing engine in Quantlib.

void checkParameters(QuantLib::Real strike,
QuantLib::Real forward,
QuantLib::Real displacement)
{
QL_REQUIRE(displacement >= 0.0, "displacement ("
<< displacement
<< ") must be non-negative");
QL_REQUIRE(strike + displacement >= 0.0,
"strike + displacement (" << strike << " + " << displacement
<< ") must be non-negative");
QL_REQUIRE(forward + displacement > 0.0, "forward + displacement ("
<< forward << " + "
<< displacement
<< ") must be positive");
}


In your code 'AddFixing' is called with the exact negative number shown in the error.

ibor_index.addFixing(ql.Date(31,1, 2019), -0.368)


From my understanding, the numeric portion of 'AddFixing' is an inflation/interest rate. The engine interprets this rate as the 'forward' portion the equation. In most cases I can understand that it should always be positive, but recently with the emergence of negative interest rates I can see why you might intend for it to be negative. If my assumptions are correct, it might be worth contributing to or filing a bug on the QuantLib codebase to allow for negative numbers here.

Edit: It appears this is expected behavior and the way to handle negative interest rates is to add displacement. More information can be found on this thread. Essentially a negative number cannot be entered into a logarithm. Displacement can be calculated or perhaps can be obtained from quote provider.

• Thank you for your answer. if i remove the addFixing I still get the error: RuntimeError: forward + displacement (-0.368 + 0) must be positive. Is there s way to get a workaround of it? – supermastercode Feb 21 '19 at 6:10
• What if you put a positive rate in addFixing as a troubleshooting step - to see if it at least runs through the code. – MonteCarloSims Feb 21 '19 at 6:11
• I think the key here is going to be in a positive displacement number. There are some discussions around negative interest rates and how to deal with them in quantlib. Here for example: quantlib.10058.n7.nabble.com/… and quantlib.10058.n7.nabble.com/… This will allow your forward rate to remain negative while correcting the lognormal interest rate model. – MonteCarloSims Feb 21 '19 at 6:32
• Also, as a side-note, I think that judging from your example link that -0.36% rate should be entered into addFixing as -0.0036. – MonteCarloSims Feb 21 '19 at 6:34
• Any luck here? If you were able to solve this question another way please post an answer and give it the green check mark. – MonteCarloSims Feb 26 '19 at 17:47