diff --git a/.gitignore b/.gitignore index 425cd6e..c32fba4 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ src/moddata/data/archive.zip .DS_Store src/moddata/data/btc.parquet -src/moddata/data/stooq_data/* -src/moddata/data/regime_model_data/* +src/moddata/data/stooq_data +src/moddata/data/regime_model_data sandbox.py .coverage moddata.egg-info diff --git a/src/moddata/_utils.py b/src/moddata/_utils.py index 7ee8763..1eae5c6 100644 --- a/src/moddata/_utils.py +++ b/src/moddata/_utils.py @@ -17,7 +17,8 @@ "spx_1901-2025", "sunspots", "geomagnetic_activity", - "world_bank_oil_gold_monthly_prices" + "world_bank_oil_gold_monthly_prices", + "eurpln_regime" ] @@ -92,6 +93,11 @@ def _load_spx_1901_to_2025() -> pd.DataFrame: resources.files('moddata.data').joinpath('spx_1901-2025.parquet') # noqa )) +def _load_eurpln_regime() -> pd.DataFrame: + return pd.read_parquet(str( + resources.files('moddata.data').joinpath('eurpln_regime.parquet') # noqa + )) + def load_data(dataset: Dataset) -> pd.DataFrame | None: if dataset == "bankchurn": @@ -108,4 +114,6 @@ def load_data(dataset: Dataset) -> pd.DataFrame | None: return _load_world_bank_oil_gold_monthly_prices() if dataset == "spx_1901-2025": return _load_spx_1901_to_2025() + if dataset == "eurpln_regime": + return _load_eurpln_regime() raise ValueError(f"Encountered invalid dataset name: {dataset}") diff --git a/src/moddata/common/aliases.py b/src/moddata/common/aliases.py new file mode 100644 index 0000000..cc01c3b --- /dev/null +++ b/src/moddata/common/aliases.py @@ -0,0 +1,4 @@ +from typing import Literal, TypeAlias + + +JoinStyle: TypeAlias = Literal["outer", "inner"] diff --git a/src/moddata/data/eurpln_regime.parquet b/src/moddata/data/eurpln_regime.parquet new file mode 100644 index 0000000..ff99bb3 Binary files /dev/null and b/src/moddata/data/eurpln_regime.parquet differ diff --git a/src/moddata/extractor/stooq_data_extractor.py b/src/moddata/extractor/stooq_data_extractor.py new file mode 100644 index 0000000..7fe33f8 --- /dev/null +++ b/src/moddata/extractor/stooq_data_extractor.py @@ -0,0 +1,32 @@ +from pathlib import Path +from typing import Final + +import pandas as pd + + +class StooqDataExtractor: + + def __init__( + self, + data_folder: Path, + files: list[str] + ): + self._data_folder: Final[Path] = data_folder + self._files: Final[list[str]] = files + + def _retrieve_ticker(self, file: str) -> str: + return file.split(".")[0].split("_")[0] + + def _extract_file(self, file: str) -> pd.DataFrame: + data = pd.read_csv(self._data_folder / file, sep=",", decimal=".",) + data.columns = [c.lower() for c in data.columns] + data["date"] = pd.to_datetime(data["date"]) + data = data.set_index("date") + data.columns = pd.MultiIndex.from_product([ + [self._retrieve_ticker(file)], + list(data.columns), + ], names=["ticker", "variable"]) + return data + + def extract(self) -> dict[str, pd.DataFrame]: + return {file: self._extract_file(file) for file in self._files} diff --git a/src/moddata/pipeline/eurpln_regime_modeling_data_pipeline.py b/src/moddata/pipeline/eurpln_regime_modeling_data_pipeline.py new file mode 100644 index 0000000..351bbf4 --- /dev/null +++ b/src/moddata/pipeline/eurpln_regime_modeling_data_pipeline.py @@ -0,0 +1,66 @@ +from datetime import date +from pathlib import Path + +import pandas as pd + +from moddata.extractor.stooq_data_extractor import StooqDataExtractor +from moddata.extractor.cboe_vix_data_extractor import CboeVIXDataExtractor +from moddata.transformer.merge_stooq_data_transformer import ( + MergeStooqDataTransformer) + + +class EURPLNRegimeModelingDataPipeline: + + def __init__( + self, + start: date = date(2016, 1, 1), + end: date = date(2025, 12, 31) + ): + self._start, self._end = start, end + self._stooq_extractor = StooqDataExtractor( + data_folder=( + Path(__file__).parent.parent / + "data" / + "regime_model_data" + ), + files=[ + "10ydey_b_d.csv", + "10yply_b_d.csv", + "10yusy_b_d.csv", + "^uslc_d.csv", + "eurczk_d.csv", + "eurhuf_d.csv", + "eurpln_d.csv", + "eurusd_d.csv", + "usdpln_d.csv" + ] + ) + self._stooq_transformer = MergeStooqDataTransformer() + self._vix_extractor = CboeVIXDataExtractor() + + def run(self): + stooq_data = self._stooq_extractor.extract() + stooq_data = self._stooq_transformer.transform(stooq_data) + + vix_data = self._vix_extractor.extract() + + data = pd.merge( + left=stooq_data, + right=vix_data, + how="outer", + left_index=True, + right_index=True + ) + data = data[f"{self._start:%Y-%m-%d}":f"{self._end:%Y-%m-%d}"] + data = data.ffill().bfill() + + data.to_parquet( + Path(__file__).parent.parent / + "data" / + "eurpln_regime.parquet", + engine="pyarrow" + ) + + +if __name__ == "__main__": + EURPLNRegimeModelingDataPipeline().run() diff --git a/src/moddata/transformer/merge_stooq_data_transformer.py b/src/moddata/transformer/merge_stooq_data_transformer.py new file mode 100644 index 0000000..5287560 --- /dev/null +++ b/src/moddata/transformer/merge_stooq_data_transformer.py @@ -0,0 +1,27 @@ +from functools import reduce + +import pandas as pd + +from moddata.common.aliases import JoinStyle + + +class MergeStooqDataTransformer: + + def __init__(self, join_style: JoinStyle = "outer"): + self._join_style: JoinStyle = join_style + + def transform(self, data: dict[str, pd.DataFrame]) -> pd.DataFrame: + data: pd.DataFrame = reduce( + lambda l_, r_: pd.merge( + left=l_, + right=r_, + left_index=True, + right_index=True, + how=self._join_style # noqa + ), + data.values() + ) + data = data.reset_index(drop=False).set_index("date") + if self._join_style == "outer": + data = data.ffill().bfill() + return data