Pricing a Forward Rate Agreement using QuantLib Python

Can someone please help with the pricing of the following forward rate agreement using QuantLib Python?

A 3×6 forward rate agreement, with a notional of $100,000, the FRA rate being 6%, The FRA settlement date is after 3 months (90 days) and the settlement is based on a 90-day USDLIBOR.

My valuation date is 30 June 2020.

This is my attempt:

import QuantLib as ql

startDate = ql.Date(30, 6, 2020)
ql.Settings.instance().evaluationDate = startDate

spotDates = [ql.Date(30, 6, 2020), ql.Date(31, 12, 2020), ql.Date(30, 6, 2021)]
spotRates = [0.05, 0.05, 0.05]

dayConvention = ql.Thirty360()
calendar = ql.UnitedStates()

maturityDate = calendar.advance(startDate, ql.Period('3M'))

compounding = ql.Simple
compoundingFrequency = ql.Annual

spotCurve = ql.ZeroCurve(spotDates, spotRates, dayConvention, calendar, ql.Linear(), compounding, compoundingFrequency)
spotCurve.enableExtrapolation()
spotCurveHandle = ql.YieldTermStructureHandle(spotCurve)

index = ql.USDLibor(ql.Period('3M'), spotCurveHandle)
index.addFixing(ql.Date(26, 6, 2020), 0.05)
notional = 100000
rate = 0.06

fra = ql.ForwardRateAgreement(startDate, maturityDate, ql.Position.Long, rate, notional, index, spotCurveHandle)
print('NPV:', fra.NPV())

And this is the answer that I get:

NPV: 0.0

The answer that I’m getting is not correct.

Answer

First off (for accuracy) the daycount for USD FRAs is Act/360, ie ql.Actual360(). Also bear in mind that the fixing calendar for FRAs is based on the UK calendar (BBA Libor), which may be different from the settlement calendar (so 4th July is not a fixing holiday, but is a settlement holiday).

The main issue with the OP code is that the FRA start date is the same as the evaluation date. Once you know the fixing, then the FRA is effectively settled, so has a NPV of zero and a cashflow.

The first parameter of ForwardRateAgreement() is the settle date of the FRA. The second, maturity, is the end date. So for a spot 3×6, the start date is 3m from now, and the maturity is 6m from now.

Amending this line to:

dayConvention = ql.Actual360()

and

fra = ql.ForwardRateAgreement(calendar.advance(startDate,ql.Period(3,ql.Months)), calendar.advance(startDate,ql.Period(6,ql.Months)), ql.Position.Long, rate, notional, index, spotCurveHandle)

will give you the dates for a spot 3×6 FRA (30-Sep-20 -> 30-Dec-20).

Output: NPV: -262.08622386063985

NB. In the QuantLib example for FRA’s, they use a selection of FRA market rates to build a PiecewiseLogLinearDiscount curve to value the FRA, rather than a zero curve, and I guess it is the subtle differences between the two which is leading to the slight change in forward rate (and hence NPV). The implied forward rate for the FRA comes out at 4.937% … ie not 5%, which also increases the NPV. In practice, if you are trying to price FRAs then build a curve using market FRA quotes.