FRA present value calculation (possible bug?)

Hi team,
during my analisys of Strata calibrated curves vs benchmark curves, I found great differences in forward ones. Digging deeper I found something strange on PV calculation of FRA instruments:

ExplainMap {
EntryType = FRA,
PaymentDate = 2016-09-11,
StartDate = 2016-09-12,
EndDate = 2016-12-12,
AccrualYearFraction = 0.25277777777777777,
Days = 91,
PaymentCurrency = EUR,
Notional = EUR -1,
TradeNotional = EUR -1,
Observations = [{
EntryType = IborIndexObservation,
FixingDate = 2016-09-08,
Index = EUR-EURIBOR-3M,
IndexValue = -0.0033000000000031724
}],

As you can see, the payment date is 1day before the start date, but in the benchmark FRA the payment and the start date are the same (2016-09-12). This happens on some FRAs of the curve, while other FRAs have the same payment and start date.

How do I ensure that the two dates are the same for all FRAs?

The Fra class allows the start and payment dates to differ.

If a Fra is created with just a start date, the payment date will be the same as the start date, with neither subject to further adjustment. So, if you are creating a Fra directly, it must be because you are passing in both start and payment dates and they are different.

A Fra can also be created from a FraConvention. The standard convention is based entirely off the Ibor index. The convention allows the “payment date offset” to be set, which allows the payment date to be derived from the start date and adjusted. However, the FraConvention factory that takes an IborIndex will create a Fra with the payment date equal to the start date (no “payment date offset”):

  @Override
  public FraTrade createTrade(
      LocalDate tradeDate,
      Period periodToStart,
      Period periodToEnd,
      BuySell buySell,
      double notional,
      double fixedRate,
      ReferenceData refData) {

    LocalDate spotValue = calculateSpotDateFromTradeDate(tradeDate, refData);
    LocalDate startDate = spotValue.plus(periodToStart);
    LocalDate endDate = spotValue.plus(periodToEnd);
    LocalDate paymentDate = getPaymentDateOffset().adjust(startDate, refData);
    return toTrade(tradeDate, startDate, endDate, paymentDate, buySell, notional, fixedRate);
  }

Since the “payment date offset” is "no adjustment by default, the payment date should equal the start date.

So, as far as I can see, the only way to get them to differ is if you have passed in a Fra convention with a payment date offset of -1 days.

Thank you Stephen.
Unfortunately, the array of FRAs is created by Strata from calibration.csv file during curve calibration. All the FRAs have the same EUR-EURIBOR-3M convention.

Using EXPLAIN_PRESENT_VALUE I found that the majority of them have the correct 0day offset between payment and start date. The 2Mx5M and 5Mx8M FRAs use this strange -1day offset without a valid reason.

Can you copy in the files so I can reproduce? And specify the valuation date.

Of course.

VAL_DATE = 7-7-2016

calibration.csv
(The 0day convention for OIS is the same described in Simple Zero Curve Calibration (Deposit+Futures+Swap) - #42 by djess )

Curve Name,Label,Symbology,Ticker,Field Name,Type,Convention,Time,Spread
,
EUR-OIS,OIS-ON,OG-Ticker,EURON-ON,MarketValue,DEP,EUR-ShortDeposit-T0,1D,
EUR-OIS,OIS-TN,OG-Ticker,EURON-TN,MarketValue,DEP,EUR-ShortDeposit-T1,1D,
EUR-OIS,OIS-SN,OG-Ticker,EURON-SN,MarketValue,DEP,EUR-ShortDeposit-T2,1D,
EUR-OIS,OIS-1W,OG-Ticker,EURON-1W,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,1W,
EUR-OIS,OIS-2W,OG-Ticker,EURON-2W,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,2W,
EUR-OIS,OIS-3W,OG-Ticker,EURON-3W,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,3W,
EUR-OIS,OIS-1M,OG-Ticker,EURON-1M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,1M,
EUR-OIS,OIS-2M,OG-Ticker,EURON-2M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,2M,
EUR-OIS,OIS-3M,OG-Ticker,EURON-3M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,3M,
EUR-OIS,OIS-4M,OG-Ticker,EURON-4M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,4M,
EUR-OIS,OIS-5M,OG-Ticker,EURON-5M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,5M,
EUR-OIS,OIS-6M,OG-Ticker,EURON-6M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,6M,
EUR-OIS,OIS-7M,OG-Ticker,EURON-7M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,7M,
EUR-OIS,OIS-8M,OG-Ticker,EURON-8M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,8M,
EUR-OIS,OIS-9M,OG-Ticker,EURON-9M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,9M,
EUR-OIS,OIS-10M,OG-Ticker,EURON-10M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,10M,
EUR-OIS,OIS-11M,OG-Ticker,EURON-11M,MarketValue,OIS,EUR-FIXED-TERM-EONIA-OIS-0DAY,11M,
EUR-OIS,OIS-1Y,OG-Ticker,EURON-1Y,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,1Y,
EUR-OIS,OIS-13M,OG-Ticker,EURON-13M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,13M,
EUR-OIS,OIS-14M,OG-Ticker,EURON-14M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,14M,
EUR-OIS,OIS-15M,OG-Ticker,EURON-15M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,15M,
EUR-OIS,OIS-16M,OG-Ticker,EURON-16M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,16M,
EUR-OIS,OIS-17M,OG-Ticker,EURON-17M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,17M,
EUR-OIS,OIS-18M,OG-Ticker,EURON-18M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,18M,
EUR-OIS,OIS-19M,OG-Ticker,EURON-19M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,19M,
EUR-OIS,OIS-20M,OG-Ticker,EURON-20M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,20M,
EUR-OIS,OIS-21M,OG-Ticker,EURON-21M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,21M,
EUR-OIS,OIS-22M,OG-Ticker,EURON-22M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,22M,
EUR-OIS,OIS-23M,OG-Ticker,EURON-23M,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,23M,
EUR-OIS,OIS-2Y,OG-Ticker,EURON-2Y,MarketValue,OIS,EUR-FIXED-1Y-EONIA-OIS-0DAY,2Y,
,
EUR-3M,FIX,OG-Ticker,EUR3M-FIX,MarketValue,FIX,EUR-EURIBOR-3M,
EUR-3M,1Mx4M,OG-Ticker,EUR3M-1Mx4M,MarketValue,FRA,EUR-EURIBOR-3M,1Mx4M,
EUR-3M,2Mx5M,OG-Ticker,EUR3M-2Mx5M,MarketValue,FRA,EUR-EURIBOR-3M,2Mx5M,
EUR-3M,3Mx6M,OG-Ticker,EUR3M-3Mx6M,MarketValue,FRA,EUR-EURIBOR-3M,3Mx6M,
EUR-3M,4Mx7M,OG-Ticker,EUR3M-4Mx7M,MarketValue,FRA,EUR-EURIBOR-3M,4Mx7M,
EUR-3M,5Mx8M,OG-Ticker,EUR3M-5Mx8M,MarketValue,FRA,EUR-EURIBOR-3M,5Mx8M,
EUR-3M,6Mx9M,OG-Ticker,EUR3M-6Mx9M,MarketValue,FRA,EUR-EURIBOR-3M,6Mx9M,
EUR-3M,7Mx10M,OG-Ticker,EUR3M-7Mx10M,MarketValue,FRA,EUR-EURIBOR-3M,7Mx10M,
EUR-3M,8Mx11M,OG-Ticker,EUR3M-8Mx11M,MarketValue,FRA,EUR-EURIBOR-3M,8Mx11M,
EUR-3M,9Mx12M,OG-Ticker,EUR3M-9Mx12M,MarketValue,FRA,EUR-EURIBOR-3M,9Mx12M,
EUR-3M,10Mx13M,OG-Ticker,EUR3M-10Mx13M,MarketValue,FRA,EUR-EURIBOR-3M,10Mx13M,
EUR-3M,11Mx14M,OG-Ticker,EUR3M-11Mx14M,MarketValue,FRA,EUR-EURIBOR-3M,11Mx14M,
EUR-3M,12Mx15M,OG-Ticker,EUR3M-12Mx15M,MarketValue,FRA,EUR-EURIBOR-3M,12Mx15M,

quotes.csv

Valuation Date,Symbology,Ticker,Field Name,Value
,
2016-07-07,OG-Ticker,EURON-ON,MarketValue,-0.0037
2016-07-07,OG-Ticker,EURON-TN,MarketValue,-0.0037
2016-07-07,OG-Ticker,EURON-SN,MarketValue,-0.00364007797
2016-07-07,OG-Ticker,EURON-1W,MarketValue,-0.00327
2016-07-07,OG-Ticker,EURON-2W,MarketValue,-0.00327
2016-07-07,OG-Ticker,EURON-3W,MarketValue,-0.00328
2016-07-07,OG-Ticker,EURON-1M,MarketValue,-0.00337
2016-07-07,OG-Ticker,EURON-2M,MarketValue,-0.00346375127
2016-07-07,OG-Ticker,EURON-3M,MarketValue,-0.00360520286
2016-07-07,OG-Ticker,EURON-4M,MarketValue,-0.00377151933
2016-07-07,OG-Ticker,EURON-5M,MarketValue,-0.00388043946
2016-07-07,OG-Ticker,EURON-6M,MarketValue,-0.00399154234
2016-07-07,OG-Ticker,EURON-7M,MarketValue,-0.00410654386
2016-07-07,OG-Ticker,EURON-8M,MarketValue,-0.00419
2016-07-07,OG-Ticker,EURON-9M,MarketValue,-0.00427
2016-07-07,OG-Ticker,EURON-10M,MarketValue,-0.00434
2016-07-07,OG-Ticker,EURON-11M,MarketValue,-0.00441
2016-07-07,OG-Ticker,EURON-1Y,MarketValue,-0.00447
2016-07-07,OG-Ticker,EURON-13M,MarketValue,-0.00452886422
2016-07-07,OG-Ticker,EURON-14M,MarketValue,-0.00458188285
2016-07-07,OG-Ticker,EURON-15M,MarketValue,-0.00463
2016-07-07,OG-Ticker,EURON-16M,MarketValue,-0.00468143568
2016-07-07,OG-Ticker,EURON-17M,MarketValue,-0.00472326175
2016-07-07,OG-Ticker,EURON-18M,MarketValue,-0.00476
2016-07-07,OG-Ticker,EURON-19M,MarketValue,-0.00479921817
2016-07-07,OG-Ticker,EURON-20M,MarketValue,-0.00482988171
2016-07-07,OG-Ticker,EURON-21M,MarketValue,-0.00486
2016-07-07,OG-Ticker,EURON-22M,MarketValue,-0.00488819847
2016-07-07,OG-Ticker,EURON-23M,MarketValue,-0.00491546463
2016-07-07,OG-Ticker,EURON-2Y,MarketValue,-0.00494
,
2016-07-07,OG-Ticker,EUR3M-FIX,MarketValue,-0.00293
2016-07-07,OG-Ticker,EUR3M-1Mx4M,MarketValue,-0.0031
2016-07-07,OG-Ticker,EUR3M-2Mx5M,MarketValue,-0.0033
2016-07-07,OG-Ticker,EUR3M-3Mx6M,MarketValue,-0.00342
2016-07-07,OG-Ticker,EUR3M-4Mx7M,MarketValue,-0.00354
2016-07-07,OG-Ticker,EUR3M-5Mx8M,MarketValue,-0.00365
2016-07-07,OG-Ticker,EUR3M-6Mx9M,MarketValue,-0.00373
2016-07-07,OG-Ticker,EUR3M-7Mx10M,MarketValue,-0.00377
2016-07-07,OG-Ticker,EUR3M-8Mx11M,MarketValue,-0.00381
2016-07-07,OG-Ticker,EUR3M-9Mx12M,MarketValue,-0.00385
2016-07-07,OG-Ticker,EUR3M-10Mx13M,MarketValue,-0.00389
2016-07-07,OG-Ticker,EUR3M-11Mx14M,MarketValue,-0.00393
2016-07-07,OG-Ticker,EUR3M-12Mx15M,MarketValue,-0.00396

settings.csv

Curve Name,Value Type,Day Count,Interpolator,Left Extrapolator,Right Extrapolator
EUR-OIS,Zero,Act/365F,DoubleQuadratic,Flat,Flat
EUR-3M,Zero,Act/365F,DoubleQuadratic,Flat,Flat

groups.csv

Group Name,Curve Type,Reference,Curve Name
EUR-group,Discount,EUR,EUR-OIS
EUR-group,Forward,EUR-EONIA,EUR-OIS
EUR-group,Forward,EUR-EURIBOR-3M,EUR-3M

EDIT: after further investigation, other FRAs with wrong -1day offset are
7Mx10M, 8Mx11M, 11Mx14M.

https://github.com/OpenGamma/Strata/issues/1301