diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e41135b..6227232 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,9 +5,10 @@ name: CI on: push: - branches: [ main ] + branches: [ main, dev ] pull_request: - + branches: [ main, dev ] + jobs: test: runs-on: ${{ matrix.os }} diff --git a/README.md b/README.md index 2c3d162..5db42d7 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,12 @@ The information provided by OPES is for educational, research and informational | Constant | L1 Regularization | | Gamma | L2 Regularization | | Lognormal | L-infinity Regularization | -| Inverse Gaussian | Entropy | +| Inverse Gaussian | Negative Entropy | | Compound Poisson-Lognormal | Weight Variance | | | Mean Pairwise Absolute Deviation | -| | KL-Divergence from Uniform (Experimental) | -| | JS-Divergence from Uniform (Experimental) | +| | Maximum Pairwise Deviation | +| | JS-Divergence from Uniform Weights | +| | Wasserstein-1 from Uniform Weights | --- @@ -143,7 +144,7 @@ You can also verify by using python. import yfinance as yf # Importing our Kelly class -from opes.objectives.utility_theory import Kelly +from opes.objectives import Kelly # Obtaining ticker data # Basic yfinance stuff diff --git a/docs/docs/backtesting.md b/docs/docs/backtesting.md index c4447b1..36a34ce 100644 --- a/docs/docs/backtesting.md +++ b/docs/docs/backtesting.md @@ -28,12 +28,12 @@ It also stores transaction cost parameters for portfolio simulations. **Args:** -- `train_data` (*pd.DataFrame*): Historical training data. Defaults to None. -- `test_data` (*pd.DataFrame*): Historical testing data. Defaults to None. +- `train_data` (*pd.DataFrame*): Historical training data. Defaults to `None`. +- `test_data` (*pd.DataFrame*): Historical testing data. Defaults to `None`. - `cost` (*dict, optional*): Transaction cost parameters. Defaults to `{'const': 10.0}`. Various cost models are given below: - `{'const': constant_bps_value}`: Constant cost value throughout time. Deterministic. - `{'gamma': (shape, scale)}`: Gamma distributed cost. Stochastic. - - `{'lognormal': (mu, sigma)}`: lognormally distributed cost. Stochastic. + - `{'lognormal': (mu, sigma)}`: Lognormally distributed cost. Stochastic. - `{'inversegaussian': (mean, shape)}`: Inverse gaussian distributed cost. Stochastic. - `{'jump': (arrival_rate, mu, sigma)}`: Poisson-compound lognormally distributed cost. Stochastic. @@ -64,8 +64,6 @@ It also stores transaction cost parameters for portfolio simulations. $$R_t = \frac{P^{(t)}}{P^{(t-1)}} - 1$$ ---- - ### Methods #### `backtest` @@ -254,9 +252,9 @@ def plot_wealth( OPES ships with a basic plotting utility for visualizing portfolio wealth over time. This method exists for quick inspection and debugging, not for deep performance analysis. -It visualizes cumulative wealth for one or multiple strategies -using their periodic returns. It also provides a breakeven reference line -and optional saving of the plot to a file. +It visualizes cumulative wealth for one or multiple strategies using their periodic +returns. It also provides a breakeven reference line and optional saving of the plot to +a file. !!! tip "Recommendation:" For serious research, reporting, or strategy comparison, we strongly recommend writing your own custom plotting pipeline. @@ -309,7 +307,7 @@ and optional saving of the plot to a file. "Maximum Mean (L2, 1e-3)": scenario_1['returns'], "Mean Variance (RA=1.5)": scenario_2, }, - timeline=scenario_1['dates'] + timeline=scenario_1['timeline'] ) ``` diff --git a/docs/docs/index.md b/docs/docs/index.md index a6fa3bf..5e07ebc 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -25,7 +25,7 @@ OPES is a research-oriented and experimentation-focused Python module for portfo # Demonstration of portfolio optimization using the Kelly Criterion # 'data' represents OHLCV market data grouped by ticker symbols - from opes.objectives.utility_theory import Kelly + from opes.objectives import Kelly # Initialize a Kelly portfolio with fractional exposure and L2 regularization kelly_portfolio = Kelly(fraction=0.8, reg="l2", strength=0.01) diff --git a/docs/docs/regularization.md b/docs/docs/regularization.md index 9575772..81531a4 100644 --- a/docs/docs/regularization.md +++ b/docs/docs/regularization.md @@ -16,24 +16,24 @@ where $R(\mathbf{w})$ encodes structural preferences over the weights $\mathbf{w ## Regularization Schemes -| Name | Formulation | Use-case | -|------------|---------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| `l1` | $\sum_i \lvert \mathbf{w}_i\rvert$ | Encourages sparse portfolios by driving many weights to zero. Mostly relevant for long-short or unconstrained gross exposure settings. | -| `l2` | $\sum_i \mathbf{w}_i^2$ | Produces smooth, stable portfolios and reduces sensitivity to noise. | -| `l-inf` | $\max_i \lvert \mathbf{w}_i\rvert$ | Penalizes the largest absolute position, enforcing a soft cap on single-asset dominance. | -| `entropy` | $-\sum_i \mathbf{w}_i \log \mathbf{w}_i$ | Encourages diversification by penalizing concentration. | -| `variance` | $\ \text{Var}(\mathbf{w})$ | Pushes allocations toward uniformity without strictly enforcing equal weights. | -| `mpad` | $\frac{1}{n^2} \sum_{i}^n \sum_{j}^n \lvert \mathbf{w}_i - \mathbf{w}_j\rvert$ | Measures and penalizes inequality across weights. | -| `kld` | $\ \text{D}_{\text{KL}}(\mathbf{w} \| \mathbf{u})$ | Measures Kullback-Leibler divergence from uniform weights. | -| `jsd` | $\ \text{D}_{\text{JSD}}(\mathbf{w} \| \mathbf{u})$ | Measures Jensen-Shannon divergence from uniform weights. | - -!!! note "Note" - For long-short portfolios, mathematically grounded regularizers such as `entropy`, `kld`, and `jsd` first normalize the weights - and constrain them to the simplex before applying the regularization, ensuring mathematical coherence is not violated. - -!!! note "Temporary Note" - Kullback-Leibler regularization and entropy are the exact same, since KL-divergence's prior distribution is uniform weights. However - it is included so that it *may* be later updated with custom prior distribution (weights). + | Regularization Scheme | Identifier | Formulation | + | ----------------------------------------------- | ---------- | ------------------------------------------------------------------------------------- | + | Taxicab Norm | `l1` | $\sum_i \lvert \mathbf{w}_i\rvert$ | + | Euclidean Norm | `l2` | $\sum_i \mathbf{w}_i^2$ | + | Chebyshev Norm | `l-inf` | $\max_i \lvert \mathbf{w}_i\rvert$ | + | Negative Entropy of Weights | `entropy` | $\sum_i \mathbf{w}_i \log \mathbf{w}_i$ | + | Jensen-Shannon Divergence from Uniform Weights | `jsd` | $\text{D}_{\text{JSD}}(\mathbf{w} \| \mathbf{u})$ | + | Variance of Weights | `variance` | $\text{Var}(\mathbf{w})$ | + | Mean Pairwise Absolute Deviation | `mpad` | $\frac{1}{n^2} \sum_{i}^n \sum_{j}^n \lvert \mathbf{w}_i - \mathbf{w}_j\rvert$ | + | Maximum Pairwise Deviation | `mpd` | $\max_{i,j} \lvert \mathbf{w}_i - \mathbf{w}_j \rvert$ | + | Wasserstein-1 Distance from Uniform Weights | `wass-1` | $\text{W}_{1}(\mathbf{w}, \mathbf{u})$ | + +!!! note "Notes" + - `l1` regularization is mainly used for long-short portfolios to encourage less extreme + allocations to meet the net exposure of 1. Using it on long-only portfolios is redundant. + - For long-short portfolios, mathematically grounded regularizers such as `entropy`, `jsd` + and `wass-1` first normalize the weights and constrain them to the simplex before applying + the regularization, ensuring mathematical coherence is not violated. --- @@ -60,18 +60,19 @@ The following objectives do not support regularization: ```python -# Importing a valid optimizer from opes -from opes.objectives.markowitz import MaxMean +# Importing an optimizer which supports regularization +from opes.objectives import MaxMean # Initializing different portfolios with various regularization schemes -maxmean_taxi = MaxMean(reg='l1', strength=0.01) -maxmean_eucl = MaxMean(reg='l2', strength=0.01) -maxmean_cheby = MaxMean(reg='l-inf', strength=0.01) -maxmean_entropy = MaxMean(reg='entropy', strength=0.01) -maxmean_var = MaxMean(reg='variance', strength=0.01) -maxmean_mpad = MaxMean(reg='mpad', strength=0.01) -maxmean_mpad = MaxMean(reg='kld', strength=0.01) -maxmean_mpad = MaxMean(reg='jsd', strength=0.01) +maxmean_taxi = MaxMean(reg='l1', strength=0.01) # Taxicab norm +maxmean_eucl = MaxMean(reg='l2', strength=0.01) # Euclidean norm +maxmean_cheby = MaxMean(reg='l-inf', strength=0.01) # Chebyshev norm +maxmean_entropy = MaxMean(reg='entropy', strength=0.01) # Negative entropy of weights +maxmean_jsd = MaxMean(reg='jsd', strength=0.01) # Jensen-Shannon divergence +maxmean_var = MaxMean(reg='variance', strength=0.01) # Variance of weights +maxmean_mpad = MaxMean(reg='mpad', strength=0.01) # Mean pairwise absolute deviation +maxmean_mpd = MaxMean(reg='mpd', strength=0.01) # Maximum pairwise deviation +maxmean_wass = MaxMean(reg='wass-1', strength=0.01) # Wasserstein-1 ``` diff --git a/opes/__init__.py b/opes/__init__.py index deded32..1cfce80 100644 --- a/opes/__init__.py +++ b/opes/__init__.py @@ -1 +1,5 @@ -__version__ = "0.8.2" +# Version Log +__version__ = "0.9.1" + +# Backtester easy import +from .backtester import Backtester diff --git a/opes/backtester.py b/opes/backtester.py index 70907c7..a67c55a 100644 --- a/opes/backtester.py +++ b/opes/backtester.py @@ -36,12 +36,12 @@ def __init__(self, train_data=None, test_data=None, cost={"const": 10.0}): """ **Args:** - - `train_data` (*pd.DataFrame*): Historical training data. Defaults to None. - - `test_data` (*pd.DataFrame*): Historical testing data. Defaults to None. + - `train_data` (*pd.DataFrame*): Historical training data. Defaults to `None`. + - `test_data` (*pd.DataFrame*): Historical testing data. Defaults to `None`. - `cost` (*dict, optional*): Transaction cost parameters. Defaults to `{'const': 10.0}`. Various cost models are given below: - `{'const': constant_bps_value}`: Constant cost value throughout time. Deterministic. - `{'gamma': (shape, scale)}`: Gamma distributed cost. Stochastic. - - `{'lognormal': (mu, sigma)}`: lognormally distributed cost. Stochastic. + - `{'lognormal': (mu, sigma)}`: Lognormally distributed cost. Stochastic. - `{'inversegaussian': (mean, shape)}`: Inverse gaussian distributed cost. Stochastic. - `{'jump': (arrival_rate, mu, sigma)}`: Poisson-compound lognormally distributed cost. Stochastic. @@ -71,8 +71,6 @@ def __init__(self, train_data=None, test_data=None, cost={"const": 10.0}): - After cleaning/truncation, close prices are extracted per asset. Returns are computed as: $$R_t = \\frac{P^{(t)}}{P^{(t-1)}} - 1$$ - - --- """ # Assigning by dropping nans to ensure proper indexing # Dropping nan rows results makes backtest loops robust and predictable @@ -489,9 +487,9 @@ def plot_wealth( OPES ships with a basic plotting utility for visualizing portfolio wealth over time. This method exists for quick inspection and debugging, not for deep performance analysis. - It visualizes cumulative wealth for one or multiple strategies - using their periodic returns. It also provides a breakeven reference line - and optional saving of the plot to a file. + It visualizes cumulative wealth for one or multiple strategies using their periodic + returns. It also provides a breakeven reference line and optional saving of the plot to + a file. !!! tip "Recommendation:" For serious research, reporting, or strategy comparison, we strongly recommend writing your own custom plotting pipeline. @@ -542,7 +540,7 @@ def plot_wealth( "Maximum Mean (L2, 1e-3)": scenario_1['returns'], "Mean Variance (RA=1.5)": scenario_2, }, - timeline=scenario_1['dates'] + timeline=scenario_1['timeline'] ) ``` """ diff --git a/opes/objectives/__init__.py b/opes/objectives/__init__.py index e69de29..12dbf18 100644 --- a/opes/objectives/__init__.py +++ b/opes/objectives/__init__.py @@ -0,0 +1,38 @@ +# Utility Theory +from .utility_theory import Kelly, QuadraticUtility, CARA, CRRA, HARA + +# Markowitz Portfolio Theory +from .markowitz import MaxMean, MinVariance, MeanVariance, MaxSharpe + +# Risk Measures +from .risk_measures import ( + VaR, + CVaR, + MeanCVaR, + EVaR, + MeanEVaR, + EntropicRisk, + WorstCaseLoss, +) + +# Principled Heuristics +from .heuristics import ( + Uniform, + InverseVolatility, + SoftmaxMean, + MaxDiversification, + RiskParity, + REPO, +) + +# Online Portfolios +from .online import UniversalPortfolios, BCRP, ExponentialGradient + +# Distributionally Robust Optimization +from .distributionally_robust import ( + KLRobustKelly, + KLRobustMaxMean, + WassRobustMaxMean, + WassRobustMinVariance, + WassRobustMeanVariance, +) diff --git a/opes/objectives/distributionally_robust.py b/opes/objectives/distributionally_robust.py index 847758f..1a62326 100644 --- a/opes/objectives/distributionally_robust.py +++ b/opes/objectives/distributionally_robust.py @@ -331,8 +331,8 @@ def _find_dual(self): def _prepare_optimization_inputs(self, data, weight_bounds, w, custom_mean=None): # Extracting trimmed return data from OHLCV and obtaining tickers and Checking for initial weights # Checking for mean and weights and assigning optimization data accordingly + self.tickers, data = extract_trim(data) self.mean = np.mean(data, axis=0) if custom_mean is None else custom_mean - self.tickers = extract_trim(data)[0] self.weights = np.array( np.ones(len(self.tickers)) / len(self.tickers) if w is None else w, dtype=float, @@ -490,6 +490,7 @@ def _find_dual(self): def _prepare_optimization_inputs(self, data, weight_bounds, w, custom_cov=None): # Extracting trimmed return data from OHLCV and obtaining tickers and Checking for initial weights # Checking for mean and weights and assigning optimization data accordingly + self.tickers, data = extract_trim(data) if custom_cov is None: # Handling invertibility using the small epsilon * identity matrix # small epsilon scales with the trace of the covariance @@ -500,7 +501,6 @@ def _prepare_optimization_inputs(self, data, weight_bounds, w, custom_cov=None): ) else: self.covariance = custom_cov - self.tickers = extract_trim(data)[0] self.weights = np.array( np.ones(len(self.tickers)) / len(self.tickers) if w is None else w, dtype=float, @@ -667,6 +667,7 @@ def _prepare_optimization_inputs( ): # Extracting trimmed return data from OHLCV and obtaining tickers and Checking for initial weights # Checking for mean and weights and assigning optimization data accordingly + self.tickers, data = extract_trim(data) self.mean = np.mean(data, axis=0) if custom_mean is None else custom_mean if custom_cov is None: # Handling invertibility using the small epsilon * identity matrix @@ -678,7 +679,6 @@ def _prepare_optimization_inputs( ) else: self.covariance = custom_cov - self.tickers = extract_trim(data)[0] self.weights = np.array( np.ones(len(self.tickers)) / len(self.tickers) if w is None else w, dtype=float, diff --git a/opes/regularizer.py b/opes/regularizer.py index 2c5894a..8560da9 100644 --- a/opes/regularizer.py +++ b/opes/regularizer.py @@ -15,24 +15,24 @@ ### Regularization Schemes -| Name | Formulation | Use-case | -|------------|---------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| -| `l1` | $\\sum_i \\lvert \\mathbf{w}_i\\rvert$ | Encourages sparse portfolios by driving many weights to zero. Mostly relevant for long-short or unconstrained gross exposure settings. | -| `l2` | $\\sum_i \\mathbf{w}_i^2$ | Produces smooth, stable portfolios and reduces sensitivity to noise. | -| `l-inf` | $\\max_i \\lvert \\mathbf{w}_i\\rvert$ | Penalizes the largest absolute position, enforcing a soft cap on single-asset dominance. | -| `entropy` | $-\\sum_i \\mathbf{w}_i \\log \\mathbf{w}_i$ | Encourages diversification by penalizing concentration. | -| `variance` | $\\ \\text{Var}(\\mathbf{w})$ | Pushes allocations toward uniformity without strictly enforcing equal weights. | -| `mpad` | $\\frac{1}{n^2} \\sum_{i}^n \\sum_{j}^n \\lvert \\mathbf{w}_i - \\mathbf{w}_j\\rvert$ | Measures and penalizes inequality across weights. | -| `kld` | $\\ \\text{D}_{\\text{KL}}(\\mathbf{w} \\| \\mathbf{u})$ | Measures Kullback-Leibler divergence from uniform weights. | -| `jsd` | $\\ \\text{D}_{\\text{JSD}}(\\mathbf{w} \\| \\mathbf{u})$ | Measures Jensen-Shannon divergence from uniform weights. | - -!!! note "Note" - For long-short portfolios, mathematically grounded regularizers such as `entropy`, `kld`, and `jsd` first normalize the weights - and constrain them to the simplex before applying the regularization, ensuring mathematical coherence is not violated. - -!!! note "Temporary Note" - Kullback-Leibler regularization and entropy are the exact same, since KL-divergence's prior distribution is uniform weights. However - it is included so that it *may* be later updated with custom prior distribution (weights). + | Regularization Scheme | Identifier | Formulation | + | ----------------------------------------------- | ---------- | ------------------------------------------------------------------------------------- | + | Taxicab Norm | `l1` | $\\sum_i \\lvert \\mathbf{w}_i\\rvert$ | + | Euclidean Norm | `l2` | $\\sum_i \\mathbf{w}_i^2$ | + | Chebyshev Norm | `l-inf` | $\\max_i \\lvert \\mathbf{w}_i\\rvert$ | + | Negative Entropy of Weights | `entropy` | $\\sum_i \\mathbf{w}_i \\log \\mathbf{w}_i$ | + | Jensen-Shannon Divergence from Uniform Weights | `jsd` | $\\text{D}_{\\text{JSD}}(\\mathbf{w} \\| \\mathbf{u})$ | + | Variance of Weights | `variance` | $\\text{Var}(\\mathbf{w})$ | + | Mean Pairwise Absolute Deviation | `mpad` | $\\frac{1}{n^2} \\sum_{i}^n \\sum_{j}^n \\lvert \\mathbf{w}_i - \\mathbf{w}_j\\rvert$ | + | Maximum Pairwise Deviation | `mpd` | $\\max_{i,j} \\lvert \\mathbf{w}_i - \\mathbf{w}_j \\rvert$ | + | Wasserstein-1 Distance from Uniform Weights | `wass-1` | $\\text{W}_{1}(\\mathbf{w}, \\mathbf{u})$ | + +!!! note "Notes" + - `l1` regularization is mainly used for long-short portfolios to encourage less extreme + allocations to meet the net exposure of 1. Using it on long-only portfolios is redundant. + - For long-short portfolios, mathematically grounded regularizers such as `entropy`, `jsd` + and `wass-1` first normalize the weights and constrain them to the simplex before applying + the regularization, ensuring mathematical coherence is not violated. --- @@ -59,23 +59,25 @@ ```python -# Importing a valid optimizer from opes -from opes.objectives.markowitz import MaxMean +# Importing an optimizer which supports regularization +from opes.objectives import MaxMean # Initializing different portfolios with various regularization schemes -maxmean_taxi = MaxMean(reg='l1', strength=0.01) -maxmean_eucl = MaxMean(reg='l2', strength=0.01) -maxmean_cheby = MaxMean(reg='l-inf', strength=0.01) -maxmean_entropy = MaxMean(reg='entropy', strength=0.01) -maxmean_var = MaxMean(reg='variance', strength=0.01) -maxmean_mpad = MaxMean(reg='mpad', strength=0.01) -maxmean_mpad = MaxMean(reg='kld', strength=0.01) -maxmean_mpad = MaxMean(reg='jsd', strength=0.01) +maxmean_taxi = MaxMean(reg='l1', strength=0.01) # Taxicab norm +maxmean_eucl = MaxMean(reg='l2', strength=0.01) # Euclidean norm +maxmean_cheby = MaxMean(reg='l-inf', strength=0.01) # Chebyshev norm +maxmean_entropy = MaxMean(reg='entropy', strength=0.01) # Negative entropy of weights +maxmean_jsd = MaxMean(reg='jsd', strength=0.01) # Jensen-Shannon divergence +maxmean_var = MaxMean(reg='variance', strength=0.01) # Variance of weights +maxmean_mpad = MaxMean(reg='mpad', strength=0.01) # Mean pairwise absolute deviation +maxmean_mpd = MaxMean(reg='mpd', strength=0.01) # Maximum pairwise deviation +maxmean_wass = MaxMean(reg='wass-1', strength=0.01) # Wasserstein-1 ``` """ import numpy as np +from scipy.stats import wasserstein_distance from opes.errors import PortfolioError # Small epsilon for numerical stability @@ -96,23 +98,6 @@ def _shannon_entropy(w): return neg_entropy -# Helper function for Kullback-Leibler regularization -# Returns KL-Divergence value from uniform weights -def _kullback_leibler(w): - - # Absoluting and Normalizing weights - w = np.abs(w) - w = w / w.sum() - - # Initiating equal weights - equal_weight = np.ones(len(w)) / len(w) - - # Computing Kullback-Leibler divergence - kl_reg = np.sum(w * np.log(w / (equal_weight + _SMALL_EPSILON))) - - return kl_reg - - # Helper function for Jensen-Shannon regularization # Returns JS-Divergence value from uniform weights def _jensen_shannon(w): @@ -139,6 +124,22 @@ def _jensen_shannon(w): return js_reg +# Helper function for Wasserstein regularization +# Returns Wasserstein-1 distance from uniform weights +def _wasserstein_distance(w): + # Absoluting and Normalizing weights + w = np.abs(w) + w = w / w.sum() + + # Initiating equal weights + equal_weight = np.ones(len(w)) / len(w) + + # Computing wasserstein-1 distance + wass_dist = wasserstein_distance(w, equal_weight) + + return wass_dist + + # Regularizer finding function # Accepts string and returns a function which can be activated while solving the objective def _find_regularizer(reg): @@ -150,11 +151,12 @@ def _find_regularizer(reg): "l-inf": lambda w: np.max(np.abs(w)), "variance": lambda w: np.var(w) if len(w) >= 2 else 0, "mpad": lambda w: np.mean(np.abs(w[:, None] - w[None, :])), + "mpd": lambda w: np.abs(np.max(w) - np.min(w)), # Regularizers using a helper function # The function is returned instead of lambda "entropy": _shannon_entropy, - "kld": _kullback_leibler, "jsd": _jensen_shannon, + "wass-1": _wasserstein_distance, } # Checking regularizer validity diff --git a/pyproject.toml b/pyproject.toml index aac5a3a..bfed9d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,7 +10,7 @@ dev = ["pytest", "other-dev-packages"] [project] name = "opes" -version = "0.9.0" +version = "0.9.1" description = "A research-focused portfolio optimization and backtesting engine." readme = "README.md" requires-python = ">=3.10" diff --git a/tests/regularizer_test.py b/tests/regularizer_test.py index 1eee8d0..8bb56d2 100644 --- a/tests/regularizer_test.py +++ b/tests/regularizer_test.py @@ -13,7 +13,16 @@ # Available regularizers # L1 regularizer is checked separately since it is primarily intended for long-short portfolios -regularizers_available = ["l2", "l-inf", "entropy", "variance", "mpad", "kld", "jsd"] +regularizers_available = [ + "l2", + "l-inf", + "entropy", + "variance", + "mpad", + "mpd", + "jsd", + "wass-1", +] # Function to fetch data from predetermined csv files