mirror of
https://github.com/robertmartin8/PyPortfolioOpt.git
synced 2022-11-27 18:02:41 +03:00
Merge branch 'v1.2.5' into master
This commit is contained in:
@@ -203,7 +203,6 @@ As of v1.2.0:
|
|||||||
- Introduced a new API, in which the function `expected_returns.return_model(method="...")` allows
|
- Introduced a new API, in which the function `expected_returns.return_model(method="...")` allows
|
||||||
all the different return models to be called. This should make testing easier.
|
all the different return models to be called. This should make testing easier.
|
||||||
- Added option to 'properly' compound returns.
|
- Added option to 'properly' compound returns.
|
||||||
- James-Stein shrinkage estimator
|
|
||||||
- CAPM return model.
|
- CAPM return model.
|
||||||
- `from pypfopt import plotting`: moved all plotting functionality into a new class and added
|
- `from pypfopt import plotting`: moved all plotting functionality into a new class and added
|
||||||
new plots. All other plotting functions (scattered in different classes) have been retained,
|
new plots. All other plotting functions (scattered in different classes) have been retained,
|
||||||
@@ -244,9 +243,6 @@ A far more comprehensive version of this can be found on [ReadTheDocs](https://p
|
|||||||
- Exponentially weighted mean historical returns:
|
- Exponentially weighted mean historical returns:
|
||||||
- similar to mean historical returns, except it gives exponentially more weight to recent prices
|
- similar to mean historical returns, except it gives exponentially more weight to recent prices
|
||||||
- it is likely the case that an asset's most recent returns hold more weight than returns from 10 years ago when it comes to estimating future returns.
|
- it is likely the case that an asset's most recent returns hold more weight than returns from 10 years ago when it comes to estimating future returns.
|
||||||
- James-Stein shrinkage:
|
|
||||||
- a slightly more robust estimate of future returns
|
|
||||||
- by shrinking mean returns to the grand average, we can reduce loss.
|
|
||||||
- Capital Asset Pricing Model (CAPM):
|
- Capital Asset Pricing Model (CAPM):
|
||||||
- a simple model to predict returns based on the beta to the market
|
- a simple model to predict returns based on the beta to the market
|
||||||
- this is used all over finance!
|
- this is used all over finance!
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,88 +0,0 @@
|
|||||||
{
|
|
||||||
"cells": [
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"# Hierarchical Risk Parity Portfolio\n",
|
|
||||||
"\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"import pandas as pd\n",
|
|
||||||
"from pypfopt import HRPOpt, risk_models, plotting, DiscreteAllocation\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
|
||||||
"df = pd.read_csv(\"tests/stock_prices.csv\", parse_dates=True, index_col=\"date\")\n",
|
|
||||||
"print(df.shape)\n",
|
|
||||||
"df.tail()\n"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"returns = risk_models.returns_from_prices(df)\n",
|
|
||||||
"hrp = HRPOpt(returns)\n",
|
|
||||||
"weights = hrp.optimize()\n",
|
|
||||||
"hrp.portfolio_performance(verbose=True);"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"plotting.plot_dendrogram(hrp);"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"da = DiscreteAllocation(weights, df.iloc[-1], total_portfolio_value=10000)\n",
|
|
||||||
"w, leftover = da.lp_portfolio()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": null,
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"plotting.plot_weights(weights);"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"kernelspec": {
|
|
||||||
"display_name": "Python 3",
|
|
||||||
"language": "python",
|
|
||||||
"name": "python3"
|
|
||||||
},
|
|
||||||
"language_info": {
|
|
||||||
"codemirror_mode": {
|
|
||||||
"name": "ipython",
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"file_extension": ".py",
|
|
||||||
"mimetype": "text/x-python",
|
|
||||||
"name": "python",
|
|
||||||
"nbconvert_exporter": "python",
|
|
||||||
"pygments_lexer": "ipython3",
|
|
||||||
"version": "3.7.5"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nbformat": 4,
|
|
||||||
"nbformat_minor": 2
|
|
||||||
}
|
|
||||||
@@ -46,20 +46,6 @@ superior models and feed them into the optimiser.
|
|||||||
the mean historical return. However, if you plan on rebalancing much more frequently,
|
the mean historical return. However, if you plan on rebalancing much more frequently,
|
||||||
there is a case to be made for lowering the span in order to capture recent trends.
|
there is a case to be made for lowering the span in order to capture recent trends.
|
||||||
|
|
||||||
.. autofunction:: james_stein_shrinkage
|
|
||||||
|
|
||||||
A surprising result in statistics is that the MLE estimator for a 3+ dimensional
|
|
||||||
Normal distribution is an *inadmissible* estimator. That is, there exists an estimator
|
|
||||||
:math:`\hat{\mu}^{JS}` such that:
|
|
||||||
|
|
||||||
.. math::
|
|
||||||
|
|
||||||
E\{ \lVert \hat{\mu}^{JS} - \mu \rVert^2 \} < E \{ \lVert \bar{\mu} - \mu \rVert^2 \}
|
|
||||||
|
|
||||||
In essence, to reduce account for the fact that our sample may not be representative and
|
|
||||||
thus reduce loss, we shrink the sample means to the "grand average" (mean of means). For
|
|
||||||
a more detailed explanation, refer to Efron and Hastie (2010) [1]_
|
|
||||||
|
|
||||||
.. autofunction:: capm_return
|
.. autofunction:: capm_return
|
||||||
|
|
||||||
.. autofunction:: returns_from_prices
|
.. autofunction:: returns_from_prices
|
||||||
@@ -69,7 +55,5 @@ superior models and feed them into the optimiser.
|
|||||||
.. autofunction:: prices_from_returns
|
.. autofunction:: prices_from_returns
|
||||||
|
|
||||||
|
|
||||||
References
|
.. References
|
||||||
==========
|
.. ==========
|
||||||
|
|
||||||
.. [1] Efron and Hastie (2010) `Empirical Bayes and the James–Stein Estimator <http://statweb.stanford.edu/~ckirby/brad/LSI/chapter1.pdf>`_.
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ Sharpe Ratio: 1.09
|
|||||||
|
|
||||||
# Black-Litterman
|
# Black-Litterman
|
||||||
spy_prices = pd.read_csv(
|
spy_prices = pd.read_csv(
|
||||||
"tests/spy_prices.csv", parse_dates=True, index_col=0, squeeze=True
|
"tests/resources/spy_prices.csv", parse_dates=True, index_col=0, squeeze=True
|
||||||
)
|
)
|
||||||
delta = black_litterman.market_implied_risk_aversion(spy_prices)
|
delta = black_litterman.market_implied_risk_aversion(spy_prices)
|
||||||
|
|
||||||
|
|||||||
359
poetry.lock
generated
359
poetry.lock
generated
@@ -16,6 +16,23 @@ optional = false
|
|||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "The secure Argon2 password hashing algorithm."
|
||||||
|
name = "argon2-cffi"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
version = "20.1.0"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
cffi = ">=1.0.0"
|
||||||
|
six = "*"
|
||||||
|
|
||||||
|
[package.extras]
|
||||||
|
dev = ["coverage (>=5.0.2)", "hypothesis", "pytest", "sphinx", "wheel", "pre-commit"]
|
||||||
|
docs = ["sphinx"]
|
||||||
|
tests = ["coverage (>=5.0.2)", "hypothesis", "pytest"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Atomic file writes."
|
description = "Atomic file writes."
|
||||||
@@ -30,13 +47,12 @@ description = "Classes Without Boilerplate"
|
|||||||
name = "attrs"
|
name = "attrs"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "19.3.0"
|
version = "20.1.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"]
|
dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "sphinx-rtd-theme", "pre-commit"]
|
||||||
dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"]
|
docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"]
|
||||||
docs = ["sphinx", "zope.interface"]
|
tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
||||||
tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -44,7 +60,7 @@ description = "Specifications for callback functions passed in to an API"
|
|||||||
name = "backcall"
|
name = "backcall"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.1.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -77,6 +93,17 @@ packaging = "*"
|
|||||||
six = ">=1.9.0"
|
six = ">=1.9.0"
|
||||||
webencodings = "*"
|
webencodings = "*"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "Foreign Function Interface for Python calling C code."
|
||||||
|
name = "cffi"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
version = "1.14.2"
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
pycparser = "*"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "Composable command line interface toolkit"
|
description = "Composable command line interface toolkit"
|
||||||
@@ -95,21 +122,28 @@ optional = false
|
|||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "main"
|
||||||
|
description = "Convex optimization package"
|
||||||
|
name = "cvxopt"
|
||||||
|
optional = false
|
||||||
|
python-versions = "*"
|
||||||
|
version = "1.2.5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "A domain-specific language for modeling convex optimization problems in Python."
|
description = "A domain-specific language for modeling convex optimization problems in Python."
|
||||||
name = "cvxpy"
|
name = "cvxpy"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = ">=3.5"
|
||||||
version = "1.0.31"
|
version = "1.1.5"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
ecos = ">=2"
|
ecos = ">=2"
|
||||||
multiprocess = "*"
|
|
||||||
numpy = ">=1.15"
|
numpy = ">=1.15"
|
||||||
osqp = ">=0.4.1"
|
osqp = ">=0.4.1"
|
||||||
scipy = ">=1.1.0"
|
scipy = ">=1.1.0"
|
||||||
scs = ">=1.1.3"
|
scs = ">=1.1.5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -127,17 +161,6 @@ optional = false
|
|||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
version = "0.6.0"
|
version = "0.6.0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
category = "main"
|
|
||||||
description = "serialize all of python"
|
|
||||||
name = "dill"
|
|
||||||
optional = false
|
|
||||||
python-versions = ">=2.6, !=3.0.*"
|
|
||||||
version = "0.3.1.1"
|
|
||||||
|
|
||||||
[package.extras]
|
|
||||||
graph = ["objgraph (>=1.7.2)"]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
description = "This is the Python package for ECOS: Embedded Cone Solver. See Github page for more information."
|
description = "This is the Python package for ECOS: Embedded Cone Solver. See Github page for more information."
|
||||||
@@ -164,7 +187,7 @@ description = "the modular source code checker: pep8 pyflakes and co"
|
|||||||
name = "flake8"
|
name = "flake8"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
|
||||||
version = "3.8.2"
|
version = "3.8.3"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
mccabe = ">=0.6.0,<0.7.0"
|
mccabe = ">=0.6.0,<0.7.0"
|
||||||
@@ -190,7 +213,7 @@ marker = "python_version < \"3.8\""
|
|||||||
name = "importlib-metadata"
|
name = "importlib-metadata"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||||
version = "1.6.1"
|
version = "1.7.0"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
zipp = ">=0.5"
|
zipp = ">=0.5"
|
||||||
@@ -205,7 +228,7 @@ description = "IPython Kernel for Jupyter"
|
|||||||
name = "ipykernel"
|
name = "ipykernel"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "5.3.0"
|
version = "5.3.4"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
appnope = "*"
|
appnope = "*"
|
||||||
@@ -288,14 +311,14 @@ description = "An autocompletion tool for Python that can be used for text edito
|
|||||||
name = "jedi"
|
name = "jedi"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||||
version = "0.17.0"
|
version = "0.17.2"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
parso = ">=0.7.0"
|
parso = ">=0.7.0,<0.8.0"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
qa = ["flake8 (3.7.9)"]
|
qa = ["flake8 (3.7.9)"]
|
||||||
testing = ["colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"]
|
testing = ["Django (<3.1)", "colorama", "docopt", "pytest (>=3.9.0,<5.0.0)"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -355,7 +378,7 @@ description = "Jupyter protocol implementation and client libraries"
|
|||||||
name = "jupyter-client"
|
name = "jupyter-client"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "6.1.3"
|
version = "6.1.7"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
jupyter-core = ">=4.6.0"
|
jupyter-core = ">=4.6.0"
|
||||||
@@ -365,7 +388,7 @@ tornado = ">=4.1"
|
|||||||
traitlets = "*"
|
traitlets = "*"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
test = ["ipykernel", "ipython", "mock", "pytest"]
|
test = ["ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "async-generator", "pytest-timeout"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -428,18 +451,7 @@ marker = "python_version > \"2.7\""
|
|||||||
name = "more-itertools"
|
name = "more-itertools"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "8.3.0"
|
version = "8.5.0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
category = "main"
|
|
||||||
description = "better multiprocessing and multithreading in python"
|
|
||||||
name = "multiprocess"
|
|
||||||
optional = false
|
|
||||||
python-versions = "*"
|
|
||||||
version = "0.70.9"
|
|
||||||
|
|
||||||
[package.dependencies]
|
|
||||||
dill = ">=0.3.1"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -475,7 +487,7 @@ description = "The Jupyter Notebook format"
|
|||||||
name = "nbformat"
|
name = "nbformat"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "5.0.6"
|
version = "5.0.7"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
ipython-genutils = "*"
|
ipython-genutils = "*"
|
||||||
@@ -484,7 +496,7 @@ jupyter-core = "*"
|
|||||||
traitlets = ">=4.1"
|
traitlets = ">=4.1"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
test = ["testpath", "pytest", "pytest-cov"]
|
test = ["pytest", "pytest-cov", "testpath"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -492,10 +504,11 @@ description = "A web-based notebook environment for interactive computing"
|
|||||||
name = "notebook"
|
name = "notebook"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=3.5"
|
python-versions = ">=3.5"
|
||||||
version = "6.0.3"
|
version = "6.1.3"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
Send2Trash = "*"
|
Send2Trash = "*"
|
||||||
|
argon2-cffi = "*"
|
||||||
ipykernel = "*"
|
ipykernel = "*"
|
||||||
ipython-genutils = "*"
|
ipython-genutils = "*"
|
||||||
jinja2 = "*"
|
jinja2 = "*"
|
||||||
@@ -505,12 +518,13 @@ nbconvert = "*"
|
|||||||
nbformat = "*"
|
nbformat = "*"
|
||||||
prometheus-client = "*"
|
prometheus-client = "*"
|
||||||
pyzmq = ">=17"
|
pyzmq = ">=17"
|
||||||
terminado = ">=0.8.1"
|
terminado = ">=0.8.3"
|
||||||
tornado = ">=5.0"
|
tornado = ">=5.0"
|
||||||
traitlets = ">=4.2.1"
|
traitlets = ">=4.2.1"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "nose-exclude"]
|
docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt"]
|
||||||
|
test = ["nose", "coverage", "requests", "nose-warnings-filters", "nbval", "nose-exclude", "selenium", "pytest", "pytest-cov", "requests-unixsocket"]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "main"
|
category = "main"
|
||||||
@@ -572,7 +586,7 @@ description = "A Python Parser"
|
|||||||
name = "parso"
|
name = "parso"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "0.7.0"
|
version = "0.7.1"
|
||||||
|
|
||||||
[package.extras]
|
[package.extras]
|
||||||
testing = ["docopt", "pytest (>=3.0.7)"]
|
testing = ["docopt", "pytest (>=3.0.7)"]
|
||||||
@@ -663,7 +677,7 @@ description = "library with cross-python path, ini-parsing, io, code, log facili
|
|||||||
name = "py"
|
name = "py"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "1.8.1"
|
version = "1.9.0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -673,6 +687,14 @@ optional = false
|
|||||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
version = "2.6.0"
|
version = "2.6.0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
category = "dev"
|
||||||
|
description = "C parser in Python"
|
||||||
|
name = "pycparser"
|
||||||
|
optional = false
|
||||||
|
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||||
|
version = "2.20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
description = "passive checker of Python programs"
|
description = "passive checker of Python programs"
|
||||||
@@ -770,7 +792,7 @@ marker = "sys_platform == \"win32\""
|
|||||||
name = "pywin32"
|
name = "pywin32"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "227"
|
version = "228"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -787,7 +809,7 @@ description = "Python bindings for 0MQ"
|
|||||||
name = "pyzmq"
|
name = "pyzmq"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*"
|
python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*"
|
||||||
version = "19.0.1"
|
version = "19.0.2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -795,7 +817,7 @@ description = "Jupyter Qt console"
|
|||||||
name = "qtconsole"
|
name = "qtconsole"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "4.7.4"
|
version = "4.7.6"
|
||||||
|
|
||||||
[package.dependencies]
|
[package.dependencies]
|
||||||
ipykernel = ">=4.1"
|
ipykernel = ">=4.1"
|
||||||
@@ -929,7 +951,7 @@ description = "Measures the displayed width of unicode strings in a terminal"
|
|||||||
name = "wcwidth"
|
name = "wcwidth"
|
||||||
optional = false
|
optional = false
|
||||||
python-versions = "*"
|
python-versions = "*"
|
||||||
version = "0.2.3"
|
version = "0.2.5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
category = "dev"
|
category = "dev"
|
||||||
@@ -977,7 +999,7 @@ matplotlib = []
|
|||||||
scikit-learn = []
|
scikit-learn = []
|
||||||
|
|
||||||
[metadata]
|
[metadata]
|
||||||
content-hash = "d25a25d8c927137e2fb52a061ae18663175879217a6a4a632b4c3e0b166acc55"
|
content-hash = "0ec98fc57e383a229e29979c086e8fc1417f6654529c9441afa00209c19f012d"
|
||||||
python-versions = "^3.5"
|
python-versions = "^3.5"
|
||||||
|
|
||||||
[metadata.files]
|
[metadata.files]
|
||||||
@@ -989,17 +1011,35 @@ appnope = [
|
|||||||
{file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"},
|
{file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"},
|
||||||
{file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"},
|
{file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"},
|
||||||
]
|
]
|
||||||
|
argon2-cffi = [
|
||||||
|
{file = "argon2-cffi-20.1.0.tar.gz", hash = "sha256:d8029b2d3e4b4cea770e9e5a0104dd8fa185c1724a0f01528ae4826a6d25f97d"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:6ea92c980586931a816d61e4faf6c192b4abce89aa767ff6581e6ddc985ed003"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:05a8ac07c7026542377e38389638a8a1e9b78f1cd8439cd7493b39f08dd75fbf"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp27-cp27m-win32.whl", hash = "sha256:0bf066bc049332489bb2d75f69216416329d9dc65deee127152caeb16e5ce7d5"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp27-cp27m-win_amd64.whl", hash = "sha256:57358570592c46c420300ec94f2ff3b32cbccd10d38bdc12dc6979c4a8484fbc"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7d455c802727710e9dfa69b74ccaab04568386ca17b0ad36350b622cd34606fe"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:b160416adc0f012fb1f12588a5e6954889510f82f698e23ed4f4fa57f12a0647"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp35-cp35m-win32.whl", hash = "sha256:9bee3212ba4f560af397b6d7146848c32a800652301843df06b9e8f68f0f7361"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp35-cp35m-win_amd64.whl", hash = "sha256:392c3c2ef91d12da510cfb6f9bae52512a4552573a9e27600bdb800e05905d2b"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp36-cp36m-win32.whl", hash = "sha256:ba7209b608945b889457f949cc04c8e762bed4fe3fec88ae9a6b7765ae82e496"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:da7f0445b71db6d3a72462e04f36544b0de871289b0bc8a7cc87c0f5ec7079fa"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp37-abi3-macosx_10_6_intel.whl", hash = "sha256:cc0e028b209a5483b6846053d5fd7165f460a1f14774d79e632e75e7ae64b82b"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp37-cp37m-win32.whl", hash = "sha256:18dee20e25e4be86680b178b35ccfc5d495ebd5792cd00781548d50880fee5c5"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:6678bb047373f52bcff02db8afab0d2a77d83bde61cfecea7c5c62e2335cb203"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp38-cp38-win32.whl", hash = "sha256:77e909cc756ef81d6abb60524d259d959bab384832f0c651ed7dcb6e5ccdbb78"},
|
||||||
|
{file = "argon2_cffi-20.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:9dfd5197852530294ecb5795c97a823839258dfd5eb9420233c7cfedec2058f2"},
|
||||||
|
]
|
||||||
atomicwrites = [
|
atomicwrites = [
|
||||||
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
{file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
|
||||||
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
{file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
|
||||||
]
|
]
|
||||||
attrs = [
|
attrs = [
|
||||||
{file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
|
{file = "attrs-20.1.0-py2.py3-none-any.whl", hash = "sha256:2867b7b9f8326499ab5b0e2d12801fa5c98842d2cbd22b35112ae04bf85b4dff"},
|
||||||
{file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"},
|
{file = "attrs-20.1.0.tar.gz", hash = "sha256:0ef97238856430dcf9228e07f316aefc17e8939fc8507e18c6501b761ef1a42a"},
|
||||||
]
|
]
|
||||||
backcall = [
|
backcall = [
|
||||||
{file = "backcall-0.1.0.tar.gz", hash = "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4"},
|
{file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
|
||||||
{file = "backcall-0.1.0.zip", hash = "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"},
|
{file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"},
|
||||||
]
|
]
|
||||||
black = [
|
black = [
|
||||||
{file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"},
|
{file = "black-18.9b0-py36-none-any.whl", hash = "sha256:817243426042db1d36617910df579a54f1afd659adb96fc5032fcf4b36209739"},
|
||||||
@@ -1009,6 +1049,36 @@ bleach = [
|
|||||||
{file = "bleach-3.1.5-py2.py3-none-any.whl", hash = "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f"},
|
{file = "bleach-3.1.5-py2.py3-none-any.whl", hash = "sha256:2bce3d8fab545a6528c8fa5d9f9ae8ebc85a56da365c7f85180bfe96a35ef22f"},
|
||||||
{file = "bleach-3.1.5.tar.gz", hash = "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"},
|
{file = "bleach-3.1.5.tar.gz", hash = "sha256:3c4c520fdb9db59ef139915a5db79f8b51bc2a7257ea0389f30c846883430a4b"},
|
||||||
]
|
]
|
||||||
|
cffi = [
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:da9d3c506f43e220336433dffe643fbfa40096d408cb9b7f2477892f369d5f82"},
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23e44937d7695c27c66a54d793dd4b45889a81b35c0751ba91040fe825ec59c4"},
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0da50dcbccd7cb7e6c741ab7912b2eff48e85af217d72b57f80ebc616257125e"},
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27m-win32.whl", hash = "sha256:76ada88d62eb24de7051c5157a1a78fd853cca9b91c0713c2e973e4196271d0c"},
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27m-win_amd64.whl", hash = "sha256:15a5f59a4808f82d8ec7364cbace851df591c2d43bc76bcbe5c4543a7ddd1bf1"},
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:e4082d832e36e7f9b2278bc774886ca8207346b99f278e54c9de4834f17232f7"},
|
||||||
|
{file = "cffi-1.14.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:57214fa5430399dffd54f4be37b56fe22cedb2b98862550d43cc085fb698dc2c"},
|
||||||
|
{file = "cffi-1.14.2-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:6843db0343e12e3f52cc58430ad559d850a53684f5b352540ca3f1bc56df0731"},
|
||||||
|
{file = "cffi-1.14.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:577791f948d34d569acb2d1add5831731c59d5a0c50a6d9f629ae1cefd9ca4a0"},
|
||||||
|
{file = "cffi-1.14.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8662aabfeab00cea149a3d1c2999b0731e70c6b5bac596d95d13f643e76d3d4e"},
|
||||||
|
{file = "cffi-1.14.2-cp35-cp35m-win32.whl", hash = "sha256:837398c2ec00228679513802e3744d1e8e3cb1204aa6ad408b6aff081e99a487"},
|
||||||
|
{file = "cffi-1.14.2-cp35-cp35m-win_amd64.whl", hash = "sha256:bf44a9a0141a082e89c90e8d785b212a872db793a0080c20f6ae6e2a0ebf82ad"},
|
||||||
|
{file = "cffi-1.14.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:29c4688ace466a365b85a51dcc5e3c853c1d283f293dfcc12f7a77e498f160d2"},
|
||||||
|
{file = "cffi-1.14.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:99cc66b33c418cd579c0f03b77b94263c305c389cb0c6972dac420f24b3bf123"},
|
||||||
|
{file = "cffi-1.14.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:65867d63f0fd1b500fa343d7798fa64e9e681b594e0a07dc934c13e76ee28fb1"},
|
||||||
|
{file = "cffi-1.14.2-cp36-cp36m-win32.whl", hash = "sha256:f5033952def24172e60493b68717792e3aebb387a8d186c43c020d9363ee7281"},
|
||||||
|
{file = "cffi-1.14.2-cp36-cp36m-win_amd64.whl", hash = "sha256:7057613efefd36cacabbdbcef010e0a9c20a88fc07eb3e616019ea1692fa5df4"},
|
||||||
|
{file = "cffi-1.14.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6539314d84c4d36f28d73adc1b45e9f4ee2a89cdc7e5d2b0a6dbacba31906798"},
|
||||||
|
{file = "cffi-1.14.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:672b539db20fef6b03d6f7a14b5825d57c98e4026401fce838849f8de73fe4d4"},
|
||||||
|
{file = "cffi-1.14.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:95e9094162fa712f18b4f60896e34b621df99147c2cee216cfa8f022294e8e9f"},
|
||||||
|
{file = "cffi-1.14.2-cp37-cp37m-win32.whl", hash = "sha256:b9aa9d8818c2e917fa2c105ad538e222a5bce59777133840b93134022a7ce650"},
|
||||||
|
{file = "cffi-1.14.2-cp37-cp37m-win_amd64.whl", hash = "sha256:e4b9b7af398c32e408c00eb4e0d33ced2f9121fd9fb978e6c1b57edd014a7d15"},
|
||||||
|
{file = "cffi-1.14.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e613514a82539fc48291d01933951a13ae93b6b444a88782480be32245ed4afa"},
|
||||||
|
{file = "cffi-1.14.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:9b219511d8b64d3fa14261963933be34028ea0e57455baf6781fe399c2c3206c"},
|
||||||
|
{file = "cffi-1.14.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c0b48b98d79cf795b0916c57bebbc6d16bb43b9fc9b8c9f57f4cf05881904c75"},
|
||||||
|
{file = "cffi-1.14.2-cp38-cp38-win32.whl", hash = "sha256:15419020b0e812b40d96ec9d369b2bc8109cc3295eac6e013d3261343580cc7e"},
|
||||||
|
{file = "cffi-1.14.2-cp38-cp38-win_amd64.whl", hash = "sha256:12a453e03124069b6896107ee133ae3ab04c624bb10683e1ed1c1663df17c13c"},
|
||||||
|
{file = "cffi-1.14.2.tar.gz", hash = "sha256:ae8f34d50af2c2154035984b8b5fc5d9ed63f32fe615646ab435b05b132ca91b"},
|
||||||
|
]
|
||||||
click = [
|
click = [
|
||||||
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
{file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
|
||||||
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
{file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
|
||||||
@@ -1017,11 +1087,38 @@ colorama = [
|
|||||||
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
|
{file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
|
||||||
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
|
{file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
|
||||||
]
|
]
|
||||||
|
cvxopt = [
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:78d0ea93f71f4b5b882ecd741ddc1e0cc8b06f4a2881f9d3fa55631d633273b6"},
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fcc447eb3a0465f9d6b78f3df9861cb6fdb18ff7ad008e17f614da580e1fdbee"},
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:d74806ea13bbe5829903d048e19402051e784e4b34a86cdee2ba80d13f29a54f"},
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:50c464cbb43d1e350759294107abbef29576ea70605f5ac86db7c022002ab22f"},
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27m-win_amd64.whl", hash = "sha256:60daaf297d195b33eab6a90c6fd9e8939541fdb209ed2972b70fba6dcbf82192"},
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ae3684ffabb135a059ba2c1947c0c1e00d79374967f40e807cb0fce65e514999"},
|
||||||
|
{file = "cvxopt-1.2.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:6517bde92411c6b57d09a60c51ef066952400b0b21cf26694a0b0b0fc3faa065"},
|
||||||
|
{file = "cvxopt-1.2.5-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:01b579ef42258f129fc1f249d5bd2945b6183e4323ff9a45a61d1224d76ab778"},
|
||||||
|
{file = "cvxopt-1.2.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:4dcb001b1c6a40ec9c61103816aae0d32dbc11894e0f5f645668f524a051bcaa"},
|
||||||
|
{file = "cvxopt-1.2.5-cp35-cp35m-win_amd64.whl", hash = "sha256:8095c783278b3b3084b0ea431fdec072f5c17ba7b4249b3a71b3866f00fd0d9f"},
|
||||||
|
{file = "cvxopt-1.2.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:7737e406e9d7ecfd86d93492c619238969023ea2b4556bf576b4a7be149aa9f0"},
|
||||||
|
{file = "cvxopt-1.2.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:9ef3bade9effc568b4b077166d5dde9632c2ead8d71e3f6c930e08ae29c7bb64"},
|
||||||
|
{file = "cvxopt-1.2.5-cp36-cp36m-win_amd64.whl", hash = "sha256:02976910f5ff456246e0454d32babe61aa5c7e4c781a330840347fcf3c54f5fa"},
|
||||||
|
{file = "cvxopt-1.2.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:1d56655abfd7e0d3051d71bffe322ce77d71e5d683bcc6a359fe207dbff02a8b"},
|
||||||
|
{file = "cvxopt-1.2.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8bcb1e4b6a92ecbd6b1bea630b666d12cf253c2b902e5d039653e850defff887"},
|
||||||
|
{file = "cvxopt-1.2.5-cp37-cp37m-win_amd64.whl", hash = "sha256:dd2c97ace2739f31bceadd29e0e9535fe7457d756a6d9d207e1a654fd1ad1dc2"},
|
||||||
|
{file = "cvxopt-1.2.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:37768ccefd3aec0dd9428d5eaa1a075d2db1d485e9b93c4d5d02464297a6530a"},
|
||||||
|
{file = "cvxopt-1.2.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:3446b360c4123949511b4aef0b3d0bcdd3ce485b8e682c221a39c886945eacd3"},
|
||||||
|
{file = "cvxopt-1.2.5.tar.gz", hash = "sha256:94ec8c36bd6628a11de9014346692daeeef99b3b7bae28cef30c7490bbcb2d72"},
|
||||||
|
]
|
||||||
cvxpy = [
|
cvxpy = [
|
||||||
{file = "cvxpy-1.0.31-cp27-cp27m-macosx_10_7_x86_64.whl", hash = "sha256:e88982b2817252803c45c3c6bd4f8919c0684c4b6595da953249253e612e3729"},
|
{file = "cvxpy-1.1.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a21e6a5c49ff04b29c47ecd1c9d4e3dee070c1d1df4f71a310addc2e54f85c7e"},
|
||||||
{file = "cvxpy-1.0.31-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:513fe05931a0c93e25d4b4ccca84d7145a43b014d7a29a981d54c76a527bc0bf"},
|
{file = "cvxpy-1.1.5-cp35-cp35m-win_amd64.whl", hash = "sha256:20f3ae7b732b33475a44ec2c810fefc0717fed35e3c677611040049751f3c854"},
|
||||||
{file = "cvxpy-1.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4db9ec8593e8ef31cc8a22f5eb09e36460877724dcd9dc807b26e6768522e7a2"},
|
{file = "cvxpy-1.1.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:9d4b7fad457875b90ba7ca93ca9797715dcfe8bc2044a13eed5ce3735ca4bdf6"},
|
||||||
{file = "cvxpy-1.0.31.tar.gz", hash = "sha256:b398754f9e9ceaa46b07806b5ae85f90fd8de748475db22a6b99c5943cebe69d"},
|
{file = "cvxpy-1.1.5-cp36-cp36m-win_amd64.whl", hash = "sha256:d12ce601b97264295f5c59d55a7fe1f479c074cbb064e7aa8818442c04c84a9d"},
|
||||||
|
{file = "cvxpy-1.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:51280efcc041855bb009bf304d0005d5b352e8027f09d40d60f69cc056e2afa2"},
|
||||||
|
{file = "cvxpy-1.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:7c10f6402fc1f075be7ac0ca0ad31340bac5a6ad4bbc01f94fa2232ec6f784d4"},
|
||||||
|
{file = "cvxpy-1.1.5-py3.5-win-amd64.egg", hash = "sha256:51e61497a1aaa43ee78430929e77f8acaf1bc9d2cb4187fee3a9a4f7f790230d"},
|
||||||
|
{file = "cvxpy-1.1.5-py3.6-win-amd64.egg", hash = "sha256:683fd7f56457a7f2375a47e2ed952adcdf47fffd290e56d213fee73ff115b315"},
|
||||||
|
{file = "cvxpy-1.1.5-py3.7-win-amd64.egg", hash = "sha256:382ea3dca38b4cf0004c1b9fa0fd2c97d07f2c1fbfd5da667e45a44ff622a297"},
|
||||||
|
{file = "cvxpy-1.1.5.tar.gz", hash = "sha256:7c826a874db2e4cefe54e63ebd3a3763d0d72e55a17c7d1cfec80008a87b8d81"},
|
||||||
]
|
]
|
||||||
decorator = [
|
decorator = [
|
||||||
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
|
{file = "decorator-4.4.2-py2.py3-none-any.whl", hash = "sha256:41fa54c2a0cc4ba648be4fd43cff00aedf5b9465c9bf18d64325bc225f08f760"},
|
||||||
@@ -1031,9 +1128,6 @@ defusedxml = [
|
|||||||
{file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"},
|
{file = "defusedxml-0.6.0-py2.py3-none-any.whl", hash = "sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93"},
|
||||||
{file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"},
|
{file = "defusedxml-0.6.0.tar.gz", hash = "sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"},
|
||||||
]
|
]
|
||||||
dill = [
|
|
||||||
{file = "dill-0.3.1.1.tar.gz", hash = "sha256:42d8ef819367516592a825746a18073ced42ca169ab1f5f4044134703e7a049c"},
|
|
||||||
]
|
|
||||||
ecos = [
|
ecos = [
|
||||||
{file = "ecos-2.0.7.post1-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:dd9f01e28fe58894fb394931804884122606fb4e2a59d4514b803e9cd11b7d2b"},
|
{file = "ecos-2.0.7.post1-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:dd9f01e28fe58894fb394931804884122606fb4e2a59d4514b803e9cd11b7d2b"},
|
||||||
{file = "ecos-2.0.7.post1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:db7433051f6072d4821ebc582e9ff853d7d631ed98770550d248eae70b29dd26"},
|
{file = "ecos-2.0.7.post1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:db7433051f6072d4821ebc582e9ff853d7d631ed98770550d248eae70b29dd26"},
|
||||||
@@ -1063,19 +1157,19 @@ entrypoints = [
|
|||||||
{file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
|
{file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
|
||||||
]
|
]
|
||||||
flake8 = [
|
flake8 = [
|
||||||
{file = "flake8-3.8.2-py2.py3-none-any.whl", hash = "sha256:ccaa799ef9893cebe69fdfefed76865aeaefbb94cb8545617b2298786a4de9a5"},
|
{file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"},
|
||||||
{file = "flake8-3.8.2.tar.gz", hash = "sha256:c69ac1668e434d37a2d2880b3ca9aafd54b3a10a3ac1ab101d22f29e29cf8634"},
|
{file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"},
|
||||||
]
|
]
|
||||||
future = [
|
future = [
|
||||||
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
{file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
|
||||||
]
|
]
|
||||||
importlib-metadata = [
|
importlib-metadata = [
|
||||||
{file = "importlib_metadata-1.6.1-py2.py3-none-any.whl", hash = "sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"},
|
{file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
|
||||||
{file = "importlib_metadata-1.6.1.tar.gz", hash = "sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545"},
|
{file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
|
||||||
]
|
]
|
||||||
ipykernel = [
|
ipykernel = [
|
||||||
{file = "ipykernel-5.3.0-py3-none-any.whl", hash = "sha256:a8362e3ae365023ca458effe93b026b8cdadc0b73ff3031472128dd8a2cf0289"},
|
{file = "ipykernel-5.3.4-py3-none-any.whl", hash = "sha256:d6fbba26dba3cebd411382bc484f7bc2caa98427ae0ddb4ab37fe8bfeb5c7dd3"},
|
||||||
{file = "ipykernel-5.3.0.tar.gz", hash = "sha256:731adb3f2c4ebcaff52e10a855ddc87670359a89c9c784d711e62d66fccdafae"},
|
{file = "ipykernel-5.3.4.tar.gz", hash = "sha256:9b2652af1607986a1b231c62302d070bc0534f564c393a5d9d130db9abbbe89d"},
|
||||||
]
|
]
|
||||||
ipython = [
|
ipython = [
|
||||||
{file = "ipython-7.9.0-py3-none-any.whl", hash = "sha256:ed7ebe1cba899c1c3ccad6f7f1c2d2369464cc77dba8eebc65e2043e19cda995"},
|
{file = "ipython-7.9.0-py3-none-any.whl", hash = "sha256:ed7ebe1cba899c1c3ccad6f7f1c2d2369464cc77dba8eebc65e2043e19cda995"},
|
||||||
@@ -1090,8 +1184,8 @@ ipywidgets = [
|
|||||||
{file = "ipywidgets-7.5.1.tar.gz", hash = "sha256:e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"},
|
{file = "ipywidgets-7.5.1.tar.gz", hash = "sha256:e945f6e02854a74994c596d9db83444a1850c01648f1574adf144fbbabe05c97"},
|
||||||
]
|
]
|
||||||
jedi = [
|
jedi = [
|
||||||
{file = "jedi-0.17.0-py2.py3-none-any.whl", hash = "sha256:cd60c93b71944d628ccac47df9a60fec53150de53d42dc10a7fc4b5ba6aae798"},
|
{file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"},
|
||||||
{file = "jedi-0.17.0.tar.gz", hash = "sha256:df40c97641cb943661d2db4c33c2e1ff75d491189423249e989bcea4464f3030"},
|
{file = "jedi-0.17.2.tar.gz", hash = "sha256:86ed7d9b750603e4ba582ea8edc678657fb4007894a12bcf6f4bb97892f31d20"},
|
||||||
]
|
]
|
||||||
jinja2 = [
|
jinja2 = [
|
||||||
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
|
{file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
|
||||||
@@ -1107,8 +1201,8 @@ jupyter = [
|
|||||||
{file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"},
|
{file = "jupyter-1.0.0.zip", hash = "sha256:3e1f86076bbb7c8c207829390305a2b1fe836d471ed54be66a3b8c41e7f46cc7"},
|
||||||
]
|
]
|
||||||
jupyter-client = [
|
jupyter-client = [
|
||||||
{file = "jupyter_client-6.1.3-py3-none-any.whl", hash = "sha256:cde8e83aab3ec1c614f221ae54713a9a46d3bf28292609d2db1b439bef5a8c8e"},
|
{file = "jupyter_client-6.1.7-py3-none-any.whl", hash = "sha256:c958d24d6eacb975c1acebb68ac9077da61b5f5c040f22f6849928ad7393b950"},
|
||||||
{file = "jupyter_client-6.1.3.tar.gz", hash = "sha256:3a32fa4d0b16d1c626b30c3002a62dfd86d6863ed39eaba3f537fade197bb756"},
|
{file = "jupyter_client-6.1.7.tar.gz", hash = "sha256:49e390b36fe4b4226724704ea28d9fb903f1a3601b6882ce3105221cd09377a1"},
|
||||||
]
|
]
|
||||||
jupyter-console = [
|
jupyter-console = [
|
||||||
{file = "jupyter_console-6.1.0-py2.py3-none-any.whl", hash = "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8"},
|
{file = "jupyter_console-6.1.0-py2.py3-none-any.whl", hash = "sha256:b392155112ec86a329df03b225749a0fa903aa80811e8eda55796a40b5e470d8"},
|
||||||
@@ -1157,25 +1251,20 @@ mistune = [
|
|||||||
{file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"},
|
{file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"},
|
||||||
]
|
]
|
||||||
more-itertools = [
|
more-itertools = [
|
||||||
{file = "more-itertools-8.3.0.tar.gz", hash = "sha256:558bb897a2232f5e4f8e2399089e35aecb746e1f9191b6584a151647e89267be"},
|
{file = "more-itertools-8.5.0.tar.gz", hash = "sha256:6f83822ae94818eae2612063a5101a7311e68ae8002005b5e05f03fd74a86a20"},
|
||||||
{file = "more_itertools-8.3.0-py3-none-any.whl", hash = "sha256:7818f596b1e87be009031c7653d01acc46ed422e6656b394b0f765ce66ed4982"},
|
{file = "more_itertools-8.5.0-py3-none-any.whl", hash = "sha256:9b30f12df9393f0d28af9210ff8efe48d10c94f73e5daf886f10c4b0b0b4f03c"},
|
||||||
]
|
|
||||||
multiprocess = [
|
|
||||||
{file = "multiprocess-0.70.9-cp27-cp27m-win32.whl", hash = "sha256:0e4e65c2e74aa14fa0c9a1f838b5e9a5f8fe5b3a173925792260843c4a6157ec"},
|
|
||||||
{file = "multiprocess-0.70.9-cp27-cp27m-win_amd64.whl", hash = "sha256:1eb7dfe2d809d53be92e8a288ed1c01614fe5407bbc9d078ed451a749fb1bd34"},
|
|
||||||
{file = "multiprocess-0.70.9.tar.gz", hash = "sha256:9fd5bd990132da77e73dec6e9613408602a4612e1d73caf2e2b813d2b61508e5"},
|
|
||||||
]
|
]
|
||||||
nbconvert = [
|
nbconvert = [
|
||||||
{file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"},
|
{file = "nbconvert-5.6.1-py2.py3-none-any.whl", hash = "sha256:f0d6ec03875f96df45aa13e21fd9b8450c42d7e1830418cccc008c0df725fcee"},
|
||||||
{file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"},
|
{file = "nbconvert-5.6.1.tar.gz", hash = "sha256:21fb48e700b43e82ba0e3142421a659d7739b65568cc832a13976a77be16b523"},
|
||||||
]
|
]
|
||||||
nbformat = [
|
nbformat = [
|
||||||
{file = "nbformat-5.0.6-py3-none-any.whl", hash = "sha256:276343c78a9660ab2a63c28cc33da5f7c58c092b3f3a40b6017ae2ce6689320d"},
|
{file = "nbformat-5.0.7-py3-none-any.whl", hash = "sha256:ea55c9b817855e2dfcd3f66d74857342612a60b1f09653440f4a5845e6e3523f"},
|
||||||
{file = "nbformat-5.0.6.tar.gz", hash = "sha256:049af048ed76b95c3c44043620c17e56bc001329e07f83fec4f177f0e3d7b757"},
|
{file = "nbformat-5.0.7.tar.gz", hash = "sha256:54d4d6354835a936bad7e8182dcd003ca3dc0cedfee5a306090e04854343b340"},
|
||||||
]
|
]
|
||||||
notebook = [
|
notebook = [
|
||||||
{file = "notebook-6.0.3-py3-none-any.whl", hash = "sha256:3edc616c684214292994a3af05eaea4cc043f6b4247d830f3a2f209fa7639a80"},
|
{file = "notebook-6.1.3-py3-none-any.whl", hash = "sha256:964cc40cff68e473f3778aef9266e867f7703cb4aebdfd250f334efe02f64c86"},
|
||||||
{file = "notebook-6.0.3.tar.gz", hash = "sha256:47a9092975c9e7965ada00b9a20f0cf637d001db60d241d479f53c0be117ad48"},
|
{file = "notebook-6.1.3.tar.gz", hash = "sha256:9990d51b9931a31e681635899aeb198b4c4b41586a9e87fbfaaed1a71d0a05b6"},
|
||||||
]
|
]
|
||||||
numpy = [
|
numpy = [
|
||||||
{file = "numpy-1.18.5-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:e91d31b34fc7c2c8f756b4e902f901f856ae53a93399368d9a0dc7be17ed2ca0"},
|
{file = "numpy-1.18.5-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:e91d31b34fc7c2c8f756b4e902f901f856ae53a93399368d9a0dc7be17ed2ca0"},
|
||||||
@@ -1268,8 +1357,8 @@ pandocfilters = [
|
|||||||
{file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"},
|
{file = "pandocfilters-1.4.2.tar.gz", hash = "sha256:b3dd70e169bb5449e6bc6ff96aea89c5eea8c5f6ab5e207fc2f521a2cf4a0da9"},
|
||||||
]
|
]
|
||||||
parso = [
|
parso = [
|
||||||
{file = "parso-0.7.0-py2.py3-none-any.whl", hash = "sha256:158c140fc04112dc45bca311633ae5033c2c2a7b732fa33d0955bad8152a8dd0"},
|
{file = "parso-0.7.1-py2.py3-none-any.whl", hash = "sha256:97218d9159b2520ff45eb78028ba8b50d2bc61dcc062a9682666f2dc4bd331ea"},
|
||||||
{file = "parso-0.7.0.tar.gz", hash = "sha256:908e9fae2144a076d72ae4e25539143d40b8e3eafbaeae03c1bfe226f4cdf12c"},
|
{file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"},
|
||||||
]
|
]
|
||||||
pathlib2 = [
|
pathlib2 = [
|
||||||
{file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"},
|
{file = "pathlib2-2.3.5-py2.py3-none-any.whl", hash = "sha256:0ec8205a157c80d7acc301c0b18fbd5d44fe655968f5d947b6ecef5290fc35db"},
|
||||||
@@ -1301,13 +1390,17 @@ ptyprocess = [
|
|||||||
{file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"},
|
{file = "ptyprocess-0.6.0.tar.gz", hash = "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0"},
|
||||||
]
|
]
|
||||||
py = [
|
py = [
|
||||||
{file = "py-1.8.1-py2.py3-none-any.whl", hash = "sha256:c20fdd83a5dbc0af9efd622bee9a5564e278f6380fffcacc43ba6f43db2813b0"},
|
{file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"},
|
||||||
{file = "py-1.8.1.tar.gz", hash = "sha256:5e27081401262157467ad6e7f851b7aa402c5852dbcb3dae06768434de5752aa"},
|
{file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
|
||||||
]
|
]
|
||||||
pycodestyle = [
|
pycodestyle = [
|
||||||
{file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"},
|
{file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"},
|
||||||
{file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"},
|
{file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"},
|
||||||
]
|
]
|
||||||
|
pycparser = [
|
||||||
|
{file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"},
|
||||||
|
{file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
|
||||||
|
]
|
||||||
pyflakes = [
|
pyflakes = [
|
||||||
{file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
|
{file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
|
||||||
{file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
|
{file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
|
||||||
@@ -1336,18 +1429,18 @@ pytz = [
|
|||||||
{file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
|
{file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
|
||||||
]
|
]
|
||||||
pywin32 = [
|
pywin32 = [
|
||||||
{file = "pywin32-227-cp27-cp27m-win32.whl", hash = "sha256:371fcc39416d736401f0274dd64c2302728c9e034808e37381b5e1b22be4a6b0"},
|
{file = "pywin32-228-cp27-cp27m-win32.whl", hash = "sha256:37dc9935f6a383cc744315ae0c2882ba1768d9b06700a70f35dc1ce73cd4ba9c"},
|
||||||
{file = "pywin32-227-cp27-cp27m-win_amd64.whl", hash = "sha256:4cdad3e84191194ea6d0dd1b1b9bdda574ff563177d2adf2b4efec2a244fa116"},
|
{file = "pywin32-228-cp27-cp27m-win_amd64.whl", hash = "sha256:11cb6610efc2f078c9e6d8f5d0f957620c333f4b23466931a247fb945ed35e89"},
|
||||||
{file = "pywin32-227-cp35-cp35m-win32.whl", hash = "sha256:f4c5be1a293bae0076d93c88f37ee8da68136744588bc5e2be2f299a34ceb7aa"},
|
{file = "pywin32-228-cp35-cp35m-win32.whl", hash = "sha256:1f45db18af5d36195447b2cffacd182fe2d296849ba0aecdab24d3852fbf3f80"},
|
||||||
{file = "pywin32-227-cp35-cp35m-win_amd64.whl", hash = "sha256:a929a4af626e530383a579431b70e512e736e9588106715215bf685a3ea508d4"},
|
{file = "pywin32-228-cp35-cp35m-win_amd64.whl", hash = "sha256:6e38c44097a834a4707c1b63efa9c2435f5a42afabff634a17f563bc478dfcc8"},
|
||||||
{file = "pywin32-227-cp36-cp36m-win32.whl", hash = "sha256:300a2db938e98c3e7e2093e4491439e62287d0d493fe07cce110db070b54c0be"},
|
{file = "pywin32-228-cp36-cp36m-win32.whl", hash = "sha256:ec16d44b49b5f34e99eb97cf270806fdc560dff6f84d281eb2fcb89a014a56a9"},
|
||||||
{file = "pywin32-227-cp36-cp36m-win_amd64.whl", hash = "sha256:9b31e009564fb95db160f154e2aa195ed66bcc4c058ed72850d047141b36f3a2"},
|
{file = "pywin32-228-cp36-cp36m-win_amd64.whl", hash = "sha256:a60d795c6590a5b6baeacd16c583d91cce8038f959bd80c53bd9a68f40130f2d"},
|
||||||
{file = "pywin32-227-cp37-cp37m-win32.whl", hash = "sha256:47a3c7551376a865dd8d095a98deba954a98f326c6fe3c72d8726ca6e6b15507"},
|
{file = "pywin32-228-cp37-cp37m-win32.whl", hash = "sha256:af40887b6fc200eafe4d7742c48417529a8702dcc1a60bf89eee152d1d11209f"},
|
||||||
{file = "pywin32-227-cp37-cp37m-win_amd64.whl", hash = "sha256:31f88a89139cb2adc40f8f0e65ee56a8c585f629974f9e07622ba80199057511"},
|
{file = "pywin32-228-cp37-cp37m-win_amd64.whl", hash = "sha256:00eaf43dbd05ba6a9b0080c77e161e0b7a601f9a3f660727a952e40140537de7"},
|
||||||
{file = "pywin32-227-cp38-cp38-win32.whl", hash = "sha256:7f18199fbf29ca99dff10e1f09451582ae9e372a892ff03a28528a24d55875bc"},
|
{file = "pywin32-228-cp38-cp38-win32.whl", hash = "sha256:fa6ba028909cfc64ce9e24bcf22f588b14871980d9787f1e2002c99af8f1850c"},
|
||||||
{file = "pywin32-227-cp38-cp38-win_amd64.whl", hash = "sha256:7c1ae32c489dc012930787f06244426f8356e129184a02c25aef163917ce158e"},
|
{file = "pywin32-228-cp38-cp38-win_amd64.whl", hash = "sha256:9b3466083f8271e1a5eb0329f4e0d61925d46b40b195a33413e0905dccb285e8"},
|
||||||
{file = "pywin32-227-cp39-cp39-win32.whl", hash = "sha256:c054c52ba46e7eb6b7d7dfae4dbd987a1bb48ee86debe3f245a2884ece46e295"},
|
{file = "pywin32-228-cp39-cp39-win32.whl", hash = "sha256:ed74b72d8059a6606f64842e7917aeee99159ebd6b8d6261c518d002837be298"},
|
||||||
{file = "pywin32-227-cp39-cp39-win_amd64.whl", hash = "sha256:f27cec5e7f588c3d1051651830ecc00294f90728d19c3bf6916e6dba93ea357c"},
|
{file = "pywin32-228-cp39-cp39-win_amd64.whl", hash = "sha256:8319bafdcd90b7202c50d6014efdfe4fde9311b3ff15fd6f893a45c0868de203"},
|
||||||
]
|
]
|
||||||
pywinpty = [
|
pywinpty = [
|
||||||
{file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"},
|
{file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"},
|
||||||
@@ -1362,38 +1455,38 @@ pywinpty = [
|
|||||||
{file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"},
|
{file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"},
|
||||||
]
|
]
|
||||||
pyzmq = [
|
pyzmq = [
|
||||||
{file = "pyzmq-19.0.1-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:58688a2dfa044fad608a8e70ba8d019d0b872ec2acd75b7b5e37da8905605891"},
|
{file = "pyzmq-19.0.2-cp27-cp27m-macosx_10_9_intel.whl", hash = "sha256:59f1e54627483dcf61c663941d94c4af9bf4163aec334171686cdaee67974fe5"},
|
||||||
{file = "pyzmq-19.0.1-cp27-cp27m-win32.whl", hash = "sha256:87c78f6936e2654397ca2979c1d323ee4a889eef536cc77a938c6b5be33351a7"},
|
{file = "pyzmq-19.0.2-cp27-cp27m-win32.whl", hash = "sha256:c36ffe1e5aa35a1af6a96640d723d0d211c5f48841735c2aa8d034204e87eb87"},
|
||||||
{file = "pyzmq-19.0.1-cp27-cp27m-win_amd64.whl", hash = "sha256:97b6255ae77328d0e80593681826a0479cb7bac0ba8251b4dd882f5145a2293a"},
|
{file = "pyzmq-19.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:0a422fc290d03958899743db091f8154958410fc76ce7ee0ceb66150f72c2c97"},
|
||||||
{file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:15b4cb21118f4589c4db8be4ac12b21c8b4d0d42b3ee435d47f686c32fe2e91f"},
|
{file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:c20dd60b9428f532bc59f2ef6d3b1029a28fc790d408af82f871a7db03e722ff"},
|
||||||
{file = "pyzmq-19.0.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:931339ac2000d12fe212e64f98ce291e81a7ec6c73b125f17cf08415b753c087"},
|
{file = "pyzmq-19.0.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d46fb17f5693244de83e434648b3dbb4f4b0fec88415d6cbab1c1452b6f2ae17"},
|
||||||
{file = "pyzmq-19.0.1-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:2a88b8fabd9cc35bd59194a7723f3122166811ece8b74018147a4ed8489e6421"},
|
{file = "pyzmq-19.0.2-cp35-cp35m-macosx_10_9_intel.whl", hash = "sha256:f1a25a61495b6f7bb986accc5b597a3541d9bd3ef0016f50be16dbb32025b302"},
|
||||||
{file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:bafd651b557dd81d89bd5f9c678872f3e7b7255c1c751b78d520df2caac80230"},
|
{file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:ab0d01148d13854de716786ca73701012e07dff4dfbbd68c4e06d8888743526e"},
|
||||||
{file = "pyzmq-19.0.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:8952f6ba6ae598e792703f3134af5a01af8f5c7cf07e9a148f05a12b02412cea"},
|
{file = "pyzmq-19.0.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:720d2b6083498a9281eaee3f2927486e9fe02cd16d13a844f2e95217f243efea"},
|
||||||
{file = "pyzmq-19.0.1-cp35-cp35m-win32.whl", hash = "sha256:54aa24fd60c4262286fc64ca632f9e747c7cc3a3a1144827490e1dc9b8a3a960"},
|
{file = "pyzmq-19.0.2-cp35-cp35m-win32.whl", hash = "sha256:29d51279060d0a70f551663bc592418bcad7f4be4eea7b324f6dd81de05cb4c1"},
|
||||||
{file = "pyzmq-19.0.1-cp35-cp35m-win_amd64.whl", hash = "sha256:dcbc3f30c11c60d709c30a213dc56e88ac016fe76ac6768e64717bd976072566"},
|
{file = "pyzmq-19.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:5120c64646e75f6db20cc16b9a94203926ead5d633de9feba4f137004241221d"},
|
||||||
{file = "pyzmq-19.0.1-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:6ca519309703e95d55965735a667809bbb65f52beda2fdb6312385d3e7a6d234"},
|
{file = "pyzmq-19.0.2-cp36-cp36m-macosx_10_9_intel.whl", hash = "sha256:8a6ada5a3f719bf46a04ba38595073df8d6b067316c011180102ba2a1925f5b5"},
|
||||||
{file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:4ee0bfd82077a3ff11c985369529b12853a4064320523f8e5079b630f9551448"},
|
{file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:fa411b1d8f371d3a49d31b0789eb6da2537dadbb2aef74a43aa99a78195c3f76"},
|
||||||
{file = "pyzmq-19.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ba6f24431b569aec674ede49cad197cad59571c12deed6ad8e3c596da8288217"},
|
{file = "pyzmq-19.0.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:00dca814469436455399660247d74045172955459c0bd49b54a540ce4d652185"},
|
||||||
{file = "pyzmq-19.0.1-cp36-cp36m-win32.whl", hash = "sha256:956775444d01331c7eb412c5fb9bb62130dfaac77e09f32764ea1865234e2ca9"},
|
{file = "pyzmq-19.0.2-cp36-cp36m-win32.whl", hash = "sha256:046b92e860914e39612e84fa760fc3f16054d268c11e0e25dcb011fb1bc6a075"},
|
||||||
{file = "pyzmq-19.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b08780e3a55215873b3b8e6e7ca8987f14c902a24b6ac081b344fd430d6ca7cd"},
|
{file = "pyzmq-19.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99cc0e339a731c6a34109e5c4072aaa06d8e32c0b93dc2c2d90345dd45fa196c"},
|
||||||
{file = "pyzmq-19.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:21f7d91f3536f480cb2c10d0756bfa717927090b7fb863e6323f766e5461ee1c"},
|
{file = "pyzmq-19.0.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e36f12f503511d72d9bdfae11cadbadca22ff632ff67c1b5459f69756a029c19"},
|
||||||
{file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:bfff5ffff051f5aa47ba3b379d87bd051c3196b0c8a603e8b7ed68a6b4f217ec"},
|
{file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c40fbb2b9933369e994b837ee72193d6a4c35dfb9a7c573257ef7ff28961272c"},
|
||||||
{file = "pyzmq-19.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:07fb8fe6826a229dada876956590135871de60dbc7de5a18c3bcce2ed1f03c98"},
|
{file = "pyzmq-19.0.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5d9fc809aa8d636e757e4ced2302569d6e60e9b9c26114a83f0d9d6519c40493"},
|
||||||
{file = "pyzmq-19.0.1-cp37-cp37m-win32.whl", hash = "sha256:342fb8a1dddc569bc361387782e8088071593e7eaf3e3ecf7d6bd4976edff112"},
|
{file = "pyzmq-19.0.2-cp37-cp37m-win32.whl", hash = "sha256:3fa6debf4bf9412e59353defad1f8035a1e68b66095a94ead8f7a61ae90b2675"},
|
||||||
{file = "pyzmq-19.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:faee2604f279d31312bc455f3d024f160b6168b9c1dde22bf62d8c88a4deca8e"},
|
{file = "pyzmq-19.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:73483a2caaa0264ac717af33d6fb3f143d8379e60a422730ee8d010526ce1913"},
|
||||||
{file = "pyzmq-19.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b9d21fc56c8aacd2e6d14738021a9d64f3f69b30578a99325a728e38a349f85"},
|
{file = "pyzmq-19.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:36ab114021c0cab1a423fe6689355e8f813979f2c750968833b318c1fa10a0fd"},
|
||||||
{file = "pyzmq-19.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:af0c02cf49f4f9eedf38edb4f3b6bb621d83026e7e5d76eb5526cc5333782fd6"},
|
{file = "pyzmq-19.0.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:8b66b94fe6243d2d1d89bca336b2424399aac57932858b9a30309803ffc28112"},
|
||||||
{file = "pyzmq-19.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:5f1f2eb22aab606f808163eb1d537ac9a0ba4283fbeb7a62eb48d9103cf015c2"},
|
{file = "pyzmq-19.0.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:654d3e06a4edc566b416c10293064732516cf8871a4522e0a2ba00cc2a2e600c"},
|
||||||
{file = "pyzmq-19.0.1-cp38-cp38-win32.whl", hash = "sha256:f9d7e742fb0196992477415bb34366c12e9bb9a0699b8b3f221ff93b213d7bec"},
|
{file = "pyzmq-19.0.2-cp38-cp38-win32.whl", hash = "sha256:276ad604bffd70992a386a84bea34883e696a6b22e7378053e5d3227321d9702"},
|
||||||
{file = "pyzmq-19.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:5b99c2ae8089ef50223c28bac57510c163bfdff158c9e90764f812b94e69a0e6"},
|
{file = "pyzmq-19.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:09d24a80ccb8cbda1af6ed8eb26b005b6743e58e9290566d2a6841f4e31fa8e0"},
|
||||||
{file = "pyzmq-19.0.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:cf5d689ba9513b9753959164cf500079383bc18859f58bf8ce06d8d4bef2b054"},
|
{file = "pyzmq-19.0.2-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:c1a31cd42905b405530e92bdb70a8a56f048c8a371728b8acf9d746ecd4482c0"},
|
||||||
{file = "pyzmq-19.0.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:aaa8b40b676576fd7806839a5de8e6d5d1b74981e6376d862af6c117af2a3c10"},
|
{file = "pyzmq-19.0.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7e7f930039ee0c4c26e4dfee015f20bd6919cd8b97c9cd7afbde2923a5167b6"},
|
||||||
{file = "pyzmq-19.0.1.tar.gz", hash = "sha256:13a5638ab24d628a6ade8f794195e1a1acd573496c3b85af2f1183603b7bf5e0"},
|
{file = "pyzmq-19.0.2.tar.gz", hash = "sha256:296540a065c8c21b26d63e3cea2d1d57902373b16e4256afe46422691903a438"},
|
||||||
]
|
]
|
||||||
qtconsole = [
|
qtconsole = [
|
||||||
{file = "qtconsole-4.7.4-py2.py3-none-any.whl", hash = "sha256:89442727940126c65c2f94a058f1b4693a0f5d4c4b192fd6518ba3b11f4791aa"},
|
{file = "qtconsole-4.7.6-py2.py3-none-any.whl", hash = "sha256:570b9e1dd4f9b727699b0ed04c6943d9d32d5a2085aa69d82d814e039bbcf74b"},
|
||||||
{file = "qtconsole-4.7.4.tar.gz", hash = "sha256:fd48bf1051d6e69cec1f9e2596cfaa94e3c726c70c5d848681ebce10c029f5fd"},
|
{file = "qtconsole-4.7.6.tar.gz", hash = "sha256:6c24397c19a49a5cf69582c931db4b0f6b00a78530a2bfd122936f2ebfae2fef"},
|
||||||
]
|
]
|
||||||
qtpy = [
|
qtpy = [
|
||||||
{file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"},
|
{file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"},
|
||||||
@@ -1466,8 +1559,8 @@ traitlets = [
|
|||||||
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
|
{file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"},
|
||||||
]
|
]
|
||||||
wcwidth = [
|
wcwidth = [
|
||||||
{file = "wcwidth-0.2.3-py2.py3-none-any.whl", hash = "sha256:980fbf4f3c196c0f329cdcd1e84c554d6a211f18e252e525a0cf4223154a41d6"},
|
{file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
|
||||||
{file = "wcwidth-0.2.3.tar.gz", hash = "sha256:edbc2b718b4db6cdf393eefe3a420183947d6aa312505ce6754516f458ff8830"},
|
{file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
|
||||||
]
|
]
|
||||||
webencodings = [
|
webencodings = [
|
||||||
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
|
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
|
||||||
|
|||||||
@@ -220,8 +220,8 @@ class BaseConvexOptimizer(BaseOptimizer):
|
|||||||
opt.solve(solver=self._solver, verbose=self._verbose)
|
opt.solve(solver=self._solver, verbose=self._verbose)
|
||||||
else:
|
else:
|
||||||
opt.solve(verbose=self._verbose)
|
opt.solve(verbose=self._verbose)
|
||||||
except (TypeError, cp.DCPError):
|
except (TypeError, cp.DCPError) as e:
|
||||||
raise exceptions.OptimizationError
|
raise exceptions.OptimizationError from e
|
||||||
|
|
||||||
if opt.status != "optimal":
|
if opt.status != "optimal":
|
||||||
raise exceptions.OptimizationError
|
raise exceptions.OptimizationError
|
||||||
@@ -341,6 +341,7 @@ class BaseConvexOptimizer(BaseOptimizer):
|
|||||||
weights_sum_to_one=True,
|
weights_sum_to_one=True,
|
||||||
constraints=None,
|
constraints=None,
|
||||||
solver="SLSQP",
|
solver="SLSQP",
|
||||||
|
initial_guess=None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Optimise some objective function using the scipy backend. This can
|
Optimise some objective function using the scipy backend. This can
|
||||||
@@ -374,6 +375,8 @@ class BaseConvexOptimizer(BaseOptimizer):
|
|||||||
:param solver: which SCIPY solver to use, e.g "SLSQP", "COBYLA", "BFGS".
|
:param solver: which SCIPY solver to use, e.g "SLSQP", "COBYLA", "BFGS".
|
||||||
User beware: different optimisers require different inputs.
|
User beware: different optimisers require different inputs.
|
||||||
:type solver: string
|
:type solver: string
|
||||||
|
:param initial_guess: the initial guess for the weights, shape (n,) or (n, 1)
|
||||||
|
:type initial_guess: np.ndarray
|
||||||
:return: asset weights that optimise the custom objective
|
:return: asset weights that optimise the custom objective
|
||||||
:rtype: OrderedDict
|
:rtype: OrderedDict
|
||||||
"""
|
"""
|
||||||
@@ -385,7 +388,8 @@ class BaseConvexOptimizer(BaseOptimizer):
|
|||||||
bound_array = np.vstack((self._lower_bounds, self._upper_bounds)).T
|
bound_array = np.vstack((self._lower_bounds, self._upper_bounds)).T
|
||||||
bounds = list(map(tuple, bound_array))
|
bounds = list(map(tuple, bound_array))
|
||||||
|
|
||||||
initial_guess = np.array([1 / self.n_assets] * self.n_assets)
|
if initial_guess is None:
|
||||||
|
initial_guess = np.array([1 / self.n_assets] * self.n_assets)
|
||||||
|
|
||||||
# Construct constraints
|
# Construct constraints
|
||||||
final_constraints = []
|
final_constraints = []
|
||||||
|
|||||||
@@ -375,7 +375,7 @@ class CLA(base_optimizer.BaseOptimizer):
|
|||||||
"""
|
"""
|
||||||
Maximise the Sharpe ratio.
|
Maximise the Sharpe ratio.
|
||||||
|
|
||||||
:return: asset weights for the volatility-minimising portfolio
|
:return: asset weights for the max-sharpe portfolio
|
||||||
:rtype: OrderedDict
|
:rtype: OrderedDict
|
||||||
"""
|
"""
|
||||||
if not self.w:
|
if not self.w:
|
||||||
|
|||||||
@@ -297,19 +297,21 @@ class DiscreteAllocation:
|
|||||||
# Integer allocation
|
# Integer allocation
|
||||||
x = cp.Variable(n, integer=True)
|
x = cp.Variable(n, integer=True)
|
||||||
# Remaining dollars
|
# Remaining dollars
|
||||||
r = self.total_portfolio_value - p.T * x
|
r = self.total_portfolio_value - p.T @ x
|
||||||
|
|
||||||
# Objective function is remaining dollars + sum of absolute deviations from ideality.
|
# Set up linear program
|
||||||
objective = r + cp.norm(w * self.total_portfolio_value - cp.multiply(x, p), 1)
|
eta = w * self.total_portfolio_value - cp.multiply(x, p)
|
||||||
constraints = [r + p.T * x == self.total_portfolio_value, x >= 0, r >= 0]
|
u = cp.Variable(n)
|
||||||
|
constraints = [eta <= u, eta >= -u, x >= 0, r >= 0]
|
||||||
|
objective = cp.sum(u) + r
|
||||||
|
|
||||||
opt = cp.Problem(cp.Minimize(objective), constraints)
|
opt = cp.Problem(cp.Minimize(objective), constraints)
|
||||||
opt.solve()
|
opt.solve(solver="GLPK_MI")
|
||||||
|
|
||||||
if opt.status not in {"optimal", "optimal_inaccurate"}:
|
if opt.status not in {"optimal", "optimal_inaccurate"}:
|
||||||
raise exceptions.OptimizationError("Please try greedy_portfolio")
|
raise exceptions.OptimizationError("Please try greedy_portfolio")
|
||||||
|
|
||||||
vals = np.rint(x.value)
|
vals = np.rint(x.value).astype(int)
|
||||||
self.allocation = self._remove_zero_positions(
|
self.allocation = self._remove_zero_positions(
|
||||||
collections.OrderedDict(zip([i[0] for i in self.weights], vals))
|
collections.OrderedDict(zip([i[0] for i in self.weights], vals))
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -271,6 +271,15 @@ class EfficientFrontier(base_optimizer.BaseConvexOptimizer):
|
|||||||
if not isinstance(target_volatility, (float, int)) or target_volatility < 0:
|
if not isinstance(target_volatility, (float, int)) or target_volatility < 0:
|
||||||
raise ValueError("target_volatility should be a positive float")
|
raise ValueError("target_volatility should be a positive float")
|
||||||
|
|
||||||
|
global_min_volatility = np.sqrt(1 / np.sum(np.linalg.inv(self.cov_matrix)))
|
||||||
|
|
||||||
|
if target_volatility < global_min_volatility:
|
||||||
|
raise ValueError(
|
||||||
|
"The minimum volatility is {:.3f}. Please use a higher target_volatility".format(
|
||||||
|
global_min_volatility
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
self._objective = objective_functions.portfolio_return(
|
self._objective = objective_functions.portfolio_return(
|
||||||
self._w, self.expected_returns
|
self._w, self.expected_returns
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ The ``exceptions`` module houses custom exceptions. Currently implemented:
|
|||||||
|
|
||||||
- OptimizationError
|
- OptimizationError
|
||||||
"""
|
"""
|
||||||
import traceback
|
|
||||||
|
|
||||||
|
|
||||||
class OptimizationError(Exception):
|
class OptimizationError(Exception):
|
||||||
@@ -16,9 +15,4 @@ class OptimizationError(Exception):
|
|||||||
default_message = (
|
default_message = (
|
||||||
"Please check your objectives/constraints or use a different solver."
|
"Please check your objectives/constraints or use a different solver."
|
||||||
)
|
)
|
||||||
|
super().__init__(default_message, *args, **kwargs)
|
||||||
if not (args or kwargs):
|
|
||||||
args = (default_message,)
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ Currently implemented:
|
|||||||
- general return model function, allowing you to run any return model from one function.
|
- general return model function, allowing you to run any return model from one function.
|
||||||
- mean historical return
|
- mean historical return
|
||||||
- exponentially weighted mean historical return
|
- exponentially weighted mean historical return
|
||||||
- James-Stein shrinkage
|
|
||||||
- CAPM estimate of returns
|
- CAPM estimate of returns
|
||||||
|
|
||||||
Additionally, we provide utility functions to convert from returns to prices and vice-versa.
|
Additionally, we provide utility functions to convert from returns to prices and vice-versa.
|
||||||
@@ -26,17 +25,22 @@ import pandas as pd
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
def returns_from_prices(prices):
|
def returns_from_prices(prices, log_returns=False):
|
||||||
"""
|
"""
|
||||||
Calculate the returns given prices.
|
Calculate the returns given prices.
|
||||||
|
|
||||||
:param prices: adjusted (daily) closing prices of the asset, each row is a
|
:param prices: adjusted (daily) closing prices of the asset, each row is a
|
||||||
date and each column is a ticker/id.
|
date and each column is a ticker/id.
|
||||||
:type prices: pd.DataFrame
|
:type prices: pd.DataFrame
|
||||||
|
:param log_returns: whether to compute using log returns
|
||||||
|
:type log_returns: bool, defaults to False
|
||||||
:return: (daily) returns
|
:return: (daily) returns
|
||||||
:rtype: pd.DataFrame
|
:rtype: pd.DataFrame
|
||||||
"""
|
"""
|
||||||
return prices.pct_change().dropna(how="all")
|
if log_returns:
|
||||||
|
return np.log(1 + prices.pct_change()).dropna(how="all")
|
||||||
|
else:
|
||||||
|
return prices.pct_change().dropna(how="all")
|
||||||
|
|
||||||
|
|
||||||
def log_returns_from_prices(prices):
|
def log_returns_from_prices(prices):
|
||||||
@@ -49,10 +53,13 @@ def log_returns_from_prices(prices):
|
|||||||
:return: (daily) returns
|
:return: (daily) returns
|
||||||
:rtype: pd.DataFrame
|
:rtype: pd.DataFrame
|
||||||
"""
|
"""
|
||||||
|
warnings.warn(
|
||||||
|
"log_returns_from_prices is deprecated. Please use returns_from_prices(prices, log_returns=True)"
|
||||||
|
)
|
||||||
return np.log(1 + prices.pct_change()).dropna(how="all")
|
return np.log(1 + prices.pct_change()).dropna(how="all")
|
||||||
|
|
||||||
|
|
||||||
def prices_from_returns(returns):
|
def prices_from_returns(returns, log_returns=False):
|
||||||
"""
|
"""
|
||||||
Calculate the pseudo-prices given returns. These are not true prices because
|
Calculate the pseudo-prices given returns. These are not true prices because
|
||||||
the initial prices are all set to 1, but it behaves as intended when passed
|
the initial prices are all set to 1, but it behaves as intended when passed
|
||||||
@@ -60,9 +67,13 @@ def prices_from_returns(returns):
|
|||||||
|
|
||||||
:param returns: (daily) percentage returns of the assets
|
:param returns: (daily) percentage returns of the assets
|
||||||
:type returns: pd.DataFrame
|
:type returns: pd.DataFrame
|
||||||
|
:param log_returns: whether to compute using log returns
|
||||||
|
:type log_returns: bool, defaults to False
|
||||||
:return: (daily) pseudo-prices.
|
:return: (daily) pseudo-prices.
|
||||||
:rtype: pd.DataFrame
|
:rtype: pd.DataFrame
|
||||||
"""
|
"""
|
||||||
|
if log_returns:
|
||||||
|
returns = np.exp(returns)
|
||||||
ret = 1 + returns
|
ret = 1 + returns
|
||||||
ret.iloc[0] = 1 # set first day pseudo-price
|
ret.iloc[0] = 1 # set first day pseudo-price
|
||||||
return ret.cumprod()
|
return ret.cumprod()
|
||||||
@@ -81,7 +92,6 @@ def return_model(prices, method="mean_historical_return", **kwargs):
|
|||||||
|
|
||||||
- ``mean_historical_return``
|
- ``mean_historical_return``
|
||||||
- ``ema_historical_return``
|
- ``ema_historical_return``
|
||||||
- ``james_stein_shrinkage``
|
|
||||||
- ``capm_return``
|
- ``capm_return``
|
||||||
|
|
||||||
:type method: str, optional
|
:type method: str, optional
|
||||||
@@ -93,19 +103,16 @@ def return_model(prices, method="mean_historical_return", **kwargs):
|
|||||||
return mean_historical_return(prices, **kwargs)
|
return mean_historical_return(prices, **kwargs)
|
||||||
elif method == "ema_historical_return":
|
elif method == "ema_historical_return":
|
||||||
return ema_historical_return(prices, **kwargs)
|
return ema_historical_return(prices, **kwargs)
|
||||||
elif method == "james_stein_shrinkage":
|
|
||||||
return james_stein_shrinkage(prices, **kwargs)
|
|
||||||
elif method == "capm_return":
|
elif method == "capm_return":
|
||||||
return capm_return(prices, **kwargs)
|
return capm_return(prices, **kwargs)
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError("Return model {} not implemented".format(method))
|
raise NotImplementedError("Return model {} not implemented".format(method))
|
||||||
|
|
||||||
|
|
||||||
def mean_historical_return(
|
def mean_historical_return(prices, returns_data=False, compounding=True, frequency=252):
|
||||||
prices, returns_data=False, compounding=False, frequency=252
|
|
||||||
):
|
|
||||||
"""
|
"""
|
||||||
Calculate annualised mean (daily) historical return from input (daily) asset prices.
|
Calculate annualised mean (daily) historical return from input (daily) asset prices.
|
||||||
|
By default, this uses the arithmetic mean (correct if log_returns are used).
|
||||||
|
|
||||||
:param prices: adjusted closing prices of the asset, each row is a date
|
:param prices: adjusted closing prices of the asset, each row is a date
|
||||||
and each column is a ticker/id.
|
and each column is a ticker/id.
|
||||||
@@ -113,7 +120,7 @@ def mean_historical_return(
|
|||||||
:param returns_data: if true, the first argument is returns instead of prices.
|
:param returns_data: if true, the first argument is returns instead of prices.
|
||||||
:type returns_data: bool, defaults to False.
|
:type returns_data: bool, defaults to False.
|
||||||
:param compounding: whether to properly compound the returns, optional.
|
:param compounding: whether to properly compound the returns, optional.
|
||||||
:type compounding: bool, defaults to False
|
:type compounding: bool, defaults to True
|
||||||
:param frequency: number of time periods in a year, defaults to 252 (the number
|
:param frequency: number of time periods in a year, defaults to 252 (the number
|
||||||
of trading days in a year)
|
of trading days in a year)
|
||||||
:type frequency: int, optional
|
:type frequency: int, optional
|
||||||
@@ -128,13 +135,13 @@ def mean_historical_return(
|
|||||||
else:
|
else:
|
||||||
returns = returns_from_prices(prices)
|
returns = returns_from_prices(prices)
|
||||||
if compounding:
|
if compounding:
|
||||||
return (1 + returns.mean()) ** frequency - 1
|
return (1 + returns).prod() ** (frequency / returns.count()) - 1
|
||||||
else:
|
else:
|
||||||
return returns.mean() * frequency
|
return returns.mean() * frequency
|
||||||
|
|
||||||
|
|
||||||
def ema_historical_return(
|
def ema_historical_return(
|
||||||
prices, returns_data=False, compounding=False, span=500, frequency=252
|
prices, returns_data=False, compounding=True, span=500, frequency=252
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Calculate the exponentially-weighted mean of (daily) historical returns, giving
|
Calculate the exponentially-weighted mean of (daily) historical returns, giving
|
||||||
@@ -146,7 +153,7 @@ def ema_historical_return(
|
|||||||
:param returns_data: if true, the first argument is returns instead of prices.
|
:param returns_data: if true, the first argument is returns instead of prices.
|
||||||
:type returns_data: bool, defaults to False.
|
:type returns_data: bool, defaults to False.
|
||||||
:param compounding: whether to properly compound the returns, optional.
|
:param compounding: whether to properly compound the returns, optional.
|
||||||
:type compounding: bool, defaults to False
|
:type compounding: bool, defaults to True
|
||||||
:param frequency: number of time periods in a year, defaults to 252 (the number
|
:param frequency: number of time periods in a year, defaults to 252 (the number
|
||||||
of trading days in a year)
|
of trading days in a year)
|
||||||
:type frequency: int, optional
|
:type frequency: int, optional
|
||||||
@@ -169,49 +176,10 @@ def ema_historical_return(
|
|||||||
return returns.ewm(span=span).mean().iloc[-1] * frequency
|
return returns.ewm(span=span).mean().iloc[-1] * frequency
|
||||||
|
|
||||||
|
|
||||||
def james_stein_shrinkage(prices, returns_data=False, compounding=False, frequency=252):
|
def james_stein_shrinkage(prices, returns_data=False, compounding=True, frequency=252):
|
||||||
r"""
|
raise NotImplementedError(
|
||||||
Compute the James-Stein shrinkage estimator, i.e
|
"Deprecated because its implementation here was misguided."
|
||||||
|
)
|
||||||
.. math::
|
|
||||||
|
|
||||||
\hat{\mu}_i^{JS} = \hat{\kappa} \bar{\mu} + (1-\hat{\kappa}) \mu_i,
|
|
||||||
|
|
||||||
where :math:`\kappa` is the shrinkage parameter, :math:`\bar{\mu}` is the shrinkage
|
|
||||||
target (grand average), and :math:`\mu` is the vector of mean returns.
|
|
||||||
|
|
||||||
:param prices: adjusted closing prices of the asset, each row is a date
|
|
||||||
and each column is a ticker/id.
|
|
||||||
:type prices: pd.DataFrame
|
|
||||||
:param returns_data: if true, the first argument is returns instead of prices.
|
|
||||||
:type returns_data: bool, defaults to False.
|
|
||||||
:param compounded: whether to properly compound the returns, optional.
|
|
||||||
:type compounding: bool, defaults to False
|
|
||||||
:param frequency: number of time periods in a year, defaults to 252 (the number
|
|
||||||
of trading days in a year)
|
|
||||||
:type frequency: int, optional
|
|
||||||
:return: James-Stein estimate of annualised return
|
|
||||||
:rtype: pd.Series
|
|
||||||
"""
|
|
||||||
if not isinstance(prices, pd.DataFrame):
|
|
||||||
warnings.warn("prices are not in a dataframe", RuntimeWarning)
|
|
||||||
prices = pd.DataFrame(prices)
|
|
||||||
if returns_data:
|
|
||||||
returns = prices
|
|
||||||
else:
|
|
||||||
returns = returns_from_prices(prices)
|
|
||||||
|
|
||||||
T, n = returns.shape
|
|
||||||
mu = returns.mean(axis=0)
|
|
||||||
mu_bar = mu.mean()
|
|
||||||
sigma_squared = 1 / T * mu_bar * (1 - mu_bar) # binomial estimate
|
|
||||||
kappa = 1 - (n - 3) * sigma_squared / np.sum((mu - mu_bar) ** 2)
|
|
||||||
theta_js = (1 - kappa) * mu + kappa * mu_bar
|
|
||||||
|
|
||||||
if compounding:
|
|
||||||
return (1 + theta_js) ** frequency - 1
|
|
||||||
else:
|
|
||||||
return theta_js * frequency
|
|
||||||
|
|
||||||
|
|
||||||
def capm_return(
|
def capm_return(
|
||||||
@@ -219,7 +187,7 @@ def capm_return(
|
|||||||
market_prices=None,
|
market_prices=None,
|
||||||
returns_data=False,
|
returns_data=False,
|
||||||
risk_free_rate=0.02,
|
risk_free_rate=0.02,
|
||||||
compounding=False,
|
compounding=True,
|
||||||
frequency=252,
|
frequency=252,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
@@ -244,7 +212,7 @@ def capm_return(
|
|||||||
to the frequency parameter.
|
to the frequency parameter.
|
||||||
:type risk_free_rate: float, optional
|
:type risk_free_rate: float, optional
|
||||||
:param compounding: whether to properly compound the returns, optional.
|
:param compounding: whether to properly compound the returns, optional.
|
||||||
:type compounding: bool, defaults to False
|
:type compounding: bool, defaults to True
|
||||||
:param frequency: number of time periods in a year, defaults to 252 (the number
|
:param frequency: number of time periods in a year, defaults to 252 (the number
|
||||||
of trading days in a year)
|
of trading days in a year)
|
||||||
:type frequency: int, optional
|
:type frequency: int, optional
|
||||||
@@ -267,7 +235,6 @@ def capm_return(
|
|||||||
if market_returns is None:
|
if market_returns is None:
|
||||||
# Append market return to right and compute sample covariance matrix
|
# Append market return to right and compute sample covariance matrix
|
||||||
returns["mkt"] = returns.mean(axis=1)
|
returns["mkt"] = returns.mean(axis=1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
market_returns.columns = ["mkt"]
|
market_returns.columns = ["mkt"]
|
||||||
returns = returns.join(market_returns, how="left")
|
returns = returns.join(market_returns, how="left")
|
||||||
@@ -279,7 +246,9 @@ def capm_return(
|
|||||||
betas = betas.drop("mkt")
|
betas = betas.drop("mkt")
|
||||||
# Find mean market return on a given time period
|
# Find mean market return on a given time period
|
||||||
if compounding:
|
if compounding:
|
||||||
mkt_mean_ret = (1 + returns["mkt"].mean()) ** frequency - 1
|
mkt_mean_ret = (1 + returns["mkt"]).prod() ** (
|
||||||
|
frequency / returns["mkt"].count()
|
||||||
|
) - 1
|
||||||
else:
|
else:
|
||||||
mkt_mean_ret = returns["mkt"].mean() * frequency
|
mkt_mean_ret = returns["mkt"].mean() * frequency
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ Currently implemented:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import warnings
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import scipy.cluster.hierarchy as sch
|
import scipy.cluster.hierarchy as sch
|
||||||
@@ -139,13 +138,19 @@ class HRPOpt(base_optimizer.BaseOptimizer):
|
|||||||
w[second_cluster] *= 1 - alpha # weight 2
|
w[second_cluster] *= 1 - alpha # weight 2
|
||||||
return w
|
return w
|
||||||
|
|
||||||
def optimize(self):
|
def optimize(self, linkage_method="single"):
|
||||||
"""
|
"""
|
||||||
Construct a hierarchical risk parity portfolio
|
Construct a hierarchical risk parity portfolio, using Scipy hierarchical clustering
|
||||||
|
(see `here <https://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html>`_)
|
||||||
|
|
||||||
|
:param linkage_method: which scipy linkage method to use
|
||||||
|
:type linkage_method: str
|
||||||
:return: weights for the HRP portfolio
|
:return: weights for the HRP portfolio
|
||||||
:rtype: OrderedDict
|
:rtype: OrderedDict
|
||||||
"""
|
"""
|
||||||
|
if linkage_method not in sch._LINKAGE_METHODS:
|
||||||
|
raise ValueError("linkage_method must be one recognised by scipy")
|
||||||
|
|
||||||
if self.returns is None:
|
if self.returns is None:
|
||||||
cov = self.cov_matrix
|
cov = self.cov_matrix
|
||||||
corr = risk_models.cov_to_corr(self.cov_matrix).round(6)
|
corr = risk_models.cov_to_corr(self.cov_matrix).round(6)
|
||||||
@@ -159,7 +164,7 @@ class HRPOpt(base_optimizer.BaseOptimizer):
|
|||||||
matrix = np.sqrt(np.clip((1.0 - corr) / 2.0, a_min=0.0, a_max=1.0))
|
matrix = np.sqrt(np.clip((1.0 - corr) / 2.0, a_min=0.0, a_max=1.0))
|
||||||
dist = ssd.squareform(matrix, checks=False)
|
dist = ssd.squareform(matrix, checks=False)
|
||||||
|
|
||||||
self.clusters = sch.linkage(dist, "single")
|
self.clusters = sch.linkage(dist, linkage_method)
|
||||||
sort_ix = HRPOpt._get_quasi_diag(self.clusters)
|
sort_ix = HRPOpt._get_quasi_diag(self.clusters)
|
||||||
ordered_tickers = corr.index[sort_ix].tolist()
|
ordered_tickers = corr.index[sort_ix].tolist()
|
||||||
hrp = HRPOpt._raw_hrp_allocation(cov, ordered_tickers)
|
hrp = HRPOpt._raw_hrp_allocation(cov, ordered_tickers)
|
||||||
|
|||||||
@@ -117,6 +117,12 @@ def L2_reg(w, gamma=1):
|
|||||||
r"""
|
r"""
|
||||||
L2 regularisation, i.e :math:`\gamma ||w||^2`, to increase the number of nonzero weights.
|
L2 regularisation, i.e :math:`\gamma ||w||^2`, to increase the number of nonzero weights.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
ef = EfficientFrontier(mu, S)
|
||||||
|
ef.add_objective(objective_functions.L2_reg, gamma=2)
|
||||||
|
ef.min_volatility()
|
||||||
|
|
||||||
:param w: asset weights in the portfolio
|
:param w: asset weights in the portfolio
|
||||||
:type w: np.ndarray OR cp.Variable
|
:type w: np.ndarray OR cp.Variable
|
||||||
:param gamma: L2 regularisation parameter, defaults to 1. Increase if you want more
|
:param gamma: L2 regularisation parameter, defaults to 1. Increase if you want more
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ def _is_positive_semidefinite(matrix):
|
|||||||
:rtype: bool
|
:rtype: bool
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
np.linalg.cholesky(matrix)
|
# Significantly more efficient than checking eigenvalues (stackoverflow.com/questions/16266720)
|
||||||
|
np.linalg.cholesky(matrix + 1e-16 * np.eye(len(matrix)))
|
||||||
return True
|
return True
|
||||||
except np.linalg.LinAlgError:
|
except np.linalg.LinAlgError:
|
||||||
return False
|
return False
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ python = "^3.5"
|
|||||||
numpy = "^1.12"
|
numpy = "^1.12"
|
||||||
scipy = "^1.3"
|
scipy = "^1.3"
|
||||||
pandas = ">=0.19"
|
pandas = ">=0.19"
|
||||||
cvxpy = "~1.0"
|
cvxpy = "^1.0"
|
||||||
|
cvxopt = "^1.2"
|
||||||
|
|
||||||
[tool.poetry.dev-dependencies]
|
[tool.poetry.dev-dependencies]
|
||||||
pytest = "^4.6"
|
pytest = "^4.6"
|
||||||
|
|||||||
@@ -3,4 +3,5 @@ numpy>=1.17
|
|||||||
pandas>=0.19
|
pandas>=0.19
|
||||||
scikit-learn>=0.19
|
scikit-learn>=0.19
|
||||||
scipy>=1.3
|
scipy>=1.3
|
||||||
cvxpy==1.0.28
|
cvxpy>=1.0
|
||||||
|
cvxopt>=1.0
|
||||||
|
|||||||
@@ -143,26 +143,26 @@ def test_bl_returns_all_views():
|
|||||||
posterior_rets,
|
posterior_rets,
|
||||||
np.array(
|
np.array(
|
||||||
[
|
[
|
||||||
0.11774473,
|
0.11168648,
|
||||||
0.1709139,
|
0.16782938,
|
||||||
0.12180833,
|
0.12516799,
|
||||||
0.21202423,
|
0.24067997,
|
||||||
0.28120945,
|
0.32848296,
|
||||||
-0.2787358,
|
-0.22789895,
|
||||||
0.17274774,
|
0.16311297,
|
||||||
0.12714698,
|
0.11928542,
|
||||||
0.25492005,
|
0.25414308,
|
||||||
0.11229777,
|
0.11007738,
|
||||||
0.07182723,
|
0.06282615,
|
||||||
-0.01521839,
|
-0.03140218,
|
||||||
-0.21235465,
|
-0.16977172,
|
||||||
0.06399515,
|
0.05254821,
|
||||||
-0.11738365,
|
-0.10463884,
|
||||||
0.28865661,
|
0.32173375,
|
||||||
0.23828607,
|
0.26399864,
|
||||||
0.12038049,
|
0.1118594,
|
||||||
0.2331218,
|
0.22999558,
|
||||||
0.10485376,
|
0.08977448,
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ def test_cla_max_sharpe_long_only():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
cla.portfolio_performance(),
|
cla.portfolio_performance(),
|
||||||
(0.3253436663900292, 0.21333530089904357, 1.4312852355106793),
|
(0.2994470912768992, 0.21764331657015668, 1.283968171780824),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ def test_cla_max_sharpe_short():
|
|||||||
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
cla.portfolio_performance(),
|
cla.portfolio_performance(),
|
||||||
(0.3799273115521356, 0.23115368271125736, 1.5570909679242886),
|
(0.44859872371106785, 0.26762066559448255, 1.601515797589826),
|
||||||
)
|
)
|
||||||
sharpe = cla.portfolio_performance()[2]
|
sharpe = cla.portfolio_performance()[2]
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ def test_cla_min_volatility():
|
|||||||
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
cla.portfolio_performance(),
|
cla.portfolio_performance(),
|
||||||
(0.1793123248125915, 0.15915084514118688, 1.00101463282373),
|
(0.1505682139948257, 0.15915084514118688, 0.8204054077060994),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ def test_cla_max_sharpe_semicovariance():
|
|||||||
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
cla.portfolio_performance(),
|
cla.portfolio_performance(),
|
||||||
(0.2936179968144084, 0.06362345488289835, 4.300583759841616),
|
(0.2686858719299194, 0.06489248187610204, 3.8322755539652578),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ def test_cla_max_sharpe_exp_cov():
|
|||||||
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
cla.portfolio_performance(),
|
cla.portfolio_performance(),
|
||||||
(0.3619453128519127, 0.1724297730592084, 1.9830990135009723),
|
(0.32971891062187103, 0.17670121760851704, 1.7527831149871063),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ def test_cla_min_volatility_exp_cov_short():
|
|||||||
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
np.testing.assert_almost_equal(cla.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
cla.portfolio_performance(),
|
cla.portfolio_performance(),
|
||||||
(0.2634735528776959, 0.13259590618253303, 1.8362071642131053),
|
(0.23215576461823062, 0.1325959061825329, 1.6000174569958052),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ def test_custom_convex_logarithmic_barrier():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.23978400459553223, 0.21100848889958182, 1.041588448605623),
|
(0.17261881638711316, 0.21100848889958182, 0.7232828270702603),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ def test_custom_convex_deviation_risk_parity_error():
|
|||||||
|
|
||||||
def deviation_risk_parity(w, cov_matrix):
|
def deviation_risk_parity(w, cov_matrix):
|
||||||
n = cov_matrix.shape[0]
|
n = cov_matrix.shape[0]
|
||||||
rp = (w * (cov_matrix @ w)) / cp.quad_form(w, cov_matrix)
|
rp = (w @ (cov_matrix @ w)) / cp.quad_form(w, cov_matrix)
|
||||||
return cp.sum_squares(rp - 1 / n)
|
return cp.sum_squares(rp - 1 / n)
|
||||||
|
|
||||||
with pytest.raises(exceptions.OptimizationError):
|
with pytest.raises(exceptions.OptimizationError):
|
||||||
@@ -287,6 +287,6 @@ def test_custom_nonconvex_objective_market_neutral_efficient_risk():
|
|||||||
)
|
)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2309497754562942, target_risk, 1.1102600451243954),
|
(0.2591296227818582, target_risk, 1.258574109251818),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -67,7 +67,9 @@ def test_greedy_allocation_rmse_error():
|
|||||||
latest_prices = get_latest_prices(df)
|
latest_prices = get_latest_prices(df)
|
||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
da.greedy_portfolio()
|
da.greedy_portfolio()
|
||||||
np.testing.assert_almost_equal(da._allocation_rmse_error(), 0.025762032436733803)
|
np.testing.assert_almost_equal(
|
||||||
|
da._allocation_rmse_error(verbose=False), 0.017086185150415774
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_greedy_portfolio_allocation_short():
|
def test_greedy_portfolio_allocation_short():
|
||||||
@@ -81,25 +83,25 @@ def test_greedy_portfolio_allocation_short():
|
|||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
allocation, leftover = da.greedy_portfolio()
|
allocation, leftover = da.greedy_portfolio()
|
||||||
|
|
||||||
assert da.allocation == {
|
assert allocation == {
|
||||||
"MA": 15,
|
"MA": 19,
|
||||||
"PFE": 45,
|
"PFE": 42,
|
||||||
"FB": 8,
|
"FB": 7,
|
||||||
|
"GOOG": 1,
|
||||||
"BABA": 5,
|
"BABA": 5,
|
||||||
"AAPL": 4,
|
"AAPL": 4,
|
||||||
"BBY": 8,
|
"SBUX": 8,
|
||||||
"AMZN": 1,
|
"BBY": 6,
|
||||||
"SBUX": 9,
|
"XOM": 4,
|
||||||
"WMT": 2,
|
"WMT": 3,
|
||||||
"XOM": 2,
|
|
||||||
"BAC": -32,
|
"BAC": -32,
|
||||||
"GM": -16,
|
"AMD": -48,
|
||||||
"GE": -43,
|
"SHLD": -132,
|
||||||
"SHLD": -110,
|
"GM": -9,
|
||||||
"AMD": -34,
|
"RRC": -19,
|
||||||
"JPM": -1,
|
"GE": -14,
|
||||||
"T": -1,
|
"T": -5,
|
||||||
"UAA": -1,
|
"UAA": -8,
|
||||||
}
|
}
|
||||||
long_total = 0
|
long_total = 0
|
||||||
short_total = 0
|
short_total = 0
|
||||||
@@ -123,7 +125,9 @@ def test_greedy_allocation_rmse_error_short():
|
|||||||
latest_prices = get_latest_prices(df)
|
latest_prices = get_latest_prices(df)
|
||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
da.greedy_portfolio()
|
da.greedy_portfolio()
|
||||||
np.testing.assert_almost_equal(da._allocation_rmse_error(), 0.033070015016740284)
|
np.testing.assert_almost_equal(
|
||||||
|
da._allocation_rmse_error(verbose=False), 0.06063511265243106
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_greedy_portfolio_allocation_short_different_params():
|
def test_greedy_portfolio_allocation_short_different_params():
|
||||||
@@ -139,27 +143,26 @@ def test_greedy_portfolio_allocation_short_different_params():
|
|||||||
)
|
)
|
||||||
allocation, leftover = da.greedy_portfolio()
|
allocation, leftover = da.greedy_portfolio()
|
||||||
|
|
||||||
assert da.allocation == {
|
assert allocation == {
|
||||||
"MA": 77,
|
"MA": 96,
|
||||||
"PFE": 225,
|
"PFE": 211,
|
||||||
"FB": 41,
|
"FB": 34,
|
||||||
"BABA": 25,
|
"GOOG": 4,
|
||||||
"AAPL": 23,
|
"BABA": 22,
|
||||||
"BBY": 44,
|
"AAPL": 17,
|
||||||
|
"SBUX": 38,
|
||||||
"AMZN": 2,
|
"AMZN": 2,
|
||||||
"SBUX": 45,
|
"BBY": 27,
|
||||||
"GOOG": 3,
|
"XOM": 19,
|
||||||
"WMT": 11,
|
"WMT": 10,
|
||||||
"XOM": 11,
|
"BAC": -269,
|
||||||
"BAC": -271,
|
"AMD": -399,
|
||||||
"GM": -133,
|
"SHLD": -1099,
|
||||||
"GE": -356,
|
"GM": -78,
|
||||||
"SHLD": -922,
|
"RRC": -154,
|
||||||
"AMD": -285,
|
"GE": -119,
|
||||||
"JPM": -5,
|
"T": -41,
|
||||||
"T": -14,
|
"UAA": -64,
|
||||||
"UAA": -8,
|
|
||||||
"RRC": -3,
|
|
||||||
}
|
}
|
||||||
long_total = 0
|
long_total = 0
|
||||||
short_total = 0
|
short_total = 0
|
||||||
@@ -182,15 +185,15 @@ def test_lp_portfolio_allocation():
|
|||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
allocation, leftover = da.lp_portfolio()
|
allocation, leftover = da.lp_portfolio()
|
||||||
|
|
||||||
assert da.allocation == {
|
assert allocation == {
|
||||||
"AAPL": 5,
|
"GOOG": 1,
|
||||||
"FB": 11,
|
"AAPL": 4,
|
||||||
"BABA": 5,
|
"FB": 12,
|
||||||
"AMZN": 1,
|
"BABA": 4,
|
||||||
"BBY": 7,
|
"BBY": 2,
|
||||||
"MA": 14,
|
"MA": 20,
|
||||||
"PFE": 50,
|
"PFE": 54,
|
||||||
"SBUX": 5,
|
"SBUX": 1,
|
||||||
}
|
}
|
||||||
total = 0
|
total = 0
|
||||||
for ticker, num in allocation.items():
|
for ticker, num in allocation.items():
|
||||||
@@ -208,7 +211,9 @@ def test_lp_allocation_rmse_error():
|
|||||||
latest_prices = get_latest_prices(df)
|
latest_prices = get_latest_prices(df)
|
||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
da.lp_portfolio()
|
da.lp_portfolio()
|
||||||
np.testing.assert_almost_equal(da._allocation_rmse_error(verbose=False), 0.017070218149194846)
|
np.testing.assert_almost_equal(
|
||||||
|
da._allocation_rmse_error(verbose=False), 0.017082871441954087, decimal=5
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_lp_portfolio_allocation_short():
|
def test_lp_portfolio_allocation_short():
|
||||||
@@ -222,25 +227,25 @@ def test_lp_portfolio_allocation_short():
|
|||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
allocation, leftover = da.lp_portfolio()
|
allocation, leftover = da.lp_portfolio()
|
||||||
|
|
||||||
assert da.allocation == {
|
assert allocation == {
|
||||||
"GOOG": 1,
|
"GOOG": 1,
|
||||||
"AAPL": 5,
|
"AAPL": 4,
|
||||||
"FB": 8,
|
"FB": 7,
|
||||||
"BABA": 5,
|
"BABA": 5,
|
||||||
"WMT": 2,
|
"WMT": 3,
|
||||||
"XOM": 2,
|
"XOM": 4,
|
||||||
"BBY": 9,
|
"BBY": 6,
|
||||||
"MA": 16,
|
"MA": 19,
|
||||||
"PFE": 46,
|
"PFE": 42,
|
||||||
"SBUX": 9,
|
"SBUX": 8,
|
||||||
"GE": -43,
|
"GE": -14,
|
||||||
"AMD": -34,
|
"AMD": -48,
|
||||||
"BAC": -32,
|
"BAC": -32,
|
||||||
"GM": -16,
|
"GM": -9,
|
||||||
"T": -1,
|
"T": -5,
|
||||||
"UAA": -1,
|
"UAA": -8,
|
||||||
"SHLD": -110,
|
"SHLD": -132,
|
||||||
"JPM": -1,
|
"RRC": -19,
|
||||||
}
|
}
|
||||||
long_total = 0
|
long_total = 0
|
||||||
short_total = 0
|
short_total = 0
|
||||||
@@ -264,7 +269,9 @@ def test_lp_allocation_rmse_error_short():
|
|||||||
latest_prices = get_latest_prices(df)
|
latest_prices = get_latest_prices(df)
|
||||||
da = DiscreteAllocation(w, latest_prices)
|
da = DiscreteAllocation(w, latest_prices)
|
||||||
da.lp_portfolio()
|
da.lp_portfolio()
|
||||||
np.testing.assert_almost_equal(da._allocation_rmse_error(), 0.027018566693989568)
|
np.testing.assert_almost_equal(
|
||||||
|
da._allocation_rmse_error(verbose=False), 0.06063511265243109
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_lp_portfolio_allocation_different_params():
|
def test_lp_portfolio_allocation_different_params():
|
||||||
@@ -280,19 +287,18 @@ def test_lp_portfolio_allocation_different_params():
|
|||||||
)
|
)
|
||||||
allocation, leftover = da.lp_portfolio()
|
allocation, leftover = da.lp_portfolio()
|
||||||
|
|
||||||
assert da.allocation == {
|
assert allocation == {
|
||||||
"GOOG": 1,
|
"GOOG": 3,
|
||||||
"AAPL": 43,
|
"AAPL": 32,
|
||||||
"FB": 95,
|
"FB": 100,
|
||||||
"BABA": 44,
|
"BABA": 34,
|
||||||
"AMZN": 4,
|
"AMZN": 2,
|
||||||
"AMD": 1,
|
"BBY": 15,
|
||||||
"SHLD": 3,
|
"MA": 164,
|
||||||
"BBY": 69,
|
"PFE": 438,
|
||||||
"MA": 114,
|
"SBUX": 15,
|
||||||
"PFE": 412,
|
|
||||||
"SBUX": 51,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
total = 0
|
total = 0
|
||||||
for ticker, num in allocation.items():
|
for ticker, num in allocation.items():
|
||||||
total += num * latest_prices[ticker]
|
total += num * latest_prices[ticker]
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ def test_min_volatility():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.17931232481259154, 0.15915084514118694, 1.00101463282373),
|
(0.15056821399482578, 0.15915084514118694, 0.8204054077060996),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ def test_min_volatility_different_solver():
|
|||||||
assert set(w.keys()) == set(ef.tickers)
|
assert set(w.keys()) == set(ef.tickers)
|
||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
test_performance = (0.179312, 0.159151, 1.001015)
|
test_performance = (0.150567, 0.159150, 0.820403)
|
||||||
np.testing.assert_allclose(ef.portfolio_performance(), test_performance, atol=1e-5)
|
np.testing.assert_allclose(ef.portfolio_performance(), test_performance, atol=1e-5)
|
||||||
|
|
||||||
ef = setup_efficient_frontier(solver="OSQP")
|
ef = setup_efficient_frontier(solver="OSQP")
|
||||||
@@ -124,7 +124,7 @@ def test_min_volatility_short():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.1721356467349655, 0.1555915367269669, 0.9777887019776287),
|
(0.1516319319875544, 0.1555915367269669, 0.8460095886741129),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Shorting should reduce volatility
|
# Shorting should reduce volatility
|
||||||
@@ -156,7 +156,7 @@ def test_min_volatility_L2_reg():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.23129890623344232, 0.1955254118258614, 1.080672349748733),
|
(0.17356099329164965, 0.1955254118258614, 0.785376140408869),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ def test_min_volatility_tx_costs_L2_reg():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2316565265271545, 0.1959773703677164, 1.0800049318450338),
|
(0.17363446634404042, 0.1959773703677164, 0.7839398296638683),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -335,7 +335,7 @@ def test_max_sharpe_long_only():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.33035037367760506, 0.21671276571944567, 1.4320816434015786),
|
(0.3047768672819914, 0.22165566922402932, 1.2847714127003216),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -380,7 +380,7 @@ def test_max_sharpe_short():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.4072439477276246, 0.24823487545231313, 1.5599900981762558),
|
(0.4937195216716211, 0.29516576454651955, 1.6049270564945908),
|
||||||
)
|
)
|
||||||
sharpe = ef.portfolio_performance()[2]
|
sharpe = ef.portfolio_performance()[2]
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ def test_max_sharpe_L2_reg():
|
|||||||
ef = setup_efficient_frontier()
|
ef = setup_efficient_frontier()
|
||||||
ef.add_objective(objective_functions.L2_reg, gamma=5)
|
ef.add_objective(objective_functions.L2_reg, gamma=5)
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(UserWarning) as w:
|
||||||
weights = ef.max_sharpe()
|
weights = ef.max_sharpe()
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
|
|
||||||
@@ -405,7 +405,7 @@ def test_max_sharpe_L2_reg():
|
|||||||
assert all([i >= 0 for i in weights.values()])
|
assert all([i >= 0 for i in weights.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2936875354933478, 0.22783545277575057, 1.2012508683744123),
|
(0.2516854357026833, 0.22043282695478603, 1.051047790401043),
|
||||||
)
|
)
|
||||||
|
|
||||||
ef2 = setup_efficient_frontier()
|
ef2 = setup_efficient_frontier()
|
||||||
@@ -481,7 +481,7 @@ def test_max_sharpe_L2_reg_with_shorts():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.3076093180094401, 0.22415982749409985, 1.2830546901496447),
|
(0.2995338981166366, 0.2234696161770517, 1.2508810052063901),
|
||||||
)
|
)
|
||||||
new_number = sum(ef.weights > 0.01)
|
new_number = sum(ef.weights > 0.01)
|
||||||
assert new_number >= initial_number
|
assert new_number >= initial_number
|
||||||
@@ -733,7 +733,7 @@ def test_max_quadratic_utility():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.40064324249527605, 0.2917825266124642, 1.3045443362029479),
|
(0.3677732711751504, 0.2921342197778279, 1.1904571516463793),
|
||||||
)
|
)
|
||||||
|
|
||||||
ret1, var1, _ = ef.portfolio_performance()
|
ret1, var1, _ = ef.portfolio_performance()
|
||||||
@@ -752,7 +752,7 @@ def test_max_quadratic_utility_with_shorts():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(1.3318330413711252, 1.0198436183533854, 1.2863080356272452),
|
(1.4170505733098597, 1.0438577623242156, 1.3383533884915872),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -764,7 +764,7 @@ def test_max_quadratic_utility_market_neutral():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 0)
|
np.testing.assert_almost_equal(ef.weights.sum(), 0)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(1.13434841843883, 0.9896404148973286, 1.1260134506071473),
|
(1.248936321062371, 1.0219175004907117, 1.2025787996313317),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -789,7 +789,7 @@ def test_max_quadratic_utility_L2_reg():
|
|||||||
assert all([i >= 0 for i in weights.values()])
|
assert all([i >= 0 for i in weights.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2602803268728476, 0.21603540587515674, 1.112226608872166),
|
(0.19774277217586125, 0.2104822672707046, 0.8444548535162986),
|
||||||
)
|
)
|
||||||
|
|
||||||
ef2 = setup_efficient_frontier()
|
ef2 = setup_efficient_frontier()
|
||||||
@@ -820,7 +820,7 @@ def test_efficient_risk():
|
|||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.28577452556155075, 0.19, 1.3988132892376837),
|
(0.2552422849133517, 0.1900000002876062, 1.2381172871434818),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -834,7 +834,7 @@ def test_efficient_risk_error():
|
|||||||
assert ef.efficient_risk(min_possible_vol + 0.01)
|
assert ef.efficient_risk(min_possible_vol + 0.01)
|
||||||
|
|
||||||
ef = setup_efficient_frontier()
|
ef = setup_efficient_frontier()
|
||||||
with pytest.raises(exceptions.OptimizationError):
|
with pytest.raises(ValueError):
|
||||||
# This volatility is too low
|
# This volatility is too low
|
||||||
ef.efficient_risk(min_possible_vol - 0.01)
|
ef.efficient_risk(min_possible_vol - 0.01)
|
||||||
|
|
||||||
@@ -859,7 +859,7 @@ def test_efficient_risk_short():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.30468522897430295, 0.19, 1.4983424153337392),
|
(0.30035471606347336, 0.1900000003049494, 1.4755511348079207),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
sharpe = ef.portfolio_performance()[2]
|
sharpe = ef.portfolio_performance()[2]
|
||||||
@@ -882,7 +882,7 @@ def test_efficient_risk_L2_reg():
|
|||||||
assert all([i >= 0 for i in weights.values()])
|
assert all([i >= 0 for i in weights.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.24087463760460398, 0.19, 1.162498090632486),
|
(0.1931352562313653, 0.18999999989010993, 0.9112381912184281),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -908,7 +908,7 @@ def test_efficient_risk_market_neutral():
|
|||||||
assert (ef.weights < 1).all() and (ef.weights > -1).all()
|
assert (ef.weights < 1).all() and (ef.weights > -1).all()
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2552600197428133, 0.21, 1.1202858085349783),
|
(0.28640632960825885, 0.20999999995100788, 1.2686015698590967),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
sharpe = ef.portfolio_performance()[2]
|
sharpe = ef.portfolio_performance()[2]
|
||||||
@@ -933,17 +933,16 @@ def test_efficient_risk_market_neutral_L2_reg():
|
|||||||
|
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.10755645826336145, 0.11079556786108302, 0.7902523535340413),
|
(0.12790320789339854, 0.1175336636355454, 0.9180621496492316),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_efficient_risk_market_neutral_warning():
|
def test_efficient_risk_market_neutral_warning():
|
||||||
ef = setup_efficient_frontier()
|
ef = setup_efficient_frontier()
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(RuntimeWarning) as w:
|
||||||
ef.efficient_risk(0.19, market_neutral=True)
|
ef.efficient_risk(0.19, market_neutral=True)
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
assert issubclass(w[0].category, RuntimeWarning)
|
|
||||||
assert (
|
assert (
|
||||||
str(w[0].message)
|
str(w[0].message)
|
||||||
== "Market neutrality requires shorting - bounds have been amended"
|
== "Market neutrality requires shorting - bounds have been amended"
|
||||||
@@ -959,7 +958,7 @@ def test_efficient_return():
|
|||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.25, 0.1738852429895079, 1.3227114391408021),
|
(0.25, 0.18723269942026335, 1.2284179030274036),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -994,7 +993,7 @@ def test_efficient_return_short():
|
|||||||
assert set(w.keys()) == set(ef.tickers)
|
assert set(w.keys()) == set(ef.tickers)
|
||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(), (0.25, 0.16826225873038014, 1.3669137793315087)
|
ef.portfolio_performance(), (0.25, 0.17149595234895817, 1.3411395245760582)
|
||||||
)
|
)
|
||||||
sharpe = ef.portfolio_performance()[2]
|
sharpe = ef.portfolio_performance()[2]
|
||||||
|
|
||||||
@@ -1014,7 +1013,7 @@ def test_efficient_return_L2_reg():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(), (0.25, 0.20033592447690426, 1.1480716731187948)
|
ef.portfolio_performance(), (0.25, 0.20961660883459776, 1.0972412981906703)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1028,22 +1027,21 @@ def test_efficient_return_market_neutral():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 0)
|
np.testing.assert_almost_equal(ef.weights.sum(), 0)
|
||||||
assert (ef.weights < 1).all() and (ef.weights > -1).all()
|
assert (ef.weights < 1).all() and (ef.weights > -1).all()
|
||||||
np.testing.assert_almost_equal(
|
np.testing.assert_almost_equal(
|
||||||
ef.portfolio_performance(), (0.25, 0.20567263154580923, 1.1182819914898223)
|
ef.portfolio_performance(), (0.25, 0.1833060046337015, 1.2547324920403273)
|
||||||
)
|
)
|
||||||
sharpe = ef.portfolio_performance()[2]
|
sharpe = ef.portfolio_performance()[2]
|
||||||
ef_long_only = setup_efficient_frontier()
|
ef_long_only = setup_efficient_frontier()
|
||||||
ef_long_only.efficient_return(0.25)
|
ef_long_only.efficient_return(0.25)
|
||||||
long_only_sharpe = ef_long_only.portfolio_performance()[2]
|
long_only_sharpe = ef_long_only.portfolio_performance()[2]
|
||||||
assert long_only_sharpe > sharpe
|
assert long_only_sharpe < sharpe
|
||||||
|
|
||||||
|
|
||||||
def test_efficient_return_market_neutral_warning():
|
def test_efficient_return_market_neutral_warning():
|
||||||
# This fails
|
# This fails
|
||||||
ef = setup_efficient_frontier()
|
ef = setup_efficient_frontier()
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(RuntimeWarning) as w:
|
||||||
ef.efficient_return(0.25, market_neutral=True)
|
ef.efficient_return(0.25, market_neutral=True)
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
assert issubclass(w[0].category, RuntimeWarning)
|
|
||||||
assert (
|
assert (
|
||||||
str(w[0].message)
|
str(w[0].message)
|
||||||
== "Market neutrality requires shorting - bounds have been amended"
|
== "Market neutrality requires shorting - bounds have been amended"
|
||||||
@@ -1061,7 +1059,7 @@ def test_max_sharpe_semicovariance():
|
|||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2972184894480104, 0.06443145011260347, 4.302533762060766),
|
(0.2732301946250426, 0.06603231922971581, 3.834943215368455),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1077,7 +1075,7 @@ def test_max_sharpe_short_semicovariance():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.3564305116656491, 0.07201282488003401, 4.671813836300796),
|
(0.3907992623559733, 0.0809285460933456, 4.581810501430255),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1097,7 +1095,7 @@ def test_min_volatilty_shrunk_L2_reg():
|
|||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.23127396601517256, 0.19563960638632416, 1.0799140824173181),
|
(0.17358178582309983, 0.19563960638632416, 0.7850239972361532),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1113,7 +1111,7 @@ def test_efficient_return_shrunk():
|
|||||||
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
np.testing.assert_almost_equal(ef.weights.sum(), 1)
|
||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(), (0.22, 0.0849639369932322, 2.353939884117318)
|
ef.portfolio_performance(), (0.22, 0.08892192396903059, 2.2491641101878916)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1128,7 +1126,7 @@ def test_max_sharpe_exp_cov():
|
|||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.3678817256187322, 0.1753405505478982, 1.9840346373481956),
|
(0.33700887443850647, 0.1807332515488447, 1.7540152225548384),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1144,7 +1142,7 @@ def test_min_volatility_exp_cov_L2_reg():
|
|||||||
assert all([i >= 0 for i in w.values()])
|
assert all([i >= 0 for i in w.values()])
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.2434082300792007, 0.17835412793427002, 1.2526103694192867),
|
(0.1829496087575576, 0.17835412793427002, 0.9136295898775636),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1161,6 +1159,6 @@ def test_efficient_risk_exp_cov_market_neutral():
|
|||||||
assert (ef.weights < 1).all() and (ef.weights > -1).all()
|
assert (ef.weights < 1).all() and (ef.weights > -1).all()
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
ef.portfolio_performance(),
|
ef.portfolio_performance(),
|
||||||
(0.3908928033782067, 0.18999999995323363, 1.9520673866815672),
|
(0.3934093962620499, 0.18999999989011893, 1.9653126130421081),
|
||||||
atol=1e-6,
|
atol=1e-6,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def test_returns_from_prices():
|
|||||||
def test_log_returns_from_prices():
|
def test_log_returns_from_prices():
|
||||||
df = get_data()
|
df = get_data()
|
||||||
old_nan = df.isnull().sum(axis=1).sum()
|
old_nan = df.isnull().sum(axis=1).sum()
|
||||||
log_rets = expected_returns.log_returns_from_prices(df)
|
log_rets = expected_returns.returns_from_prices(df, log_returns=True)
|
||||||
new_nan = log_rets.isnull().sum(axis=1).sum()
|
new_nan = log_rets.isnull().sum(axis=1).sum()
|
||||||
assert new_nan == old_nan
|
assert new_nan == old_nan
|
||||||
np.testing.assert_almost_equal(log_rets.iloc[-1, -1], 0.0001682740081102576)
|
np.testing.assert_almost_equal(log_rets.iloc[-1, -1], 0.0001682740081102576)
|
||||||
@@ -55,17 +55,12 @@ def test_mean_historical_returns_dummy():
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
mean = expected_returns.mean_historical_return(data, frequency=1)
|
mean = expected_returns.mean_historical_return(data, frequency=1)
|
||||||
test_answer = pd.Series([0.00865598, 0.025, 0.01286968, -0.03632333])
|
test_answer = pd.Series([0.0061922, 0.0241137, 0.0122722, -0.0421775])
|
||||||
pd.testing.assert_series_equal(mean, test_answer)
|
pd.testing.assert_series_equal(mean, test_answer, check_less_precise=4)
|
||||||
mean = expected_returns.mean_historical_return(data, compounding=True, frequency=1)
|
|
||||||
pd.testing.assert_series_equal(mean, test_answer)
|
|
||||||
|
|
||||||
|
mean = expected_returns.mean_historical_return(data, compounding=False, frequency=1)
|
||||||
def test_mean_historical_returns_compounding():
|
test_answer = pd.Series([0.0086560, 0.0250000, 0.0128697, -0.03632333])
|
||||||
df = get_data()
|
pd.testing.assert_series_equal(mean, test_answer, check_less_precise=4)
|
||||||
mean = expected_returns.mean_historical_return(df)
|
|
||||||
mean2 = expected_returns.mean_historical_return(df, compounding=True)
|
|
||||||
assert (mean2 >= mean).all()
|
|
||||||
|
|
||||||
|
|
||||||
def test_mean_historical_returns():
|
def test_mean_historical_returns():
|
||||||
@@ -77,26 +72,26 @@ def test_mean_historical_returns():
|
|||||||
assert mean.dtype == "float64"
|
assert mean.dtype == "float64"
|
||||||
correct_mean = np.array(
|
correct_mean = np.array(
|
||||||
[
|
[
|
||||||
0.26770284,
|
0.247967,
|
||||||
0.3637864,
|
0.294304,
|
||||||
0.31709032,
|
0.284037,
|
||||||
0.22616723,
|
0.1923164,
|
||||||
0.49982007,
|
0.371327,
|
||||||
0.16888704,
|
0.1360093,
|
||||||
0.22754479,
|
0.0328503,
|
||||||
0.14783539,
|
0.1200115,
|
||||||
0.19001915,
|
0.105540,
|
||||||
0.08150653,
|
0.0423457,
|
||||||
0.12826351,
|
0.1002559,
|
||||||
0.25797816,
|
0.1442237,
|
||||||
0.07580128,
|
-0.0792602,
|
||||||
0.16087243,
|
0.1430506,
|
||||||
0.20510267,
|
0.0736356,
|
||||||
0.3511536,
|
0.238835,
|
||||||
0.38808003,
|
0.388665,
|
||||||
0.24635612,
|
0.226717,
|
||||||
0.21798433,
|
0.1561701,
|
||||||
0.28474973,
|
0.2318153,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
np.testing.assert_array_almost_equal(mean.values, correct_mean)
|
np.testing.assert_array_almost_equal(mean.values, correct_mean)
|
||||||
@@ -106,10 +101,9 @@ def test_mean_historical_returns_type_warning():
|
|||||||
df = get_data()
|
df = get_data()
|
||||||
mean = expected_returns.mean_historical_return(df)
|
mean = expected_returns.mean_historical_return(df)
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(RuntimeWarning) as w:
|
||||||
mean_from_array = expected_returns.mean_historical_return(np.array(df))
|
mean_from_array = expected_returns.mean_historical_return(np.array(df))
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
assert issubclass(w[0].category, RuntimeWarning)
|
|
||||||
assert str(w[0].message) == "prices are not in a dataframe"
|
assert str(w[0].message) == "prices are not in a dataframe"
|
||||||
|
|
||||||
np.testing.assert_array_almost_equal(mean.values, mean_from_array.values, decimal=6)
|
np.testing.assert_array_almost_equal(mean.values, mean_from_array.values, decimal=6)
|
||||||
@@ -117,8 +111,8 @@ def test_mean_historical_returns_type_warning():
|
|||||||
|
|
||||||
def test_mean_historical_returns_frequency():
|
def test_mean_historical_returns_frequency():
|
||||||
df = get_data()
|
df = get_data()
|
||||||
mean = expected_returns.mean_historical_return(df)
|
mean = expected_returns.mean_historical_return(df, compounding=False)
|
||||||
mean2 = expected_returns.mean_historical_return(df, frequency=52)
|
mean2 = expected_returns.mean_historical_return(df, compounding=False, frequency=52)
|
||||||
np.testing.assert_array_almost_equal(mean / 252, mean2 / 52)
|
np.testing.assert_array_almost_equal(mean / 252, mean2 / 52)
|
||||||
|
|
||||||
|
|
||||||
@@ -133,56 +127,18 @@ def test_ema_historical_return():
|
|||||||
|
|
||||||
def test_ema_historical_return_frequency():
|
def test_ema_historical_return_frequency():
|
||||||
df = get_data()
|
df = get_data()
|
||||||
mean = expected_returns.ema_historical_return(df)
|
mean = expected_returns.ema_historical_return(df, compounding=False)
|
||||||
mean2 = expected_returns.ema_historical_return(df, frequency=52)
|
mean2 = expected_returns.ema_historical_return(df, compounding=False, frequency=52)
|
||||||
np.testing.assert_array_almost_equal(mean / 252, mean2 / 52)
|
np.testing.assert_array_almost_equal(mean / 252, mean2 / 52)
|
||||||
|
|
||||||
mean3 = expected_returns.ema_historical_return(df, compounding=True)
|
|
||||||
assert (abs(mean3) > mean).all()
|
|
||||||
|
|
||||||
|
|
||||||
def test_ema_historical_return_limit():
|
def test_ema_historical_return_limit():
|
||||||
df = get_data()
|
df = get_data()
|
||||||
sma = expected_returns.mean_historical_return(df)
|
sma = expected_returns.mean_historical_return(df, compounding=False)
|
||||||
ema = expected_returns.ema_historical_return(df, span=1e10)
|
ema = expected_returns.ema_historical_return(df, compounding=False, span=1e10)
|
||||||
np.testing.assert_array_almost_equal(ema.values, sma.values)
|
np.testing.assert_array_almost_equal(ema.values, sma.values)
|
||||||
|
|
||||||
|
|
||||||
def test_james_stein():
|
|
||||||
df = get_data()
|
|
||||||
js = expected_returns.james_stein_shrinkage(df)
|
|
||||||
correct_mean = np.array(
|
|
||||||
[
|
|
||||||
0.25870218,
|
|
||||||
0.32318595,
|
|
||||||
0.29184719,
|
|
||||||
0.23082673,
|
|
||||||
0.41448111,
|
|
||||||
0.19238474,
|
|
||||||
0.23175124,
|
|
||||||
0.17825652,
|
|
||||||
0.20656697,
|
|
||||||
0.13374178,
|
|
||||||
0.16512141,
|
|
||||||
0.25217574,
|
|
||||||
0.12991287,
|
|
||||||
0.18700597,
|
|
||||||
0.21668984,
|
|
||||||
0.3147078,
|
|
||||||
0.33948993,
|
|
||||||
0.24437593,
|
|
||||||
0.225335,
|
|
||||||
0.27014272,
|
|
||||||
]
|
|
||||||
)
|
|
||||||
np.testing.assert_array_almost_equal(js.values, correct_mean)
|
|
||||||
|
|
||||||
# Test shrinkage
|
|
||||||
y = expected_returns.returns_from_prices(df).mean(axis=0) * 252
|
|
||||||
nu = y.mean()
|
|
||||||
assert (((js <= nu) & (js >= y)) | ((js >= nu) & (js <= y))).all()
|
|
||||||
|
|
||||||
|
|
||||||
def test_capm_no_benchmark():
|
def test_capm_no_benchmark():
|
||||||
df = get_data()
|
df = get_data()
|
||||||
mu = expected_returns.capm_return(df)
|
mu = expected_returns.capm_return(df)
|
||||||
@@ -192,26 +148,26 @@ def test_capm_no_benchmark():
|
|||||||
assert mu.dtype == "float64"
|
assert mu.dtype == "float64"
|
||||||
correct_mu = np.array(
|
correct_mu = np.array(
|
||||||
[
|
[
|
||||||
0.21803135,
|
0.22148462799238577,
|
||||||
0.27902605,
|
0.2835429647498704,
|
||||||
0.14475533,
|
0.14693081977908462,
|
||||||
0.14668971,
|
0.1488989354304723,
|
||||||
0.40944875,
|
0.4162399750335195,
|
||||||
0.22361704,
|
0.22716772604184535,
|
||||||
0.39057166,
|
0.3970337136813829,
|
||||||
0.164807,
|
0.16733214988182069,
|
||||||
0.31280876,
|
0.31791477659742146,
|
||||||
0.17018046,
|
0.17279931642386534,
|
||||||
0.15044284,
|
0.15271750464365566,
|
||||||
0.34609161,
|
0.351778014382922,
|
||||||
0.3233097,
|
0.32859883451716376,
|
||||||
0.1479624,
|
0.1501938182844417,
|
||||||
0.26403991,
|
0.268295486802897,
|
||||||
0.31124465,
|
0.31632339201710874,
|
||||||
0.27312086,
|
0.27753479916328516,
|
||||||
0.16703193,
|
0.16959588523287855,
|
||||||
0.30396023,
|
0.3089119447773357,
|
||||||
0.25182927,
|
0.2558719211959501,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
np.testing.assert_array_almost_equal(mu.values, correct_mu)
|
np.testing.assert_array_almost_equal(mu.values, correct_mu)
|
||||||
@@ -228,26 +184,26 @@ def test_capm_with_benchmark():
|
|||||||
assert mu.dtype == "float64"
|
assert mu.dtype == "float64"
|
||||||
correct_mu = np.array(
|
correct_mu = np.array(
|
||||||
[
|
[
|
||||||
0.10903299,
|
0.09115799375654746,
|
||||||
0.11891232,
|
0.09905386632033128,
|
||||||
0.0659977,
|
0.05676282405265752,
|
||||||
0.07369941,
|
0.06291827346436336,
|
||||||
0.15948144,
|
0.13147799781014877,
|
||||||
0.12308759,
|
0.10239088012000815,
|
||||||
0.15907944,
|
0.1311567086884512,
|
||||||
0.08680978,
|
0.07339649698626659,
|
||||||
0.15778843,
|
0.1301248935078549,
|
||||||
0.0903294,
|
0.07620949056643983,
|
||||||
0.09043133,
|
0.07629095442513395,
|
||||||
0.14716681,
|
0.12163575425541985,
|
||||||
0.12510181,
|
0.10400070536161658,
|
||||||
0.0927869,
|
0.0781736030988492,
|
||||||
0.10990104,
|
0.09185177050469516,
|
||||||
0.12317033,
|
0.10245700691271296,
|
||||||
0.13596521,
|
0.11268307946677197,
|
||||||
0.09344662,
|
0.07870087187919145,
|
||||||
0.15457909,
|
0.1275598841214107,
|
||||||
0.11430041,
|
0.09536788741392595,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
np.testing.assert_array_almost_equal(mu.values, correct_mu)
|
np.testing.assert_array_almost_equal(mu.values, correct_mu)
|
||||||
@@ -257,12 +213,7 @@ def test_risk_matrix_and_returns_data():
|
|||||||
# Test the switcher method for simple calls
|
# Test the switcher method for simple calls
|
||||||
df = get_data()
|
df = get_data()
|
||||||
|
|
||||||
for method in {
|
for method in {"mean_historical_return", "ema_historical_return", "capm_return"}:
|
||||||
"mean_historical_return",
|
|
||||||
"ema_historical_return",
|
|
||||||
"james_stein_shrinkage",
|
|
||||||
"capm_return",
|
|
||||||
}:
|
|
||||||
mu = expected_returns.return_model(df, method=method)
|
mu = expected_returns.return_model(df, method=method)
|
||||||
|
|
||||||
assert isinstance(mu, pd.Series)
|
assert isinstance(mu, pd.Series)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ def test_hrp_portfolio():
|
|||||||
df = get_data()
|
df = get_data()
|
||||||
returns = df.pct_change().dropna(how="all")
|
returns = df.pct_change().dropna(how="all")
|
||||||
hrp = HRPOpt(returns)
|
hrp = HRPOpt(returns)
|
||||||
w = hrp.optimize()
|
w = hrp.optimize(linkage_method="single")
|
||||||
|
|
||||||
# uncomment this line if you want generating a new file
|
# uncomment this line if you want generating a new file
|
||||||
# pd.Series(w).to_csv(resource("weights_hrp.csv"))
|
# pd.Series(w).to_csv(resource("weights_hrp.csv"))
|
||||||
@@ -32,7 +32,7 @@ def test_portfolio_performance():
|
|||||||
hrp = HRPOpt(returns)
|
hrp = HRPOpt(returns)
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
hrp.portfolio_performance()
|
hrp.portfolio_performance()
|
||||||
hrp.optimize()
|
hrp.optimize(linkage_method="single")
|
||||||
np.testing.assert_allclose(
|
np.testing.assert_allclose(
|
||||||
hrp.portfolio_performance(),
|
hrp.portfolio_performance(),
|
||||||
(0.21353402380950973, 0.17844159743748936, 1.084579081272277),
|
(0.21353402380950973, 0.17844159743748936, 1.084579081272277),
|
||||||
@@ -43,7 +43,7 @@ def test_pass_cov_matrix():
|
|||||||
df = get_data()
|
df = get_data()
|
||||||
S = CovarianceShrinkage(df).ledoit_wolf()
|
S = CovarianceShrinkage(df).ledoit_wolf()
|
||||||
hrp = HRPOpt(cov_matrix=S)
|
hrp = HRPOpt(cov_matrix=S)
|
||||||
hrp.optimize()
|
hrp.optimize(linkage_method="single")
|
||||||
perf = hrp.portfolio_performance()
|
perf = hrp.portfolio_performance()
|
||||||
assert perf[0] is None and perf[2] is None
|
assert perf[0] is None and perf[2] is None
|
||||||
np.testing.assert_almost_equal(perf[1], 0.10002783894982334)
|
np.testing.assert_almost_equal(perf[1], 0.10002783894982334)
|
||||||
@@ -62,6 +62,6 @@ def test_quasi_dag():
|
|||||||
df = get_data()
|
df = get_data()
|
||||||
returns = df.pct_change().dropna(how="all")
|
returns = df.pct_change().dropna(how="all")
|
||||||
hrp = HRPOpt(returns)
|
hrp = HRPOpt(returns)
|
||||||
hrp.optimize()
|
hrp.optimize(linkage_method="single")
|
||||||
clusters = hrp.clusters
|
clusters = hrp.clusters
|
||||||
assert HRPOpt._get_quasi_diag(clusters)[:5] == [12, 6, 15, 14, 2]
|
assert HRPOpt._get_quasi_diag(clusters)[:5] == [12, 6, 15, 14, 2]
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ def test_ef_plot():
|
|||||||
cla = CLA(rets, S)
|
cla = CLA(rets, S)
|
||||||
|
|
||||||
ax = plotting.plot_efficient_frontier(cla, showfig=False)
|
ax = plotting.plot_efficient_frontier(cla, showfig=False)
|
||||||
assert len(ax.findobj()) == 137
|
assert len(ax.findobj()) == 143
|
||||||
ax = plotting.plot_efficient_frontier(cla, show_assets=False, showfig=False)
|
ax = plotting.plot_efficient_frontier(cla, show_assets=False, showfig=False)
|
||||||
assert len(ax.findobj()) == 149
|
assert len(ax.findobj()) == 161
|
||||||
|
|
||||||
|
|
||||||
def test_weight_plot():
|
def test_weight_plot():
|
||||||
|
|||||||
@@ -27,6 +27,11 @@ def test_sample_cov_dummy():
|
|||||||
pd.testing.assert_frame_equal(S, test_answer)
|
pd.testing.assert_frame_equal(S, test_answer)
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_positive_semidefinite():
|
||||||
|
a = np.zeros((100, 100))
|
||||||
|
assert risk_models._is_positive_semidefinite(a)
|
||||||
|
|
||||||
|
|
||||||
def test_sample_cov_real_data():
|
def test_sample_cov_real_data():
|
||||||
df = get_data()
|
df = get_data()
|
||||||
S = risk_models.sample_cov(df)
|
S = risk_models.sample_cov(df)
|
||||||
@@ -42,11 +47,9 @@ def test_sample_cov_type_warning():
|
|||||||
cov_from_df = risk_models.sample_cov(df)
|
cov_from_df = risk_models.sample_cov(df)
|
||||||
|
|
||||||
returns_as_array = np.array(df)
|
returns_as_array = np.array(df)
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(RuntimeWarning) as w:
|
||||||
cov_from_array = risk_models.sample_cov(returns_as_array)
|
cov_from_array = risk_models.sample_cov(returns_as_array)
|
||||||
|
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
assert issubclass(w[0].category, RuntimeWarning)
|
|
||||||
assert str(w[0].message) == "data is not in a dataframe"
|
assert str(w[0].message) == "data is not in a dataframe"
|
||||||
|
|
||||||
np.testing.assert_array_almost_equal(
|
np.testing.assert_array_almost_equal(
|
||||||
@@ -59,11 +62,10 @@ def test_sample_cov_npd():
|
|||||||
assert not risk_models._is_positive_semidefinite(S)
|
assert not risk_models._is_positive_semidefinite(S)
|
||||||
|
|
||||||
for method in {"spectral", "diag"}:
|
for method in {"spectral", "diag"}:
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(UserWarning) as w:
|
||||||
S2 = risk_models.fix_nonpositive_semidefinite(S, fix_method=method)
|
S2 = risk_models.fix_nonpositive_semidefinite(S, fix_method=method)
|
||||||
assert risk_models._is_positive_semidefinite(S2)
|
assert risk_models._is_positive_semidefinite(S2)
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
assert issubclass(w[0].category, UserWarning)
|
|
||||||
assert (
|
assert (
|
||||||
str(w[0].message)
|
str(w[0].message)
|
||||||
== "The covariance matrix is non positive semidefinite. Amending eigenvalues."
|
== "The covariance matrix is non positive semidefinite. Amending eigenvalues."
|
||||||
@@ -148,10 +150,9 @@ def test_cov_to_corr():
|
|||||||
test_corr = risk_models.cov_to_corr(rets.cov())
|
test_corr = risk_models.cov_to_corr(rets.cov())
|
||||||
pd.testing.assert_frame_equal(test_corr, rets.corr())
|
pd.testing.assert_frame_equal(test_corr, rets.corr())
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
with pytest.warns(RuntimeWarning) as w:
|
||||||
test_corr_numpy = risk_models.cov_to_corr(rets.cov().values)
|
test_corr_numpy = risk_models.cov_to_corr(rets.cov().values)
|
||||||
assert len(w) == 1
|
assert len(w) == 1
|
||||||
assert issubclass(w[0].category, RuntimeWarning)
|
|
||||||
assert str(w[0].message) == "cov_matrix is not a dataframe"
|
assert str(w[0].message) == "cov_matrix is not a dataframe"
|
||||||
np.testing.assert_array_almost_equal(test_corr_numpy, rets.corr().values)
|
np.testing.assert_array_almost_equal(test_corr_numpy, rets.corr().values)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user