Skip to content

Commit

Permalink
Python 3.12 compatibility (#27)
Browse files Browse the repository at this point in the history
* format; make def arg immutable
* bump up versions; cap peewee due to bug
* address deprecation
* update GHA  python versions
* updated installation instructions
* make pandas-datareader optional
* update black version
* update and fix GHA black version
* update tox envs
  • Loading branch information
stefan-jansen authored May 10, 2024
1 parent c809f03 commit 3a363e4
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 55 deletions.
14 changes: 11 additions & 3 deletions .github/workflows/ci_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ jobs:
with:
options: "--check --diff"
src: "./src ./tests"
version: "24.4.2"

flake8-lint:
name: Lint Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/setup-python@v5
with:
python-version: "3.10"

Expand All @@ -36,13 +37,20 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
python-version: [ "3.8", "3.9", "3.10", "3.11" ]
python-version: [ "3.9", "3.10", "3.11", "3.12" ]
exclude:
- os: macos-latest
python-version: "3.9"
- os: macos-latest
python-version: "3.11"
- os: macos-latest
python-version: "3.12"
steps:
- name: Checkout empyrical
uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, windows-latest, macos-latest ]
python-version: [ 3.7, 3.8, 3.9 ]
python-version: [ 3.9, "3.10", "3.11", "3.12" ]
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: [3.7, 3.8, 3.9]
python-version: [3.9, "3.10", "3.11", "3.12" ]
steps:
- name: Checkout empyrical
uses: actions/checkout@v4
Expand Down
80 changes: 57 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
</p>

