diff --git a/.bumpversion.cfg b/.bumpversion.cfg index aced4326..7268e3d8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,9 +1,13 @@ [bumpversion] -current_version = 0.9.1 +current_version = 1.0.0 commit = False tag = False tag_name = {new_version} -[bumpversion:file:setup.cfg] +[bumpversion:file:pyproject.toml] +search = version = "{current_version}" +replace = version = "{new_version}" [bumpversion:file:docs/source/conf.py] +search = release = "{current_version}" +replace = release = "{new_version}" diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index a80dc918..00000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,66 +0,0 @@ -version: 2.1 - -workflows: - version: 2 - main: - jobs: - - test-code - - test-docs - -jobs: - test-code: - docker: - - image: circleci/python:3.8 - steps: - # Step 1: obtain repo from GitHub - - checkout - # Step 2: create virtual env and install dependencies - - run: - name: Create Virtualenv - command: | - virtualenv venv - - run: - name: Install Dependencies - command: | - . venv/bin/activate - pip3 install -r requirements.txt - # Step 3: run pylint styling check - - run: - name: Run Pylint Check - command: | - . venv/bin/activate - pylint arbitragelab tests --rcfile=.pylintrc -f text - # Step 4: run coverage check - - run: - name: Run Unit Tests and Coverage Check - command: | - bash coverage - - store_test_results: - path: test-reports - - store_artifacts: - path: test-reports - - test-docs: - docker: - - image: circleci/python:3.8 - steps: - - checkout - - run: - name: Install requirements - command: | - python3 -m venv venv - . venv/bin/activate - pip install -r requirements.txt - pip install -r docs/source/requirements.txt - - - run: - name: Build documentation - command: | - . venv/bin/activate - make -C docs html - - - run: - name: Run doctests - command: | - . venv/bin/activate - make -C docs doctest diff --git a/.coveragerc b/.coveragerc index c9a46bc4..5fd5c26f 100644 --- a/.coveragerc +++ b/.coveragerc @@ -3,6 +3,7 @@ directory = build/coverage/html [run] branch = True +parallel = True omit = *__init__* arbitragelab/network/imports.py diff --git a/.github/workflows/publish-final-dist.yaml b/.github/workflows/publish-final-dist.yaml new file mode 100644 index 00000000..75af550c --- /dev/null +++ b/.github/workflows/publish-final-dist.yaml @@ -0,0 +1,36 @@ +name: Publish Distribution to PyPI + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]' + +jobs: + build-and-publish-final-dist: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.8' + + - name: Install Poetry + run: | + pip install poetry + + - name: Install dependencies + run: | + poetry install --without docs,tests + + - name: Build the package + run: | + poetry build + + - name: Publish to TestPyPI + env: + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.PYPI_API_TOKEN }} + run: | + poetry publish diff --git a/.github/workflows/publish-test-dist.yaml b/.github/workflows/publish-test-dist.yaml new file mode 100644 index 00000000..50172a74 --- /dev/null +++ b/.github/workflows/publish-test-dist.yaml @@ -0,0 +1,37 @@ +name: Publish Distribution to TestPyPI + +on: + push: + tags: + - '[0-9]+.[0-9]+.[0-9]+-dev' + +jobs: + build-and-publish-test-dist: + runs-on: ubuntu-latest + steps: + - name: Check out code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + + - name: Install Poetry + run: | + pip install poetry + + - name: Install dependencies + run: | + poetry install --without docs,tests + + - name: Build the package + run: | + poetry build + + - name: Publish to TestPyPI + env: + POETRY_PYPI_TOKEN_TESTPYPI: ${{ secrets.TEST_PYPI_API_TOKEN }} + run: | + poetry config repositories.testpypi https://test.pypi.org/legacy/ + poetry publish -r testpypi diff --git a/.github/workflows/python-tests.yaml b/.github/workflows/python-tests.yaml new file mode 100644 index 00000000..a7819a07 --- /dev/null +++ b/.github/workflows/python-tests.yaml @@ -0,0 +1,126 @@ +name: Test code and documentation + +on: + push: + branches: + - develop + pull_request: + branches: + - develop + +jobs: + test-code-style: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + run: | + pip install --upgrade pip + pip install poetry + + + - name: Install dependencies + run: | + poetry install --without docs + + - name: Run Pylint + run: | + poetry run pylint arbitragelab tests --rcfile=.pylintrc --output-format=text --output=pylint-report.txt + + - name: Upload test results + uses: actions/upload-artifact@v4 + with: + name: pylint-report-${{ matrix.python-version }} + path: pylint-report.txt + + + test-coverage: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + run: | + pip install --upgrade pip + pip install poetry + + - name: Install dependencies + run: | + poetry install --without docs + + - name: Run tests with coverage + run: | + poetry run pytest tests/ --cov=arbitragelab --cov-report=term --cov-branch --cov-config=.coveragerc + + - name: Generate coverage HTML report + run: poetry run coverage html + + - name: Upload Coverage HTML Report as Artifact + uses: actions/upload-artifact@v4 + with: + name: coverage-html-${{ matrix.python-version }} + path: build/coverage/html/index.html + + - name: Check coverage + run: poetry run coverage report --fail-under=100 + + test-docs: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.8] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install Poetry + run: | + pip install poetry + + - name: Install requirements + run: | + poetry install --without tests + + - name: Build documentation + run: | + cd docs + poetry run make html + + - name: Run doctests + run: | + cd docs + poetry run make doctest + + - name: Upload doctest results as an artifact + uses: actions/upload-artifact@v4 + with: + name: doctest-results + path: docs/build/doctest/output.txt + diff --git a/.pylintrc b/.pylintrc index 30bc5156..b9b01a8b 100644 --- a/.pylintrc +++ b/.pylintrc @@ -41,11 +41,11 @@ load-plugins= # Reference: http://pylint-messages.wikidot.com/all-codes disable=I, maybe-no-member, - star-args, - abstract-class-not-used, +# star-args, +# abstract-class-not-used, duplicate-code, superfluous-parens, - abstract-class-little-used, +# abstract-class-little-used, too-few-public-methods, RP0401, RP0801, @@ -70,7 +70,7 @@ output-format=text # Put messages in a separate file for each module / package specified on the # command line instead of printing them on stdout. Reports (if any) will be # written in a file name "pylint_global.[txt|html]". -files-output=no +#files-output=no # Tells whether to display a full report or only the messages reports=yes @@ -91,7 +91,7 @@ msg-template={C}:{line:3d},{column:2d}: [{obj}] {msg} ({msg_id} - {symbol}) [BASIC] # List of builtins function names that should not be used, separated by a comma -bad-functions=map,filter,apply,input +#bad-functions=map,filter,apply,input # Regular expression which should only match correct module names module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ @@ -275,4 +275,4 @@ int-import-graph= # Exceptions that will emit a warning when being caught. Defaults to # "Exception" -overgeneral-exceptions=Exception +overgeneral-exceptions=builtins.Exception diff --git a/.readthedocs.yml b/.readthedocs.yml index d2a0bb56..5df10a44 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -1,24 +1,20 @@ # .readthedocs.yml # Read the Docs configuration file -# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details -# Required version: 2 build: - os: ubuntu-22.04 + os: "ubuntu-22.04" tools: python: "3.8" + jobs: + post_create_environment: + # Install poetry + - pip install poetry + post_install: + # Install dependencies + - VIRTUAL_ENV=$READTHEDOCS_VIRTUALENV_PATH poetry install --only docs -# Build documentation in the docs/ directory with Sphinx sphinx: - configuration: docs/source/conf.py - -# Optionally build your docs in additional formats such as PDF -formats: [] - -# Optionally set the version of Python and requirements required to build your docs -python: - install: - - requirements: docs/source/requirements.txt + configuration: docs/source/conf.py diff --git a/arbitragelab/__init__.py b/arbitragelab/__init__.py index ec992d79..e5a0f45d 100644 --- a/arbitragelab/__init__.py +++ b/arbitragelab/__init__.py @@ -2,6 +2,7 @@ ArbitrageLab helps portfolio managers and traders who want to leverage the power of Statistical Arbitrage by providing reproducible, interpretable, and easy to use tools. """ +# pylint: disable=consider-using-from-import import arbitragelab.codependence as codependence import arbitragelab.cointegration_approach as cointegration_approach diff --git a/arbitragelab/codependence/codependence_matrix.py b/arbitragelab/codependence/codependence_matrix.py index 231d3702..75b40334 100644 --- a/arbitragelab/codependence/codependence_matrix.py +++ b/arbitragelab/codependence/codependence_matrix.py @@ -13,7 +13,7 @@ from arbitragelab.codependence.optimal_transport import optimal_transport_dependence -# pylint: disable=invalid-name +# pylint: disable=invalid-name, unnecessary-lambda-assignment def get_dependence_matrix(df: pd.DataFrame, dependence_method: str, theta: float = 0.5, n_bins: int = None, normalize: bool = True, estimator: str = 'standard', target_dependence: str = 'comonotonicity', diff --git a/arbitragelab/codependence/optimal_transport.py b/arbitragelab/codependence/optimal_transport.py index 1fc0ca43..5aa109ee 100644 --- a/arbitragelab/codependence/optimal_transport.py +++ b/arbitragelab/codependence/optimal_transport.py @@ -2,6 +2,7 @@ Implementations of Optimal Copula Transport dependence measure proposed by Marti et al.: https://arxiv.org/abs/1610.09659 And implemented in the blog post by Marti: https://gmarti.gitlab.io/qfin/2020/06/25/copula-optimal-transport-dependence.html """ +# pylint: disable=broad-exception-raised import warnings import numpy as np diff --git a/arbitragelab/cointegration_approach/coint_sim.py b/arbitragelab/cointegration_approach/coint_sim.py index bd0f5a8b..bdd27ab3 100644 --- a/arbitragelab/cointegration_approach/coint_sim.py +++ b/arbitragelab/cointegration_approach/coint_sim.py @@ -2,6 +2,7 @@ """ This module allows simulation of cointegrated time series pairs. """ +# pylint: disable=consider-using-f-string from typing import Tuple, Optional diff --git a/arbitragelab/cointegration_approach/engle_granger.py b/arbitragelab/cointegration_approach/engle_granger.py index 93a28244..b30395bb 100644 --- a/arbitragelab/cointegration_approach/engle_granger.py +++ b/arbitragelab/cointegration_approach/engle_granger.py @@ -7,7 +7,6 @@ import pandas as pd from statsmodels.tsa.stattools import adfuller from sklearn.linear_model import LinearRegression - from arbitragelab.cointegration_approach.base import CointegratedPortfolio diff --git a/arbitragelab/cointegration_approach/minimum_profit.py b/arbitragelab/cointegration_approach/minimum_profit.py index 1d3d7866..f6d9d5ea 100644 --- a/arbitragelab/cointegration_approach/minimum_profit.py +++ b/arbitragelab/cointegration_approach/minimum_profit.py @@ -1,7 +1,8 @@ -# pylint: disable=invalid-name, too-many-arguments """ This module optimizes the upper and lower bounds for mean-reversion cointegration pair trading. """ +# pylint: disable=invalid-name, too-many-arguments +# pylint: disable=broad-exception-raised, consider-using-f-string import sys import warnings diff --git a/arbitragelab/cointegration_approach/multi_coint.py b/arbitragelab/cointegration_approach/multi_coint.py index bec930df..f146a7c7 100644 --- a/arbitragelab/cointegration_approach/multi_coint.py +++ b/arbitragelab/cointegration_approach/multi_coint.py @@ -1,6 +1,7 @@ """ This module generates a cointegration vector for mean-reversion trading of three or more cointegrated assets. """ +# pylint: disable=consider-using-f-string import warnings from typing import Tuple, Optional @@ -147,7 +148,7 @@ def fit(self, log_price: pd.DataFrame, sig_level: str = "95%", suppress_warnings coint_vec = jo_portfolio.cointegration_vectors.loc[0] self.__coint_vec = coint_vec - return coint_vec + return self.__coint_vec # pylint: disable=invalid-name, too-many-locals def get_coint_vec(self) -> Tuple[pd.DataFrame, ...]: diff --git a/arbitragelab/cointegration_approach/sparse_mr_portfolio.py b/arbitragelab/cointegration_approach/sparse_mr_portfolio.py index 73ed01c0..7220378c 100644 --- a/arbitragelab/cointegration_approach/sparse_mr_portfolio.py +++ b/arbitragelab/cointegration_approach/sparse_mr_portfolio.py @@ -1,4 +1,3 @@ -# pylint: disable=invalid-name """ This module selects sparse mean-reverting portfolios out of an asset universe. The methods implemented in this module include the following: @@ -12,6 +11,8 @@ 7. Semidefinite programming approach to portmanteau statistics optimization under a minimum volatility constraint. 8. Semidefinite programming approach to crossing statistics optimization under a minimum volatility constraint. """ +# pylint: disable=consider-using-f-string +# pylint: disable=invalid-name from typing import Tuple import warnings diff --git a/arbitragelab/copula_approach/archimedean/joe.py b/arbitragelab/copula_approach/archimedean/joe.py index 726579cf..f841389f 100644 --- a/arbitragelab/copula_approach/archimedean/joe.py +++ b/arbitragelab/copula_approach/archimedean/joe.py @@ -2,7 +2,7 @@ Module that houses Joe copula class. """ -# pylint: disable = invalid-name, too-many-lines +# pylint: disable=invalid-name, too-many-lines, unnecessary-lambda-assignment from typing import Callable import numpy as np diff --git a/arbitragelab/copula_approach/archimedean/n13.py b/arbitragelab/copula_approach/archimedean/n13.py index f98756fa..27a7a271 100644 --- a/arbitragelab/copula_approach/archimedean/n13.py +++ b/arbitragelab/copula_approach/archimedean/n13.py @@ -2,7 +2,7 @@ Module that houses N13 copula class. """ -# pylint: disable = invalid-name, too-many-lines +# pylint: disable=invalid-name, too-many-lines, unnecessary-lambda-assignment from typing import Callable import numpy as np diff --git a/arbitragelab/copula_approach/copula_calculation.py b/arbitragelab/copula_approach/copula_calculation.py index 5033ab81..5035642d 100644 --- a/arbitragelab/copula_approach/copula_calculation.py +++ b/arbitragelab/copula_approach/copula_calculation.py @@ -20,7 +20,7 @@ `__ """ -# pylint: disable = invalid-name +# pylint: disable=invalid-name, unnecessary-lambda-assignment from typing import Callable, Tuple import numpy as np diff --git a/arbitragelab/copula_approach/mixed_copulas/cfg_mix_copula.py b/arbitragelab/copula_approach/mixed_copulas/cfg_mix_copula.py index b90d0b01..555413a0 100644 --- a/arbitragelab/copula_approach/mixed_copulas/cfg_mix_copula.py +++ b/arbitragelab/copula_approach/mixed_copulas/cfg_mix_copula.py @@ -2,7 +2,7 @@ Module that implements Clayton, Frank and Gumbel mixed copula. """ -# pylint: disable = invalid-name, too-many-locals, arguments-differ +# pylint: disable=invalid-name, too-many-locals, arguments-differ, arguments-renamed import numpy as np import pandas as pd from scipy.optimize import minimize diff --git a/arbitragelab/copula_approach/mixed_copulas/ctg_mix_copula.py b/arbitragelab/copula_approach/mixed_copulas/ctg_mix_copula.py index 3b3c79d8..7c96e478 100644 --- a/arbitragelab/copula_approach/mixed_copulas/ctg_mix_copula.py +++ b/arbitragelab/copula_approach/mixed_copulas/ctg_mix_copula.py @@ -2,7 +2,7 @@ Module that implements Clayton, Student-t and Gumbel mixed copula. """ -# pylint: disable = invalid-name, too-many-locals, arguments-differ +# pylint: disable=invalid-name, too-many-locals, arguments-differ, arguments-renamed import numpy as np import pandas as pd from scipy.optimize import minimize diff --git a/arbitragelab/copula_approach/vine_copula_partner_selection.py b/arbitragelab/copula_approach/vine_copula_partner_selection.py index ba8cce6f..f7f3b193 100644 --- a/arbitragelab/copula_approach/vine_copula_partner_selection.py +++ b/arbitragelab/copula_approach/vine_copula_partner_selection.py @@ -2,7 +2,7 @@ Module for implementing partner selection approaches for vine copulas. """ -# pylint: disable = invalid-name +# pylint: disable = invalid-name, broad-exception-raised import functools import itertools import matplotlib.pyplot as plt diff --git a/arbitragelab/copula_approach/vinecop_generate.py b/arbitragelab/copula_approach/vinecop_generate.py index 0bb4930c..97a64c50 100644 --- a/arbitragelab/copula_approach/vinecop_generate.py +++ b/arbitragelab/copula_approach/vinecop_generate.py @@ -10,7 +10,7 @@ import numpy as np import pandas as pd import pyvinecopulib as pv -import scipy.integrate as integrate +from scipy import integrate class RVineCop: @@ -87,8 +87,8 @@ def fit_auto(self, data: pd.DataFrame, pv_target_idx: int = 1, if_renew: bool = # Fit among all possible structures. controls = pv.FitControlsVinecop(family_set=self._bicop_family) # Bivar copula constituents for the C-vine - aics = dict() # Dictionary for AIC values for all candidate C-vine copulas - cvine_cops = dict() # Dictionary for storing all candidate C-vine copulas + aics = {} # Dictionary for AIC values for all candidate C-vine copulas + cvine_cops = {} # Dictionary for storing all candidate C-vine copulas for cvine_structure in possible_cvine_structures: temp_cvine_struct = pv.CVineStructure(order=cvine_structure) # Specific C-vine structure temp_cvine_cop = pv.Vinecop(structure=temp_cvine_struct) # Construct the C-vine copula diff --git a/arbitragelab/distance_approach/basic_distance_approach.py b/arbitragelab/distance_approach/basic_distance_approach.py index 51d04351..21fba865 100644 --- a/arbitragelab/distance_approach/basic_distance_approach.py +++ b/arbitragelab/distance_approach/basic_distance_approach.py @@ -4,7 +4,7 @@ "Pairs trading: Performance of a relative-value arbitrage rule." (2006) https://papers.ssrn.com/sol3/papers.cfm?abstract_id=141615. """ - +# pylint: disable=broad-exception-raised) import numpy as np import pandas as pd import matplotlib.pyplot as plt diff --git a/arbitragelab/ml_approach/optics_dbscan_pairs_clustering.py b/arbitragelab/ml_approach/optics_dbscan_pairs_clustering.py index b7a87190..0f3d824c 100644 --- a/arbitragelab/ml_approach/optics_dbscan_pairs_clustering.py +++ b/arbitragelab/ml_approach/optics_dbscan_pairs_clustering.py @@ -2,6 +2,7 @@ This module implements the ML based Pairs Selection Framework described by Simão Moraes Sarmento and Nuno Horta in `"A Machine Learning based Pairs Trading Investment Strategy." `__. """ +# pylint: disable=broad-exception-raised) import itertools import numpy as np @@ -167,7 +168,7 @@ def plot_clustering_info(self, n_dimensions: int = 2, method: str = "", fig = plt.figure(facecolor='white', figsize=figsize) - tsne = TSNE(n_components=n_dimensions) + tsne = TSNE(n_components=n_dimensions, init='random') tsne_fv = pd.DataFrame(tsne.fit_transform(self.feature_vector), index=self.feature_vector.index) diff --git a/arbitragelab/optimal_mean_reversion/cir_model.py b/arbitragelab/optimal_mean_reversion/cir_model.py index a5b56928..e7caf149 100644 --- a/arbitragelab/optimal_mean_reversion/cir_model.py +++ b/arbitragelab/optimal_mean_reversion/cir_model.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-module-docstring, invalid-name, no-name-in-module +# pylint: disable=missing-module-docstring, invalid-name, no-name-in-module, unnecessary-lambda-assignment import warnings import numpy as np diff --git a/arbitragelab/optimal_mean_reversion/heat_potentials.py b/arbitragelab/optimal_mean_reversion/heat_potentials.py index 67900da0..c462d4ed 100644 --- a/arbitragelab/optimal_mean_reversion/heat_potentials.py +++ b/arbitragelab/optimal_mean_reversion/heat_potentials.py @@ -1,4 +1,5 @@ # pylint: disable=missing-module-docstring, invalid-name, too-many-locals +# pylint: disable=unnecessary-lambda-assignment, consider-using-generator from typing import Callable import numpy as np diff --git a/arbitragelab/optimal_mean_reversion/ou_model.py b/arbitragelab/optimal_mean_reversion/ou_model.py index b76c2714..f96515d2 100644 --- a/arbitragelab/optimal_mean_reversion/ou_model.py +++ b/arbitragelab/optimal_mean_reversion/ou_model.py @@ -1,4 +1,5 @@ # pylint: disable=missing-module-docstring, invalid-name, too-many-instance-attributes +# pylint: disable=unnecessary-lambda-assignment, broad-exception-raised import warnings from scipy.integrate import quad from scipy.optimize import root_scalar diff --git a/arbitragelab/optimal_mean_reversion/xou_model.py b/arbitragelab/optimal_mean_reversion/xou_model.py index df97647c..24756a1b 100644 --- a/arbitragelab/optimal_mean_reversion/xou_model.py +++ b/arbitragelab/optimal_mean_reversion/xou_model.py @@ -1,4 +1,4 @@ -# pylint: disable=missing-module-docstring, invalid-name +# pylint: disable=missing-module-docstring, invalid-name, unnecessary-lambda-assignment, broad-exception-raised import warnings from scipy.optimize import root_scalar, fsolve, minimize import numpy as np diff --git a/arbitragelab/spread_selection/base.py b/arbitragelab/spread_selection/base.py index 4bf14525..018243ac 100644 --- a/arbitragelab/spread_selection/base.py +++ b/arbitragelab/spread_selection/base.py @@ -1,7 +1,7 @@ """ Abstract pair selector class. """ - +# pylint: disable=consider-using-f-string from abc import ABC from abc import abstractmethod diff --git a/arbitragelab/spread_selection/cointegration.py b/arbitragelab/spread_selection/cointegration.py index a81ab65f..4e143c9e 100644 --- a/arbitragelab/spread_selection/cointegration.py +++ b/arbitragelab/spread_selection/cointegration.py @@ -3,7 +3,7 @@ Sarmento and Nuno Horta in `"A Machine Learning based Pairs Trading Investment Strategy." `__. """ -# pylint: disable=arguments-differ +# pylint: disable=arguments-differ, consider-using-f-string from functools import reduce import numpy as np diff --git a/arbitragelab/stochastic_control_approach/optimal_convergence.py b/arbitragelab/stochastic_control_approach/optimal_convergence.py index dcc91023..6b0bef70 100644 --- a/arbitragelab/stochastic_control_approach/optimal_convergence.py +++ b/arbitragelab/stochastic_control_approach/optimal_convergence.py @@ -3,7 +3,7 @@ `Liu, J. and Timmermann, A., 2013. Optimal convergence trade strategies. The Review of Financial Studies, 26(4), pp.1048-1086. `__ """ -# pylint: disable=invalid-name, too-many-instance-attributes +# pylint: disable=invalid-name, too-many-instance-attributes, broad-exception-raised import warnings import numpy as np diff --git a/arbitragelab/stochastic_control_approach/ou_model_jurek.py b/arbitragelab/stochastic_control_approach/ou_model_jurek.py index 9cf5b856..099e2284 100644 --- a/arbitragelab/stochastic_control_approach/ou_model_jurek.py +++ b/arbitragelab/stochastic_control_approach/ou_model_jurek.py @@ -6,7 +6,7 @@ `Jurek, J.W. and Yang, H., 2007, April. Dynamic portfolio selection in arbitrage. In EFA 2006 Meetings Paper. `__ """ -# pylint: disable=invalid-name, too-many-instance-attributes, too-many-locals +# pylint: disable=invalid-name, too-many-instance-attributes, too-many-locals, broad-exception-raised import warnings import cvxpy as cp diff --git a/arbitragelab/stochastic_control_approach/ou_model_mudchanatongsuk.py b/arbitragelab/stochastic_control_approach/ou_model_mudchanatongsuk.py index 3c4814d4..a46a7257 100644 --- a/arbitragelab/stochastic_control_approach/ou_model_mudchanatongsuk.py +++ b/arbitragelab/stochastic_control_approach/ou_model_mudchanatongsuk.py @@ -6,7 +6,7 @@ `Mudchanatongsuk, S., Primbs, J.A. and Wong, W., 2008, June. Optimal pairs trading: A stochastic control approach. `__ """ -# pylint: disable=invalid-name, too-many-instance-attributes +# pylint: disable=invalid-name, too-many-instance-attributes, broad-exception-raised import math import numpy as np diff --git a/arbitragelab/tearsheet/tearsheet.py b/arbitragelab/tearsheet/tearsheet.py index 9e6bff3f..fc6d051f 100644 --- a/arbitragelab/tearsheet/tearsheet.py +++ b/arbitragelab/tearsheet/tearsheet.py @@ -1,11 +1,10 @@ """ This module implements interactive Tear Sheets for various modules of the ArbitrageLab package. """ -# pylint: disable=too-many-lines, too-many-locals, invalid-name, unused-argument -# pylint: disable=too-many-arguments, too-many-statements, unused-variable, broad-except +# pylint: disable=too-many-lines, too-many-locals, invalid-name, unused-argument, use-dict-literal, broad-except +# pylint: disable=too-many-arguments, too-many-statements, unused-variable, consider-using-f-string import warnings - import pandas as pd import numpy as np from scipy import stats @@ -18,7 +17,6 @@ from dash import dash_table import plotly.graph_objects as go from jupyter_dash import JupyterDash - from arbitragelab.optimal_mean_reversion import OrnsteinUhlenbeck from arbitragelab.cointegration_approach import (get_half_life_of_mean_reversion, EngleGrangerPortfolio, JohansenPortfolio) diff --git a/arbitragelab/time_series_approach/arima_predict.py b/arbitragelab/time_series_approach/arima_predict.py index bbd0a3db..38f9be88 100644 --- a/arbitragelab/time_series_approach/arima_predict.py +++ b/arbitragelab/time_series_approach/arima_predict.py @@ -1,6 +1,7 @@ """ The module implements the ARIMA forecast of any time series using the Auto-ARIMA approach. """ +# pylint: disable=consider-using-f-string, broad-exception-raised import warnings import sys diff --git a/arbitragelab/time_series_approach/h_strategy.py b/arbitragelab/time_series_approach/h_strategy.py index 1fa7709a..f67f830f 100644 --- a/arbitragelab/time_series_approach/h_strategy.py +++ b/arbitragelab/time_series_approach/h_strategy.py @@ -3,7 +3,7 @@ `Bogomolov, T. (2013). Pairs trading based on statistical variability of the spread process. Quantitative Finance, 13(9): 1411–1430. `_ """ -# pylint: disable=invalid-name +# pylint: disable=invalid-name, broad-exception-raised from itertools import compress, combinations diff --git a/arbitragelab/time_series_approach/ou_optimal_threshold.py b/arbitragelab/time_series_approach/ou_optimal_threshold.py index 663cda6a..16051e12 100644 --- a/arbitragelab/time_series_approach/ou_optimal_threshold.py +++ b/arbitragelab/time_series_approach/ou_optimal_threshold.py @@ -1,7 +1,7 @@ """ The module implements the base class for OU Optimal Threshold Model. """ -# pylint: disable=invalid-name +# pylint: disable=invalid-name, broad-exception-raised, unnecessary-lambda-assignment from typing import Union diff --git a/arbitragelab/time_series_approach/ou_optimal_threshold_bertram.py b/arbitragelab/time_series_approach/ou_optimal_threshold_bertram.py index 82e006b3..441678f0 100644 --- a/arbitragelab/time_series_approach/ou_optimal_threshold_bertram.py +++ b/arbitragelab/time_series_approach/ou_optimal_threshold_bertram.py @@ -1,7 +1,7 @@ """ The module implements the Bertram class for OU Optimal Threshold Model. """ -# pylint: disable=invalid-name +# pylint: disable=invalid-name, unnecessary-lambda-assignment, broad-exception-raised, consider-iterating-dictionary import numpy as np from scipy import optimize, special @@ -19,13 +19,6 @@ class OUModelOptimalThresholdBertram(OUModelOptimalThreshold): `_ """ - def __init__(self): - """ - Initializes the module parameters. - """ - - super().__init__() - def expected_trade_length(self, a: float, m: float) -> float: """ Calculates equation (9) in the paper to get the expected trade length. diff --git a/arbitragelab/time_series_approach/ou_optimal_threshold_zeng.py b/arbitragelab/time_series_approach/ou_optimal_threshold_zeng.py index e0d5fbea..0e8f30a3 100644 --- a/arbitragelab/time_series_approach/ou_optimal_threshold_zeng.py +++ b/arbitragelab/time_series_approach/ou_optimal_threshold_zeng.py @@ -1,7 +1,7 @@ """ The module implements the Zeng class for OU Optimal Threshold Model. """ -# pylint: disable=invalid-name +# pylint: disable=invalid-name, unnecessary-lambda-assignment, broad-exception-raised, consider-iterating-dictionary import numpy as np from scipy import optimize @@ -20,13 +20,6 @@ class OUModelOptimalThresholdZeng(OUModelOptimalThreshold): `_. """ - def __init__(self): - """ - Initializes the module parameters. - """ - - super().__init__() - def expected_trade_length(self, a: float, b: float) -> float: """ Calculates the expected trade length. diff --git a/arbitragelab/time_series_approach/quantile_time_series.py b/arbitragelab/time_series_approach/quantile_time_series.py index d4c1ed59..4bbe6551 100644 --- a/arbitragelab/time_series_approach/quantile_time_series.py +++ b/arbitragelab/time_series_approach/quantile_time_series.py @@ -3,6 +3,7 @@ `"A Machine Learning based Pairs Trading Investment Strategy" `__ (pages 37-43) by Simão Moraes Sarmento and Nuno Horta. """ +# pylint: disable=consider-using-f-string import pandas as pd import seaborn as sns diff --git a/arbitragelab/time_series_approach/regime_switching_arbitrage_rule.py b/arbitragelab/time_series_approach/regime_switching_arbitrage_rule.py index 60299dff..eec2027c 100644 --- a/arbitragelab/time_series_approach/regime_switching_arbitrage_rule.py +++ b/arbitragelab/time_series_approach/regime_switching_arbitrage_rule.py @@ -1,7 +1,7 @@ """ The module implements a statistical arbitrage strategy based on the Markov regime-switching model. """ -# pylint: disable=invalid-name +# pylint: disable=invalid-name, unnecessary-lambda-assignment, broad-exception-raised from typing import Union, Callable import warnings @@ -128,7 +128,8 @@ def get_signal(self, data: Union[np.array, pd.Series, pd.DataFrame], switching_v res = mod.fit() except (RuntimeError, LinAlgError): warnings.warn("Unable to get a fit") - return np.full(4, False) # Since we were unable to detect the regime, we just return False for every possible strategy. + return np.full(4, + False) # Since we were unable to detect the regime, we just return False for every possible strategy. # Unpacking parameters mu = res.params[2:4] @@ -230,7 +231,7 @@ def get_trades(self, signals: np.array) -> np.array: if not (long_exit[i] or short_exit[i]): positions[i] = positions[i - 1] else: - positions[i] = 0 + positions[i] = 0 # pragma: no cover return np.column_stack((long_entry, long_exit, short_entry, short_exit)) diff --git a/arbitragelab/util/data_importer.py b/arbitragelab/util/data_importer.py index 2c5dba94..ca7524e4 100644 --- a/arbitragelab/util/data_importer.py +++ b/arbitragelab/util/data_importer.py @@ -116,7 +116,7 @@ def get_ticker_sector_info(self, tickers: list, yf_call_chunk: int = 20) -> pd.D # Set end as the limit value equals to the chunk size. # If we hit the last chunk, set the end value as the # full length of the ticker list. - end = i+yf_call_chunk if i <= len(tickers) else len(tickers) + end = i + yf_call_chunk if i <= len(tickers) else len(tickers) ticker_sector_queue.append(self._sector_info_helper(tickers[i: end])) @@ -132,13 +132,16 @@ def _sector_info_helper(tickers: list) -> pd.DataFrame: and industry information. """ - tckrs = yf.Tickers(' '.join(tickers)) + tickers_obj = yf.Tickers(' '.join(tickers)) tckr_info = [] + for name in tickers: - for tckr in tickers: - ticker_info = tckrs.tickers[tckr].info - tckr_tuple = (tckr, ticker_info['industry'], ticker_info['sector']) + sector = tickers_obj.tickers[name].info.get('sector', 'NA') + industry = tickers_obj.tickers[name].info.get('industry', 'NA') + + # Append to list storage + tckr_tuple = (name, industry, sector) tckr_info.append(tckr_tuple) return pd.DataFrame(data=tckr_info, columns=['ticker', 'industry', 'sector']) diff --git a/coverage b/coverage index 0e89836f..635fd13e 100755 --- a/coverage +++ b/coverage @@ -14,7 +14,7 @@ coverage run --concurrency=multiprocessing -m pytest tests/ res_test=$? if [ $res_test -ne 0 ] then - echo -e "Circle CI Build FAILURE: Unit tests failed" + echo -e "Build FAILURE: Unit tests failed" exit 1 fi @@ -24,7 +24,7 @@ coverage combine res_combine=$? if [ $res_combine -ne 0 ] then - echo -e "Circle CI Build FAILURE: Coverage combine failed" + echo -e "Build FAILURE: Coverage combine failed" exit 1 fi @@ -33,6 +33,6 @@ coverage report --fail-under=100 coverage_report=$? if [ $coverage_report -ne 0 ] then - echo -e "Circle CI Build FAILURE: Coverage percentage failed" + echo -e "Build FAILURE: Coverage percentage failed" exit 1 fi diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 166cb4ba..cf7a06ca 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -9,6 +9,10 @@ Changelog * :feature:`50` Add a distutils command for marbles * :bug:`58` Fixed test failure on OSX + +* :release:`1.0.0 <2024-05-10>` +* :support:`93` Add poetry package manager for dependency management. + * :release:`0.9.1 <2024-01-10>` * :bug:`92` Released a new version because of pypi version was wrong diff --git a/docs/source/conf.py b/docs/source/conf.py index 07d537a0..ed8e39be 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -21,7 +21,7 @@ author = 'Hudson & Thames Quantitative Research' # The full version, including alpha/beta/rc tags -release = '0.9.1' +release = "1.0.0" # -- General configuration --------------------------------------------------- @@ -82,7 +82,11 @@ # # html_theme_options = {} -html_context = {'logo': 'logo_white.png', 'theme_logo_only': True} +html_logo = '_static/logo_white.png' +html_theme_options = { + 'logo_only': True, + 'display_version': True, +} html_favicon = '_static/favicon_arbitragelab.png' # Add any paths that contain custom static files (such as style sheets) here, diff --git a/docs/source/index.rst b/docs/source/index.rst index 22cbb476..b8733046 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,6 +3,7 @@ :target: https://hudsonthames.org/ | + =============================================== Welcome to the Statistical Arbitrage Laboratory =============================================== @@ -117,6 +118,7 @@ Including publications from: :target: https://hudsonthames.org/ | + Who is Hudson & Thames? +++++++++++++++++++++++ diff --git a/docs/source/requirements.txt b/docs/source/requirements.txt deleted file mode 100644 index 1aea1fb9..00000000 --- a/docs/source/requirements.txt +++ /dev/null @@ -1,45 +0,0 @@ -# Production -POT==0.9.0 -analytics-python>=1.2.7, <2.0.0 -arch==5.5.0 -cvxpy==1.3.1 -cython==0.29.28 -dash==2.10.2 -getmac>=0.8.0, <1.0.0 -jupyter-dash>=0.2.0, <1.0.0 -keras==2.12.0 -lxml>=4.9.1 -matplotlib==3.7.1 -mpmath==1.2.1 -networkx>=2.2, <2.6 -numpy==1.23.5 -pandas==2.0.0 -pmdarima==2.0.3 -protobuf>=3.20.3 -pyvinecopulib==0.5.5 -requests_html==0.10.0 -scikit-learn==1.1.3 -scipy>=1.2.0, <2.0.0 -scs==3.2.0 -seaborn==0.12.2 -statsmodels==0.14.0 -tensorflow-macos==2.12.0; sys_platform == 'darwin' and platform_machine == 'arm64' -tensorflow==2.12.0; sys_platform != 'darwin' or platform_machine != 'arm64' -werkzeug==2.2.3 -yahoo-fin==0.8.9.1 -yfinance==0.2.24 - -# Develop -coverage==7.2.7 -docutils==0.18.1 # Docs -hudsonthames-sphinx-theme==0.1.5 # Docs -jinja2<3.1 # Docs -pyarmor==8.2.5 # Encryption -pylint==2.17.4 -pytest==7.3.1 -releases==1.6.3 # Docs -sphinx-copybutton==0.5.2 # docs -sphinx-rtd-theme==1.2.2 # docs -sphinx-autoapi==2.1.1 -sphinx-tabs==3.4.1 -myst-parser==2.0.0 # Docs diff --git a/docs/source/visualization/tearsheet.rst b/docs/source/visualization/tearsheet.rst index f643b390..c51abe80 100644 --- a/docs/source/visualization/tearsheet.rst +++ b/docs/source/visualization/tearsheet.rst @@ -88,11 +88,14 @@ Implementation ************** .. automodule:: arbitragelab.tearsheet.tearsheet + :noindex: .. autoclass:: TearSheet + :noindex: :members: __init__ .. automethod:: TearSheet.cointegration_tearsheet + :noindex: Code Example ************ @@ -172,6 +175,7 @@ Implementation ************** .. automethod:: TearSheet.ou_tearsheet + :noindex: Code Example ************ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..93263f18 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,133 @@ +[tool.poetry] +name = "arbitragelab" +version = "1.0.0" +description = "ArbitrageLab is a collection of algorithms from the best academic journals and graduate-level textbooks, which focuses on the branch of statistical arbitrage known as pairs trading. We have extended the implementations to include the latest methods that trade a portfolio of n-assets (mean-reverting portfolios)." +authors = ["Hudson and Thames Quantitative Research "] +license = "BSD-3-Clause" +readme = "README.md" +homepage = "https://www.hudsonthames.org/" +repository = "https://github.com/hudson-and-thames/arbitragelab" +documentation = "https://hudson-and-thames-arbitragelab.readthedocs-hosted.com/en/latest/index.html" +keywords = ["arbitrage", "finance", "investment", "education", "trading"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + "Intended Audience :: Financial and Insurance Industry", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3.8", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Topic :: Office/Business :: Financial :: Investment" +] + +packages = [ + { include = "arbitragelab" } +] + +exclude = ["docs", "tests"] + +[tool.poetry.dependencies] +python = "^3.8" +POT = "0.9.0" +arch = "5.5.0" +werkzeug = "2.2.3" +yahoo-fin = "0.8.9.1" +yfinance = "0.2.37" +cython = "0.29.28" +dash = "2.10.2" +mpmath = "1.2.1" +pandas = "2.0.0" +pmdarima = "2.0.4" +pyvinecopulib = "0.6.5" +requests_html = "0.10.0" +pyzmq = "26.0.0" +seaborn = "0.12.2" + +statsmodels = {version = "0.14.0" } +lxml = { version = "^4.9.1" } +protobuf = { version = ">=3.20.3" } +networkx = { version = ">=2.2,<2.6" } +jupyter-dash = { version = ">=0.2.0,<1.0.0" } + +numpy = [ + { version = "1.23.5", python = ">=3.8,<3.12" }, + { version = "1.26.4", python = ">=3.12,<4" }, +] + +matplotlib = [ + { version = "3.7.1", python = ">=3.8,<3.12" }, + { version = "3.8.4", python = ">=3.12,<4" }, +] + +scikit-learn = [ + { version = "1.1.3", python = ">=3.8,<3.12" }, + { version = "1.3.0", python = ">=3.12,<4" }, +] + +scipy = [ + { version = "1.10.1", python = ">=3.8,<3.9" }, + { version = "1.11.0", python = ">=3.9,<3.10" }, + { version = "1.12.0", python = ">=3.10,<4" }, +] + +tensorflow-macos = [ + { version = "2.13.0", markers = "sys_platform == 'darwin' and platform_machine == 'arm64'", python = ">=3.8, <3.12" }, + { version = "2.16.1", markers = "sys_platform == 'darwin' and platform_machine == 'x86_64'", python = ">=3.12, <4" } +] + +tensorflow = [ + { version = "2.13.0", markers = "sys_platform != 'darwin' or platform_machine != 'arm64'", python = ">=3.8, <3.12" }, + { version = "2.16.1", markers = "sys_platform != 'darwin' or platform_machine != 'x86_64'", python = ">=3.12, <4" } +] + +tensorflow-io-gcs-filesystem = { version = "0.34.0", markers = "sys_platform != 'darwin' or platform_machine != 'arm64'", python = ">=3.8, <3.12" } + +keras = [ + { version = "2.13.1", python = ">=3.8,<3.12" }, + { version = "3.0.0", python = ">=3.12,<4" }, +] + +wrapt = { version = "1.14.0" , python = ">=3.8,<3.12" } + +cvxpy = [ + { version = "1.4.3", markers = "sys_platform != 'darwin' or platform_machine != 'arm64'", python = ">=3.8,<3.10"}, + { version = "1.4.3", python = ">=3.10,<4"}, +] # install manually with command conda install -c conda-forge cvxpy, + # otherwise causes problems whit some of its dependecy 'ecos' and 'scs -> + #refer to this link: https://apple.stackexchange.com/questions/254380/why-am-i-getting-an-invalid-active-developer-path-when-attempting-to-use-git-a + +[tool.poetry.urls] +"Bug Reports" = "https://github.com/hudson-and-thames/arbitragelab/issues" +"Blog" = "https://hudsonthames.org/blog/" +"Apprenticeship Program" = "https://hudsonthames.org/apprenticeship-program/" + +[build-system] +requires = ["poetry-core>=1.0"] +build-backend = "poetry.core.masonry.api" + + +[tool.poetry.group.tests.dependencies] +coverage = "7.2.7" +pylint = "3.1.0" +pytest = "7.3.1" +pytest-cov = "3.0.0" + +[tool.poetry.group.docs.dependencies] +releases = "1.6.3" +jinja2 = "<3.1" +docutils = "0.18.1" +hudsonthames-sphinx-theme = "0.1.5" +myst-parser = "2.0.0" #3.0.0 +sphinx-rtd-theme = "2.0.0" #2.0.0 +sphinx-tabs = "3.4.1" #3.4.5 +sphinx = "6.2.1" +sphinx-autoapi = "3.0.0" +sphinx-copybutton = "0.5.2" +six = "*" + +[tool.poetry.extras] +tests = ["coverage", "pylint", "pytest", "pytest-cov"] +docs = ["sphinx", "sphinx-rtd-theme", "sphinx-tabs", "sphinx-autoapi", "sphinx-copybutton", "myst-parser", "hudsonthames-sphinx-theme", "docutils", "jinja2", "releases"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index e5a1b229..00000000 --- a/requirements.txt +++ /dev/null @@ -1,39 +0,0 @@ -# Production -POT==0.9.0 -arch==5.5.0 -cvxpy==1.3.1 -cython==0.29.28 -dash==2.10.2 -jupyter-dash>=0.2.0, <1.0.0 -keras==2.12.0 -lxml>=4.9.1 -matplotlib==3.7.1 -mpmath==1.2.1 -networkx>=2.2, <2.6 -numpy==1.23.5 -pandas==2.0.0 -pmdarima==2.0.3 -protobuf>=3.20.3 -pyvinecopulib==0.5.5 -requests_html==0.10.0 -scikit-learn==1.1.3 -scipy>=1.2.0, <2.0.0 -scs==3.2.0 -seaborn==0.12.2 -statsmodels==0.14.0 -tensorflow-macos==2.12.0; sys_platform == 'darwin' and platform_machine == 'arm64' -tensorflow==2.12.0; sys_platform != 'darwin' or platform_machine != 'arm64' -werkzeug==2.2.3 -yahoo-fin==0.8.9.1 -yfinance==0.2.24 - -# Develop -coverage==7.2.7 -docutils==0.16 # Docs -hudsonthames-sphinx-theme==0.1.5 # Docs -jinja2<3.1 # Docs -pylint==2.6.0 -pytest==7.3.1 -releases==1.6.3 # Docs -sphinx-rtd-theme==0.5.2 # Docs -sphinx==3.4.3 # Docs diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index ef34907e..00000000 --- a/setup.cfg +++ /dev/null @@ -1,80 +0,0 @@ -[metadata] -name = arbitragelab -version = 0.9.1 -author = Hudson and Thames Quantitative Research -author_email = opensource@hudsonthames.org -licence = BSD 3-Clause License -licence-file = LICENSE.txt -description = ArbitrageLab is a collection of algorithms from the best academic journals and graduate-level textbooks, which focuses on the branch of statistical arbitrage known as pairs trading. We have extended the implementations to include the latest methods that trade a portfolio of n-assets (mean-reverting portfolios). -long_description = file: README.md -long_description_content_type = text/markdown -platform = any -url = https://www.hudsonthames.org/ -project_urls = - Documentation = https://hudson-and-thames-arbitragelab.readthedocs-hosted.com/en/latest/index.html - Bug Reports = https://github.com/hudson-and-thames/arbitragelab/issues - Source = https://github.com/hudson-and-thames/arbitragelab - Blog = https://hudsonthames.org/blog/ - Apprenticeship Program = https://hudsonthames.org/apprenticeship-program/ -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Developers - Intended Audience :: Education - Intended Audience :: Science/Research - Intended Audience :: Financial and Insurance Industry - License :: OSI Approved :: BSD License - Operating System :: OS Independent - Programming Language :: Python :: 3.8 - Topic :: Scientific/Engineering - Topic :: Scientific/Engineering :: Artificial Intelligence - Topic :: Office/Business :: Financial :: Investment -keywords = - arbitrage - finance - investment - education - trading - -[options] -include_package_data = True -packages = find: -python_requires = - ~=3.8 -setup_requires = - setuptools -install_requires = - POT==0.9.1 - arch==5.5.0 - cvxpy==1.3.1 - cython==0.29.28 - dash==2.10.2 - jupyter-dash>=0.2.0, <1.0.0 - keras==2.12.0 - lxml>=4.9.1 - matplotlib==3.7.1 - mpmath==1.2.1 - networkx>=2.2, <2.6 - numpy==1.23.5 - pandas==2.0.0 - pmdarima==2.0.3 - protobuf>=3.20.3 - pyvinecopulib==0.5.5 - requests_html==0.10.0 - scikit-learn==1.1.3 - scipy>=1.2.0, <2.0.0 - scs==3.2.0 - seaborn==0.12.2 - statsmodels==0.14.0 - tensorflow-macos==2.12.0; sys_platform == 'darwin' and platform_machine == 'arm64' - tensorflow==2.12.0; sys_platform != 'darwin' or platform_machine != 'arm64' - werkzeug==2.2.3 - yahoo-fin==0.8.9.1 - yfinance==0.2.24 - -[options.packages.find] -package_dir = - arbitragelab -exclude = - contrib - docs - tests diff --git a/setup.py b/setup.py deleted file mode 100644 index 41c16ce9..00000000 --- a/setup.py +++ /dev/null @@ -1,21 +0,0 @@ -# Always prefer setuptools over distutils -from setuptools import setup - -setup() - -# ---------------------------------------------------------------------------------- - -# Pull new commits -# Bump version -# Update Changelog release -# Update version in docs cfg and library setup.cfg -# Make sure you double check pushing all changes to git: git push - -# Tagging -# git tag [1.4.0] -# git push origin [1.4.0] -# On Github, go to tags and use the GUI to push a Release. - -# Create package -# python setup.py bdist_wheel -# twine upload dist/* (This is official repo) diff --git a/tests/test_copula_pairs_selection.py b/tests/test_copula_pairs_selection.py index 5b1e3795..9bddf4ff 100644 --- a/tests/test_copula_pairs_selection.py +++ b/tests/test_copula_pairs_selection.py @@ -8,7 +8,7 @@ import numpy as np import pandas as pd -import arbitragelab.copula_approach.pairs_selection as pairs_selection +from arbitragelab.copula_approach import pairs_selection class TestPairsSelector(unittest.TestCase): diff --git a/tests/test_copulas.py b/tests/test_copulas.py index 877892d6..a6e28bda 100644 --- a/tests/test_copulas.py +++ b/tests/test_copulas.py @@ -1,7 +1,8 @@ """ Unit tests for copula functions. """ -# pylint: disable = invalid-name, protected-access, too-many-locals, unexpected-keyword-arg, too-many-public-methods +# pylint: disable=invalid-name, protected-access, too-many-locals, unexpected-keyword-arg +# pylint: disable=too-many-public-methods, unnecessary-lambda-assignment import os import unittest @@ -698,7 +699,7 @@ def test_fit_copula(self): # Fit through the copulas and the last one we do not update copulas = [Gumbel, Clayton, Frank, Joe, N13, N14, GaussianCopula, StudentCopula] - aics = dict() + aics = {} for cop in copulas: result_dict, _, _, _ = fit_copula_to_empirical_data(x=BKD_clr, y=ESC_clr, copula=cop) @@ -709,8 +710,8 @@ def test_fit_copula(self): 'N13': -2211.6295423299603, 'N14': -2111.9831835080827, 'Gaussian': -2211.4486204860873, 'Student': -2275.069087841567} - for key in aics: - self.assertAlmostEqual(aics[key], expeced_aics[key], delta=1) + for key, aic_item in aics.items(): + self.assertAlmostEqual(aic_item, expeced_aics[key], delta=1) @staticmethod def test_construct_ecdf_lin(): @@ -823,7 +824,7 @@ def test_plot_abs_class_method(self): student = StudentCopula(cov=cov, nu=nu) # Initiate without an axes - axs = dict() + axs = {} axs['Gumbel'] = gumbel.plot_scatter(200) axs['Frank'] = frank.plot_scatter(200) axs['Clayton'] = clayton.plot_scatter(200) diff --git a/tests/test_data_importer.py b/tests/test_data_importer.py index 4843b6b0..5bd383b8 100644 --- a/tests/test_data_importer.py +++ b/tests/test_data_importer.py @@ -9,6 +9,7 @@ import numpy as np from arbitragelab.util import DataImporter + class TestDataImporter(unittest.TestCase): """ Tests Data Importer class. diff --git a/tests/test_neural_networks.py b/tests/test_neural_networks.py index b5b7c8bd..0ec49261 100644 --- a/tests/test_neural_networks.py +++ b/tests/test_neural_networks.py @@ -1,19 +1,18 @@ """ Tests Spread Modeling Neural Network Classes. """ +# pylint: disable=unbalanced-tuple-unpacking, no-name-in-module import unittest import numpy as np import tensorflow as tf -from keras.engine.training import Model +from keras.models import Model from keras.callbacks import History from sklearn.model_selection import train_test_split from sklearn.datasets import make_regression - +from tensorflow.python.keras import backend from arbitragelab.ml_approach.neural_networks import MultiLayerPerceptron, RecurrentNeuralNetwork, PiSigmaNeuralNetwork -# pylint: disable=unbalanced-tuple-unpacking - class TestNeuralNetworks(unittest.TestCase): """ Test Neural Network Implementations. @@ -28,10 +27,9 @@ def setUp(self): seed_value = 0 np.random.seed(seed_value) tf.random.set_seed(seed_value) - session_conf = tf.compat.v1.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) sess = tf.compat.v1.Session(graph=tf.compat.v1.get_default_graph(), config=session_conf) - tf.compat.v1.keras.backend.set_session(sess) + backend.set_session(sess) self.seed_value = seed_value diff --git a/tests/test_regime_switching_arbitrage_rule.py b/tests/test_regime_switching_arbitrage_rule.py index 337af4e5..77495178 100644 --- a/tests/test_regime_switching_arbitrage_rule.py +++ b/tests/test_regime_switching_arbitrage_rule.py @@ -2,6 +2,7 @@ Tests functions from Regime Switching Arbitrage Rule module. """ # pylint: disable=invalid-name +# pylint: disable=unnecessary-lambda-assignment import unittest import os @@ -26,8 +27,7 @@ def setUp(self): self.path = project_path + '/test_data/CL=F_NG=F_data.csv' data = pd.read_csv(self.path) data = data.set_index('Date') - Ratt = data["NG=F"]/data["CL=F"] - + Ratt = data["NG=F"] / data["CL=F"] self.Ratts = Ratt.values, Ratt, pd.DataFrame(Ratt) @@ -37,7 +37,7 @@ def test_signal(self): """ # Creating an object of class - test = RegimeSwitchingArbitrageRule(delta = 1.5, rho = 0.6) + test = RegimeSwitchingArbitrageRule(delta=1.5, rho=0.6) # Setting window size window_size = 60 @@ -97,9 +97,9 @@ def test_change(self): window_size = 60 # Changing rules in the high regime - ol_rule = lambda Xt, mu, delta, sigma: Xt <= mu - delta*sigma + ol_rule = lambda Xt, mu, delta, sigma: Xt <= mu - delta * sigma cl_rule = lambda Xt, mu, delta, sigma: Xt >= mu - os_rule = lambda Xt, mu, delta, sigma: Xt >= mu + delta*sigma + os_rule = lambda Xt, mu, delta, sigma: Xt >= mu + delta * sigma cs_rule = lambda Xt, mu, delta, sigma: Xt <= mu test.change_strategy("High", "Long", "Open", ol_rule) @@ -114,9 +114,9 @@ def test_change(self): self.assertEqual(test.strategy["High"]["Short"]["Close"], cs_rule) # Changing rules in the low regime - ol_rule = lambda Xt, mu, delta, sigma, prob: Xt <= mu - delta*sigma and prob >= 0.7 + ol_rule = lambda Xt, mu, delta, sigma, prob: Xt <= mu - delta * sigma and prob >= 0.7 cl_rule = lambda Xt, mu, delta, sigma: Xt >= mu - os_rule = lambda Xt, mu, delta, sigma, prob, rho: Xt >= mu + delta*sigma and prob >= rho + os_rule = lambda Xt, mu, delta, sigma, prob, rho: Xt >= mu + delta * sigma and prob >= rho cs_rule = lambda Xt, mu, delta, sigma: Xt <= mu test.change_strategy("Low", "Long", "Open", ol_rule) @@ -144,5 +144,5 @@ def test_change(self): # Testing the exception with self.assertRaises(Exception): - ol_rule = lambda Xt, mu, delta, sigma, error: Xt <= mu - delta*sigma*error + ol_rule = lambda Xt, mu, delta, sigma, error: Xt <= mu - delta * sigma * error test.change_strategy("High", "Long", "Open", ol_rule) diff --git a/tests/test_spread_modeling_helper.py b/tests/test_spread_modeling_helper.py index 5a741fa7..3b7a5109 100644 --- a/tests/test_spread_modeling_helper.py +++ b/tests/test_spread_modeling_helper.py @@ -1,12 +1,14 @@ """ Tests Spread Modeling Helper Class. """ +# pylint: disable=no-name-in-module + import os import unittest import numpy as np import pandas as pd import tensorflow as tf - +from tensorflow.python.keras import backend from arbitragelab.cointegration_approach.johansen import JohansenPortfolio from arbitragelab.ml_approach.regressor_committee import RegressorCommittee from arbitragelab.util.spread_modeling_helper import SpreadModelingHelper @@ -28,7 +30,7 @@ def setUp(self): session_conf = tf.compat.v1.ConfigProto(intra_op_parallelism_threads=1, inter_op_parallelism_threads=1) sess = tf.compat.v1.Session(graph=tf.compat.v1.get_default_graph(), config=session_conf) - tf.compat.v1.keras.backend.set_session(sess) + backend.set_session(sess) # Collect all contract price data. project_path = os.path.dirname(__file__) diff --git a/tests/test_trading_copula_strategy_mpi.py b/tests/test_trading_copula_strategy_mpi.py index fa0d9bf4..cb00e2f7 100644 --- a/tests/test_trading_copula_strategy_mpi.py +++ b/tests/test_trading_copula_strategy_mpi.py @@ -2,7 +2,7 @@ Unit tests for copula strategy using mispricing index (MPI). """ -# pylint: disable = invalid-name, protected-access +# pylint: disable=invalid-name, protected-access import os import unittest @@ -324,9 +324,9 @@ def test_get_position_and_reset_flag_or_and(): (-1, False, [-1, 2]), (-1, False, [-1, 1]), (1, False, [1, 2]), (1, False, [1, 1]), (-1, False, [-1, 1]), (-1, False, [-1, 1])] # Check matching with expecteation - np.testing.assert_array_equal(results_1, expected) + np.testing.assert_array_equal(np.array(results_1, dtype=object), np.array(expected, dtype=object)) # Check results are indep of open_based_on conditions - np.testing.assert_array_equal(results_1, results_2) + np.testing.assert_array_equal(np.array(results_1, dtype=object), np.array(results_2, dtype=object)) def test_cur_flag_and_position_or_and(self): """ @@ -486,9 +486,9 @@ def test_get_position_and_reset_flag_and_or(): (1, False, [-1, 2]), (1, False, [-1, 1]), (-1, False, [1, 2]), (-1, False, [1, 1]), (0, True, [0, 0]), (0, True, [0, 0]), (-1, False, [-1, 2])] # Check matching with expecteation - np.testing.assert_array_equal(results_1, expected) + np.testing.assert_array_equal(np.array(results_1, dtype=object), np.array(expected, dtype=object)) # Check results are indep of open_based_on conditions - np.testing.assert_array_equal(results_1, results_2) + np.testing.assert_array_equal(np.array(results_1, dtype=object), np.array(results_2, dtype=object)) def test_cur_flag_and_position_and_or(self): """ @@ -651,9 +651,9 @@ def test_get_position_and_reset_flag_and_and(): (1, False, [-1, 2]), (1, False, [-1, 1]), (-1, False, [1, 2]), (-1, False, [1, 1]), (-1, False, [-1, 1]), (-1, False, [-1, 1]), (-1, False, [-1, 2])] # Check matching with expecteation - np.testing.assert_array_equal(results_1, expected) + np.testing.assert_array_equal(np.array(results_1, dtype=object), np.array(expected, dtype=object)) # Check results are indep of open_based_on conditions - np.testing.assert_array_equal(results_1, results_2) + np.testing.assert_array_equal(np.array(results_1, dtype=object), np.array(results_2, dtype=object)) def test_cur_flag_and_position_and_and(self): """