detquantlib 3.10.4__tar.gz → 3.12.0__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.
- {detquantlib-3.10.4 → detquantlib-3.12.0}/PKG-INFO +2 -1
- detquantlib-3.12.0/detquantlib/forecasting/__init__.py +1 -0
- detquantlib-3.12.0/detquantlib/forecasting/forecasting.py +59 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/tradable_products/tradable_products.py +0 -1
- detquantlib-3.12.0/detquantlib/utils/logging.py +122 -0
- detquantlib-3.12.0/detquantlib/utils/utils.py +38 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/pyproject.toml +6 -1
- detquantlib-3.10.4/detquantlib/utils/utils.py +0 -11
- {detquantlib-3.10.4 → detquantlib-3.12.0}/LICENSE.txt +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/README.md +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/data/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/data/databases/detdatabase.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/dates/dates.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/outputs/outputs_interface.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/utils/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detquantlib
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.12.0
|
|
4
4
|
Summary: An internal library containing functions and classes that can be used across Quant models.
|
|
5
5
|
License-File: LICENSE.txt
|
|
6
6
|
Author: DET
|
|
@@ -11,6 +11,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.13
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Requires-Dist: colorlog (>=6.10.1,<7.0.0)
|
|
14
15
|
Requires-Dist: numpy (>=2.2.6,<3.0.0)
|
|
15
16
|
Requires-Dist: pandas (>=2.3.3,<3.0.0)
|
|
16
17
|
Requires-Dist: paramiko (>=4.0.0,<5.0.0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .forecasting import *
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Python built-in packages
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
# Third-party packages
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def forecast_knife_strategy(
|
|
10
|
+
dates: list[datetime] | list[pd.Timestamp] | pd.DatetimeIndex, values: list | np.ndarray
|
|
11
|
+
) -> np.ndarray:
|
|
12
|
+
"""
|
|
13
|
+
Generates a forecast using the knife strategy (i.e. random walk).
|
|
14
|
+
|
|
15
|
+
The knife strategy works as follows:
|
|
16
|
+
- Set the forecasted value at time t equal to the observed value on the most recent
|
|
17
|
+
previous date that has the same time (hour, minute, and second) and same day type
|
|
18
|
+
(Mon-Fri, Sat, or Sun).
|
|
19
|
+
- If there is no previous date with matching time and day type (i.e. very first observation
|
|
20
|
+
for a given time and day type), the strategy sets the forecasted value at time t equal
|
|
21
|
+
to the observed value at time t.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
dates: Delivery dates
|
|
25
|
+
values: Observed values
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
Forecasted values
|
|
29
|
+
"""
|
|
30
|
+
# Make sure input dates are stored as pd.DatetimeIndex and values as np.array
|
|
31
|
+
dates = pd.DatetimeIndex(dates)
|
|
32
|
+
values = np.array(values)
|
|
33
|
+
|
|
34
|
+
# Get times
|
|
35
|
+
hours = dates.hour
|
|
36
|
+
minutes = dates.minute
|
|
37
|
+
seconds = dates.second
|
|
38
|
+
|
|
39
|
+
# Get weekday types (1 = Mon-Fri, 2 = Sat, 3 = Sun)
|
|
40
|
+
weekdays = dates.weekday
|
|
41
|
+
day_types = np.zeros_like(weekdays)
|
|
42
|
+
day_types[weekdays < 5] = 1
|
|
43
|
+
day_types[weekdays == 5] = 2
|
|
44
|
+
day_types[weekdays == 6] = 3
|
|
45
|
+
|
|
46
|
+
# Get forecasted values
|
|
47
|
+
# Note: The logic below has been designed to optimize code speed, and works as follows:
|
|
48
|
+
# - 1. Get all the unique pairs of time and day type in the input dataset.
|
|
49
|
+
# - 2. For each pair:
|
|
50
|
+
# - 2.1. Get all the corresponding observed data.
|
|
51
|
+
# - 2.2. Set the forecast as: data[0], data[0], data[1], data[2], ..., data[N-1]
|
|
52
|
+
fc_values = np.zeros_like(values)
|
|
53
|
+
pairs = np.column_stack([hours, minutes, seconds, day_types])
|
|
54
|
+
for hh, mm, ss, dt in np.unique(pairs, axis=0):
|
|
55
|
+
idx = (hours == hh) & (minutes == mm) & (seconds == ss) & (day_types == dt)
|
|
56
|
+
i_values = values[idx]
|
|
57
|
+
fc_values[idx] = np.insert(i_values[:-1], 0, i_values[0])
|
|
58
|
+
|
|
59
|
+
return fc_values
|
{detquantlib-3.10.4 → detquantlib-3.12.0}/detquantlib/tradable_products/tradable_products.py
RENAMED
|
@@ -29,7 +29,6 @@ def convert_delivery_start_date_to_maturity(
|
|
|
29
29
|
Product maturity
|
|
30
30
|
|
|
31
31
|
Raises:
|
|
32
|
-
ValueError: Raises an error when the delivery start date is older than the trading date
|
|
33
32
|
ValueError: Raises an error when the input product type is not recognized
|
|
34
33
|
"""
|
|
35
34
|
# Make input product string lower case only
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Python built-in packages
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
import threading
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from types import TracebackType
|
|
7
|
+
|
|
8
|
+
# Third-party packages
|
|
9
|
+
from colorlog import ColoredFormatter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def setup_logger(log_dir: Path):
|
|
13
|
+
"""
|
|
14
|
+
Initializes logging with both console and file handlers.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
log_dir: Full path to the log file
|
|
18
|
+
"""
|
|
19
|
+
# Clear existing handlers to avoid duplicate logs
|
|
20
|
+
for handler in logging.root.handlers[:]:
|
|
21
|
+
logging.root.removeHandler(handler)
|
|
22
|
+
|
|
23
|
+
# Console handler with colors
|
|
24
|
+
console_formatter = CustomFormatter(
|
|
25
|
+
fmt=(
|
|
26
|
+
"%(log_color)s%(asctime)-8s%(reset)s "
|
|
27
|
+
"%(log_color)s[%(levelname)s]%(reset)s "
|
|
28
|
+
"%(log_color)s%(message)s"
|
|
29
|
+
),
|
|
30
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
31
|
+
reset=True,
|
|
32
|
+
)
|
|
33
|
+
console_handler = logging.StreamHandler()
|
|
34
|
+
console_handler.setLevel(logging.INFO)
|
|
35
|
+
console_handler.setFormatter(console_formatter)
|
|
36
|
+
|
|
37
|
+
# File handler (plain, no colors)
|
|
38
|
+
file_formatter = logging.Formatter(
|
|
39
|
+
fmt="%(asctime)s [%(levelname)s] %(message)s",
|
|
40
|
+
datefmt="%Y-%m-%d %H:%M:%S",
|
|
41
|
+
)
|
|
42
|
+
file_handler = logging.FileHandler(log_dir, mode="a", encoding="utf-8")
|
|
43
|
+
file_handler.setLevel(logging.INFO)
|
|
44
|
+
file_handler.setFormatter(file_formatter)
|
|
45
|
+
|
|
46
|
+
# Configure root logger
|
|
47
|
+
root_logger = logging.getLogger()
|
|
48
|
+
root_logger.setLevel(logging.INFO)
|
|
49
|
+
root_logger.addHandler(console_handler)
|
|
50
|
+
root_logger.addHandler(file_handler)
|
|
51
|
+
|
|
52
|
+
# Capture uncaught exceptions
|
|
53
|
+
sys.excepthook = exception_handler
|
|
54
|
+
threading.excepthook = thread_exception_handler
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def exception_handler(
|
|
58
|
+
exc_type: type[BaseException], exc_value: BaseException, exc_traceback: TracebackType
|
|
59
|
+
):
|
|
60
|
+
"""
|
|
61
|
+
Handles uncaught exceptions in the main thread and logs them.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
exc_type: Exception type
|
|
65
|
+
exc_value: Exception instance
|
|
66
|
+
exc_traceback: Traceback object
|
|
67
|
+
"""
|
|
68
|
+
if issubclass(exc_type, KeyboardInterrupt):
|
|
69
|
+
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
logging.critical(
|
|
73
|
+
"Uncaught exception",
|
|
74
|
+
exc_info=(exc_type, exc_value, exc_traceback),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def thread_exception_handler(args: threading.ExceptHookArgs):
|
|
79
|
+
"""
|
|
80
|
+
Handles uncaught exceptions in threads and logs them.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
args: Thread exception arguments
|
|
84
|
+
"""
|
|
85
|
+
logging.critical(
|
|
86
|
+
f"Uncaught thread exception in {args.thread.name}",
|
|
87
|
+
exc_info=(args.exc_type, args.exc_value, args.exc_traceback),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class CustomFormatter(ColoredFormatter):
|
|
92
|
+
"""Custom formatter to override default colorlog colors."""
|
|
93
|
+
|
|
94
|
+
# ANSI color codes
|
|
95
|
+
COLORS = dict(
|
|
96
|
+
BRIGHT_WHITE="\033[97m",
|
|
97
|
+
LIGHT_WHITE="\033[38;5;250m",
|
|
98
|
+
BRIGHT_YELLOW="\033[93m",
|
|
99
|
+
RED="\033[91m",
|
|
100
|
+
RESET="\033[0m",
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
def _get_escape_code(self, log_colors: dict, level_name: str) -> str:
|
|
104
|
+
"""
|
|
105
|
+
Returns ANSI escape codes for log levels.
|
|
106
|
+
|
|
107
|
+
Overrides DEBUG/INFO to light-white, CRITICAL to non-bold red. WARNING/ERROR remain
|
|
108
|
+
default.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
log_colors: Mapping of log levels to colors
|
|
112
|
+
level_name: Log level name
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
ANSI escape code string
|
|
116
|
+
"""
|
|
117
|
+
if level_name in ("DEBUG", "INFO"):
|
|
118
|
+
return CustomFormatter.COLORS["LIGHT_WHITE"]
|
|
119
|
+
if level_name == "CRITICAL":
|
|
120
|
+
return super()._get_escape_code(log_colors, "ERROR")
|
|
121
|
+
else:
|
|
122
|
+
return super()._get_escape_code(log_colors, level_name)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Python built-in packages
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
|
|
4
|
+
# Third-party packages
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def add_log(message: str):
|
|
9
|
+
"""
|
|
10
|
+
Short helper function to print log messages, including time stamps.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
message: Message to be printed
|
|
14
|
+
"""
|
|
15
|
+
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def divide_with_nan(
|
|
19
|
+
numerator: np.array, denominator: np.array, nan=np.nan, posinf=np.nan, neginf=np.nan
|
|
20
|
+
) -> np.array:
|
|
21
|
+
"""
|
|
22
|
+
A short utility function to perform an element-wise division of two arrays and replace
|
|
23
|
+
potential NaN, inf, and -inf results with user-defined values.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
numerator: Numerator array
|
|
27
|
+
denominator: Denominator array
|
|
28
|
+
nan: Value to use to replace NaN results
|
|
29
|
+
posinf: Value to use to replace positive inf results
|
|
30
|
+
neginf: Value to use to replace negative inf results
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Quotient array
|
|
34
|
+
"""
|
|
35
|
+
quotient = np.nan_to_num(
|
|
36
|
+
x=np.divide(numerator, denominator), nan=nan, posinf=posinf, neginf=neginf
|
|
37
|
+
)
|
|
38
|
+
return quotient
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "detquantlib"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.12.0"
|
|
4
4
|
description = "An internal library containing functions and classes that can be used across Quant models."
|
|
5
5
|
authors = ["DET"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -20,6 +20,7 @@ numpy = "^2.2.6"
|
|
|
20
20
|
pandas = "^2.3.3"
|
|
21
21
|
pyarrow = "^21.0.0"
|
|
22
22
|
scipy = "^1.15.2"
|
|
23
|
+
colorlog = "^6.10.1"
|
|
23
24
|
|
|
24
25
|
[tool.poetry.group.dev.dependencies]
|
|
25
26
|
toml = "^0.10.2"
|
|
@@ -37,6 +38,10 @@ md-toc = "^9.0.0"
|
|
|
37
38
|
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
38
39
|
build-backend = "poetry.core.masonry.api"
|
|
39
40
|
|
|
41
|
+
[tool.pytest.ini_options]
|
|
42
|
+
testpaths = ["tests"] # Tells pytest to look for tests only under the tests/ folder
|
|
43
|
+
pythonpath = ["."] # Adds project root (.) to sys.path, so import detquantlib works anywhere
|
|
44
|
+
|
|
40
45
|
[tool.isort]
|
|
41
46
|
multi_line_output = 3
|
|
42
47
|
include_trailing_comma = true
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
def add_log(message: str):
|
|
5
|
-
"""
|
|
6
|
-
Short helper function to print log messages, including time stamps.
|
|
7
|
-
|
|
8
|
-
Args:
|
|
9
|
-
message: Message to be printed
|
|
10
|
-
"""
|
|
11
|
-
print(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}")
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|