PYield 0.12.0__tar.gz → 0.12.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. {pyield-0.12.0 → pyield-0.12.2}/PKG-INFO +1 -1
  2. pyield-0.12.2/pyield/__about__.py +1 -0
  3. {pyield-0.12.0 → pyield-0.12.2}/pyield/bonds/ntnf.py +61 -18
  4. {pyield-0.12.0 → pyield-0.12.2}/pyield/di.py +1 -3
  5. {pyield-0.12.0 → pyield-0.12.2}/pyproject.toml +2 -2
  6. pyield-0.12.0/pyield/__about__.py +0 -1
  7. {pyield-0.12.0 → pyield-0.12.2}/LICENSE +0 -0
  8. {pyield-0.12.0 → pyield-0.12.2}/README.md +0 -0
  9. {pyield-0.12.0 → pyield-0.12.2}/pyield/__init__.py +0 -0
  10. {pyield-0.12.0 → pyield-0.12.2}/pyield/bday.py +0 -0
  11. {pyield-0.12.0 → pyield-0.12.2}/pyield/bonds/__init__.py +0 -0
  12. {pyield-0.12.0 → pyield-0.12.2}/pyield/bonds/lft.py +0 -0
  13. {pyield-0.12.0 → pyield-0.12.2}/pyield/bonds/ltn.py +0 -0
  14. {pyield-0.12.0 → pyield-0.12.2}/pyield/bonds/ntnb.py +0 -0
  15. {pyield-0.12.0 → pyield-0.12.2}/pyield/bonds/utils.py +0 -0
  16. {pyield-0.12.0 → pyield-0.12.2}/pyield/date_converter.py +0 -0
  17. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/__init__.py +0 -0
  18. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/anbima.py +0 -0
  19. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/futures_data/__init__.py +0 -0
  20. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/futures_data/common.py +0 -0
  21. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/futures_data/historical.py +0 -0
  22. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/futures_data/intraday.py +0 -0
  23. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/futures_data/xml.py +0 -0
  24. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/indicators.py +0 -0
  25. {pyield-0.12.0 → pyield-0.12.2}/pyield/fetchers/projections.py +0 -0
  26. {pyield-0.12.0 → pyield-0.12.2}/pyield/holidays/__init__.py +0 -0
  27. {pyield-0.12.0 → pyield-0.12.2}/pyield/holidays/br_holidays_new.txt +0 -0
  28. {pyield-0.12.0 → pyield-0.12.2}/pyield/holidays/br_holidays_old.txt +0 -0
  29. {pyield-0.12.0 → pyield-0.12.2}/pyield/holidays/holidays_manager.py +0 -0
  30. {pyield-0.12.0 → pyield-0.12.2}/pyield/interpolator.py +0 -0
  31. {pyield-0.12.0 → pyield-0.12.2}/pyield/py.typed +0 -0
  32. {pyield-0.12.0 → pyield-0.12.2}/tests/__init__.py +0 -0
  33. {pyield-0.12.0 → pyield-0.12.2}/tests/data/SPRD231222.zip +0 -0
  34. {pyield-0.12.0 → pyield-0.12.2}/tests/data/SPRD240426.zip +0 -0
  35. {pyield-0.12.0 → pyield-0.12.2}/tests/test_bday.py +0 -0
  36. {pyield-0.12.0 → pyield-0.12.2}/tests/test_futures.py +0 -0
  37. {pyield-0.12.0 → pyield-0.12.2}/tests/test_futures_xml.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PYield
3
- Version: 0.12.0
3
+ Version: 0.12.2
4
4
  Summary: A Python library for analysis of fixed income instruments in Brazil
5
5
  Keywords: fixed-income, brazil, finance, analysis, bonds
6
6
  Author-Email: Carlos Carvalho <cr.cj@outlook.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.12.2"
@@ -321,43 +321,86 @@ def gross_di_spreads(reference_date: str | pd.Timestamp) -> pd.Series:
321
321
  return df["DISpread"]
322
322
 
323
323
 
