detquantlib 3.9.0__tar.gz → 3.10.1__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.9.0 → detquantlib-3.10.1}/PKG-INFO +2 -1
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/data/databases/detdatabase.py +73 -90
- {detquantlib-3.9.0 → detquantlib-3.10.1}/pyproject.toml +3 -2
- {detquantlib-3.9.0 → detquantlib-3.10.1}/LICENSE.txt +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/README.md +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/data/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/dates/dates.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/outputs/outputs_interface.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/tradable_products/tradable_products.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/utils/__init__.py +0 -0
- {detquantlib-3.9.0 → detquantlib-3.10.1}/detquantlib/utils/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: detquantlib
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.10.1
|
|
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
|
|
@@ -18,6 +18,7 @@ Requires-Dist: plotly (>=6.3.1,<7.0.0)
|
|
|
18
18
|
Requires-Dist: pyodbc (>=5.2.0,<6.0.0)
|
|
19
19
|
Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
|
|
20
20
|
Requires-Dist: scipy (>=1.15.2,<2.0.0)
|
|
21
|
+
Requires-Dist: sqlalchemy (>=2.0.43,<3.0.0)
|
|
21
22
|
Project-URL: Repository, https://github.com/Dynamic-Energy-Trading/detquantlib
|
|
22
23
|
Description-Content-Type: text/markdown
|
|
23
24
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Python built-in packages
|
|
2
2
|
import os
|
|
3
|
-
import warnings
|
|
4
3
|
from datetime import datetime
|
|
4
|
+
from urllib.parse import quote_plus
|
|
5
5
|
from zoneinfo import ZoneInfo
|
|
6
6
|
|
|
7
7
|
# Third-party packages
|
|
8
8
|
import pandas as pd
|
|
9
|
-
import pyodbc
|
|
10
9
|
from dateutil.relativedelta import *
|
|
10
|
+
from sqlalchemy import Engine, create_engine, text
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class DetDatabase:
|
|
@@ -15,36 +15,34 @@ class DetDatabase:
|
|
|
15
15
|
A class to easily interact with the DET database, including fetching and processing data.
|
|
16
16
|
"""
|
|
17
17
|
|
|
18
|
-
def __init__(
|
|
19
|
-
self,
|
|
20
|
-
connection: pyodbc.Connection = None,
|
|
21
|
-
driver: str = "{ODBC Driver 18 for SQL Server}",
|
|
22
|
-
):
|
|
18
|
+
def __init__(self, engine: Engine = None, driver: str = "ODBC Driver 18 for SQL Server"):
|
|
23
19
|
"""
|
|
24
20
|
Constructor method.
|
|
25
21
|
|
|
26
22
|
Args:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
the open_connection() method.
|
|
23
|
+
engine: Instance of SQLAlchemy engine. If None, the instance is automatically
|
|
24
|
+
initialized.
|
|
30
25
|
driver: ODBC driver
|
|
26
|
+
"""
|
|
27
|
+
# Only allow object creation if mandatory environment variables exist
|
|
28
|
+
DetDatabase.check_environment_variables()
|
|
29
|
+
|
|
30
|
+
self.driver = driver
|
|
31
|
+
self.engine = engine if engine is not None else self.initialize_engine()
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def check_environment_variables():
|
|
35
|
+
"""
|
|
36
|
+
Checks if environment variables needed by the class are defined.
|
|
31
37
|
|
|
32
38
|
Raises:
|
|
33
39
|
EnvironmentError: Raises an error if environment variables are not defined
|
|
34
40
|
"""
|
|
35
|
-
self.connection = connection
|
|
36
|
-
self.driver = driver
|
|
37
|
-
|
|
38
|
-
# Check if environment variables needed by the class are defined
|
|
39
41
|
required_env_vars = [
|
|
40
|
-
dict(name="DET_DB_NAME", value=None, description="
|
|
41
|
-
dict(name="DET_DB_SERVER", value=None, description="
|
|
42
|
-
dict(
|
|
43
|
-
|
|
44
|
-
),
|
|
45
|
-
dict(
|
|
46
|
-
name="DET_DB_PASSWORD", value=None, description="Password to connect to database"
|
|
47
|
-
),
|
|
42
|
+
dict(name="DET_DB_NAME", value=None, description="Database name"),
|
|
43
|
+
dict(name="DET_DB_SERVER", value=None, description="Server name"),
|
|
44
|
+
dict(name="DET_DB_USERNAME", value=None, description="Username"),
|
|
45
|
+
dict(name="DET_DB_PASSWORD", value=None, description="Password"),
|
|
48
46
|
]
|
|
49
47
|
available_env_vars = os.environ
|
|
50
48
|
for d in required_env_vars:
|
|
@@ -57,21 +55,28 @@ class DetDatabase:
|
|
|
57
55
|
f"(description: '{d['description']}') not found."
|
|
58
56
|
)
|
|
59
57
|
|
|
60
|
-
def
|
|
61
|
-
"""
|
|
58
|
+
def initialize_engine(self) -> Engine:
|
|
59
|
+
"""
|
|
60
|
+
Initializes the SQLAlchemy engine.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Instance of SQLAlchemy engine
|
|
64
|
+
"""
|
|
62
65
|
# Create the connection string
|
|
63
66
|
connection_str = (
|
|
64
67
|
f"DRIVER={self.driver};"
|
|
65
68
|
f"SERVER={os.getenv('DET_DB_SERVER')};"
|
|
66
69
|
f"DATABASE={os.getenv('DET_DB_NAME')};"
|
|
67
70
|
f"UID={os.getenv('DET_DB_USERNAME')};"
|
|
68
|
-
f"PWD={os.getenv('DET_DB_PASSWORD')}"
|
|
71
|
+
f"PWD={quote_plus(os.getenv('DET_DB_PASSWORD'))}"
|
|
69
72
|
)
|
|
70
|
-
|
|
73
|
+
url = quote_plus(connection_str)
|
|
74
|
+
engine = create_engine(f"mssql+pyodbc:///?odbc_connect={url}")
|
|
75
|
+
return engine
|
|
71
76
|
|
|
72
|
-
def
|
|
73
|
-
"""
|
|
74
|
-
self.
|
|
77
|
+
def terminate_engine(self):
|
|
78
|
+
"""Disconnects the SQLAlchemy engine and releases all underlying resources."""
|
|
79
|
+
self.engine.dispose()
|
|
75
80
|
|
|
76
81
|
def query_db(self, query: str) -> pd.DataFrame:
|
|
77
82
|
"""
|
|
@@ -86,17 +91,12 @@ class DetDatabase:
|
|
|
86
91
|
Raises:
|
|
87
92
|
Exception: Raises an error if the SQL query fails
|
|
88
93
|
"""
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
df = pd.read_sql(query, self.connection)
|
|
96
|
-
except Exception as e:
|
|
97
|
-
# If query fails, close connection before raising the error
|
|
98
|
-
self.close_connection()
|
|
99
|
-
raise
|
|
94
|
+
try:
|
|
95
|
+
df = pd.read_sql_query(query, con=self.engine)
|
|
96
|
+
except Exception as e:
|
|
97
|
+
# If query fails, close connection before raising the error
|
|
98
|
+
self.terminate_engine()
|
|
99
|
+
raise
|
|
100
100
|
|
|
101
101
|
return df
|
|
102
102
|
|
|
@@ -229,7 +229,7 @@ class DetDatabase:
|
|
|
229
229
|
end_delivery_date = end_delivery_date.astimezone(ZoneInfo("UTC"))
|
|
230
230
|
end_date_str = end_delivery_date.strftime("%Y-%m-%d %H:%M:%S")
|
|
231
231
|
|
|
232
|
-
#
|
|
232
|
+
# Query db
|
|
233
233
|
table = DetDatabase.get_table_name("entsoe_day_ahead_spot_price")
|
|
234
234
|
query = (
|
|
235
235
|
f"SELECT {columns_str} FROM {table} "
|
|
@@ -237,11 +237,7 @@ class DetDatabase:
|
|
|
237
237
|
f"AND [DateTime(UTC)]>='{start_date_str}' "
|
|
238
238
|
f"AND [DateTime(UTC)]<'{end_date_str}' "
|
|
239
239
|
)
|
|
240
|
-
|
|
241
|
-
# Query db
|
|
242
|
-
self.open_connection()
|
|
243
240
|
df = self.query_db(query)
|
|
244
|
-
self.close_connection()
|
|
245
241
|
|
|
246
242
|
if df.empty:
|
|
247
243
|
raise ValueError("No price data found for user-defined inputs.")
|
|
@@ -428,7 +424,7 @@ class DetDatabase:
|
|
|
428
424
|
end_delivery_date = end_delivery_date.astimezone(ZoneInfo("UTC"))
|
|
429
425
|
end_date_str = end_delivery_date.strftime("%Y-%m-%d %H:%M:%S")
|
|
430
426
|
|
|
431
|
-
#
|
|
427
|
+
# Query db
|
|
432
428
|
table = DetDatabase.get_table_name("entsoe_imbalance_price")
|
|
433
429
|
query = (
|
|
434
430
|
f"SELECT {columns_str} FROM {table} "
|
|
@@ -436,11 +432,7 @@ class DetDatabase:
|
|
|
436
432
|
f"AND [DateTime(UTC)]>='{start_date_str}' "
|
|
437
433
|
f"AND [DateTime(UTC)]<'{end_date_str}' "
|
|
438
434
|
)
|
|
439
|
-
|
|
440
|
-
# Query db
|
|
441
|
-
self.open_connection()
|
|
442
435
|
df = self.query_db(query)
|
|
443
|
-
self.close_connection()
|
|
444
436
|
|
|
445
437
|
if df.empty:
|
|
446
438
|
raise ValueError("No price data found for user-defined inputs.")
|
|
@@ -561,7 +553,7 @@ class DetDatabase:
|
|
|
561
553
|
start_trading_date_str = start_trading_date.strftime("%Y-%m-%d")
|
|
562
554
|
end_trading_date_str = end_trading_date.strftime("%Y-%m-%d")
|
|
563
555
|
|
|
564
|
-
#
|
|
556
|
+
# Query db
|
|
565
557
|
table = DetDatabase.get_table_name("futures_tt")
|
|
566
558
|
query = (
|
|
567
559
|
f"SELECT {columns_str} FROM {table} "
|
|
@@ -571,11 +563,7 @@ class DetDatabase:
|
|
|
571
563
|
f"AND Tenor IN {tenors_str} "
|
|
572
564
|
f"AND DeliveryType='{delivery_type}'"
|
|
573
565
|
)
|
|
574
|
-
|
|
575
|
-
# Query db
|
|
576
|
-
self.open_connection()
|
|
577
566
|
df = self.query_db(query)
|
|
578
|
-
self.close_connection()
|
|
579
567
|
|
|
580
568
|
if df.empty:
|
|
581
569
|
raise ValueError("No price data found for user-defined inputs.")
|
|
@@ -633,14 +621,10 @@ class DetDatabase:
|
|
|
633
621
|
else:
|
|
634
622
|
columns_str = f"[{'], ['.join(columns)}]"
|
|
635
623
|
|
|
636
|
-
#
|
|
624
|
+
# Query db
|
|
637
625
|
table = DetDatabase.get_table_name("commodity")
|
|
638
626
|
query = f"SELECT {columns_str} FROM {table} {conditions}"
|
|
639
|
-
|
|
640
|
-
# Query db
|
|
641
|
-
self.open_connection()
|
|
642
627
|
df = self.query_db(query)
|
|
643
|
-
self.close_connection()
|
|
644
628
|
|
|
645
629
|
return df
|
|
646
630
|
|
|
@@ -716,18 +700,14 @@ class DetDatabase:
|
|
|
716
700
|
start_trading_date_str = start_trading_date.strftime("%Y-%m-%d")
|
|
717
701
|
end_trading_date_str = end_trading_date.strftime("%Y-%m-%d")
|
|
718
702
|
|
|
719
|
-
#
|
|
703
|
+
# Query db
|
|
720
704
|
table = DetDatabase.get_table_name("account_position")
|
|
721
705
|
query = (
|
|
722
706
|
f"SELECT {columns_str} FROM {table} "
|
|
723
707
|
f"WHERE InsertionTimestamp>='{start_trading_date_str}' "
|
|
724
708
|
f"AND InsertionTimestamp<'{end_trading_date_str}'"
|
|
725
709
|
)
|
|
726
|
-
|
|
727
|
-
# Query db
|
|
728
|
-
self.open_connection()
|
|
729
710
|
df = self.query_db(query)
|
|
730
|
-
self.close_connection()
|
|
731
711
|
|
|
732
712
|
# Assert data
|
|
733
713
|
if df.empty:
|
|
@@ -774,14 +754,10 @@ class DetDatabase:
|
|
|
774
754
|
# Convert tenors from list to string
|
|
775
755
|
identifiers_str = ", ".join(f"'{i}'" for i in identifiers)
|
|
776
756
|
|
|
777
|
-
#
|
|
757
|
+
# Query db
|
|
778
758
|
table = DetDatabase.get_table_name("instrument")
|
|
779
759
|
query = f"SELECT {columns_str} FROM {table} WHERE [id] IN ({identifiers_str})"
|
|
780
|
-
|
|
781
|
-
# Query db
|
|
782
|
-
self.open_connection()
|
|
783
760
|
df = self.query_db(query)
|
|
784
|
-
self.close_connection()
|
|
785
761
|
|
|
786
762
|
# Assert data
|
|
787
763
|
if df.empty:
|
|
@@ -835,7 +811,7 @@ class DetDatabase:
|
|
|
835
811
|
start_trading_date_str = start_trading_date.strftime("%Y-%m-%d")
|
|
836
812
|
end_trading_date_str = end_trading_date.strftime("%Y-%m-%d")
|
|
837
813
|
|
|
838
|
-
#
|
|
814
|
+
# Query db
|
|
839
815
|
table = DetDatabase.get_table_name("futures_eex")
|
|
840
816
|
query = (
|
|
841
817
|
f"SELECT {columns_str} FROM {table} "
|
|
@@ -843,11 +819,7 @@ class DetDatabase:
|
|
|
843
819
|
f"AND TradingDate>='{start_trading_date_str}' "
|
|
844
820
|
f"AND TradingDate<='{end_trading_date_str}'"
|
|
845
821
|
)
|
|
846
|
-
|
|
847
|
-
# Query db
|
|
848
|
-
self.open_connection()
|
|
849
822
|
df = self.query_db(query)
|
|
850
|
-
self.close_connection()
|
|
851
823
|
|
|
852
824
|
# Assert data
|
|
853
825
|
if df.empty:
|
|
@@ -944,7 +916,7 @@ class DetDatabase:
|
|
|
944
916
|
# Convert dates from datetime to string
|
|
945
917
|
forecast_date_str = forecast_date.strftime("%Y-%m-%d")
|
|
946
918
|
|
|
947
|
-
#
|
|
919
|
+
# Query db
|
|
948
920
|
table = DetDatabase.get_table_name("client_volume_forecast")
|
|
949
921
|
query = (
|
|
950
922
|
f"SELECT {columns_str} FROM {table} "
|
|
@@ -953,11 +925,7 @@ class DetDatabase:
|
|
|
953
925
|
f"AND Datetime>='{start_date_str}' "
|
|
954
926
|
f"AND Datetime<'{end_date_str}'"
|
|
955
927
|
)
|
|
956
|
-
|
|
957
|
-
# Query db
|
|
958
|
-
self.open_connection()
|
|
959
928
|
df = self.query_db(query)
|
|
960
|
-
self.close_connection()
|
|
961
929
|
|
|
962
930
|
# Assert data
|
|
963
931
|
if df.empty:
|
|
@@ -1049,7 +1017,7 @@ class DetDatabase:
|
|
|
1049
1017
|
else:
|
|
1050
1018
|
columns_str = f"[{'], ['.join(columns)}]"
|
|
1051
1019
|
|
|
1052
|
-
#
|
|
1020
|
+
# Query db
|
|
1053
1021
|
table = DetDatabase.get_table_name("client_day_ahead_auction_bids")
|
|
1054
1022
|
query = (
|
|
1055
1023
|
f"SELECT {columns_str} FROM {table} "
|
|
@@ -1057,11 +1025,7 @@ class DetDatabase:
|
|
|
1057
1025
|
f"AND DeliveryStart>='{start_date_str}' "
|
|
1058
1026
|
f"AND DeliveryStart<'{end_date_str}'"
|
|
1059
1027
|
)
|
|
1060
|
-
|
|
1061
|
-
# Query db
|
|
1062
|
-
self.open_connection()
|
|
1063
1028
|
df = self.query_db(query)
|
|
1064
|
-
self.close_connection()
|
|
1065
1029
|
|
|
1066
1030
|
# Assert data
|
|
1067
1031
|
if df.empty:
|
|
@@ -1120,14 +1084,10 @@ class DetDatabase:
|
|
|
1120
1084
|
else:
|
|
1121
1085
|
columns_str = f"[{'], ['.join(columns)}]"
|
|
1122
1086
|
|
|
1123
|
-
#
|
|
1087
|
+
# Query db
|
|
1124
1088
|
table = DetDatabase.get_table_name("client")
|
|
1125
1089
|
query = f"SELECT {columns_str} FROM {table} {conditions}"
|
|
1126
|
-
|
|
1127
|
-
# Query db
|
|
1128
|
-
self.open_connection()
|
|
1129
1090
|
df = self.query_db(query)
|
|
1130
|
-
self.close_connection()
|
|
1131
1091
|
|
|
1132
1092
|
return df
|
|
1133
1093
|
|
|
@@ -1168,6 +1128,29 @@ class DetDatabase:
|
|
|
1168
1128
|
|
|
1169
1129
|
return client_info
|
|
1170
1130
|
|
|
1131
|
+
def add_table(self, df: pd.DataFrame, table_name: str, schema: str):
|
|
1132
|
+
"""
|
|
1133
|
+
Creates a new database table and populates it with the user-input data.
|
|
1134
|
+
|
|
1135
|
+
Args:
|
|
1136
|
+
df: Data used to populate the database table
|
|
1137
|
+
table_name: Table name
|
|
1138
|
+
schema: Schema
|
|
1139
|
+
"""
|
|
1140
|
+
df.to_sql(name=table_name, schema=schema, con=self.engine, if_exists="fail", index=False)
|
|
1141
|
+
|
|
1142
|
+
def remove_table(self, table_name: str, schema: str):
|
|
1143
|
+
"""
|
|
1144
|
+
Deletes a database table.
|
|
1145
|
+
|
|
1146
|
+
Args:
|
|
1147
|
+
table_name: Table name
|
|
1148
|
+
schema: Schema
|
|
1149
|
+
"""
|
|
1150
|
+
with self.engine.connect() as conn:
|
|
1151
|
+
conn.execute(text(f"DROP TABLE IF EXISTS [{schema}].[{table_name}]"))
|
|
1152
|
+
conn.commit()
|
|
1153
|
+
|
|
1171
1154
|
|
|
1172
1155
|
class DetDatabaseDefinitions:
|
|
1173
1156
|
"""A class containing some hard-coded definitions related to the DET database."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "detquantlib"
|
|
3
|
-
version = "3.
|
|
3
|
+
version = "3.10.1"
|
|
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"
|
|
@@ -15,9 +15,10 @@ paramiko = "^4.0.0"
|
|
|
15
15
|
numpy = "^2.2.6"
|
|
16
16
|
pandas = "^2.3.3"
|
|
17
17
|
plotly = "^6.3.1"
|
|
18
|
-
pyodbc = "^5.2.0"
|
|
19
18
|
python-dotenv = "^1.1.0"
|
|
20
19
|
scipy = "^1.15.2"
|
|
20
|
+
sqlalchemy = "^2.0.43"
|
|
21
|
+
pyodbc = "^5.2.0"
|
|
21
22
|
|
|
22
23
|
[tool.poetry.group.dev.dependencies]
|
|
23
24
|
toml = "^0.10.2"
|
|
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
|
|
File without changes
|