├── tests ├── __init__.py ├── test_data │ ├── intercepts.csv │ ├── residuals.csv │ ├── positions.csv │ ├── returns.csv │ ├── factor_returns.csv │ └── factor_loadings.csv ├── test_perf_attrib.py └── conftest.py ├── .gitattributes ├── MANIFEST.in ├── codecov.yml ├── docs ├── source │ ├── index.rst │ ├── perf_attrib.rst │ ├── utils.rst │ ├── stats.rst │ └── conf.py ├── make_docs.bat ├── make.bat ├── deploy.py ├── conf.py └── Makefile ├── .flake8 ├── src └── empyrical │ ├── periods.py │ ├── deprecate.py │ ├── __init__.py │ ├── perf_attrib.py │ └── utils.py ├── .github ├── dependabot.yml ├── workflows │ ├── unit_tests.yml │ ├── test_wheels.yml │ ├── ci_tests.yml │ ├── build_wheels.yml │ ├── codeql-analysis.yml │ └── conda_package.yml └── ISSUE_TEMPLATE.md ├── conda └── recipe │ └── meta.yaml ├── .gitignore ├── pyproject.toml ├── README.md └── LICENSE /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | empyrical/_version.py export-subst 2 | -------------------------------------------------------------------------------- /tests/test_data/intercepts.csv: -------------------------------------------------------------------------------- 1 | 19001,0.0 2 | 19002,0.0 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include versioneer.py 2 | include empyrical/_version.py 3 | include LICENSE 4 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "**/__init__.py" 3 | - "**/_version.py" 4 | - "**/deprecate.py" 5 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. title:: empyrical 2 | 3 | .. mdinclude:: ../../README.md 4 | 5 | .. toctree:: 6 | :maxdepth: 4 7 | 8 | stats 9 | perf_attrib 10 | utils 11 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = .git, __pycache__, docs, conda, tools 3 | max-line-length = 88 4 | max-complexity = 18 5 | select = B,C,E,F,W,T4,B9, B950 6 | ignore = E266, W503, F403, F401, E231 7 | extend-ignore = E203, E501 8 | -------------------------------------------------------------------------------- /docs/make_docs.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | REM Makes the Sphinx documentation files 3 | 4 | FOR %%a IN (%~dp0\.) do set SOURCE=%%~dpa 5 | set OLD_PYTHONPATH=%PYTHONPATH% 6 | set PYTHONPATH=%PYTHONPATH%;%SOURCE% 7 | 8 | sphinx-apidoc -f -o . ../empyrical 9 | ./make.bat html 10 | set PYTHONPATH=%OLD_PYTHONPATH% 11 | -------------------------------------------------------------------------------- /docs/source/perf_attrib.rst: -------------------------------------------------------------------------------- 1 | .. _perf_attrib: 2 | 3 | Performance Attribution 4 | ----------------------- 5 | 6 | The module :mod:`empyrical.perf_attrib` facilitates the attribution of performance to various risk factors. 7 | 8 | .. automodule:: empyrical.perf_attrib 9 | :members: 10 | :undoc-members: 11 | :show-inheritance: 12 | -------------------------------------------------------------------------------- /docs/source/utils.rst: -------------------------------------------------------------------------------- 1 | .. _utils: 2 | 3 | Utilities 4 | --------- 5 | 6 | The module :mod:`empyrical.utils` contains various helper functions, e.g. to source risk factor and 7 | return data using `pandas-datareader and `yfinance`. 8 | 9 | .. automodule:: empyrical.utils 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /docs/source/stats.rst: -------------------------------------------------------------------------------- 1 | .. _stats: 2 | 3 | Performance & Risk Metrics 4 | -------------------------- 5 | 6 | The module :mod:`empyrical.stats` includes various return and risk metrics, such as the 7 | computation of returns and volatility, alpha and beta, Value at Risk, and Shorpe or Sortino ratios. 8 | 9 | .. automodule:: empyrical.stats 10 | :members: 11 | :undoc-members: 12 | :show-inheritance: 13 | -------------------------------------------------------------------------------- /src/empyrical/periods.py: -------------------------------------------------------------------------------- 1 | APPROX_BDAYS_PER_MONTH = 21 2 | APPROX_BDAYS_PER_YEAR = 252 3 | 4 | MONTHS_PER_YEAR = 12 5 | WEEKS_PER_YEAR = 52 6 | QTRS_PER_YEAR = 4 7 | 8 | DAILY = "daily" 9 | WEEKLY = "weekly" 10 | MONTHLY = "monthly" 11 | QUARTERLY = "quarterly" 12 | YEARLY = "yearly" 13 | 14 | ANNUALIZATION_FACTORS = { 15 | DAILY: APPROX_BDAYS_PER_YEAR, 16 | WEEKLY: WEEKS_PER_YEAR, 17 | MONTHLY: MONTHS_PER_YEAR, 18 | QUARTERLY: QTRS_PER_YEAR, 19 | YEARLY: 1, 20 | } 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | # Maintain dependencies for GitHub Actions 9 | - package-ecosystem: "github-actions" 10 | # Workflow files stored in the default location of `.github/workflows` 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | open-pull-requests-limit: 10 15 | -------------------------------------------------------------------------------- /conda/recipe/meta.yaml: -------------------------------------------------------------------------------- 1 | {% set name = "empyrical-reloaded" %} 2 | {% set version = "0.5.8" %} 3 | 4 | package: 5 | name: {{ name|lower }} 6 | version: {{ version }} 7 | 8 | source: 9 | url: https://pypi.io/packages/source/{{ name[0] }}/{{ name }}/{{ name }}-{{ version }}.tar.gz 10 | md5: b6db529711262a020c7d1085b591d69d 11 | 12 | 13 | build: 14 | number: 0 15 | skip: true # [py<37 or not x86_64] 16 | script: {{ PYTHON }} -m pip install . -vv 17 | 18 | requirements: 19 | build: 20 | - python 21 | - setuptools 22 | 23 | run: 24 | - python 25 | - numpy >=1.9.2 26 | - pandas >=1.0.0 27 | - scipy >=0.15.1 28 | - yfinance >=0.1.63 29 | - pandas-datareader >=0.4 30 | 31 | test: 32 | imports: 33 | - empyrical 34 | 35 | about: 36 | home: https://empyrical.ml4trading.io 37 | summary: 'Common financial risk metrics' 38 | license: Apache-2.0 39 | license_file: LICENSE 40 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=empyrical 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /.github/workflows/unit_tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 9 * * 6" 7 | jobs: 8 | build: 9 | runs-on: ${{ matrix.os }} 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | os: [ubuntu-latest, windows-latest, macos-latest] 14 | python-version: ["3.10", "3.11", "3.12", "3.13" ] 15 | steps: 16 | - name: Checkout empyrical 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Install empyrical 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install tox tox-gh-actions 28 | pip install -e .[test] 29 | 30 | - name: Lint with flake8 31 | run: | 32 | flake8 33 | 34 | - name: Unittests with tox & pytest 35 | run: | 36 | tox -e ci_full_tests 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Dear empyrical maintainers, 2 | 3 | Before I tell you about my issue, let me describe my environment: 4 | 5 | # Environment 6 | 7 |
8 | 9 | * Operating System: (Windows Version or `$ uname --all`) 10 | * Python Version: `$ python --version` 11 | * Python Bitness: `$ python -c 'import math, sys;print(int(math.log(sys.maxsize + 1, 2) + 1))'` 12 | * How did you install empyrical: (`pip`, `conda`, or `other (please explain)`) 13 | * Python packages: `$ pip freeze` or `$ conda list` 14 | 15 |
16 | 17 | Now that you know a little about me, let me tell you about the issue I am having: 18 | 19 | # Description of Issue 20 | 21 | * What did you expect to happen? 22 | * What happened instead? 23 | 24 | Here is how you can reproduce this issue on your machine: 25 | 26 | ## Reproduction Steps 27 | 28 | 1. 29 | 2. 30 | 3. 31 | 32 | ... 33 | 34 | ## What steps have you taken to resolve this already? 35 | 36 | ... 37 | 38 | # Anything else? 39 | 40 | ... 41 | 42 | Sincerely, 43 | `$ whoami` 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # exclude vscode and venv 2 | .venv 3 | .vscode 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *,cover 49 | .hypothesis/ 50 | 51 | # Translations 52 | *.mo 53 | *.pot 54 | 55 | # Django stuff: 56 | *.log 57 | 58 | # Sphinx documentation 59 | docs/_build/ 60 | 61 | # PyBuilder 62 | target/ 63 | 64 | #Ipython Notebook 65 | .ipynb_checkpoints 66 | 67 | # JetBrains 68 | .idea/ 69 | 70 | .pre-commit-config.yaml 71 | .python-version 72 | 73 | conda/recipe/linux-64/ 74 | conda/recipe/osx-64/ 75 | conda/recipe/win-64/ 76 | docs/test/ 77 | admin 78 | src/empyrical/_version.py 79 | venv/ 80 | -------------------------------------------------------------------------------- /.github/workflows/test_wheels.yml: -------------------------------------------------------------------------------- 1 | name: Test Wheels 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | test_wheels: 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ ubuntu-latest, windows-latest, macos-latest ] 12 | python-version: [ 3.9, "3.10", "3.11", "3.12" ] 13 | steps: 14 | - name: Set up Python ${{ matrix.python-version }} 15 | uses: actions/setup-python@v5 16 | with: 17 | python-version: ${{ matrix.python-version }} 18 | 19 | - name: Checkout empyrical 20 | uses: actions/checkout@v4 21 | 22 | # - name: Install wheel & run tests 23 | # run: | 24 | # pip install -U pip wheel tox-gh-actions 25 | # pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple empyrical-reloaded[test] 26 | # tox -p auto -q 27 | 28 | - name: Unittests with tox & pytest 29 | uses: nick-fields/retry@v3 30 | with: 31 | timeout_minutes: 90 32 | max_attempts: 3 33 | retry_on: error 34 | new_command_on_retry: | 35 | python -m pip install -U pip wheel tox tox-gh-actions 36 | python -m pip install -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple empyrical-reloaded[test] 37 | tox -p auto -q 38 | 39 | command: tox 40 | -------------------------------------------------------------------------------- /src/empyrical/deprecate.py: -------------------------------------------------------------------------------- 1 | """Utilities for marking deprecated functions.""" 2 | 3 | # Copyright 2018 Quantopian, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import warnings 18 | from functools import wraps 19 | 20 | 21 | def deprecated(msg=None, stacklevel=2): 22 | """ 23 | Used to mark a function as deprecated. 24 | Parameters 25 | ---------- 26 | msg : str 27 | The message to display in the deprecation warning. 28 | stacklevel : int 29 | How far up the stack the warning needs to go, before 30 | showing the relevant calling lines. 31 | Usage 32 | ----- 33 | @deprecated(msg='function_a is deprecated! Use function_b instead.') 34 | def function_a(*args, **kwargs): 35 | """ 36 | 37 | def deprecated_dec(fn): 38 | @wraps(fn) 39 | def wrapper(*args, **kwargs): 40 | warnings.warn( 41 | msg or "Function %s is deprecated." % fn.__name__, 42 | category=DeprecationWarning, 43 | stacklevel=stacklevel, 44 | ) 45 | return fn(*args, **kwargs) 46 | 47 | return wrapper 48 | 49 | return deprecated_dec 50 | -------------------------------------------------------------------------------- /.github/workflows/ci_tests.yml: -------------------------------------------------------------------------------- 1 | name: CI Tests 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 8 * * 6" 7 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | branches: 12 | - main 13 | 14 | jobs: 15 | black-format: 16 | name: Formatting Check 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: psf/black@stable 21 | with: 22 | options: "--check --diff" 23 | src: "./src ./tests" 24 | version: "24.4.2" 25 | 26 | flake8-lint: 27 | name: Lint Check 28 | runs-on: ubuntu-latest 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: actions/setup-python@v5 32 | with: 33 | python-version: "3.12" 34 | 35 | - name: flake8 Lint 36 | uses: py-actions/flake8@v2 37 | 38 | tests: 39 | runs-on: ${{ matrix.os }} 40 | strategy: 41 | fail-fast: false 42 | matrix: 43 | os: [ ubuntu-latest, windows-latest, macos-latest ] 44 | python-version: [ "3.10", "3.11", "3.12", "3.13" ] 45 | exclude: 46 | - os: macos-latest 47 | python-version: "3.9" 48 | - os: macos-latest 49 | python-version: "3.11" 50 | - os: macos-latest 51 | python-version: "3.12" 52 | steps: 53 | - name: Checkout empyrical 54 | uses: actions/checkout@v4 55 | 56 | - name: Set up Python ${{ matrix.python-version }} 57 | uses: actions/setup-python@v5 58 | with: 59 | python-version: ${{ matrix.python-version }} 60 | 61 | - name: Install empyrical 62 | run: | 63 | python -VV 64 | python -m pip install --upgrade pip setuptools wheel 65 | pip install tox tox-gh-actions 66 | pip install -e .[test] 67 | 68 | - name: Unittests with tox & pytest 69 | run: tox 70 | 71 | - name: Upload coverage data to Codecov 72 | if: ${{ matrix.os == 'ubuntu-latest' }} 73 | uses: codecov/codecov-action@v5 74 | with: 75 | fail_ci_if_error: false 76 | name: codecov-umbrella 77 | verbose: true 78 | -------------------------------------------------------------------------------- /.github/workflows/build_wheels.yml: -------------------------------------------------------------------------------- 1 | name: PyPI 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | target: 7 | type: choice 8 | description: 'Package Index' 9 | required: true 10 | default: 'PYPI' 11 | options: [ 'TESTPYPI', 'PYPI' ] 12 | version: 13 | description: 'Version to publish' 14 | required: true 15 | default: '0.5.12' 16 | 17 | jobs: 18 | dist: 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | os: [ ubuntu-latest ] 24 | python-version: [ "3.12" ] 25 | 26 | steps: 27 | - name: Checkout empyrical 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | ref: ${{ inputs.version }} 32 | 33 | - name: Set up Python ${{ matrix.python-version }} 34 | uses: actions/setup-python@v5 35 | with: 36 | python-version: ${{ matrix.python-version }} 37 | 38 | - name: Build wheels 39 | run: pipx run build 40 | 41 | - name: Store artifacts 42 | uses: actions/upload-artifact@v4 43 | with: 44 | path: dist/* 45 | 46 | - name: Check metadata 47 | run: pipx run twine check dist/* 48 | 49 | upload_pypi: 50 | needs: [ dist ] 51 | runs-on: ubuntu-latest 52 | permissions: 53 | contents: read 54 | id-token: write 55 | steps: 56 | - uses: actions/download-artifact@v4 57 | with: 58 | name: artifact 59 | path: dist 60 | 61 | - name: Publish to PyPI 62 | if: ${{ github.event.inputs.target == 'PYPI' }} 63 | uses: pypa/gh-action-pypi-publish@release/v1 64 | with: 65 | user: __token__ 66 | password: ${{ secrets.PYPI_TOKEN }} 67 | 68 | - name: Publish to PyPI - Test 69 | if: ${{ github.event.inputs.target == 'TESTPYPI' }} 70 | uses: pypa/gh-action-pypi-publish@release/v1 71 | with: 72 | user: __token__ 73 | password: ${{ secrets.TESTPYPI_TOKEN }} 74 | repository-url: https://test.pypi.org/legacy/ 75 | skip-existing: true 76 | verbose: true 77 | -------------------------------------------------------------------------------- /src/empyrical/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2016 Quantopian, Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # flake8: noqa 16 | 17 | try: 18 | from ._version import version as __version__ 19 | from ._version import version_tuple 20 | except ImportError: 21 | __version__ = "unknown version" 22 | version_tuple = (0, 0, "unknown version") 23 | 24 | from .stats import ( 25 | aggregate_returns, 26 | alpha, 27 | alpha_aligned, 28 | alpha_beta, 29 | alpha_beta_aligned, 30 | annual_return, 31 | annual_volatility, 32 | beta, 33 | beta_aligned, 34 | cagr, 35 | beta_fragility_heuristic, 36 | beta_fragility_heuristic_aligned, 37 | gpd_risk_estimates, 38 | gpd_risk_estimates_aligned, 39 | calmar_ratio, 40 | capture, 41 | conditional_value_at_risk, 42 | cum_returns, 43 | cum_returns_final, 44 | down_alpha_beta, 45 | down_capture, 46 | downside_risk, 47 | excess_sharpe, 48 | max_drawdown, 49 | omega_ratio, 50 | roll_alpha, 51 | roll_alpha_aligned, 52 | roll_alpha_beta, 53 | roll_alpha_beta_aligned, 54 | roll_annual_volatility, 55 | roll_beta, 56 | roll_beta_aligned, 57 | roll_down_capture, 58 | roll_max_drawdown, 59 | roll_sharpe_ratio, 60 | roll_sortino_ratio, 61 | roll_up_capture, 62 | roll_up_down_capture, 63 | sharpe_ratio, 64 | simple_returns, 65 | sortino_ratio, 66 | stability_of_timeseries, 67 | tail_ratio, 68 | up_alpha_beta, 69 | up_capture, 70 | up_down_capture, 71 | batting_average, 72 | value_at_risk, 73 | ) 74 | 75 | from .periods import DAILY, WEEKLY, MONTHLY, QUARTERLY, YEARLY 76 | 77 | 78 | from .perf_attrib import ( 79 | perf_attrib, 80 | compute_exposures, 81 | ) 82 | -------------------------------------------------------------------------------- /docs/deploy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from contextlib import contextmanager 3 | from glob import glob 4 | import os 5 | from os.path import basename, exists, isfile 6 | from pathlib import Path 7 | from shutil import move, rmtree 8 | from subprocess import check_call 9 | 10 | HERE = Path(__file__).resolve(strict=True).parent 11 | EMPYRICAL_ROOT = HERE.parent 12 | TEMP_LOCATION = "/tmp/empyrical-doc" 13 | TEMP_LOCATION_GLOB = TEMP_LOCATION + "/*" 14 | 15 | 16 | @contextmanager 17 | def removing(path): 18 | try: 19 | yield 20 | finally: 21 | rmtree(path) 22 | 23 | 24 | def ensure_not_exists(path): 25 | if not exists(path): 26 | return 27 | if isfile(path): 28 | os.unlink(path) 29 | else: 30 | rmtree(path) 31 | 32 | 33 | def main(): 34 | old_dir = Path.cwd() 35 | print("Moving to %s." % HERE) 36 | os.chdir(HERE) 37 | 38 | try: 39 | print("Cleaning docs with 'make clean'") 40 | check_call(["make", "clean"]) 41 | print("Building docs with 'make html'") 42 | check_call(["make", "html"]) 43 | 44 | print("Clearing temp location '%s'" % TEMP_LOCATION) 45 | rmtree(TEMP_LOCATION, ignore_errors=True) 46 | 47 | with removing(TEMP_LOCATION): 48 | print("Copying built files to temp location.") 49 | move("build/html", TEMP_LOCATION) 50 | 51 | print("Moving to '%s'" % EMPYRICAL_ROOT) 52 | os.chdir(EMPYRICAL_ROOT) 53 | 54 | print("Checking out gh-pages branch.") 55 | check_call( 56 | [ 57 | "git", 58 | "branch", 59 | "-f", 60 | "--track", 61 | "gh-pages", 62 | "origin/gh-pages", 63 | ] 64 | ) 65 | check_call(["git", "checkout", "gh-pages"]) 66 | check_call(["git", "reset", "--hard", "origin/gh-pages"]) 67 | 68 | print("Copying built files:") 69 | for file_ in glob(TEMP_LOCATION_GLOB): 70 | base = basename(file_) 71 | 72 | print("%s -> %s" % (file_, base)) 73 | ensure_not_exists(base) 74 | move(file_, ".") 75 | finally: 76 | os.chdir(old_dir) 77 | 78 | print() 79 | print("Updated documentation branch in directory %s" % EMPYRICAL_ROOT) 80 | print("If you are happy with these changes, commit and push to gh-pages.") 81 | 82 | 83 | if __name__ == "__main__": 84 | main() 85 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '41 4 * * 0' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'python' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 37 | # Learn more: 38 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 39 | 40 | steps: 41 | - name: Checkout repository 42 | uses: actions/checkout@v4 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v3 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v3 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v3 72 | -------------------------------------------------------------------------------- /.github/workflows/conda_package.yml: -------------------------------------------------------------------------------- 1 | name: Anaconda 2 | 3 | on: workflow_dispatch 4 | 5 | jobs: 6 | build_wheels: 7 | name: py${{ matrix.python }} on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | env: 10 | ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} 11 | defaults: 12 | run: 13 | shell: bash -l {0} 14 | 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | os: [ macos-latest, windows-latest, ubuntu-latest ] 19 | python: [ '3.7', '3.8', '3.9' ] 20 | # python: [ '3.9' ] 21 | 22 | steps: 23 | - name: Checkout empyrical-reloaded 24 | uses: actions/checkout@v4 25 | 26 | - name: Setup miniconda3 27 | uses: conda-incubator/setup-miniconda@v3 28 | with: 29 | miniconda-version: latest 30 | auto-update-conda: true 31 | channel-priority: true 32 | mamba-version: "*" 33 | python-version: ${{ matrix.python }} 34 | activate-environment: recipe 35 | channels: ml4t, conda-forge, defaults, anaconda, ranaroussi 36 | 37 | - name: create uploader 38 | # address broken client under py3.9 39 | if: ${{ matrix.python == '3.9' }} 40 | run: conda create -n up python=3.7 anaconda-client 41 | 42 | - name: conda build for ${{ matrix.os }} 43 | run: | 44 | conda activate recipe 45 | mamba install -n recipe boa conda-verify anaconda-client 46 | conda mambabuild --output-folder . --python ${{ matrix.python }} conda/recipe 47 | 48 | - name: activate uploader 49 | # address broken client under py3.9 50 | if: ${{ matrix.python == '3.9' }} 51 | run: conda activate up 52 | 53 | - name: store windows result 54 | uses: actions/upload-artifact@v4 55 | if: ${{ matrix.os == 'windows-latest' }} 56 | with: 57 | path: win-64/*.tar.bz2 58 | 59 | - name: upload windows 60 | if: ${{ matrix.os == 'windows-latest' }} 61 | run: anaconda upload -l main -u ml4t win-64/*.tar.bz2 62 | 63 | - name: store linux result 64 | uses: actions/upload-artifact@v4 65 | if: ${{ matrix.os == 'ubuntu-latest' }} 66 | with: 67 | path: linux-64/*.tar.bz2 68 | 69 | - name: upload linux 70 | if: ${{ matrix.os == 'ubuntu-latest' }} 71 | run: anaconda upload -l main -u ml4t linux-64/*.tar.bz2 72 | 73 | - name: store macos result 74 | uses: actions/upload-artifact@v4 75 | if: ${{ matrix.os == 'macos-latest' }} 76 | with: 77 | path: osx-64/*.tar.bz2 78 | 79 | - name: upload macos 80 | if: ${{ matrix.os == 'macos-latest' }} 81 | run: anaconda upload -l main -u ml4t osx-64/*.tar.bz2 82 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from pathlib import Path 4 | import pydata_sphinx_theme 5 | from empyrical import __version__ as version 6 | 7 | sys.path.insert(0, Path("../..").resolve(strict=True).as_posix()) 8 | 9 | 10 | # This is the expected signature of the handler for this event, cf doc 11 | def autodoc_skip_member_handler(app, what, name, obj, skip, options): 12 | # Basic approach; you might want a regex instead 13 | return name.startswith(("cache", "_")) 14 | 15 | 16 | # Automatically called by sphinx at startup 17 | def setup(app): 18 | # Connect the autodoc-skip-member event from apidoc to the callback 19 | app.connect("autodoc-skip-member", autodoc_skip_member_handler) 20 | 21 | 22 | extensions = [ 23 | "sphinx.ext.autodoc", 24 | "numpydoc", 25 | "m2r2", 26 | "sphinx_markdown_tables", 27 | "nbsphinx", 28 | "sphinx.ext.mathjax", 29 | "sphinx_copybutton", 30 | ] 31 | 32 | templates_path = ["_templates"] 33 | 34 | source_suffix = {".rst": "restructuredtext", ".md": "markdown"} 35 | 36 | master_doc = "index" 37 | 38 | project = "empyrical" 39 | copyright = "2016, Quantopian, Inc." 40 | author = "Quantopian, Inc." 41 | 42 | release = version 43 | language = None 44 | 45 | exclude_patterns = [] 46 | 47 | highlight_language = "python" 48 | 49 | pygments_style = "sphinx" 50 | 51 | todo_include_todos = False 52 | 53 | html_theme = "pydata_sphinx_theme" 54 | html_theme_path = pydata_sphinx_theme.get_html_theme_path() 55 | 56 | html_theme_options = { 57 | "github_url": "https://github.com/stefan-jansen/empyrical-reloaded", 58 | "twitter_url": "https://twitter.com/ml4trading", 59 | "external_links": [ 60 | {"name": "ML for Trading", "url": "https://ml4trading.io"}, 61 | {"name": "Community", "url": "https://exchange.ml4trading.io"}, 62 | ], 63 | "google_analytics_id": "UA-74956955-3", 64 | "use_edit_page_button": True, 65 | } 66 | 67 | html_context = { 68 | "github_url": "https://github.com", 69 | "github_user": "stefan-jansen", 70 | "github_repo": "empyrical-reloaded", 71 | "github_version": "main", 72 | "doc_path": "docs/source", 73 | } 74 | 75 | html_static_path = [] 76 | 77 | htmlhelp_basename = "Empyricaldoc" 78 | 79 | latex_elements = {} 80 | 81 | latex_documents = [ 82 | ( 83 | master_doc, 84 | "Empyrical.tex", 85 | "Empyrical Documentation", 86 | "Quantopian, Inc.", 87 | "manual", 88 | ) 89 | ] 90 | 91 | man_pages = [(master_doc, "empyrical", "Empyrical Documentation", [author], 1)] 92 | 93 | texinfo_documents = [ 94 | ( 95 | master_doc, 96 | "Empyrical", 97 | "Empyrical Documentation", 98 | author, 99 | "Empyrical", 100 | "One line description of project.", 101 | "Miscellaneous", 102 | ) 103 | ] 104 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "empyrical-reloaded" 3 | description = "empyrical computes performance and risk statistics commonly used in quantitative finance" 4 | readme = "README.md" 5 | 6 | authors = [ 7 | { name = "Quantopian Inc" }, 8 | { email = "pm@ml4trading.io" } 9 | ] 10 | maintainers = [ 11 | { name = "Stefan Jansen" }, 12 | { email = "pm@ml4trading.io" } 13 | ] 14 | 15 | classifiers = [ 16 | "Development Status :: 4 - Beta", 17 | "Programming Language :: Python", 18 | "Programming Language :: Python :: 3.10", 19 | "Programming Language :: Python :: 3.11", 20 | "Programming Language :: Python :: 3.12", 21 | "Programming Language :: Python :: 3.13", 22 | "License :: OSI Approved :: Apache Software License", 23 | "Intended Audience :: Science/Research", 24 | "Topic :: Scientific/Engineering", 25 | "Topic :: Scientific/Engineering :: Mathematics", 26 | "Operating System :: OS Independent" 27 | ] 28 | 29 | requires-python = ">=3.9" 30 | dynamic = ["version"] 31 | 32 | 33 | license = { file = "LICENSE" } 34 | 35 | dependencies = [ 36 | # following pandas 37 | "numpy>=1.23.5; python_version<'3.12'", 38 | "numpy>=1.26.0; python_version>='3.12'", 39 | # "numpy>=2.0; python_version>='3.12'", 40 | "pandas >=1.5.0; python_version<'3.12'", 41 | "pandas>=2.2.2; python_version>='3.12'", 42 | "bottleneck >=1.3.0", 43 | "pandas >=1.5.0", 44 | "scipy >=0.15.1", 45 | "peewee<3.17.4" # awwaiting bugfix in latest version: https://github.com/coleifer/peewee/issues/2891 46 | ] 47 | 48 | [project.urls] 49 | homepage = 'https://ml4trading.io' 50 | documentation = "https://empyrical.ml4trading.io" 51 | repository = 'https://github.com/stefan-jansen/empyrical-reloaded' 52 | 53 | [build-system] 54 | requires = [ 55 | "setuptools>=54.0.0", 56 | "setuptools_scm[toml]>=6.2", 57 | # "wheel>=0.31.0", 58 | # "numpy>=2.0rc1; python_version>='3.9'", 59 | # "oldest-supported-numpy; python_version>='3.9'" 60 | # "wheel>=0.31.0", 61 | # "numpy>=2.0rc1; python_version>='3.9'", 62 | # "oldest-supported-numpy; python_version>='3.9'" 63 | ] 64 | build-backend = "setuptools.build_meta" 65 | 66 | [project.optional-dependencies] 67 | test = [ 68 | "tox>=2.3.1", 69 | "pytest>=6.2.3", 70 | "pytest-cov>=2.11.1", 71 | "flake8>=3.9.1", 72 | "black" 73 | ] 74 | 75 | dev = [ 76 | "flake8 >=3.9.1", 77 | "black", 78 | "pre-commit >=2.12.1" 79 | ] 80 | 81 | doc = [ 82 | "Cython", 83 | "Sphinx >=1.3.2", 84 | "numpydoc >=0.5.0", 85 | "sphinx-autobuild >=0.6.0", 86 | "pydata-sphinx-theme<0.8.0", 87 | "sphinx-markdown-tables", 88 | "sphinx_copybutton", 89 | "nbsphinx", 90 | "m2r2", 91 | "lxml-html-clean" 92 | 93 | ] 94 | 95 | yfinance = [ 96 | "yfinance >=0.1.63", 97 | ] 98 | 99 | datareader = [ 100 | "pandas-datareader >=0.4" 101 | ] 102 | 103 | [tool.setuptools] 104 | include-package-data = true 105 | zip-safe = false 106 | 107 | [tool.setuptools.packages.find] 108 | where = ['src'] 109 | exclude = ['tests*'] 110 | 111 | [tool.setuptools_scm] 112 | write_to = "src/empyrical/_version.py" 113 | version_scheme = 'guess-next-dev' 114 | local_scheme = 'dirty-tag' 115 | 116 | [tool.pytest.ini_options] 117 | pythonpath = 'src' 118 | testpaths = 'tests' 119 | addopts = '-v' 120 | 121 | [tool.cibuildwheel] 122 | test-extras = "test" 123 | test-command = "pytest -n 2 --reruns 5 {package}/tests" 124 | build-verbosity = 3 125 | 126 | [tool.cibuildwheel.macos] 127 | archs = ["x86_64", "arm64", "universal2"] 128 | test-skip = ["*universal2:arm64"] 129 | 130 | [tool.cibuildwheel.linux] 131 | archs = ["auto64"] 132 | skip = "*musllinux*" 133 | 134 | [tool.black] 135 | line-length = 88 136 | target-version = ['py39', 'py310', 'py311', 'py312'] 137 | exclude = ''' 138 | ( 139 | asv_bench/env 140 | | \.egg 141 | | \.git 142 | | \.hg 143 | | _build 144 | | build 145 | | dist 146 | | setup.py 147 | ) 148 | ''' 149 | 150 | [tool.tox] 151 | legacy_tox_ini = """ 152 | [tox] 153 | 154 | envlist = 155 | py310-pandas{15,20,21,22}-numpy1 156 | py311-pandas{15,20,21,22}-numpy1 157 | py312-pandas{15,20,21,22}-numpy1 158 | py310-pandas222-numpy2{0,1,2} 159 | py311-pandas222-numpy2{0,1,2} 160 | py312-pandas222-numpy2{0,1,2} 161 | py313-pandas222-numpy2{1,2} 162 | 163 | isolated_build = True 164 | skip_missing_interpreters = True 165 | minversion = 3.23.0 166 | 167 | [gh-actions] 168 | python = 169 | 3.10: py310 170 | 3.11: py311 171 | 3.12: py312 172 | 3.13: py313 173 | 174 | [testenv] 175 | usedevelop = True 176 | setenv = 177 | MPLBACKEND = Agg 178 | 179 | changedir = tmp 180 | extras = test 181 | deps = 182 | pandas15: pandas>=1.5.0,<1.6 183 | pandas20: pandas>=2.0,<2.1 184 | pandas21: pandas>=2.1,<2.2 185 | pandas22: pandas>=2.2,<2.3 186 | pandas222: pandas>=2.2.2,<2.3 187 | numpy1: numpy>=1.23.5,<2.0 188 | numpy20: numpy>=2.0,<2.1 189 | numpy21: numpy>=2.1,<2.2 190 | numpy22: numpy>=2.2,<2.3 191 | 192 | 193 | commands = 194 | pytest --cov={toxinidir}/src --cov-report term --cov-report=xml --cov-report=html:htmlcov {toxinidir}/tests 195 | """ 196 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # empyrical documentation build configuration file, created by 5 | # sphinx-quickstart on Thu Jan 25 22:49:14 2018. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | # import os 21 | # import sys 22 | # sys.path.insert(0, os.path.abspath('.')) 23 | import empyrical 24 | 25 | 26 | # -- General configuration ------------------------------------------------ 27 | 28 | # If your documentation needs a minimal Sphinx version, state it here. 29 | # 30 | # needs_sphinx = '1.0' 31 | 32 | # Add any Sphinx extension module names here, as strings. They can be 33 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 34 | # ones. 35 | extensions = [ 36 | "sphinx.ext.autodoc", 37 | "sphinx.ext.githubpages", 38 | "sphinx.ext.napoleon", 39 | ] 40 | # Add any paths that contain templates here, relative to this directory. 41 | templates_path = ["_templates"] 42 | 43 | # The suffix(es) of source filenames. 44 | # You can specify multiple suffix as a list of string: 45 | # 46 | # source_suffix = ['.rst', '.md'] 47 | source_suffix = ".rst" 48 | 49 | # The master toctree document. 50 | master_doc = "index" 51 | 52 | # General information about the project. 53 | project = "empyrical" 54 | copyright = "2018, Quantopian" 55 | author = "Quantopian" 56 | 57 | # The version info for the project you're documenting, acts as replacement for 58 | # |version| and |release|, also used in various other places throughout the 59 | # built documents. 60 | # 61 | # The short X.Y version. 62 | ver = empyrical.__version__ 63 | version = ver[: ver.find("+")] # get only the main part of the version 64 | # The full version, including alpha/beta/rc tags. 65 | release = version 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # 70 | # This is also used if you do content translation via gettext catalogs. 71 | # Usually you set "language" from the command line for these cases. 72 | language = None 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This patterns also effect to html_static_path and html_extra_path 77 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "**tests**"] 78 | 79 | # The name of the Pygments (syntax highlighting) style to use. 80 | pygments_style = "sphinx" 81 | 82 | # If true, `todo` and `todoList` produce output, else they produce nothing. 83 | todo_include_todos = False 84 | 85 | 86 | # -- Options for HTML output ---------------------------------------------- 87 | 88 | # The theme to use for HTML and HTML Help pages. See the documentation for 89 | # a list of builtin themes. 90 | # 91 | html_theme = "default" 92 | 93 | # Theme options are theme-specific and customize the look and feel of a theme 94 | # further. For a list of options available for each theme, see the 95 | # documentation. 96 | # 97 | # html_theme_options = {} 98 | 99 | # Add any paths that contain custom static files (such as style sheets) here, 100 | # relative to this directory. They are copied after the builtin static files, 101 | # so a file named "default.css" will overwrite the builtin "default.css". 102 | html_static_path = ["_static"] 103 | 104 | # -- Options for HTMLHelp output ------------------------------------------ 105 | 106 | # Output file base name for HTML help builder. 107 | htmlhelp_basename = "empyricaldoc" 108 | 109 | 110 | # -- Options for LaTeX output --------------------------------------------- 111 | 112 | latex_elements = { 113 | # The paper size ('letterpaper' or 'a4paper'). 114 | # 115 | # 'papersize': 'letterpaper', 116 | # The font size ('10pt', '11pt' or '12pt'). 117 | # 118 | # 'pointsize': '10pt', 119 | # Additional stuff for the LaTeX preamble. 120 | # 121 | # 'preamble': '', 122 | # Latex figure (float) alignment 123 | # 124 | # 'figure_align': 'htbp', 125 | } 126 | 127 | # Grouping the document tree into LaTeX files. List of tuples 128 | # (source start file, target name, title, 129 | # author, documentclass [howto, manual, or own class]). 130 | latex_documents = [ 131 | ( 132 | master_doc, 133 | "empyrical.tex", 134 | "empyrical Documentation", 135 | "Quontopian", 136 | "manual", 137 | ), 138 | ] 139 | 140 | 141 | # -- Options for manual page output --------------------------------------- 142 | 143 | # One entry per manual page. List of tuples 144 | # (source start file, name, description, authors, manual section). 145 | man_pages = [(master_doc, "empyrical", "empyrical Documentation", [author], 1)] 146 | 147 | 148 | # -- Options for Texinfo output ------------------------------------------- 149 | 150 | # Grouping the document tree into Texinfo files. List of tuples 151 | # (source start file, target name, title, author, 152 | # dir menu entry, description, category) 153 | texinfo_documents = [ 154 | ( 155 | master_doc, 156 | "empyrical", 157 | "empyrical Documentation", 158 | author, 159 | "empyrical", 160 | "One line description of project.", 161 | "Miscellaneous", 162 | ), 163 | ] 164 | -------------------------------------------------------------------------------- /src/empyrical/perf_attrib.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | import pandas as pd 3 | 4 | 5 | def perf_attrib(returns, positions, factor_returns, factor_loadings): 6 | """ 7 | Attributes the performance of a returns stream to a set of risk factors. 8 | 9 | Performance attribution determines how much each risk factor, e.g., 10 | momentum, the technology sector, etc., contributed to total returns, as 11 | well as the daily exposure to each of the risk factors. The returns that 12 | can be attributed to one of the given risk factors are the 13 | `common_returns`, and the returns that _cannot_ be attributed to a risk 14 | factor are the `specific_returns`. The `common_returns` and 15 | `specific_returns` summed together will always equal the total returns. 16 | 17 | Parameters 18 | ---------- 19 | returns : pd.Series 20 | Returns for each day in the date range. 21 | - Example: 22 | 2017-01-01 -0.017098 23 | 2017-01-02 0.002683 24 | 2017-01-03 -0.008669 25 | 26 | positions: pd.Series 27 | Daily holdings in percentages, indexed by date. 28 | - Examples: 29 | dt ticker 30 | 2017-01-01 AAPL 0.417582 31 | TLT 0.010989 32 | XOM 0.571429 33 | 2017-01-02 AAPL 0.202381 34 | TLT 0.535714 35 | XOM 0.261905 36 | 37 | factor_returns : pd.DataFrame 38 | Returns by factor, with date as index and factors as columns 39 | - Example: 40 | momentum reversal 41 | 2017-01-01 0.002779 -0.005453 42 | 2017-01-02 0.001096 0.010290 43 | 44 | factor_loadings : pd.DataFrame 45 | Factor loadings for all days in the date range, with date and ticker as 46 | index, and factors as columns. 47 | - Example: 48 | momentum reversal 49 | dt ticker 50 | 2017-01-01 AAPL -1.592914 0.852830 51 | TLT 0.184864 0.895534 52 | XOM 0.993160 1.149353 53 | 2017-01-02 AAPL -0.140009 -0.524952 54 | TLT -1.066978 0.185435 55 | XOM -1.798401 0.761549 56 | 57 | Returns 58 | ------- 59 | tuple of (risk_exposures_portfolio, perf_attribution) 60 | 61 | risk_exposures_portfolio : pd.DataFrame 62 | df indexed by datetime, with factors as columns 63 | - Example: 64 | momentum reversal 65 | dt 66 | 2017-01-01 -0.238655 0.077123 67 | 2017-01-02 0.821872 1.520515 68 | 69 | perf_attribution : pd.DataFrame 70 | df with factors, common returns, and specific returns as columns, 71 | and datetimes as index 72 | - Example: 73 | momentum reversal common_returns specific_returns 74 | dt 75 | 2017-01-01 0.249087 0.935925 1.185012 1.185012 76 | 2017-01-02 -0.003194 -0.400786 -0.403980 -0.403980 77 | 78 | Note 79 | ---- 80 | See https://en.wikipedia.org/wiki/Performance_attribution for more details. 81 | """ 82 | 83 | # track freq info to reassign to return values 84 | freq = returns.index.freq 85 | 86 | # Make risk data match time range of returns 87 | start = returns.index[0] 88 | end = returns.index[-1] 89 | factor_returns = factor_returns.loc[start:end] 90 | factor_loadings = factor_loadings.loc[start:end] 91 | 92 | factor_loadings.index = factor_loadings.index.set_names(["dt", "ticker"]) 93 | 94 | positions = positions.copy() 95 | positions.index = positions.index.set_names(["dt", "ticker"]) 96 | 97 | risk_exposures_portfolio = compute_exposures(positions, factor_loadings) 98 | if freq is not None: 99 | risk_exposures_portfolio = risk_exposures_portfolio.asfreq(freq) 100 | 101 | perf_attrib_by_factor = risk_exposures_portfolio.multiply(factor_returns) 102 | common_returns = perf_attrib_by_factor.sum(axis="columns") 103 | 104 | tilt_exposure = risk_exposures_portfolio.mean() 105 | tilt_returns = factor_returns.multiply(tilt_exposure).sum(axis="columns") 106 | timing_returns = common_returns - tilt_returns 107 | specific_returns = returns - common_returns 108 | 109 | returns_df = pd.DataFrame( 110 | OrderedDict( 111 | [ 112 | ("total_returns", returns), 113 | ("common_returns", common_returns), 114 | ("specific_returns", specific_returns), 115 | ("tilt_returns", tilt_returns), 116 | ("timing_returns", timing_returns), 117 | ] 118 | ) 119 | ) 120 | 121 | return ( 122 | risk_exposures_portfolio, 123 | pd.concat([perf_attrib_by_factor, returns_df], axis="columns"), 124 | ) 125 | 126 | 127 | def compute_exposures(positions, factor_loadings): 128 | """ 129 | Compute daily risk factor exposures. 130 | 131 | Parameters 132 | ---------- 133 | positions: pd.Series 134 | A series of holdings as percentages indexed by date and ticker. 135 | - Examples: 136 | dt ticker 137 | 2017-01-01 AAPL 0.417582 138 | TLT 0.010989 139 | XOM 0.571429 140 | 2017-01-02 AAPL 0.202381 141 | TLT 0.535714 142 | XOM 0.261905 143 | 144 | factor_loadings : pd.DataFrame 145 | Factor loadings for all days in the date range, with date and ticker as 146 | index, and factors as columns. 147 | - Example: 148 | momentum reversal 149 | dt ticker 150 | 2017-01-01 AAPL -1.592914 0.852830 151 | TLT 0.184864 0.895534 152 | XOM 0.993160 1.149353 153 | 2017-01-02 AAPL -0.140009 -0.524952 154 | TLT -1.066978 0.185435 155 | XOM -1.798401 0.761549 156 | 157 | Returns 158 | ------- 159 | risk_exposures_portfolio : pd.DataFrame 160 | df indexed by datetime, with factors as columns 161 | - Example: 162 | momentum reversal 163 | dt 164 | 2017-01-01 -0.238655 0.077123 165 | 2017-01-02 0.821872 1.520515 166 | """ 167 | risk_exposures = factor_loadings.multiply(positions, axis="rows") 168 | return risk_exposures.groupby(level="dt").sum() 169 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 | ![PyPI](https://img.shields.io/pypi/v/empyrical-reloaded) 8 | ![PyPI - Downloads](https://img.shields.io/pypi/dm/empyrical-reloaded) 9 | 10 | [![Conda Version](https://img.shields.io/conda/vn/conda-forge/empyrical-reloaded.svg)](https://anaconda.org/conda-forge/empyrical-reloaded) 11 | [![Conda Downloads](https://img.shields.io/conda/dn/conda-forge/empyrical-reloaded.svg)](https://anaconda.org/conda-forge/empyrical-reloaded) 12 | 13 | [![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) 14 | [![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) 15 | [![CI Tests](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/unit_tests.yml) 16 | 17 | Common financial return and risk metrics in Python. 18 | 19 | ## Installation 20 | 21 | empyrical requires Python 3.10+. You can install it using `pip`: 22 | 23 | ```bash 24 | pip install empyrical-reloaded 25 | ``` 26 | 27 | or `conda` from the `conda-forge` channel 28 | 29 | ```bash 30 | conda install empyrical-reloaded -c conda-forge 31 | ``` 32 | 33 | empyrical requires and installs the following packages while executing the above commands: 34 | 35 | - numpy>=1.23.5 36 | - pandas>=1.3.0 37 | - scipy>=0.15.1 38 | 39 | > Note that Numpy>=2.0 requires pandas>=2.2.2. If you are using an older version of pandas, you may need to upgrade 40 | > accordingly, otherwise you may encounter compatibility issues. 41 | 42 | Optional dependencies include [yfinance](https://github.com/ranaroussi/yfinance) to download price data 43 | from [Yahoo! Finance](https://finance.yahoo.com/) 44 | and [pandas-datareader](https://pandas-datareader.readthedocs.io/en/latest/) to 45 | access [Fama-French](https://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html) risk factors and FRED 46 | treasury yields. 47 | 48 | > Note that `pandas-datareader` is not compatible with Python>=3.12. 49 | 50 | To install the optional dependencies, use: 51 | 52 | ```bash 53 | pip install empyrical-reloaded[yfinance] 54 | ``` 55 | 56 | or 57 | 58 | ```bash 59 | pip install empyrical-reloaded[datreader] 60 | ``` 61 | 62 | or 63 | 64 | ```bash 65 | pip install empyrical-reloaded[yfinance,datreader] 66 | ``` 67 | 68 | ## Usage 69 | 70 | ### Simple Statistics 71 | 72 | Empyrical computes basic metrics from returns and volatility to alpha and beta, Value at Risk, and Sharpe or Sortino 73 | ratios. 74 | 75 | ```python 76 | import numpy as np 77 | from empyrical import max_drawdown, alpha_beta 78 | 79 | returns = np.array([.01, .02, .03, -.4, -.06, -.02]) 80 | benchmark_returns = np.array([.02, .02, .03, -.35, -.05, -.01]) 81 | 82 | # calculate the max drawdown 83 | max_drawdown(returns) 84 | 85 | # calculate alpha and beta 86 | alpha, beta = alpha_beta(returns, benchmark_returns) 87 | ``` 88 | 89 | ### Rolling Measures 90 | 91 | Empyrical also aggregates return and risk metrics for rolling windows: 92 | 93 | ```python 94 | import numpy as np 95 | from empyrical import roll_max_drawdown 96 | 97 | returns = np.array([.01, .02, .03, -.4, -.06, -.02]) 98 | 99 | # calculate the rolling max drawdown 100 | roll_max_drawdown(returns, window=3) 101 | ``` 102 | 103 | ### Pandas Support 104 | 105 | Empyrical also works with both [NumPy](https://numpy.org/) arrays and [Pandas](https://pandas.pydata.org/) data 106 | structures: 107 | 108 | ```python 109 | import pandas as pd 110 | from empyrical import roll_up_capture, capture 111 | 112 | returns = pd.Series([.01, .02, .03, -.4, -.06, -.02]) 113 | factor_returns = pd.Series([.02, .01, .03, -.01, -.02, .02]) 114 | 115 | # calculate a capture ratio 116 | capture(returns, factor_returns) 117 | -0.147387712263491 118 | 119 | ``` 120 | 121 | ### Fama-French Risk Factors 122 | 123 | Empyrical downloads Fama-French risk factors from 1970 onward: 124 | 125 | > Note: requires optional dependency `pandas-datareader` - see installation instructions above.gst 126 | 127 | ```python 128 | import pandas as pd 129 | import empyrical as emp 130 | 131 | risk_factors = emp.utils.get_fama_french() 132 | 133 | pd.concat([risk_factors.head(), risk_factors.tail()]) 134 | 135 | Mkt - RF 136 | SMB 137 | HML 138 | RF 139 | Mom 140 | Date 141 | 1970 - 01 - 02 142 | 00: 00:00 + 00: 00 143 | 0.0118 144 | 0.0129 145 | 0.0101 146 | 0.00029 - 0.0340 147 | 1970 - 01 - 05 148 | 00: 00:00 + 00: 00 149 | 0.0059 150 | 0.0067 151 | 0.0072 152 | 0.00029 - 0.0153 153 | 1970 - 01 - 06 154 | 00: 00:00 + 00: 00 - 0.0074 155 | 0.0010 156 | 0.0021 157 | 0.00029 158 | 0.0038 159 | 1970 - 01 - 07 160 | 00: 00:00 + 00: 00 - 0.0015 161 | 0.0040 - 0.0033 162 | 0.00029 163 | 0.0011 164 | 1970 - 01 - 0 165 | 8 166 | 00: 00:00 + 00: 00 167 | 0.0004 168 | 0.0018 - 0.0017 169 | 0.00029 170 | 0.0033 171 | 2024 - 03 - 22 172 | 00: 00:00 + 00: 00 - 0.0023 - 0.0087 - 0.0053 173 | 0.00021 174 | 0.0043 175 | 2024 - 03 - 25 176 | 00: 00:00 + 00: 00 - 0.0026 - 0.0024 177 | 0.0088 178 | 0.00021 - 0.0034 179 | 2024 - 03 - 26 180 | 00: 00:00 + 00: 00 - 0.0026 181 | 0.0009 - 0.0013 182 | 0.00021 183 | 0.0009 184 | 2024 - 03 - 27 185 | 00: 00:00 + 00: 00 186 | 0.0088 187 | 0.0104 188 | 0.0091 189 | 0.00021 - 0.0134 190 | 2024 - 03 - 28 191 | 00: 00:00 + 00: 00 192 | 0.0010 193 | 0.0029 194 | 0.0048 195 | 0.00021 - 0.0044 196 | ``` 197 | 198 | ### Asset Prices and Benchmark Returns 199 | 200 | Empyrical use [yfinance](https://github.com/ranaroussi/yfinance) to download price data 201 | from [Yahoo! Finance](https://finance.yahoo.com/). To obtain the S&P returns since 1950, use: 202 | 203 | > Note: requires optional dependency `yfinance` - see installation instructions above. 204 | 205 | ```python 206 | import empyrical as emp 207 | 208 | symbol = '^GSPC' 209 | returns = emp.utils.get_symbol_returns_from_yahoo(symbol, 210 | start='1950-01-01') 211 | 212 | import seaborn as sns # requires separate installation 213 | import matplotlib.pyplot as plt # requires separate installation 214 | 215 | fig, axes = plt.subplots(ncols=2, figsize=(14, 5)) 216 | 217 | with sns.axes_style('whitegrid'): 218 | returns.plot(ax=axes[0], rot=0, title='Time Series', legend=False) 219 | sns.histplot(returns, ax=axes[1], legend=False) 220 | axes[1].set_title('Histogram') 221 | sns.despine() 222 | plt.tight_layout() 223 | plt.suptitle('Daily S&P 500 Returns') 224 | ``` 225 | 226 | 227 | 228 | 229 | 230 | ### Documentation 231 | 232 | See the [documentation](https://empyrical.ml4trading.io) for details on the API. 233 | 234 | ## Support 235 | 236 | Please [open an issue](https://github.com/stefan-jansen/empyrical-reloaded/issues/new) for support. 237 | 238 | ## Contributing 239 | 240 | Please contribute using [Github Flow](https://guides.github.com/introduction/flow/). Create a branch, add commits, 241 | and [open a pull request](https://github.com/stefan-jansen/empyrical-reloaded/compare/). 242 | 243 | ## Testing 244 | 245 | - install requirements 246 | - "pytest>=6.2.0", 247 | 248 | ```bash 249 | pytest tests 250 | ``` 251 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SPHINXPROJ = empyrical 8 | SOURCEDIR = . 9 | BUILDDIR = build 10 | 11 | # User-friendly check for sphinx-build 12 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 13 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 14 | endif 15 | 16 | # Internal variables. 17 | PAPEROPT_a4 = -D latex_paper_size=a4 18 | PAPEROPT_letter = -D latex_paper_size=letter 19 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 20 | # the i18n builder cannot share the environment and doctrees with the others 21 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source 22 | 23 | .PHONY: help 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | .PHONY: clean 52 | clean: 53 | rm -rf $(BUILDDIR)/* 54 | 55 | .PHONY: html 56 | html: 57 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 58 | @echo 59 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 60 | 61 | .PHONY: dirhtml 62 | dirhtml: 63 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 64 | @echo 65 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 66 | 67 | .PHONY: singlehtml 68 | singlehtml: 69 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 70 | @echo 71 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 72 | 73 | .PHONY: pickle 74 | pickle: 75 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 76 | @echo 77 | @echo "Build finished; now you can process the pickle files." 78 | 79 | .PHONY: json 80 | json: 81 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 82 | @echo 83 | @echo "Build finished; now you can process the JSON files." 84 | 85 | .PHONY: htmlhelp 86 | htmlhelp: 87 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 88 | @echo 89 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 90 | ".hhp project file in $(BUILDDIR)/htmlhelp." 91 | 92 | .PHONY: qthelp 93 | qthelp: 94 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 95 | @echo 96 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 97 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 98 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/Qfactor.qhcp" 99 | @echo "To view the help file:" 100 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/Qfactor.qhc" 101 | 102 | .PHONY: applehelp 103 | applehelp: 104 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 105 | @echo 106 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 107 | @echo "N.B. You won't be able to view it unless you put it in" \ 108 | "~/Library/Documentation/Help or install it in your application" \ 109 | "bundle." 110 | 111 | .PHONY: devhelp 112 | devhelp: 113 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 114 | @echo 115 | @echo "Build finished." 116 | @echo "To view the help file:" 117 | @echo "# mkdir -p $$HOME/.local/share/devhelp/Qfactor" 118 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/Qfactor" 119 | @echo "# devhelp" 120 | 121 | .PHONY: epub 122 | epub: 123 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 124 | @echo 125 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 126 | 127 | .PHONY: latex 128 | latex: 129 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 130 | @echo 131 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 132 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 133 | "(use \`make latexpdf' here to do that automatically)." 134 | 135 | .PHONY: latexpdf 136 | latexpdf: 137 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 138 | @echo "Running LaTeX files through pdflatex..." 139 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 140 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 141 | 142 | .PHONY: latexpdfja 143 | latexpdfja: 144 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 145 | @echo "Running LaTeX files through platex and dvipdfmx..." 146 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 147 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 148 | 149 | .PHONY: text 150 | text: 151 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 152 | @echo 153 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 154 | 155 | .PHONY: man 156 | man: 157 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 158 | @echo 159 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 160 | 161 | .PHONY: texinfo 162 | texinfo: 163 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 164 | @echo 165 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 166 | @echo "Run \`make' in that directory to run these through makeinfo" \ 167 | "(use \`make info' here to do that automatically)." 168 | 169 | .PHONY: info 170 | info: 171 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 172 | @echo "Running Texinfo files through makeinfo..." 173 | make -C $(BUILDDIR)/texinfo info 174 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 175 | 176 | .PHONY: gettext 177 | gettext: 178 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 179 | @echo 180 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 181 | 182 | .PHONY: changes 183 | changes: 184 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 185 | @echo 186 | @echo "The overview file is in $(BUILDDIR)/changes." 187 | 188 | .PHONY: linkcheck 189 | linkcheck: 190 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 191 | @echo 192 | @echo "Link check complete; look for any errors in the above output " \ 193 | "or in $(BUILDDIR)/linkcheck/output.txt." 194 | 195 | .PHONY: doctest 196 | doctest: 197 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 198 | @echo "Testing of doctests in the sources finished, look at the " \ 199 | "results in $(BUILDDIR)/doctest/output.txt." 200 | 201 | .PHONY: coverage 202 | coverage: 203 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 204 | @echo "Testing of coverage in the sources finished, look at the " \ 205 | "results in $(BUILDDIR)/coverage/python.txt." 206 | 207 | .PHONY: xml 208 | xml: 209 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 210 | @echo 211 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 212 | 213 | .PHONY: pseudoxml 214 | pseudoxml: 215 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 216 | @echo 217 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 218 | -------------------------------------------------------------------------------- /tests/test_perf_attrib.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from pathlib import Path 4 | from packaging.version import Version 5 | from empyrical.perf_attrib import perf_attrib 6 | 7 | TEST_DATA = Path(__file__).parent / "test_data" 8 | 9 | 10 | class TestPerfAttrib: 11 | def test_perf_attrib_simple(self): 12 | start_date = "2017-01-01" 13 | periods = 2 14 | dts = pd.date_range(start_date, periods=periods, name="dt") 15 | 16 | tickers = ["stock1", "stock2"] 17 | styles = ["risk_factor1", "risk_factor2"] 18 | 19 | returns = pd.Series(data=[0.1, 0.1], index=dts) 20 | 21 | factor_returns = pd.DataFrame( 22 | columns=styles, 23 | index=dts, 24 | data={"risk_factor1": [0.1, 0.1], "risk_factor2": [0.1, 0.1]}, 25 | ) 26 | 27 | index = pd.MultiIndex.from_product([dts, tickers], names=["dt", "ticker"]) 28 | 29 | positions = pd.Series( 30 | [ 31 | 0.2857142857142857, 32 | 0.7142857142857143, 33 | 0.2857142857142857, 34 | 0.7142857142857143, 35 | ], 36 | index=index, 37 | ) 38 | 39 | factor_loadings = pd.DataFrame( 40 | columns=styles, 41 | index=index, 42 | data={ 43 | "risk_factor1": [0.25, 0.25, 0.25, 0.25], 44 | "risk_factor2": [0.25, 0.25, 0.25, 0.25], 45 | }, 46 | ) 47 | 48 | expected_perf_attrib_output = pd.DataFrame( 49 | index=dts, 50 | columns=[ 51 | "risk_factor1", 52 | "risk_factor2", 53 | "total_returns", 54 | "common_returns", 55 | "specific_returns", 56 | "tilt_returns", 57 | "timing_returns", 58 | ], 59 | data={ 60 | "risk_factor1": [0.025, 0.025], 61 | "risk_factor2": [0.025, 0.025], 62 | "common_returns": [0.05, 0.05], 63 | "specific_returns": [0.05, 0.05], 64 | "tilt_returns": [0.05, 0.05], 65 | "timing_returns": [0.0, 0.0], 66 | "total_returns": returns, 67 | }, 68 | ) 69 | 70 | expected_exposures_portfolio = pd.DataFrame( 71 | index=dts, 72 | columns=["risk_factor1", "risk_factor2"], 73 | data={"risk_factor1": [0.25, 0.25], "risk_factor2": [0.25, 0.25]}, 74 | ) 75 | 76 | exposures_portfolio, perf_attrib_output = perf_attrib( 77 | returns, positions, factor_returns, factor_loadings 78 | ) 79 | 80 | pd.testing.assert_frame_equal(expected_perf_attrib_output, perf_attrib_output) 81 | 82 | pd.testing.assert_frame_equal(expected_exposures_portfolio, exposures_portfolio) 83 | 84 | # test long and short positions 85 | positions = pd.Series([0.5, -0.5, 0.5, -0.5], index=index) 86 | 87 | exposures_portfolio, perf_attrib_output = perf_attrib( 88 | returns, positions, factor_returns, factor_loadings 89 | ) 90 | 91 | expected_perf_attrib_output = pd.DataFrame( 92 | index=dts, 93 | columns=[ 94 | "risk_factor1", 95 | "risk_factor2", 96 | "total_returns", 97 | "common_returns", 98 | "specific_returns", 99 | "tilt_returns", 100 | "timing_returns", 101 | ], 102 | data={ 103 | "risk_factor1": [0.0, 0.0], 104 | "risk_factor2": [0.0, 0.0], 105 | "common_returns": [0.0, 0.0], 106 | "specific_returns": [0.1, 0.1], 107 | "tilt_returns": [0.0, 0.0], 108 | "timing_returns": [0.0, 0.0], 109 | "total_returns": returns, 110 | }, 111 | ) 112 | 113 | expected_exposures_portfolio = pd.DataFrame( 114 | index=dts, 115 | columns=["risk_factor1", "risk_factor2"], 116 | data={"risk_factor1": [0.0, 0.0], "risk_factor2": [0.0, 0.0]}, 117 | ) 118 | 119 | pd.testing.assert_frame_equal(expected_perf_attrib_output, perf_attrib_output) 120 | 121 | pd.testing.assert_frame_equal(expected_exposures_portfolio, exposures_portfolio) 122 | 123 | # test long and short positions with tilt exposure 124 | positions = pd.Series([1.0, -0.5, 1.0, -0.5], index=index) 125 | 126 | exposures_portfolio, perf_attrib_output = perf_attrib( 127 | returns, positions, factor_returns, factor_loadings 128 | ) 129 | 130 | expected_perf_attrib_output = pd.DataFrame( 131 | index=dts, 132 | columns=[ 133 | "risk_factor1", 134 | "risk_factor2", 135 | "total_returns", 136 | "common_returns", 137 | "specific_returns", 138 | "tilt_returns", 139 | "timing_returns", 140 | ], 141 | data={ 142 | "risk_factor1": [0.0125, 0.0125], 143 | "risk_factor2": [0.0125, 0.0125], 144 | "common_returns": [0.025, 0.025], 145 | "specific_returns": [0.075, 0.075], 146 | "tilt_returns": [0.025, 0.025], 147 | "timing_returns": [0.0, 0.0], 148 | "total_returns": returns, 149 | }, 150 | ) 151 | 152 | expected_exposures_portfolio = pd.DataFrame( 153 | index=dts, 154 | columns=["risk_factor1", "risk_factor2"], 155 | data={ 156 | "risk_factor1": [0.125, 0.125], 157 | "risk_factor2": [0.125, 0.125], 158 | }, 159 | ) 160 | 161 | pd.testing.assert_frame_equal(expected_perf_attrib_output, perf_attrib_output) 162 | 163 | pd.testing.assert_frame_equal(expected_exposures_portfolio, exposures_portfolio) 164 | 165 | def test_perf_attrib_regression(self): 166 | positions = pd.read_csv( 167 | TEST_DATA / "positions.csv", 168 | index_col=0, 169 | parse_dates=True, 170 | ) 171 | 172 | positions.columns = [ 173 | int(col) if col != "cash" else col for col in positions.columns 174 | ] 175 | 176 | positions = positions.divide(positions.sum(axis="columns"), axis="rows") 177 | positions = positions.drop("cash", axis="columns").stack() 178 | 179 | returns = pd.read_csv( 180 | TEST_DATA / "returns.csv", 181 | index_col=0, 182 | parse_dates=True, 183 | header=None, 184 | ).squeeze("columns") 185 | 186 | if Version(pd.__version__) >= Version("2.0.0"): 187 | factor_loadings = pd.read_csv( 188 | TEST_DATA / "factor_loadings.csv", 189 | index_col=[0, 1], 190 | parse_dates=True, 191 | date_format="%Y-%m-%d", 192 | ) 193 | else: 194 | factor_loadings = pd.read_csv( 195 | TEST_DATA / "factor_loadings.csv", 196 | index_col=[0, 1], 197 | parse_dates=True, 198 | ) 199 | 200 | factor_returns = pd.read_csv( 201 | TEST_DATA / "factor_returns.csv", 202 | index_col=0, 203 | parse_dates=True, 204 | ) 205 | 206 | residuals = pd.read_csv( 207 | TEST_DATA / "residuals.csv", 208 | index_col=0, 209 | parse_dates=True, 210 | ) 211 | 212 | residuals.columns = [int(col) for col in residuals.columns] 213 | 214 | intercepts = pd.read_csv( 215 | TEST_DATA / "intercepts.csv", 216 | index_col=0, 217 | header=None, 218 | ).squeeze("columns") 219 | 220 | risk_exposures_portfolio, perf_attrib_output = perf_attrib( 221 | returns, 222 | positions, 223 | factor_returns, 224 | factor_loadings, 225 | ) 226 | 227 | specific_returns = perf_attrib_output["specific_returns"] 228 | common_returns = perf_attrib_output["common_returns"] 229 | combined_returns = specific_returns + common_returns 230 | 231 | # since all returns are factor returns, common returns should be 232 | # equivalent to total returns, and specific returns should be 0 233 | pd.testing.assert_series_equal(returns, common_returns, check_names=False) 234 | 235 | assert np.isclose(specific_returns, 0).all() 236 | 237 | # specific and common returns combined should equal total returns 238 | pd.testing.assert_series_equal(returns, combined_returns, check_names=False) 239 | 240 | # check that residuals + intercepts = specific returns 241 | assert np.isclose((residuals + intercepts), 0).all() 242 | 243 | # check that exposure * factor returns = common returns 244 | expected_common_returns = risk_exposures_portfolio.multiply( 245 | factor_returns, axis="rows" 246 | ).sum(axis="columns") 247 | 248 | pd.testing.assert_series_equal( 249 | expected_common_returns, common_returns, check_names=False 250 | ) 251 | 252 | # since factor loadings are ones, portfolio risk exposures 253 | # should be ones 254 | pd.testing.assert_frame_equal( 255 | risk_exposures_portfolio, 256 | pd.DataFrame( 257 | np.ones_like(risk_exposures_portfolio), 258 | index=risk_exposures_portfolio.index, 259 | columns=risk_exposures_portfolio.columns, 260 | ), 261 | ) 262 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import pandas as pd 3 | import numpy as np 4 | from packaging.version import Version 5 | 6 | PANDAS22 = Version(pd.__version__) >= Version("2.2.0") 7 | monthly = "ME" if PANDAS22 else "M" 8 | annual = "YE" if PANDAS22 else "A" 9 | 10 | 11 | @pytest.fixture(scope="function") 12 | def set_helpers(request): 13 | rand = np.random.RandomState(1337) 14 | request.cls.ser_length = 120 15 | request.cls.window = 12 16 | 17 | request.cls.returns = pd.Series( 18 | rand.randn(1, 120)[0] / 100.0, 19 | index=pd.date_range("2000-1-30", periods=120, freq=monthly), 20 | ) 21 | 22 | request.cls.factor_returns = pd.Series( 23 | rand.randn(1, 120)[0] / 100.0, 24 | index=pd.date_range("2000-1-30", periods=120, freq=monthly), 25 | ) 26 | 27 | 28 | @pytest.fixture(scope="session") 29 | def input_data(): 30 | simple_benchmark = pd.Series( 31 | np.array([0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0, 1.0, 0.0]) / 100, 32 | index=pd.date_range("2000-1-30", periods=9, freq="D"), 33 | ) 34 | 35 | rand = np.random.RandomState(1337) 36 | 37 | noise = pd.Series( 38 | rand.normal(0, 0.001, 1000), 39 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 40 | ) 41 | 42 | inv_noise = noise.multiply(-1) 43 | 44 | noise_uniform = pd.Series( 45 | rand.uniform(-0.01, 0.01, 1000), 46 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 47 | ) 48 | 49 | random_100k = pd.Series(rand.randn(100_000)) 50 | mixed_returns = pd.Series( 51 | np.array([np.nan, 1.0, 10.0, -4.0, 2.0, 3.0, 2.0, 1.0, -10.0]) / 100, 52 | index=pd.date_range("2000-1-30", periods=9, freq="D"), 53 | ) 54 | 55 | one = [ 56 | -0.00171614, 57 | 0.01322056, 58 | 0.03063862, 59 | -0.01422057, 60 | -0.00489779, 61 | 0.01268925, 62 | -0.03357711, 63 | 0.01797036, 64 | ] 65 | 66 | two = [ 67 | 0.01846232, 68 | 0.00793951, 69 | -0.01448395, 70 | 0.00422537, 71 | -0.00339611, 72 | 0.03756813, 73 | 0.0151531, 74 | 0.03549769, 75 | ] 76 | 77 | # Sparse noise, same as noise but with np.nan sprinkled in 78 | replace_nan = rand.choice(noise.index.tolist(), rand.randint(1, 10)) 79 | sparse_noise = noise.replace(replace_nan, np.nan) 80 | 81 | # Flat line tz 82 | flat_line_1_tz = pd.Series( 83 | np.linspace(0.01, 0.01, num=1000), 84 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 85 | ) 86 | 87 | # Sparse flat line at 0.01 88 | # replace_nan = rand.choice(noise.index.tolist(), rand.randint(1, 10)) 89 | sparse_flat_line_1_tz = flat_line_1_tz.replace(replace_nan, np.nan) 90 | 91 | df_index_simple = pd.date_range("2000-1-30", periods=8, freq="D") 92 | df_index_week = pd.date_range("2000-1-30", periods=8, freq="W") 93 | df_index_month = pd.date_range("2000-1-30", periods=8, freq=monthly) 94 | 95 | df_week = pd.DataFrame( 96 | { 97 | "one": pd.Series(one, index=df_index_week), 98 | "two": pd.Series(two, index=df_index_week), 99 | } 100 | ) 101 | 102 | df_month = pd.DataFrame( 103 | { 104 | "one": pd.Series(one, index=df_index_month), 105 | "two": pd.Series(two, index=df_index_month), 106 | } 107 | ) 108 | 109 | df_simple = pd.DataFrame( 110 | { 111 | "one": pd.Series(one, index=df_index_simple), 112 | "two": pd.Series(two, index=df_index_simple), 113 | } 114 | ) 115 | 116 | df_week = pd.DataFrame( 117 | { 118 | "one": pd.Series(one, index=df_index_week), 119 | "two": pd.Series(two, index=df_index_week), 120 | } 121 | ) 122 | 123 | df_month = pd.DataFrame( 124 | { 125 | "one": pd.Series(one, index=df_index_month), 126 | "two": pd.Series(two, index=df_index_month), 127 | } 128 | ) 129 | 130 | input_one = [ 131 | np.nan, 132 | 0.01322056, 133 | 0.03063862, 134 | -0.01422057, 135 | -0.00489779, 136 | 0.01268925, 137 | -0.03357711, 138 | 0.01797036, 139 | ] 140 | input_two = [ 141 | 0.01846232, 142 | 0.00793951, 143 | -0.01448395, 144 | 0.00422537, 145 | -0.00339611, 146 | 0.03756813, 147 | 0.0151531, 148 | np.nan, 149 | ] 150 | 151 | df_index = pd.date_range("2000-1-30", periods=8, freq="D") 152 | 153 | return { 154 | # Simple benchmark, no drawdown 155 | "simple_benchmark": simple_benchmark, 156 | "simple_benchmark_w_noise": simple_benchmark 157 | + rand.normal(0, 0.001, len(simple_benchmark)), 158 | "simple_benchmark_df": simple_benchmark.rename("returns").to_frame(), 159 | # All positive returns, small variance 160 | "positive_returns": pd.Series( 161 | np.array([1.0, 2.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]) / 100, 162 | index=pd.date_range("2000-1-30", periods=9, freq="D"), 163 | ), 164 | # All negative returns 165 | "negative_returns": pd.Series( 166 | np.array([0.0, -6.0, -7.0, -1.0, -9.0, -2.0, -6.0, -8.0, -5.0]) / 100, 167 | index=pd.date_range("2000-1-30", periods=9, freq="D"), 168 | ), 169 | # All negative returns 170 | "all_negative_returns": pd.Series( 171 | np.array([-2.0, -6.0, -7.0, -1.0, -9.0, -2.0, -6.0, -8.0, -5.0]) / 100, 172 | index=pd.date_range("2000-1-30", periods=9, freq="D"), 173 | ), 174 | # Positive and negative returns with max drawdown 175 | "mixed_returns": mixed_returns, 176 | # Weekly returns 177 | "weekly_returns": pd.Series( 178 | np.array([0.0, 1.0, 10.0, -4.0, 2.0, 3.0, 2.0, 1.0, -10.0]) / 100, 179 | index=pd.date_range("2000-1-30", periods=9, freq="W"), 180 | ), 181 | # Monthly returns 182 | "monthly_returns": pd.Series( 183 | np.array([0.0, 1.0, 10.0, -4.0, 2.0, 3.0, 2.0, 1.0, -10.0]) / 100, 184 | index=pd.date_range("2000-1-30", periods=9, freq=monthly), 185 | ), 186 | # Series of length 1 187 | "one_return": pd.Series( 188 | np.array([1.0]) / 100, 189 | index=pd.date_range("2000-1-30", periods=1, freq="D"), 190 | ), 191 | "udu_returns": pd.Series( 192 | np.array([10, -10, 10]) / 100, 193 | index=pd.date_range("2000-1-30", periods=3, freq="D"), 194 | ), 195 | # Empty series 196 | "empty_returns": pd.Series( 197 | np.array([]) / 100, 198 | index=pd.date_range("2000-1-30", periods=0, freq="D"), 199 | ), 200 | # Random noise 201 | "noise": noise, 202 | "noise_uniform": noise_uniform, 203 | "random_100k": random_100k, 204 | # Random noise inv 205 | "inv_noise": inv_noise, 206 | # Flat line 207 | "flat_line_0": pd.Series( 208 | np.linspace(0, 0, num=1000), 209 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 210 | ), 211 | "flat_line_1": pd.Series( 212 | np.array([1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]) / 100, 213 | index=pd.date_range("2000-1-30", periods=9, freq="D"), 214 | ), 215 | # Flat line with tz 216 | "flat_line_1_tz": pd.Series( 217 | np.linspace(0.01, 0.01, num=1000), 218 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 219 | ), 220 | "flat_line_yearly": pd.Series( 221 | np.array([3.0, 3.0, 3.0]) / 100, 222 | index=pd.date_range("2000-1-30", periods=3, freq=annual), 223 | ), 224 | # Positive line 225 | "pos_line": pd.Series( 226 | np.linspace(0, 1, num=1000), 227 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 228 | ), 229 | # Negative line 230 | "neg_line": pd.Series( 231 | np.linspace(0, -1, num=1000), 232 | index=pd.date_range("2000-1-30", periods=1000, freq="D", tz="UTC"), 233 | ), 234 | # Sparse noise, same as noise but with np.nan sprinkled in 235 | "sparse_noise": sparse_noise, 236 | # Sparse flat line at 0.01 237 | "sparse_flat_line_1_tz": sparse_flat_line_1_tz, 238 | "one": one, 239 | "two": two, 240 | "df_index_simple": df_index_simple, 241 | "df_index_week": df_index_week, 242 | "df_index_month": df_index_month, 243 | "df_simple": df_simple, 244 | "df_week": df_week, 245 | "df_month": df_month, 246 | "df_empty": pd.DataFrame(), 247 | "df_input": pd.DataFrame( 248 | { 249 | "one": pd.Series(input_one, index=df_index), 250 | "two": pd.Series(input_two, index=df_index), 251 | } 252 | ), 253 | } 254 | 255 | 256 | @pytest.fixture 257 | def returns(input_data, request): 258 | name = request.param 259 | if name not in input_data.keys(): 260 | return eval(name, input_data) 261 | return input_data[name] 262 | 263 | 264 | required_return = returns 265 | benchmark = returns 266 | risk_free = returns 267 | factor_returns = returns 268 | add_noise = returns 269 | prices = returns 270 | 271 | 272 | @pytest.fixture(scope="session") 273 | def expected_data(): 274 | expected_0_one = [ 275 | 0.000000, 276 | 0.013221, 277 | 0.044264, 278 | 0.029414, 279 | 0.024372, 280 | 0.037371, 281 | 0.002539, 282 | 0.020555, 283 | ] 284 | expected_0_two = [ 285 | 0.018462, 286 | 0.026548, 287 | 0.011680, 288 | 0.015955, 289 | 0.012504, 290 | 0.050542, 291 | 0.066461, 292 | 0.066461, 293 | ] 294 | 295 | expected_100_one = [ 296 | 100.000000, 297 | 101.322056, 298 | 104.426424, 299 | 102.941421, 300 | 102.437235, 301 | 103.737087, 302 | 100.253895, 303 | 102.055494, 304 | ] 305 | expected_100_two = [ 306 | 101.846232, 307 | 102.654841, 308 | 101.167994, 309 | 101.595466, 310 | 101.250436, 311 | 105.054226, 312 | 106.646123, 313 | 106.646123, 314 | ] 315 | 316 | mixed_returns_gpd_risk = [ 317 | 0.1, 318 | 0.10001255835838491, 319 | 1.5657360018514067e-06, 320 | 0.4912526273742347, 321 | 0.59126595492541179, 322 | ] 323 | 324 | negative_returns_gpd_risk = [ 325 | 0.05, 326 | 0.068353586736348199, 327 | 9.4304947982121171e-07, 328 | 0.34511639904932639, 329 | 0.41347032855617882, 330 | ] 331 | 332 | df_index = pd.date_range("2000-1-30", periods=8, freq="D") 333 | 334 | df_0_expected = pd.DataFrame( 335 | { 336 | "one": pd.Series(expected_0_one, index=df_index), 337 | "two": pd.Series(expected_0_two, index=df_index), 338 | } 339 | ) 340 | 341 | df_100_expected = pd.DataFrame( 342 | { 343 | "one": pd.Series(expected_100_one, index=df_index), 344 | "two": pd.Series(expected_100_two, index=df_index), 345 | } 346 | ) 347 | return { 348 | "df_empty": pd.DataFrame(), 349 | "df_0_expected": df_0_expected, 350 | "df_100_expected": df_100_expected, 351 | "mixed_returns_expected_gpd_risk_result": mixed_returns_gpd_risk, 352 | "negative_returns_expected_gpd_risk_result": negative_returns_gpd_risk, 353 | } 354 | 355 | 356 | @pytest.fixture 357 | def expected(expected_data, request): 358 | name = request.param 359 | if name not in expected_data.keys(): 360 | return eval(name) 361 | return expected_data[name] 362 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Quantopian, Inc. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /tests/test_data/residuals.csv: -------------------------------------------------------------------------------- 1 | ,19001,19002 2 | 2016-01-04,0.0,0.0 3 | 2016-01-05,0.0,0.0 4 | 2016-01-06,0.0,0.0 5 | 2016-01-07,0.0,0.0 6 | 2016-01-08,0.0,0.0 7 | 2016-01-11,0.0,0.0 8 | 2016-01-12,0.0,0.0 9 | 2016-01-13,0.0,0.0 10 | 2016-01-14,0.0,0.0 11 | 2016-01-15,0.0,0.0 12 | 2016-01-18,0.0,0.0 13 | 2016-01-19,0.0,0.0 14 | 2016-01-20,0.0,0.0 15 | 2016-01-21,0.0,0.0 16 | 2016-01-22,0.0,0.0 17 | 2016-01-25,0.0,0.0 18 | 2016-01-26,0.0,0.0 19 | 2016-01-27,0.0,0.0 20 | 2016-01-28,0.0,0.0 21 | 2016-01-29,0.0,0.0 22 | 2016-02-01,0.0,0.0 23 | 2016-02-02,0.0,0.0 24 | 2016-02-03,0.0,0.0 25 | 2016-02-04,0.0,0.0 26 | 2016-02-05,0.0,0.0 27 | 2016-02-08,0.0,0.0 28 | 2016-02-09,0.0,0.0 29 | 2016-02-10,0.0,0.0 30 | 2016-02-11,0.0,0.0 31 | 2016-02-12,0.0,0.0 32 | 2016-02-15,0.0,0.0 33 | 2016-02-16,0.0,0.0 34 | 2016-02-17,0.0,0.0 35 | 2016-02-18,0.0,0.0 36 | 2016-02-19,0.0,0.0 37 | 2016-02-22,0.0,0.0 38 | 2016-02-23,0.0,0.0 39 | 2016-02-24,0.0,0.0 40 | 2016-02-25,0.0,0.0 41 | 2016-02-26,0.0,0.0 42 | 2016-02-29,0.0,0.0 43 | 2016-03-01,0.0,0.0 44 | 2016-03-02,0.0,0.0 45 | 2016-03-03,0.0,0.0 46 | 2016-03-04,0.0,0.0 47 | 2016-03-07,0.0,0.0 48 | 2016-03-08,0.0,0.0 49 | 2016-03-09,0.0,0.0 50 | 2016-03-10,0.0,0.0 51 | 2016-03-11,0.0,0.0 52 | 2016-03-14,0.0,0.0 53 | 2016-03-15,0.0,0.0 54 | 2016-03-16,0.0,0.0 55 | 2016-03-17,0.0,0.0 56 | 2016-03-18,0.0,0.0 57 | 2016-03-21,0.0,0.0 58 | 2016-03-22,0.0,0.0 59 | 2016-03-23,0.0,0.0 60 | 2016-03-24,0.0,0.0 61 | 2016-03-25,0.0,0.0 62 | 2016-03-28,0.0,0.0 63 | 2016-03-29,0.0,0.0 64 | 2016-03-30,0.0,0.0 65 | 2016-03-31,0.0,0.0 66 | 2016-04-01,0.0,0.0 67 | 2016-04-04,0.0,0.0 68 | 2016-04-05,0.0,0.0 69 | 2016-04-06,0.0,0.0 70 | 2016-04-07,0.0,0.0 71 | 2016-04-08,0.0,0.0 72 | 2016-04-11,0.0,0.0 73 | 2016-04-12,0.0,0.0 74 | 2016-04-13,0.0,0.0 75 | 2016-04-14,0.0,0.0 76 | 2016-04-15,0.0,0.0 77 | 2016-04-18,0.0,0.0 78 | 2016-04-19,0.0,0.0 79 | 2016-04-20,0.0,0.0 80 | 2016-04-21,0.0,0.0 81 | 2016-04-22,0.0,0.0 82 | 2016-04-25,0.0,0.0 83 | 2016-04-26,0.0,0.0 84 | 2016-04-27,0.0,0.0 85 | 2016-04-28,0.0,0.0 86 | 2016-04-29,0.0,0.0 87 | 2016-05-02,0.0,0.0 88 | 2016-05-03,0.0,0.0 89 | 2016-05-04,0.0,0.0 90 | 2016-05-05,0.0,0.0 91 | 2016-05-06,0.0,0.0 92 | 2016-05-09,0.0,0.0 93 | 2016-05-10,0.0,0.0 94 | 2016-05-11,0.0,0.0 95 | 2016-05-12,0.0,0.0 96 | 2016-05-13,0.0,0.0 97 | 2016-05-16,0.0,0.0 98 | 2016-05-17,0.0,0.0 99 | 2016-05-18,0.0,0.0 100 | 2016-05-19,0.0,0.0 101 | 2016-05-20,0.0,0.0 102 | 2016-05-23,0.0,0.0 103 | 2016-05-24,0.0,0.0 104 | 2016-05-25,0.0,0.0 105 | 2016-05-26,0.0,0.0 106 | 2016-05-27,0.0,0.0 107 | 2016-05-30,0.0,0.0 108 | 2016-05-31,0.0,0.0 109 | 2016-06-01,0.0,0.0 110 | 2016-06-02,0.0,0.0 111 | 2016-06-03,0.0,0.0 112 | 2016-06-06,0.0,0.0 113 | 2016-06-07,0.0,0.0 114 | 2016-06-08,0.0,0.0 115 | 2016-06-09,0.0,0.0 116 | 2016-06-10,0.0,0.0 117 | 2016-06-13,0.0,0.0 118 | 2016-06-14,0.0,0.0 119 | 2016-06-15,0.0,0.0 120 | 2016-06-16,0.0,0.0 121 | 2016-06-17,0.0,0.0 122 | 2016-06-20,0.0,0.0 123 | 2016-06-21,0.0,0.0 124 | 2016-06-22,0.0,0.0 125 | 2016-06-23,0.0,0.0 126 | 2016-06-24,0.0,0.0 127 | 2016-06-27,0.0,0.0 128 | 2016-06-28,0.0,0.0 129 | 2016-06-29,0.0,0.0 130 | 2016-06-30,0.0,0.0 131 | 2016-07-01,0.0,0.0 132 | 2016-07-04,0.0,0.0 133 | 2016-07-05,0.0,0.0 134 | 2016-07-06,0.0,0.0 135 | 2016-07-07,0.0,0.0 136 | 2016-07-08,0.0,0.0 137 | 2016-07-11,0.0,0.0 138 | 2016-07-12,0.0,0.0 139 | 2016-07-13,0.0,0.0 140 | 2016-07-14,0.0,0.0 141 | 2016-07-15,0.0,0.0 142 | 2016-07-18,0.0,0.0 143 | 2016-07-19,0.0,0.0 144 | 2016-07-20,0.0,0.0 145 | 2016-07-21,0.0,0.0 146 | 2016-07-22,0.0,0.0 147 | 2016-07-25,0.0,0.0 148 | 2016-07-26,0.0,0.0 149 | 2016-07-27,0.0,0.0 150 | 2016-07-28,0.0,0.0 151 | 2016-07-29,0.0,0.0 152 | 2016-08-01,0.0,0.0 153 | 2016-08-02,0.0,0.0 154 | 2016-08-03,0.0,0.0 155 | 2016-08-04,0.0,0.0 156 | 2016-08-05,0.0,0.0 157 | 2016-08-08,0.0,0.0 158 | 2016-08-09,0.0,0.0 159 | 2016-08-10,0.0,0.0 160 | 2016-08-11,0.0,0.0 161 | 2016-08-12,0.0,0.0 162 | 2016-08-15,0.0,0.0 163 | 2016-08-16,0.0,0.0 164 | 2016-08-17,0.0,0.0 165 | 2016-08-18,0.0,0.0 166 | 2016-08-19,0.0,0.0 167 | 2016-08-22,0.0,0.0 168 | 2016-08-23,0.0,0.0 169 | 2016-08-24,0.0,0.0 170 | 2016-08-25,0.0,0.0 171 | 2016-08-26,0.0,0.0 172 | 2016-08-29,0.0,0.0 173 | 2016-08-30,0.0,0.0 174 | 2016-08-31,0.0,0.0 175 | 2016-09-01,0.0,0.0 176 | 2016-09-02,0.0,0.0 177 | 2016-09-05,0.0,0.0 178 | 2016-09-06,0.0,0.0 179 | 2016-09-07,0.0,0.0 180 | 2016-09-08,0.0,0.0 181 | 2016-09-09,0.0,0.0 182 | 2016-09-12,0.0,0.0 183 | 2016-09-13,0.0,0.0 184 | 2016-09-14,0.0,0.0 185 | 2016-09-15,0.0,0.0 186 | 2016-09-16,0.0,0.0 187 | 2016-09-19,0.0,0.0 188 | 2016-09-20,0.0,0.0 189 | 2016-09-21,0.0,0.0 190 | 2016-09-22,0.0,0.0 191 | 2016-09-23,0.0,0.0 192 | 2016-09-26,0.0,0.0 193 | 2016-09-27,0.0,0.0 194 | 2016-09-28,0.0,0.0 195 | 2016-09-29,0.0,0.0 196 | 2016-09-30,0.0,0.0 197 | 2016-10-03,0.0,0.0 198 | 2016-10-04,0.0,0.0 199 | 2016-10-05,0.0,0.0 200 | 2016-10-06,0.0,0.0 201 | 2016-10-07,0.0,0.0 202 | 2016-10-10,0.0,0.0 203 | 2016-10-11,0.0,0.0 204 | 2016-10-12,0.0,0.0 205 | 2016-10-13,0.0,0.0 206 | 2016-10-14,0.0,0.0 207 | 2016-10-17,0.0,0.0 208 | 2016-10-18,0.0,0.0 209 | 2016-10-19,0.0,0.0 210 | 2016-10-20,0.0,0.0 211 | 2016-10-21,0.0,0.0 212 | 2016-10-24,0.0,0.0 213 | 2016-10-25,0.0,0.0 214 | 2016-10-26,0.0,0.0 215 | 2016-10-27,0.0,0.0 216 | 2016-10-28,0.0,0.0 217 | 2016-10-31,0.0,0.0 218 | 2016-11-01,0.0,0.0 219 | 2016-11-02,0.0,0.0 220 | 2016-11-03,0.0,0.0 221 | 2016-11-04,0.0,0.0 222 | 2016-11-07,0.0,0.0 223 | 2016-11-08,0.0,0.0 224 | 2016-11-09,0.0,0.0 225 | 2016-11-10,0.0,0.0 226 | 2016-11-11,0.0,0.0 227 | 2016-11-14,0.0,0.0 228 | 2016-11-15,0.0,0.0 229 | 2016-11-16,0.0,0.0 230 | 2016-11-17,0.0,0.0 231 | 2016-11-18,0.0,0.0 232 | 2016-11-21,0.0,0.0 233 | 2016-11-22,0.0,0.0 234 | 2016-11-23,0.0,0.0 235 | 2016-11-24,0.0,0.0 236 | 2016-11-25,0.0,0.0 237 | 2016-11-28,0.0,0.0 238 | 2016-11-29,0.0,0.0 239 | 2016-11-30,0.0,0.0 240 | 2016-12-01,0.0,0.0 241 | 2016-12-02,0.0,0.0 242 | 2016-12-05,0.0,0.0 243 | 2016-12-06,0.0,0.0 244 | 2016-12-07,0.0,0.0 245 | 2016-12-08,0.0,0.0 246 | 2016-12-09,0.0,0.0 247 | 2016-12-12,0.0,0.0 248 | 2016-12-13,0.0,0.0 249 | 2016-12-14,0.0,0.0 250 | 2016-12-15,0.0,0.0 251 | 2016-12-16,0.0,0.0 252 | 2016-12-19,0.0,0.0 253 | 2016-12-20,0.0,0.0 254 | 2016-12-21,0.0,0.0 255 | 2016-12-22,0.0,0.0 256 | 2016-12-23,0.0,0.0 257 | 2016-12-26,0.0,0.0 258 | 2016-12-27,0.0,0.0 259 | 2016-12-28,0.0,0.0 260 | 2016-12-29,0.0,0.0 261 | 2016-12-30,0.0,0.0 262 | 2017-01-02,0.0,0.0 263 | 2017-01-03,0.0,0.0 264 | 2017-01-04,0.0,0.0 265 | 2017-01-05,0.0,0.0 266 | 2017-01-06,0.0,0.0 267 | 2017-01-09,0.0,0.0 268 | 2017-01-10,0.0,0.0 269 | 2017-01-11,0.0,0.0 270 | 2017-01-12,0.0,0.0 271 | 2017-01-13,0.0,0.0 272 | 2017-01-16,0.0,0.0 273 | 2017-01-17,0.0,0.0 274 | 2017-01-18,0.0,0.0 275 | 2017-01-19,0.0,0.0 276 | 2017-01-20,0.0,0.0 277 | 2017-01-23,0.0,0.0 278 | 2017-01-24,0.0,0.0 279 | 2017-01-25,0.0,0.0 280 | 2017-01-26,0.0,0.0 281 | 2017-01-27,0.0,0.0 282 | 2017-01-30,0.0,0.0 283 | 2017-01-31,0.0,0.0 284 | 2017-02-01,0.0,0.0 285 | 2017-02-02,0.0,0.0 286 | 2017-02-03,0.0,0.0 287 | 2017-02-06,0.0,0.0 288 | 2017-02-07,0.0,0.0 289 | 2017-02-08,0.0,0.0 290 | 2017-02-09,0.0,0.0 291 | 2017-02-10,0.0,0.0 292 | 2017-02-13,0.0,0.0 293 | 2017-02-14,0.0,0.0 294 | 2017-02-15,0.0,0.0 295 | 2017-02-16,0.0,0.0 296 | 2017-02-17,0.0,0.0 297 | 2017-02-20,0.0,0.0 298 | 2017-02-21,0.0,0.0 299 | 2017-02-22,0.0,0.0 300 | 2017-02-23,0.0,0.0 301 | 2017-02-24,0.0,0.0 302 | 2017-02-27,0.0,0.0 303 | 2017-02-28,0.0,0.0 304 | 2017-03-01,0.0,0.0 305 | 2017-03-02,0.0,0.0 306 | 2017-03-03,0.0,0.0 307 | 2017-03-06,0.0,0.0 308 | 2017-03-07,0.0,0.0 309 | 2017-03-08,0.0,0.0 310 | 2017-03-09,0.0,0.0 311 | 2017-03-10,0.0,0.0 312 | 2017-03-13,0.0,0.0 313 | 2017-03-14,0.0,0.0 314 | 2017-03-15,0.0,0.0 315 | 2017-03-16,0.0,0.0 316 | 2017-03-17,0.0,0.0 317 | 2017-03-20,0.0,0.0 318 | 2017-03-21,0.0,0.0 319 | 2017-03-22,0.0,0.0 320 | 2017-03-23,0.0,0.0 321 | 2017-03-24,0.0,0.0 322 | 2017-03-27,0.0,0.0 323 | 2017-03-28,0.0,0.0 324 | 2017-03-29,0.0,0.0 325 | 2017-03-30,0.0,0.0 326 | 2017-03-31,0.0,0.0 327 | 2017-04-03,0.0,0.0 328 | 2017-04-04,0.0,0.0 329 | 2017-04-05,0.0,0.0 330 | 2017-04-06,0.0,0.0 331 | 2017-04-07,0.0,0.0 332 | 2017-04-10,0.0,0.0 333 | 2017-04-11,0.0,0.0 334 | 2017-04-12,0.0,0.0 335 | 2017-04-13,0.0,0.0 336 | 2017-04-14,0.0,0.0 337 | 2017-04-17,0.0,0.0 338 | 2017-04-18,0.0,0.0 339 | 2017-04-19,0.0,0.0 340 | 2017-04-20,0.0,0.0 341 | 2017-04-21,0.0,0.0 342 | 2017-04-24,0.0,0.0 343 | 2017-04-25,0.0,0.0 344 | 2017-04-26,0.0,0.0 345 | 2017-04-27,0.0,0.0 346 | 2017-04-28,0.0,0.0 347 | 2017-05-01,0.0,0.0 348 | 2017-05-02,0.0,0.0 349 | 2017-05-03,0.0,0.0 350 | 2017-05-04,0.0,0.0 351 | 2017-05-05,0.0,0.0 352 | 2017-05-08,0.0,0.0 353 | 2017-05-09,0.0,0.0 354 | 2017-05-10,0.0,0.0 355 | 2017-05-11,0.0,0.0 356 | 2017-05-12,0.0,0.0 357 | 2017-05-15,0.0,0.0 358 | 2017-05-16,0.0,0.0 359 | 2017-05-17,0.0,0.0 360 | 2017-05-18,0.0,0.0 361 | 2017-05-19,0.0,0.0 362 | 2017-05-22,0.0,0.0 363 | 2017-05-23,0.0,0.0 364 | 2017-05-24,0.0,0.0 365 | 2017-05-25,0.0,0.0 366 | 2017-05-26,0.0,0.0 367 | 2017-05-29,0.0,0.0 368 | 2017-05-30,0.0,0.0 369 | 2017-05-31,0.0,0.0 370 | 2017-06-01,0.0,0.0 371 | 2017-06-02,0.0,0.0 372 | 2017-06-05,0.0,0.0 373 | 2017-06-06,0.0,0.0 374 | 2017-06-07,0.0,0.0 375 | 2017-06-08,0.0,0.0 376 | 2017-06-09,0.0,0.0 377 | 2017-06-12,0.0,0.0 378 | 2017-06-13,0.0,0.0 379 | 2017-06-14,0.0,0.0 380 | 2017-06-15,0.0,0.0 381 | 2017-06-16,0.0,0.0 382 | 2017-06-19,0.0,0.0 383 | 2017-06-20,0.0,0.0 384 | 2017-06-21,0.0,0.0 385 | 2017-06-22,0.0,0.0 386 | 2017-06-23,0.0,0.0 387 | 2017-06-26,0.0,0.0 388 | 2017-06-27,0.0,0.0 389 | 2017-06-28,0.0,0.0 390 | 2017-06-29,0.0,0.0 391 | 2017-06-30,0.0,0.0 392 | 2017-07-03,0.0,0.0 393 | 2017-07-04,0.0,0.0 394 | 2017-07-05,0.0,0.0 395 | 2017-07-06,0.0,0.0 396 | 2017-07-07,0.0,0.0 397 | 2017-07-10,0.0,0.0 398 | 2017-07-11,0.0,0.0 399 | 2017-07-12,0.0,0.0 400 | 2017-07-13,0.0,0.0 401 | 2017-07-14,0.0,0.0 402 | 2017-07-17,0.0,0.0 403 | 2017-07-18,0.0,0.0 404 | 2017-07-19,0.0,0.0 405 | 2017-07-20,0.0,0.0 406 | 2017-07-21,0.0,0.0 407 | 2017-07-24,0.0,0.0 408 | 2017-07-25,0.0,0.0 409 | 2017-07-26,0.0,0.0 410 | 2017-07-27,0.0,0.0 411 | 2017-07-28,0.0,0.0 412 | 2017-07-31,0.0,0.0 413 | 2017-08-01,0.0,0.0 414 | 2017-08-02,0.0,0.0 415 | 2017-08-03,0.0,0.0 416 | 2017-08-04,0.0,0.0 417 | 2017-08-07,0.0,0.0 418 | 2017-08-08,0.0,0.0 419 | 2017-08-09,0.0,0.0 420 | 2017-08-10,0.0,0.0 421 | 2017-08-11,0.0,0.0 422 | 2017-08-14,0.0,0.0 423 | 2017-08-15,0.0,0.0 424 | 2017-08-16,0.0,0.0 425 | 2017-08-17,0.0,0.0 426 | 2017-08-18,0.0,0.0 427 | 2017-08-21,0.0,0.0 428 | 2017-08-22,0.0,0.0 429 | 2017-08-23,0.0,0.0 430 | 2017-08-24,0.0,0.0 431 | 2017-08-25,0.0,0.0 432 | 2017-08-28,0.0,0.0 433 | 2017-08-29,0.0,0.0 434 | 2017-08-30,0.0,0.0 435 | 2017-08-31,0.0,0.0 436 | 2017-09-01,0.0,0.0 437 | 2017-09-04,0.0,0.0 438 | 2017-09-05,0.0,0.0 439 | 2017-09-06,0.0,0.0 440 | 2017-09-07,0.0,0.0 441 | 2017-09-08,0.0,0.0 442 | 2017-09-11,0.0,0.0 443 | 2017-09-12,0.0,0.0 444 | 2017-09-13,0.0,0.0 445 | 2017-09-14,0.0,0.0 446 | 2017-09-15,0.0,0.0 447 | 2017-09-18,0.0,0.0 448 | 2017-09-19,0.0,0.0 449 | 2017-09-20,0.0,0.0 450 | 2017-09-21,0.0,0.0 451 | 2017-09-22,0.0,0.0 452 | 2017-09-25,0.0,0.0 453 | 2017-09-26,0.0,0.0 454 | 2017-09-27,0.0,0.0 455 | 2017-09-28,0.0,0.0 456 | 2017-09-29,0.0,0.0 457 | 2017-10-02,0.0,0.0 458 | 2017-10-03,0.0,0.0 459 | 2017-10-04,0.0,0.0 460 | 2017-10-05,0.0,0.0 461 | 2017-10-06,0.0,0.0 462 | 2017-10-09,0.0,0.0 463 | 2017-10-10,0.0,0.0 464 | 2017-10-11,0.0,0.0 465 | 2017-10-12,0.0,0.0 466 | 2017-10-13,0.0,0.0 467 | 2017-10-16,0.0,0.0 468 | 2017-10-17,0.0,0.0 469 | 2017-10-18,0.0,0.0 470 | 2017-10-19,0.0,0.0 471 | 2017-10-20,0.0,0.0 472 | 2017-10-23,0.0,0.0 473 | 2017-10-24,0.0,0.0 474 | 2017-10-25,0.0,0.0 475 | 2017-10-26,0.0,0.0 476 | 2017-10-27,0.0,0.0 477 | 2017-10-30,0.0,0.0 478 | 2017-10-31,0.0,0.0 479 | 2017-11-01,0.0,0.0 480 | 2017-11-02,0.0,0.0 481 | 2017-11-03,0.0,0.0 482 | 2017-11-06,0.0,0.0 483 | 2017-11-07,0.0,0.0 484 | 2017-11-08,0.0,0.0 485 | 2017-11-09,0.0,0.0 486 | 2017-11-10,0.0,0.0 487 | 2017-11-13,0.0,0.0 488 | 2017-11-14,0.0,0.0 489 | 2017-11-15,0.0,0.0 490 | 2017-11-16,0.0,0.0 491 | 2017-11-17,0.0,0.0 492 | 2017-11-20,0.0,0.0 493 | 2017-11-21,0.0,0.0 494 | 2017-11-22,0.0,0.0 495 | 2017-11-23,0.0,0.0 496 | 2017-11-24,0.0,0.0 497 | 2017-11-27,0.0,0.0 498 | 2017-11-28,0.0,0.0 499 | 2017-11-29,0.0,0.0 500 | 2017-11-30,0.0,0.0 501 | 2017-12-01,0.0,0.0 502 | 2017-12-04,0.0,0.0 503 | 2017-12-05,0.0,0.0 504 | 2017-12-06,0.0,0.0 505 | 2017-12-07,0.0,0.0 506 | -------------------------------------------------------------------------------- /tests/test_data/positions.csv: -------------------------------------------------------------------------------- 1 | ,19001,19002,cash 2 | 2016-01-04,1.0,1.0,0.0 3 | 2016-01-05,1.0,1.0,0.0 4 | 2016-01-06,1.0,1.0,0.0 5 | 2016-01-07,1.0,1.0,0.0 6 | 2016-01-08,1.0,1.0,0.0 7 | 2016-01-11,1.0,1.0,0.0 8 | 2016-01-12,1.0,1.0,0.0 9 | 2016-01-13,1.0,1.0,0.0 10 | 2016-01-14,1.0,1.0,0.0 11 | 2016-01-15,1.0,1.0,0.0 12 | 2016-01-18,1.0,1.0,0.0 13 | 2016-01-19,1.0,1.0,0.0 14 | 2016-01-20,1.0,1.0,0.0 15 | 2016-01-21,1.0,1.0,0.0 16 | 2016-01-22,1.0,1.0,0.0 17 | 2016-01-25,1.0,1.0,0.0 18 | 2016-01-26,1.0,1.0,0.0 19 | 2016-01-27,1.0,1.0,0.0 20 | 2016-01-28,1.0,1.0,0.0 21 | 2016-01-29,1.0,1.0,0.0 22 | 2016-02-01,1.0,1.0,0.0 23 | 2016-02-02,1.0,1.0,0.0 24 | 2016-02-03,1.0,1.0,0.0 25 | 2016-02-04,1.0,1.0,0.0 26 | 2016-02-05,1.0,1.0,0.0 27 | 2016-02-08,1.0,1.0,0.0 28 | 2016-02-09,1.0,1.0,0.0 29 | 2016-02-10,1.0,1.0,0.0 30 | 2016-02-11,1.0,1.0,0.0 31 | 2016-02-12,1.0,1.0,0.0 32 | 2016-02-15,1.0,1.0,0.0 33 | 2016-02-16,1.0,1.0,0.0 34 | 2016-02-17,1.0,1.0,0.0 35 | 2016-02-18,1.0,1.0,0.0 36 | 2016-02-19,1.0,1.0,0.0 37 | 2016-02-22,1.0,1.0,0.0 38 | 2016-02-23,1.0,1.0,0.0 39 | 2016-02-24,1.0,1.0,0.0 40 | 2016-02-25,1.0,1.0,0.0 41 | 2016-02-26,1.0,1.0,0.0 42 | 2016-02-29,1.0,1.0,0.0 43 | 2016-03-01,1.0,1.0,0.0 44 | 2016-03-02,1.0,1.0,0.0 45 | 2016-03-03,1.0,1.0,0.0 46 | 2016-03-04,1.0,1.0,0.0 47 | 2016-03-07,1.0,1.0,0.0 48 | 2016-03-08,1.0,1.0,0.0 49 | 2016-03-09,1.0,1.0,0.0 50 | 2016-03-10,1.0,1.0,0.0 51 | 2016-03-11,1.0,1.0,0.0 52 | 2016-03-14,1.0,1.0,0.0 53 | 2016-03-15,1.0,1.0,0.0 54 | 2016-03-16,1.0,1.0,0.0 55 | 2016-03-17,1.0,1.0,0.0 56 | 2016-03-18,1.0,1.0,0.0 57 | 2016-03-21,1.0,1.0,0.0 58 | 2016-03-22,1.0,1.0,0.0 59 | 2016-03-23,1.0,1.0,0.0 60 | 2016-03-24,1.0,1.0,0.0 61 | 2016-03-25,1.0,1.0,0.0 62 | 2016-03-28,1.0,1.0,0.0 63 | 2016-03-29,1.0,1.0,0.0 64 | 2016-03-30,1.0,1.0,0.0 65 | 2016-03-31,1.0,1.0,0.0 66 | 2016-04-01,1.0,1.0,0.0 67 | 2016-04-04,1.0,1.0,0.0 68 | 2016-04-05,1.0,1.0,0.0 69 | 2016-04-06,1.0,1.0,0.0 70 | 2016-04-07,1.0,1.0,0.0 71 | 2016-04-08,1.0,1.0,0.0 72 | 2016-04-11,1.0,1.0,0.0 73 | 2016-04-12,1.0,1.0,0.0 74 | 2016-04-13,1.0,1.0,0.0 75 | 2016-04-14,1.0,1.0,0.0 76 | 2016-04-15,1.0,1.0,0.0 77 | 2016-04-18,1.0,1.0,0.0 78 | 2016-04-19,1.0,1.0,0.0 79 | 2016-04-20,1.0,1.0,0.0 80 | 2016-04-21,1.0,1.0,0.0 81 | 2016-04-22,1.0,1.0,0.0 82 | 2016-04-25,1.0,1.0,0.0 83 | 2016-04-26,1.0,1.0,0.0 84 | 2016-04-27,1.0,1.0,0.0 85 | 2016-04-28,1.0,1.0,0.0 86 | 2016-04-29,1.0,1.0,0.0 87 | 2016-05-02,1.0,1.0,0.0 88 | 2016-05-03,1.0,1.0,0.0 89 | 2016-05-04,1.0,1.0,0.0 90 | 2016-05-05,1.0,1.0,0.0 91 | 2016-05-06,1.0,1.0,0.0 92 | 2016-05-09,1.0,1.0,0.0 93 | 2016-05-10,1.0,1.0,0.0 94 | 2016-05-11,1.0,1.0,0.0 95 | 2016-05-12,1.0,1.0,0.0 96 | 2016-05-13,1.0,1.0,0.0 97 | 2016-05-16,1.0,1.0,0.0 98 | 2016-05-17,1.0,1.0,0.0 99 | 2016-05-18,1.0,1.0,0.0 100 | 2016-05-19,1.0,1.0,0.0 101 | 2016-05-20,1.0,1.0,0.0 102 | 2016-05-23,1.0,1.0,0.0 103 | 2016-05-24,1.0,1.0,0.0 104 | 2016-05-25,1.0,1.0,0.0 105 | 2016-05-26,1.0,1.0,0.0 106 | 2016-05-27,1.0,1.0,0.0 107 | 2016-05-30,1.0,1.0,0.0 108 | 2016-05-31,1.0,1.0,0.0 109 | 2016-06-01,1.0,1.0,0.0 110 | 2016-06-02,1.0,1.0,0.0 111 | 2016-06-03,1.0,1.0,0.0 112 | 2016-06-06,1.0,1.0,0.0 113 | 2016-06-07,1.0,1.0,0.0 114 | 2016-06-08,1.0,1.0,0.0 115 | 2016-06-09,1.0,1.0,0.0 116 | 2016-06-10,1.0,1.0,0.0 117 | 2016-06-13,1.0,1.0,0.0 118 | 2016-06-14,1.0,1.0,0.0 119 | 2016-06-15,1.0,1.0,0.0 120 | 2016-06-16,1.0,1.0,0.0 121 | 2016-06-17,1.0,1.0,0.0 122 | 2016-06-20,1.0,1.0,0.0 123 | 2016-06-21,1.0,1.0,0.0 124 | 2016-06-22,1.0,1.0,0.0 125 | 2016-06-23,1.0,1.0,0.0 126 | 2016-06-24,1.0,1.0,0.0 127 | 2016-06-27,1.0,1.0,0.0 128 | 2016-06-28,1.0,1.0,0.0 129 | 2016-06-29,1.0,1.0,0.0 130 | 2016-06-30,1.0,1.0,0.0 131 | 2016-07-01,1.0,1.0,0.0 132 | 2016-07-04,1.0,1.0,0.0 133 | 2016-07-05,1.0,1.0,0.0 134 | 2016-07-06,1.0,1.0,0.0 135 | 2016-07-07,1.0,1.0,0.0 136 | 2016-07-08,1.0,1.0,0.0 137 | 2016-07-11,1.0,1.0,0.0 138 | 2016-07-12,1.0,1.0,0.0 139 | 2016-07-13,1.0,1.0,0.0 140 | 2016-07-14,1.0,1.0,0.0 141 | 2016-07-15,1.0,1.0,0.0 142 | 2016-07-18,1.0,1.0,0.0 143 | 2016-07-19,1.0,1.0,0.0 144 | 2016-07-20,1.0,1.0,0.0 145 | 2016-07-21,1.0,1.0,0.0 146 | 2016-07-22,1.0,1.0,0.0 147 | 2016-07-25,1.0,1.0,0.0 148 | 2016-07-26,1.0,1.0,0.0 149 | 2016-07-27,1.0,1.0,0.0 150 | 2016-07-28,1.0,1.0,0.0 151 | 2016-07-29,1.0,1.0,0.0 152 | 2016-08-01,1.0,1.0,0.0 153 | 2016-08-02,1.0,1.0,0.0 154 | 2016-08-03,1.0,1.0,0.0 155 | 2016-08-04,1.0,1.0,0.0 156 | 2016-08-05,1.0,1.0,0.0 157 | 2016-08-08,1.0,1.0,0.0 158 | 2016-08-09,1.0,1.0,0.0 159 | 2016-08-10,1.0,1.0,0.0 160 | 2016-08-11,1.0,1.0,0.0 161 | 2016-08-12,1.0,1.0,0.0 162 | 2016-08-15,1.0,1.0,0.0 163 | 2016-08-16,1.0,1.0,0.0 164 | 2016-08-17,1.0,1.0,0.0 165 | 2016-08-18,1.0,1.0,0.0 166 | 2016-08-19,1.0,1.0,0.0 167 | 2016-08-22,1.0,1.0,0.0 168 | 2016-08-23,1.0,1.0,0.0 169 | 2016-08-24,1.0,1.0,0.0 170 | 2016-08-25,1.0,1.0,0.0 171 | 2016-08-26,1.0,1.0,0.0 172 | 2016-08-29,1.0,1.0,0.0 173 | 2016-08-30,1.0,1.0,0.0 174 | 2016-08-31,1.0,1.0,0.0 175 | 2016-09-01,1.0,1.0,0.0 176 | 2016-09-02,1.0,1.0,0.0 177 | 2016-09-05,1.0,1.0,0.0 178 | 2016-09-06,1.0,1.0,0.0 179 | 2016-09-07,1.0,1.0,0.0 180 | 2016-09-08,1.0,1.0,0.0 181 | 2016-09-09,1.0,1.0,0.0 182 | 2016-09-12,1.0,1.0,0.0 183 | 2016-09-13,1.0,1.0,0.0 184 | 2016-09-14,1.0,1.0,0.0 185 | 2016-09-15,1.0,1.0,0.0 186 | 2016-09-16,1.0,1.0,0.0 187 | 2016-09-19,1.0,1.0,0.0 188 | 2016-09-20,1.0,1.0,0.0 189 | 2016-09-21,1.0,1.0,0.0 190 | 2016-09-22,1.0,1.0,0.0 191 | 2016-09-23,1.0,1.0,0.0 192 | 2016-09-26,1.0,1.0,0.0 193 | 2016-09-27,1.0,1.0,0.0 194 | 2016-09-28,1.0,1.0,0.0 195 | 2016-09-29,1.0,1.0,0.0 196 | 2016-09-30,1.0,1.0,0.0 197 | 2016-10-03,1.0,1.0,0.0 198 | 2016-10-04,1.0,1.0,0.0 199 | 2016-10-05,1.0,1.0,0.0 200 | 2016-10-06,1.0,1.0,0.0 201 | 2016-10-07,1.0,1.0,0.0 202 | 2016-10-10,1.0,1.0,0.0 203 | 2016-10-11,1.0,1.0,0.0 204 | 2016-10-12,1.0,1.0,0.0 205 | 2016-10-13,1.0,1.0,0.0 206 | 2016-10-14,1.0,1.0,0.0 207 | 2016-10-17,1.0,1.0,0.0 208 | 2016-10-18,1.0,1.0,0.0 209 | 2016-10-19,1.0,1.0,0.0 210 | 2016-10-20,1.0,1.0,0.0 211 | 2016-10-21,1.0,1.0,0.0 212 | 2016-10-24,1.0,1.0,0.0 213 | 2016-10-25,1.0,1.0,0.0 214 | 2016-10-26,1.0,1.0,0.0 215 | 2016-10-27,1.0,1.0,0.0 216 | 2016-10-28,1.0,1.0,0.0 217 | 2016-10-31,1.0,1.0,0.0 218 | 2016-11-01,1.0,1.0,0.0 219 | 2016-11-02,1.0,1.0,0.0 220 | 2016-11-03,1.0,1.0,0.0 221 | 2016-11-04,1.0,1.0,0.0 222 | 2016-11-07,1.0,1.0,0.0 223 | 2016-11-08,1.0,1.0,0.0 224 | 2016-11-09,1.0,1.0,0.0 225 | 2016-11-10,1.0,1.0,0.0 226 | 2016-11-11,1.0,1.0,0.0 227 | 2016-11-14,1.0,1.0,0.0 228 | 2016-11-15,1.0,1.0,0.0 229 | 2016-11-16,1.0,1.0,0.0 230 | 2016-11-17,1.0,1.0,0.0 231 | 2016-11-18,1.0,1.0,0.0 232 | 2016-11-21,1.0,1.0,0.0 233 | 2016-11-22,1.0,1.0,0.0 234 | 2016-11-23,1.0,1.0,0.0 235 | 2016-11-24,1.0,1.0,0.0 236 | 2016-11-25,1.0,1.0,0.0 237 | 2016-11-28,1.0,1.0,0.0 238 | 2016-11-29,1.0,1.0,0.0 239 | 2016-11-30,1.0,1.0,0.0 240 | 2016-12-01,1.0,1.0,0.0 241 | 2016-12-02,1.0,1.0,0.0 242 | 2016-12-05,1.0,1.0,0.0 243 | 2016-12-06,1.0,1.0,0.0 244 | 2016-12-07,1.0,1.0,0.0 245 | 2016-12-08,1.0,1.0,0.0 246 | 2016-12-09,1.0,1.0,0.0 247 | 2016-12-12,1.0,1.0,0.0 248 | 2016-12-13,1.0,1.0,0.0 249 | 2016-12-14,1.0,1.0,0.0 250 | 2016-12-15,1.0,1.0,0.0 251 | 2016-12-16,1.0,1.0,0.0 252 | 2016-12-19,1.0,1.0,0.0 253 | 2016-12-20,1.0,1.0,0.0 254 | 2016-12-21,1.0,1.0,0.0 255 | 2016-12-22,1.0,1.0,0.0 256 | 2016-12-23,1.0,1.0,0.0 257 | 2016-12-26,1.0,1.0,0.0 258 | 2016-12-27,1.0,1.0,0.0 259 | 2016-12-28,1.0,1.0,0.0 260 | 2016-12-29,1.0,1.0,0.0 261 | 2016-12-30,1.0,1.0,0.0 262 | 2017-01-02,1.0,1.0,0.0 263 | 2017-01-03,1.0,1.0,0.0 264 | 2017-01-04,1.0,1.0,0.0 265 | 2017-01-05,1.0,1.0,0.0 266 | 2017-01-06,1.0,1.0,0.0 267 | 2017-01-09,1.0,1.0,0.0 268 | 2017-01-10,1.0,1.0,0.0 269 | 2017-01-11,1.0,1.0,0.0 270 | 2017-01-12,1.0,1.0,0.0 271 | 2017-01-13,1.0,1.0,0.0 272 | 2017-01-16,1.0,1.0,0.0 273 | 2017-01-17,1.0,1.0,0.0 274 | 2017-01-18,1.0,1.0,0.0 275 | 2017-01-19,1.0,1.0,0.0 276 | 2017-01-20,1.0,1.0,0.0 277 | 2017-01-23,1.0,1.0,0.0 278 | 2017-01-24,1.0,1.0,0.0 279 | 2017-01-25,1.0,1.0,0.0 280 | 2017-01-26,1.0,1.0,0.0 281 | 2017-01-27,1.0,1.0,0.0 282 | 2017-01-30,1.0,1.0,0.0 283 | 2017-01-31,1.0,1.0,0.0 284 | 2017-02-01,1.0,1.0,0.0 285 | 2017-02-02,1.0,1.0,0.0 286 | 2017-02-03,1.0,1.0,0.0 287 | 2017-02-06,1.0,1.0,0.0 288 | 2017-02-07,1.0,1.0,0.0 289 | 2017-02-08,1.0,1.0,0.0 290 | 2017-02-09,1.0,1.0,0.0 291 | 2017-02-10,1.0,1.0,0.0 292 | 2017-02-13,1.0,1.0,0.0 293 | 2017-02-14,1.0,1.0,0.0 294 | 2017-02-15,1.0,1.0,0.0 295 | 2017-02-16,1.0,1.0,0.0 296 | 2017-02-17,1.0,1.0,0.0 297 | 2017-02-20,1.0,1.0,0.0 298 | 2017-02-21,1.0,1.0,0.0 299 | 2017-02-22,1.0,1.0,0.0 300 | 2017-02-23,1.0,1.0,0.0 301 | 2017-02-24,1.0,1.0,0.0 302 | 2017-02-27,1.0,1.0,0.0 303 | 2017-02-28,1.0,1.0,0.0 304 | 2017-03-01,1.0,1.0,0.0 305 | 2017-03-02,1.0,1.0,0.0 306 | 2017-03-03,1.0,1.0,0.0 307 | 2017-03-06,1.0,1.0,0.0 308 | 2017-03-07,1.0,1.0,0.0 309 | 2017-03-08,1.0,1.0,0.0 310 | 2017-03-09,1.0,1.0,0.0 311 | 2017-03-10,1.0,1.0,0.0 312 | 2017-03-13,1.0,1.0,0.0 313 | 2017-03-14,1.0,1.0,0.0 314 | 2017-03-15,1.0,1.0,0.0 315 | 2017-03-16,1.0,1.0,0.0 316 | 2017-03-17,1.0,1.0,0.0 317 | 2017-03-20,1.0,1.0,0.0 318 | 2017-03-21,1.0,1.0,0.0 319 | 2017-03-22,1.0,1.0,0.0 320 | 2017-03-23,1.0,1.0,0.0 321 | 2017-03-24,1.0,1.0,0.0 322 | 2017-03-27,1.0,1.0,0.0 323 | 2017-03-28,1.0,1.0,0.0 324 | 2017-03-29,1.0,1.0,0.0 325 | 2017-03-30,1.0,1.0,0.0 326 | 2017-03-31,1.0,1.0,0.0 327 | 2017-04-03,1.0,1.0,0.0 328 | 2017-04-04,1.0,1.0,0.0 329 | 2017-04-05,1.0,1.0,0.0 330 | 2017-04-06,1.0,1.0,0.0 331 | 2017-04-07,1.0,1.0,0.0 332 | 2017-04-10,1.0,1.0,0.0 333 | 2017-04-11,1.0,1.0,0.0 334 | 2017-04-12,1.0,1.0,0.0 335 | 2017-04-13,1.0,1.0,0.0 336 | 2017-04-14,1.0,1.0,0.0 337 | 2017-04-17,1.0,1.0,0.0 338 | 2017-04-18,1.0,1.0,0.0 339 | 2017-04-19,1.0,1.0,0.0 340 | 2017-04-20,1.0,1.0,0.0 341 | 2017-04-21,1.0,1.0,0.0 342 | 2017-04-24,1.0,1.0,0.0 343 | 2017-04-25,1.0,1.0,0.0 344 | 2017-04-26,1.0,1.0,0.0 345 | 2017-04-27,1.0,1.0,0.0 346 | 2017-04-28,1.0,1.0,0.0 347 | 2017-05-01,1.0,1.0,0.0 348 | 2017-05-02,1.0,1.0,0.0 349 | 2017-05-03,1.0,1.0,0.0 350 | 2017-05-04,1.0,1.0,0.0 351 | 2017-05-05,1.0,1.0,0.0 352 | 2017-05-08,1.0,1.0,0.0 353 | 2017-05-09,1.0,1.0,0.0 354 | 2017-05-10,1.0,1.0,0.0 355 | 2017-05-11,1.0,1.0,0.0 356 | 2017-05-12,1.0,1.0,0.0 357 | 2017-05-15,1.0,1.0,0.0 358 | 2017-05-16,1.0,1.0,0.0 359 | 2017-05-17,1.0,1.0,0.0 360 | 2017-05-18,1.0,1.0,0.0 361 | 2017-05-19,1.0,1.0,0.0 362 | 2017-05-22,1.0,1.0,0.0 363 | 2017-05-23,1.0,1.0,0.0 364 | 2017-05-24,1.0,1.0,0.0 365 | 2017-05-25,1.0,1.0,0.0 366 | 2017-05-26,1.0,1.0,0.0 367 | 2017-05-29,1.0,1.0,0.0 368 | 2017-05-30,1.0,1.0,0.0 369 | 2017-05-31,1.0,1.0,0.0 370 | 2017-06-01,1.0,1.0,0.0 371 | 2017-06-02,1.0,1.0,0.0 372 | 2017-06-05,1.0,1.0,0.0 373 | 2017-06-06,1.0,1.0,0.0 374 | 2017-06-07,1.0,1.0,0.0 375 | 2017-06-08,1.0,1.0,0.0 376 | 2017-06-09,1.0,1.0,0.0 377 | 2017-06-12,1.0,1.0,0.0 378 | 2017-06-13,1.0,1.0,0.0 379 | 2017-06-14,1.0,1.0,0.0 380 | 2017-06-15,1.0,1.0,0.0 381 | 2017-06-16,1.0,1.0,0.0 382 | 2017-06-19,1.0,1.0,0.0 383 | 2017-06-20,1.0,1.0,0.0 384 | 2017-06-21,1.0,1.0,0.0 385 | 2017-06-22,1.0,1.0,0.0 386 | 2017-06-23,1.0,1.0,0.0 387 | 2017-06-26,1.0,1.0,0.0 388 | 2017-06-27,1.0,1.0,0.0 389 | 2017-06-28,1.0,1.0,0.0 390 | 2017-06-29,1.0,1.0,0.0 391 | 2017-06-30,1.0,1.0,0.0 392 | 2017-07-03,1.0,1.0,0.0 393 | 2017-07-04,1.0,1.0,0.0 394 | 2017-07-05,1.0,1.0,0.0 395 | 2017-07-06,1.0,1.0,0.0 396 | 2017-07-07,1.0,1.0,0.0 397 | 2017-07-10,1.0,1.0,0.0 398 | 2017-07-11,1.0,1.0,0.0 399 | 2017-07-12,1.0,1.0,0.0 400 | 2017-07-13,1.0,1.0,0.0 401 | 2017-07-14,1.0,1.0,0.0 402 | 2017-07-17,1.0,1.0,0.0 403 | 2017-07-18,1.0,1.0,0.0 404 | 2017-07-19,1.0,1.0,0.0 405 | 2017-07-20,1.0,1.0,0.0 406 | 2017-07-21,1.0,1.0,0.0 407 | 2017-07-24,1.0,1.0,0.0 408 | 2017-07-25,1.0,1.0,0.0 409 | 2017-07-26,1.0,1.0,0.0 410 | 2017-07-27,1.0,1.0,0.0 411 | 2017-07-28,1.0,1.0,0.0 412 | 2017-07-31,1.0,1.0,0.0 413 | 2017-08-01,1.0,1.0,0.0 414 | 2017-08-02,1.0,1.0,0.0 415 | 2017-08-03,1.0,1.0,0.0 416 | 2017-08-04,1.0,1.0,0.0 417 | 2017-08-07,1.0,1.0,0.0 418 | 2017-08-08,1.0,1.0,0.0 419 | 2017-08-09,1.0,1.0,0.0 420 | 2017-08-10,1.0,1.0,0.0 421 | 2017-08-11,1.0,1.0,0.0 422 | 2017-08-14,1.0,1.0,0.0 423 | 2017-08-15,1.0,1.0,0.0 424 | 2017-08-16,1.0,1.0,0.0 425 | 2017-08-17,1.0,1.0,0.0 426 | 2017-08-18,1.0,1.0,0.0 427 | 2017-08-21,1.0,1.0,0.0 428 | 2017-08-22,1.0,1.0,0.0 429 | 2017-08-23,1.0,1.0,0.0 430 | 2017-08-24,1.0,1.0,0.0 431 | 2017-08-25,1.0,1.0,0.0 432 | 2017-08-28,1.0,1.0,0.0 433 | 2017-08-29,1.0,1.0,0.0 434 | 2017-08-30,1.0,1.0,0.0 435 | 2017-08-31,1.0,1.0,0.0 436 | 2017-09-01,1.0,1.0,0.0 437 | 2017-09-04,1.0,1.0,0.0 438 | 2017-09-05,1.0,1.0,0.0 439 | 2017-09-06,1.0,1.0,0.0 440 | 2017-09-07,1.0,1.0,0.0 441 | 2017-09-08,1.0,1.0,0.0 442 | 2017-09-11,1.0,1.0,0.0 443 | 2017-09-12,1.0,1.0,0.0 444 | 2017-09-13,1.0,1.0,0.0 445 | 2017-09-14,1.0,1.0,0.0 446 | 2017-09-15,1.0,1.0,0.0 447 | 2017-09-18,1.0,1.0,0.0 448 | 2017-09-19,1.0,1.0,0.0 449 | 2017-09-20,1.0,1.0,0.0 450 | 2017-09-21,1.0,1.0,0.0 451 | 2017-09-22,1.0,1.0,0.0 452 | 2017-09-25,1.0,1.0,0.0 453 | 2017-09-26,1.0,1.0,0.0 454 | 2017-09-27,1.0,1.0,0.0 455 | 2017-09-28,1.0,1.0,0.0 456 | 2017-09-29,1.0,1.0,0.0 457 | 2017-10-02,1.0,1.0,0.0 458 | 2017-10-03,1.0,1.0,0.0 459 | 2017-10-04,1.0,1.0,0.0 460 | 2017-10-05,1.0,1.0,0.0 461 | 2017-10-06,1.0,1.0,0.0 462 | 2017-10-09,1.0,1.0,0.0 463 | 2017-10-10,1.0,1.0,0.0 464 | 2017-10-11,1.0,1.0,0.0 465 | 2017-10-12,1.0,1.0,0.0 466 | 2017-10-13,1.0,1.0,0.0 467 | 2017-10-16,1.0,1.0,0.0 468 | 2017-10-17,1.0,1.0,0.0 469 | 2017-10-18,1.0,1.0,0.0 470 | 2017-10-19,1.0,1.0,0.0 471 | 2017-10-20,1.0,1.0,0.0 472 | 2017-10-23,1.0,1.0,0.0 473 | 2017-10-24,1.0,1.0,0.0 474 | 2017-10-25,1.0,1.0,0.0 475 | 2017-10-26,1.0,1.0,0.0 476 | 2017-10-27,1.0,1.0,0.0 477 | 2017-10-30,1.0,1.0,0.0 478 | 2017-10-31,1.0,1.0,0.0 479 | 2017-11-01,1.0,1.0,0.0 480 | 2017-11-02,1.0,1.0,0.0 481 | 2017-11-03,1.0,1.0,0.0 482 | 2017-11-06,1.0,1.0,0.0 483 | 2017-11-07,1.0,1.0,0.0 484 | 2017-11-08,1.0,1.0,0.0 485 | 2017-11-09,1.0,1.0,0.0 486 | 2017-11-10,1.0,1.0,0.0 487 | 2017-11-13,1.0,1.0,0.0 488 | 2017-11-14,1.0,1.0,0.0 489 | 2017-11-15,1.0,1.0,0.0 490 | 2017-11-16,1.0,1.0,0.0 491 | 2017-11-17,1.0,1.0,0.0 492 | 2017-11-20,1.0,1.0,0.0 493 | 2017-11-21,1.0,1.0,0.0 494 | 2017-11-22,1.0,1.0,0.0 495 | 2017-11-23,1.0,1.0,0.0 496 | 2017-11-24,1.0,1.0,0.0 497 | 2017-11-27,1.0,1.0,0.0 498 | 2017-11-28,1.0,1.0,0.0 499 | 2017-11-29,1.0,1.0,0.0 500 | 2017-11-30,1.0,1.0,0.0 501 | 2017-12-01,1.0,1.0,0.0 502 | 2017-12-04,1.0,1.0,0.0 503 | 2017-12-05,1.0,1.0,0.0 504 | 2017-12-06,1.0,1.0,0.0 505 | 2017-12-07,1.0,1.0,0.0 506 | -------------------------------------------------------------------------------- /tests/test_data/returns.csv: -------------------------------------------------------------------------------- 1 | 2016-01-04,2.16420955433 2 | 2016-01-05,3.21963118331 3 | 2016-01-06,0.890280110274 4 | 2016-01-07,0.798731209228 5 | 2016-01-08,0.307379650145 6 | 2016-01-11,1.59831707812 7 | 2016-01-12,0.88271274164 8 | 2016-01-13,0.77753756012 9 | 2016-01-14,1.28892080939 10 | 2016-01-15,-0.541028037651 11 | 2016-01-18,-1.89937122039 12 | 2016-01-19,0.122271178453 13 | 2016-01-20,0.815388949389 14 | 2016-01-21,-0.141425332724 15 | 2016-01-22,3.00213798426 16 | 2016-01-25,0.533109945299 17 | 2016-01-26,-2.86858221585 18 | 2016-01-27,-0.191563180222 19 | 2016-01-28,2.43267052951 20 | 2016-01-29,-0.689629567983 21 | 2016-02-01,-2.46857090225 22 | 2016-02-02,0.244505204607 23 | 2016-02-03,-0.947726483363 24 | 2016-02-04,-0.475305004218 25 | 2016-02-05,-1.82663812777 26 | 2016-02-08,-0.508564063334 27 | 2016-02-09,-1.69143732169 28 | 2016-02-10,0.400149642192 29 | 2016-02-11,0.368989120123 30 | 2016-02-12,-0.997063259668 31 | 2016-02-15,-1.03201360932 32 | 2016-02-16,-2.53942888438 33 | 2016-02-17,-0.224354793955 34 | 2016-02-18,-1.16741609144 35 | 2016-02-19,-0.855352968587 36 | 2016-02-22,0.858073472935 37 | 2016-02-23,-0.0954251358104 38 | 2016-02-24,-0.282468449763 39 | 2016-02-25,-1.44964681395 40 | 2016-02-26,-0.255387189898 41 | 2016-02-29,-0.264323353829 42 | 2016-03-01,-1.07058124655 43 | 2016-03-02,3.38414136983 44 | 2016-03-03,0.998854735347 45 | 2016-03-04,-0.0163008945794 46 | 2016-03-07,0.819268123409 47 | 2016-03-08,1.18491401456 48 | 2016-03-09,1.06293956537 49 | 2016-03-10,1.79637051463 50 | 2016-03-11,0.528901456148 51 | 2016-03-14,0.535391635914 52 | 2016-03-15,-0.301088290328 53 | 2016-03-16,0.770497780535 54 | 2016-03-17,-1.1610737922 55 | 2016-03-18,3.40345681791 56 | 2016-03-21,2.7736036187 57 | 2016-03-22,1.04883926804 58 | 2016-03-23,0.534453024845 59 | 2016-03-24,0.792241874683 60 | 2016-03-25,1.53628604191 61 | 2016-03-28,-0.722975259429 62 | 2016-03-29,1.62462407089 63 | 2016-03-30,-0.844202400059 64 | 2016-03-31,1.41411017676 65 | 2016-04-01,1.07975659325 66 | 2016-04-04,-0.230666883153 67 | 2016-04-05,-0.642502102383 68 | 2016-04-06,0.0405872165676 69 | 2016-04-07,0.368292061037 70 | 2016-04-08,-0.697054796069 71 | 2016-04-11,-1.05186589144 72 | 2016-04-12,0.801704932265 73 | 2016-04-13,3.32762426185 74 | 2016-04-14,0.204194062652 75 | 2016-04-15,-1.77749201533 76 | 2016-04-18,1.64510111632 77 | 2016-04-19,-1.57119336071 78 | 2016-04-20,-0.761930810788 79 | 2016-04-21,0.0467044137431 80 | 2016-04-22,-1.58528869716 81 | 2016-04-25,1.43149960312 82 | 2016-04-26,1.03697204831 83 | 2016-04-27,-0.381072542429 84 | 2016-04-28,-2.54498644417 85 | 2016-04-29,1.50497240428 86 | 2016-05-02,1.23958647672 87 | 2016-05-03,0.205805018603 88 | 2016-05-04,-0.352648323503 89 | 2016-05-05,-1.49295944192 90 | 2016-05-06,-0.438053344492 91 | 2016-05-09,-1.72894520467 92 | 2016-05-10,-2.86702155506 93 | 2016-05-11,-0.97682620458 94 | 2016-05-12,-1.05221826017 95 | 2016-05-13,0.803451599015 96 | 2016-05-16,-1.02580604037 97 | 2016-05-17,-1.20737631597 98 | 2016-05-18,0.35173032931 99 | 2016-05-19,1.59529470518 100 | 2016-05-20,3.49976389872 101 | 2016-05-23,-0.608561015518 102 | 2016-05-24,1.75492332661 103 | 2016-05-25,-0.976824518213 104 | 2016-05-26,-0.762357033605 105 | 2016-05-27,0.1817742094 106 | 2016-05-30,1.22739712328 107 | 2016-05-31,0.319908865373 108 | 2016-06-01,-1.35449594912 109 | 2016-06-02,0.362131321694 110 | 2016-06-03,2.21705179903 111 | 2016-06-06,-1.30192677619 112 | 2016-06-07,0.0178854991274 113 | 2016-06-08,-1.47753502024 114 | 2016-06-09,0.388687574166 115 | 2016-06-10,-0.835237798701 116 | 2016-06-13,-1.91738079234 117 | 2016-06-14,-0.126811429755 118 | 2016-06-15,-0.374984330112 119 | 2016-06-16,-0.575500480522 120 | 2016-06-17,1.10316676581 121 | 2016-06-20,-1.03470883988 122 | 2016-06-21,-0.430671456989 123 | 2016-06-22,-1.98501677538 124 | 2016-06-23,2.23195015682 125 | 2016-06-24,-2.27978858701 126 | 2016-06-27,-0.0547230933603 127 | 2016-06-28,-0.177375253824 128 | 2016-06-29,1.38628789473 129 | 2016-06-30,-2.10896133386 130 | 2016-07-01,-0.972559018228 131 | 2016-07-04,-1.69567561208 132 | 2016-07-05,-0.64888133472 133 | 2016-07-06,-1.74750120905 134 | 2016-07-07,0.612313110879 135 | 2016-07-08,-0.21348600543 136 | 2016-07-11,-2.37354641079 137 | 2016-07-12,2.34600563094 138 | 2016-07-13,-1.04336195757 139 | 2016-07-14,0.377637838315 140 | 2016-07-15,0.0338083935778 141 | 2016-07-18,0.909632054483 142 | 2016-07-19,0.844327206461 143 | 2016-07-20,0.895187523368 144 | 2016-07-21,0.165891923536 145 | 2016-07-22,1.9916643941 146 | 2016-07-25,-1.1091146781 147 | 2016-07-26,1.24390087496 148 | 2016-07-27,1.00094166192 149 | 2016-07-28,0.680678647468 150 | 2016-07-29,-0.0293931414154 151 | 2016-08-01,0.351603827883 152 | 2016-08-02,-0.798342249125 153 | 2016-08-03,0.205663294643 154 | 2016-08-04,-2.6809759772 155 | 2016-08-05,0.534199714544 156 | 2016-08-08,0.944042246308 157 | 2016-08-09,-1.85750356162 158 | 2016-08-10,-0.290528219864 159 | 2016-08-11,-0.32905864368 160 | 2016-08-12,-0.168931678387 161 | 2016-08-15,-1.53259737711 162 | 2016-08-16,-0.616398725272 163 | 2016-08-17,-1.46964751032 164 | 2016-08-18,2.09905648113 165 | 2016-08-19,0.238560449113 166 | 2016-08-22,-0.441756620999 167 | 2016-08-23,-0.410627662791 168 | 2016-08-24,-2.05285271364 169 | 2016-08-25,-1.30495612163 170 | 2016-08-26,0.975539898453 171 | 2016-08-29,0.615123595465 172 | 2016-08-30,-1.90191501412 173 | 2016-08-31,-0.721278127477 174 | 2016-09-01,-0.207989689119 175 | 2016-09-02,0.928175954722 176 | 2016-09-05,-2.20193539771 177 | 2016-09-06,0.675082663553 178 | 2016-09-07,-1.17348291224 179 | 2016-09-08,-2.3210435542 180 | 2016-09-09,0.140702484336 181 | 2016-09-12,0.702228038194 182 | 2016-09-13,1.27181335792 183 | 2016-09-14,0.145246056696 184 | 2016-09-15,-0.585503007615 185 | 2016-09-16,-1.39574486836 186 | 2016-09-19,-0.712681905613 187 | 2016-09-20,0.592172683913 188 | 2016-09-21,0.543331757931 189 | 2016-09-22,-0.927308943571 190 | 2016-09-23,0.673275235917 191 | 2016-09-26,-1.31082534404 192 | 2016-09-27,-3.27807107304 193 | 2016-09-28,-1.61808455048 194 | 2016-09-29,-2.45734574515 195 | 2016-09-30,1.81236268769 196 | 2016-10-03,0.344615177338 197 | 2016-10-04,-1.96990593741 198 | 2016-10-05,-1.05332957456 199 | 2016-10-06,1.99902579095 200 | 2016-10-07,2.31913065504 201 | 2016-10-10,-1.71455092288 202 | 2016-10-11,1.12295599912 203 | 2016-10-12,-1.41305665793 204 | 2016-10-13,0.873445411669 205 | 2016-10-14,-0.992702158626 206 | 2016-10-17,-0.646236750223 207 | 2016-10-18,-0.542581106315 208 | 2016-10-19,2.41722229378 209 | 2016-10-20,0.512886806468 210 | 2016-10-21,3.23958416818 211 | 2016-10-24,1.51172970288 212 | 2016-10-25,-1.97088115697 213 | 2016-10-26,-0.0361537248081 214 | 2016-10-27,-1.79663107987 215 | 2016-10-28,-0.299407698529 216 | 2016-10-31,-1.88375165918 217 | 2016-11-01,1.14583539274 218 | 2016-11-02,-0.656287365929 219 | 2016-11-03,0.826878358349 220 | 2016-11-04,0.878824978593 221 | 2016-11-07,-1.55464949905 222 | 2016-11-08,0.108362171074 223 | 2016-11-09,0.7607252931 224 | 2016-11-10,-0.507196407513 225 | 2016-11-11,-0.893018454854 226 | 2016-11-14,-0.23438062666 227 | 2016-11-15,0.742226093711 228 | 2016-11-16,2.3599476867 229 | 2016-11-17,-2.67030547347 230 | 2016-11-18,0.148696655935 231 | 2016-11-21,-1.49634890187 232 | 2016-11-22,-0.257851092584 233 | 2016-11-23,1.9096369789 234 | 2016-11-24,-1.75362174434 235 | 2016-11-25,-2.03713562499 236 | 2016-11-28,-2.55586126117 237 | 2016-11-29,-0.985398500407 238 | 2016-11-30,2.73326706877 239 | 2016-12-01,0.436718057752 240 | 2016-12-02,1.62459501086 241 | 2016-12-05,1.80084477746 242 | 2016-12-06,-1.33308086694 243 | 2016-12-07,-1.79302308165 244 | 2016-12-08,2.06646014678 245 | 2016-12-09,0.174803695097 246 | 2016-12-12,-1.3798786479 247 | 2016-12-13,2.39830631055 248 | 2016-12-14,2.62229938628 249 | 2016-12-15,-1.17278693274 250 | 2016-12-16,-1.09589663123 251 | 2016-12-19,0.34849014948 252 | 2016-12-20,0.862131044321 253 | 2016-12-21,-0.928719129359 254 | 2016-12-22,-3.20040225054 255 | 2016-12-23,0.122270141027 256 | 2016-12-26,2.27022433928 257 | 2016-12-27,-3.30083634438 258 | 2016-12-28,-0.484237366838 259 | 2016-12-29,1.54666243088 260 | 2016-12-30,2.02694845146 261 | 2017-01-02,-1.13568489899 262 | 2017-01-03,-2.57018957359 263 | 2017-01-04,-0.646602296369 264 | 2017-01-05,2.34907016957 265 | 2017-01-06,-1.50553460473 266 | 2017-01-09,-1.83810500357 267 | 2017-01-10,1.28972667054 268 | 2017-01-11,-1.86512037748 269 | 2017-01-12,-0.443890229501 270 | 2017-01-13,-0.312779620076 271 | 2017-01-16,-0.995093604823 272 | 2017-01-17,1.27624134049 273 | 2017-01-18,-0.828481516298 274 | 2017-01-19,-1.48098736263 275 | 2017-01-20,0.549474843283 276 | 2017-01-23,0.260249928374 277 | 2017-01-24,0.674873372985 278 | 2017-01-25,0.619820009087 279 | 2017-01-26,-2.34383963544 280 | 2017-01-27,-2.10949881089 281 | 2017-01-30,1.96666125501 282 | 2017-01-31,-1.58649315855 283 | 2017-02-01,-0.532487258066 284 | 2017-02-02,0.971644247506 285 | 2017-02-03,0.535632107372 286 | 2017-02-06,-1.37595849837 287 | 2017-02-07,0.804908129643 288 | 2017-02-08,0.226021010764 289 | 2017-02-09,-1.92393843186 290 | 2017-02-10,1.00202586802 291 | 2017-02-13,-2.61169583121 292 | 2017-02-14,-0.354844934186 293 | 2017-02-15,-1.02494728473 294 | 2017-02-16,0.228443680958 295 | 2017-02-17,-3.43853205295 296 | 2017-02-20,0.98235484906 297 | 2017-02-21,-1.303577649 298 | 2017-02-22,0.731015644217 299 | 2017-02-23,-0.686764353276 300 | 2017-02-24,-1.10874559461 301 | 2017-02-27,-1.13311052405 302 | 2017-02-28,-0.706265342992 303 | 2017-03-01,-1.99602056214 304 | 2017-03-02,-1.77118921694 305 | 2017-03-03,-0.26399968974 306 | 2017-03-06,-3.04559895192 307 | 2017-03-07,1.50067606963 308 | 2017-03-08,0.272853172261 309 | 2017-03-09,0.553466545441 310 | 2017-03-10,-0.221014391134 311 | 2017-03-13,0.294451776784 312 | 2017-03-14,-0.526508664707 313 | 2017-03-15,-1.60134330844 314 | 2017-03-16,1.85428223205 315 | 2017-03-17,-0.0575180631839 316 | 2017-03-20,-0.804773583575 317 | 2017-03-21,0.0959239853297 318 | 2017-03-22,-0.0505395008888 319 | 2017-03-23,-0.665508142742 320 | 2017-03-24,2.18027033894 321 | 2017-03-27,1.27721523253 322 | 2017-03-28,0.0381972461105 323 | 2017-03-29,-1.52290214945 324 | 2017-03-30,0.956648485035 325 | 2017-03-31,0.951585622391 326 | 2017-04-03,-2.03368978779 327 | 2017-04-04,0.837201240864 328 | 2017-04-05,0.675320754703 329 | 2017-04-06,-1.38567147857 330 | 2017-04-07,-1.31631979878 331 | 2017-04-10,-2.1958092599 332 | 2017-04-11,0.550385238052 333 | 2017-04-12,-1.09750329041 334 | 2017-04-13,1.05577162309 335 | 2017-04-14,-1.62733919465 336 | 2017-04-17,-2.430297819 337 | 2017-04-18,-2.8584865773 338 | 2017-04-19,0.612572489773 339 | 2017-04-20,0.0780394187355 340 | 2017-04-21,1.81907008147 341 | 2017-04-24,0.533016516702 342 | 2017-04-25,1.62280310702 343 | 2017-04-26,-3.49101818025 344 | 2017-04-27,0.505912618034 345 | 2017-04-28,2.34497727936 346 | 2017-05-01,1.27982322983 347 | 2017-05-02,-3.28006352412 348 | 2017-05-03,0.558046942455 349 | 2017-05-04,-1.14088576872 350 | 2017-05-05,1.27990250842 351 | 2017-05-08,-2.6554831932 352 | 2017-05-09,0.305969120203 353 | 2017-05-10,2.36697493652 354 | 2017-05-11,0.901350548961 355 | 2017-05-12,1.47657485082 356 | 2017-05-15,-0.0249465082623 357 | 2017-05-16,-0.986723754665 358 | 2017-05-17,1.22650120974 359 | 2017-05-18,-1.26747907878 360 | 2017-05-19,0.469249912172 361 | 2017-05-22,-0.897163586484 362 | 2017-05-23,-0.201564266035 363 | 2017-05-24,-2.48901699082 364 | 2017-05-25,0.310530342949 365 | 2017-05-26,1.39993342151 366 | 2017-05-29,-1.32114985926 367 | 2017-05-30,-1.55939770421 368 | 2017-05-31,0.251878743216 369 | 2017-06-01,-0.720543762919 370 | 2017-06-02,-1.09234543399 371 | 2017-06-05,-2.31782526342 372 | 2017-06-06,1.62199773143 373 | 2017-06-07,-0.209915230395 374 | 2017-06-08,0.730383073908 375 | 2017-06-09,-1.52065275148 376 | 2017-06-12,-0.888903454012 377 | 2017-06-13,2.14437685725 378 | 2017-06-14,0.80654823367 379 | 2017-06-15,-0.0369352471997 380 | 2017-06-16,-1.52722797628 381 | 2017-06-19,-0.185615062136 382 | 2017-06-20,0.747712618986 383 | 2017-06-21,-0.382922482812 384 | 2017-06-22,-0.0824178900418 385 | 2017-06-23,1.63542459048 386 | 2017-06-26,-0.477665414151 387 | 2017-06-27,-0.726359595805 388 | 2017-06-28,-2.15638276459 389 | 2017-06-29,-0.376129645064 390 | 2017-06-30,-1.69955745668 391 | 2017-07-03,2.01065971035 392 | 2017-07-04,-0.729569532852 393 | 2017-07-05,0.625347950302 394 | 2017-07-06,0.951673860043 395 | 2017-07-07,-1.40118153706 396 | 2017-07-10,-0.80795495471 397 | 2017-07-11,0.415069440239 398 | 2017-07-12,-1.75791454491 399 | 2017-07-13,-1.00251266286 400 | 2017-07-14,-1.25462789997 401 | 2017-07-17,2.19697589072 402 | 2017-07-18,-0.448686570639 403 | 2017-07-19,1.3461216949 404 | 2017-07-20,0.471860167339 405 | 2017-07-21,-1.80069601033 406 | 2017-07-24,0.112565354251 407 | 2017-07-25,0.353891388233 408 | 2017-07-26,2.20426423196 409 | 2017-07-27,1.0142090195 410 | 2017-07-28,-0.829626091563 411 | 2017-07-31,0.000353288028221 412 | 2017-08-01,-1.42886114567 413 | 2017-08-02,-0.340757690955 414 | 2017-08-03,2.55597944625 415 | 2017-08-04,0.861145764153 416 | 2017-08-07,1.32198759659 417 | 2017-08-08,-0.0390397541084 418 | 2017-08-09,0.918851571578 419 | 2017-08-10,-1.17398999163 420 | 2017-08-11,0.781880216401 421 | 2017-08-14,-0.130218406447 422 | 2017-08-15,3.10640403635 423 | 2017-08-16,0.213238792126 424 | 2017-08-17,0.216607652142 425 | 2017-08-18,-0.716881597089 426 | 2017-08-21,-3.73674699662 427 | 2017-08-22,-1.70135071407 428 | 2017-08-23,-1.46939143935 429 | 2017-08-24,-2.04903708979 430 | 2017-08-25,-0.509864956148 431 | 2017-08-28,1.32668844699 432 | 2017-08-29,0.120516478373 433 | 2017-08-30,-0.789345873489 434 | 2017-08-31,0.193975917066 435 | 2017-09-01,-0.505107059727 436 | 2017-09-04,0.450000046009 437 | 2017-09-05,-1.11952813426 438 | 2017-09-06,-0.361841803858 439 | 2017-09-07,-1.08139691805 440 | 2017-09-08,-1.74327499448 441 | 2017-09-11,0.361855218159 442 | 2017-09-12,-0.152628361654 443 | 2017-09-13,-1.64989464856 444 | 2017-09-14,0.410757950451 445 | 2017-09-15,-0.530326700757 446 | 2017-09-18,-0.17493428176 447 | 2017-09-19,0.755092093784 448 | 2017-09-20,0.57603620811 449 | 2017-09-21,-2.39813670791 450 | 2017-09-22,2.19039229392 451 | 2017-09-25,-2.14517245505 452 | 2017-09-26,0.557856453616 453 | 2017-09-27,0.970994402874 454 | 2017-09-28,-1.7062662684 455 | 2017-09-29,2.289756245 456 | 2017-10-02,-2.21884039066 457 | 2017-10-03,-1.01688534564 458 | 2017-10-04,-0.259175509346 459 | 2017-10-05,-0.319289896615 460 | 2017-10-06,0.200042182949 461 | 2017-10-09,-0.0226113761569 462 | 2017-10-10,1.53034661666 463 | 2017-10-11,2.38475882145 464 | 2017-10-12,-0.53600982685 465 | 2017-10-13,1.83580320538 466 | 2017-10-16,1.33419812274 467 | 2017-10-17,-1.0697522211 468 | 2017-10-18,-1.1522665034 469 | 2017-10-19,0.674744963968 470 | 2017-10-20,-1.32389256982 471 | 2017-10-23,1.66367405489 472 | 2017-10-24,3.24047024041 473 | 2017-10-25,0.184048461979 474 | 2017-10-26,1.71065006077 475 | 2017-10-27,0.391009250722 476 | 2017-10-30,-0.703045138945 477 | 2017-10-31,0.990963037634 478 | 2017-11-01,0.775091407101 479 | 2017-11-02,0.0587659177434 480 | 2017-11-03,1.0674859235 481 | 2017-11-06,0.57254145092 482 | 2017-11-07,1.12671933158 483 | 2017-11-08,-0.570907316663 484 | 2017-11-09,1.58149159817 485 | 2017-11-10,1.48710113275 486 | 2017-11-13,0.310956546026 487 | 2017-11-14,1.61472697925 488 | 2017-11-15,1.70729437889 489 | 2017-11-16,-1.27034812155 490 | 2017-11-17,-0.525604960667 491 | 2017-11-20,0.214937582637 492 | 2017-11-21,0.702985855346 493 | 2017-11-22,-0.504772278 494 | 2017-11-23,0.318426777681 495 | 2017-11-24,1.0821632933 496 | 2017-11-27,0.619825773006 497 | 2017-11-28,-0.558634889801 498 | 2017-11-29,0.701991325725 499 | 2017-11-30,-0.10420659651 500 | 2017-12-01,-1.50572502032 501 | 2017-12-04,1.44843656704 502 | 2017-12-05,-0.317600794692 503 | 2017-12-06,0.429533271829 504 | 2017-12-07,-1.27730404508 505 | -------------------------------------------------------------------------------- /tests/test_data/factor_returns.csv: -------------------------------------------------------------------------------- 1 | ,materials,financials 2 | 2016-01-04,1.76405234597,0.400157208367 3 | 2016-01-05,0.978737984106,2.2408931992 4 | 2016-01-06,1.86755799015,-0.977277879876 5 | 2016-01-07,0.950088417526,-0.151357208298 6 | 2016-01-08,-0.103218851794,0.410598501938 7 | 2016-01-11,0.144043571161,1.45427350696 8 | 2016-01-12,0.761037725147,0.121675016493 9 | 2016-01-13,0.443863232745,0.333674327374 10 | 2016-01-14,1.49407907316,-0.205158263766 11 | 2016-01-15,0.313067701651,-0.854095739302 12 | 2016-01-18,-2.55298981583,0.65361859544 13 | 2016-01-19,0.86443619886,-0.742165020406 14 | 2016-01-20,2.26975462399,-1.4543656746 15 | 2016-01-21,0.0457585173014,-0.187183850026 16 | 2016-01-22,1.53277921436,1.4693587699 17 | 2016-01-25,0.154947425697,0.378162519602 18 | 2016-01-26,-0.88778574763,-1.98079646822 19 | 2016-01-27,-0.347912149326,0.156348969104 20 | 2016-01-28,1.23029068073,1.20237984878 21 | 2016-01-29,-0.387326817408,-0.302302750575 22 | 2016-02-01,-1.04855296507,-1.42001793718 23 | 2016-02-02,-1.70627019063,1.95077539523 24 | 2016-02-03,-0.509652181752,-0.438074301611 25 | 2016-02-04,-1.25279536005,0.777490355832 26 | 2016-02-05,-1.61389784756,-0.212740280214 27 | 2016-02-08,-0.895466561194,0.386902497859 28 | 2016-02-09,-0.510805137569,-1.18063218412 29 | 2016-02-10,-0.0281822283387,0.42833187053 30 | 2016-02-11,0.0665172223832,0.30247189774 31 | 2016-02-12,-0.634322093681,-0.362741165987 32 | 2016-02-15,-0.672460447776,-0.359553161541 33 | 2016-02-16,-0.813146282044,-1.72628260233 34 | 2016-02-17,0.177426142254,-0.401780936208 35 | 2016-02-18,-1.63019834697,0.462782255526 36 | 2016-02-19,-0.907298364383,0.0519453957961 37 | 2016-02-22,0.729090562178,0.128982910757 38 | 2016-02-23,1.13940068454,-1.23482582035 39 | 2016-02-24,0.402341641178,-0.68481009094 40 | 2016-02-25,-0.870797149182,-0.578849664764 41 | 2016-02-26,-0.311552532127,0.0561653422297 42 | 2016-02-29,-1.16514984078,0.900826486954 43 | 2016-03-01,0.46566243973,-1.53624368628 44 | 2016-03-02,1.4882521938,1.89588917603 45 | 2016-03-03,1.17877957116,-0.179924835812 46 | 2016-03-04,-1.07075262151,1.05445172693 47 | 2016-03-07,-0.403176946973,1.22244507038 48 | 2016-03-08,0.208274978077,0.976639036484 49 | 2016-03-09,0.356366397174,0.706573168192 50 | 2016-03-10,0.0105000207208,1.78587049391 51 | 2016-03-11,0.126912092704,0.401989363445 52 | 2016-03-14,1.88315069706,-1.34775906114 53 | 2016-03-15,-1.27048499849,0.969396708158 54 | 2016-03-16,-1.17312340511,1.94362118565 55 | 2016-03-17,-0.41361898076,-0.747454811441 56 | 2016-03-18,1.92294202648,1.48051479143 57 | 2016-03-21,1.86755896043,0.906044658275 58 | 2016-03-22,-0.861225685055,1.9100649531 59 | 2016-03-23,-0.268003370951,0.802456395796 60 | 2016-03-24,0.947251967774,-0.155010093091 61 | 2016-03-25,0.614079370346,0.922206671567 62 | 2016-03-28,0.376425531156,-1.09940079058 63 | 2016-03-29,0.298238174206,1.32638589669 64 | 2016-03-30,-0.694567859731,-0.149634540328 65 | 2016-03-31,-0.435153551722,1.84926372848 66 | 2016-04-01,0.672294757012,0.407461836241 67 | 2016-04-04,-0.769916074445,0.539249191292 68 | 2016-04-05,-0.674332660657,0.0318305582744 69 | 2016-04-06,-0.635846078379,0.676433294946 70 | 2016-04-07,0.576590816615,-0.208298755578 71 | 2016-04-08,0.396006712662,-1.09306150873 72 | 2016-04-11,-1.49125759271,0.439391701265 73 | 2016-04-12,0.166673495373,0.635031436892 74 | 2016-04-13,2.38314477486,0.94447948699 75 | 2016-04-14,-0.912822225444,1.1170162881 76 | 2016-04-15,-1.31590741051,-0.461584604815 77 | 2016-04-18,-0.0682416053246,1.71334272165 78 | 2016-04-19,-0.744754822048,-0.826438538659 79 | 2016-04-20,-0.0984525244254,-0.663478286362 80 | 2016-04-21,1.12663592211,-1.07993150836 81 | 2016-04-22,-1.14746865241,-0.437820044744 82 | 2016-04-25,-0.498032450692,1.92953205382 83 | 2016-04-26,0.949420806926,0.0875512413852 84 | 2016-04-27,-1.22543551883,0.844362976402 85 | 2016-04-28,-1.00021534739,-1.54477109678 86 | 2016-04-29,1.18802979235,0.316942611925 87 | 2016-05-02,0.920858823781,0.318727652943 88 | 2016-05-03,0.856830611903,-0.6510255933 89 | 2016-05-04,-1.03424284178,0.681594518282 90 | 2016-05-05,-0.803409664174,-0.68954977775 91 | 2016-05-06,-0.455532503517,0.0174791590251 92 | 2016-05-09,-0.353993911253,-1.37495129342 93 | 2016-05-10,-0.643618402833,-2.22340315222 94 | 2016-05-11,0.625231451027,-1.60205765561 95 | 2016-05-12,-1.10438333943,0.052165079261 96 | 2016-05-13,-0.739562996391,1.54301459541 97 | 2016-05-16,-1.29285690972,0.267050869349 98 | 2016-05-17,-0.0392828182275,-1.16809349774 99 | 2016-05-18,0.523276660532,-0.171546331222 100 | 2016-05-19,0.771790551214,0.823504153964 101 | 2016-05-20,2.16323594928,1.33652794944 102 | 2016-05-23,-0.369181837942,-0.239379177576 103 | 2016-05-24,1.09965959589,0.655263730723 104 | 2016-05-25,0.640131526098,-1.61695604431 105 | 2016-05-26,-0.0243261243989,-0.738030909206 106 | 2016-05-27,0.279924599043,-0.098150389643 107 | 2016-05-30,0.910178908093,0.317218215191 108 | 2016-05-31,0.786327962109,-0.466419096736 109 | 2016-06-01,-0.944446255918,-0.410049693203 110 | 2016-06-02,-0.0170204138614,0.379151735555 111 | 2016-06-03,2.25930895069,-0.0422571516606 112 | 2016-06-06,-0.955945000493,-0.345981775699 113 | 2016-06-07,-0.463595974646,0.481481473773 114 | 2016-06-08,-1.54079701444,0.0632619942003 115 | 2016-06-09,0.156506537965,0.2321810362 116 | 2016-06-10,-0.597316068965,-0.237921729736 117 | 2016-06-13,-1.42406090898,-0.493319883362 118 | 2016-06-14,-0.542861476017,0.416050046261 119 | 2016-06-15,-1.15618243182,0.78119810171 120 | 2016-06-16,1.49448454449,-2.06998502501 121 | 2016-06-17,0.426258730778,0.67690803503 122 | 2016-06-20,-0.637437025552,-0.397271814329 123 | 2016-06-21,-0.132880577587,-0.297790879402 124 | 2016-06-22,-0.309012969047,-1.67600380633 125 | 2016-06-23,1.15233156478,1.07961859204 126 | 2016-06-24,-0.813364259204,-1.4664243278 127 | 2016-06-27,0.521064876453,-0.575787969813 128 | 2016-06-28,0.141953163321,-0.319328417145 129 | 2016-06-29,0.69153875107,0.694749143656 130 | 2016-06-30,-0.725597378464,-1.3833639554 131 | 2016-07-01,-1.58293839734,0.610379379107 132 | 2016-07-04,-1.18885925778,-0.506816354299 133 | 2016-07-05,-0.596314038451,-0.0525672962695 134 | 2016-07-06,-1.93627980585,0.188778596794 135 | 2016-07-07,0.523891023834,0.0884220870447 136 | 2016-07-08,-0.310886171698,0.0974001662688 137 | 2016-07-11,0.39904634564,-2.77259275643 138 | 2016-07-12,1.95591230825,0.390093322688 139 | 2016-07-13,-0.652408582387,-0.390953375188 140 | 2016-07-14,0.493741777349,-0.116103939034 141 | 2016-07-15,-2.03068446778,2.06449286136 142 | 2016-07-18,-0.110540657232,1.02017271172 143 | 2016-07-19,-0.692049847784,1.53637705425 144 | 2016-07-20,0.286343688892,0.608843834475 145 | 2016-07-21,-1.04525336615,1.21114528968 146 | 2016-07-22,0.689818164535,1.30184622956 147 | 2016-07-25,-0.628087559642,-0.481027118461 148 | 2016-07-26,2.30391669768,-1.06001582272 149 | 2016-07-27,-0.135949700678,1.1368913626 150 | 2016-07-28,0.0977249677149,0.582953679753 151 | 2016-07-29,-0.399449029263,0.370055887848 152 | 2016-08-01,-1.30652685174,1.65813067962 153 | 2016-08-02,-0.118164045129,-0.680178203997 154 | 2016-08-03,0.666383082032,-0.460719787389 155 | 2016-08-04,-1.3342584714,-1.3467175058 156 | 2016-08-05,0.69377315269,-0.159573438146 157 | 2016-08-08,-0.133701559668,1.07774380598 158 | 2016-08-09,-1.12682580876,-0.730677752865 159 | 2016-08-10,-0.384879809181,0.0943515893171 160 | 2016-08-11,-0.0421714512906,-0.28688719239 161 | 2016-08-12,-0.0616264020956,-0.107305276291 162 | 2016-08-15,-0.719604388552,-0.812992988554 163 | 2016-08-16,0.274516357724,-0.890915082996 164 | 2016-08-17,-1.15735525919,-0.312292251126 165 | 2016-08-18,-0.157667016164,2.2567234973 166 | 2016-08-19,-0.704700275856,0.943260724969 167 | 2016-08-22,0.747188334205,-1.1889449552 168 | 2016-08-23,0.773252977403,-1.18388064019 169 | 2016-08-24,-2.659172238,0.606319524359 170 | 2016-08-25,-1.75589058344,0.450934461806 171 | 2016-08-26,-0.684010897737,1.65955079619 172 | 2016-08-29,1.06850939932,-0.453385803851 173 | 2016-08-30,-0.687837611029,-1.21407740309 174 | 2016-08-31,-0.440922632293,-0.280355495185 175 | 2016-09-01,-0.364693544392,0.156703855272 176 | 2016-09-02,0.578521497729,0.349654456993 177 | 2016-09-05,-0.764143923906,-1.4377914738 178 | 2016-09-06,1.3645318481,-0.68944918455 179 | 2016-09-07,-0.652293599935,-0.521189312301 180 | 2016-09-08,-1.84306955016,-0.47797400404 181 | 2016-09-09,-0.479655814008,0.620358298344 182 | 2016-09-12,0.698457149107,0.00377088908627 183 | 2016-09-13,0.931848374114,0.339964983801 184 | 2016-09-14,-0.0156821116026,0.160928168298 185 | 2016-09-15,-0.190653493581,-0.394849514033 186 | 2016-09-16,-0.267733536894,-1.12801133147 187 | 2016-09-19,0.280441705316,-0.99312361093 188 | 2016-09-20,0.841631264074,-0.249458580161 189 | 2016-09-21,0.0494949816501,0.493836776281 190 | 2016-09-22,0.643314465063,-1.57062340863 191 | 2016-09-23,-0.206903676164,0.880178912081 192 | 2016-09-26,-1.69810581943,0.387280475395 193 | 2016-09-27,-2.2555642294,-1.02250684364 194 | 2016-09-28,0.0386305518402,-1.65671510232 195 | 2016-09-29,-0.985510737684,-1.47183500746 196 | 2016-09-30,1.64813493221,0.164227755487 197 | 2016-10-03,0.567290277853,-0.222675100515 198 | 2016-10-04,-0.353431748757,-1.61647418865 199 | 2016-10-05,-0.291837362748,-0.761492211812 200 | 2016-10-06,0.857923924292,1.14110186666 201 | 2016-10-07,1.46657871557,0.852551939461 202 | 2016-10-10,-0.598653936923,-1.11589698596 203 | 2016-10-11,0.766663181645,0.356292817472 204 | 2016-10-12,-1.76853845068,0.355481792744 205 | 2016-10-13,0.814519822488,0.0589255891816 206 | 2016-10-14,-0.185053671009,-0.807648487616 207 | 2016-10-17,-1.44653469956,0.80029794934 208 | 2016-10-18,-0.309114444772,-0.233466661544 209 | 2016-10-19,1.73272118692,0.684501106859 210 | 2016-10-20,0.370825001281,0.142061805187 211 | 2016-10-21,1.51999486077,1.71958930742 212 | 2016-10-24,0.92950511148,0.582224591398 213 | 2016-10-25,-2.09460307121,0.123721914234 214 | 2016-10-26,-0.130106954194,0.0939532293856 215 | 2016-10-27,0.943046087323,-2.73967716719 216 | 2016-10-28,-0.56931205347,0.269904354941 217 | 2016-10-31,-0.466845546053,-1.41690611313 218 | 2016-11-01,0.868963486897,0.276871905846 219 | 2016-11-02,-0.971104570444,0.314817204516 220 | 2016-11-03,0.82158571205,0.00529264629936 221 | 2016-11-04,0.800564803431,0.0782601751617 222 | 2016-11-07,-0.395228982654,-1.1594205164 223 | 2016-11-08,-0.0859307669716,0.194292938046 224 | 2016-11-09,0.875832761587,-0.115107468487 225 | 2016-11-10,0.457415606221,-0.964612013734 226 | 2016-11-11,-0.782629155828,-0.110389299027 227 | 2016-11-14,-1.05462846399,0.820247837325 228 | 2016-11-15,0.463130329319,0.279095764392 229 | 2016-11-16,0.338904125216,2.02104356148 230 | 2016-11-17,-0.468864187967,-2.2014412855 231 | 2016-11-18,0.199300196896,-0.0506035409617 232 | 2016-11-21,-0.51751904251,-0.978829859359 233 | 2016-11-22,-0.439189521802,0.181338429218 234 | 2016-11-23,-0.502816700643,2.41245367954 235 | 2016-11-24,-0.960504381633,-0.793117362708 236 | 2016-11-25,-2.28862004001,0.251484415022 237 | 2016-11-28,-2.0164066278,-0.539454633375 238 | 2016-11-29,-0.275670534561,-0.709727965847 239 | 2016-11-30,1.73887267745,0.994394391315 240 | 2016-12-01,1.3191368763,-0.88241881855 241 | 2016-12-02,1.12859406451,0.496000946344 242 | 2016-12-05,0.771405948677,1.02943882878 243 | 2016-12-06,-0.908763245959,-0.424317620978 244 | 2016-12-07,0.862596011328,-2.65561909297 245 | 2016-12-08,1.51332808257,0.553132064208 246 | 2016-12-09,-0.0457039606602,0.220507655757 247 | 2016-12-12,-1.02993528331,-0.349943364589 248 | 2016-12-13,1.10028433822,1.29802197233 249 | 2016-12-14,2.69622405256,-0.0739246662804 250 | 2016-12-15,-0.658552966805,-0.51423396594 251 | 2016-12-16,-1.01804187529,-0.0778547559409 252 | 2016-12-19,0.382732430012,-0.034242280532 253 | 2016-12-20,1.09634684567,-0.234215801345 254 | 2016-12-21,-0.347450652499,-0.58126847686 255 | 2016-12-22,-1.63263452623,-1.56776772431 256 | 2016-12-23,-1.17915793064,1.30142807166 257 | 2016-12-26,0.89526027289,1.37496406639 258 | 2016-12-27,-1.33221165459,-1.96862468979 259 | 2016-12-28,-0.660056320134,0.175818953296 260 | 2016-12-29,0.49869027491,1.04797215597 261 | 2016-12-30,0.284279670807,1.74266878066 262 | 2017-01-02,-0.222605680948,-0.913079218042 263 | 2017-01-03,-1.68121821549,-0.888971358095 264 | 2017-01-04,0.242117960985,-0.888720257354 265 | 2017-01-05,0.936742463535,1.41232770604 266 | 2017-01-06,-2.36958690523,0.864052300498 267 | 2017-01-09,-2.23960405866,0.40149905509 268 | 2017-01-10,1.22487056419,0.0648561063436 269 | 2017-01-11,-1.2796891732,-0.585431204278 270 | 2017-01-12,-0.261645445711,-0.18224478379 271 | 2017-01-13,-0.202896840767,-0.109882779309 272 | 2017-01-16,0.21348004891,-1.20857365373 273 | 2017-01-17,-0.24201982987,1.51826117036 274 | 2017-01-18,-0.384645423143,-0.443836093155 275 | 2017-01-19,1.07819730371,-2.55918466634 276 | 2017-01-20,1.18137860129,-0.631903758005 277 | 2017-01-23,0.163928572453,0.0963213559212 278 | 2017-01-24,0.94246811922,-0.267594746235 279 | 2017-01-25,-0.678025781564,1.29784579065 280 | 2017-01-26,-2.36417381714,0.0203341817052 281 | 2017-01-27,-1.34792542263,-0.761573388257 282 | 2017-01-30,2.01125668146,-0.0445954264559 283 | 2017-01-31,0.195069697151,-1.78156285571 284 | 2017-02-01,-0.729044658795,0.196557400729 285 | 2017-02-02,0.354757693113,0.616886554393 286 | 2017-02-03,0.00862789891758,0.527004208455 287 | 2017-02-06,0.453781912636,-1.829740411 288 | 2017-02-07,0.0370057219101,0.767902407733 289 | 2017-02-08,0.589879820735,-0.363858809971 290 | 2017-02-09,-0.805626507539,-1.11831192432 291 | 2017-02-10,-0.131054011541,1.13307987956 292 | 2017-02-13,-1.95180410148,-0.659891729729 293 | 2017-02-14,-1.13980245543,0.784957521241 294 | 2017-02-15,-0.554309626571,-0.470637658155 295 | 2017-02-16,-0.216949569937,0.445393250895 296 | 2017-02-17,-0.39238899815,-3.0461430548 297 | 2017-02-20,0.543311891388,0.439042957672 298 | 2017-02-21,-0.219541028331,-1.08403662067 299 | 2017-02-22,0.351780110681,0.379235533536 300 | 2017-02-23,-0.470032882701,-0.216731470576 301 | 2017-02-24,-0.930156502524,-0.178589092087 302 | 2017-02-27,-1.55042934508,0.417318821032 303 | 2017-02-28,-0.944368490824,0.238103147832 304 | 2017-03-01,-1.40596291627,-0.59005764587 305 | 2017-03-02,-0.110489405066,-1.66069981187 306 | 2017-03-03,0.11514787314,-0.37914756288 307 | 2017-03-06,-1.74235619781,-1.30324275411 308 | 2017-03-07,0.605120084082,0.895555985551 309 | 2017-03-08,-0.13190863978,0.40476181204 310 | 2017-03-09,0.223843563313,0.329622982128 311 | 2017-03-10,1.28598400708,-1.50699839821 312 | 2017-03-13,0.676460732362,-0.382008955578 313 | 2017-03-14,-0.224258934252,-0.302249730455 314 | 2017-03-15,-0.375147116661,-1.22619619178 315 | 2017-03-16,0.183339199258,1.67094303279 316 | 2017-03-17,-0.0561330204488,-0.0013850427351 317 | 2017-03-20,-0.687299037157,-0.117474546418 318 | 2017-03-21,0.466166426034,-0.370242440704 319 | 2017-03-22,-0.453804041052,0.403264540163 320 | 2017-03-23,-0.918004769819,0.252496627077 321 | 2017-03-24,0.820321797261,1.35994854168 322 | 2017-03-27,-0.0903820072769,1.36759723981 323 | 2017-03-28,1.03440988648,-0.996212640371 324 | 2017-03-29,-1.21793851159,-0.304963637854 325 | 2017-03-30,1.02893549259,-0.07228700756 326 | 2017-03-31,-0.600657557658,1.55224318005 327 | 2017-04-03,0.286904488003,-2.32059427579 328 | 2017-04-04,0.317160626293,0.520040614571 329 | 2017-04-05,0.225608654471,0.449712100232 330 | 2017-04-06,-0.067275608923,-1.31839586964 331 | 2017-04-07,-0.37070400322,-0.945615795556 332 | 2017-04-10,-0.932740910794,-1.2630683491 333 | 2017-04-11,0.45248909264,0.0978961454126 334 | 2017-04-12,-0.448165362681,-0.64933792773 335 | 2017-04-13,-0.0234231050215,1.07919472811 336 | 2017-04-14,-2.0042157155,0.376876520851 337 | 2017-04-17,-0.545711974018,-1.88458584498 338 | 2017-04-18,-1.94570308316,-0.912783494135 339 | 2017-04-19,0.219509555793,0.39306293398 340 | 2017-04-20,-0.938981572678,1.01702099141 341 | 2017-04-21,1.42298349652,0.396086584957 342 | 2017-04-24,-0.591402667808,1.12441918451 343 | 2017-04-25,0.755395695663,0.867407411355 344 | 2017-04-26,-0.656463674972,-2.83455450527 345 | 2017-04-27,2.11679102148,-1.61087840345 346 | 2017-04-28,-0.0357680718602,2.38074535122 347 | 2017-05-01,0.330576756274,0.949246473558 348 | 2017-05-02,-1.50239656938,-1.77766695473 349 | 2017-05-03,-0.53270279198,1.09074973443 350 | 2017-05-04,-0.346249447647,-0.794636321071 351 | 2017-05-05,0.197967289945,1.08193521848 352 | 2017-05-08,-1.44494019907,-1.21054299412 353 | 2017-05-09,-0.788669254509,1.09463837471 354 | 2017-05-10,0.234821525949,2.13215341057 355 | 2017-05-11,0.936445725831,-0.0350951768697 356 | 2017-05-12,1.26507783809,0.211497012732 357 | 2017-05-15,-0.704921352507,0.679974844245 358 | 2017-05-16,-0.696326653861,-0.290397100804 359 | 2017-05-17,1.32778269596,-0.101281486217 360 | 2017-05-18,-0.803141387342,-0.464337691435 361 | 2017-05-19,1.02179058559,-0.552540673417 362 | 2017-05-22,-0.386870846851,-0.510292739634 363 | 2017-05-23,0.18392549434,-0.385489760376 364 | 2017-05-24,-1.60183604897,-0.887180941845 365 | 2017-05-25,-0.932789041506,1.24331938446 366 | 2017-05-26,0.812674042109,0.5872593794 367 | 2017-05-29,-0.505358317264,-0.815791541994 368 | 2017-05-30,-0.507517601657,-1.05188010255 369 | 2017-05-31,2.49720039159,-2.24532164837 370 | 2017-06-01,0.564008535074,-1.28455229799 371 | 2017-06-02,-0.104343491495,-0.988001942494 372 | 2017-06-05,-1.17762896248,-1.14019630093 373 | 2017-06-06,1.75498615374,-0.13298842231 374 | 2017-06-07,-0.765702194478,0.555786964083 375 | 2017-06-08,0.0103493145663,0.720033759342 376 | 2017-06-09,-1.82425665594,0.303603904462 377 | 2017-06-12,0.772694837102,-1.66159829111 378 | 2017-06-13,0.448195284423,1.69618157283 379 | 2017-06-14,-0.0148577033547,0.821405937025 380 | 2017-06-15,0.670570450311,-0.707505697511 381 | 2017-06-16,0.0397667345865,-1.56699471086 382 | 2017-06-19,-0.451303037103,0.265687974966 383 | 2017-06-20,0.723100493738,0.0246121252479 384 | 2017-06-21,0.719983730143,-1.10290621296 385 | 2017-06-22,-0.101697274555,0.0192793845131 386 | 2017-06-23,1.84959124668,-0.2141666562 387 | 2017-06-26,-0.499016637994,0.0213512238435 388 | 2017-06-27,-0.91911344487,0.192753849065 389 | 2017-06-28,-0.365055216546,-1.79132754804 390 | 2017-06-29,-0.0585865511339,-0.31754309393 391 | 2017-06-30,-1.63242330207,-0.0671341546145 392 | 2017-07-03,1.48935596207,0.521303748276 393 | 2017-07-04,0.611927192731,-1.34149672558 394 | 2017-07-05,0.476898368922,0.14844958138 395 | 2017-07-06,0.529045238334,0.422628621709 396 | 2017-07-07,-1.3597807255,-0.041400811558 397 | 2017-07-10,-0.757870860425,-0.0500840942848 398 | 2017-07-11,-0.897400926902,1.31247036714 399 | 2017-07-12,-0.858972388444,-0.898942156466 400 | 2017-07-13,0.0745864065436,-1.0770990694 401 | 2017-07-14,-0.424663302433,-0.829964597538 402 | 2017-07-17,1.41117206389,0.785803826831 403 | 2017-07-18,-0.0574695184654,-0.391217052174 404 | 2017-07-19,0.940917614575,0.405204080323 405 | 2017-07-20,0.498052404683,-0.0261922373443 406 | 2017-07-21,-1.68823002777,-0.11246598256 407 | 2017-07-24,-0.532489919209,0.64505527346 408 | 2017-07-25,1.01184243299,-0.657951044761 409 | 2017-07-26,0.468385234277,1.73587899769 410 | 2017-07-27,-0.667712720571,1.68192174007 411 | 2017-07-28,-0.852585847171,0.022959755608 412 | 2017-07-31,-0.0111456118418,0.0114988998701 413 | 2017-08-01,-0.837678041908,-0.591183103764 414 | 2017-08-02,-0.667720286359,0.326962595404 415 | 2017-08-03,0.33003511451,2.22594433174 416 | 2017-08-04,1.37098900629,-0.509843242138 417 | 2017-08-07,0.324869615796,0.997117980792 418 | 2017-08-08,0.0306018243385,-0.0696415784469 419 | 2017-08-09,0.0515749427699,0.867276628808 420 | 2017-08-10,-0.848320522805,-0.32566946882 421 | 2017-08-11,0.470433144846,0.311447071554 422 | 2017-08-14,0.239582759856,-0.369801166304 423 | 2017-08-15,0.972535789143,2.1338682472 424 | 2017-08-16,0.406415493676,-0.19317670155 425 | 2017-08-17,0.755740288895,-0.539132636753 426 | 2017-08-18,-0.749690344703,0.0328087476137 427 | 2017-08-21,-2.58279663297,-1.15395036365 428 | 2017-08-22,-0.347961855921,-1.35338885815 429 | 2017-08-23,-1.03264310189,-0.436748337458 430 | 2017-08-24,-1.64296529353,-0.40607179626 431 | 2017-08-25,-0.535270164533,0.025405208385 432 | 2017-08-28,1.15418403049,0.172504416493 433 | 2017-08-29,0.0210620213421,0.0994544570307 434 | 2017-08-30,0.227392775121,-1.01673864861 435 | 2017-08-31,-0.114775324771,0.308751241837 436 | 2017-09-01,-1.37075998254,0.865652922816 437 | 2017-09-04,1.08137603446,-0.631375988449 438 | 2017-09-05,-0.241337791453,-0.87819034281 439 | 2017-09-06,0.699380483588,-1.06122228745 440 | 2017-09-07,-0.222477010243,-0.858919907808 441 | 2017-09-08,0.0509542770113,-1.79422927149 442 | 2017-09-11,1.32646164237,-0.964606424206 443 | 2017-09-12,0.0598946831163,-0.21252304477 444 | 2017-09-13,-0.762114511922,-0.887780136636 445 | 2017-09-14,0.936398543552,-0.525640593102 446 | 2017-09-15,0.271170184637,-0.801496885394 447 | 2017-09-18,-0.647181431848,0.472247150088 448 | 2017-09-19,0.930408496111,-0.175316402327 449 | 2017-09-20,-1.42191987164,1.99795607975 450 | 2017-09-21,-0.856549308234,-1.54158739967 451 | 2017-09-22,2.59442458777,-0.404032293851 452 | 2017-09-25,-1.46173268826,-0.683439766789 453 | 2017-09-26,0.367544896022,0.190311557594 454 | 2017-09-27,-0.851729197254,1.82272360013 455 | 2017-09-28,-0.521579677993,-1.18468659041 456 | 2017-09-29,0.960693398461,1.32906284654 457 | 2017-10-02,-0.817493097616,-1.40134729304 458 | 2017-10-03,1.03043826742,-2.04732361306 459 | 2017-10-04,-1.2266216594,0.96744615005 460 | 2017-10-05,-0.0553525480224,-0.263937348593 461 | 2017-10-06,0.352816606494,-0.152774423545 462 | 2017-10-09,-1.29868672216,1.27607534601 463 | 2017-10-10,1.32501405289,0.205332563778 464 | 2017-10-11,0.045134015432,2.33962480602 465 | 2017-10-12,-0.276432845016,-0.259576981834 466 | 2017-10-13,0.364481249241,1.47132195614 467 | 2017-10-16,1.59277075442,-0.258572631677 468 | 2017-10-17,0.308331245959,-1.37808346706 469 | 2017-10-18,-0.311976107916,-0.840290395479 470 | 2017-10-19,-1.0068317523,1.68157671627 471 | 2017-10-20,-0.792286661806,-0.531605908011 472 | 2017-10-23,0.365848787917,1.29782526697 473 | 2017-10-24,0.481115126389,2.75935511402 474 | 2017-10-25,-0.0746679782511,0.25871644023 475 | 2017-10-26,0.275600673984,1.43504938679 476 | 2017-10-27,0.50723895111,-0.116229700387 477 | 2017-10-30,-0.947488594907,0.244443455962 478 | 2017-10-31,1.40134483129,-0.410381793658 479 | 2017-11-01,0.528943618417,0.246147788685 480 | 2017-11-02,0.863519658381,-0.804753740638 481 | 2017-11-03,2.34664703053,-1.27916110703 482 | 2017-11-06,-0.365551089986,0.938092540906 483 | 2017-11-07,0.296733172494,0.829986159081 484 | 2017-11-08,-0.496102333983,-0.0748049826803 485 | 2017-11-09,0.0122319836388,1.56925961454 486 | 2017-11-10,0.690429024383,0.796672108365 487 | 2017-11-13,-0.657926092537,0.968882638563 488 | 2017-11-14,0.225581663569,1.38914531568 489 | 2017-11-15,2.01406015492,-0.306765776027 490 | 2017-11-16,-0.406303130445,-0.864044991102 491 | 2017-11-17,-0.143579511716,-0.38202544895 492 | 2017-11-20,0.359504399571,-0.144566816934 493 | 2017-11-21,-0.361599280782,1.06458513613 494 | 2017-11-22,-0.937880231151,0.433107953151 495 | 2017-11-23,-0.405941727188,0.72436850487 496 | 2017-11-24,1.38526154672,-0.303098253424 497 | 2017-11-27,0.441032907273,0.178792865733 498 | 2017-11-28,-0.799422399543,0.240787509742 499 | 2017-11-29,0.289120505279,0.412870820446 500 | 2017-11-30,-0.19839889682,0.0941923003101 501 | 2017-12-01,-1.14761094484,-0.35811407548 502 | 2017-12-04,0.55596267971,0.892473887332 503 | 2017-12-05,-0.422314824125,0.104714029433 504 | 2017-12-06,0.228053325124,0.201479946704 505 | 2017-12-07,0.5407735853,-1.81807763038 506 | -------------------------------------------------------------------------------- /src/empyrical/utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2018 Quantopian, Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | import errno 16 | import warnings 17 | from datetime import datetime 18 | from functools import wraps, partial 19 | from os import makedirs, environ 20 | from os.path import expanduser, join, getmtime, isdir 21 | 22 | import numpy as np 23 | import pandas as pd 24 | from numpy.lib.stride_tricks import as_strided 25 | from pandas.tseries.offsets import BDay 26 | from pytz import UTC 27 | 28 | try: # incompatible with Python 3.12 29 | from pandas_datareader import data as web 30 | except ImportError: 31 | web = None 32 | 33 | try: 34 | import yfinance as yf 35 | except ImportError: 36 | yf = None 37 | 38 | try: 39 | # fast versions 40 | import bottleneck as bn 41 | 42 | def _wrap_function(f): 43 | @wraps(f) 44 | def wrapped(*args, **kwargs): 45 | out = kwargs.pop("out", None) 46 | data = f(*args, **kwargs) 47 | if out is None: 48 | out = data 49 | else: 50 | out[()] = data 51 | 52 | return out 53 | 54 | return wrapped 55 | 56 | nanmean = _wrap_function(bn.nanmean) 57 | nanstd = _wrap_function(bn.nanstd) 58 | nansum = _wrap_function(bn.nansum) 59 | nanmax = _wrap_function(bn.nanmax) 60 | nanmin = _wrap_function(bn.nanmin) 61 | nanargmax = _wrap_function(bn.nanargmax) 62 | nanargmin = _wrap_function(bn.nanargmin) 63 | except ImportError: 64 | # slower numpy 65 | nanmean = np.nanmean 66 | nanstd = np.nanstd 67 | nansum = np.nansum 68 | nanmax = np.nanmax 69 | nanmin = np.nanmin 70 | nanargmax = np.nanargmax 71 | nanargmin = np.nanargmin 72 | 73 | 74 | def roll(*args, **kwargs): 75 | """ 76 | Calculates a given statistic across a rolling time period. 77 | 78 | Parameters 79 | ---------- 80 | returns : pd.Series or np.ndarray 81 | Daily returns of the strategy, noncumulative. 82 | - See full explanation in :func:`~empyrical.stats.cum_returns`. 83 | factor_returns (optional): float / series 84 | Benchmark return to compare returns against. 85 | function: 86 | the function to run for each rolling window. 87 | window (keyword): int 88 | the number of periods included in each calculation. 89 | (other keywords): other keywords that are required to be passed to the 90 | function in the 'function' argument may also be passed in. 91 | 92 | Returns 93 | ------- 94 | np.ndarray, pd.Series 95 | depends on input type 96 | ndarray(s) ==> ndarray 97 | Series(s) ==> pd.Series 98 | 99 | A Series or ndarray of the results of the stat across the rolling 100 | window. 101 | 102 | """ 103 | func = kwargs.pop("function") 104 | window = kwargs.pop("window") 105 | if len(args) > 2: 106 | raise ValueError("Cannot pass more than 2 return sets") 107 | 108 | if len(args) == 2: 109 | if not isinstance(args[0], type(args[1])): 110 | raise ValueError("The two returns arguments are not the same.") 111 | 112 | if isinstance(args[0], np.ndarray): 113 | return _roll_ndarray(func, window, *args, **kwargs) 114 | return _roll_pandas(func, window, *args, **kwargs) 115 | 116 | 117 | def up(returns, factor_returns, **kwargs): 118 | """ 119 | Calculates a given statistic filtering only positive factor return periods. 120 | 121 | Parameters 122 | ---------- 123 | returns : pd.Series or np.ndarray 124 | Daily returns of the strategy, noncumulative. 125 | - See full explanation in :func:`~empyrical.stats.cum_returns`. 126 | factor_returns (optional): float / series 127 | Benchmark return to compare returns against. 128 | function: 129 | the function to run for each rolling window. 130 | (other keywords): other keywords that are required to be passed to the 131 | function in the 'function' argument may also be passed in. 132 | 133 | Returns 134 | ------- 135 | Same as the return of the function 136 | """ 137 | func = kwargs.pop("function") 138 | returns = returns[factor_returns > 0] 139 | factor_returns = factor_returns[factor_returns > 0] 140 | return func(returns, factor_returns, **kwargs) 141 | 142 | 143 | def down(returns, factor_returns, **kwargs): 144 | """ 145 | Calculates a given statistic filtering only negative factor return periods. 146 | 147 | Parameters 148 | ---------- 149 | returns : pd.Series or np.ndarray 150 | Daily returns of the strategy, noncumulative. 151 | - See full explanation in :func:`~empyrical.stats.cum_returns`. 152 | factor_returns (optional): float / series 153 | Benchmark return to compare returns against. 154 | function: 155 | the function to run for each rolling window. 156 | (other keywords): other keywords that are required to be passed to the 157 | function in the 'function' argument may also be passed in. 158 | 159 | Returns 160 | ------- 161 | Same as the return of the 'function' 162 | """ 163 | func = kwargs.pop("function") 164 | returns = returns[factor_returns < 0] 165 | factor_returns = factor_returns[factor_returns < 0] 166 | return func(returns, factor_returns, **kwargs) 167 | 168 | 169 | def _roll_ndarray(func, window, *args, **kwargs): 170 | data = [] 171 | for i in range(window, len(args[0]) + 1): 172 | rets = [s[i - window : i] for s in args] 173 | data.append(func(*rets, **kwargs)) 174 | return np.array(data) 175 | 176 | 177 | def _roll_pandas(func, window, *args, **kwargs): 178 | data = {} 179 | index_values = [] 180 | for i in range(window, len(args[0]) + 1): 181 | rets = [s.iloc[i - window : i] for s in args] 182 | index_value = args[0].index[i - 1] 183 | index_values.append(index_value) 184 | data[index_value] = func(*rets, **kwargs) 185 | return pd.Series( 186 | data, 187 | index=type(args[0].index)(index_values), 188 | dtype=np.float64, 189 | ) 190 | 191 | 192 | def cache_dir(environ=environ): 193 | try: 194 | return environ["EMPYRICAL_CACHE_DIR"] 195 | except KeyError: 196 | return join( 197 | environ.get( 198 | "XDG_CACHE_HOME", 199 | expanduser("~/.cache/"), 200 | ), 201 | "empyrical", 202 | ) 203 | 204 | 205 | def data_path(name): 206 | return join(cache_dir(), name) 207 | 208 | 209 | def ensure_directory(path): 210 | """ 211 | Ensure that a directory named "path" exists. 212 | """ 213 | 214 | try: 215 | makedirs(path) 216 | except OSError as exc: 217 | if exc.errno != errno.EEXIST or not isdir(path): 218 | raise 219 | 220 | 221 | def get_utc_timestamp(dt): 222 | """ 223 | Returns the Timestamp/DatetimeIndex 224 | with either localized or converted to UTC. 225 | 226 | Parameters 227 | ---------- 228 | dt : Timestamp/DatetimeIndex 229 | the date(s) to be converted 230 | 231 | Returns 232 | ------- 233 | same type as input 234 | date(s) converted to UTC 235 | """ 236 | 237 | dt = pd.to_datetime(dt) 238 | try: 239 | dt = dt.tz_localize("UTC") 240 | except TypeError: 241 | dt = dt.tz_convert("UTC") 242 | return dt 243 | 244 | 245 | _1_bday = BDay() 246 | 247 | 248 | def _1_bday_ago(): 249 | return pd.Timestamp.now().normalize() - _1_bday 250 | 251 | 252 | def get_fama_french( 253 | start="1-1-1970", 254 | end=None, 255 | datasets=("F-F_Research_Data_Factors_daily", "F-F_Momentum_Factor_daily"), 256 | ): 257 | """ 258 | Retrieve Fama-French factors via pandas-datareader 259 | Parameters 260 | ---------- 261 | start: str or datetime or Timestamp, start date 262 | end: str or datetime or Timestamp, end date 263 | datasets: list of factors (default is the five factors) 264 | Returns 265 | ------- 266 | pandas.DataFrame 267 | Percent change of Fama-French factors 268 | """ 269 | if web is None: 270 | raise ImportError("pandas-datareader is required to fetch Fama-French data") 271 | 272 | if not isinstance(start, datetime): 273 | start = pd.Timestamp(start).date() 274 | if not isinstance(end, datetime): 275 | end = pd.Timestamp(end).date() 276 | 277 | ff_function = partial( 278 | web.DataReader, 279 | data_source="famafrench", 280 | start=start, 281 | end=end, 282 | ) 283 | 284 | df = [ff_function(dataset)[0] for dataset in datasets] 285 | df = pd.concat(df, axis=1).dropna().div(100).rename(columns=str.strip) 286 | 287 | df.index = df.index.tz_localize("utc") 288 | return df 289 | 290 | 291 | def get_returns_cached(filepath, update_func, latest_dt, **kwargs): 292 | """ 293 | Get returns from a cached file if the cache is recent enough, 294 | otherwise, try to retrieve via a provided update function and 295 | update the cache file. 296 | Parameters 297 | ---------- 298 | filepath : str 299 | Path to cached csv file 300 | update_func : function 301 | Function to call in case cache is not up-to-date. 302 | latest_dt : pd.Timestamp (tz=UTC) 303 | Latest datetime required in csv file. 304 | **kwargs : Keyword arguments 305 | Optional keyword arguments will be passed to update_func() 306 | Returns 307 | ------- 308 | pandas.DataFrame 309 | DataFrame containing returns 310 | """ 311 | 312 | update_cache = False 313 | 314 | try: 315 | mtime = getmtime(filepath) 316 | except OSError as e: 317 | if e.errno != errno.ENOENT: 318 | raise 319 | update_cache = True 320 | else: 321 | 322 | file_dt = pd.Timestamp(mtime, unit="s") 323 | 324 | if latest_dt.tzinfo: 325 | file_dt = file_dt.tz_localize("utc") 326 | 327 | if file_dt < latest_dt: 328 | update_cache = True 329 | else: 330 | returns = pd.read_csv(filepath, index_col=0, parse_dates=True) 331 | if returns.index.tz != UTC: 332 | returns.index = returns.index.tz_localize("UTC") 333 | 334 | if update_cache: 335 | returns = update_func(**kwargs) 336 | try: 337 | ensure_directory(cache_dir()) 338 | except OSError as e: 339 | warnings.warn( 340 | "could not update cache: {}. {}: {}".format( 341 | filepath, 342 | type(e).__name__, 343 | e, 344 | ), 345 | UserWarning, 346 | ) 347 | 348 | try: 349 | returns.to_csv(filepath) 350 | except OSError as e: 351 | warnings.warn( 352 | "could not update cache {}. {}: {}".format( 353 | filepath, 354 | type(e).__name__, 355 | e, 356 | ), 357 | UserWarning, 358 | ) 359 | 360 | return returns 361 | 362 | 363 | def load_portfolio_risk_factors(filepath_prefix=None, start=None, end=None): 364 | """ 365 | Load risk factors Mkt-Rf, SMB, HML, Rf, and UMD. 366 | Data is stored in HDF5 file. If the data is more than 2 367 | days old, redownload from Dartmouth. 368 | Returns 369 | ------- 370 | five_factors : pd.DataFrame 371 | Risk factors timeseries. 372 | """ 373 | 374 | if start is None: 375 | start = "1/1/1970" 376 | if end is None: 377 | end = _1_bday_ago() 378 | 379 | start = get_utc_timestamp(start) 380 | end = get_utc_timestamp(end) 381 | 382 | if filepath_prefix is None: 383 | filepath = data_path("factors.csv") 384 | else: 385 | filepath = filepath_prefix 386 | 387 | five_factors = get_returns_cached(filepath, get_fama_french, end) 388 | 389 | return five_factors.loc[start:end] 390 | 391 | 392 | def get_treasury_yield(start=None, end=None, period="3MO"): 393 | """ 394 | Load treasury yields from FRED. 395 | 396 | Parameters 397 | ---------- 398 | start : date, optional 399 | Earliest date to fetch data for. 400 | Defaults to earliest date available. 401 | end : date, optional 402 | Latest date to fetch data for. 403 | Defaults to latest date available. 404 | period : {'1MO', '3MO', '6MO', 1', '5', '10'}, optional 405 | Which maturity to use. 406 | Returns 407 | ------- 408 | pd.Series 409 | Annual treasury yield for every day. 410 | """ 411 | 412 | if start is None: 413 | start = "1970-01-01" 414 | if end is None: 415 | end = _1_bday_ago() 416 | 417 | treasury = web.DataReader(f"DGS{period}", data_source="fred", start=start, end=end) 418 | return treasury.ffill() 419 | 420 | 421 | def get_symbol_returns_from_yahoo(symbol, start=None, end=None): 422 | """ 423 | Wrapper for pandas.io.data.get_data_yahoo(). 424 | Retrieves prices for symbol from yahoo and computes returns 425 | based on adjusted closing prices. 426 | 427 | Parameters 428 | ---------- 429 | symbol : str 430 | Yahoo symbol name to load, e.g. 'SPY' 431 | start : pandas.Timestamp compatible, optional 432 | Start date of time period to retrieve 433 | end : pandas.Timestamp compatible, optional 434 | End date of time period to retrieve 435 | 436 | Returns 437 | ------- 438 | pandas.DataFrame 439 | Returns of symbol in requested period. 440 | """ 441 | 442 | try: 443 | 444 | px = yf.download(symbol, start=start, end=end) 445 | rets = px[["Adj Close"]].pct_change().dropna() 446 | except Exception as e: 447 | warnings.warn( 448 | "Yahoo Finance read failed: {}".format(e), 449 | UserWarning, 450 | ) 451 | 452 | rets.index = rets.index.tz_localize("UTC") 453 | rets.columns = [symbol] 454 | return rets 455 | 456 | 457 | def default_returns_func(symbol, start=None, end=None): 458 | """ 459 | Gets returns for a symbol. 460 | Queries Yahoo Finance. Attempts to cache SPY. 461 | 462 | Parameters 463 | ---------- 464 | symbol : str 465 | Ticker symbol, e.g. APPL. 466 | start : date, optional 467 | Earliest date to fetch data for. 468 | Defaults to earliest date available. 469 | end : date, optional 470 | Latest date to fetch data for. 471 | Defaults to latest date available. 472 | 473 | Returns 474 | ------- 475 | pd.Series 476 | Daily returns for the symbol. 477 | - See full explanation in tears.create_full_tear_sheet (returns). 478 | """ 479 | 480 | if start is None: 481 | start = "1970-01-01" 482 | if end is None: 483 | end = _1_bday_ago() 484 | 485 | start = get_utc_timestamp(start) 486 | end = get_utc_timestamp(end) 487 | 488 | if symbol == "SPY": 489 | filepath = data_path("spy.csv") 490 | rets = get_returns_cached( 491 | filepath, 492 | get_symbol_returns_from_yahoo, 493 | end, 494 | symbol="SPY", 495 | start="1/1/1970", 496 | end=datetime.now(), 497 | ) 498 | rets = rets[start:end] 499 | else: 500 | rets = get_symbol_returns_from_yahoo(symbol, start=start, end=end) 501 | 502 | return rets[symbol] 503 | 504 | 505 | def rolling_window(array, length, mutable=False): 506 | """ 507 | Restride an array of shape 508 | 509 | (X_0, ... X_N) 510 | 511 | into an array of shape 512 | 513 | (length, X_0 - length + 1, ... X_N) 514 | 515 | where each slice at index i along the first axis is equivalent to 516 | 517 | result[i] = array[length * i:length * (i + 1)] 518 | 519 | Parameters 520 | ---------- 521 | array : np.ndarray 522 | The base array. 523 | length : int 524 | Length of the synthetic first axis to generate. 525 | mutable : bool, optional 526 | Return a mutable array? The returned array shares the same memory as 527 | the input array. This means that writes into the returned array affect 528 | ``array``. The returned array also uses strides to map the same values 529 | to multiple indices. Writes to a single index may appear to change many 530 | values in the returned array. 531 | 532 | Returns 533 | ------- 534 | out : np.ndarray 535 | 536 | Example 537 | ------- 538 | >>> from numpy import arange 539 | >>> a = arange(25).reshape(5, 5) 540 | >>> a 541 | array([[ 0, 1, 2, 3, 4], 542 | [ 5, 6, 7, 8, 9], 543 | [10, 11, 12, 13, 14], 544 | [15, 16, 17, 18, 19], 545 | [20, 21, 22, 23, 24]]) 546 | 547 | >>> rolling_window(a, 2) 548 | array([[[ 0, 1, 2, 3, 4], 549 | [ 5, 6, 7, 8, 9]], 550 | 551 | [[ 5, 6, 7, 8, 9], 552 | [10, 11, 12, 13, 14]], 553 | 554 | [[10, 11, 12, 13, 14], 555 | [15, 16, 17, 18, 19]], 556 | 557 | [[15, 16, 17, 18, 19], 558 | [20, 21, 22, 23, 24]]]) 559 | """ 560 | if not length: 561 | raise ValueError("Can't have 0-length window") 562 | 563 | orig_shape = array.shape 564 | if not orig_shape: 565 | raise IndexError("Can't restride a scalar.") 566 | elif orig_shape[0] < length: 567 | raise IndexError( 568 | "Can't restride array of shape {shape} with" 569 | " a window length of {len}".format( 570 | shape=orig_shape, 571 | len=length, 572 | ) 573 | ) 574 | 575 | num_windows = orig_shape[0] - length + 1 576 | new_shape = (num_windows, length) + orig_shape[1:] 577 | 578 | new_strides = (array.strides[0],) + array.strides 579 | 580 | out = as_strided(array, new_shape, new_strides) 581 | out.setflags(write=mutable) 582 | return out 583 | 584 | 585 | def _create_unary_vectorized_roll_function(function): 586 | def unary_vectorized_roll(arr, window, out=None, **kwargs): 587 | """ 588 | Computes the {human_readable} measure over a rolling window. 589 | 590 | Parameters 591 | ---------- 592 | arr : array-like 593 | The array to compute the rolling {human_readable} over. 594 | window : int 595 | Size of the rolling window in terms of the periodicity of the data. 596 | out : array-like, optional 597 | Array to use as output buffer. 598 | If not passed, a new array will be created. 599 | **kwargs 600 | Forwarded to :func:`~empyrical.{name}`. 601 | 602 | Returns 603 | ------- 604 | rolling_{name} : array-like 605 | The rolling {human_readable}. 606 | """ 607 | allocated_output = out is None 608 | 609 | if len(arr): 610 | out = function( 611 | rolling_window(_flatten(arr), min(len(arr), window)).T, 612 | out=out, 613 | **kwargs, 614 | ) 615 | else: 616 | out = np.empty(0, dtype="float64") 617 | 618 | if allocated_output and isinstance(arr, pd.Series): 619 | out = pd.Series(out, index=arr.index[-len(out) :]) 620 | 621 | return out 622 | 623 | unary_vectorized_roll.__doc__ = unary_vectorized_roll.__doc__.format( 624 | name=function.__name__, 625 | human_readable=function.__name__.replace("_", " "), 626 | ) 627 | unary_vectorized_roll.__name__ = f"rolling_{function.__name__}" 628 | 629 | return unary_vectorized_roll 630 | 631 | 632 | def _create_binary_vectorized_roll_function(function): 633 | def binary_vectorized_roll(lhs, rhs, window, out=None, **kwargs): 634 | """ 635 | Computes the {human_readable} measure over a rolling window. 636 | 637 | Parameters 638 | ---------- 639 | lhs : array-like 640 | The first array to pass to the rolling {human_readable}. 641 | rhs : array-like 642 | The second array to pass to the rolling {human_readable}. 643 | window : int 644 | Size of the rolling window in terms of the periodicity of the data. 645 | out : array-like, optional 646 | Array to use as output buffer. 647 | If not passed, a new array will be created. 648 | **kwargs 649 | Forwarded to :func:`~empyrical.{name}`. 650 | 651 | Returns 652 | ------- 653 | rolling_{name} : array-like 654 | The rolling {human_readable}. 655 | """ 656 | allocated_output = out is None 657 | 658 | if window >= 1 and len(lhs) and len(rhs): 659 | out = function( 660 | rolling_window(_flatten(lhs), min(len(lhs), window)).T, 661 | rolling_window(_flatten(rhs), min(len(rhs), window)).T, 662 | out=out, 663 | **kwargs, 664 | ) 665 | elif allocated_output: 666 | out = np.empty(0, dtype="float64") 667 | else: 668 | out[()] = np.nan 669 | 670 | if allocated_output: 671 | if out.ndim == 1 and isinstance(lhs, pd.Series): 672 | out = pd.Series(out, index=lhs.index[-len(out) :]) 673 | elif out.ndim == 2 and isinstance(lhs, pd.Series): 674 | out = pd.DataFrame(out, index=lhs.index[-len(out) :]) 675 | return out 676 | 677 | binary_vectorized_roll.__doc__ = binary_vectorized_roll.__doc__.format( 678 | name=function.__name__, 679 | human_readable=function.__name__.replace("_", " "), 680 | ) 681 | 682 | binary_vectorized_roll.__name__ = f"rolling_{function.__name__}" 683 | 684 | return binary_vectorized_roll 685 | 686 | 687 | def _flatten(arr): 688 | return arr if not isinstance(arr, pd.Series) else arr.values 689 | 690 | 691 | def _aligned_series(*many_series): 692 | """ 693 | Return a new list of series containing the data in the input series, but 694 | with their indices aligned. NaNs will be filled in for missing values. 695 | 696 | Parameters 697 | ---------- 698 | *many_series 699 | The series to align. 700 | 701 | Returns 702 | ------- 703 | aligned_series : iterable[array-like] 704 | A new list of series containing the data in the input series, but 705 | with their indices aligned. NaNs will be filled in for missing values. 706 | 707 | """ 708 | head = many_series[0] 709 | tail = many_series[1:] 710 | n = len(head) 711 | if isinstance(head, np.ndarray) and all( 712 | len(s) == n and isinstance(s, np.ndarray) for s in tail 713 | ): 714 | # optimization: ndarrays of the same length are already aligned 715 | return many_series 716 | 717 | # dataframe has no ``itervalues`` 718 | return (v for _, v in pd.concat(map(_to_pandas, many_series), axis=1).items()) 719 | 720 | 721 | def _to_pandas(ob): 722 | """Convert an array-like to a pandas object. 723 | 724 | Parameters 725 | ---------- 726 | ob : array-like 727 | The object to convert. 728 | 729 | Returns 730 | ------- 731 | pandas_structure : pd.Series or pd.DataFrame 732 | The correct structure based on the dimensionality of the data. 733 | """ 734 | if isinstance(ob, (pd.Series, pd.DataFrame)): 735 | return ob 736 | 737 | if ob.ndim == 1: 738 | return pd.Series(ob) 739 | elif ob.ndim == 2: 740 | return pd.DataFrame(ob) 741 | else: 742 | raise ValueError( 743 | "cannot convert array of dim > 2 to a pandas structure", 744 | ) 745 | 746 | 747 | def _adjust_returns(returns, adjustment_factor): 748 | """ 749 | Returns the returns series adjusted by adjustment_factor. Optimizes for the 750 | case of adjustment_factor being 0 by returning returns itself, not a copy! 751 | 752 | Parameters 753 | ---------- 754 | returns : pd.Series or np.ndarray 755 | adjustment_factor : pd.Series or np.ndarray or float or int 756 | 757 | Returns 758 | ------- 759 | adjusted_returns : array-like 760 | """ 761 | if isinstance(adjustment_factor, (float, int)) and adjustment_factor == 0: 762 | return returns 763 | return returns - adjustment_factor 764 | -------------------------------------------------------------------------------- /tests/test_data/factor_loadings.csv: -------------------------------------------------------------------------------- 1 | dt,ticker,materials,financials 2 | 2016-01-04,19001,1.0,1.0 3 | 2016-01-04,19002,1.0,1.0 4 | 2016-01-05,19001,1.0,1.0 5 | 2016-01-05,19002,1.0,1.0 6 | 2016-01-06,19001,1.0,1.0 7 | 2016-01-06,19002,1.0,1.0 8 | 2016-01-07,19001,1.0,1.0 9 | 2016-01-07,19002,1.0,1.0 10 | 2016-01-08,19001,1.0,1.0 11 | 2016-01-08,19002,1.0,1.0 12 | 2016-01-11,19001,1.0,1.0 13 | 2016-01-11,19002,1.0,1.0 14 | 2016-01-12,19001,1.0,1.0 15 | 2016-01-12,19002,1.0,1.0 16 | 2016-01-13,19001,1.0,1.0 17 | 2016-01-13,19002,1.0,1.0 18 | 2016-01-14,19001,1.0,1.0 19 | 2016-01-14,19002,1.0,1.0 20 | 2016-01-15,19001,1.0,1.0 21 | 2016-01-15,19002,1.0,1.0 22 | 2016-01-18,19001,1.0,1.0 23 | 2016-01-18,19002,1.0,1.0 24 | 2016-01-19,19001,1.0,1.0 25 | 2016-01-19,19002,1.0,1.0 26 | 2016-01-20,19001,1.0,1.0 27 | 2016-01-20,19002,1.0,1.0 28 | 2016-01-21,19001,1.0,1.0 29 | 2016-01-21,19002,1.0,1.0 30 | 2016-01-22,19001,1.0,1.0 31 | 2016-01-22,19002,1.0,1.0 32 | 2016-01-25,19001,1.0,1.0 33 | 2016-01-25,19002,1.0,1.0 34 | 2016-01-26,19001,1.0,1.0 35 | 2016-01-26,19002,1.0,1.0 36 | 2016-01-27,19001,1.0,1.0 37 | 2016-01-27,19002,1.0,1.0 38 | 2016-01-28,19001,1.0,1.0 39 | 2016-01-28,19002,1.0,1.0 40 | 2016-01-29,19001,1.0,1.0 41 | 2016-01-29,19002,1.0,1.0 42 | 2016-02-01,19001,1.0,1.0 43 | 2016-02-01,19002,1.0,1.0 44 | 2016-02-02,19001,1.0,1.0 45 | 2016-02-02,19002,1.0,1.0 46 | 2016-02-03,19001,1.0,1.0 47 | 2016-02-03,19002,1.0,1.0 48 | 2016-02-04,19001,1.0,1.0 49 | 2016-02-04,19002,1.0,1.0 50 | 2016-02-05,19001,1.0,1.0 51 | 2016-02-05,19002,1.0,1.0 52 | 2016-02-08,19001,1.0,1.0 53 | 2016-02-08,19002,1.0,1.0 54 | 2016-02-09,19001,1.0,1.0 55 | 2016-02-09,19002,1.0,1.0 56 | 2016-02-10,19001,1.0,1.0 57 | 2016-02-10,19002,1.0,1.0 58 | 2016-02-11,19001,1.0,1.0 59 | 2016-02-11,19002,1.0,1.0 60 | 2016-02-12,19001,1.0,1.0 61 | 2016-02-12,19002,1.0,1.0 62 | 2016-02-15,19001,1.0,1.0 63 | 2016-02-15,19002,1.0,1.0 64 | 2016-02-16,19001,1.0,1.0 65 | 2016-02-16,19002,1.0,1.0 66 | 2016-02-17,19001,1.0,1.0 67 | 2016-02-17,19002,1.0,1.0 68 | 2016-02-18,19001,1.0,1.0 69 | 2016-02-18,19002,1.0,1.0 70 | 2016-02-19,19001,1.0,1.0 71 | 2016-02-19,19002,1.0,1.0 72 | 2016-02-22,19001,1.0,1.0 73 | 2016-02-22,19002,1.0,1.0 74 | 2016-02-23,19001,1.0,1.0 75 | 2016-02-23,19002,1.0,1.0 76 | 2016-02-24,19001,1.0,1.0 77 | 2016-02-24,19002,1.0,1.0 78 | 2016-02-25,19001,1.0,1.0 79 | 2016-02-25,19002,1.0,1.0 80 | 2016-02-26,19001,1.0,1.0 81 | 2016-02-26,19002,1.0,1.0 82 | 2016-02-29,19001,1.0,1.0 83 | 2016-02-29,19002,1.0,1.0 84 | 2016-03-01,19001,1.0,1.0 85 | 2016-03-01,19002,1.0,1.0 86 | 2016-03-02,19001,1.0,1.0 87 | 2016-03-02,19002,1.0,1.0 88 | 2016-03-03,19001,1.0,1.0 89 | 2016-03-03,19002,1.0,1.0 90 | 2016-03-04,19001,1.0,1.0 91 | 2016-03-04,19002,1.0,1.0 92 | 2016-03-07,19001,1.0,1.0 93 | 2016-03-07,19002,1.0,1.0 94 | 2016-03-08,19001,1.0,1.0 95 | 2016-03-08,19002,1.0,1.0 96 | 2016-03-09,19001,1.0,1.0 97 | 2016-03-09,19002,1.0,1.0 98 | 2016-03-10,19001,1.0,1.0 99 | 2016-03-10,19002,1.0,1.0 100 | 2016-03-11,19001,1.0,1.0 101 | 2016-03-11,19002,1.0,1.0 102 | 2016-03-14,19001,1.0,1.0 103 | 2016-03-14,19002,1.0,1.0 104 | 2016-03-15,19001,1.0,1.0 105 | 2016-03-15,19002,1.0,1.0 106 | 2016-03-16,19001,1.0,1.0 107 | 2016-03-16,19002,1.0,1.0 108 | 2016-03-17,19001,1.0,1.0 109 | 2016-03-17,19002,1.0,1.0 110 | 2016-03-18,19001,1.0,1.0 111 | 2016-03-18,19002,1.0,1.0 112 | 2016-03-21,19001,1.0,1.0 113 | 2016-03-21,19002,1.0,1.0 114 | 2016-03-22,19001,1.0,1.0 115 | 2016-03-22,19002,1.0,1.0 116 | 2016-03-23,19001,1.0,1.0 117 | 2016-03-23,19002,1.0,1.0 118 | 2016-03-24,19001,1.0,1.0 119 | 2016-03-24,19002,1.0,1.0 120 | 2016-03-25,19001,1.0,1.0 121 | 2016-03-25,19002,1.0,1.0 122 | 2016-03-28,19001,1.0,1.0 123 | 2016-03-28,19002,1.0,1.0 124 | 2016-03-29,19001,1.0,1.0 125 | 2016-03-29,19002,1.0,1.0 126 | 2016-03-30,19001,1.0,1.0 127 | 2016-03-30,19002,1.0,1.0 128 | 2016-03-31,19001,1.0,1.0 129 | 2016-03-31,19002,1.0,1.0 130 | 2016-04-01,19001,1.0,1.0 131 | 2016-04-01,19002,1.0,1.0 132 | 2016-04-04,19001,1.0,1.0 133 | 2016-04-04,19002,1.0,1.0 134 | 2016-04-05,19001,1.0,1.0 135 | 2016-04-05,19002,1.0,1.0 136 | 2016-04-06,19001,1.0,1.0 137 | 2016-04-06,19002,1.0,1.0 138 | 2016-04-07,19001,1.0,1.0 139 | 2016-04-07,19002,1.0,1.0 140 | 2016-04-08,19001,1.0,1.0 141 | 2016-04-08,19002,1.0,1.0 142 | 2016-04-11,19001,1.0,1.0 143 | 2016-04-11,19002,1.0,1.0 144 | 2016-04-12,19001,1.0,1.0 145 | 2016-04-12,19002,1.0,1.0 146 | 2016-04-13,19001,1.0,1.0 147 | 2016-04-13,19002,1.0,1.0 148 | 2016-04-14,19001,1.0,1.0 149 | 2016-04-14,19002,1.0,1.0 150 | 2016-04-15,19001,1.0,1.0 151 | 2016-04-15,19002,1.0,1.0 152 | 2016-04-18,19001,1.0,1.0 153 | 2016-04-18,19002,1.0,1.0 154 | 2016-04-19,19001,1.0,1.0 155 | 2016-04-19,19002,1.0,1.0 156 | 2016-04-20,19001,1.0,1.0 157 | 2016-04-20,19002,1.0,1.0 158 | 2016-04-21,19001,1.0,1.0 159 | 2016-04-21,19002,1.0,1.0 160 | 2016-04-22,19001,1.0,1.0 161 | 2016-04-22,19002,1.0,1.0 162 | 2016-04-25,19001,1.0,1.0 163 | 2016-04-25,19002,1.0,1.0 164 | 2016-04-26,19001,1.0,1.0 165 | 2016-04-26,19002,1.0,1.0 166 | 2016-04-27,19001,1.0,1.0 167 | 2016-04-27,19002,1.0,1.0 168 | 2016-04-28,19001,1.0,1.0 169 | 2016-04-28,19002,1.0,1.0 170 | 2016-04-29,19001,1.0,1.0 171 | 2016-04-29,19002,1.0,1.0 172 | 2016-05-02,19001,1.0,1.0 173 | 2016-05-02,19002,1.0,1.0 174 | 2016-05-03,19001,1.0,1.0 175 | 2016-05-03,19002,1.0,1.0 176 | 2016-05-04,19001,1.0,1.0 177 | 2016-05-04,19002,1.0,1.0 178 | 2016-05-05,19001,1.0,1.0 179 | 2016-05-05,19002,1.0,1.0 180 | 2016-05-06,19001,1.0,1.0 181 | 2016-05-06,19002,1.0,1.0 182 | 2016-05-09,19001,1.0,1.0 183 | 2016-05-09,19002,1.0,1.0 184 | 2016-05-10,19001,1.0,1.0 185 | 2016-05-10,19002,1.0,1.0 186 | 2016-05-11,19001,1.0,1.0 187 | 2016-05-11,19002,1.0,1.0 188 | 2016-05-12,19001,1.0,1.0 189 | 2016-05-12,19002,1.0,1.0 190 | 2016-05-13,19001,1.0,1.0 191 | 2016-05-13,19002,1.0,1.0 192 | 2016-05-16,19001,1.0,1.0 193 | 2016-05-16,19002,1.0,1.0 194 | 2016-05-17,19001,1.0,1.0 195 | 2016-05-17,19002,1.0,1.0 196 | 2016-05-18,19001,1.0,1.0 197 | 2016-05-18,19002,1.0,1.0 198 | 2016-05-19,19001,1.0,1.0 199 | 2016-05-19,19002,1.0,1.0 200 | 2016-05-20,19001,1.0,1.0 201 | 2016-05-20,19002,1.0,1.0 202 | 2016-05-23,19001,1.0,1.0 203 | 2016-05-23,19002,1.0,1.0 204 | 2016-05-24,19001,1.0,1.0 205 | 2016-05-24,19002,1.0,1.0 206 | 2016-05-25,19001,1.0,1.0 207 | 2016-05-25,19002,1.0,1.0 208 | 2016-05-26,19001,1.0,1.0 209 | 2016-05-26,19002,1.0,1.0 210 | 2016-05-27,19001,1.0,1.0 211 | 2016-05-27,19002,1.0,1.0 212 | 2016-05-30,19001,1.0,1.0 213 | 2016-05-30,19002,1.0,1.0 214 | 2016-05-31,19001,1.0,1.0 215 | 2016-05-31,19002,1.0,1.0 216 | 2016-06-01,19001,1.0,1.0 217 | 2016-06-01,19002,1.0,1.0 218 | 2016-06-02,19001,1.0,1.0 219 | 2016-06-02,19002,1.0,1.0 220 | 2016-06-03,19001,1.0,1.0 221 | 2016-06-03,19002,1.0,1.0 222 | 2016-06-06,19001,1.0,1.0 223 | 2016-06-06,19002,1.0,1.0 224 | 2016-06-07,19001,1.0,1.0 225 | 2016-06-07,19002,1.0,1.0 226 | 2016-06-08,19001,1.0,1.0 227 | 2016-06-08,19002,1.0,1.0 228 | 2016-06-09,19001,1.0,1.0 229 | 2016-06-09,19002,1.0,1.0 230 | 2016-06-10,19001,1.0,1.0 231 | 2016-06-10,19002,1.0,1.0 232 | 2016-06-13,19001,1.0,1.0 233 | 2016-06-13,19002,1.0,1.0 234 | 2016-06-14,19001,1.0,1.0 235 | 2016-06-14,19002,1.0,1.0 236 | 2016-06-15,19001,1.0,1.0 237 | 2016-06-15,19002,1.0,1.0 238 | 2016-06-16,19001,1.0,1.0 239 | 2016-06-16,19002,1.0,1.0 240 | 2016-06-17,19001,1.0,1.0 241 | 2016-06-17,19002,1.0,1.0 242 | 2016-06-20,19001,1.0,1.0 243 | 2016-06-20,19002,1.0,1.0 244 | 2016-06-21,19001,1.0,1.0 245 | 2016-06-21,19002,1.0,1.0 246 | 2016-06-22,19001,1.0,1.0 247 | 2016-06-22,19002,1.0,1.0 248 | 2016-06-23,19001,1.0,1.0 249 | 2016-06-23,19002,1.0,1.0 250 | 2016-06-24,19001,1.0,1.0 251 | 2016-06-24,19002,1.0,1.0 252 | 2016-06-27,19001,1.0,1.0 253 | 2016-06-27,19002,1.0,1.0 254 | 2016-06-28,19001,1.0,1.0 255 | 2016-06-28,19002,1.0,1.0 256 | 2016-06-29,19001,1.0,1.0 257 | 2016-06-29,19002,1.0,1.0 258 | 2016-06-30,19001,1.0,1.0 259 | 2016-06-30,19002,1.0,1.0 260 | 2016-07-01,19001,1.0,1.0 261 | 2016-07-01,19002,1.0,1.0 262 | 2016-07-04,19001,1.0,1.0 263 | 2016-07-04,19002,1.0,1.0 264 | 2016-07-05,19001,1.0,1.0 265 | 2016-07-05,19002,1.0,1.0 266 | 2016-07-06,19001,1.0,1.0 267 | 2016-07-06,19002,1.0,1.0 268 | 2016-07-07,19001,1.0,1.0 269 | 2016-07-07,19002,1.0,1.0 270 | 2016-07-08,19001,1.0,1.0 271 | 2016-07-08,19002,1.0,1.0 272 | 2016-07-11,19001,1.0,1.0 273 | 2016-07-11,19002,1.0,1.0 274 | 2016-07-12,19001,1.0,1.0 275 | 2016-07-12,19002,1.0,1.0 276 | 2016-07-13,19001,1.0,1.0 277 | 2016-07-13,19002,1.0,1.0 278 | 2016-07-14,19001,1.0,1.0 279 | 2016-07-14,19002,1.0,1.0 280 | 2016-07-15,19001,1.0,1.0 281 | 2016-07-15,19002,1.0,1.0 282 | 2016-07-18,19001,1.0,1.0 283 | 2016-07-18,19002,1.0,1.0 284 | 2016-07-19,19001,1.0,1.0 285 | 2016-07-19,19002,1.0,1.0 286 | 2016-07-20,19001,1.0,1.0 287 | 2016-07-20,19002,1.0,1.0 288 | 2016-07-21,19001,1.0,1.0 289 | 2016-07-21,19002,1.0,1.0 290 | 2016-07-22,19001,1.0,1.0 291 | 2016-07-22,19002,1.0,1.0 292 | 2016-07-25,19001,1.0,1.0 293 | 2016-07-25,19002,1.0,1.0 294 | 2016-07-26,19001,1.0,1.0 295 | 2016-07-26,19002,1.0,1.0 296 | 2016-07-27,19001,1.0,1.0 297 | 2016-07-27,19002,1.0,1.0 298 | 2016-07-28,19001,1.0,1.0 299 | 2016-07-28,19002,1.0,1.0 300 | 2016-07-29,19001,1.0,1.0 301 | 2016-07-29,19002,1.0,1.0 302 | 2016-08-01,19001,1.0,1.0 303 | 2016-08-01,19002,1.0,1.0 304 | 2016-08-02,19001,1.0,1.0 305 | 2016-08-02,19002,1.0,1.0 306 | 2016-08-03,19001,1.0,1.0 307 | 2016-08-03,19002,1.0,1.0 308 | 2016-08-04,19001,1.0,1.0 309 | 2016-08-04,19002,1.0,1.0 310 | 2016-08-05,19001,1.0,1.0 311 | 2016-08-05,19002,1.0,1.0 312 | 2016-08-08,19001,1.0,1.0 313 | 2016-08-08,19002,1.0,1.0 314 | 2016-08-09,19001,1.0,1.0 315 | 2016-08-09,19002,1.0,1.0 316 | 2016-08-10,19001,1.0,1.0 317 | 2016-08-10,19002,1.0,1.0 318 | 2016-08-11,19001,1.0,1.0 319 | 2016-08-11,19002,1.0,1.0 320 | 2016-08-12,19001,1.0,1.0 321 | 2016-08-12,19002,1.0,1.0 322 | 2016-08-15,19001,1.0,1.0 323 | 2016-08-15,19002,1.0,1.0 324 | 2016-08-16,19001,1.0,1.0 325 | 2016-08-16,19002,1.0,1.0 326 | 2016-08-17,19001,1.0,1.0 327 | 2016-08-17,19002,1.0,1.0 328 | 2016-08-18,19001,1.0,1.0 329 | 2016-08-18,19002,1.0,1.0 330 | 2016-08-19,19001,1.0,1.0 331 | 2016-08-19,19002,1.0,1.0 332 | 2016-08-22,19001,1.0,1.0 333 | 2016-08-22,19002,1.0,1.0 334 | 2016-08-23,19001,1.0,1.0 335 | 2016-08-23,19002,1.0,1.0 336 | 2016-08-24,19001,1.0,1.0 337 | 2016-08-24,19002,1.0,1.0 338 | 2016-08-25,19001,1.0,1.0 339 | 2016-08-25,19002,1.0,1.0 340 | 2016-08-26,19001,1.0,1.0 341 | 2016-08-26,19002,1.0,1.0 342 | 2016-08-29,19001,1.0,1.0 343 | 2016-08-29,19002,1.0,1.0 344 | 2016-08-30,19001,1.0,1.0 345 | 2016-08-30,19002,1.0,1.0 346 | 2016-08-31,19001,1.0,1.0 347 | 2016-08-31,19002,1.0,1.0 348 | 2016-09-01,19001,1.0,1.0 349 | 2016-09-01,19002,1.0,1.0 350 | 2016-09-02,19001,1.0,1.0 351 | 2016-09-02,19002,1.0,1.0 352 | 2016-09-05,19001,1.0,1.0 353 | 2016-09-05,19002,1.0,1.0 354 | 2016-09-06,19001,1.0,1.0 355 | 2016-09-06,19002,1.0,1.0 356 | 2016-09-07,19001,1.0,1.0 357 | 2016-09-07,19002,1.0,1.0 358 | 2016-09-08,19001,1.0,1.0 359 | 2016-09-08,19002,1.0,1.0 360 | 2016-09-09,19001,1.0,1.0 361 | 2016-09-09,19002,1.0,1.0 362 | 2016-09-12,19001,1.0,1.0 363 | 2016-09-12,19002,1.0,1.0 364 | 2016-09-13,19001,1.0,1.0 365 | 2016-09-13,19002,1.0,1.0 366 | 2016-09-14,19001,1.0,1.0 367 | 2016-09-14,19002,1.0,1.0 368 | 2016-09-15,19001,1.0,1.0 369 | 2016-09-15,19002,1.0,1.0 370 | 2016-09-16,19001,1.0,1.0 371 | 2016-09-16,19002,1.0,1.0 372 | 2016-09-19,19001,1.0,1.0 373 | 2016-09-19,19002,1.0,1.0 374 | 2016-09-20,19001,1.0,1.0 375 | 2016-09-20,19002,1.0,1.0 376 | 2016-09-21,19001,1.0,1.0 377 | 2016-09-21,19002,1.0,1.0 378 | 2016-09-22,19001,1.0,1.0 379 | 2016-09-22,19002,1.0,1.0 380 | 2016-09-23,19001,1.0,1.0 381 | 2016-09-23,19002,1.0,1.0 382 | 2016-09-26,19001,1.0,1.0 383 | 2016-09-26,19002,1.0,1.0 384 | 2016-09-27,19001,1.0,1.0 385 | 2016-09-27,19002,1.0,1.0 386 | 2016-09-28,19001,1.0,1.0 387 | 2016-09-28,19002,1.0,1.0 388 | 2016-09-29,19001,1.0,1.0 389 | 2016-09-29,19002,1.0,1.0 390 | 2016-09-30,19001,1.0,1.0 391 | 2016-09-30,19002,1.0,1.0 392 | 2016-10-03,19001,1.0,1.0 393 | 2016-10-03,19002,1.0,1.0 394 | 2016-10-04,19001,1.0,1.0 395 | 2016-10-04,19002,1.0,1.0 396 | 2016-10-05,19001,1.0,1.0 397 | 2016-10-05,19002,1.0,1.0 398 | 2016-10-06,19001,1.0,1.0 399 | 2016-10-06,19002,1.0,1.0 400 | 2016-10-07,19001,1.0,1.0 401 | 2016-10-07,19002,1.0,1.0 402 | 2016-10-10,19001,1.0,1.0 403 | 2016-10-10,19002,1.0,1.0 404 | 2016-10-11,19001,1.0,1.0 405 | 2016-10-11,19002,1.0,1.0 406 | 2016-10-12,19001,1.0,1.0 407 | 2016-10-12,19002,1.0,1.0 408 | 2016-10-13,19001,1.0,1.0 409 | 2016-10-13,19002,1.0,1.0 410 | 2016-10-14,19001,1.0,1.0 411 | 2016-10-14,19002,1.0,1.0 412 | 2016-10-17,19001,1.0,1.0 413 | 2016-10-17,19002,1.0,1.0 414 | 2016-10-18,19001,1.0,1.0 415 | 2016-10-18,19002,1.0,1.0 416 | 2016-10-19,19001,1.0,1.0 417 | 2016-10-19,19002,1.0,1.0 418 | 2016-10-20,19001,1.0,1.0 419 | 2016-10-20,19002,1.0,1.0 420 | 2016-10-21,19001,1.0,1.0 421 | 2016-10-21,19002,1.0,1.0 422 | 2016-10-24,19001,1.0,1.0 423 | 2016-10-24,19002,1.0,1.0 424 | 2016-10-25,19001,1.0,1.0 425 | 2016-10-25,19002,1.0,1.0 426 | 2016-10-26,19001,1.0,1.0 427 | 2016-10-26,19002,1.0,1.0 428 | 2016-10-27,19001,1.0,1.0 429 | 2016-10-27,19002,1.0,1.0 430 | 2016-10-28,19001,1.0,1.0 431 | 2016-10-28,19002,1.0,1.0 432 | 2016-10-31,19001,1.0,1.0 433 | 2016-10-31,19002,1.0,1.0 434 | 2016-11-01,19001,1.0,1.0 435 | 2016-11-01,19002,1.0,1.0 436 | 2016-11-02,19001,1.0,1.0 437 | 2016-11-02,19002,1.0,1.0 438 | 2016-11-03,19001,1.0,1.0 439 | 2016-11-03,19002,1.0,1.0 440 | 2016-11-04,19001,1.0,1.0 441 | 2016-11-04,19002,1.0,1.0 442 | 2016-11-07,19001,1.0,1.0 443 | 2016-11-07,19002,1.0,1.0 444 | 2016-11-08,19001,1.0,1.0 445 | 2016-11-08,19002,1.0,1.0 446 | 2016-11-09,19001,1.0,1.0 447 | 2016-11-09,19002,1.0,1.0 448 | 2016-11-10,19001,1.0,1.0 449 | 2016-11-10,19002,1.0,1.0 450 | 2016-11-11,19001,1.0,1.0 451 | 2016-11-11,19002,1.0,1.0 452 | 2016-11-14,19001,1.0,1.0 453 | 2016-11-14,19002,1.0,1.0 454 | 2016-11-15,19001,1.0,1.0 455 | 2016-11-15,19002,1.0,1.0 456 | 2016-11-16,19001,1.0,1.0 457 | 2016-11-16,19002,1.0,1.0 458 | 2016-11-17,19001,1.0,1.0 459 | 2016-11-17,19002,1.0,1.0 460 | 2016-11-18,19001,1.0,1.0 461 | 2016-11-18,19002,1.0,1.0 462 | 2016-11-21,19001,1.0,1.0 463 | 2016-11-21,19002,1.0,1.0 464 | 2016-11-22,19001,1.0,1.0 465 | 2016-11-22,19002,1.0,1.0 466 | 2016-11-23,19001,1.0,1.0 467 | 2016-11-23,19002,1.0,1.0 468 | 2016-11-24,19001,1.0,1.0 469 | 2016-11-24,19002,1.0,1.0 470 | 2016-11-25,19001,1.0,1.0 471 | 2016-11-25,19002,1.0,1.0 472 | 2016-11-28,19001,1.0,1.0 473 | 2016-11-28,19002,1.0,1.0 474 | 2016-11-29,19001,1.0,1.0 475 | 2016-11-29,19002,1.0,1.0 476 | 2016-11-30,19001,1.0,1.0 477 | 2016-11-30,19002,1.0,1.0 478 | 2016-12-01,19001,1.0,1.0 479 | 2016-12-01,19002,1.0,1.0 480 | 2016-12-02,19001,1.0,1.0 481 | 2016-12-02,19002,1.0,1.0 482 | 2016-12-05,19001,1.0,1.0 483 | 2016-12-05,19002,1.0,1.0 484 | 2016-12-06,19001,1.0,1.0 485 | 2016-12-06,19002,1.0,1.0 486 | 2016-12-07,19001,1.0,1.0 487 | 2016-12-07,19002,1.0,1.0 488 | 2016-12-08,19001,1.0,1.0 489 | 2016-12-08,19002,1.0,1.0 490 | 2016-12-09,19001,1.0,1.0 491 | 2016-12-09,19002,1.0,1.0 492 | 2016-12-12,19001,1.0,1.0 493 | 2016-12-12,19002,1.0,1.0 494 | 2016-12-13,19001,1.0,1.0 495 | 2016-12-13,19002,1.0,1.0 496 | 2016-12-14,19001,1.0,1.0 497 | 2016-12-14,19002,1.0,1.0 498 | 2016-12-15,19001,1.0,1.0 499 | 2016-12-15,19002,1.0,1.0 500 | 2016-12-16,19001,1.0,1.0 501 | 2016-12-16,19002,1.0,1.0 502 | 2016-12-19,19001,1.0,1.0 503 | 2016-12-19,19002,1.0,1.0 504 | 2016-12-20,19001,1.0,1.0 505 | 2016-12-20,19002,1.0,1.0 506 | 2016-12-21,19001,1.0,1.0 507 | 2016-12-21,19002,1.0,1.0 508 | 2016-12-22,19001,1.0,1.0 509 | 2016-12-22,19002,1.0,1.0 510 | 2016-12-23,19001,1.0,1.0 511 | 2016-12-23,19002,1.0,1.0 512 | 2016-12-26,19001,1.0,1.0 513 | 2016-12-26,19002,1.0,1.0 514 | 2016-12-27,19001,1.0,1.0 515 | 2016-12-27,19002,1.0,1.0 516 | 2016-12-28,19001,1.0,1.0 517 | 2016-12-28,19002,1.0,1.0 518 | 2016-12-29,19001,1.0,1.0 519 | 2016-12-29,19002,1.0,1.0 520 | 2016-12-30,19001,1.0,1.0 521 | 2016-12-30,19002,1.0,1.0 522 | 2017-01-02,19001,1.0,1.0 523 | 2017-01-02,19002,1.0,1.0 524 | 2017-01-03,19001,1.0,1.0 525 | 2017-01-03,19002,1.0,1.0 526 | 2017-01-04,19001,1.0,1.0 527 | 2017-01-04,19002,1.0,1.0 528 | 2017-01-05,19001,1.0,1.0 529 | 2017-01-05,19002,1.0,1.0 530 | 2017-01-06,19001,1.0,1.0 531 | 2017-01-06,19002,1.0,1.0 532 | 2017-01-09,19001,1.0,1.0 533 | 2017-01-09,19002,1.0,1.0 534 | 2017-01-10,19001,1.0,1.0 535 | 2017-01-10,19002,1.0,1.0 536 | 2017-01-11,19001,1.0,1.0 537 | 2017-01-11,19002,1.0,1.0 538 | 2017-01-12,19001,1.0,1.0 539 | 2017-01-12,19002,1.0,1.0 540 | 2017-01-13,19001,1.0,1.0 541 | 2017-01-13,19002,1.0,1.0 542 | 2017-01-16,19001,1.0,1.0 543 | 2017-01-16,19002,1.0,1.0 544 | 2017-01-17,19001,1.0,1.0 545 | 2017-01-17,19002,1.0,1.0 546 | 2017-01-18,19001,1.0,1.0 547 | 2017-01-18,19002,1.0,1.0 548 | 2017-01-19,19001,1.0,1.0 549 | 2017-01-19,19002,1.0,1.0 550 | 2017-01-20,19001,1.0,1.0 551 | 2017-01-20,19002,1.0,1.0 552 | 2017-01-23,19001,1.0,1.0 553 | 2017-01-23,19002,1.0,1.0 554 | 2017-01-24,19001,1.0,1.0 555 | 2017-01-24,19002,1.0,1.0 556 | 2017-01-25,19001,1.0,1.0 557 | 2017-01-25,19002,1.0,1.0 558 | 2017-01-26,19001,1.0,1.0 559 | 2017-01-26,19002,1.0,1.0 560 | 2017-01-27,19001,1.0,1.0 561 | 2017-01-27,19002,1.0,1.0 562 | 2017-01-30,19001,1.0,1.0 563 | 2017-01-30,19002,1.0,1.0 564 | 2017-01-31,19001,1.0,1.0 565 | 2017-01-31,19002,1.0,1.0 566 | 2017-02-01,19001,1.0,1.0 567 | 2017-02-01,19002,1.0,1.0 568 | 2017-02-02,19001,1.0,1.0 569 | 2017-02-02,19002,1.0,1.0 570 | 2017-02-03,19001,1.0,1.0 571 | 2017-02-03,19002,1.0,1.0 572 | 2017-02-06,19001,1.0,1.0 573 | 2017-02-06,19002,1.0,1.0 574 | 2017-02-07,19001,1.0,1.0 575 | 2017-02-07,19002,1.0,1.0 576 | 2017-02-08,19001,1.0,1.0 577 | 2017-02-08,19002,1.0,1.0 578 | 2017-02-09,19001,1.0,1.0 579 | 2017-02-09,19002,1.0,1.0 580 | 2017-02-10,19001,1.0,1.0 581 | 2017-02-10,19002,1.0,1.0 582 | 2017-02-13,19001,1.0,1.0 583 | 2017-02-13,19002,1.0,1.0 584 | 2017-02-14,19001,1.0,1.0 585 | 2017-02-14,19002,1.0,1.0 586 | 2017-02-15,19001,1.0,1.0 587 | 2017-02-15,19002,1.0,1.0 588 | 2017-02-16,19001,1.0,1.0 589 | 2017-02-16,19002,1.0,1.0 590 | 2017-02-17,19001,1.0,1.0 591 | 2017-02-17,19002,1.0,1.0 592 | 2017-02-20,19001,1.0,1.0 593 | 2017-02-20,19002,1.0,1.0 594 | 2017-02-21,19001,1.0,1.0 595 | 2017-02-21,19002,1.0,1.0 596 | 2017-02-22,19001,1.0,1.0 597 | 2017-02-22,19002,1.0,1.0 598 | 2017-02-23,19001,1.0,1.0 599 | 2017-02-23,19002,1.0,1.0 600 | 2017-02-24,19001,1.0,1.0 601 | 2017-02-24,19002,1.0,1.0 602 | 2017-02-27,19001,1.0,1.0 603 | 2017-02-27,19002,1.0,1.0 604 | 2017-02-28,19001,1.0,1.0 605 | 2017-02-28,19002,1.0,1.0 606 | 2017-03-01,19001,1.0,1.0 607 | 2017-03-01,19002,1.0,1.0 608 | 2017-03-02,19001,1.0,1.0 609 | 2017-03-02,19002,1.0,1.0 610 | 2017-03-03,19001,1.0,1.0 611 | 2017-03-03,19002,1.0,1.0 612 | 2017-03-06,19001,1.0,1.0 613 | 2017-03-06,19002,1.0,1.0 614 | 2017-03-07,19001,1.0,1.0 615 | 2017-03-07,19002,1.0,1.0 616 | 2017-03-08,19001,1.0,1.0 617 | 2017-03-08,19002,1.0,1.0 618 | 2017-03-09,19001,1.0,1.0 619 | 2017-03-09,19002,1.0,1.0 620 | 2017-03-10,19001,1.0,1.0 621 | 2017-03-10,19002,1.0,1.0 622 | 2017-03-13,19001,1.0,1.0 623 | 2017-03-13,19002,1.0,1.0 624 | 2017-03-14,19001,1.0,1.0 625 | 2017-03-14,19002,1.0,1.0 626 | 2017-03-15,19001,1.0,1.0 627 | 2017-03-15,19002,1.0,1.0 628 | 2017-03-16,19001,1.0,1.0 629 | 2017-03-16,19002,1.0,1.0 630 | 2017-03-17,19001,1.0,1.0 631 | 2017-03-17,19002,1.0,1.0 632 | 2017-03-20,19001,1.0,1.0 633 | 2017-03-20,19002,1.0,1.0 634 | 2017-03-21,19001,1.0,1.0 635 | 2017-03-21,19002,1.0,1.0 636 | 2017-03-22,19001,1.0,1.0 637 | 2017-03-22,19002,1.0,1.0 638 | 2017-03-23,19001,1.0,1.0 639 | 2017-03-23,19002,1.0,1.0 640 | 2017-03-24,19001,1.0,1.0 641 | 2017-03-24,19002,1.0,1.0 642 | 2017-03-27,19001,1.0,1.0 643 | 2017-03-27,19002,1.0,1.0 644 | 2017-03-28,19001,1.0,1.0 645 | 2017-03-28,19002,1.0,1.0 646 | 2017-03-29,19001,1.0,1.0 647 | 2017-03-29,19002,1.0,1.0 648 | 2017-03-30,19001,1.0,1.0 649 | 2017-03-30,19002,1.0,1.0 650 | 2017-03-31,19001,1.0,1.0 651 | 2017-03-31,19002,1.0,1.0 652 | 2017-04-03,19001,1.0,1.0 653 | 2017-04-03,19002,1.0,1.0 654 | 2017-04-04,19001,1.0,1.0 655 | 2017-04-04,19002,1.0,1.0 656 | 2017-04-05,19001,1.0,1.0 657 | 2017-04-05,19002,1.0,1.0 658 | 2017-04-06,19001,1.0,1.0 659 | 2017-04-06,19002,1.0,1.0 660 | 2017-04-07,19001,1.0,1.0 661 | 2017-04-07,19002,1.0,1.0 662 | 2017-04-10,19001,1.0,1.0 663 | 2017-04-10,19002,1.0,1.0 664 | 2017-04-11,19001,1.0,1.0 665 | 2017-04-11,19002,1.0,1.0 666 | 2017-04-12,19001,1.0,1.0 667 | 2017-04-12,19002,1.0,1.0 668 | 2017-04-13,19001,1.0,1.0 669 | 2017-04-13,19002,1.0,1.0 670 | 2017-04-14,19001,1.0,1.0 671 | 2017-04-14,19002,1.0,1.0 672 | 2017-04-17,19001,1.0,1.0 673 | 2017-04-17,19002,1.0,1.0 674 | 2017-04-18,19001,1.0,1.0 675 | 2017-04-18,19002,1.0,1.0 676 | 2017-04-19,19001,1.0,1.0 677 | 2017-04-19,19002,1.0,1.0 678 | 2017-04-20,19001,1.0,1.0 679 | 2017-04-20,19002,1.0,1.0 680 | 2017-04-21,19001,1.0,1.0 681 | 2017-04-21,19002,1.0,1.0 682 | 2017-04-24,19001,1.0,1.0 683 | 2017-04-24,19002,1.0,1.0 684 | 2017-04-25,19001,1.0,1.0 685 | 2017-04-25,19002,1.0,1.0 686 | 2017-04-26,19001,1.0,1.0 687 | 2017-04-26,19002,1.0,1.0 688 | 2017-04-27,19001,1.0,1.0 689 | 2017-04-27,19002,1.0,1.0 690 | 2017-04-28,19001,1.0,1.0 691 | 2017-04-28,19002,1.0,1.0 692 | 2017-05-01,19001,1.0,1.0 693 | 2017-05-01,19002,1.0,1.0 694 | 2017-05-02,19001,1.0,1.0 695 | 2017-05-02,19002,1.0,1.0 696 | 2017-05-03,19001,1.0,1.0 697 | 2017-05-03,19002,1.0,1.0 698 | 2017-05-04,19001,1.0,1.0 699 | 2017-05-04,19002,1.0,1.0 700 | 2017-05-05,19001,1.0,1.0 701 | 2017-05-05,19002,1.0,1.0 702 | 2017-05-08,19001,1.0,1.0 703 | 2017-05-08,19002,1.0,1.0 704 | 2017-05-09,19001,1.0,1.0 705 | 2017-05-09,19002,1.0,1.0 706 | 2017-05-10,19001,1.0,1.0 707 | 2017-05-10,19002,1.0,1.0 708 | 2017-05-11,19001,1.0,1.0 709 | 2017-05-11,19002,1.0,1.0 710 | 2017-05-12,19001,1.0,1.0 711 | 2017-05-12,19002,1.0,1.0 712 | 2017-05-15,19001,1.0,1.0 713 | 2017-05-15,19002,1.0,1.0 714 | 2017-05-16,19001,1.0,1.0 715 | 2017-05-16,19002,1.0,1.0 716 | 2017-05-17,19001,1.0,1.0 717 | 2017-05-17,19002,1.0,1.0 718 | 2017-05-18,19001,1.0,1.0 719 | 2017-05-18,19002,1.0,1.0 720 | 2017-05-19,19001,1.0,1.0 721 | 2017-05-19,19002,1.0,1.0 722 | 2017-05-22,19001,1.0,1.0 723 | 2017-05-22,19002,1.0,1.0 724 | 2017-05-23,19001,1.0,1.0 725 | 2017-05-23,19002,1.0,1.0 726 | 2017-05-24,19001,1.0,1.0 727 | 2017-05-24,19002,1.0,1.0 728 | 2017-05-25,19001,1.0,1.0 729 | 2017-05-25,19002,1.0,1.0 730 | 2017-05-26,19001,1.0,1.0 731 | 2017-05-26,19002,1.0,1.0 732 | 2017-05-29,19001,1.0,1.0 733 | 2017-05-29,19002,1.0,1.0 734 | 2017-05-30,19001,1.0,1.0 735 | 2017-05-30,19002,1.0,1.0 736 | 2017-05-31,19001,1.0,1.0 737 | 2017-05-31,19002,1.0,1.0 738 | 2017-06-01,19001,1.0,1.0 739 | 2017-06-01,19002,1.0,1.0 740 | 2017-06-02,19001,1.0,1.0 741 | 2017-06-02,19002,1.0,1.0 742 | 2017-06-05,19001,1.0,1.0 743 | 2017-06-05,19002,1.0,1.0 744 | 2017-06-06,19001,1.0,1.0 745 | 2017-06-06,19002,1.0,1.0 746 | 2017-06-07,19001,1.0,1.0 747 | 2017-06-07,19002,1.0,1.0 748 | 2017-06-08,19001,1.0,1.0 749 | 2017-06-08,19002,1.0,1.0 750 | 2017-06-09,19001,1.0,1.0 751 | 2017-06-09,19002,1.0,1.0 752 | 2017-06-12,19001,1.0,1.0 753 | 2017-06-12,19002,1.0,1.0 754 | 2017-06-13,19001,1.0,1.0 755 | 2017-06-13,19002,1.0,1.0 756 | 2017-06-14,19001,1.0,1.0 757 | 2017-06-14,19002,1.0,1.0 758 | 2017-06-15,19001,1.0,1.0 759 | 2017-06-15,19002,1.0,1.0 760 | 2017-06-16,19001,1.0,1.0 761 | 2017-06-16,19002,1.0,1.0 762 | 2017-06-19,19001,1.0,1.0 763 | 2017-06-19,19002,1.0,1.0 764 | 2017-06-20,19001,1.0,1.0 765 | 2017-06-20,19002,1.0,1.0 766 | 2017-06-21,19001,1.0,1.0 767 | 2017-06-21,19002,1.0,1.0 768 | 2017-06-22,19001,1.0,1.0 769 | 2017-06-22,19002,1.0,1.0 770 | 2017-06-23,19001,1.0,1.0 771 | 2017-06-23,19002,1.0,1.0 772 | 2017-06-26,19001,1.0,1.0 773 | 2017-06-26,19002,1.0,1.0 774 | 2017-06-27,19001,1.0,1.0 775 | 2017-06-27,19002,1.0,1.0 776 | 2017-06-28,19001,1.0,1.0 777 | 2017-06-28,19002,1.0,1.0 778 | 2017-06-29,19001,1.0,1.0 779 | 2017-06-29,19002,1.0,1.0 780 | 2017-06-30,19001,1.0,1.0 781 | 2017-06-30,19002,1.0,1.0 782 | 2017-07-03,19001,1.0,1.0 783 | 2017-07-03,19002,1.0,1.0 784 | 2017-07-04,19001,1.0,1.0 785 | 2017-07-04,19002,1.0,1.0 786 | 2017-07-05,19001,1.0,1.0 787 | 2017-07-05,19002,1.0,1.0 788 | 2017-07-06,19001,1.0,1.0 789 | 2017-07-06,19002,1.0,1.0 790 | 2017-07-07,19001,1.0,1.0 791 | 2017-07-07,19002,1.0,1.0 792 | 2017-07-10,19001,1.0,1.0 793 | 2017-07-10,19002,1.0,1.0 794 | 2017-07-11,19001,1.0,1.0 795 | 2017-07-11,19002,1.0,1.0 796 | 2017-07-12,19001,1.0,1.0 797 | 2017-07-12,19002,1.0,1.0 798 | 2017-07-13,19001,1.0,1.0 799 | 2017-07-13,19002,1.0,1.0 800 | 2017-07-14,19001,1.0,1.0 801 | 2017-07-14,19002,1.0,1.0 802 | 2017-07-17,19001,1.0,1.0 803 | 2017-07-17,19002,1.0,1.0 804 | 2017-07-18,19001,1.0,1.0 805 | 2017-07-18,19002,1.0,1.0 806 | 2017-07-19,19001,1.0,1.0 807 | 2017-07-19,19002,1.0,1.0 808 | 2017-07-20,19001,1.0,1.0 809 | 2017-07-20,19002,1.0,1.0 810 | 2017-07-21,19001,1.0,1.0 811 | 2017-07-21,19002,1.0,1.0 812 | 2017-07-24,19001,1.0,1.0 813 | 2017-07-24,19002,1.0,1.0 814 | 2017-07-25,19001,1.0,1.0 815 | 2017-07-25,19002,1.0,1.0 816 | 2017-07-26,19001,1.0,1.0 817 | 2017-07-26,19002,1.0,1.0 818 | 2017-07-27,19001,1.0,1.0 819 | 2017-07-27,19002,1.0,1.0 820 | 2017-07-28,19001,1.0,1.0 821 | 2017-07-28,19002,1.0,1.0 822 | 2017-07-31,19001,1.0,1.0 823 | 2017-07-31,19002,1.0,1.0 824 | 2017-08-01,19001,1.0,1.0 825 | 2017-08-01,19002,1.0,1.0 826 | 2017-08-02,19001,1.0,1.0 827 | 2017-08-02,19002,1.0,1.0 828 | 2017-08-03,19001,1.0,1.0 829 | 2017-08-03,19002,1.0,1.0 830 | 2017-08-04,19001,1.0,1.0 831 | 2017-08-04,19002,1.0,1.0 832 | 2017-08-07,19001,1.0,1.0 833 | 2017-08-07,19002,1.0,1.0 834 | 2017-08-08,19001,1.0,1.0 835 | 2017-08-08,19002,1.0,1.0 836 | 2017-08-09,19001,1.0,1.0 837 | 2017-08-09,19002,1.0,1.0 838 | 2017-08-10,19001,1.0,1.0 839 | 2017-08-10,19002,1.0,1.0 840 | 2017-08-11,19001,1.0,1.0 841 | 2017-08-11,19002,1.0,1.0 842 | 2017-08-14,19001,1.0,1.0 843 | 2017-08-14,19002,1.0,1.0 844 | 2017-08-15,19001,1.0,1.0 845 | 2017-08-15,19002,1.0,1.0 846 | 2017-08-16,19001,1.0,1.0 847 | 2017-08-16,19002,1.0,1.0 848 | 2017-08-17,19001,1.0,1.0 849 | 2017-08-17,19002,1.0,1.0 850 | 2017-08-18,19001,1.0,1.0 851 | 2017-08-18,19002,1.0,1.0 852 | 2017-08-21,19001,1.0,1.0 853 | 2017-08-21,19002,1.0,1.0 854 | 2017-08-22,19001,1.0,1.0 855 | 2017-08-22,19002,1.0,1.0 856 | 2017-08-23,19001,1.0,1.0 857 | 2017-08-23,19002,1.0,1.0 858 | 2017-08-24,19001,1.0,1.0 859 | 2017-08-24,19002,1.0,1.0 860 | 2017-08-25,19001,1.0,1.0 861 | 2017-08-25,19002,1.0,1.0 862 | 2017-08-28,19001,1.0,1.0 863 | 2017-08-28,19002,1.0,1.0 864 | 2017-08-29,19001,1.0,1.0 865 | 2017-08-29,19002,1.0,1.0 866 | 2017-08-30,19001,1.0,1.0 867 | 2017-08-30,19002,1.0,1.0 868 | 2017-08-31,19001,1.0,1.0 869 | 2017-08-31,19002,1.0,1.0 870 | 2017-09-01,19001,1.0,1.0 871 | 2017-09-01,19002,1.0,1.0 872 | 2017-09-04,19001,1.0,1.0 873 | 2017-09-04,19002,1.0,1.0 874 | 2017-09-05,19001,1.0,1.0 875 | 2017-09-05,19002,1.0,1.0 876 | 2017-09-06,19001,1.0,1.0 877 | 2017-09-06,19002,1.0,1.0 878 | 2017-09-07,19001,1.0,1.0 879 | 2017-09-07,19002,1.0,1.0 880 | 2017-09-08,19001,1.0,1.0 881 | 2017-09-08,19002,1.0,1.0 882 | 2017-09-11,19001,1.0,1.0 883 | 2017-09-11,19002,1.0,1.0 884 | 2017-09-12,19001,1.0,1.0 885 | 2017-09-12,19002,1.0,1.0 886 | 2017-09-13,19001,1.0,1.0 887 | 2017-09-13,19002,1.0,1.0 888 | 2017-09-14,19001,1.0,1.0 889 | 2017-09-14,19002,1.0,1.0 890 | 2017-09-15,19001,1.0,1.0 891 | 2017-09-15,19002,1.0,1.0 892 | 2017-09-18,19001,1.0,1.0 893 | 2017-09-18,19002,1.0,1.0 894 | 2017-09-19,19001,1.0,1.0 895 | 2017-09-19,19002,1.0,1.0 896 | 2017-09-20,19001,1.0,1.0 897 | 2017-09-20,19002,1.0,1.0 898 | 2017-09-21,19001,1.0,1.0 899 | 2017-09-21,19002,1.0,1.0 900 | 2017-09-22,19001,1.0,1.0 901 | 2017-09-22,19002,1.0,1.0 902 | 2017-09-25,19001,1.0,1.0 903 | 2017-09-25,19002,1.0,1.0 904 | 2017-09-26,19001,1.0,1.0 905 | 2017-09-26,19002,1.0,1.0 906 | 2017-09-27,19001,1.0,1.0 907 | 2017-09-27,19002,1.0,1.0 908 | 2017-09-28,19001,1.0,1.0 909 | 2017-09-28,19002,1.0,1.0 910 | 2017-09-29,19001,1.0,1.0 911 | 2017-09-29,19002,1.0,1.0 912 | 2017-10-02,19001,1.0,1.0 913 | 2017-10-02,19002,1.0,1.0 914 | 2017-10-03,19001,1.0,1.0 915 | 2017-10-03,19002,1.0,1.0 916 | 2017-10-04,19001,1.0,1.0 917 | 2017-10-04,19002,1.0,1.0 918 | 2017-10-05,19001,1.0,1.0 919 | 2017-10-05,19002,1.0,1.0 920 | 2017-10-06,19001,1.0,1.0 921 | 2017-10-06,19002,1.0,1.0 922 | 2017-10-09,19001,1.0,1.0 923 | 2017-10-09,19002,1.0,1.0 924 | 2017-10-10,19001,1.0,1.0 925 | 2017-10-10,19002,1.0,1.0 926 | 2017-10-11,19001,1.0,1.0 927 | 2017-10-11,19002,1.0,1.0 928 | 2017-10-12,19001,1.0,1.0 929 | 2017-10-12,19002,1.0,1.0 930 | 2017-10-13,19001,1.0,1.0 931 | 2017-10-13,19002,1.0,1.0 932 | 2017-10-16,19001,1.0,1.0 933 | 2017-10-16,19002,1.0,1.0 934 | 2017-10-17,19001,1.0,1.0 935 | 2017-10-17,19002,1.0,1.0 936 | 2017-10-18,19001,1.0,1.0 937 | 2017-10-18,19002,1.0,1.0 938 | 2017-10-19,19001,1.0,1.0 939 | 2017-10-19,19002,1.0,1.0 940 | 2017-10-20,19001,1.0,1.0 941 | 2017-10-20,19002,1.0,1.0 942 | 2017-10-23,19001,1.0,1.0 943 | 2017-10-23,19002,1.0,1.0 944 | 2017-10-24,19001,1.0,1.0 945 | 2017-10-24,19002,1.0,1.0 946 | 2017-10-25,19001,1.0,1.0 947 | 2017-10-25,19002,1.0,1.0 948 | 2017-10-26,19001,1.0,1.0 949 | 2017-10-26,19002,1.0,1.0 950 | 2017-10-27,19001,1.0,1.0 951 | 2017-10-27,19002,1.0,1.0 952 | 2017-10-30,19001,1.0,1.0 953 | 2017-10-30,19002,1.0,1.0 954 | 2017-10-31,19001,1.0,1.0 955 | 2017-10-31,19002,1.0,1.0 956 | 2017-11-01,19001,1.0,1.0 957 | 2017-11-01,19002,1.0,1.0 958 | 2017-11-02,19001,1.0,1.0 959 | 2017-11-02,19002,1.0,1.0 960 | 2017-11-03,19001,1.0,1.0 961 | 2017-11-03,19002,1.0,1.0 962 | 2017-11-06,19001,1.0,1.0 963 | 2017-11-06,19002,1.0,1.0 964 | 2017-11-07,19001,1.0,1.0 965 | 2017-11-07,19002,1.0,1.0 966 | 2017-11-08,19001,1.0,1.0 967 | 2017-11-08,19002,1.0,1.0 968 | 2017-11-09,19001,1.0,1.0 969 | 2017-11-09,19002,1.0,1.0 970 | 2017-11-10,19001,1.0,1.0 971 | 2017-11-10,19002,1.0,1.0 972 | 2017-11-13,19001,1.0,1.0 973 | 2017-11-13,19002,1.0,1.0 974 | 2017-11-14,19001,1.0,1.0 975 | 2017-11-14,19002,1.0,1.0 976 | 2017-11-15,19001,1.0,1.0 977 | 2017-11-15,19002,1.0,1.0 978 | 2017-11-16,19001,1.0,1.0 979 | 2017-11-16,19002,1.0,1.0 980 | 2017-11-17,19001,1.0,1.0 981 | 2017-11-17,19002,1.0,1.0 982 | 2017-11-20,19001,1.0,1.0 983 | 2017-11-20,19002,1.0,1.0 984 | 2017-11-21,19001,1.0,1.0 985 | 2017-11-21,19002,1.0,1.0 986 | 2017-11-22,19001,1.0,1.0 987 | 2017-11-22,19002,1.0,1.0 988 | 2017-11-23,19001,1.0,1.0 989 | 2017-11-23,19002,1.0,1.0 990 | 2017-11-24,19001,1.0,1.0 991 | 2017-11-24,19002,1.0,1.0 992 | 2017-11-27,19001,1.0,1.0 993 | 2017-11-27,19002,1.0,1.0 994 | 2017-11-28,19001,1.0,1.0 995 | 2017-11-28,19002,1.0,1.0 996 | 2017-11-29,19001,1.0,1.0 997 | 2017-11-29,19002,1.0,1.0 998 | 2017-11-30,19001,1.0,1.0 999 | 2017-11-30,19002,1.0,1.0 1000 | 2017-12-01,19001,1.0,1.0 1001 | 2017-12-01,19002,1.0,1.0 1002 | 2017-12-04,19001,1.0,1.0 1003 | 2017-12-04,19002,1.0,1.0 1004 | 2017-12-05,19001,1.0,1.0 1005 | 2017-12-05,19002,1.0,1.0 1006 | 2017-12-06,19001,1.0,1.0 1007 | 2017-12-06,19002,1.0,1.0 1008 | 2017-12-07,19001,1.0,1.0 1009 | 2017-12-07,19002,1.0,1.0 1010 | --------------------------------------------------------------------------------