AnomalyLab 0.2.7__tar.gz → 0.2.8__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.
- {anomalylab-0.2.7 → anomalylab-0.2.8/AnomalyLab.egg-info}/PKG-INFO +1 -1
- {anomalylab-0.2.7 → anomalylab-0.2.8}/AnomalyLab.egg-info/SOURCES.txt +1 -0
- {anomalylab-0.2.7/AnomalyLab.egg-info → anomalylab-0.2.8}/PKG-INFO +1 -1
- anomalylab-0.2.8/anomalylab/config.py +1 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/core/core.py +12 -3
- anomalylab-0.2.8/anomalylab/empirical/factor_return.py +43 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/fm_regression.py +0 -1
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/portfolio.py +12 -4
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/visualization/format.py +46 -10
- {anomalylab-0.2.7 → anomalylab-0.2.8}/setup.py +1 -1
- anomalylab-0.2.7/anomalylab/config.py +0 -11
- {anomalylab-0.2.7 → anomalylab-0.2.8}/.gitattributes +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/.github/workflows/python-publish.yml +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/.gitignore +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/AnomalyLab.egg-info/dependency_links.txt +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/AnomalyLab.egg-info/requires.txt +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/AnomalyLab.egg-info/top_level.txt +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/LICENSE +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/MANIFEST.in +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/README.md +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/core/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/datasets/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/datasets/dataset.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/datasets/panel_data.csv +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/datasets/time_series_data.csv +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/datasets/transition_matrix.png +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/correlation.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/empirical.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/persistence.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/empirical/summary.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/fillna.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/normalize.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/outliers.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/preprocessor.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/shift.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/preprocess/truncate.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/structure/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/structure/data.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/structure/panel_data.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/structure/time_series.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/utils/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/utils/imports.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/utils/utils.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/anomalylab/visualization/__init__.py +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/requirements.txt +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/setup.cfg +0 -0
- {anomalylab-0.2.7 → anomalylab-0.2.8}/tests/__init__.py +0 -0
|
@@ -23,6 +23,7 @@ anomalylab/datasets/transition_matrix.png
|
|
|
23
23
|
anomalylab/empirical/__init__.py
|
|
24
24
|
anomalylab/empirical/correlation.py
|
|
25
25
|
anomalylab/empirical/empirical.py
|
|
26
|
+
anomalylab/empirical/factor_return.py
|
|
26
27
|
anomalylab/empirical/fm_regression.py
|
|
27
28
|
anomalylab/empirical/persistence.py
|
|
28
29
|
anomalylab/empirical/portfolio.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
DECIMAL = 2
|
|
@@ -280,6 +280,7 @@ class Panel:
|
|
|
280
280
|
factors_series: Optional[TimeSeries] = None,
|
|
281
281
|
format: bool = False,
|
|
282
282
|
decimal: Optional[int] = None,
|
|
283
|
+
factor_return: bool = False,
|
|
283
284
|
) -> tuple:
|
|
284
285
|
return self.portfolio_analysis_processor(
|
|
285
286
|
endog=endog, weight=weight, models=models, factors_series=factors_series
|
|
@@ -288,6 +289,7 @@ class Panel:
|
|
|
288
289
|
core_g=core_g,
|
|
289
290
|
format=format,
|
|
290
291
|
decimal=decimal,
|
|
292
|
+
factor_return=factor_return,
|
|
291
293
|
)
|
|
292
294
|
|
|
293
295
|
def bivariate_analysis(
|
|
@@ -304,6 +306,7 @@ class Panel:
|
|
|
304
306
|
format: bool = False,
|
|
305
307
|
type: str = "dependent",
|
|
306
308
|
decimal: Optional[int] = None,
|
|
309
|
+
factor_return: bool = False,
|
|
307
310
|
) -> tuple:
|
|
308
311
|
return self.portfolio_analysis_processor(
|
|
309
312
|
endog=endog, weight=weight, models=models, factors_series=factors_series
|
|
@@ -316,6 +319,7 @@ class Panel:
|
|
|
316
319
|
format=format,
|
|
317
320
|
type=type,
|
|
318
321
|
decimal=decimal,
|
|
322
|
+
factor_return=factor_return,
|
|
319
323
|
)
|
|
320
324
|
|
|
321
325
|
def fm_reg(
|
|
@@ -348,8 +352,12 @@ class Panel:
|
|
|
348
352
|
return_intermediate=return_intermediate,
|
|
349
353
|
)
|
|
350
354
|
|
|
351
|
-
def format_excel(
|
|
352
|
-
self
|
|
355
|
+
def format_excel(
|
|
356
|
+
self, path: str, align=True, line=True, convert_brackets=False
|
|
357
|
+
) -> None:
|
|
358
|
+
self.format_preprocessor(path=path).process(
|
|
359
|
+
align=align, line=line, convert_brackets=convert_brackets
|
|
360
|
+
)
|
|
353
361
|
|
|
354
362
|
|
|
355
363
|
if __name__ == "__main__":
|
|
@@ -402,7 +410,7 @@ if __name__ == "__main__":
|
|
|
402
410
|
)
|
|
403
411
|
)
|
|
404
412
|
uni_ew, uni_vw = panel.univariate_analysis(
|
|
405
|
-
"return", "MktCap", "Illiq", 10, Models, time_series
|
|
413
|
+
"return", "MktCap", "Illiq", 10, Models, time_series, factor_return=True
|
|
406
414
|
)
|
|
407
415
|
pp(uni_ew)
|
|
408
416
|
pp(uni_vw)
|
|
@@ -419,6 +427,7 @@ if __name__ == "__main__":
|
|
|
419
427
|
True,
|
|
420
428
|
False,
|
|
421
429
|
"dependent",
|
|
430
|
+
factor_return=True,
|
|
422
431
|
)
|
|
423
432
|
pp(bi_ew)
|
|
424
433
|
pp(bi_vw)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from pandas.core.frame import DataFrame
|
|
2
|
+
|
|
3
|
+
from anomalylab.config import *
|
|
4
|
+
from anomalylab.empirical.empirical import Empirical
|
|
5
|
+
from anomalylab.empirical.portfolio import PortfolioAnalysis
|
|
6
|
+
from anomalylab.structure import PanelData, TimeSeries
|
|
7
|
+
from anomalylab.utils.imports import *
|
|
8
|
+
from anomalylab.utils.utils import *
|
|
9
|
+
|
|
10
|
+
if __name__ == "__main__":
|
|
11
|
+
from anomalylab.datasets import DataSet
|
|
12
|
+
|
|
13
|
+
df: DataFrame = DataSet.get_panel_data()
|
|
14
|
+
ts: DataFrame = DataSet.get_time_series_data()
|
|
15
|
+
Models: dict[str, list[str]] = {
|
|
16
|
+
"CAPM": ["MKT(3F)"],
|
|
17
|
+
"FF3": ["MKT(3F)", "SMB(3F)", "HML(3F)"],
|
|
18
|
+
"FF5": ["MKT(5F)", "SMB(5F)", "HML(5F)", "RMW(5F)", "CMA(5F)"],
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
panel: PanelData = PanelData(df=df, name="Stocks", classifications="industry")
|
|
22
|
+
time_series: TimeSeries = TimeSeries(df=ts, name="Factor Series")
|
|
23
|
+
|
|
24
|
+
portfolio = PortfolioAnalysis(
|
|
25
|
+
panel,
|
|
26
|
+
endog="return",
|
|
27
|
+
weight="MktCap",
|
|
28
|
+
# models=Models,
|
|
29
|
+
# factors_series=time_series,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
group = portfolio.GroupN(["MktCap", "Illiq", "IdioVol"], [3, 3, 3])
|
|
33
|
+
pp(group)
|
|
34
|
+
|
|
35
|
+
# uni_ew, uni_vw = portfolio.univariate_analysis("Illiq", 10)
|
|
36
|
+
# pp(uni_ew)
|
|
37
|
+
# pp(uni_vw)
|
|
38
|
+
|
|
39
|
+
# bi_ew, bi_vw = portfolio.bivariate_analysis(
|
|
40
|
+
# "Illiq", "IdioVol", 10, 10, True, False, "dependent"
|
|
41
|
+
# )
|
|
42
|
+
# pp(bi_ew)
|
|
43
|
+
# pp(bi_vw)
|
|
@@ -267,6 +267,7 @@ class PortfolioAnalysis(Empirical):
|
|
|
267
267
|
core_g: int,
|
|
268
268
|
format: bool = False,
|
|
269
269
|
decimal: Optional[int] = None,
|
|
270
|
+
factor_return: bool = False,
|
|
270
271
|
) -> tuple:
|
|
271
272
|
"""Perform univariate analysis on the specified core variable.
|
|
272
273
|
|
|
@@ -332,6 +333,9 @@ class PortfolioAnalysis(Empirical):
|
|
|
332
333
|
.reset_index(level=0, drop=True)
|
|
333
334
|
)
|
|
334
335
|
|
|
336
|
+
if factor_return:
|
|
337
|
+
return ew_ret_d, vw_ret_d
|
|
338
|
+
|
|
335
339
|
def generate_time_series_dict(series: Series) -> dict:
|
|
336
340
|
"""Generate a dictionary of time series data from the DataFrame.
|
|
337
341
|
|
|
@@ -422,6 +426,7 @@ class PortfolioAnalysis(Empirical):
|
|
|
422
426
|
format: bool = False,
|
|
423
427
|
type: str = "dependent",
|
|
424
428
|
decimal: Optional[int] = None,
|
|
429
|
+
factor_return: bool = False,
|
|
425
430
|
) -> tuple:
|
|
426
431
|
"""Perform bivariate analysis on two specified variables.
|
|
427
432
|
|
|
@@ -519,6 +524,9 @@ class PortfolioAnalysis(Empirical):
|
|
|
519
524
|
.reset_index(level=0, drop=True)
|
|
520
525
|
)
|
|
521
526
|
|
|
527
|
+
if factor_return:
|
|
528
|
+
return ew_ret_d, vw_ret_d
|
|
529
|
+
|
|
522
530
|
def generate_time_series_dict(df: pd.DataFrame) -> dict:
|
|
523
531
|
"""Generate a dictionary of time series data from the DataFrame.
|
|
524
532
|
|
|
@@ -677,19 +685,19 @@ if __name__ == "__main__":
|
|
|
677
685
|
panel,
|
|
678
686
|
endog="return",
|
|
679
687
|
weight="MktCap",
|
|
680
|
-
|
|
681
|
-
|
|
688
|
+
models=Models,
|
|
689
|
+
factors_series=time_series,
|
|
682
690
|
)
|
|
683
691
|
|
|
684
692
|
group = portfolio.GroupN("Illiq", 10)
|
|
685
693
|
pp(group)
|
|
686
694
|
|
|
687
|
-
uni_ew, uni_vw = portfolio.univariate_analysis("Illiq", 10)
|
|
695
|
+
uni_ew, uni_vw = portfolio.univariate_analysis("Illiq", 10, factor_return=False)
|
|
688
696
|
pp(uni_ew)
|
|
689
697
|
pp(uni_vw)
|
|
690
698
|
|
|
691
699
|
bi_ew, bi_vw = portfolio.bivariate_analysis(
|
|
692
|
-
"Illiq", "IdioVol", 10, 10,
|
|
700
|
+
"Illiq", "IdioVol", 10, 10, False, False, "dependent", factor_return=False
|
|
693
701
|
)
|
|
694
702
|
pp(bi_ew)
|
|
695
703
|
pp(bi_vw)
|
|
@@ -60,30 +60,66 @@ class FormatExcel:
|
|
|
60
60
|
for cell in ws[max_row]:
|
|
61
61
|
cell.border = Border(bottom=thin)
|
|
62
62
|
|
|
63
|
+
def convert_brackets(self, direction="to_square"):
|
|
64
|
+
"""Converts parentheses () to square brackets [] and vice versa in the Excel cells.
|
|
65
|
+
|
|
66
|
+
This method goes through all the cells in the workbook and:
|
|
67
|
+
- Converts () to [] if direction is 'to_square'.
|
|
68
|
+
- Converts [] to () if direction is 'to_round'.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
direction (str): Direction of the bracket conversion.
|
|
72
|
+
- 'to_square' for converting () to [].
|
|
73
|
+
- 'to_round' for converting [] to ().
|
|
74
|
+
Default is 'to_square'.
|
|
75
|
+
"""
|
|
76
|
+
# Validate the direction parameter
|
|
77
|
+
if direction not in ["to_square", "to_round"]:
|
|
78
|
+
raise ValueError(
|
|
79
|
+
"Invalid direction. Choose either 'to_square' or 'to_round'."
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
for ws in self.wb.worksheets:
|
|
83
|
+
for row in ws.iter_rows(
|
|
84
|
+
min_row=1, max_row=ws.max_row, min_col=1, max_col=ws.max_column
|
|
85
|
+
):
|
|
86
|
+
for cell in row:
|
|
87
|
+
if isinstance(
|
|
88
|
+
cell.value, str
|
|
89
|
+
): # Check if the cell contains a string
|
|
90
|
+
if direction == "to_square":
|
|
91
|
+
cell.value = cell.value.replace("(", "[").replace(")", "]")
|
|
92
|
+
elif direction == "to_round":
|
|
93
|
+
cell.value = cell.value.replace("[", "(").replace("]", ")")
|
|
94
|
+
|
|
63
95
|
def save(self):
|
|
64
96
|
"""Saves the currently loaded workbook to its file path."""
|
|
65
97
|
self.wb.save(self.file_path)
|
|
66
98
|
|
|
67
|
-
def process(self):
|
|
99
|
+
def process(self, align=True, line=True, convert_brackets=False):
|
|
68
100
|
"""Processes and formats Excel files.
|
|
69
101
|
|
|
70
102
|
- If the provided path is a directory, it formats all Excel files in that directory.
|
|
71
103
|
- If the provided path is a file, it formats that specific Excel file.
|
|
72
104
|
"""
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
105
|
+
files_to_process = (
|
|
106
|
+
glob(os.path.join(self.path, "*.xlsx"))
|
|
107
|
+
if os.path.isdir(self.path)
|
|
108
|
+
else [self.path]
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
for file in files_to_process:
|
|
112
|
+
self.load_workbook(file)
|
|
113
|
+
if align:
|
|
76
114
|
self.align()
|
|
115
|
+
if line:
|
|
77
116
|
self.line()
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
self.load_workbook(self.path)
|
|
81
|
-
self.align()
|
|
82
|
-
self.line()
|
|
117
|
+
if convert_brackets:
|
|
118
|
+
self.convert_brackets()
|
|
83
119
|
self.save()
|
|
84
120
|
|
|
85
121
|
|
|
86
122
|
if __name__ == "__main__":
|
|
87
123
|
path = "..."
|
|
88
124
|
excel_formatter = FormatExcel(path)
|
|
89
|
-
excel_formatter.process()
|
|
125
|
+
excel_formatter.process(convert_brackets=True)
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|