Skip to content
All library documents

Choosing Day-Count Conventions When Porting MATLAB Calculations

Article Quant Q&A · Author: Martin Kabelka

Summary

This discussion examines why MATLAB’s actual/actual date-to-time calculation may not match QuantLib’s actual/actual day-count variants when code is ported between the two platforms. The example compares a period spanning 2006 to 2036 and reports different year fractions from QuantLib’s ISDA, Bond, and AFB conventions, alongside results from other conventions. The answers emphasize that the label actual/actual covers multiple rules rather than a single universal calculation.

Several actual/actual methods are designed around coupon periods and need a reference period; when those dates are omitted, a library must make assumptions. That makes such conventions potentially unsuitable for measuring elapsed time generally. One response also describes a mismatch in a semiannual bond compounding example and says a custom calculation was used to reproduce MATLAB behavior, but supplies no implementation. The discussion does not establish the precise MATLAB algorithm or a universal replacement: the right convention depends on the financial use case and required compatibility.

Key ideas

  • Actual/actual refers to multiple day-count conventions with different year-fraction results.
  • Some actual/actual methods depend on coupon reference periods, which may be missing in general date calculations.
  • Library defaults and assumptions can produce differences when porting financial code.
  • Choose a convention based on the instrument and purpose, and verify compatibility with representative dates.

Tags

Full text
# Python QuantLib datecount ActualActual basis vs Matlab daycount basis


# Python QuantLib datecount ActualActual basis vs Matlab daycount basis












I am translating a code from MATLAB to Python and I need to find equivalent setting to MATLAB’s day-count basis of 0 = actual / actual. My MATLAB code uses date2time function to determine the length of period (measured in years) between two dates, with the default setting for the Basis parameter set to “0”.

MATLAB documentation for the day-count bases is here: https://uk.mathworks.com/help/fininst/day-count-basis.html. The original Matlab code is using default first option ‘actual/actual’.

The most appropriate Python library I could find for this sort of task is ‘QuantLib’, which has several day-count basis options as described here: https://quantlib-python-docs.readthedocs.io/en/latest/dates.html#daycounter.

However, having tried all the ql.ActualActual options (ISMA, Bond, ISDA, … ) it seems none of them replicates the result of the default MATLAB actual/actual basis precisely.

How can I get an identical result? I could obviously dive deep into the MATLAB function implementation and write it in python from scratch, but I would have hoped that there is already an existing solution out there.

As a reproducible example, Matlab gives a period length of 30.0302 years between 20-Mar-2006 and 31-Mar-2031 as follows:

```
period = date2time(datetime(2006,3,20), datetime(2036,3,31), 1, 0)
period = 30.0302
```

I'm trying to find a function in Python that would replicate the 30.0302 figure. However, I'm seeing the following:

```
import QuantLib as ql

dateS = ql.Date(20,3,2006)
dateE = ql.Date(31,3,2036)
ql.ActualActual(ql.ActualActual.ISDA).yearFraction(dateS, dateE) = 30.032203009207276
ql.ActualActual(ql.ActualActual.Bond).yearFraction(dateS, dateE) = 30.083333333333332
ql.ActualActual(ql.ActualActual.AFB).yearFraction(dateS, dateE)  = 30.03013698630137
```

## Answer by TourEiffel (score 4)

https://quant.stackexchange.com/a/79769

I think you will never find 30.0302 using quantlib :

```
dateS = ql.Date(20, 3, 2006)
dateE = ql.Date(31, 3, 2036)

# List of day count conventions to try
day_counts = {
    'Actual360': ql.Actual360(),

    'Actual365Fixed': ql.Actual365Fixed(),
    'Actual365Fixed(Canadian)': ql.Actual365Fixed(ql.Actual365Fixed.Canadian),
    'Actual365FixedNoLeap': ql.Actual365Fixed(ql.Actual365Fixed.NoLeap),

    'ActualActual ISDA': ql.ActualActual(ql.ActualActual.ISDA),
    'ActualActual Bond': ql.ActualActual(ql.ActualActual.Bond),
    'ActualActual ISMA': ql.ActualActual(ql.ActualActual.ISMA),
    'ActualActual Historical': ql.ActualActual(ql.ActualActual.Historical),
    'ActualActual Actual365': ql.ActualActual(ql.ActualActual.Actual365),
    'ActualActual AFB': ql.ActualActual(ql.ActualActual.AFB),
    'ActualActual Euro': ql.ActualActual(ql.ActualActual.Euro),

    'Business252' : ql.Business252(),

    'Thirty360 ISDA': ql.Thirty360(ql.Thirty360.ISDA),
    'Thirty360 USA': ql.Thirty360(ql.Thirty360.USA),
    'Thirty360 BondBAsis': ql.Thirty360(ql.Thirty360.BondBasis),
    'Thirty360 European': ql.Thirty360(ql.Thirty360.European),
    'Thirty360 EuroBondBasis': ql.Thirty360(ql.Thirty360.EurobondBasis),

    'SimpleDayCounter NASD': ql.SimpleDayCounter(),    

    'Business252': ql.Business252()
}

for name, day_count in day_counts.items():
    try:
        year_fraction = day_count.yearFraction(dateS, dateE)
        print(f"{name}: {year_fraction:.4f}")
    except RuntimeError as e:
        print(f"Error calculating year fraction for {name}: {e}")
```