![PyPI](https://img.shields.io/pypi/v/empyrical-reloaded)
![Conda (channel only)](https://img.shields.io/conda/vn/ml4t/empyrical-reloaded)
![PyPI - Downloads](https://img.shields.io/pypi/dm/empyrical-reloaded)

[![Conda Version](https://img.shields.io/conda/vn/conda-forge/empyrical-reloaded.svg)](https://anaconda.org/conda-forge/empyrical-reloaded)
[![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/empyrical-reloaded.svg)](https://anaconda.org/conda-forge/empyrical-reloaded)

[![PyPI Wheels](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/build_wheels.yml/badge.svg)](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/build_wheels.yml)
[![Conda packages](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/conda_package.yml/badge.svg)](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/conda_package.yml)
Expand All @@ -15,33 +18,56 @@ Common financial return and risk metrics in Python.

## Installation

empyrical requires Python 3.7+. You can install it using `pip`:
empyrical requires Python 3.9+. You can install it using `pip`:

```bash
pip install empyrical-reloaded
```

or `conda`:
or `conda` from the `conda-forge` channel

```bash
conda install empyrical-reloaded -c ml4t -c ranaroussi
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
- scipy>=0.15.1
- pandas-datareader>=0.4
- yfinance>=0.1.59

Empyrical uses [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 access [Fama-French](https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html) risk factors.
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
access [Fama-French](https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html) risk factors and FRED
treasury yields.

> Note that `pandas-datareader` is not compatible with Python>=3.12.
To install the optional dependencies, use:

```bash
pip install empyrical-reloaded[yfinance]
```

or

```bash
pip install empyrical-reloaded[datreader]
```

or

```bash
pip install empyrical-reloaded[yfinance,datreader]
```

## Usage

### Simple Statistics

Empyrical computes basic metrics from returns and volatility to alpha and beta, Value at Risk, and Shorpe or Sortino ratios.
Empyrical computes basic metrics from returns and volatility to alpha and beta, Value at Risk, and Shorpe or Sortino
ratios.

```python
import numpy as np
Expand All @@ -59,7 +85,7 @@ alpha, beta = alpha_beta(returns, benchmark_returns)

### Rolling Measures

Empyrical also aggregates returna nd risk metrics for rolling windows:
Empyrical also aggregates return and risk metrics for rolling windows:

```python
import numpy as np
Expand All @@ -73,7 +99,8 @@ roll_max_drawdown(returns, window=3)

### Pandas Support

Empyrical also works with both [NumPy](https://numpy.org/) arrays and [Pandas](https://pandas.pydata.org/) data structures:
Empyrical also works with both [NumPy](https://numpy.org/) arrays and [Pandas](https://pandas.pydata.org/) data
structures:

```python
import pandas as pd
Expand All @@ -92,30 +119,36 @@ capture(returns, factor_returns)

Empyrical downloads Fama-French risk factors from 1970 onward:

> Note: requires optional dependency `pandas-datareader` - see installation instructions above.gst
```python
import pandas as pd
import empyrical as emp

risk_factors = emp.utils.get_fama_french()

risk_factors.head().append(risk_factors.tail())
pd.concat([risk_factors.head(), risk_factors.tail()])

Mkt-RF SMB HML RF Mom
Date
1970-01-02 00:00:00+00:00 0.0118 0.0131 0.0100 0.00029 -0.0341
1970-01-05 00:00:00+00:00 0.0059 0.0066 0.0072 0.00029 -0.0152
1970-01-06 00:00:00+00:00 -0.0074 0.0010 0.0020 0.00029 0.0040
1970-01-07 00:00:00+00:00 -0.0015 0.0039 -0.0032 0.00029 0.0011
1970-01-08 00:00:00+00:00 0.0004 0.0018 -0.0015 0.00029 0.0033
2021-02-22 00:00:00+00:00 -0.0112 -0.0009 0.0314 0.00000 -0.0325
2021-02-23 00:00:00+00:00 -0.0015 -0.0128 0.0090 0.00000 -0.0185
2021-02-24 00:00:00+00:00 0.0115 0.0120 0.0134 0.00000 -0.0007
2021-02-25 00:00:00+00:00 -0.0273 -0.0112 0.0087 0.00000 -0.0195
2021-02-26 00:00:00+00:00 -0.0028 0.0072 -0.0156 0.00000 0.0195
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
```

### Asset Prices and Benchmark Returns

Empyrical [yfinance](https://github.com/ranaroussi/yfinance) to download price data from [Yahoo! Finance](https://finance.yahoo.com/). To obtain the S&P returns since 1950, use:
Empyrical use [yfinance](https://github.com/ranaroussi/yfinance) to download price data
from [Yahoo! Finance](https://finance.yahoo.com/). To obtain the S&P returns since 1950, use:

> Note: requires optional dependency `yfinance` - see installation instructions above.
```python
import empyrical as emp
Expand Down Expand Up @@ -152,7 +185,8 @@ Please [open an issue](https://github.com/stefan-jansen/empyrical-reloaded/issue

## Contributing

Please contribute using [Github Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits, and [open a pull request](https://github.com/stefan-jansen/empyrical-reloaded/compare/).
Please contribute using [Github Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits,
and [open a pull request](https://github.com/stefan-jansen/empyrical-reloaded/compare/).

## Testing

Expand Down
14 changes: 9 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ dependencies = [
"bottleneck >=1.3.0",
"pandas >=1.0.0",
"scipy >=0.15.1",
"pandas-datareader >=0.4",
"peewee<3.17.4" # awwaiting bugfix in latest version: https://github.com/coleifer/peewee/issues/2891
]

[project.urls]
Expand Down Expand Up @@ -85,6 +85,10 @@ yfinance = [
"yfinance >=0.1.63",
]

datareader = [
"pandas-datareader >=0.4"
]

[tool.setuptools]
include-package-data = true
zip-safe = false
Expand Down Expand Up @@ -117,7 +121,7 @@ skip = "*musllinux*"

[tool.black]
line-length = 88
target-version = ['py38', 'py39', 'py310']
target-version = ['py39', 'py310', 'py311', 'py312']
exclude = '''
(
asv_bench/env
Expand All @@ -134,18 +138,17 @@ exclude = '''
[tool.tox]
legacy_tox_ini = """
[tox]
envlist = py{38,39}-pandas12, py{38,39,310}-pandas{13,14,15}, py311-pandas15
envlist = py39-pandas12, py{39,310}-pandas{13,14,15}, py311-pandas15, py312-pandas20
isolated_build = True
skip_missing_interpreters = True
minversion = 3.23.0
[gh-actions]
python =
3.7: py37
3.8: py38
3.9: py39
3.10: py310
3.11: py311
3.12: py312
[testenv]
usedevelop = True
Expand All @@ -159,6 +162,7 @@ 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
commands =
pytest --cov={toxinidir}/src --cov-report term --cov-report=xml --cov-report=html:htmlcov {toxinidir}/tests
Expand Down
1 change: 1 addition & 0 deletions src/empyrical/deprecate.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Utilities for marking deprecated functions."""

# Copyright 2018 Quantopian, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand Down
15 changes: 13 additions & 2 deletions src/empyrical/stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
from collections import OrderedDict
from math import pow
from sys import float_info
import warnings

import numpy as np
import pandas as pd
from scipy import optimize, stats
from numpy.typing import ArrayLike

from .periods import (
ANNUALIZATION_FACTORS,
Expand Down Expand Up @@ -112,7 +114,7 @@ def simple_returns(prices):
return out


def cum_returns(returns, starting_value=0, out=None):
def cum_returns(returns, starting_value=0, out=None) -> ArrayLike:
"""
Compute cumulative returns from simple returns.
Expand Down Expand Up @@ -832,10 +834,19 @@ def downside_risk(
return out


roll_downsize_risk = _create_unary_vectorized_roll_function(downside_risk) # Typo spotted. Should be roll_downside_risk. Ideally be depreciated.
roll_downside_risk = _create_unary_vectorized_roll_function(downside_risk)


# Deprecated typo version
def roll_downsize_risk(*args, **kwargs):
warnings.warn(
"roll_downsize_risk is deprecated; use roll_downside_risk instead.",
DeprecationWarning,
stacklevel=2,
)
return roll_downside_risk(*args, **kwargs)


def excess_sharpe(returns, factor_returns, out=None):
"""
Determines the Excess Sharpe of a strategy.
Expand Down
Loading

0 comments on commit 3a363e4

Please sign in to comment.