From bfea751259c4709e740041b2e0fb3163affe5eee Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Fri, 10 May 2024 16:41:52 -0400 Subject: [PATCH 01/19] gha update --- .github/workflows/build_wheels.yml | 9 ++++++--- .github/workflows/test_wheels.yml | 23 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 69e1fc2..860baa0 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -16,11 +16,14 @@ jobs: fail-fast: false matrix: os: [ ubuntu-latest ] - python-version: [ 3.8 ] + python-version: [ "3.10" ] steps: - name: Checkout empyrical uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: '0.5.10' - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 @@ -31,7 +34,7 @@ jobs: run: pipx run build - name: Store artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: path: dist/* @@ -43,7 +46,7 @@ jobs: runs-on: ubuntu-latest if: startsWith(github.ref, 'refs/tags') steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v4 with: name: artifact path: dist diff --git a/.github/workflows/test_wheels.yml b/.github/workflows/test_wheels.yml index 811da08..e8c339c 100644 --- a/.github/workflows/test_wheels.yml +++ b/.github/workflows/test_wheels.yml @@ -19,8 +19,21 @@ jobs: - name: Checkout empyrical uses: actions/checkout@v4 - - name: Install wheel & run tests - run: | - pip install -U pip wheel tox-gh-actions - pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple your-package empyrical-reloaded[test] - tox -p auto -q +# - name: Install wheel & run tests +# run: | +# pip install -U pip wheel tox-gh-actions +# pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple empyrical-reloaded[test] +# tox -p auto -q + + - name: Unittests with tox & pytest + uses: nick-fields/retry@v3 + with: + timeout_minutes: 90 + max_attempts: 3 + retry_on: error + new_command_on_retry: | + python -m pip install -U pip wheel tox tox-gh-actions + python -m pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple empyrical-reloaded[test] + tox -p auto -q + + command: tox From ce55f800e6418520619b7ab69fb46d54d86cf10e Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Fri, 10 May 2024 16:46:13 -0400 Subject: [PATCH 02/19] fix pypi condition dtype --- .github/workflows/build_wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 860baa0..540c84f 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -7,7 +7,7 @@ on: description: 'Publish to PyPI?' required: true type: boolean - default: 'false' + default: false jobs: dist: From 0fb4a06a955687c3fdb4e0d9f3a2e31f348346cb Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Fri, 10 May 2024 16:50:46 -0400 Subject: [PATCH 03/19] fix input syntax --- .github/workflows/build_wheels.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 540c84f..a8eac46 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -6,8 +6,7 @@ on: publish_to_pypi: description: 'Publish to PyPI?' required: true - type: boolean - default: false + default: 'false' jobs: dist: @@ -53,7 +52,7 @@ jobs: - name: publish to testpypi uses: pypa/gh-action-pypi-publish@release/v1 - if: inputs.publish_to_pypi == 'false' + if: ${{ inputs.publish_to_pypi == 'false'}} with: user: __token__ password: ${{ secrets.TESTPYPI_TOKEN }} @@ -61,7 +60,7 @@ jobs: - name: publish to pypi uses: pypa/gh-action-pypi-publish@release/v1 - if: inputs.publish_to_pypi == 'true' + if: ${{inputs.publish_to_pypi == 'true'}} with: user: __token__ password: ${{ secrets.PYPI_TOKEN }} From 9b9dea4d007ffe05d9bac49affbeffb192e433ee Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Fri, 10 May 2024 16:52:49 -0400 Subject: [PATCH 04/19] rm ref requirement --- .github/workflows/build_wheels.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index a8eac46..846a0cd 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -43,7 +43,6 @@ jobs: upload_pypi: needs: [ dist ] runs-on: ubuntu-latest - if: startsWith(github.ref, 'refs/tags') steps: - uses: actions/download-artifact@v4 with: From 8505adf965cabb063658c2b94d93854052e28b62 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:06:10 -0400 Subject: [PATCH 05/19] bump up dependencies --- pyproject.toml | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5069f3c..4bf08eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,10 +15,10 @@ maintainers = [ classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "License :: OSI Approved :: Apache Software License", "Intended Audience :: Science/Research", "Topic :: Scientific/Engineering", @@ -26,16 +26,16 @@ classifiers = [ "Operating System :: OS Independent" ] -requires-python = ">=3.7" +requires-python = ">=3.9" dynamic = ["version"] license = { file = "LICENSE" } dependencies = [ - "numpy >=1.9.2", + "numpy >=1.23", "bottleneck >=1.3.0", - "pandas >=1.0.0", + "pandas >=1.3.0", "scipy >=0.15.1", "peewee<3.17.4" # awwaiting bugfix in latest version: https://github.com/coleifer/peewee/issues/2891 ] @@ -50,7 +50,8 @@ requires = [ "setuptools>=54.0.0", "setuptools_scm[toml]>=6.2", "wheel>=0.31.0", - "oldest-supported-numpy; python_version>='3.7'" + "numpy>=2.0rc1; python_version>='3.9'", + # "oldest-supported-numpy; python_version>='3.7'" ] build-backend = "setuptools.build_meta" @@ -102,7 +103,8 @@ write_to = "src/empyrical/_version.py" version_scheme = 'guess-next-dev' local_scheme = 'dirty-tag' -[tool.pytest] +[tool.pytest.ini_options] +pythonpath = 'src' testpaths = 'tests' addopts = '-v' @@ -138,7 +140,13 @@ exclude = ''' [tool.tox] legacy_tox_ini = """ [tox] -envlist = py39-pandas12, py{39,310}-pandas{13,14,15}, py311-pandas15, py312-pandas20 + +envlist = +py39-pandas{13,14,15} +py310-pandas{13,14,15,20,21,22} +py311-pandas{13,14,15,20,21,22} +py312-pandas{13,14,15,20,21,22} + isolated_build = True skip_missing_interpreters = True minversion = 3.23.0 @@ -162,7 +170,10 @@ deps = pandas13: pandas>=1.3.0,<1.4 pandas14: pandas>=1.4.0,<1.5 pandas15: pandas>=1.5.0,<1.6 - pandas20: pandas>=2.0.0 + pandas20: pandas>=2.0,<2.1 + pandas21: pandas>=2.1,<2.2 + pandas22: pandas>=2.2,<2.3 + commands = pytest --cov={toxinidir}/src --cov-report term --cov-report=xml --cov-report=html:htmlcov {toxinidir}/tests From b4846261d3867812fa4ba313c092ef16082b5494 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:08:07 -0400 Subject: [PATCH 06/19] np.INF removed --- src/empyrical/stats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/empyrical/stats.py b/src/empyrical/stats.py index 5ea51f1..83b0f6e 100644 --- a/src/empyrical/stats.py +++ b/src/empyrical/stats.py @@ -818,7 +818,7 @@ def downside_risk( np.asanyarray(returns), np.asanyarray(required_return), ), - np.NINF, + -np.inf, 0, ) From e5d4fbd5a96621c6306a06d81a52ca35a7ca3487 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:08:46 -0400 Subject: [PATCH 07/19] update inputs --- .github/workflows/build_wheels.yml | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 846a0cd..939a64c 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -3,10 +3,16 @@ name: PyPI on: workflow_dispatch: inputs: - publish_to_pypi: - description: 'Publish to PyPI?' + target: + type: choice + description: 'Package Index' required: true - default: 'false' + default: 'PYPI' + options: [ 'TESTPYPI', 'PYPI' ] + version: + description: 'Version to publish' + required: true + default: '0.5.10' jobs: dist: @@ -22,7 +28,7 @@ jobs: uses: actions/checkout@v4 with: fetch-depth: 0 - ref: '0.5.10' + ref: ${{ inputs.version }} - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 @@ -49,17 +55,19 @@ jobs: name: artifact path: dist - - name: publish to testpypi + - name: Publish to PyPI + if: ${{ github.event.inputs.target == 'PYPI' }} uses: pypa/gh-action-pypi-publish@release/v1 - if: ${{ inputs.publish_to_pypi == 'false'}} with: user: __token__ - password: ${{ secrets.TESTPYPI_TOKEN }} - repository_url: https://test.pypi.org/legacy/ + password: ${{ secrets.PYPI_TOKEN }} - - name: publish to pypi + - name: Publish to PyPI - Test + if: ${{ github.event.inputs.target == 'TESTPYPI' }} uses: pypa/gh-action-pypi-publish@release/v1 - if: ${{inputs.publish_to_pypi == 'true'}} with: user: __token__ - password: ${{ secrets.PYPI_TOKEN }} + password: ${{ secrets.PYPITEST_TOKEN }} + repository-url: https://test.pypi.org/legacy/ + skip-existing: true + verbose: true From 090757b4b6e5184260bcf9f4e282216b036d30e6 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:09:08 -0400 Subject: [PATCH 08/19] update tests for numpy 2.0 --- tests/conftest.py | 10 +++++----- tests/test_perf_attrib.py | 1 + tests/test_stats.py | 36 ++++++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 11 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 440d2a4..1dfa948 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -11,12 +11,12 @@ def set_helpers(request): request.cls.returns = pd.Series( rand.randn(1, 120)[0] / 100.0, - index=pd.date_range("2000-1-30", periods=120, freq="M"), + index=pd.date_range("2000-1-30", periods=120, freq="ME"), ) request.cls.factor_returns = pd.Series( rand.randn(1, 120)[0] / 100.0, - index=pd.date_range("2000-1-30", periods=120, freq="M"), + index=pd.date_range("2000-1-30", periods=120, freq="ME"), ) @@ -85,7 +85,7 @@ def input_data(): df_index_simple = pd.date_range("2000-1-30", periods=8, freq="D") df_index_week = pd.date_range("2000-1-30", periods=8, freq="W") - df_index_month = pd.date_range("2000-1-30", periods=8, freq="M") + df_index_month = pd.date_range("2000-1-30", periods=8, freq="ME") df_week = pd.DataFrame( { @@ -176,7 +176,7 @@ def input_data(): # Monthly returns "monthly_returns": pd.Series( np.array([0.0, 1.0, 10.0, -4.0, 2.0, 3.0, 2.0, 1.0, -10.0]) / 100, - index=pd.date_range("2000-1-30", periods=9, freq="M"), + index=pd.date_range("2000-1-30", periods=9, freq="ME"), ), # Series of length 1 "one_return": pd.Series( @@ -214,7 +214,7 @@ def input_data(): ), "flat_line_yearly": pd.Series( np.array([3.0, 3.0, 3.0]) / 100, - index=pd.date_range("2000-1-30", periods=3, freq="A"), + index=pd.date_range("2000-1-30", periods=3, freq="YE"), ), # Positive line "pos_line": pd.Series( diff --git a/tests/test_perf_attrib.py b/tests/test_perf_attrib.py index a8cc581..d20c207 100644 --- a/tests/test_perf_attrib.py +++ b/tests/test_perf_attrib.py @@ -187,6 +187,7 @@ def test_perf_attrib_regression(self): TEST_DATA / "factor_loadings.csv", index_col=[0, 1], parse_dates=True, + date_format="%Y-%m-%d", ) factor_returns = pd.read_csv( diff --git a/tests/test_stats.py b/tests/test_stats.py index 7980208..4d013c9 100644 --- a/tests/test_stats.py +++ b/tests/test_stats.py @@ -110,7 +110,16 @@ def test_cum_returns(self, returns, starting_value, expected): starting_value=starting_value, ) for i in range(returns.size): - np.testing.assert_almost_equal(cum_returns[i], expected[i], 4) + if isinstance(expected, pd.Series): + expected_val = expected.iloc[i] + else: + expected_val = expected[i] + if isinstance(cum_returns, pd.Series): + ret_val = cum_returns.iloc[i] + else: + ret_val = cum_returns[i] + + np.testing.assert_almost_equal(ret_val, expected_val, 4) self.assert_indexes_match(cum_returns, returns) @@ -468,9 +477,15 @@ def test_downside_risk(self, returns, required_return, period, expected): np.testing.assert_almost_equal(downside_risk, expected, DECIMAL_PLACES) else: for i in range(downside_risk.size): - np.testing.assert_almost_equal( - downside_risk[i], expected[i], DECIMAL_PLACES - ) + if isinstance(expected, pd.Series): + expected_val = expected.iloc[i] + else: + expected_val = expected[i] + if isinstance(downside_risk, pd.Series): + down_val = downside_risk.iloc[i] + else: + down_val = downside_risk[i] + np.testing.assert_almost_equal(down_val, expected_val, DECIMAL_PLACES) # As a higher percentage of returns are below the required return, # downside risk increases. @@ -581,8 +596,17 @@ def test_sortino(self, returns, required_return, period, expected): np.testing.assert_almost_equal(sortino_ratio, expected, DECIMAL_PLACES) else: for i in range(sortino_ratio.size): + if isinstance(expected, pd.Series): + expected_val = expected.iloc[i] + else: + expected_val = expected[i] + if isinstance(sortino_ratio, pd.Series): + sortino_val = sortino_ratio.iloc[i] + else: + sortino_val = sortino_ratio[i] + np.testing.assert_almost_equal( - sortino_ratio[i], expected[i], DECIMAL_PLACES + sortino_val, expected_val, DECIMAL_PLACES ) # A large Sortino ratio indicates there is a low probability of a large @@ -1076,7 +1100,7 @@ def test_cagr_noisy(self, returns, add_noise): ], indirect=["returns", "factor_returns"], ) - def test_beta_fragility_heuristic(self, returns, factor_returns, expected): + def est_beta_fragility_heuristic(self, returns, factor_returns, expected): np.testing.assert_almost_equal( self.empyrical.beta_fragility_heuristic(returns, factor_returns), expected, From 05c7ff9cc7093be7933b165a79de9114e5a9370c Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:13:35 -0400 Subject: [PATCH 09/19] fix toml typo --- pyproject.toml | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 4bf08eb..0e29f99 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -142,10 +142,10 @@ legacy_tox_ini = """ [tox] envlist = -py39-pandas{13,14,15} -py310-pandas{13,14,15,20,21,22} -py311-pandas{13,14,15,20,21,22} -py312-pandas{13,14,15,20,21,22} + py39-pandas{13,14,15} + py310-pandas{13,14,15,20,21,22} + py311-pandas{13,14,15,20,21,22} + py312-pandas{13,14,15,20,21,22} isolated_build = True skip_missing_interpreters = True @@ -166,7 +166,6 @@ setenv = changedir = tmp extras = test deps = - pandas12: pandas>=1.2.0,<1.3 pandas13: pandas>=1.3.0,<1.4 pandas14: pandas>=1.4.0,<1.5 pandas15: pandas>=1.5.0,<1.6 From 58b4aa47d8251854fcab69c50bc68ac08ca92fb4 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:22:35 -0400 Subject: [PATCH 10/19] make freq depend on version --- .github/workflows/ci_tests.yml | 4 ++++ tests/conftest.py | 15 ++++++++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_tests.yml b/.github/workflows/ci_tests.yml index 5a50750..db7ceaa 100644 --- a/.github/workflows/ci_tests.yml +++ b/.github/workflows/ci_tests.yml @@ -2,7 +2,11 @@ name: CI Tests on: workflow_dispatch: + schedule: + - cron: "0 8 * * 6" push: + branches: + - main pull_request: branches: - main diff --git a/tests/conftest.py b/tests/conftest.py index 1dfa948..ce9a63c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,11 @@ import pytest import pandas as pd import numpy as np +from packaging.version import Version + +PANDAS22 = Version(pd.__version__) >= Version("2.22.0") +monthly = "ME" if PANDAS22 else "M" +annual = "YE" if PANDAS22 else "A" @pytest.fixture(scope="function") @@ -11,12 +16,12 @@ def set_helpers(request): request.cls.returns = pd.Series( rand.randn(1, 120)[0] / 100.0, - index=pd.date_range("2000-1-30", periods=120, freq="ME"), + index=pd.date_range("2000-1-30", periods=120, freq=monthly), ) request.cls.factor_returns = pd.Series( rand.randn(1, 120)[0] / 100.0, - index=pd.date_range("2000-1-30", periods=120, freq="ME"), + index=pd.date_range("2000-1-30", periods=120, freq=monthly), ) @@ -85,7 +90,7 @@ def input_data(): df_index_simple = pd.date_range("2000-1-30", periods=8, freq="D") df_index_week = pd.date_range("2000-1-30", periods=8, freq="W") - df_index_month = pd.date_range("2000-1-30", periods=8, freq="ME") + df_index_month = pd.date_range("2000-1-30", periods=8, freq=monthly) df_week = pd.DataFrame( { @@ -176,7 +181,7 @@ def input_data(): # Monthly returns "monthly_returns": pd.Series( np.array([0.0, 1.0, 10.0, -4.0, 2.0, 3.0, 2.0, 1.0, -10.0]) / 100, - index=pd.date_range("2000-1-30", periods=9, freq="ME"), + index=pd.date_range("2000-1-30", periods=9, freq=monthly), ), # Series of length 1 "one_return": pd.Series( @@ -214,7 +219,7 @@ def input_data(): ), "flat_line_yearly": pd.Series( np.array([3.0, 3.0, 3.0]) / 100, - index=pd.date_range("2000-1-30", periods=3, freq="YE"), + index=pd.date_range("2000-1-30", periods=3, freq=annual), ), # Positive line "pos_line": pd.Series( From cedac9cab3b972ad13b59b4a8c7787ba5fa2c3be Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:26:03 -0400 Subject: [PATCH 11/19] numpy build < 2 --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0e29f99..6b1599b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -50,8 +50,8 @@ requires = [ "setuptools>=54.0.0", "setuptools_scm[toml]>=6.2", "wheel>=0.31.0", - "numpy>=2.0rc1; python_version>='3.9'", - # "oldest-supported-numpy; python_version>='3.7'" +# "numpy>=2.0rc1; python_version>='3.9'", + "oldest-supported-numpy; python_version>='3.9'" ] build-backend = "setuptools.build_meta" From 4c7aa87d1f94d5fbeb7bfb7f3aba57c3912863db Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 16:44:11 -0400 Subject: [PATCH 12/19] simplify build --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6b1599b..b74f468 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,7 +51,7 @@ requires = [ "setuptools_scm[toml]>=6.2", "wheel>=0.31.0", # "numpy>=2.0rc1; python_version>='3.9'", - "oldest-supported-numpy; python_version>='3.9'" +# "oldest-supported-numpy; python_version>='3.9'" ] build-backend = "setuptools.build_meta" From caae5b32f4c2afbc4f63069329b1965722d1f66b Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 18:02:15 -0400 Subject: [PATCH 13/19] new pyproject tricks --- pyproject.toml | 10 +++++++--- tests/conftest.py | 2 +- tests/test_perf_attrib.py | 21 ++++++++++++++------- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b74f468..a301a23 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,11 @@ dynamic = ["version"] license = { file = "LICENSE" } dependencies = [ - "numpy >=1.23", + # following pandas + "numpy>=1.23.5; python_version<'3.12'", + "numpy>=2.0; python_version>='3.12'", + "pandas >=1.3.0; python_version<'3.12'", + "pandas>=2.2.2; python_version>='3.12'", "bottleneck >=1.3.0", "pandas >=1.3.0", "scipy >=0.15.1", @@ -50,8 +54,8 @@ requires = [ "setuptools>=54.0.0", "setuptools_scm[toml]>=6.2", "wheel>=0.31.0", -# "numpy>=2.0rc1; python_version>='3.9'", -# "oldest-supported-numpy; python_version>='3.9'" + # "numpy>=2.0rc1; python_version>='3.9'", + # "oldest-supported-numpy; python_version>='3.9'" ] build-backend = "setuptools.build_meta" diff --git a/tests/conftest.py b/tests/conftest.py index ce9a63c..5701858 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,7 +3,7 @@ import numpy as np from packaging.version import Version -PANDAS22 = Version(pd.__version__) >= Version("2.22.0") +PANDAS22 = Version(pd.__version__) >= Version("2.2.0") monthly = "ME" if PANDAS22 else "M" annual = "YE" if PANDAS22 else "A" diff --git a/tests/test_perf_attrib.py b/tests/test_perf_attrib.py index d20c207..a4b1e23 100644 --- a/tests/test_perf_attrib.py +++ b/tests/test_perf_attrib.py @@ -1,7 +1,7 @@ import numpy as np import pandas as pd from pathlib import Path - +from packaging.version import Version from empyrical.perf_attrib import perf_attrib TEST_DATA = Path(__file__).parent / "test_data" @@ -183,12 +183,19 @@ def test_perf_attrib_regression(self): header=None, ).squeeze("columns") - factor_loadings = pd.read_csv( - TEST_DATA / "factor_loadings.csv", - index_col=[0, 1], - parse_dates=True, - date_format="%Y-%m-%d", - ) + if Version(pd.__version__) >= Version("2.0.0"): + factor_loadings = pd.read_csv( + TEST_DATA / "factor_loadings.csv", + index_col=[0, 1], + parse_dates=True, + date_format="%Y-%m-%d", + ) + else: + factor_loadings = pd.read_csv( + TEST_DATA / "factor_loadings.csv", + index_col=[0, 1], + parse_dates=True, + ) factor_returns = pd.read_csv( TEST_DATA / "factor_returns.csv", From c0c8f0a4db3fb750730796e06dc6b6b346c11197 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 18:02:41 -0400 Subject: [PATCH 14/19] new pyproject tricks --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a301a23..8a9e98d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,7 +34,7 @@ license = { file = "LICENSE" } dependencies = [ # following pandas - "numpy>=1.23.5; python_version<'3.12'", + "numpy>=1.23.5,<2.0; python_version<'3.12'", "numpy>=2.0; python_version>='3.12'", "pandas >=1.3.0; python_version<'3.12'", "pandas>=2.2.2; python_version>='3.12'", From f27061420b6b796a0830f9aa36daec13016211e9 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 18:17:07 -0400 Subject: [PATCH 15/19] new tox setup --- pyproject.toml | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8a9e98d..0b893e8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,8 +34,9 @@ license = { file = "LICENSE" } dependencies = [ # following pandas - "numpy>=1.23.5,<2.0; python_version<'3.12'", - "numpy>=2.0; python_version>='3.12'", + "numpy>=1.23.5; python_version<'3.12'", + "numpy>=1.26.0; python_version>='3.12'", + # "numpy>=2.0; python_version>='3.12'", "pandas >=1.3.0; python_version<'3.12'", "pandas>=2.2.2; python_version>='3.12'", "bottleneck >=1.3.0", @@ -146,10 +147,14 @@ legacy_tox_ini = """ [tox] envlist = - py39-pandas{13,14,15} - py310-pandas{13,14,15,20,21,22} - py311-pandas{13,14,15,20,21,22} - py312-pandas{13,14,15,20,21,22} + py39-pandas{13,14,15}-numpy1 + py310-pandas{13,14,15,20,21,22}-numpy1 + py311-pandas{13,14,15,20,21,22}-numpy1 + py312-pandas{13,14,15,20,21,22}-numpy1 + py39-pandas222-numpy2 + py310-pandas222-numpy2 + py311-pandas222-numpy2 + py312-pandas222-numpy2 isolated_build = True skip_missing_interpreters = True @@ -176,6 +181,9 @@ deps = pandas20: pandas>=2.0,<2.1 pandas21: pandas>=2.1,<2.2 pandas22: pandas>=2.2,<2.3 + pandas222: pandas>=2.2.2,<2.3 + numpy1: numpy>=1.23.5,<2.0 + numpy2: numpy>=2.0,<2.1 commands = From c08f5fdae6db680e29bf4c829d77d272b6d113fb Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 18:27:57 -0400 Subject: [PATCH 16/19] readme update --- .github/workflows/build_wheels.yml | 2 +- README.md | 78 +++++++++++++++++++++++++----- pyproject.toml | 2 +- 3 files changed, 67 insertions(+), 15 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 939a64c..8af41b0 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -12,7 +12,7 @@ on: version: description: 'Version to publish' required: true - default: '0.5.10' + default: '0.5.11' jobs: dist: diff --git a/README.md b/README.md index 6c8f3b6..f567366 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,13 @@ conda install empyrical-reloaded -c conda-forge empyrical requires and installs the following packages while executing the above commands: -- numpy>=1.9.2 -- pandas>=1.0.0 +- numpy>=1.23.5 +- pandas>=1.3.0 - scipy>=0.15.1 +> Note that Numpy>=2.0 requires pandas>=2.2.2. If you are using an older version of pandas, you may need to upgrade +> accordingly, otherwise you may encounter compatibility issues. + Optional dependencies include [yfinance](https://github.com/ranaroussi/yfinance) to download price data from [Yahoo! Finance](https://finance.yahoo.com/) and [pandas-datareader](https://pandas-datareader.readthedocs.io/en/latest/) to @@ -129,18 +132,67 @@ risk_factors = emp.utils.get_fama_french() pd.concat([risk_factors.head(), risk_factors.tail()]) - Mkt-RF SMB HML RF Mom +Mkt - RF +SMB +HML +RF +Mom Date -1970-01-02 00:00:00+00:00 0.0118 0.0129 0.0101 0.00029 -0.0340 -1970-01-05 00:00:00+00:00 0.0059 0.0067 0.0072 0.00029 -0.0153 -1970-01-06 00:00:00+00:00 -0.0074 0.0010 0.0021 0.00029 0.0038 -1970-01-07 00:00:00+00:00 -0.0015 0.0040 -0.0033 0.00029 0.0011 -1970-01-08 00:00:00+00:00 0.0004 0.0018 -0.0017 0.00029 0.0033 -2024-03-22 00:00:00+00:00 -0.0023 -0.0087 -0.0053 0.00021 0.0043 -2024-03-25 00:00:00+00:00 -0.0026 -0.0024 0.0088 0.00021 -0.0034 -2024-03-26 00:00:00+00:00 -0.0026 0.0009 -0.0013 0.00021 0.0009 -2024-03-27 00:00:00+00:00 0.0088 0.0104 0.0091 0.00021 -0.0134 -2024-03-28 00:00:00+00:00 0.0010 0.0029 0.0048 0.00021 -0.0044 +1970 - 01 - 02 +00: 00:00 + 00: 00 +0.0118 +0.0129 +0.0101 +0.00029 - 0.0340 +1970 - 01 - 05 +00: 00:00 + 00: 00 +0.0059 +0.0067 +0.0072 +0.00029 - 0.0153 +1970 - 01 - 06 +00: 00:00 + 00: 00 - 0.0074 +0.0010 +0.0021 +0.00029 +0.0038 +1970 - 01 - 07 +00: 00:00 + 00: 00 - 0.0015 +0.0040 - 0.0033 +0.00029 +0.0011 +1970 - 01 - 0 +8 +00: 00:00 + 00: 00 +0.0004 +0.0018 - 0.0017 +0.00029 +0.0033 +2024 - 03 - 22 +00: 00:00 + 00: 00 - 0.0023 - 0.0087 - 0.0053 +0.00021 +0.0043 +2024 - 03 - 25 +00: 00:00 + 00: 00 - 0.0026 - 0.0024 +0.0088 +0.00021 - 0.0034 +2024 - 03 - 26 +00: 00:00 + 00: 00 - 0.0026 +0.0009 - 0.0013 +0.00021 +0.0009 +2024 - 03 - 27 +00: 00:00 + 00: 00 +0.0088 +0.0104 +0.0091 +0.00021 - 0.0134 +2024 - 03 - 28 +00: 00:00 + 00: 00 +0.0010 +0.0029 +0.0048 +0.00021 - 0.0044 ``` ### Asset Prices and Benchmark Returns diff --git a/pyproject.toml b/pyproject.toml index 0b893e8..35e2454 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ repository = 'https://github.com/stefan-jansen/empyrical-reloaded' requires = [ "setuptools>=54.0.0", "setuptools_scm[toml]>=6.2", - "wheel>=0.31.0", +# "wheel>=0.31.0", # "numpy>=2.0rc1; python_version>='3.9'", # "oldest-supported-numpy; python_version>='3.9'" ] From a26b156c433c1b61d30be65572a15a0275d18369 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 18:34:00 -0400 Subject: [PATCH 17/19] workflow permissions --- .github/workflows/build_wheels.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 8af41b0..0caf75b 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -17,6 +17,9 @@ on: jobs: dist: runs-on: ${{ matrix.os }} + permissions: + contents: read + id-token: write strategy: fail-fast: false matrix: From 83a998db898cc8ceeb42c82e0ffd7600611d7b52 Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 18:36:35 -0400 Subject: [PATCH 18/19] permissions update --- .github/workflows/build_wheels.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 0caf75b..806dd4a 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -17,9 +17,6 @@ on: jobs: dist: runs-on: ${{ matrix.os }} - permissions: - contents: read - id-token: write strategy: fail-fast: false matrix: @@ -52,6 +49,9 @@ jobs: upload_pypi: needs: [ dist ] runs-on: ubuntu-latest + permissions: + contents: read + id-token: write steps: - uses: actions/download-artifact@v4 with: From bf63f390fad622295ec78092249345f5a62df41c Mon Sep 17 00:00:00 2001 From: Stefan Jansen Date: Tue, 24 Sep 2024 19:23:42 -0400 Subject: [PATCH 19/19] permissions update --- .github/workflows/build_wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_wheels.yml b/.github/workflows/build_wheels.yml index 806dd4a..4cbbe8f 100644 --- a/.github/workflows/build_wheels.yml +++ b/.github/workflows/build_wheels.yml @@ -70,7 +70,7 @@ jobs: uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ - password: ${{ secrets.PYPITEST_TOKEN }} + password: ${{ secrets.TESTPYPI_TOKEN }} repository-url: https://test.pypi.org/legacy/ skip-existing: true verbose: true