YieldCurve bootstrapping example


#1

Hello,

I am looking for a simple example where we feed in cash, eurodollar futures and par swap rates (hard coded numbers) and build the curve using various interpolation schemes. I have seen some examples in strata-market tests but I am getting lost in the code. Is there any simple example I can look at ? Thanks for the awesome library.


#2

+1 for me too, I wanted to start in the same way, build up a curve, validate against a couple of other systems, then build up some valuations for different trade types. There’s a good set of examples but something really bare-bones would be very helpful


#3

The best example to look at is probably: ‘CalibrationZeroRateUsd2OisFuturesIrsTest’.

That example uses OIS discounting (i.e dual curve calibration) and IR futures for the forward curve. So if you want only one curve, you have to simplify that code. Mainly remove everything related to the DSC curve and add back some deposit at the start. You need to change the ‘CURVE_GROUP_CONFIG’ object to have all discounting and forward on the same curve. This means on line 278 replace it by:

  private static final CurveGroupDefinition CURVE_GROUP_CONFIG =
      CurveGroupDefinition.builder()
          .name(CURVE_GROUP_NAME)
          .addCurve(FWD3_CURVE_DEFN, USD, USD_FED_FUND, USD_LIBOR_3M).build();

The interpolators/extrapolators used for the curve are defined on lines 103/104:

private static final CurveInterpolator INTERPOLATOR_LINEAR = CurveInterpolators.LINEAR;
private static final CurveExtrapolator EXTRAPOLATOR_FLAT = CurveExtrapolators.FLAT;

The list of available interpolators is in the class: CurveInterpolators.

Regards,

Marc


#4

Following on from that, (and to check I’m not heading in the right direction) ideally for the step 1 I have in mind, I want to compare against results from bloomberg swap manager curve screen, so the two curve example you pointed to is a good match, but I need to extract the zero rates and discount factors.

Is that all contained within the RatesProvider object? Any pointers to a test/example case where these are pulled out already? I’m working through the code, just with the unusual problem that there is almost too much for my simple starter requirements!


#5

All is contained in the RatesProvider object. If you have only one curve, you can extract it with

DiscountFactors df = result.discountFactors(USD);
double d = df.discountFactor(xxx);
double z = df.zeroRate(xxx);

The first line retrieve the object object used for the discounting in USD. The second line is the method for the discount factor between the calibration date and the date xxx. The third line is the same for the zero rate.

If you want the foward rates associated to a Ibor rate:

IborIndexRates fwd = result.iborIndexRates(USD_LIBOR_3M);
double f = fwd.rate(IborIndexObservation.of(USD_LIBOR_3M, xxx, REF_DATA));

First retrieve the object and then ask the forward rate for fixing on date xxx for the LIBOR 3M. The REF_DATA (class ReferenceData) contains the information about the holidays, to provide the forward rate with the correct adjusted dates. Strata has a pre-build set of calendars (you can override them if you want), in this case the USNY calendar is used.

You can access the calendar associated to the USD LIBOR 3M for fixings by

HolidayCalendar fixingCalendar = REF_DATA.getValue(USD_LIBOR_3M.getFixingCalendar());


#6

Took me a while to have a proper look at this, but things are looking good, taking that example and then moving towards the others that took market data source from a CSV file.

The next step, I wanted to then use this market data set to analyse a simple swap - so was taking the build up from the SwapPricingExample file for fixed v libor3m (the createStub3mFixedVsLibor3mSwap() section as it had more detail on the trade construction). All builds OK, but I get a problem:

FAIL: Unable to calculate measure ‘PresentValue’. Reason: [AccruedInterest, CashFlows, CurrencyExposure, CurrentCash, ExplainPresentValue, LegInitialNotional, LegPresentValue, PV01CalibratedBucketed, PV01CalibratedSum, PV01MarketQuoteBucketed, PV01MarketQuoteSum, PV01SemiParallelGammaBucketed, PV01SingleNodeGammaBucketed, ParRate, ParSpread, PresentValue, ResolvedTarget] - [Market data not found for identifier ‘CurveId{curveGroupName=USD-DSCON-LIBOR3M, curveName=USD-Disc, observableSource=None}’ of type ‘CurveId’]

Is there a good approach to investigate these kind of issues, as I can imagine hitting it in the future. I can’t tell whether its saying, it can’t find the discount curve, or that for a given day in the calculation, there is no market data (e.g. there’s a settlement date earlier than the market data starts maybe).

Thanks!


#7

The key bit is:

