Why a Forward Rate Agreement Can Have Zero NPV at Its Value Date
Summary
This QuantLib example clarifies that the instrument in the question is a forward rate agreement (FRA), not an FX forward. The accepted answer explains why its net present value is zero when valued on the settlement or value date: an FRA settles at the start of its interest period, so it has expired by that date even though the contractual maturity date is later.
The answer demonstrates the timing effect by moving the evaluation date to earlier dates and checking the instrument’s expired status. Before the value date, the example reports a nonzero NPV; on the value date, QuantLib marks the FRA expired and returns zero. The discussion also points out that the example uses a rates product and does not provide an FX forward valuation method. Its explanation is specific to the FRA lifecycle and the dates and setup shown; it does not analyze curve construction or establish broader pricing results for FX contracts.
Key ideas
- The example constructs a forward rate agreement, which is an interest-rate product rather than an FX forward.
- An FRA settles at the beginning of its interest period, on its value date.
- Valuing the FRA on or after that date can produce zero because the instrument is expired.
- Changing the evaluation date to before the value date demonstrates the difference in NPV and expiry status.
Tags
Full text
# Why is the NPV of this FX Forward 0?
# Why is the NPV of this FX Forward 0?
I've checked similar questions and answers but even after setting the evaluation date in ql.settings I still get zero. Like another poster, I also have the QL cookbook and have read everything I could find, but still struggling. Thanks for any help!
```
import QuantLib as ql
calc_date= ql.Date(21,ql.July,2023)
ql.Settings.instance().setEvaluationDate(calc_date)
dayConvention = ql.Thirty360(ql.Thirty360.BondBasis)
calendar = ql.UnitedStates(ql.UnitedStates.NYSE)
trade_date = ql.Date(20,ql.July,2023)
maturityDate = ql.Date(4,ql.August,2023)
spotDates = [ql.Date(20,ql.July,2023), ql.Date(4,ql.August,2023), ql.Date(4,ql.August,2024)]
spotRates = [0.01318, 00.01318,0.01318]
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('1W'), spotCurveHandle)
index.addFixing(ql.Date(18, ql.July, 2023),0.01318)
# index.addFixing(ql.Date(26, 6, 2020), 0.05)
notional = 54760000
rate = 1.37825/100
fra = ql.ForwardRateAgreement(trade_date, maturityDate, ql.Position.Long, rate, notional, index, spotCurveHandle)
print('NPV:', fra.NPV())
```
## Answer by oronimbus (score 4, accepted)
https://quant.stackexchange.com/a/76297
This is not an FX Forward but a Forward Rate Agreement (a rates product). I'm not sure if QuantLib has a FX Forward pricer but they do have one for FX swaps (see FXSwapRateHelper).
Now, to get a non-zero NPV you probably want to add a valuation date before the settlement date:
```
for day in [18, 19, 20]:
ql.Settings.instance().evaluationDate = ql.Date(day, 7, 2023)
fra = ql.ForwardRateAgreement(trade_date, maturityDate, ql.Position.Long, rate, notional, index, spotCurveHandle)
print('NPV:', round(fra.NPV(), 2), "Expired:", fra.isExpired())
```
Which gives:
```
NPV: -1373.95 Expired: False
NPV: -1373.95 Expired: False
NPV: 0.0 Expired: True
```
Remember that a FRA pays at the beginning of the period (see e.g. this paper). This is also explained in the QL documentation here: "the FRA settles and expires on the valueDate, not on the (later) maturityDate". The life cycle looks as follows: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.