324
- def net_di_spread(
325
- reference_date: str | pd.Timestamp,
324
+ def net_di_spread( # noqa
325
+ settlement_date: str | pd.Timestamp,
326
326
  maturity_date: str | pd.Timestamp,
327
+ ytm: float,
328
+ di_rates: list[float] | pd.Series,
329
+ di_expirations: list[pd.Timestamp] | pd.Series,
330
+ initial_guess: float | None = None,
327
331
  ) -> float:
328
- reference_date = dc.convert_date(reference_date)
329
- maturity_date = dc.convert_date(maturity_date)
330
- settlement_date = bday.offset(reference_date, 0)
332
+ """
333
+ Calculate the net DI spread for a bond given the YTM and the DI rates.
334
+
335
+ This function determines the spread over the DI curve that equates the present value
336
+ of the bond's cash flows to its market price. It interpolates the DI rates to match
337
+ the bond's cash flow payment dates and uses the Brent method to find the spread
338
+ (in bps) that zeroes the difference between the bond's market price and its
339
+ discounted cash flows.
340
+
341
+ Parameters:
342
+ ----------
343
+ settlement_date : pd.Timestamp
344
+ The date on which the bond is settled.
345
+ maturity_date : pd.Timestamp
346
+ The date on which the bond matures.
347
+ ytm : float
348
+ The yield to maturity of the bond.
349
+ di_rates : list[float]
350
+ A list of DI rates corresponding to the DI expirations.
351
+ di_expirations : list[pd.Timestamp]
352
+ A list of DI expiration dates.
331
353
 
332
- # Fetch DI rates for the reference date
333
- df_di = di_data.settlement_rates(reference_date, adjust_exp_date=True)
354
+ Returns:
355
+ -------
356
+ float
357
+ The net DI spread in basis points. Returns NaN if a solution cannot be found.
358
+ """
359
+
360
+ # Create an interpolator for the DI rates using the flat-forward method
361
+ settlement_date = dc.convert_date(settlement_date)
362
+ maturity_date = dc.convert_date(maturity_date)
334
363
 
335
364
  ff_interpolator = Interpolator(
336
365
  "flat_forward",
337
- bday.count(settlement_date, df_di["ExpirationDate"]),
338
- df_di["SettlementRate"],
366
+ bday.count(settlement_date, di_expirations),
367
+ di_rates,
339
368
  )
340
369
 
341
- # Get the corresponding YTM for the NTN-F bond
342
- ytm = indicative_rates(reference_date, maturity_date)
370
+ # Ensure the DI rates and expirations lists are of equal length
371
+ if len(di_rates) != len(di_expirations):
372
+ raise ValueError("di_rates and di_expirations must have the same length.")
343
373
 
374
+ # Calculate cash flows and business days between settlement and payment dates
344
375
  df = cash_flows(settlement_date, maturity_date).reset_index()
345
376
  df["BDays"] = bday.count(settlement_date, df["PaymentDate"])
346
377
 
378
+ # Calculate business years (252 business days per year)
347
379
  byears = bday.count(settlement_date, df["PaymentDate"]) / 252
348
- di_rates = df["BDays"].apply(ff_interpolator)
380
+ di_interp = df["BDays"].apply(ff_interpolator)
349
381
  bond_price = price(settlement_date, maturity_date, ytm)
350
382
  bond_cash_flows = df["CashFlow"]
351
383
 
352
384
  def price_difference(p):
353
- return (bond_cash_flows / (1 + di_rates + p) ** (byears)).sum() - bond_price
385
+ # Calculate the difference between the bond's price and its disc. cash flows
386
+ return (bond_cash_flows / (1 + di_interp + p) ** byears).sum() - bond_price
354
387
 
355
388
  try:
356
- # Tentar encontrar o valor de p que zera a diferença entre os preços
357
- # Intervalo de busca de -0.01 a 0.01 (100 bps)
358
- p_solution = brentq(price_difference, -0.01, 0.01, maxiter=100)
389
+ if initial_guess:
390
+ # Search interval is 50 bps above and below the initial guess
391
+ a = initial_guess - 50 / 10_000 # 50 bps below the initial guess
392
+ b = initial_guess + 50 / 10_000 # 50 bps above the initial guess
393
+ else:
394
+ # Search interval is from -0.01 to 0.01 (i.e., -100 to 100 bps)
395
+ a = -0.01
396
+ b = 0.01
397
+
398
+ # Find the spread (p) that zeroes the price difference
399
+ p_solution = brentq(price_difference, a, b, maxiter=100)
400
+ # Convert the solution to basis points (bps) and round to two decimal places
401
+ p_solution = round((p_solution * 10_000), 2)
359
402
  except (ValueError, RuntimeError):
360
- # Se não houver solução, retornar float('nan')
403
+ # If no solution is found, return NaN
361
404
  p_solution = float("nan")
362
405
 
363
- return p_solution * 10_000
406
+ return p_solution
@@ -44,7 +44,7 @@ class DIData:
44
44
  adjust_exp_date: bool = False,
45
45
  ) -> pd.DataFrame:
46
46
  cls._check_for_updates()
47
- df = cls._df.copy()
47
+ df = cls._df[["TradeDate", "ExpirationDate", "SettlementRate"]].copy()
48
48
 
49
49
  if trade_date:
50
50
  trade_date = dc.convert_date(trade_date)
@@ -56,8 +56,6 @@ class DIData:
56
56
  expiration_date = bday.offset(expiration_date, 0)
57
57
  df.query("ExpirationDate == @expiration_date", inplace=True)
58
58
 
59
- df = df[["TradeDate", "ExpirationDate", "SettlementRate"]].copy()
60
-
61
59
  if adjust_exp_date:
62
60
  df["ExpirationDate"] = (
63
61
  df["ExpirationDate"].dt.to_period("M").dt.to_timestamp()
@@ -34,7 +34,7 @@ dependencies = [
34
34
  "pyarrow",
35
35
  ]
36
36
  dynamic = []
37
- version = "0.12.0"
37
+ version = "0.12.2"
38
38
 
39
39
  [project.license]
40
40
  file = "LICENSE"
@@ -80,4 +80,4 @@ select = [
80
80
 
81
81
  [tool.ruff.format]
82
82
  docstring-code-format = true
83
- docstring-code-line-length = 79
83
+ docstring-code-line-length = 88
@@ -1 +0,0 @@
1
- __version__ = "0.12.0"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes