Skip to content

Commit 28359f9

Browse files
committed
Use holidays for trading centres
1 parent 1fef375 commit 28359f9

File tree

9 files changed

+754
-831
lines changed

9 files changed

+754
-831
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#IDE
55
.idea
6+
.vscode
67

78
#python
89
*.pyc

.vscode/settings.json

-3
This file was deleted.

ccy/core/daycounter.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ def act_act_years(dt: date) -> float:
3131
a = 0.0
3232
if r > 0:
3333
a = 1.0
34-
dd = (dt - date(y, 1, 1)).days
34+
dd = (dt - date(y, 1, 1)).total_seconds() / 86400
3535
return y + dd / (365.0 + a)
3636

3737

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

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

5556
def dcf(self, start: date, end: date) -> float:
5657
return self.count(start, end) / 360.0

ccy/tradingcentres/__init__.py

+91-8
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,96 @@
1-
from .centres import TradingCentre, centres
1+
from __future__ import annotations
22

3-
__all__ = ["centres", "TradingCentre", "prevbizday", "nextbizday"]
3+
from dataclasses import dataclass, field
4+
from datetime import date, timedelta
5+
import holidays
6+
import holidays.countries
7+
import holidays.financial
48

9+
isoweekend = frozenset((6, 7))
10+
oneday = timedelta(days=1)
511

6-
def prevbizday(dte=None, nd=1, tcs=None):
7-
tcs = centres(tcs)
8-
return tcs.prevbizday(dte, nd)
12+
_tcs: dict[str, TradingCentre] = {}
913

1014

11-
def nextbizday(dte=None, nd=1, tcs=None):
12-
tcs = centres(tcs)
13-
return tcs.nextbizday(dte, nd)
15+
def prevbizday(dte: date, nd: int = 1, tcs: str | None = None) -> date:
16+
return centres(tcs).prevbizday(dte, nd)
17+
18+
19+
def nextbizday(dte: date, nd: int = 1, tcs: str | None = None) -> date:
20+
return centres(tcs).nextbizday(dte, nd)
21+
22+
23+
def centres(codes: str | None = None) -> TradingCentres:
24+
tcs = TradingCentres()
25+
if codes:
26+
lcs = codes.upper().replace(" ", "").split(",")
27+
for code in lcs:
28+
tc = _tcs.get(code)
29+
if tc:
30+
tcs.centres[tc.code] = tc
31+
return tcs
32+
33+
34+
@dataclass
35+
class TradingCentre:
36+
code: str
37+
calendar: holidays.HolidayBase
38+
39+
def isholiday(self, dte: date) -> bool:
40+
return dte not in self.calendar
41+
42+
43+
@dataclass
44+
class TradingCentres:
45+
centres: dict[str, TradingCentre] = field(default_factory=dict)
46+
47+
def isbizday(self, dte: date) -> bool:
48+
if dte.isoweekday() in isoweekend:
49+
return False
50+
for c in self.centres.values():
51+
if c.isholiday(dte):
52+
return False
53+
return True
54+
55+
def nextbizday(self, dte: date, nd: int = 1) -> date:
56+
n = 0
57+
while not self.isbizday(dte):
58+
dte += oneday
59+
while n < nd:
60+
dte += oneday
61+
if self.isbizday(dte):
62+
n += 1
63+
return dte
64+
65+
def prevbizday(self, dte: date, nd: int = 1) -> date:
66+
n = 0
67+
if nd < 0:
68+
return self.nextbizday(dte, -nd)
69+
else:
70+
while not self.isbizday(dte):
71+
dte -= oneday
72+
n = 0
73+
while n < nd:
74+
dte -= oneday
75+
if self.isbizday(dte):
76+
n += 1
77+
return dte
78+
79+
80+
_tcs.update(
81+
(tc.code, tc)
82+
for tc in (
83+
TradingCentre(
84+
code="TGT",
85+
calendar=holidays.financial.european_central_bank.EuropeanCentralBank(), # type: ignore [no-untyped-call]
86+
),
87+
TradingCentre(
88+
code="LON",
89+
calendar=holidays.countries.united_kingdom.UnitedKingdom(), # type: ignore [no-untyped-call]
90+
),
91+
TradingCentre(
92+
code="NY",
93+
calendar=holidays.countries.united_states.UnitedStates(), # type: ignore [no-untyped-call]
94+
),
95+
)
96+
)

ccy/tradingcentres/centres.py

-169
This file was deleted.

ccy/tradingcentres/holiday.py

-28
This file was deleted.

0 commit comments

Comments
 (0)