domokane / FinancePy

A Python Finance Library that focuses on the pricing and risk-management of Financial Derivatives, including fixed-income, equity, FX and credit derivatives.
GNU General Public License v3.0
2.16k stars 322 forks source link

Issue with OIS swap rate #107

Closed stanlxr closed 3 years ago

stanlxr commented 3 years ago

Hi, I am currently pricing OIS products using Financepy. I notice that for different notional, I got a swap rate that is inversely proportional to the notional I put in, which in theory should remain the same whatever the notional of the swap is. Below is an example:

Sample code: for notional in range(10): print(OIS(effective_date=from_datetime(datetime.date(2021, 9, 8)), termination_date_or_tenor='5y', fixed_leg_type=SwapTypes.PAY, fixed_coupon=0.05 / 100.0, fixedFreqType=usd_ois_sofr_curve.ois_swap_fixleg_freq, fixed_day_count_type=usd_ois_sofr_curve.ois_swap_fixleg_daycount, notional=10notional, payment_lag=2, float_spread=0.0, floatFreqType=usd_ois_sofr_curve.ois_swap_fltleg_freq, float_day_count_type=usd_ois_sofr_curve.ois_swap_fltleg_daycount, calendar_type=usd_ois_sofr_curve.calendar_type, bus_day_adjust_type=usd_ois_sofr_curve.bus_day_adjust_type, date_gen_rule_type=usd_ois_sofr_curve.date_gen_rule_type ).swap_rate(today, usd_ois_sofr_curve.ois_curve)) Output: 0.006460395437823774 0.0006460395437823774 6.460395437823775e-05 6.4603954378237746e-06 6.460395437823774e-07 6.460395437823772e-08 6.460395437823773e-09 6.460395437823774e-10 6.460395437823775e-11 6.460395437823774e-12 Potential Issue:** I digged into the code: def swap_rate(self, valuation_date, oisCurve): """ Calculate the fixed leg coupon that makes the swap worth zero. If the valuation date is before the swap payments start then this is the forward swap rate as it starts in the future. The swap rate is then a forward swap rate and so we use a forward discount factor. If the swap fixed leg has begun then we have a spot starting swap. """

    pv01 = self.pv01(valuation_date, oisCurve)

    if valuation_date < self._effective_date:
        df0 = oisCurve.df(self._effective_date)
    else:
        df0 = oisCurve.df(valuation_date)

    floatLegPV = 0.0

    dfT = oisCurve.df(self._maturity_date)
    floatLegPV = (df0 - dfT) 
    floatLegPV /= self._fixed_leg._notional        # This seems unnecessary
    cpn = floatLegPV / pv01           
    return cpn

Apparently the commented line seems redundant in that the floatLegPV represented as the difference between two discount factors implicitly says the notional is 1. After removing this line, I no longer have this issue. Could you take a look at this and see if I am missing anything? Thanks as always.

domokane commented 3 years ago

Fixed. Thanks. I will push a new version into pip as soon as I can.