output:

```
Actual360: 30.4694
Actual365Fixed: 30.0521
Error calculating year fraction for Actual365Fixed(Canadian): invalid refPeriodStart
Actual365FixedNoLeap: 30.0301
ActualActual ISDA: 30.0322
ActualActual Bond: 30.0833
ActualActual ISMA: 30.0833
ActualActual Historical: 30.0322
ActualActual Actual365: 30.0322
ActualActual AFB: 30.0301
ActualActual Euro: 30.0301
Business252: 29.9206
Thirty360 ISDA: 30.0278
Thirty360 USA: 30.0306
Thirty360 BondBAsis: 30.0306
Thirty360 European: 30.0278
Thirty360 EuroBondBasis: 30.0278
SimpleDayCounter NASD: 30.0306
```

## Answer by Luigi Ballabio (score 3)

https://quant.stackexchange.com/a/79772

I'm not sure what convention Matlab means when they say "actual/actual"; there are a few of them, see e.g. https://www.isda.org/a/AIJEE/1998-ISDA-memo-%E2%80%9CEMU-and-Market-Conventions-Recent-Developments%E2%80%9D.pdf .

In general, though, the actual/actual conventions are used to calculate coupon period and require to define some kind of underlying reference period (annual, semiannual or whatever the coupons say). If the start and end date of the reference period are not passed, QuantLib is forced to make some kind of assumption. This might be the source of the difference.

Also, the requirement of a reference period might make the actual/actual conventions a poor choice for measuring time in general, as you seem to be doing here (or so I'm guessing, since your two sample dates are 30 years apart and therefore are unlikely to define a coupon). Again, hard to say without knowing what's your purpose—but you might be better served by using some simpler day count convention like actual/360 or actual/365.

## Answer by AKdemy (score 1)

https://quant.stackexchange.com/a/79780

I think the underlying question should be why you would want to use Matlab's date2time computation as opposed to others? Also, do you know the exact use case of date2time specifically? According to Matlab, the following applies:

Honestly, I am not sure of the use case of this, especially when combined with daycounts (it should be what yearfrac shows imho). Yearfrac is also consistent with implementations like quantlib.

What I find even more worrysome is that splitting the dates in date2time gives different answers.

## Answer by TheRealMKS (score 0)

https://quant.stackexchange.com/a/79981

I ran into a similar problem when trying to move MATLAB code to QuantLib. This one is for pricing a bond, but the problem was the underlying calculation of year Fraction.

After some research, I figured out that the problem was due to how time periods were calculated in MATLAB vs how they should be calculated.

Consider the following bond:

Settlement Date: 20/05/2023 Maturity Date: 24/12/2023 Interest Rate (Assume Flat): 5% with Semiannual compounding

To calculate the time periods for compounding, both MATLAB and Quantlib move back in time from maturity date.

Days in First half moving back (24/12/2022 - 24/06/2023): 182 Days in Second half moving back (24/06/2023 - 24/12/2023): 183

We get a nice table of how many days are there in each of these periods:

Now MATLAB would output the periods as 1.192307692 (1+0.192307692) while Quantlib would give them as 1.194520548 (0.597260274 x 2). You can see there is a small difference here which carries over to pricing, yields etc.

As far as I know, there is no way to do mimic MATLAB's calculation in QuantLib.

I created a custom function to calculate the discounting period for prices, yields and durations in python and was able to match Quantlib. I will have to dig that function up but I could post it if it would be of interest.

Shown in full with attribution under the source's licence. Licence: CC BY-SA 4.0 (Stack Exchange)

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.