-
Notifications
You must be signed in to change notification settings - Fork 265
Fix OREAnalytics Test Suite #326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Fix OREAnalytics Test Suite #326
Conversation
Remove inflation instrument from test market that results in a helper with start date equal to maturity date. It is ignored in the zero coupon inflation term structure bootstrap but it gives an exception in the construction of the Schedule on the YoY inflation swap helper.
If year on year inflation is included in the TestMarket and the evaluation date is moved forward, we get errors in the YoY inflation swap helpers due to degenerate schedules where start date is greater than the maturity date.
The iterative bootstrap fails on the inflation helpers on future dates duing simulation. This happens because the evaluation date moves forward and calculate() is called without a prior call to initialize(). This then leaves helpers that should not be still "alive" in the boostrap and they fail because of degenerate schedules.
The test is failing as it stands because: - the zciis_ member of the ZCIIS helper is an observer. - the value pointed to by zciis_ gets added to the deferredObservers_ - during updates, ZeroCouponInflationSwapHelper::initializeDates() gets called and zciis_ gets re-assigned i.e. the value pointed to by zciis_ just before this line, and that is still in deferredObservers_, gets deleted. - deferredObserver->update() is then called on the deleted pointer causing the test to crash.
Revert the last 3 commits and attempt to fix tests in a different way.
Adding the start date to the ZeroCouponInflationSwapHelper sets updateDates_ to false in RelativeDateBootstrapHelper and means that the helper does not observe Settings evaluationDate. This brings the test in line with the way that the helper is created in InflationCurve::buildZeroInflationCurve in OREData also. There is no need to explicitly unregister the evaluationDate after creation of the helper with an explicit non-default constructed start date. Note: YearOnYearInflationSwapHelper should probably be updated in QuantLib to have the same behaviour i.e. that if an explicit start date is provided, updateDates_ is set to false in RelativeDateBootstrapHelper and the helper does not observe Settings evaluationDate. Until then, we still need the explicit unregsiter there in InflationCurve::buildYoYInflationCurve in OREData.
This allows all scen gen tests except testCpiSwapExposure test to run. That test still fails because the grid has only one tenor point at 5Y and the zero inflation curve is recalculated with evaluation date set to that date and fails because fixings are missing. The recalculation should not be happening - it is not intended for this inflation curve that is used in the DK inflation scenario generation to recalculate on future dates.
This reverts commit ac5e034.
Currently the DK inflation model is not using the t_0 zero inflation term structure properly when calculating future scenarios. This fix only fixes the scenario generation in the orea test suite. It is up for discussion whether similar changes should be applied at the point where the zero inflation term structure is created in ORE Data. I will open a separate issue for this. What was happening before this fix is that the zero inflation term structure, due to a series of notifications, was being forced to recalculate on the first simulation date for every sample and this recalculated curve was being used in the generation of the CPI index scenario values rather than the t_0 calculated curve. On sample 0, there is a call to `ScenarioSimMarket::updateScenario( const Date& d)` which changes the evaluationDate to the first grid date. The inflation index in the ZCIIS inside the helper is observing this evaluationDate and the inflation curve is observing the index in turn. Due to this, the inflation curve is forced to recalculate on the first grid date t_1 for sample 0. This t_1 recalculated curve is used for the generation of all scenarios for t_2,...,t_N for sample 0. Note that if relative to date t_1, some of the inflation helpers in the curve require a CPI fixing, e.g. t_1 >= fixing date of some helper, the test fails with exception from iterative bootstrap about missing fixing. On samples > 0, the `applyScenario(scenario);` in `ScenarioSimMarket::updateScenario(const Date& d)` triggers a notification chain that leads to the inflation curve being recalculated on t_1 for every sample. The chain is: - ScenarioSimMarket simData_ setValue on CPI index quote - InflationIndexObserver which in update() calls setFixing() - adds fixings in IndexManager which in turn calls `notifier(name)->notifyObservers();` - the indices in the ZCIIS swap instruments in the ZC inflation curve helpers are observing the notifier => they get notified - the ZC inflation curve is observing the indices in the previous bullet => the curve gets notified and in its `update()` call sets `calculated_` to `false` => recalculation on t_1 for next sample.
Without the changes here, YearOnYearInflationSwapHelper observes
Settings evaluationDate. Because of this
YearOnYearInflationSwapHelper::initializeDates() gets called when
the evaluation date changes. This leads to the yyiis_ member getting
reassigned to a new YOY inflation swap. However, the original pointee is
still in deferredObervers_ of ObservableSettings. The testDefer test
then crashes when deferredObserver->update(); is called in
ObservableSettings::enableUpdates() with null deferredObserver.
Note: should open a PR in QuantLib so that when swap start is provided
to YearOnYearInflationSwapHelper, updateDates_ gets set to false
in RelativeDateBootstrapHelper similar to ZCIIS helper.
On this I see that it is already done in the main QuantLib at https://github.com/lballabio/QuantLib/blob/1daa40d013ff700292f421ec9473f419893b1379/ql/termstructures/inflation/inflationhelpers.cpp#L186 but not in the ORE engine fork of QuantLib. I guess I can just add it to ORE engine version. |
|
Thank you, @francisduffy.
I think we can (and should) apply these changes: The objects created in As for the |
Make the change discussed in PR OpenSourceRisk#326. The change unregisters the index from the ZC and YoY helpers and hence the inflation curve does not observe the inflation index. It was interfering with scenario generation as outlined in the PR. The YoY helpers and inflation curve are still not observing the evaluationDate.
|
Thanks @pcaspers. I pushed the changes for the objects created in On the I can't think of a quick fix other than a (simpler) version of what is done when |
|
It does make sense.
We probably do not need to remove an invalid deferred observer from the set, since the whole set is cleared anyways after the update calls. The disadvantages of
If we want to avoid this, we could go for a more manual approach by introducing another flag Either way, we should probably try to get this back into Luigi's QuantLib, he will probably have additional thoughts on this. |
|
Thanks again. Your proposed fix makes sense to me. I have opened a pull request at lballabio/QuantLib#2409. Feel free to propose changes there if I misinterpreted your proposal. Instead of |
|
Sounds good. As you say, the set of invalid observers is probably small in general, therefore using 'find' to identify an invalid observer should be fine. Since we are using two sorted sets we could optimize the update loop a bit if we want, from O(n log n) to O(n) but that's probably overkill? |
|
I took another quick look and added an alternative approach in the PR here. I think it is cleaner than having the two sets if we are ok with the memory overhead of storing a bool for every deferred observer. |
|
This looks good and is O(n). I think we can live with the added memory overhead. |
There were two issues with OREAnalytics test suite:
*/ObservationModeTest/testDefertest was crashing due to dereferencing a null pointer.*/ObservationModeTest/testDefer, there were failures due to the zero coupon and year on year inflation curves.testDeferIssueThe issue with
testDeferwas coming from the zero coupon and year on year inflation curve helpers atEngine/OREAnalytics/test/testmarket.cpp
Line 581 in 1c7fb03
Engine/OREAnalytics/test/testmarket.cpp
Line 610 in 1c7fb03
The
ZeroCouponInflationSwapHelperis not provided an explicitstartdate in its constructor call. Therefore,updateDates_inRelativeDateBootstrapHelperis not set tofalseand hence theZeroCouponInflationSwapHelperinstances observe theSettings::instance().evaluationDate(). Thestartdate was added in commit 721f3c4. The explicitunregisterWith(Settings::instance().evaluationDate())call could be removed as a consequence.Similarly for the
YearOnYearInflationSwapHelper. However, QuantLib needs to be updated here - it does not setupdateDates_tofalseinRelativeDateBootstrapHelperwhen an explicitstartdate is provided in the constructor. Therefore you still need to unregister withSettings::instance().evaluationDate()manually. This is done as part of another commit, ede6ce1.Without these changes,
updateDates_inRelativeDateBootstrapHelperistrueand whenSettings::instance().evaluationDate()changes a call is made toinitializeDates()here. This leads tozciis_in theZeroCouponInflationSwapHelperandyyiis_inYearOnYearInflationSwapHelpergetting reassigned to new values and the original pointees are destroyed. But, the original pointees are stored indeferredObservers_here. An attempt is made to callupdate()on the null pointer here and the test crashes.Inflation Curve Issues
Zero Tenor Quote Removal
The commit 56c5bfe removes the inflation curve quote with tenor 0. This was ignored in the zero coupon inflation curve bootstrap but caused the year on year inflation curve helpers to fail with errors on schedule creation i.e. errors like
effective date (December 15th, 2016) later than or equal to termination date (December 15th, 2016).Dodgson-Kainth Issue
There is an error on Dodgson-Kainth inflation scenario generation. A fix for the test suite is added in commit b806d85.
Before this fix the zero inflation term structure, due to a series of notifications, was being forced to recalculate on the first simulation date, t_1 say, for every sample and this recalculated curve was being used in the generation of the CPI index scenario values rather than the t_0 calculated curve. The reason for this is outlined below.
On sample 0, there is a call to
ScenarioSimMarket::updateScenario(const Date& d)which changes theevaluationDateto the first grid date t_1. The inflation index in the ZCIISzciis_inside the helper is observing thisevaluationDateand the inflation curve is observing the inflation index. Due to this, the inflation curve is forced to recalculate on the first grid date t_1 for sample 0. This t_1 recalculated curve is used for the generation of all scenarios for t_2,...,t_N for sample 0. Note that if relative to date t_1, some of the inflation helpers in the curve require a CPI fixing, e.g. t_1 >= fixing date of some helper, the test fails with exception from iterative bootstrap about missing fixing. In other words, we see errors of the formQuantLib::Error: 1st iteration: failed at 1st alive instrument, pillar May 1st, 2016, maturity May 1st, 2016, reference date May 1st, 2015: Missing UK RPI fixing for May 1st, 2016.On samples > 0, the
applyScenario(scenario);inScenarioSimMarket::updateScenario(const Date& d)triggers a notification chain that leads to the inflation curve being recalculated on t_1 for every sample. In summary, the chain is:ScenarioSimMarket'ssimData_,setValueis called on CPI index quoteInflationIndexObserverinupdate()callssetFixing()IndexManagerwhich in turn callsnotifier(name)->notifyObservers();zciis_, in the inflation curve helpers are observing thenotifier=> they get notifiedupdate()call setscalculated_tofalse=> recalculation on t_1 for next sample.The changes above only fix the test suite. For example, you will see the incorrect behaviour if you run example
Examples/Exposure/Input/ore_inflation_dk.xml. Would need to discuss further if similar changes i.e.for all instruments should be applied at
Engine/OREData/ored/marketdata/inflationcurve.cpp
Line 284 in 1c7fb03
Engine/OREData/ored/marketdata/inflationcurve.cpp
Line 380 in 1c7fb03
The only question I guess is are there any analytics where we rely on the inflation curves reacting to inflation index values being added for the inflation index referenced in the curve's helpers. If not, I think we can safely apply the change above there. If yes, then we need to leave it configurable.