Skip to content

Commit 78ef6b8

Browse files
mikelyncattack68
andauthored
ENH: add cashflows_table to _WithCashflows (#160) (#1085)
Co-authored-by: JHM Darbyshire <[email protected]> Co-authored-by: JHM Darbyshire (M1) <[email protected]>
1 parent b9916c3 commit 78ef6b8

File tree

4 files changed

+119
-22
lines changed

4 files changed

+119
-22
lines changed

python/rateslib/instruments/components/protocols/cashflows.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from pandas import DataFrame, concat, isna
77

8+
from rateslib import defaults
89
from rateslib.enums.generics import NoInput
910
from rateslib.instruments.components.protocols.kwargs import _KWArgs
1011
from rateslib.instruments.components.protocols.pricing import (
@@ -156,3 +157,57 @@ def _cashflows_from_instruments(self, *args: Any, **kwargs: Any) -> DataFrame:
156157
keys=[f"inst{i}" for i in range(len(self.instruments))],
157158
)
158159
return _
160+
161+
def cashflows_table(
162+
self,
163+
curves: Curves_ = NoInput(0),
164+
solver: Solver_ = NoInput(0),
165+
fx: FXForwards_ = NoInput(0),
166+
fx_vol: FXVolOption_ = NoInput(0),
167+
base: str_ = NoInput(0),
168+
settlement: datetime_ = NoInput(0),
169+
forward: datetime_ = NoInput(0),
170+
) -> DataFrame:
171+
"""
172+
Aggregate the values derived from a
173+
:meth:`~rateslib.instruments.components._BaseInstrument.cashflows`
174+
method on a :class:`~rateslib.instruments.components._BaseInstrument`.
175+
176+
Parameters
177+
----------
178+
XXX
179+
180+
Returns
181+
-------
182+
DataFrame
183+
"""
184+
cashflows = self.cashflows(
185+
curves=curves,
186+
solver=solver,
187+
fx=fx,
188+
fx_vol=fx_vol,
189+
base=base,
190+
settlement=settlement,
191+
forward=forward,
192+
)
193+
cashflows = cashflows[
194+
[
195+
defaults.headers["currency"],
196+
defaults.headers["collateral"],
197+
defaults.headers["payment"],
198+
defaults.headers["cashflow"],
199+
]
200+
]
201+
_: DataFrame = cashflows.groupby( # type: ignore[assignment]
202+
[
203+
defaults.headers["currency"],
204+
defaults.headers["collateral"],
205+
defaults.headers["payment"],
206+
],
207+
dropna=False,
208+
)
209+
_ = _.sum().unstack([0, 1]).droplevel(0, axis=1)
210+
_.columns.names = ["local_ccy", "collateral_ccy"]
211+
_.index.names = ["payment"]
212+
_ = _.sort_index(ascending=True, axis=0).infer_objects().fillna(0.0)
213+
return _

python/tests/instruments/test_instruments_legacy.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4736,7 +4736,8 @@ def test_fixings_table_null_inst(self, curve):
47364736
irs = IRS(dt(2022, 1, 15), "6m", spec="eur_irs3", curves=curve)
47374737
frb = FixedRateBond(dt(2022, 1, 1), "5y", "A", fixed_rate=2.0, curves=curve)
47384738
pf = Portfolio([irs, frb])
4739-
assert isinstance(pf.fixings_table(), DataFrame)
4739+
table = pf.local_analytic_rate_fixings()
4740+
assert isinstance(table, DataFrame)
47404741

47414742

47424743
class TestFly:
@@ -4906,7 +4907,8 @@ def test_fixings_table_null_inst(self, curve):
49064907
irs = IRS(dt(2022, 1, 15), "6m", spec="eur_irs3", curves=curve)
49074908
frb = FixedRateBond(dt(2022, 1, 1), "5y", "A", fixed_rate=2.0, curves=curve)
49084909
spd = Spread(irs, frb)
4909-
assert isinstance(spd.fixings_table(), DataFrame)
4910+
table = spd.local_analytic_rate_fixings()
4911+
assert isinstance(table, DataFrame)
49104912

49114913

49124914
class TestSensitivities:
@@ -5126,6 +5128,8 @@ def test_bill(self) -> None:
51265128
assert bill.kwargs["fixed_rate"] == 0.0
51275129

51285130
def test_fra(self) -> None:
5131+
from rateslib.enums.parameters import FloatFixingMethod
5132+
51295133
fra = FRA(
51305134
effective=dt(2022, 1, 1),
51315135
termination="3m",
@@ -5134,12 +5138,13 @@ def test_fra(self) -> None:
51345138
modifier="F",
51355139
fixed_rate=2.0,
51365140
)
5137-
assert fra.kwargs["leg2_fixing_method"] == "ibor"
5138-
assert fra.kwargs["convention"] == "act360"
5139-
assert fra.kwargs["currency"] == "eur"
5140-
assert fra.kwargs["fixed_rate"] == 2.0
5141-
assert fra.kwargs["leg2_schedule"].payment_adjuster == Adjuster.BusDaysLagSettle(5)
5142-
assert fra.kwargs["leg2_schedule"].modifier == Adjuster.Following()
5141+
assert fra.kwargs.leg2["fixing_method"] == FloatFixingMethod.IBOR
5142+
assert fra.kwargs.leg1["convention"] == "act360"
5143+
assert fra.kwargs.leg1["currency"] == "eur"
5144+
assert fra.kwargs.leg2["currency"] == "eur"
5145+
assert fra.kwargs.leg1["fixed_rate"] == 2.0
5146+
assert fra.kwargs.leg2["schedule"].payment_adjuster == Adjuster.BusDaysLagSettleInAdvance(5)
5147+
assert fra.kwargs.leg2["schedule"].modifier == Adjuster.Following()
51435148

51445149
def test_frn(self) -> None:
51455150
frn = FloatRateNote(
@@ -5176,12 +5181,19 @@ def test_xcs(self) -> None:
51765181
("inst", "expected"),
51775182
[
51785183
(
5179-
IRS(dt(2022, 1, 1), "9M", "Q", currency="eur", curves=["eureur", "eur_eurusd"]),
5184+
IRS(
5185+
dt(2022, 1, 1),
5186+
"9M",
5187+
"Q",
5188+
currency="eur",
5189+
curves=["eureur", "eur_eurusd"],
5190+
fixed_rate=4.0,
5191+
),
51805192
DataFrame(
5181-
[-0.21319, -0.00068, 0.21656],
5193+
data=[-3808.80973, -3850.91496, -3893.01546],
51825194
index=Index([dt(2022, 4, 3), dt(2022, 7, 3), dt(2022, 10, 3)], name="payment"),
51835195
columns=MultiIndex.from_tuples(
5184-
[("EUR", "usd,eur")],
5196+
tuples=[("EUR", "usd,eur")],
51855197
names=["local_ccy", "collateral_ccy"],
51865198
),
51875199
),
@@ -5196,7 +5208,7 @@ def test_xcs(self) -> None:
51965208
curves=["eureur", "eurusd", "eureur"],
51975209
),
51985210
DataFrame(
5199-
[-0.51899, -6260.7208, 6299.28759],
5211+
[-0.00, -6260.19615, 6299.81823],
52005212
index=Index([dt(2022, 4, 3), dt(2022, 7, 3), dt(2022, 10, 3)], name="payment"),
52015213
columns=MultiIndex.from_tuples(
52025214
[("EUR", "usd")],
@@ -5205,9 +5217,16 @@ def test_xcs(self) -> None:
52055217
),
52065218
),
52075219
(
5208-
FRA(dt(2022, 1, 15), "3M", "Q", currency="eur", curves=["eureur", "eureur"]),
5220+
FRA(
5221+
dt(2022, 1, 15),
5222+
"3M",
5223+
"Q",
5224+
currency="eur",
5225+
curves=["eureur", "eureur"],
5226+
fixed_rate=4.0,
5227+
),
52095228
DataFrame(
5210-
[0],
5229+
[-16091.56434],
52115230
index=Index([dt(2022, 1, 15)], name="payment"),
52125231
columns=MultiIndex.from_tuples(
52135232
[("EUR", "eur")],
@@ -5237,17 +5256,18 @@ def test_xcs(self) -> None:
52375256
"M",
52385257
currency="eur",
52395258
leg2_currency="usd",
5259+
leg2_mtm=True,
52405260
curves=["eureur", "eurusd", "usdusd", "usdusd"],
52415261
),
52425262
DataFrame(
52435263
[
5244-
[1000000.0, -1100306.44592],
5245-
[0.0, -2377.85237],
5246-
[-2042.44624, 4630.97800],
5247-
[0.0, -2152.15417],
5248-
[-1844.59236, 4191.00589],
5249-
[-1000000, 1104836.45246],
5250-
[-2042.44624, 4650.04393],
5264+
[1000000.0, -1100306.44743],
5265+
[0.0, -2377.86409],
5266+
[-2128.20822, 4630.97804],
5267+
[0.0, -2152.16480],
5268+
[-1922.05479, 4191.00596],
5269+
[-1000000, 1104836.47633],
5270+
[-2128.20822, 4650.04405],
52515271
],
52525272
index=Index(
52535273
[
@@ -5275,7 +5295,7 @@ def test_xcs(self) -> None:
52755295
curves=["eureur", "eurusd", "usdusd", "usdusd"],
52765296
),
52775297
DataFrame(
5278-
[[1000000.0, -1100306.44592], [-1005943.73163, 1113805.13741]],
5298+
[[-1000000.0, 1100306.44743], [1000000.0, -1107224.13024]],
52795299
index=Index([dt(2022, 1, 5), dt(2022, 4, 5)], name="payment"),
52805300
columns=MultiIndex.from_tuples(
52815301
[("EUR", "usd"), ("USD", "usd")],

python/tests/scheduling/test_schedulers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,18 @@ def test_single_period_schedule2():
9191
from rateslib import IRS
9292

9393
IRS(dt(2022, 7, 1), "3M", "A", curves="eureur", notional=1e6)
94+
95+
96+
@pytest.mark.parametrize(
97+
("a", "b", "expected"),
98+
[
99+
(Adjuster.ModifiedFollowing(), Adjuster.ModifiedFollowing(), True),
100+
(Adjuster.Following(), Adjuster.ModifiedFollowing(), False),
101+
(Adjuster.BusDaysLagSettleInAdvance(3), Adjuster.BusDaysLagSettleInAdvance(3), True),
102+
(Adjuster.BusDaysLagSettleInAdvance(3), Adjuster.Following(), False),
103+
(Adjuster.BusDaysLagSettle(2), Adjuster.BusDaysLagSettle(1), False),
104+
],
105+
)
106+
def test_adjuster_equality(a, b, expected):
107+
result = a == b
108+
assert result is expected

rust/scheduling/calendars/adjuster.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,13 @@ mod tests {
250250
Cal::new(hols, vec![5, 6])
251251
}
252252

253+
#[test]
254+
fn test_equality() {
255+
assert_eq!(Adjuster::Following {}, Adjuster::Following {});
256+
assert_eq!(Adjuster::BusDaysLagSettle(3), Adjuster::BusDaysLagSettle(3));
257+
assert_ne!(Adjuster::BusDaysLagSettle(3), Adjuster::BusDaysLagSettle(5));
258+
}
259+
253260
#[test]
254261
fn test_adjusts() {
255262
let cal = fixture_hol_cal();

0 commit comments

Comments
 (0)