Skip to content

Commit

Permalink
Use holidays for trading centres
Browse files Browse the repository at this point in the history
  • Loading branch information
lsbardel committed Dec 31, 2024
1 parent 1fef375 commit 28359f9
Show file tree
Hide file tree
Showing 9 changed files with 754 additions and 831 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#IDE
.idea
.vscode

#python
*.pyc
Expand Down
3 changes: 0 additions & 3 deletions .vscode/settings.json

This file was deleted.

7 changes: 4 additions & 3 deletions ccy/core/daycounter.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def act_act_years(dt: date) -> float:
a = 0.0
if r > 0:
a = 1.0
dd = (dt - date(y, 1, 1)).days
dd = (dt - date(y, 1, 1)).total_seconds() / 86400
return y + dd / (365.0 + a)


Expand All @@ -49,8 +49,9 @@ def __new__(cls, name: str, bases: Any, attrs: Any) -> DayCounterMeta:
class DayCounter(metaclass=DayCounterMeta):
name: str = ""

def count(self, start: date, end: date) -> int:
return (end - start).days
def count(self, start: date, end: date) -> float:
"""Count the number of days between 2 dates"""
return (end - start).total_seconds() / 86400

def dcf(self, start: date, end: date) -> float:
return self.count(start, end) / 360.0
Expand Down
99 changes: 91 additions & 8 deletions ccy/tradingcentres/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,96 @@
from .centres import TradingCentre, centres
from __future__ import annotations

__all__ = ["centres", "TradingCentre", "prevbizday", "nextbizday"]
from dataclasses import dataclass, field
from datetime import date, timedelta
import holidays
import holidays.countries
import holidays.financial

isoweekend = frozenset((6, 7))
oneday = timedelta(days=1)

def prevbizday(dte=None, nd=1, tcs=None):
tcs = centres(tcs)
return tcs.prevbizday(dte, nd)
_tcs: dict[str, TradingCentre] = {}


def nextbizday(dte=None, nd=1, tcs=None):
tcs = centres(tcs)
return tcs.nextbizday(dte, nd)
def prevbizday(dte: date, nd: int = 1, tcs: str | None = None) -> date:
return centres(tcs).prevbizday(dte, nd)


def nextbizday(dte: date, nd: int = 1, tcs: str | None = None) -> date:
return centres(tcs).nextbizday(dte, nd)


def centres(codes: str | None = None) -> TradingCentres:
tcs = TradingCentres()
if codes:
lcs = codes.upper().replace(" ", "").split(",")
for code in lcs:
tc = _tcs.get(code)
if tc:
tcs.centres[tc.code] = tc
return tcs


@dataclass
class TradingCentre:
code: str
calendar: holidays.HolidayBase

def isholiday(self, dte: date) -> bool:
return dte not in self.calendar


@dataclass
class TradingCentres:
centres: dict[str, TradingCentre] = field(default_factory=dict)

def isbizday(self, dte: date) -> bool:
if dte.isoweekday() in isoweekend:
return False
for c in self.centres.values():
if c.isholiday(dte):
return False
return True

def nextbizday(self, dte: date, nd: int = 1) -> date:
n = 0
while not self.isbizday(dte):
dte += oneday
while n < nd:
dte += oneday
if self.isbizday(dte):
n += 1
return dte

def prevbizday(self, dte: date, nd: int = 1) -> date:
n = 0
if nd < 0:
return self.nextbizday(dte, -nd)
else:
while not self.isbizday(dte):
dte -= oneday
n = 0
while n < nd:
dte -= oneday
if self.isbizday(dte):
n += 1
return dte


_tcs.update(
(tc.code, tc)
for tc in (
TradingCentre(
code="TGT",
calendar=holidays.financial.european_central_bank.EuropeanCentralBank(), # type: ignore [no-untyped-call]
),
TradingCentre(
code="LON",
calendar=holidays.countries.united_kingdom.UnitedKingdom(), # type: ignore [no-untyped-call]
),
TradingCentre(
code="NY",
calendar=holidays.countries.united_states.UnitedStates(), # type: ignore [no-untyped-call]
),
)
)
169 changes: 0 additions & 169 deletions ccy/tradingcentres/centres.py

This file was deleted.

28 changes: 0 additions & 28 deletions ccy/tradingcentres/holiday.py

This file was deleted.

Loading

0 comments on commit 28359f9

Please sign in to comment.