AnomalyLab 0.2.5__tar.gz → 0.2.7__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.5 → anomalylab-0.2.7/AnomalyLab.egg-info}/PKG-INFO +1 -1
- {anomalylab-0.2.5/AnomalyLab.egg-info → anomalylab-0.2.7}/PKG-INFO +1 -1
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/core/core.py +6 -4
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/fm_regression.py +74 -19
- {anomalylab-0.2.5 → anomalylab-0.2.7}/setup.py +1 -1
- {anomalylab-0.2.5 → anomalylab-0.2.7}/.gitattributes +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/.github/workflows/python-publish.yml +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/.gitignore +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/AnomalyLab.egg-info/SOURCES.txt +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/AnomalyLab.egg-info/dependency_links.txt +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/AnomalyLab.egg-info/requires.txt +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/AnomalyLab.egg-info/top_level.txt +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/LICENSE +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/MANIFEST.in +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/README.md +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/config.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/core/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/datasets/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/datasets/dataset.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/datasets/panel_data.csv +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/datasets/time_series_data.csv +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/datasets/transition_matrix.png +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/correlation.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/empirical.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/persistence.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/portfolio.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/empirical/summary.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/fillna.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/normalize.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/outliers.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/preprocessor.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/shift.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/preprocess/truncate.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/structure/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/structure/data.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/structure/panel_data.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/structure/time_series.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/utils/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/utils/imports.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/utils/utils.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/visualization/__init__.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/anomalylab/visualization/format.py +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/requirements.txt +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/setup.cfg +0 -0
- {anomalylab-0.2.5 → anomalylab-0.2.7}/tests/__init__.py +0 -0
|
@@ -19,7 +19,7 @@ from anomalylab.visualization import FormatExcel
|
|
|
19
19
|
|
|
20
20
|
@dataclass
|
|
21
21
|
class Panel:
|
|
22
|
-
|
|
22
|
+
_df: pd.DataFrame = field(repr=False)
|
|
23
23
|
name: Optional[str] = None
|
|
24
24
|
id: str = "permno"
|
|
25
25
|
time: str = "date"
|
|
@@ -30,7 +30,7 @@ class Panel:
|
|
|
30
30
|
|
|
31
31
|
def __post_init__(self) -> None:
|
|
32
32
|
self.panel_data: PanelData = PanelData(
|
|
33
|
-
df=self.
|
|
33
|
+
df=self._df,
|
|
34
34
|
name=self.name,
|
|
35
35
|
id=self.id,
|
|
36
36
|
time=self.time,
|
|
@@ -54,9 +54,9 @@ class Panel:
|
|
|
54
54
|
return repr(self.panel_data)
|
|
55
55
|
|
|
56
56
|
def get_original_data(self) -> DataFrame:
|
|
57
|
-
return self.
|
|
57
|
+
return self._df
|
|
58
58
|
|
|
59
|
-
def
|
|
59
|
+
def get_processed_data(self) -> DataFrame:
|
|
60
60
|
return self.panel_data.df
|
|
61
61
|
|
|
62
62
|
@property
|
|
@@ -331,6 +331,7 @@ class Panel:
|
|
|
331
331
|
is_winsorize: bool = False,
|
|
332
332
|
is_normalize: bool = False,
|
|
333
333
|
decimal: Optional[int] = None,
|
|
334
|
+
return_intermediate: bool = False,
|
|
334
335
|
) -> DataFrame:
|
|
335
336
|
return self.fm_preprocessor.fit(
|
|
336
337
|
endog=endog,
|
|
@@ -344,6 +345,7 @@ class Panel:
|
|
|
344
345
|
is_winsorize=is_winsorize,
|
|
345
346
|
is_normalize=is_normalize,
|
|
346
347
|
decimal=decimal,
|
|
348
|
+
return_intermediate=return_intermediate,
|
|
347
349
|
)
|
|
348
350
|
|
|
349
351
|
def format_excel(self, path: str) -> None:
|
|
@@ -11,12 +11,12 @@ from anomalylab.utils.utils import RegModels, RegResult
|
|
|
11
11
|
class FamaMacBethRegression(Empirical):
|
|
12
12
|
"""Class for performing Fama-MacBeth regression analysis.
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
This class inherits from the Empirical class and provides methods to
|
|
15
|
+
winsorize data, calculate industry-weighted returns, fit the Fama-MacBeth
|
|
16
|
+
regression model, and format the output results.
|
|
17
17
|
|
|
18
18
|
Attributes:
|
|
19
|
-
|
|
19
|
+
panel_data (PanelData): Panel data used for analysis.
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
22
|
def _winsorize(self, is_winsorize: bool, exog: list[str]):
|
|
@@ -38,7 +38,8 @@ class FamaMacBethRegression(Empirical):
|
|
|
38
38
|
OutlierHandler(panel_data=self.panel_data)
|
|
39
39
|
.winsorize(
|
|
40
40
|
columns=exog,
|
|
41
|
-
|
|
41
|
+
method="winsorize",
|
|
42
|
+
group_columns=self.panel_data.time,
|
|
42
43
|
)
|
|
43
44
|
.panel_data
|
|
44
45
|
)
|
|
@@ -52,6 +53,9 @@ class FamaMacBethRegression(Empirical):
|
|
|
52
53
|
) -> None:
|
|
53
54
|
"""Calculates industry-weighted returns for the specified endogenous variables.
|
|
54
55
|
|
|
56
|
+
This method adjusts the endogenous variables by industry-specific returns,
|
|
57
|
+
either using equal weights or value weights (based on the 'industry_weighed_method').
|
|
58
|
+
|
|
55
59
|
Args:
|
|
56
60
|
endog (list[str]): List of endogenous variable names to adjust.
|
|
57
61
|
industry (Optional[str]): The industry column to group by.
|
|
@@ -89,9 +93,14 @@ class FamaMacBethRegression(Empirical):
|
|
|
89
93
|
df: DataFrame,
|
|
90
94
|
reg: RegModel,
|
|
91
95
|
is_normalize: bool,
|
|
96
|
+
return_intermediate: bool = False,
|
|
92
97
|
) -> RegResult:
|
|
93
98
|
"""Performs Fama-MacBeth regression on the provided DataFrame.
|
|
94
99
|
|
|
100
|
+
This method runs the Fama-MacBeth two-step regression on a given dataset.
|
|
101
|
+
It can optionally normalize the exogenous variables and return intermediate
|
|
102
|
+
results for each time period.
|
|
103
|
+
|
|
95
104
|
Args:
|
|
96
105
|
df (DataFrame): DataFrame containing the data for regression.
|
|
97
106
|
reg (RegModel): Model specification containing endogenous and exogenous variables.
|
|
@@ -112,6 +121,23 @@ class FamaMacBethRegression(Empirical):
|
|
|
112
121
|
)
|
|
113
122
|
df[self.time] = df[self.time].dt.to_timestamp()
|
|
114
123
|
df = df.set_index([self.id, self.time])
|
|
124
|
+
|
|
125
|
+
if return_intermediate:
|
|
126
|
+
coef_df = []
|
|
127
|
+
for time, group in df.groupby(self.time):
|
|
128
|
+
y = group[dependent]
|
|
129
|
+
X = sm.add_constant(group[exogenous]) # Add constant term
|
|
130
|
+
model = sm.OLS(y, X)
|
|
131
|
+
results = model.fit()
|
|
132
|
+
coefs = results.params
|
|
133
|
+
coefs[self.time] = time
|
|
134
|
+
coef_df.append(coefs)
|
|
135
|
+
coef_df = pd.DataFrame(coef_df)
|
|
136
|
+
coef_df = coef_df[
|
|
137
|
+
[self.time] + [col for col in coef_df.columns if col != self.time]
|
|
138
|
+
]
|
|
139
|
+
return pd.DataFrame(coef_df)
|
|
140
|
+
|
|
115
141
|
# Fama-MacBeth regression with Newey-West adjustment
|
|
116
142
|
fmb = FamaMacBeth(
|
|
117
143
|
dependent=df[dependent],
|
|
@@ -141,6 +167,9 @@ class FamaMacBethRegression(Empirical):
|
|
|
141
167
|
def _cal_adjusted_r2(self, group, dependent: str, exogenous: list[str]):
|
|
142
168
|
"""Calculates the adjusted R² for the given group of data.
|
|
143
169
|
|
|
170
|
+
This method fits an OLS model to the provided group of data and computes
|
|
171
|
+
the adjusted R².
|
|
172
|
+
|
|
144
173
|
Args:
|
|
145
174
|
group: DataFrame group to fit the OLS model.
|
|
146
175
|
dependent (str): Name of the dependent variable.
|
|
@@ -168,6 +197,9 @@ class FamaMacBethRegression(Empirical):
|
|
|
168
197
|
) -> Series:
|
|
169
198
|
"""Formats the regression results into a Pandas Series.
|
|
170
199
|
|
|
200
|
+
This method prepares the output of the regression results, rounding values
|
|
201
|
+
to the specified decimal places and adding significance stars.
|
|
202
|
+
|
|
171
203
|
Args:
|
|
172
204
|
reg_result (RegResult): Results of the regression.
|
|
173
205
|
decimal (int): Number of decimal places for rounding.
|
|
@@ -202,6 +234,9 @@ class FamaMacBethRegression(Empirical):
|
|
|
202
234
|
) -> RegModels:
|
|
203
235
|
"""Parses the model specifications into a RegModels object.
|
|
204
236
|
|
|
237
|
+
This method parses the model specifications and converts them into a `RegModels` object.
|
|
238
|
+
It supports input in multiple formats, including lists and dictionaries.
|
|
239
|
+
|
|
205
240
|
Args:
|
|
206
241
|
regs (Optional[list[list[str] | dict[str, list[str]]] | list | dict]): Model specifications.
|
|
207
242
|
endog (Optional[str]): Name of the dependent variable.
|
|
@@ -244,8 +279,13 @@ class FamaMacBethRegression(Empirical):
|
|
|
244
279
|
is_winsorize: bool = False,
|
|
245
280
|
is_normalize: bool = False,
|
|
246
281
|
decimal: Optional[int] = None,
|
|
282
|
+
return_intermediate: bool = False, # New parameter to control whether intermediate results are returned
|
|
247
283
|
) -> DataFrame:
|
|
248
|
-
"""Fits the Fama-MacBeth regression model and returns the results DataFrame.
|
|
284
|
+
"""Fits the Fama-MacBeth regression model and returns the results DataFrame or intermediate results.
|
|
285
|
+
|
|
286
|
+
This method fits the Fama-MacBeth regression model, processes data
|
|
287
|
+
(including winsorization, industry weighting, normalization), and
|
|
288
|
+
returns either the final regression results or intermediate results.
|
|
249
289
|
|
|
250
290
|
Args:
|
|
251
291
|
endog (Optional[str]): Name of the dependent variable.
|
|
@@ -259,12 +299,12 @@ class FamaMacBethRegression(Empirical):
|
|
|
259
299
|
is_winsorize (bool): Indicates whether to apply winsorization.
|
|
260
300
|
is_normalize (bool): Indicates whether to normalize exogenous variables.
|
|
261
301
|
decimal (Optional[int]): Number of decimal places for rounding in output.
|
|
302
|
+
return_intermediate (bool): If True, returns the intermediate results (e.g., coefficients for each time period).
|
|
262
303
|
|
|
263
304
|
Returns:
|
|
264
|
-
DataFrame: DataFrame containing the regression results
|
|
305
|
+
DataFrame: DataFrame containing the regression results or intermediate results based on `return_intermediate`.
|
|
265
306
|
"""
|
|
266
307
|
# Preparation
|
|
267
|
-
# self.temp_panel: PanelData = self.panel_data.copy()
|
|
268
308
|
reg_models: RegModels = self._model_parse(
|
|
269
309
|
regs=regs, endog=endog, exog=columns_to_list(exog)
|
|
270
310
|
)
|
|
@@ -276,6 +316,19 @@ class FamaMacBethRegression(Empirical):
|
|
|
276
316
|
weight=weight,
|
|
277
317
|
)
|
|
278
318
|
exog_order = (exog_order or reg_models.exogenous) + ["const"]
|
|
319
|
+
|
|
320
|
+
if return_intermediate:
|
|
321
|
+
intermediate_results = [
|
|
322
|
+
self._reg(
|
|
323
|
+
df=self.panel_data.df,
|
|
324
|
+
reg=model,
|
|
325
|
+
is_normalize=is_normalize,
|
|
326
|
+
return_intermediate=True,
|
|
327
|
+
)
|
|
328
|
+
for model in reg_models.models
|
|
329
|
+
]
|
|
330
|
+
return intermediate_results
|
|
331
|
+
|
|
279
332
|
# Regression
|
|
280
333
|
df: DataFrame = (
|
|
281
334
|
pd.concat(
|
|
@@ -315,13 +368,13 @@ if __name__ == "__main__":
|
|
|
315
368
|
result = fm.fit(
|
|
316
369
|
# endog="return",
|
|
317
370
|
# exog="MktCap",
|
|
318
|
-
exog_order=["MktCap"],
|
|
371
|
+
# exog_order=["MktCap", "Illiq", "IdioVol"],
|
|
319
372
|
regs=[
|
|
320
|
-
"return",
|
|
321
|
-
"MktCap",
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
373
|
+
# "return",
|
|
374
|
+
# "MktCap",
|
|
375
|
+
["return", "Illiq"],
|
|
376
|
+
["return", "IdioVol"],
|
|
377
|
+
["return", "MktCap", "Illiq", "IdioVol"],
|
|
325
378
|
],
|
|
326
379
|
# models=[
|
|
327
380
|
# {"return": ["MktCap"]},
|
|
@@ -329,11 +382,13 @@ if __name__ == "__main__":
|
|
|
329
382
|
# {"return": ["IdioVol"]},
|
|
330
383
|
# {"return": ["MktCap", "Illiq", "IdioVol"]},
|
|
331
384
|
# ],
|
|
332
|
-
industry="industry",
|
|
333
|
-
industry_weighed_method="value",
|
|
334
|
-
weight="MktCap",
|
|
335
|
-
|
|
385
|
+
# industry="industry",
|
|
386
|
+
# industry_weighed_method="value",
|
|
387
|
+
# weight="MktCap",
|
|
388
|
+
is_winsorize=True,
|
|
336
389
|
is_normalize=True,
|
|
390
|
+
return_intermediate=True,
|
|
337
391
|
# decimal=2,
|
|
338
392
|
)
|
|
339
|
-
pp(
|
|
393
|
+
pp(result)
|
|
394
|
+
pp(result[0])
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|