Skip to content

Commit 6f09ed7

Browse files
attack68mikelync
andauthored
ENH: LegMtm enum and non-deliverability of Legs (#207) (#1123)
Co-authored-by: mikelync <[email protected]>
1 parent 646763a commit 6f09ed7

File tree

19 files changed

+1116
-495
lines changed

19 files changed

+1116
-495
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ ignore = [
133133
# "B006", # mutable data structures for argument defaults, e.g. []
134134
"B904", # raising within except clauses
135135
"B028", # no explicit stack level
136+
"E702", # semi-colons for multiple line statements
136137
]
137138

138139
[tool.ruff.lint.per-file-ignores]

python/rateslib/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def __exit__(self, *args) -> None: # type: ignore[no-untyped-def]
7070
IIRS,
7171
IRS,
7272
NDF,
73+
NDXCS,
7374
SBS,
7475
XCS,
7576
ZCIS,
@@ -256,6 +257,7 @@ def __exit__(self, *args) -> None: # type: ignore[no-untyped-def]
256257
"FXSwap",
257258
"FXForward",
258259
"XCS",
260+
"NDXCS",
259261
"Spread",
260262
"Fly",
261263
"Portfolio",

python/rateslib/data/__instrument_spec.csv

Lines changed: 77 additions & 77 deletions
Large diffs are not rendered by default.

python/rateslib/enums/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
FXDeltaMethod,
55
FXOptionMetric,
66
IndexMethod,
7+
LegMtm,
78
SpreadCompoundMethod,
89
)
910

@@ -13,6 +14,7 @@
1314
"IndexMethod",
1415
"FXDeltaMethod",
1516
"FXOptionMetric",
17+
"LegMtm",
1618
"NoInput",
1719
"Result",
1820
"Ok",

python/rateslib/enums/parameters.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,38 @@ class FXOptionMetric(Enum):
2121
Percent = 1
2222

2323