Market data not found for identifier
'CurveId{
  curveGroupName=USD-DSCON-LIBOR3M,
  curveName=USD-Disc,
  observableSource=None
}' of type 'CurveId'

This means that the MarketData instance (which is essentially a big HashMap) does not contain an item with the CurveId above.

Without seeing your whole setup its hard to say why that is. It could be that the group name does not match, or the curve name does not match, or the observable source does not match. I’d recommend taking your MarketData and examining the contents in a debugger to see what keys are present, to see if that helps find the problem.

Note that it may help to know that the interface RatesMarketDataLookup is the class that allows a MarketData to be converted to a RatesProvider.


#8

OK some progress… I tried exploring the MarketData object via Eclipse, but was a bit of information overload, so instead tried using the ExampleMarketDataBuilder class to see what was different there compared to my approach. Once I added in a new CSV file for the curve for date of interest, it worked with that approach. That got me thinking, and ties back to the error message, that I’m not sure the computed curve was in the right place in the initial approach.

So the marketData instance I used is built up from quotes:
MarketData marketData = ImmutableMarketData.of(VAL_DATE, quotes);
and then a few steps later, the curve computed:
RatesProvider multiCurve = calibrator.calibrate(curveGroupDefinition, marketData, ref_data);

I was trying this approach:
RatesMarketDataLookup myRatesLookup = RatesMarketDataLookup.of(curveGroupDefinition);
CalculationRules rules = CalculationRules.of(functions, myRatesLookup);

Results results = runner.calculate(rules, trades, columns, marketData, refData);

But I’m guessing that’s not valid as it’s not seeing the computed curve values? Suspect I’ve missed a step but still trying to build up a mental model of how these structures sit together.


#9

The sections of code you’ve included look fine.

It is worth reviewing this documentation page if you’ve not done so already. There doesn’t look anything inherently wrong with the lines of code you’ve included, so its hard to solve without more info.

Ultimately, the MarketData is the complete set of available data, and RatesMarketDataLookup provides a view onto it so as to obtain a RatesProvider. The rates provider can then be used to price trades, where the discount and forward curves are obtains from the rates provider backed by the MarketData, see DefaultLookupRatesProvider.


#10

Ah that got it, went back through docs and other examples, looks like I’d missed this step:

MarketDataConfig marketDataConfig = MarketDataConfig.builder()
        .add(curveGroupName, curveGroupDefinition)
        .build();

So, one last question, then I have a nice end to end working case. I’m now pricing a swap with this curve setup, but I’d like to pull out the cashflows to cross check dates and values. It looks like there isn’t a report setup for this, but I see in another thread you’ve suggested using

Results results = ...
ExplainMap explain = results.get(indexOfSwapTrade, ColumnName.of(Measures.EXPLAIN_PRESENT_VALUE.getName()));

So the swap trade is first in my results, but this:

ExplainMap explain = results.get(0, ColumnName.of(Measures.EXPLAIN_PRESENT_VALUE.getName()));
System.out.println(explain.explanationString());

Gives me the error:
incompatible types: com.opengamma.strata.collect.result.Result<capture#1 of ?> cannot be converted to com.opengamma.strata.market.explain.ExplainMap

Hopefully its an easy one, then I’m up and running. Thanks!


#11

The method Results.get(int, ColumnName) returns a Result, not an ExplainMap. If you call this, you should succeed:

Result<ExplainMap> r = results.get(0, ColumnName.of(Measures.EXPLAIN_PRESENT_VALUE), ExplainMap.class);
if (result.isSuccess()) {
    ExplainMap em = result.getValue();
}

#12

Brill, just what I needed! Thanks alot for the support


#13

Hi - sorry, one more on this, I’m interested in pulling out just a subset of data for further automated analysis for a set of trades, using the ExplainMap, but I’m afraid I’m struggling to make headway though the api docs. I haven’t been able to find an example that I could make use of in the source - I was trying CashFlowReportRunner but I get the feeling this may be doing way more than I need. I have something in mind like, for each leg, for each flow, get from date, to date, and flow value. Would you have any pointer for that, or somewhere in the source I could base something on?

Thanks


#14

The ExplainMap concept is deliberately open - essentially a hash map. The best way to see what it contains is to call .explanationString() which returns a string of the contents. You can then identify the things you care about and extract them, using instances of ExplainKey. In general, the structure of the explain map will follow the structure of the instrument, so a two leg swap will contain separate information for each leg.

Note that we also have a CashFlows object, just for the monetary aspect of the cash flow. This can be queried at the pricer, measure and calc levels.