├── 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 | 
8 | 
9 |
10 | [](https://anaconda.org/conda-forge/empyrical-reloaded)
11 | [](https://anaconda.org/conda-forge/empyrical-reloaded)
12 |
13 | [](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/build_wheels.yml)
14 | [](https://github.com/stefan-jansen/empyrical-reloaded/actions/workflows/conda_package.yml)
15 | [](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 |
--------------------------------------------------------------------------------