@@ -78,7 +78,7 @@ def curve2():
7878 dt (2022 , 7 , 1 ): 0.97 ,
7979 dt (2022 , 10 , 1 ): 0.95 ,
8080 }
81- return Curve (nodes = nodes , interpolation = "log_linear" )
81+ return Curve (nodes = nodes , interpolation = "log_linear" , index_base = 100.0 )
8282
8383
8484@pytest .fixture
@@ -4355,6 +4355,18 @@ def test_bad_metric_raises(self):
43554355 leg2_fixed = True ,
43564356 ).rate (metric = "bad" )
43574357
4358+ def test_leg1_mtm (self ):
4359+ # if notional given on leg1 this will error with nd `pair` not given.
4360+ XCS (
4361+ effective = dt (2000 , 1 , 1 ),
4362+ termination = "6m" ,
4363+ frequency = "Q" ,
4364+ currency = "eur" ,
4365+ leg2_currency = "usd" ,
4366+ mtm = True ,
4367+ leg2_notional = 10.0 ,
4368+ )
4369+
43584370
43594371class TestFixedFloatXCS :
43604372 def test_mtmfixxcs_rate (self , curve , curve2 ) -> None :
@@ -8039,3 +8051,175 @@ def test_sabr_surface(self):
80398051def test_unpriced_cashflows_string_id (inst ):
80408052 result = inst .cashflows ()
80418053 assert isinstance (result , DataFrame )
8054+
8055+
8056+ @pytest .mark .parametrize (
8057+ ("inst" , "curves" ),
8058+ [
8059+ (IRS (dt (2022 , 2 , 1 ), "1m" , spec = "usd_irs" , fixed_rate = 2.0 ), ["c" ]),
8060+ (
8061+ SBS (dt (2022 , 2 , 1 ), "2m" , frequency = "2M" , leg2_frequency = "1M" , float_spread = 2.0 ),
8062+ ["c" , "c" , "c2" , "c" ],
8063+ ),
8064+ (STIRFuture (dt (2022 , 2 , 1 ), "1m" , spec = "usd_stir1" , price = 99.0 ), ["c" ]),
8065+ (
8066+ XCS (
8067+ dt (2022 , 1 , 1 ),
8068+ "2m" ,
8069+ fixed = True ,
8070+ leg2_fixed = True ,
8071+ fixed_rate = 2.0 ,
8072+ leg2_fixed_rate = 2.5 ,
8073+ frequency = "1M" ,
8074+ leg2_fx_fixings = 2.0 ,
8075+ ),
8076+ ["c" , "c" , "c2" , "c" ],
8077+ ),
8078+ (
8079+ XCS (
8080+ dt (2022 , 1 , 1 ),
8081+ "2m" ,
8082+ fixed = True ,
8083+ fixed_rate = 2.0 ,
8084+ frequency = "1M" ,
8085+ leg2_fx_fixings = 2.0 ,
8086+ ),
8087+ ["c" , "c" , "c2" , "c" ],
8088+ ),
8089+ (
8090+ XCS (
8091+ dt (2022 , 1 , 1 ),
8092+ "2m" ,
8093+ leg2_fixed = True ,
8094+ leg2_fixed_rate = 2.0 ,
8095+ float_spread = 0.0 ,
8096+ frequency = "1M" ,
8097+ leg2_fx_fixings = 2.0 ,
8098+ ),
8099+ ["c" , "c" , "c2" , "c" ],
8100+ ),
8101+ (
8102+ XCS (
8103+ dt (2022 , 1 , 1 ),
8104+ "2m" ,
8105+ float_spread = 0.0 ,
8106+ leg2_float_spread = 0.0 ,
8107+ frequency = "1M" ,
8108+ leg2_fx_fixings = 2.0 ,
8109+ ),
8110+ ["c" , "c" , "c2" , "c" ],
8111+ ),
8112+ (CDS (dt (2022 , 2 , 1 ), "1m" , frequency = "1M" , fixed_rate = 1.0 ), ["c2" , "c" ]),
8113+ (ZCS (dt (2022 , 1 , 1 ), "2m" , frequency = "3M" , fixed_rate = 2.0 ), ["c" ]),
8114+ (
8115+ ZCIS (
8116+ dt (2022 , 1 , 1 ),
8117+ "2M" ,
8118+ frequency = "3M" ,
8119+ fixed_rate = 2.0 ,
8120+ leg2_index_base = 99.0 ,
8121+ leg2_index_lag = 0 ,
8122+ ),
8123+ ["c2" , "c" ],
8124+ ),
8125+ (
8126+ IIRS (
8127+ dt (2022 , 1 , 1 ), "2M" , spec = "usd_irs" , fixed_rate = 2.0 , index_base = 99.0 , index_lag = 0
8128+ ),
8129+ ["c2" , "c" , "c" , "c" ],
8130+ ),
8131+ (FRA (dt (2022 , 2 , 1 ), "1m" , fixed_rate = 1.0 , frequency = "1M" ), ["c" ]),
8132+ ],
8133+ )
8134+ def test_forward_npv_argument (curve , curve2 , inst , curves ):
8135+ c_ = {"c" : curve , "c2" : curve2 }
8136+ npv = inst .npv (curves = [c_ [v ] for v in curves ])
8137+ forward_npv = inst .npv (curves = [c_ [v ] for v in curves ], forward = dt (2022 , 3 , 15 ))
8138+ assert abs (forward_npv - npv / curve [dt (2022 , 3 , 15 )]) < 1e-10
8139+
8140+
8141+ @pytest .mark .parametrize (
8142+ ("inst" , "curves" ),
8143+ [
8144+ (
8145+ XCS (
8146+ dt (2022 , 1 , 1 ),
8147+ "2m" ,
8148+ mtm = True ,
8149+ fixed = True ,
8150+ leg2_fixed = True ,
8151+ fixed_rate = 2.0 ,
8152+ leg2_fixed_rate = 3.0 ,
8153+ frequency = "1M" ,
8154+ fx_fixings = 2.0 ,
8155+ currency = "eur" ,
8156+ leg2_currency = "usd" ,
8157+ leg2_notional = 10e6 ,
8158+ ),
8159+ ["c" , "c" , "c2" , "c2" ],
8160+ ),
8161+ (
8162+ XCS (
8163+ dt (2022 , 1 , 1 ),
8164+ "2m" ,
8165+ mtm = True ,
8166+ fixed = True ,
8167+ fixed_rate = 2.0 ,
8168+ frequency = "1M" ,
8169+ fx_fixings = 2.0 ,
8170+ currency = "eur" ,
8171+ leg2_currency = "usd" ,
8172+ leg2_notional = 10e6 ,
8173+ ),
8174+ ["c" , "c" , "c2" , "c2" ],
8175+ ),
8176+ (
8177+ XCS (
8178+ dt (2022 , 1 , 1 ),
8179+ "2m" ,
8180+ mtm = True ,
8181+ leg2_fixed = True ,
8182+ leg2_fixed_rate = 2.0 ,
8183+ float_spread = 0.0 ,
8184+ frequency = "1M" ,
8185+ fx_fixings = 2.0 ,
8186+ currency = "eur" ,
8187+ leg2_currency = "usd" ,
8188+ leg2_notional = 10e6 ,
8189+ ),
8190+ ["c" , "c" , "c2" , "c2" ],
8191+ ),
8192+ (
8193+ XCS (
8194+ dt (2022 , 1 , 1 ),
8195+ "2m" ,
8196+ mtm = True ,
8197+ float_spread = 0.0 ,
8198+ leg2_float_spread = 0.0 ,
8199+ frequency = "1M" ,
8200+ fx_fixings = 2.0 ,
8201+ currency = "eur" ,
8202+ leg2_currency = "usd" ,
8203+ leg2_notional = 10e6 ,
8204+ ),
8205+ ["c" , "c" , "c2" , "c2" ],
8206+ ),
8207+ (NDF (dt (2022 , 2 , 15 ), pair = "eurusd" , fx_rate = 1.15 ), ["c2" ]),
8208+ (
8209+ FXSwap (dt (2022 , 2 , 15 ), "1m" , pair = "eurusd" , leg2_fx_fixings = 1.15 , points = 56.5 ),
8210+ ["c" , "c2" ],
8211+ ),
8212+ (FXForward (dt (2022 , 2 , 16 ), "eurusd" , fx_rate = 1.15 ), ["c" , "c2" ]),
8213+ ],
8214+ )
8215+ def test_forward_npv_argument_with_fx (curve , curve2 , inst , curves ):
8216+ fxr = FXRates ({"eurusd" : 1.12 }, settlement = dt (2022 , 1 , 3 ))
8217+ fxf = FXForwards (fx_rates = fxr , fx_curves = {"eureur" : curve , "eurusd" : curve , "usdusd" : curve2 })
8218+ c_ = {"c" : curve , "c2" : curve2 }
8219+ npv = inst .npv (curves = [c_ [v ] for v in curves ], fx = fxf , base = "eur" )
8220+ forward_npv = inst .npv (
8221+ curves = [c_ [v ] for v in curves ], forward = dt (2022 , 3 , 15 ), fx = fxf , base = "eur"
8222+ )
8223+
8224+ result = npv / curve [dt (2022 , 3 , 15 )] - forward_npv
8225+ assert abs (result ) < 1e-7
0 commit comments