24+
class LegMtm(Enum):
25+
"""
26+
Enumerable type to define :class:`~rateslib.data.fixings.FXFixing` dates for non-deliverable
27+
*Legs*.
28+
29+
For further information see non-deliverability **Notes** of :class:`~rateslib.legs.FixedLeg`.
30+
"""
31+
32+
Initial = 0
33+
XCS = 1
34+
Payment = 2
35+
36+
37+
_LEG_MTM_MAP = {
38+
"initial": LegMtm.Initial,
39+
"xcs": LegMtm.XCS,
40+
"payment": LegMtm.Payment,
41+
}
42+
43+
44+
def _get_let_mtm(leg_mtm: str | LegMtm) -> LegMtm:
45+
if isinstance(leg_mtm, LegMtm):
46+
return leg_mtm
47+
else:
48+
try:
49+
return _LEG_MTM_MAP[leg_mtm.lower()]
50+
except KeyError:
51+
raise ValueError(
52+
f"`mtm` as string: '{leg_mtm}' is not a valid option. Please consult docs."
53+
)
54+
55+
2456
class IndexMethod(Enum):
2557
"""
2658
Enumerable type to define determining the index value on some reference value date.

python/rateslib/instruments/bonds/bill.py

Lines changed: 104 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
_Vol,
2121
)
2222
from rateslib.legs import FixedLeg
23+
from rateslib.scheduling import Schedule
2324
from rateslib.scheduling.frequency import _get_frequency
2425

2526
if TYPE_CHECKING:
@@ -30,10 +31,12 @@
3031
DualTypes_,
3132
FXForwards_,
3233
Number,
34+
RollDay,
3335
Sequence,
3436
Solver_,
3537
VolT_,
3638
_BaseLeg,
39+
bool_,
3740
datetime,
3841
datetime_,
3942
int_,
@@ -43,153 +46,110 @@
4346

4447
class Bill(_BaseBondInstrument):
4548
"""
46-
Create a discount security.
49+
A *bill*, or discount security, composed of a :class:`~rateslib.legs.FixedLeg`.
4750
48-
Parameters
49-
----------
50-
effective : datetime
51-
The adjusted or unadjusted effective date.
52-
termination : datetime or str
53-
The adjusted or unadjusted termination date. If a string, then a tenor must be
54-
given expressed in days (`"D"`), months (`"M"`) or years (`"Y"`), e.g. `"7M"`.
55-
frequency : str in {"M", "B", "Q", "T", "S", "A"}, optional
56-
The frequency used only by the :meth:`~rateslib.instruments.Bill.ytm` method.
57-
All *Bills* have an implicit frequency of "Z" for schedule construction.
58-
modifier : str, optional
59-
The modification rule, in {"F", "MF", "P", "MP"}
60-
calendar : calendar or str, optional
61-
The holiday calendar object to use. If str, looks up named calendar from
62-
static data.
63-
payment_lag : int, optional
64-
The number of business days to lag payments by.
65-
notional : float, optional
66-
The leg notional, which is applied to each period.
67-
currency : str, optional
68-
The currency of the leg (3-digit code).
69-
convention: str, optional
70-
The day count convention applied to calculations of period accrual dates.
71-
See :meth:`~rateslib.scheduling.dcf`.
72-
settle : int
73-
The number of business days for regular settlement time, i.e, 1 is T+1.
74-
calc_mode : str, optional (defaults.calc_mode["Bill"])
75-
A calculation mode for dealing with bonds that are in short stub or accrual
76-
periods. All modes give the same value for YTM at issue date for regular
77-
bonds but differ slightly for bonds with stubs or with accrued.
78-
curves : CurveType, str or list of such, optional
79-
A single *Curve* or string id or a list of such.
80-
81-
A list defines the following curves in the order:
82-
83-
- Forecasting *Curve* for ``leg1``.
84-
- Discounting :class:`~rateslib.curves.Curve` for ``leg1``.
85-
spec : str, optional
86-
An identifier to pre-populate many field with conventional values. See
87-
:ref:`here<defaults-doc>` for more info and available values.
88-
metric: str, optional
89-
The pricing metric returned by the ``rate`` method of the *Instrument*.
90-
91-
Examples
92-
--------
93-
This example is taken from the US Treasury Federal website. A copy of
94-
which is available :download:`here<_static/ofcalc6decTbill.pdf>`.
95-
96-
We demonstrate the use of **analogue methods** which do not need *Curves* or
97-
*Solvers*,
98-
:meth:`~rateslib.instruments.Bill.price`,
99-
:meth:`~rateslib.instruments.Bill.simple_rate`,
100-
:meth:`~rateslib.instruments.Bill.discount_rate`,
101-
:meth:`~rateslib.instruments.FixedRateBond.ytm`,
102-
:meth:`~rateslib.instruments.FixedRateBond.ex_div`,
103-
:meth:`~rateslib.instruments.FixedRateBond.accrued`,
104-
:meth:`~rateslib.instruments.FixedRateBond.repo_from_fwd`
105-
:meth:`~rateslib.instruments.FixedRateBond.fwd_from_repo`
106-
:meth:`~rateslib.instruments.FixedRateBond.duration`,
107-
:meth:`~rateslib.instruments.FixedRateBond.convexity`.
51+
.. rubric:: Examples
10852
10953
.. ipython:: python
11054
:suppress:
11155
112-
from rateslib import Bill, Solver
56+
from rateslib.instruments import Bill
57+
from datetime import datetime as dt
11358
11459
.. ipython:: python
11560
11661
bill = Bill(
117-
effective=dt(2004, 1, 22),
118-
termination=dt(2004, 2, 19),
119-
calendar="nyc",
120-
modifier="NONE",
121-
currency="usd",
122-
convention="Act360",
123-
settle=1,
124-
notional=-1e6, # negative notional receives fixed, i.e. buys a bill
125-
curves="bill_curve",
126-
calc_mode="us_gbb",
127-
)
128-
bill.ex_div(dt(2004, 1, 22))
129-
bill.price(rate=0.80, settlement=dt(2004, 1, 22))
130-
bill.simple_rate(price=99.937778, settlement=dt(2004, 1, 22))
131-
bill.discount_rate(price=99.937778, settlement=dt(2004, 1, 22))
132-
bill.ytm(price=99.937778, settlement=dt(2004, 1, 22))
133-
bill.accrued(dt(2004, 1, 22))
134-
bill.fwd_from_repo(
135-
price=99.937778,
136-
settlement=dt(2004, 1, 22),
137-
forward_settlement=dt(2004, 2, 19),
138-
repo_rate=0.8005,
139-
convention="Act360",
62+
effective=dt(2000, 1, 1),
63+
termination="3y",
64+
spec="us_gbb",
14065
)
141-
bill.repo_from_fwd(
142-
price=99.937778,
143-
settlement=dt(2004, 1, 22),
144-
forward_settlement=dt(2004, 2, 19),
145-
forward_price=100.00,
146-
convention="Act360",
147-
)
148-
bill.duration(settlement=dt(2004, 1, 22), ytm=0.8005, metric="risk")
149-
bill.duration(settlement=dt(2004, 1, 22), ytm=0.8005, metric="modified")
150-
bill.convexity(settlement=dt(2004, 1, 22), ytm=0.8005)
66+
bill.cashflows()
15167
68+
.. rubric:: Pricing
15269
153-
The following **digital methods** consistent with the library's ecosystem are
154-
also available,
155-
:meth:`~rateslib.instruments.Bill.rate`,
156-
:meth:`~rateslib.instruments.FixedRateBond.npv`,
157-
:meth:`~rateslib.instruments.FixedRateBond.analytic_delta`,
158-
:meth:`~rateslib.instruments.FixedRateBond.cashflows`,
159-
:meth:`~rateslib.instruments.FixedRateBond.delta`,
160-
:meth:`~rateslib.instruments.FixedRateBond.gamma`,
70+
A *Bill* requires one *disc curve*. The following input formats are
71+
allowed:
16172
162-
.. ipython:: python
73+
.. code-block:: python
16374
164-
bill_curve = Curve({dt(2004, 1, 21): 1.0, dt(2004, 3, 21): 1.0}, id="bill_curve")
165-
instruments = [
166-
(bill, {"metric": "ytm"}),
167-
]
168-
solver = Solver(
169-
curves=[bill_curve],
170-
instruments=instruments,
171-
s=[0.8005],
172-
instrument_labels=["Feb04 Tbill"],
173-
id="bill_solver",
174-
)
175-
bill.npv(solver=solver)
176-
bill.analytic_delta(curves=bill_curve)
177-
bill.rate(solver=solver, metric="price")
75+
curves = curve | [curve] # a single curve is repeated for all required curves
76+
curves = {"disc_curve": disc_curve} # dict form is explicit
17877
179-
The sensitivities are also available. In this case the *Solver* is calibrated
180-
with *instruments* priced in yield terms so sensitivities are measured in basis
181-
points (bps).
78+
.. role:: red
18279
183-
.. ipython:: python
80+
.. role:: green
18481
185-
bill.delta(solver=solver)
186-
bill.gamma(solver=solver)
82+
Parameters
83+
----------
84+
.
85+
86+
.. note::
87+
88+
The following define generalised **scheduling** parameters.
89+
90+
effective : datetime, :red:`required`
91+
The unadjusted effective date. If given as adjusted, unadjusted alternatives may be
92+
inferred.
93+
termination : datetime, str, :red:`required`
94+
The unadjusted termination date. If given as adjusted, unadjusted alternatives may be
95+
inferred. If given as string tenor will be calculated from ``effective``.
96+
frequency : Frequency, str, :red:`required`
97+
The frequency of the schedule.
98+
If given as string will derive a :class:`~rateslib.scheduling.Frequency` aligning with:
99+
monthly ("M"), quarterly ("Q"), semi-annually ("S"), annually("A") or zero-coupon ("Z"), or
100+
a set number of calendar or business days ("_D", "_B"), weeks ("_W"), months ("_M") or
101+
years ("_Y").
102+
Where required, the :class:`~rateslib.scheduling.RollDay` is derived as per ``roll``
103+
and business day calendar as per ``calendar``.
104+
roll : RollDay, int in [1, 31], str in {"eom", "imm", "som"}, :green:`optional`
105+
The roll day of the schedule. If not given or not available in ``frequency`` will be
106+
inferred for monthly frequency variants.
107+
eom : bool, :green:`optional`
108+
Use an end of month preference rather than regular rolls for ``roll`` inference. Set by
109+
default. Not required if ``roll`` is defined.
110+
modifier : Adjuster, str in {"NONE", "F", "MF", "P", "MP"}, :green:`optional`
111+
The :class:`~rateslib.scheduling.Adjuster` used for adjusting unadjusted schedule dates
112+
into adjusted dates. If given as string must define simple date rolling rules.
113+
calendar : calendar, str, :green:`optional`
114+
The business day calendar object to use. If string will call
115+
:meth:`~rateslib.scheduling.get_calendar`.
116+
payment_lag: Adjuster, int, :green:`optional`
117+
The :class:`~rateslib.scheduling.Adjuster` to use to map adjusted schedule dates into
118+
a payment date. If given as integer will define the number of business days to
119+
lag payments by.
120+
ex_div: Adjuster, int, :green:`optional`
121+
The :class:`~rateslib.scheduling.Adjuster` to use to map adjusted schedule dates into
122+
additional dates, which may be used, for example by fixings schedules. If given as integer
123+
will define the number of business days to lag dates by.
124+
convention: str, :green:`optional (set by 'defaults')`
125+
The day count convention applied to calculations of period accrual dates.
126+
See :meth:`~rateslib.scheduling.dcf`.
187127
188-
The DataFrame of cashflows.
128+
.. note::
189129
190-
.. ipython:: python
130+
The following define generalised **settlement** parameters.
191131
192-
bill.cashflows(solver=solver)
132+
currency : str, :green:`optional (set by 'defaults')`
133+
The local settlement currency of the *Instrument* (3-digit code).
134+
notional : float, Dual, Dual2, Variable, :green:`optional (set by 'defaults')`
135+
The initial leg notional, defined in units of *reference currency*.
136+
137+
.. note::
138+
139+
The following are **meta parameters**.
140+
141+
curves : _BaseCurve, str, dict, _Curves, Sequence, :green:`optional`
142+
Pricing objects passed directly to the *Instrument's* methods' ``curves`` argument. See
143+
**Pricing**.
144+
calc_mode : str or BillCalcMode
145+
A calculation mode for dealing with bonds under different conventions. See notes.
146+
settle: int
147+
The number of days by which to lag 'today' to arrive at standard settlement.
148+
metric : str, :green:`optional` (set as 'price')
149+
The pricing metric returned by :meth:`~rateslib.instruments.FixedRateBond.rate`.
150+
spec: str, :green:`optional`
151+
A collective group of parameters. See
152+
:ref:`default argument specifications <defaults-arg-input>`.
193153
194154
"""
195155

@@ -210,12 +170,15 @@ def __init__(
210170
effective: datetime_ = NoInput(0),
211171
termination: datetime | str_ = NoInput(0),
212172
frequency: str_ = NoInput(0),
173+
roll: int | RollDay | str_ = NoInput(0),
174+
eom: bool_ = NoInput(0),
213175
modifier: str_ = NoInput(0),
214176
calendar: CalInput = NoInput(0),
215177
payment_lag: int_ = NoInput(0),
216178
notional: DualTypes_ = NoInput(0),
217179
currency: str_ = NoInput(0),
218180
convention: str_ = NoInput(0),
181+
ex_div: int_ = NoInput(0),
219182
settle: int_ = NoInput(0),
220183
calc_mode: BillCalcMode | str_ = NoInput(0),
221184
curves: CurvesT_ = NoInput(0),
@@ -229,6 +192,9 @@ def __init__(
229192
modifier=modifier,
230193
calendar=calendar,
231194
payment_lag=payment_lag,
195+
ex_div=ex_div,
196+
roll=roll,
197+
eom=eom,
232198
notional=notional,
233199
currency=currency,
234200
convention=convention,
@@ -260,6 +226,17 @@ def __init__(
260226
meta_args=["curves", "calc_mode", "settle", "metric", "frequency", "vol"],
261227
)
262228
self.kwargs.meta["calc_mode"] = _get_bill_calc_mode(self.kwargs.meta["calc_mode"])
229+
if isinstance(self.kwargs.leg1["termination"], str):
230+
s_ = Schedule(
231+
effective=self.kwargs.leg1["effective"],
232+
termination=self.kwargs.leg1["termination"],
233+
frequency=self.kwargs.leg1["termination"],
234+
modifier=self.kwargs.leg1["modifier"],
235+
calendar=self.kwargs.leg1["calendar"],
236+
roll=self.kwargs.leg1["roll"],
237+
eom=self.kwargs.leg1["eom"],
238+
)
239+
self._kwargs.leg1["termination"] = s_.termination
263240
self._kwargs.leg1["frequency"] = "Z"
264241
self._kwargs.meta["frequency"] = _drb(
265242
self.kwargs.meta["calc_mode"]._ytm_clone_kwargs["frequency"],

python/rateslib/instruments/bonds/bond_future.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class BondFuture(_BaseInstrument):
9292
.. ipython:: python
9393
:suppress:
9494
95-
from rateslib import BondFuture
95+
from rateslib import BondFuture, Solver
9696
9797
.. ipython:: python
9898

0 commit comments

Comments
 (0)