mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-29 13:32:08 +00:00
Merge pull request #329 from jmaslek/add_port_opt
Portfolio optimization
This commit is contained in:
@@ -142,8 +142,13 @@
|
||||
* [ ] Add Brokers (td, webull, etc) (@jmaslek)
|
||||
* [ ] Refactoring
|
||||
* [ ] Summaries / tear sheets (@jmaslek)
|
||||
* [ ] Portfolio Optimization (@jmaslek)
|
||||
___
|
||||
##Portfolio Optimization
|
||||
* [x] Basic Optimization through PyPortFolioOpt(@jmaslek) - [PR #329](https://github.com/DidierRLopes/GamestonkTerminal/pull/329)
|
||||
|
||||
**NEXT**
|
||||
* [ ] Decide what features should be present
|
||||
* [ ] Allow for more custom optimization constrains
|
||||
---
|
||||
|
||||
## FRED
|
||||
|
||||
@@ -241,6 +241,21 @@ Command|Description|Brokers
|
||||
`hold` | view net holdings across all logins
|
||||
|
||||
|
||||
## Portfolio Optimization [»](portfolio_optimization/README.md)
|
||||
command|desciription
|
||||
------|------
|
||||
`equal_weight`|Returns equal weights for all selected stocks
|
||||
`mkt_cap`| Returns weights that are weighted by market cap
|
||||
`div_yield`|Returns weights that are weighted by dividend yield
|
||||
`max_sharpe`| Returns portfolio of Efficient Frontier with maximum sharpe ratio
|
||||
`min_vol`| Returns portfolio on Efficient Frontier with minimum volatility
|
||||
`eff_risk`| Returns portfolio on Efficient Frontier that maximizes returns at a given risk
|
||||
`eff_ret`| Returns portfolio on Efficient Frontier that minimizes risk at a given return level
|
||||
`show_eff`|Plots random portfolios and shows the Efficient Frontier
|
||||
|
||||
|
||||
|
||||
|
||||
## Cryptocurrency [»](cryptocurrency/README.md)
|
||||
|
||||
Command|Description
|
||||
|
||||
@@ -16,6 +16,7 @@ from gamestonk_terminal.comparison_analysis import yahoo_finance_api as yf_api
|
||||
from gamestonk_terminal.comparison_analysis import market_watch_api as mw_api
|
||||
from gamestonk_terminal.comparison_analysis import finbrain_api as f_api
|
||||
from gamestonk_terminal.comparison_analysis import finviz_compare_view
|
||||
from gamestonk_terminal.portfolio_optimization import po_controller
|
||||
from gamestonk_terminal.menu import session
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
@@ -43,6 +44,7 @@ class ComparisonAnalysisController:
|
||||
"ownership",
|
||||
"performance",
|
||||
"technical",
|
||||
"po",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -108,6 +110,8 @@ class ComparisonAnalysisController:
|
||||
print(" performance brief performance comparison")
|
||||
print(" technical brief technical comparison")
|
||||
print("")
|
||||
print(" > po portfolio optimization for selected tickers")
|
||||
print("")
|
||||
return
|
||||
|
||||
@staticmethod
|
||||
@@ -315,6 +319,10 @@ class ComparisonAnalysisController:
|
||||
"""Process technical command"""
|
||||
finviz_compare_view.screener(other_args, "technical", self.ticker, self.similar)
|
||||
|
||||
def call_po(self, _):
|
||||
"""Open Portfolio Optimization menu with ticker and similar"""
|
||||
return po_controller.menu([self.ticker] + self.similar)
|
||||
|
||||
|
||||
def menu(stock: pd.DataFrame, ticker: str, start: datetime, interval: str):
|
||||
"""Comparison Analysis Menu"""
|
||||
|
||||
@@ -60,6 +60,9 @@ def print_help(s_ticker, s_start, s_interval, b_is_market_open):
|
||||
)
|
||||
print(" pa portfolio analysis, \t\t supports: robinhood, alpaca, ally ")
|
||||
print(" crypto cryptocurrencies, \t\t uses coingecko api")
|
||||
print(
|
||||
" po portfolio optimization, \t\t optimal portfolio weights from pyportfolioopt"
|
||||
)
|
||||
|
||||
if s_ticker:
|
||||
print(
|
||||
|
||||
169
gamestonk_terminal/portfolio_optimization/README.md
Normal file
169
gamestonk_terminal/portfolio_optimization/README.md
Normal file
@@ -0,0 +1,169 @@
|
||||
# Portfolio Optimization
|
||||
|
||||
This menu aims to discover optimal portfolios for selected stocks.
|
||||
|
||||
Currently we support two methods:
|
||||
|
||||
* [Property Weighting](#weighting)
|
||||
* Equal Weights
|
||||
* Market Cap Weighting
|
||||
* Divident Yield Weighting
|
||||
* [Mean-Variance-Optimization](#eff_front)
|
||||
* Max sharpe ratio
|
||||
* Minimum volatility
|
||||
* Maximum returns at given risk level
|
||||
* Minimum risk level at a given return
|
||||
|
||||
## Procedure
|
||||
There are three ways to load stocks to be analyzed.
|
||||
* add
|
||||
* select
|
||||
* from ca menu
|
||||
###add
|
||||
Adds selected tickers to the menu to be considered
|
||||
|
||||
````
|
||||
usage: add ticker1,ticker2,ticker3,...
|
||||
````
|
||||
###select
|
||||
Clears current list and loads selected ticker
|
||||
````
|
||||
usage: select ticker1,ticker2,ticker3,...
|
||||
````
|
||||
|
||||
### ca menu
|
||||
From the ca menu, the loaded ticker and the selected similar tickers can be loaded by entering the `> po` menu.
|
||||
|
||||
From this `po` menu, you can return to `ca` using `ca`, which will keep your loaded stock, but reset the selected stocks from that menu.
|
||||
|
||||
Once your stocks are listed, you can select one of the options.
|
||||
## Property weighted <a name="weighting"></a>
|
||||
* equal weights
|
||||
* market cap weighted
|
||||
* dividend yield weighted
|
||||
### equal weights
|
||||
Returns an equally weighted portfolio where the weights are 1/number of stocks.
|
||||
````
|
||||
equal_weight [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
### market cap weighted
|
||||
Returns portfolio values that are weighted by their relative Market Cap.
|
||||
````
|
||||
mkt_cap [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
###dividend yield weighted
|
||||
Returns portfolio values that are weighted by relative Dividend Yield.
|
||||
````
|
||||
div_yield [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
## Mean Variance Optimization<a name="eff_front"></a>
|
||||
|
||||
These approaches are based off of the efficient frontier approach, which is meant to solve the following optimization problem.
|
||||
|
||||
<img src="https://latex.codecogs.com/svg.image?w^TS&space;w" title="w^TS w" />
|
||||
|
||||
With constraints:
|
||||
|
||||
<img src="https://latex.codecogs.com/svg.image?w^TR&space;>&space;R^*" title="w^TR > R^*" />
|
||||
|
||||
\
|
||||
<img src="https://latex.codecogs.com/svg.image?w_1+w_2+...w_n&space;=&space;1" title="w_1+w_2+...w_n = 1" />
|
||||
|
||||
Where S is the covariance matrix between stocks and R is the expected returns. The condition that all weights add up to 1
|
||||
just implies that you want to have a net long portfolio (with no margin).
|
||||
A long-short portfolio can have negative weights and usually wants to have everything add up to 0 for a market-neutral strategy.
|
||||
|
||||
Currently, we do not allow for changing risk models or adding constraints. If there is something specific, please submit a feature request, or if you can
|
||||
write it, feel free to add a PR!
|
||||
|
||||
All of our current implementations use the [PyPortFolioOpt](#https://pyportfolioopt.readthedocs.io/en/latest/index.html) package.
|
||||
|
||||
### max_sharpe
|
||||
The sharpe ratio is defined as
|
||||
|
||||
(Mean Returns - Risk Free Rate)/(Standard Deviation of Returns)
|
||||
|
||||
The usage is:
|
||||
````
|
||||
max_sharpe [-p PERIOD] [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
### min_vol
|
||||
This portfolio minimizes the total volatility, which also means it has the smallest returns among the efficient frontier.
|
||||
The usage is:
|
||||
````
|
||||
min_vol [-p PERIOD] [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
### eff_risk
|
||||
This portfolio maximizes the returns at a given risk tolerance
|
||||
The usage is:
|
||||
````
|
||||
eff_risk [-p PERIOD] [-r --risk RISK_LEVEL] [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -r/--risk Risk tolerance. Default is 0.1 (10%)
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
### eff_ret
|
||||
This portfolio minimizes the risk at a given return level
|
||||
The usage is:
|
||||
````
|
||||
eff_ret [-p PERIOD] [-r --return] [-v --value VALUE] [--pie]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -r/--return. Desired return. Default is 0.1 (10%)
|
||||
* -v/--value If provided, this represents an actual allocation amount for the portfolio. Defaults to 1, which just returns the weights.
|
||||
* --pie Flag that displays a pie chart of the allocations.
|
||||
|
||||
### show_eff
|
||||
This function plots random portfolios based on their risk and returns and shows the efficient frontier.
|
||||
The usage is:
|
||||
````
|
||||
show_eff [-p PERIOD] [-n N_PORTFOLIOS]
|
||||
````
|
||||
* -p/--period Amount of time to retrieve data from yfinance. Options are: 1d,5d,1mo,3mo,6mo,1y,2y,5y,10y,ytd,max and it defaults to 3mo.
|
||||
* -n Number of portfolios to simulate.
|
||||
|
||||
##Sample Usage
|
||||
In this example, we generate weights for a list of 6 stocks using the eff_ret command. This optimization looks to maximize returns
|
||||
at a given risk level. We start by adding the stocks we want to analyze:
|
||||
````
|
||||
select aapl,amzn,msft,f,gm,ge
|
||||
````
|
||||
Which shows:
|
||||
````
|
||||
Current Tickers: GE, GM, AMZN, AAPL, F, MSFT
|
||||
````
|
||||
To perform the optimization, we will set a target return of 25% (.25). Given how stocks performed during COVID,
|
||||
there is a lot of volatility, so many optimizations may not get low volatility. The module also returns annualized volatility,
|
||||
so the number is your portfolio volatility * `sqrt(252`. This optimization will be (including a pie chart!). Note we could also supply a different
|
||||
time period, which changes the expected returns and historical volatility, which changes the optimization. We could also specify a dollar
|
||||
amount that you wish to allocate using the `-v` flag.
|
||||
````
|
||||
eff_ret -r .25 --pie
|
||||
````
|
||||
The console will show (numbers will vary based on when this is done).
|
||||

|
||||
|
||||
And the pie chart:
|
||||
|
||||

|
||||
|
||||
Note that since `AAPL` had zero allocation, it was omitted from the chart.
|
||||
226
gamestonk_terminal/portfolio_optimization/optimizer_helper.py
Normal file
226
gamestonk_terminal/portfolio_optimization/optimizer_helper.py
Normal file
@@ -0,0 +1,226 @@
|
||||
""" Portfolio Optimization Helper Functions"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import math
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import yfinance as yf
|
||||
from pypfopt.efficient_frontier import EfficientFrontier
|
||||
from pypfopt import risk_models
|
||||
from pypfopt import expected_returns
|
||||
from gamestonk_terminal.config_plot import PLOT_DPI
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import plot_autoscale, parse_known_args_and_warn
|
||||
|
||||
title_maps = {
|
||||
"max_sharpe": "Maximum Sharpe Portfolio",
|
||||
"min_volatility": "Minimum Volatility Portfolio",
|
||||
"eff_risk": "Maximum Return Portfolio at Risk = {:1f} %",
|
||||
"eff_ret": "Minimum Volatility Portfolio at Target Return = {:.1f} %",
|
||||
"equal": "Equally Weighted Portfolio",
|
||||
"marketCap": "MarketCap Weighted Portfolio",
|
||||
"dividendYield": "Dividend Yield Weighted Portfolio",
|
||||
}
|
||||
|
||||
|
||||
def process_stocks(list_of_stocks: List[str], period: str = "3mo") -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
list_of_stocks: List[str]
|
||||
List of tickers to get historical data for
|
||||
period: str
|
||||
Period to get data from yfinance
|
||||
|
||||
Returns
|
||||
-------
|
||||
stock_closes: DataFrame
|
||||
DataFrame containing daily (adjusted) close prices for each stock in list
|
||||
"""
|
||||
stock_prices = yf.download(
|
||||
list_of_stocks, period=period, progress=False, group_by="ticker"
|
||||
)
|
||||
stock_closes = pd.DataFrame(index=stock_prices.index)
|
||||
for stock in list_of_stocks:
|
||||
stock_closes[stock] = stock_prices[stock]["Adj Close"]
|
||||
return stock_closes
|
||||
|
||||
|
||||
def prepare_efficient_frontier(stock_prices: pd.DataFrame):
|
||||
"""
|
||||
Take in a dataframe of prices and return an efficient frontier object
|
||||
Parameters
|
||||
----------
|
||||
stock_prices : DataFrame
|
||||
DataFrame where indices are DateTime and columns are stocks
|
||||
|
||||
Returns
|
||||
-------
|
||||
ef: EfficientFrontier
|
||||
EfficientFrontier object
|
||||
"""
|
||||
mu = expected_returns.mean_historical_return(stock_prices)
|
||||
S = risk_models.sample_cov(stock_prices)
|
||||
ef = EfficientFrontier(mu, S)
|
||||
return ef
|
||||
|
||||
|
||||
def display_weights(weights: dict):
|
||||
"""
|
||||
Print weights in a nice format
|
||||
Parameters
|
||||
----------
|
||||
weights: dict
|
||||
weights to display. Keys are stocks. Values are either weights or values if -v specified
|
||||
"""
|
||||
if not weights:
|
||||
return
|
||||
weight_df = pd.DataFrame.from_dict(data=weights, orient="index", columns=["value"])
|
||||
if math.isclose(weight_df.sum()["value"], 1, rel_tol=0.1):
|
||||
weight_df["weight"] = (weight_df["value"] * 100).astype(str).apply(
|
||||
lambda s: s[:6]
|
||||
) + " %"
|
||||
print(pd.DataFrame(weight_df["weight"]))
|
||||
else:
|
||||
print(weight_df)
|
||||
|
||||
|
||||
def my_autopct(x):
|
||||
"""Function for autopct of plt.pie. This results in values not being printed in the pie if they are 'too small'"""
|
||||
if x > 4:
|
||||
return f"{x:.2f} %"
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def pie_chart_weights(weights: dict, optimizer: str, value: float):
|
||||
"""
|
||||
Show a pie chart of holdings
|
||||
Parameters
|
||||
----------
|
||||
weights: dict
|
||||
weights to display. Keys are stocks. Values are either weights or values if -v specified
|
||||
optimzer: str
|
||||
Optimization technique used for title
|
||||
"""
|
||||
plt.close("all")
|
||||
if not weights:
|
||||
return
|
||||
|
||||
init_stocks = list(weights.keys())
|
||||
init_sizes = list(weights.values())
|
||||
stocks = []
|
||||
sizes = []
|
||||
for stock, size in zip(init_stocks, init_sizes):
|
||||
if size > 0:
|
||||
stocks.append(stock)
|
||||
sizes.append(size)
|
||||
|
||||
total_size = np.sum(sizes)
|
||||
|
||||
leg_labels = [
|
||||
f"{str(a)}: {str(round(100*b/total_size,3))[:4]}%"
|
||||
for a, b in zip(stocks, sizes)
|
||||
]
|
||||
plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
if math.isclose(sum(sizes), 1, rel_tol=0.1):
|
||||
wedges, _, autotexts = plt.pie(
|
||||
sizes,
|
||||
labels=stocks,
|
||||
autopct=my_autopct,
|
||||
textprops=dict(color="k"),
|
||||
explode=[s / (5 * total_size) for s in sizes],
|
||||
normalize=True,
|
||||
shadow=True,
|
||||
)
|
||||
else:
|
||||
wedges, _, autotexts = plt.pie(
|
||||
sizes,
|
||||
labels=stocks,
|
||||
autopct="",
|
||||
textprops=dict(color="k"),
|
||||
explode=[s / (5 * total_size) for s in sizes],
|
||||
normalize=True,
|
||||
shadow=True,
|
||||
)
|
||||
for i, a in enumerate(autotexts):
|
||||
if sizes[i] / total_size > 0.05:
|
||||
a.set_text(f"{sizes[i]:.2f}")
|
||||
else:
|
||||
a.set_text("")
|
||||
|
||||
plt.axis("equal")
|
||||
|
||||
plt.legend(
|
||||
wedges,
|
||||
leg_labels,
|
||||
title="Stocks",
|
||||
loc="center left",
|
||||
bbox_to_anchor=(0.85, 0, 0.5, 1),
|
||||
)
|
||||
|
||||
plt.setp(autotexts, size=8, weight="bold")
|
||||
|
||||
if optimizer in ["eff_ret", "eff_risk"]:
|
||||
plt.title(title_maps[optimizer].format(100 * value))
|
||||
else:
|
||||
plt.title(title_maps[optimizer])
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
|
||||
def parse_from_port_type(
|
||||
parser_in: argparse.ArgumentParser, port_type: str, other_args: List[str]
|
||||
):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
parser_in: ArgumentParser
|
||||
parser to get data from
|
||||
port_type: str
|
||||
Type of optimization that will be done. One of ["max_sharpe","min_vol", "eff_risk", "eff_ret"]
|
||||
other_args: List[str]
|
||||
Arguments passed to function
|
||||
Returns
|
||||
-------
|
||||
|
||||
ns_parser:
|
||||
Parsed arguments
|
||||
"""
|
||||
|
||||
if port_type in ["max_sharpe", "min_volatility"]:
|
||||
ns_parser = parse_known_args_and_warn(parser_in, other_args)
|
||||
if not ns_parser:
|
||||
return None
|
||||
return ns_parser
|
||||
|
||||
elif port_type == "eff_risk":
|
||||
parser_in.add_argument(
|
||||
"-r", "--risk", type=float, dest="risk_level", default=0.1
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser_in, other_args)
|
||||
if not ns_parser:
|
||||
return None
|
||||
return ns_parser
|
||||
|
||||
elif port_type == "eff_ret":
|
||||
parser_in.add_argument(
|
||||
"-r", "--return", type=float, dest="target_return", default=0.1
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser_in, other_args)
|
||||
if not ns_parser:
|
||||
return None
|
||||
return ns_parser
|
||||
else:
|
||||
print("Incorrect portfolio optimizer type\n")
|
||||
return None
|
||||
337
gamestonk_terminal/portfolio_optimization/optimizer_view.py
Normal file
337
gamestonk_terminal/portfolio_optimization/optimizer_view.py
Normal file
@@ -0,0 +1,337 @@
|
||||
""" Portfolio Optimization Functions"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import yfinance as yf
|
||||
from pypfopt import plotting
|
||||
from pypfopt import risk_models
|
||||
from pypfopt import expected_returns
|
||||
from pypfopt import EfficientFrontier
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn, plot_autoscale
|
||||
from gamestonk_terminal.portfolio_optimization.optimizer_helper import (
|
||||
process_stocks,
|
||||
prepare_efficient_frontier,
|
||||
pie_chart_weights,
|
||||
display_weights,
|
||||
parse_from_port_type,
|
||||
)
|
||||
from gamestonk_terminal.config_plot import PLOT_DPI
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
period_choices = [
|
||||
"1d",
|
||||
"5d",
|
||||
"1mo",
|
||||
"3mo",
|
||||
"6mo",
|
||||
"1y",
|
||||
"2y",
|
||||
"5y",
|
||||
"10y",
|
||||
"ytd",
|
||||
"max",
|
||||
]
|
||||
|
||||
|
||||
def equal_weight(stocks: List[str], other_args: List[str]):
|
||||
"""
|
||||
Equally weighted portfolio, where weight = 1/# of stocks
|
||||
|
||||
Parameters
|
||||
----------
|
||||
stocks: List[str]
|
||||
List of tickers to be included in optimization
|
||||
|
||||
Returns
|
||||
-------
|
||||
weights : dict
|
||||
Dictionary of weights where keys are the tickers
|
||||
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="equal_weight",
|
||||
description="Return equally weighted portfolio holdings",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--value",
|
||||
default=1,
|
||||
type=float,
|
||||
dest="value",
|
||||
help="Amount to allocate to portfolio",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pie",
|
||||
action="store_true",
|
||||
dest="pie",
|
||||
default=False,
|
||||
help="Display a pie chart for weights",
|
||||
)
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
values = {}
|
||||
n_stocks = len(stocks)
|
||||
for stock in stocks:
|
||||
values[stock] = ns_parser.value * round(1 / n_stocks, 5)
|
||||
if ns_parser.pie:
|
||||
pie_chart_weights(values, "equal", 0)
|
||||
if n_stocks >= 1:
|
||||
print("Equal Weight Portfolio: ")
|
||||
display_weights(values)
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("")
|
||||
|
||||
|
||||
def property_weighting(stocks: List[str], property_type: str, other_args: List[str]):
|
||||
"""
|
||||
Property weighted portfolio where each weight is the relative fraction.
|
||||
Parameters
|
||||
----------
|
||||
stocks: List[str]
|
||||
List of tickers to be included in optimization
|
||||
property_type: str
|
||||
Property to weight by. Can be anything in yfinance.Ticker().info. Examples:
|
||||
"marketCap", "dividendYield", etc
|
||||
|
||||
Returns
|
||||
-------
|
||||
weights: dict
|
||||
Dictionary of weights where keys are the tickers
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="market_cap_weighted",
|
||||
description="Return portfolio weights/values that are weighted by marketcap",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--value",
|
||||
default=1,
|
||||
type=float,
|
||||
dest="value",
|
||||
help="Amount to allocate to portfolio",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pie",
|
||||
action="store_true",
|
||||
dest="pie",
|
||||
default=False,
|
||||
help="Display a pie chart for weights",
|
||||
)
|
||||
weights = {}
|
||||
prop = {}
|
||||
prop_sum = 0
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
for stock in stocks:
|
||||
stock_prop = yf.Ticker(stock).info[property_type]
|
||||
if stock_prop is None:
|
||||
stock_prop = 0
|
||||
prop[stock] = stock_prop
|
||||
prop_sum += stock_prop
|
||||
for k, v in prop.items():
|
||||
weights[k] = round(v / prop_sum, 5) * ns_parser.value
|
||||
|
||||
if ns_parser.pie:
|
||||
pie_chart_weights(weights, property_type, 0)
|
||||
|
||||
if property_type == "marketCap":
|
||||
print("Market Cap Weighted Portfolio: ")
|
||||
|
||||
elif property_type == "dividendYield":
|
||||
print("Dividend Yield Weighted Portfolio: ")
|
||||
|
||||
if len(stocks) >= 1:
|
||||
display_weights(weights)
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("")
|
||||
return
|
||||
|
||||
|
||||
def ef_portfolio(stocks: List[str], port_type: str, other_args: List[str]):
|
||||
"""
|
||||
Return a portfolio based on condition in port_type Defaults to 3m of historical data
|
||||
Parameters
|
||||
----------
|
||||
stocks: List[str]
|
||||
List of the stocks to be included in the weights
|
||||
port_type: str
|
||||
Method to be used on ef object (example: max_sharpe, min_volatility)
|
||||
Returns
|
||||
-------
|
||||
weights: dict
|
||||
Dictionary of weights where keys are the tickers.
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(add_help=False, prog=port_type)
|
||||
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--period",
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--value",
|
||||
dest="value",
|
||||
help="Amount to allocate to portfolio",
|
||||
type=float,
|
||||
default=1.0,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--pie",
|
||||
action="store_true",
|
||||
dest="pie",
|
||||
default=False,
|
||||
help="Display a pie chart for weights",
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_from_port_type(parser, port_type, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if len(stocks) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
period = ns_parser.period
|
||||
stock_prices = process_stocks(stocks, period)
|
||||
ef = prepare_efficient_frontier(stock_prices)
|
||||
|
||||
if port_type == "max_sharpe":
|
||||
|
||||
ef_sharpe = dict(ef.max_sharpe())
|
||||
weights = {
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_sharpe.items()
|
||||
}
|
||||
val = 0
|
||||
print("Weights that maximize Sharpe Ratio:")
|
||||
|
||||
elif port_type == "min_volatility":
|
||||
|
||||
ef_min_vol = dict(ef.min_volatility())
|
||||
weights = {
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_min_vol.items()
|
||||
}
|
||||
val = 0
|
||||
print("Weights that minimize volatility")
|
||||
|
||||
elif port_type == "eff_risk":
|
||||
|
||||
ef_eff_risk = dict(ef.efficient_risk(ns_parser.risk_level))
|
||||
weights = {
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_eff_risk.items()
|
||||
}
|
||||
val = ns_parser.risk_level
|
||||
print(f"Weights for maximizing returns at risk = {100*val:.1f} %")
|
||||
|
||||
elif port_type == "eff_ret":
|
||||
|
||||
ef_eff_risk = dict(ef.efficient_return(ns_parser.target_return))
|
||||
weights = {
|
||||
key: ns_parser.value * round(value, 5)
|
||||
for key, value in ef_eff_risk.items()
|
||||
}
|
||||
val = ns_parser.target_return
|
||||
print(f"Weights for minimizing risk at target return = {100*val:.1f} %")
|
||||
|
||||
else:
|
||||
raise ValueError("EF Method not found")
|
||||
|
||||
if ns_parser.pie:
|
||||
pie_chart_weights(weights, port_type, val)
|
||||
|
||||
print("")
|
||||
ef.portfolio_performance(verbose=True)
|
||||
print("")
|
||||
display_weights(weights)
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("")
|
||||
return
|
||||
|
||||
|
||||
def show_ef(stocks: List[str], other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(add_help=False, prog="ef")
|
||||
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--period",
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n", default=300, dest="n_port", help="number of portfolios to simulate"
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if len(stocks) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
|
||||
stock_prices = process_stocks(stocks, ns_parser.period)
|
||||
mu = expected_returns.mean_historical_return(stock_prices)
|
||||
S = risk_models.sample_cov(stock_prices)
|
||||
ef = EfficientFrontier(mu, S)
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
|
||||
# Generate random portfolios
|
||||
n_samples = ns_parser.n_port
|
||||
w = np.random.dirichlet(np.ones(len(mu)), n_samples)
|
||||
rets = w.dot(mu)
|
||||
stds = np.sqrt(np.diag(w @ S @ w.T))
|
||||
sharpes = rets / stds
|
||||
ax.scatter(stds, rets, marker=".", c=sharpes, cmap="viridis_r")
|
||||
|
||||
plotting.plot_efficient_frontier(ef, ax=ax, show_assets=True)
|
||||
# Find the tangency portfolio
|
||||
ef.max_sharpe()
|
||||
ret_sharpe, std_sharpe, _ = ef.portfolio_performance()
|
||||
ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe")
|
||||
|
||||
ax.set_title("Efficient Frontier")
|
||||
ax.legend()
|
||||
plt.tight_layout()
|
||||
plt.grid(b=True, which="major", color="#666666", linestyle="-")
|
||||
plt.minorticks_on()
|
||||
plt.grid(b=True, which="minor", color="#999999", linestyle="-", alpha=0.2)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("")
|
||||
206
gamestonk_terminal/portfolio_optimization/po_controller.py
Normal file
206
gamestonk_terminal/portfolio_optimization/po_controller.py
Normal file
@@ -0,0 +1,206 @@
|
||||
"""Portfolio Optimization Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import matplotlib.pyplot as plt
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import get_flair, parse_known_args_and_warn
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.portfolio_optimization import optimizer_view as po_api
|
||||
|
||||
|
||||
class PortfolioOptimization:
|
||||
|
||||
CHOICES = [
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"select",
|
||||
"add",
|
||||
"equal_weight",
|
||||
"mkt_cap",
|
||||
"div_yield",
|
||||
"max_sharpe",
|
||||
"min_vol",
|
||||
"eff_risk",
|
||||
"eff_ret",
|
||||
"show_ef",
|
||||
"yolo",
|
||||
]
|
||||
|
||||
def __init__(self, tickers: List[str] = []):
|
||||
"""
|
||||
Construct Portfolio Optimization
|
||||
"""
|
||||
|
||||
self.po_parser = argparse.ArgumentParser(add_help=False, prog="po")
|
||||
self.po_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.tickers = list(set(tickers))
|
||||
# These will allow the ca menu to be re-access
|
||||
self.ca_ticker = None
|
||||
self.ca_similar = None
|
||||
|
||||
@staticmethod
|
||||
def print_help(tickers: List[str]):
|
||||
"""Print help"""
|
||||
print("\nPortfolio Optimization:")
|
||||
print(" help show this menu again")
|
||||
print(" q quit this menu, and shows back to main menu")
|
||||
print(" quit quit to abandon program")
|
||||
print(f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}")
|
||||
print("")
|
||||
print(" add add ticker to optimize")
|
||||
print(" select overwrite current tickers with new tickers")
|
||||
print("")
|
||||
print("Optimization:")
|
||||
print("")
|
||||
print(" equal_weight equally weighted portfolio")
|
||||
print(" mkt_cap marketcap weighted portfolio")
|
||||
print(" div_yield dividend weighted portfolio")
|
||||
print(" max_sharpe portfolio with maximum sharpe ratio")
|
||||
print(" min_vol portfolio with minimum volatility")
|
||||
print(" eff_risk portfolio that maximizes returns at given risk")
|
||||
print(" eff_ret portfolio that minimizes risk at given return")
|
||||
print(" show_ef show the efficient frontier")
|
||||
print("")
|
||||
plt.close("all")
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
"""
|
||||
(known_args, other_args) = self.po_parser.parse_known_args(an_input.split())
|
||||
|
||||
return getattr(
|
||||
self, "call_" + known_args.cmd, lambda: "Command not recognized!"
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help(self.tickers)
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
|
||||
def call_add(self, other_args: List[str]):
|
||||
self.add_stocks(self, other_args)
|
||||
|
||||
def call_select(self, other_args: List[str]):
|
||||
self.tickers = []
|
||||
self.add_stocks(self, other_args)
|
||||
|
||||
def call_equal_weight(self, other_args: List[str]):
|
||||
po_api.equal_weight(self.tickers, other_args)
|
||||
|
||||
def call_mkt_cap(self, other_args: List[str]):
|
||||
po_api.property_weighting(self.tickers, "marketCap", other_args)
|
||||
|
||||
def call_div_yield(self, other_args: List[str]):
|
||||
po_api.property_weighting(self.tickers, "dividendYield", other_args)
|
||||
|
||||
def call_max_sharpe(self, other_args: List[str]):
|
||||
po_api.ef_portfolio(self.tickers, "max_sharpe", other_args)
|
||||
|
||||
def call_min_vol(self, other_args: List[str]):
|
||||
po_api.ef_portfolio(self.tickers, "min_volatility", other_args)
|
||||
|
||||
def call_eff_risk(self, other_args: List[str]):
|
||||
po_api.ef_portfolio(self.tickers, "eff_risk", other_args)
|
||||
|
||||
def call_eff_ret(self, other_args: List[str]):
|
||||
po_api.ef_portfolio(self.tickers, "eff_ret", other_args)
|
||||
|
||||
def call_show_ef(self, other_args):
|
||||
po_api.show_ef(self.tickers, other_args)
|
||||
print("")
|
||||
|
||||
def call_yolo(self, _):
|
||||
# Easter egg :)
|
||||
print("DFV YOLO")
|
||||
print({"GME": 200})
|
||||
print("")
|
||||
|
||||
@staticmethod
|
||||
def add_stocks(self, other_args: List[str]):
|
||||
""" Add ticker to current list for optimization"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="add",
|
||||
description="""Add tickers for optimizing.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--tickers",
|
||||
dest="add_tickers",
|
||||
type=lambda s: [str(item).upper() for item in s.split(",")],
|
||||
default=[],
|
||||
help="add tickers to optimzation.",
|
||||
)
|
||||
try:
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
tickers = set(self.tickers)
|
||||
for ticker in ns_parser.add_tickers:
|
||||
tickers.add(ticker)
|
||||
self.tickers = list(tickers)
|
||||
|
||||
print(
|
||||
f"\nCurrent Tickers: {('None', ', '.join(self.tickers))[bool(self.tickers)]}"
|
||||
)
|
||||
print("")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
print("")
|
||||
|
||||
|
||||
def menu(tickers: List[str]):
|
||||
"""Portfolio Optimization Menu"""
|
||||
if tickers == [""]:
|
||||
tickers = []
|
||||
plt.close("all")
|
||||
po_controller = PortfolioOptimization(tickers)
|
||||
po_controller.call_help(tickers)
|
||||
|
||||
while True:
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in po_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (po)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (po)> ")
|
||||
|
||||
try:
|
||||
process_input = po_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
@@ -23,7 +23,8 @@ This menu aims to filter stocks based on pre-specified preset filters, and the u
|
||||
* contains [Oversold (-s) signal example](#signal-oversold)
|
||||
* [signals](#signals)
|
||||
* view filter signals (e.g. -s top_gainers) [Finviz]
|
||||
|
||||
* [po](#port_opt)
|
||||
* go to the portfolio optimization menu
|
||||
|
||||
## view <a name="view"></a>
|
||||
|
||||
@@ -180,3 +181,13 @@ Prints list of available signals. [Source: Finviz]
|
||||
|
||||
<img width="937" alt="Captura de ecrã 2021-04-05, às 20 25 13" src="https://user-images.githubusercontent.com/25267873/113616495-0ece9580-964d-11eb-97af-4150f928a170.png">
|
||||
|
||||
## po <a name="port_opt"></a>
|
||||
Goes to the portfolio menu with list of passed stocks. In order to pass the stocks, just type the tickers of interest, no commas.
|
||||
|
||||
```
|
||||
usage: po ticker1 ticker2 ...
|
||||
```
|
||||
|
||||
````
|
||||
example: po aapl msft tsla gme
|
||||
````
|
||||
@@ -14,6 +14,7 @@ from gamestonk_terminal.helper_funcs import (
|
||||
)
|
||||
from gamestonk_terminal.screener import finviz_view
|
||||
from gamestonk_terminal.screener import yahoo_finance_view
|
||||
from gamestonk_terminal.portfolio_optimization import po_controller
|
||||
|
||||
|
||||
class ScreenerController:
|
||||
@@ -34,6 +35,7 @@ class ScreenerController:
|
||||
"performance",
|
||||
"technical",
|
||||
"signals",
|
||||
"po",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
@@ -69,6 +71,8 @@ class ScreenerController:
|
||||
print("")
|
||||
print(" signals view filter signals (e.g. -s top_gainers)")
|
||||
print("")
|
||||
print(" > po go to the portfolio optimization menu")
|
||||
print("")
|
||||
|
||||
@staticmethod
|
||||
def view_available_presets(other_args: List[str]):
|
||||
@@ -217,6 +221,10 @@ class ScreenerController:
|
||||
"""Process signals command"""
|
||||
finviz_view.view_signals(other_args)
|
||||
|
||||
def call_po(self, other_args: List[str]):
|
||||
"""Call the portfolio optimization menu"""
|
||||
return po_controller.menu(other_args)
|
||||
|
||||
|
||||
def menu():
|
||||
"""Screener Menu"""
|
||||
|
||||
173
poetry.lock
generated
173
poetry.lock
generated
@@ -379,6 +379,21 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
|
||||
[[package]]
|
||||
name = "cvxpy"
|
||||
version = "1.1.12"
|
||||
description = "A domain-specific language for modeling convex optimization problems in Python."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.5"
|
||||
|
||||
[package.dependencies]
|
||||
ecos = ">=2"
|
||||
numpy = ">=1.15"
|
||||
osqp = ">=0.4.1"
|
||||
scipy = ">=1.1.0"
|
||||
scs = ">=1.1.6"
|
||||
|
||||
[[package]]
|
||||
name = "cycler"
|
||||
version = "0.10.0"
|
||||
@@ -456,6 +471,18 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "ecos"
|
||||
version = "2.0.7.post1"
|
||||
description = "This is the Python package for ECOS: Embedded Cone Solver. See Github page for more information."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.6"
|
||||
scipy = ">=0.9"
|
||||
|
||||
[[package]]
|
||||
name = "entrypoints"
|
||||
version = "0.3"
|
||||
@@ -1491,6 +1518,19 @@ numpy = ">=1.7"
|
||||
docs = ["sphinx (==1.2.3)", "sphinxcontrib-napoleon", "sphinx-rtd-theme", "numpydoc"]
|
||||
tests = ["pytest", "pytest-cov", "pytest-pep8"]
|
||||
|
||||
[[package]]
|
||||
name = "osqp"
|
||||
version = "0.6.2.post0"
|
||||
description = "OSQP: The Operator Splitting QP Solver"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.7"
|
||||
qdldl = "*"
|
||||
scipy = ">=0.13.2"
|
||||
|
||||
[[package]]
|
||||
name = "overrides"
|
||||
version = "3.1.0"
|
||||
@@ -1930,6 +1970,23 @@ category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
[[package]]
|
||||
name = "pyportfolioopt"
|
||||
version = "1.4.1"
|
||||
description = "Financial portfolio optimization in python"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=3.6.1,<4.0.0"
|
||||
|
||||
[package.dependencies]
|
||||
cvxpy = ">=1.1.10,<2.0.0"
|
||||
numpy = ">=1.12,<2.0"
|
||||
pandas = ">=0.19"
|
||||
scipy = ">=1.3,<2.0"
|
||||
|
||||
[package.extras]
|
||||
optionals = ["cvxopt (>=1.2,!=1.2.5.post1,<2.0)", "scikit-learn (>=0.24.1,<0.25.0)", "matplotlib (>=3.2.0,<4.0.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pyrsistent"
|
||||
version = "0.14.11"
|
||||
@@ -2073,6 +2130,18 @@ python-versions = ">=3.6"
|
||||
cffi = {version = "*", markers = "implementation_name == \"pypy\""}
|
||||
py = {version = "*", markers = "implementation_name == \"pypy\""}
|
||||
|
||||
[[package]]
|
||||
name = "qdldl"
|
||||
version = "0.1.5.post0"
|
||||
description = "QDLDL, a free LDL factorization routine."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.7"
|
||||
scipy = ">=0.13.2"
|
||||
|
||||
[[package]]
|
||||
name = "qtconsole"
|
||||
version = "5.0.3"
|
||||
@@ -2265,6 +2334,18 @@ Cython = {version = "*", markers = "sys_platform == \"darwin\""}
|
||||
dataclasses = {version = "*", markers = "python_version < \"3.7\""}
|
||||
pyobjc-framework-Cocoa = {version = "*", markers = "sys_platform == \"darwin\""}
|
||||
|
||||
[[package]]
|
||||
name = "scs"
|
||||
version = "2.1.2"
|
||||
description = "scs: splitting conic solver"
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
numpy = ">=1.7"
|
||||
scipy = ">=0.13.2"
|
||||
|
||||
[[package]]
|
||||
name = "seaborn"
|
||||
version = "0.11.1"
|
||||
@@ -2837,7 +2918,7 @@ prediction = ["fbprophet", "tensorflow", "pmdarima", "transformers", "flair", "t
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.6.8"
|
||||
content-hash = "144771641ef753b1e937c0db16496c409905f6f692ef81a03d9ba716d9207bb0"
|
||||
content-hash = "5b3abe79834fc75092d1d9e8e3a809252f56eddc170fb61a93c36ffef9328b73"
|
||||
|
||||
[metadata.files]
|
||||
absl-py = [
|
||||
@@ -3063,6 +3144,20 @@ cssselect = [
|
||||
{file = "cssselect-1.1.0-py2.py3-none-any.whl", hash = "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf"},
|
||||
{file = "cssselect-1.1.0.tar.gz", hash = "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc"},
|
||||
]
|
||||
cvxpy = [
|
||||
{file = "cvxpy-1.1.12-cp36-cp36m-win_amd64.whl", hash = "sha256:86f8e4aa1a79482431b89aa526249d9fda04c74e04a3ec224d1175d281643d71"},
|
||||
{file = "cvxpy-1.1.12-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:90b09a7361b9c614166fa673e1fe9321317df6939135330e1621b9a0bf10e3a8"},
|
||||
{file = "cvxpy-1.1.12-cp37-cp37m-win_amd64.whl", hash = "sha256:084e5292f2a13133a1cd28d68f5af1df33a5119b0f1aec7cecf834eea3e2d11d"},
|
||||
{file = "cvxpy-1.1.12-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:575f6e1c70274008961edff97a7494701faed20b40fbd49327010c0e7d186256"},
|
||||
{file = "cvxpy-1.1.12-cp38-cp38-win_amd64.whl", hash = "sha256:76d04444cb50ad2840bbc72a625ac246b5eeeeb580ee20c71bec057822667738"},
|
||||
{file = "cvxpy-1.1.12-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2de5e0e75316839bc71b5df2c2db0131cee7ef06dac566c6c44dc852c091502b"},
|
||||
{file = "cvxpy-1.1.12-cp39-cp39-win_amd64.whl", hash = "sha256:9fa4d033ab31855aea090e4b358017b36880390a7d0e6e97ecc5226c77eab489"},
|
||||
{file = "cvxpy-1.1.12-py3.6-win-amd64.egg", hash = "sha256:d2c28e0eea1b6dc70262c9ed8b63c5c1747d85c28b3ec24cbaab07583b3ad729"},
|
||||
{file = "cvxpy-1.1.12-py3.7-win-amd64.egg", hash = "sha256:44d0e3d60693831ac70bdc9b2e23faf84401347bfcdef4eb5da5ae22ac57f5ff"},
|
||||
{file = "cvxpy-1.1.12-py3.8-win-amd64.egg", hash = "sha256:c86f8de9a63ccbd45844a4482df8099824688b5329060f089aad1c3beb939190"},
|
||||
{file = "cvxpy-1.1.12-py3.9-win-amd64.egg", hash = "sha256:08c720706abd94c25903a4db5dd2171232e72d0969b874a5de9a8181f96357d5"},
|
||||
{file = "cvxpy-1.1.12.tar.gz", hash = "sha256:b499ebfae4fc645e9523621573ffcb1c5b6854a1b5c0ce1d66a8a9a458605807"},
|
||||
]
|
||||
cycler = [
|
||||
{file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"},
|
||||
{file = "cycler-0.10.0.tar.gz", hash = "sha256:cd7b2d1018258d7247a71425e9f26463dfb444d411c39569972f4ce586b0c9d8"},
|
||||
@@ -3126,6 +3221,30 @@ detecta = [
|
||||
{file = "detecta-0.0.5-py3-none-any.whl", hash = "sha256:dbaf1938e5d785386c904034e2553824e2aa31793e967984ca65983aca06d23e"},
|
||||
{file = "detecta-0.0.5.tar.gz", hash = "sha256:d2ea7d13dfbbc994d6ce385a7f8dc0a85fe675a8a8e712a64ec56e54c40603ed"},
|
||||
]
|
||||
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-manylinux1_i686.whl", hash = "sha256:db7433051f6072d4821ebc582e9ff853d7d631ed98770550d248eae70b29dd26"},
|
||||
{file = "ecos-2.0.7.post1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:96ddc1c4e440820bb343c44785da480a64a9c468ec467b997e40f0c7d3236226"},
|
||||
{file = "ecos-2.0.7.post1-cp27-cp27m-win32.whl", hash = "sha256:574fa26661d192e48551a30e217296875020fdb7eebf9a072a14d68a0b9de03a"},
|
||||
{file = "ecos-2.0.7.post1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:7c871b7a49a5855e7df0d60a06d0e2148a4b183d612f7db5e155988629c3ec21"},
|
||||
{file = "ecos-2.0.7.post1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:845455f99cd579ee0cdfbcea675b4e4f7674b563e6a54a225977fc0819cda7e0"},
|
||||
{file = "ecos-2.0.7.post1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:e002df0f4b6777be68c73756e60f3cf76cb5f2f7d36c4f1a482c5538aac1a287"},
|
||||
{file = "ecos-2.0.7.post1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:feda86ddd191b1ae34d5ea615743894d1baa800f2dbb552cb7c7095a87037831"},
|
||||
{file = "ecos-2.0.7.post1-cp34-cp34m-win32.whl", hash = "sha256:6b0829b76ba49f6ebf8b9512673a0b702b756704927cbb106043e1b2273dadea"},
|
||||
{file = "ecos-2.0.7.post1-cp34-cp34m-win_amd64.whl", hash = "sha256:54f7c480029fbfa738ddb8e538388868b2a17c9189a426d924ca0c36e4cbbbd5"},
|
||||
{file = "ecos-2.0.7.post1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1f811e7244a58a7037474b3dead85c7ffd524cf631c90584cc544e438f114cf9"},
|
||||
{file = "ecos-2.0.7.post1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:72657189f71dbde01d1df841d2139e04da17071c8d3270919c8501f239d8c8e4"},
|
||||
{file = "ecos-2.0.7.post1-cp35-cp35m-win32.whl", hash = "sha256:30e7e9c5ad8a012ba1f69aa6827beb99f208323f678a82fcccc30e4ab8090aad"},
|
||||
{file = "ecos-2.0.7.post1-cp35-cp35m-win_amd64.whl", hash = "sha256:94dd0f82a18550232e4924e6c42730c46d7cdc03c4e2dc889e98ec97c0f24061"},
|
||||
{file = "ecos-2.0.7.post1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:361758a3568eb5a3a9b37c00a22b36f9548bbd0cd21f1da904a3627285bb4274"},
|
||||
{file = "ecos-2.0.7.post1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:831acb6bac205025ffe87002d8f425d2764a70db3d9d053a1f7e0e50bc2a18b9"},
|
||||
{file = "ecos-2.0.7.post1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:912b17f72476aff33b32e92decd7c02ddb929db28227563b3750948783cff6f4"},
|
||||
{file = "ecos-2.0.7.post1-cp36-cp36m-win32.whl", hash = "sha256:4fdfee011853cd07d494ef58a9299b1f0ccf0268c375109cb2b60727f746ea74"},
|
||||
{file = "ecos-2.0.7.post1-cp36-cp36m-win_amd64.whl", hash = "sha256:fb64fb29aef26474f807df4f0c198a6d192291edc9faa3bb05e3bc9b1e2b960a"},
|
||||
{file = "ecos-2.0.7.post1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:4b3068ef023c4f39f6c0d6fd73d27d8a8008f8b14e1f4ddd8d0f4876e841b986"},
|
||||
{file = "ecos-2.0.7.post1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4f775caf828597d094cddae54f7ed12b4a5aa760f535a92ccabec47741e4d61c"},
|
||||
{file = "ecos-2.0.7.post1.tar.gz", hash = "sha256:83e90f42b3f32e2a93f255c3cfad2da78dbd859119e93844c45d2fca20bdc758"},
|
||||
]
|
||||
entrypoints = [
|
||||
{file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"},
|
||||
{file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"},
|
||||
@@ -3810,6 +3929,29 @@ opt-einsum = [
|
||||
{file = "opt_einsum-3.3.0-py3-none-any.whl", hash = "sha256:2455e59e3947d3c275477df7f5205b30635e266fe6dc300e3d9f9646bfcea147"},
|
||||
{file = "opt_einsum-3.3.0.tar.gz", hash = "sha256:59f6475f77bbc37dcf7cd748519c0ec60722e91e63ca114e68821c0c54a46549"},
|
||||
]
|
||||
osqp = [
|
||||
{file = "osqp-0.6.2.post0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:21047e9ceb4c2224ba15dd95f5125e2dcabd178aa93d8efa407e67216ea4fe62"},
|
||||
{file = "osqp-0.6.2.post0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:598a7bb1534a0ab69bcaf22d6e6c4ec042a41184f007c01b7fa863b528c6b61d"},
|
||||
{file = "osqp-0.6.2.post0-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:6b745ba55add8be63eaee059858b7c0cf59925074fcc8355c17c3863fad01a95"},
|
||||
{file = "osqp-0.6.2.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:1104dd56d6bfe5ac5e5e076e1cf9e2be76932cadae4e0cfbdf495f3f43e4f8fa"},
|
||||
{file = "osqp-0.6.2.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6f89feaf7c30dfb5031b370e8c45aed23a60d6471606bad9b7d23506ddf807e6"},
|
||||
{file = "osqp-0.6.2.post0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:198b66cbe9326ef181f7598856aae20dcc5b7824b119cc119b412206e858bc28"},
|
||||
{file = "osqp-0.6.2.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:9aaecd3ed00a99d063e5bd45ebce965d1ebc8f4754d6c05ab5d31c3577a68513"},
|
||||
{file = "osqp-0.6.2.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:2d150d880dc5bee015aadac6cff32ab0c2721cd4bb8a3f5b1f0cec819fe70b39"},
|
||||
{file = "osqp-0.6.2.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:20cd6a352694cc182bb79a8e19e4e03b7970ecb4b95d6abf86e3c578a45783a9"},
|
||||
{file = "osqp-0.6.2.post0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:96e42524dd156f62ac8e35d71831588acb2919b5ba27e0e695c757539708dfa5"},
|
||||
{file = "osqp-0.6.2.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:d6dd2b70ffcee6089758518620087a5909a10621a59493be8ce0ef2a74502a27"},
|
||||
{file = "osqp-0.6.2.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:7c9f7e661fe2da984eab6533a56b5366f10a7360a28b8dd18c222fcf6655e247"},
|
||||
{file = "osqp-0.6.2.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b102c26b2017b039032797a388548dcfe11dbd3d06b3294721fa65976fe1065e"},
|
||||
{file = "osqp-0.6.2.post0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:a835746179c81944e51a6a2e0efb0aca66402be351af912bfd682527f15a35a7"},
|
||||
{file = "osqp-0.6.2.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:57f5026b91bb8f39059576c5ee50c0a1c2705d775a776e2e24be2aa47e63d8b0"},
|
||||
{file = "osqp-0.6.2.post0-cp38-cp38-win_amd64.whl", hash = "sha256:fb0d77e634362fe59ab2bedba3dde40d5357922e9719bcb4d16a99766cefd5a2"},
|
||||
{file = "osqp-0.6.2.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e21463d97bcab58350a41a975e9e5e4a685087b6357899a58813d2238795315"},
|
||||
{file = "osqp-0.6.2.post0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:02fdbabfffd72a8118328d117a83998800ca015182053fced9d5e8be6ffd702a"},
|
||||
{file = "osqp-0.6.2.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:b238b9049a29f6955f08567b285b0734dd578b529a3780d617854ffd91e5e4ed"},
|
||||
{file = "osqp-0.6.2.post0-cp39-cp39-win_amd64.whl", hash = "sha256:2c6cb14fd1365004d0e7a01f6101e16414a0348db736ee45aac8865fac165e57"},
|
||||
{file = "osqp-0.6.2.post0.tar.gz", hash = "sha256:5f0695f26a3bef0fae91254bc283fab790dcca0064bfe0f425167f9c9e8b4cbc"},
|
||||
]
|
||||
overrides = [
|
||||
{file = "overrides-3.1.0.tar.gz", hash = "sha256:30f761124579e59884b018758c4d7794914ef02a6c038621123fec49ea7599c6"},
|
||||
]
|
||||
@@ -4069,6 +4211,10 @@ pyparsing = [
|
||||
{file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
|
||||
{file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
|
||||
]
|
||||
pyportfolioopt = [
|
||||
{file = "PyPortfolioOpt-1.4.1-py3-none-any.whl", hash = "sha256:73f0b4ac2a5d879879a7a91af038b4d7ff82e9998fb2cad7c249302294e8c09e"},
|
||||
{file = "PyPortfolioOpt-1.4.1.tar.gz", hash = "sha256:e6c8e983db92cd13caf41d87fb4dcd945d37053f28b75dd19b5be699b6b1d19e"},
|
||||
]
|
||||
pyrsistent = [
|
||||
{file = "pyrsistent-0.14.11.tar.gz", hash = "sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"},
|
||||
]
|
||||
@@ -4212,6 +4358,24 @@ pyzmq = [
|
||||
{file = "pyzmq-22.0.3-pp37-pypy37_pp73-win32.whl", hash = "sha256:279cc9b51db48bec2db146f38e336049ac5a59e5f12fb3a8ad864e238c1c62e3"},
|
||||
{file = "pyzmq-22.0.3.tar.gz", hash = "sha256:f7f63ce127980d40f3e6a5fdb87abf17ce1a7c2bd8bf2c7560e1bbce8ab1f92d"},
|
||||
]
|
||||
qdldl = [
|
||||
{file = "qdldl-0.1.5.post0-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:787d59b4301608e96bdf32ab3a572d9f41b3ea08581774826720986e18da261e"},
|
||||
{file = "qdldl-0.1.5.post0-cp35-cp35m-manylinux2014_x86_64.whl", hash = "sha256:223dd49f4aa625c727c3053e9f62814fab2f929e0575b34dd1f38e66b536a849"},
|
||||
{file = "qdldl-0.1.5.post0-cp35-cp35m-win_amd64.whl", hash = "sha256:cd0090d5512a8e386534a755f9ebd8be82087d401746cebbfb9f9faa21073c39"},
|
||||
{file = "qdldl-0.1.5.post0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:3e5e2ee6b45dd655120ca8d6645331c800c494ce765bc0674714ba86e1ebec7d"},
|
||||
{file = "qdldl-0.1.5.post0-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:a2ab0d4618d5516101fc0ce99c873df61a085c3d7cb4d8347d5eca818795e48f"},
|
||||
{file = "qdldl-0.1.5.post0-cp36-cp36m-win_amd64.whl", hash = "sha256:ac7a2891628554de6260132fa090c5b00cf32ae083ece4991a43b887ec6f2114"},
|
||||
{file = "qdldl-0.1.5.post0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:817a226b2450e56c50f61a92b727c69f50a4d043876e5f1bcdff096c5a82bc37"},
|
||||
{file = "qdldl-0.1.5.post0-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:c10f91dda6a422a6d879668fd293132c8939431fffe64abd9700dce170a3aec4"},
|
||||
{file = "qdldl-0.1.5.post0-cp37-cp37m-win_amd64.whl", hash = "sha256:e70c57ea65d0bb09708bc9ecafd4fb4b797e64af4da475404e6bcf5a2cc92596"},
|
||||
{file = "qdldl-0.1.5.post0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:64f531ef2a12cc77be85045cdffdb2fe339244c73fc07aebe49977541bfc7e79"},
|
||||
{file = "qdldl-0.1.5.post0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:cff34b89258a3c9b1b398f1c8eeb480a9c4ae2ba14c4ffc8c78cdb123dab11c6"},
|
||||
{file = "qdldl-0.1.5.post0-cp38-cp38-win_amd64.whl", hash = "sha256:3a7ec584aaddff7036d22013c911132572f3d59c65ec6bf7e32beb01765accc4"},
|
||||
{file = "qdldl-0.1.5.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a7c086ae21a2b2e43b31ac6a5024c79dadf16e7c3152eda9e3263cefe1675e26"},
|
||||
{file = "qdldl-0.1.5.post0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:2c09f4b1a1c6f3a0579af004443417e084491e7c844ff9fb73170bb5d43f70b5"},
|
||||
{file = "qdldl-0.1.5.post0-cp39-cp39-win_amd64.whl", hash = "sha256:640cab781f87c2f1c04ada65615b3d1d136d319c4595176f37e2e3c99149108f"},
|
||||
{file = "qdldl-0.1.5.post0.tar.gz", hash = "sha256:c392c7427651d8b226423c7aba4a0f2338a1f38a4bbdabac6bc4afd8bc934f06"},
|
||||
]
|
||||
qtconsole = [
|
||||
{file = "qtconsole-5.0.3-py3-none-any.whl", hash = "sha256:4a38053993ca2da058f76f8d75b3d8906efbf9183de516f92f222ac8e37d9614"},
|
||||
{file = "qtconsole-5.0.3.tar.gz", hash = "sha256:c091a35607d2a2432e004c4a112d241ce908086570cf68594176dd52ccaa212d"},
|
||||
@@ -4359,7 +4523,6 @@ scikit-learn = [
|
||||
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c658432d8a20e95398f6bb95ff9731ce9dfa343fdf21eea7ec6a7edfacd4b4d9"},
|
||||
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9dfa564ef27e8e674aa1cc74378416d580ac4ede1136c13dd555a87996e13422"},
|
||||
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:9c6097b6a9b2bafc5e0f31f659e6ab5e131383209c30c9e978c5b8abdac5ed2a"},
|
||||
{file = "scikit_learn-0.24.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:5580eba7345a4d3b097be2f067cc71a306c44bab19e8717a30361f279c929bea"},
|
||||
{file = "scikit_learn-0.24.1-cp36-cp36m-win32.whl", hash = "sha256:7b04691eb2f41d2c68dbda8d1bd3cb4ef421bdc43aaa56aeb6c762224552dfb6"},
|
||||
{file = "scikit_learn-0.24.1-cp36-cp36m-win_amd64.whl", hash = "sha256:1adf483e91007a87171d7ce58c34b058eb5dab01b5fee6052f15841778a8ecd8"},
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:ddb52d088889f5596bc4d1de981f2eca106b58243b6679e4782f3ba5096fd645"},
|
||||
@@ -4367,7 +4530,6 @@ scikit-learn = [
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:0567a2d29ad08af98653300c623bd8477b448fe66ced7198bef4ed195925f082"},
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:99349d77f54e11f962d608d94dfda08f0c9e5720d97132233ebdf35be2858b2d"},
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:83b21ff053b1ff1c018a2d24db6dd3ea339b1acfbaa4d9c881731f43748d8b3b"},
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:abe835a851610f87201819cb315f8d554e1a3e8128912783a31e87264ba5ffb7"},
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-win32.whl", hash = "sha256:c3deb3b19dd9806acf00cf0d400e84562c227723013c33abefbbc3cf906596e9"},
|
||||
{file = "scikit_learn-0.24.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d54dbaadeb1425b7d6a66bf44bee2bb2b899fe3e8850b8e94cfb9c904dcb46d0"},
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:3c4f07f47c04e81b134424d53c3f5e16dfd7f494e44fd7584ba9ce9de2c5e6c1"},
|
||||
@@ -4375,7 +4537,6 @@ scikit-learn = [
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:4ddd2b6f7449a5d539ff754fa92d75da22de261fd8fdcfb3596799fadf255101"},
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:826b92bf45b8ad80444814e5f4ac032156dd481e48d7da33d611f8fe96d5f08b"},
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:259ec35201e82e2db1ae2496f229e63f46d7f1695ae68eef9350b00dc74ba52f"},
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:9599a3f3bf33f73fed0fe06d1dfa4e6081365a58c1c807acb07271be0dce9733"},
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-win32.whl", hash = "sha256:8772b99d683be8f67fcc04789032f1b949022a0e6880ee7b75a7ec97dbbb5d0b"},
|
||||
{file = "scikit_learn-0.24.1-cp38-cp38-win_amd64.whl", hash = "sha256:ed9d65594948678827f4ff0e7ae23344e2f2b4cabbca057ccaed3118fdc392ca"},
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:8aa1b3ac46b80eaa552b637eeadbbce3be5931e4b5002b964698e33a1b589e1e"},
|
||||
@@ -4383,7 +4544,6 @@ scikit-learn = [
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:087dfede39efb06ab30618f9ab55a0397f29c38d63cd0ab88d12b500b7d65fd7"},
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:895dbf2030aa7337649e36a83a007df3c9811396b4e2fa672a851160f36ce90c"},
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9a24d1ccec2a34d4cd3f2a1f86409f3f5954cc23d4d2270ba0d03cf018aa4780"},
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:54be0a60a5a35005ad69c75902e0f5c9f699db4547ead427e97ef881c3242e6f"},
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-win32.whl", hash = "sha256:fab31f48282ebf54dd69f6663cd2d9800096bad1bb67bbc9c9ac84eb77b41972"},
|
||||
{file = "scikit_learn-0.24.1-cp39-cp39-win_amd64.whl", hash = "sha256:4562dcf4793e61c5d0f89836d07bc37521c3a1889da8f651e2c326463c4bd697"},
|
||||
]
|
||||
@@ -4417,6 +4577,9 @@ scipy = [
|
||||
screeninfo = [
|
||||
{file = "screeninfo-0.6.7.tar.gz", hash = "sha256:1c4bac1ca329da3f68cbc4d2fbc92256aa9bb8ff8583ee3e14f91f0a7baa69cb"},
|
||||
]
|
||||
scs = [
|
||||
{file = "scs-2.1.2.tar.gz", hash = "sha256:667ed6019bb4e2f925bd9291161d2c61cc0077443094437b703ea905333fd585"},
|
||||
]
|
||||
seaborn = [
|
||||
{file = "seaborn-0.11.1-py3-none-any.whl", hash = "sha256:4e1cce9489449a1c6ff3c567f2113cdb41122f727e27a984950d004a88ef3c5c"},
|
||||
{file = "seaborn-0.11.1.tar.gz", hash = "sha256:44e78eaed937c5a87fc7a892c329a7cc091060b67ebd1d0d306b446a74ba01ad"},
|
||||
|
||||
@@ -54,6 +54,7 @@ detecta = "^0.0.5"
|
||||
tradingview-ta = "^3.2.3"
|
||||
finvizfinance = "^0.9.3"
|
||||
Pillow = "^8.2.0"
|
||||
pyportfolioopt = "^1.4.1"
|
||||
|
||||
[tool.poetry.dev-dependencies]
|
||||
pytest = "^6.2.2"
|
||||
|
||||
@@ -133,6 +133,7 @@ pycparser==2.20; python_version >= "3.5" and python_full_version < "3.0.0" or py
|
||||
pygments==2.8.1; python_version >= "3.6"
|
||||
pymeeus==0.5.9; python_version >= "3.5"
|
||||
pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6")
|
||||
pyportfolioopt==1.4.1
|
||||
pyrsistent==0.17.3; python_version >= "3.6"
|
||||
pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
|
||||
pystan==2.19.1.1
|
||||
|
||||
@@ -90,6 +90,7 @@ pyasn1==0.4.8; python_version >= "3.5" and python_full_version < "3.0.0" and pyt
|
||||
pycoingecko==1.4.0
|
||||
pymeeus==0.4.3; python_version >= "3.5"
|
||||
pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6"
|
||||
pyportfolioopt==1.4.1
|
||||
pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
|
||||
pystan==2.18
|
||||
python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0")
|
||||
|
||||
@@ -92,6 +92,7 @@ pyasn1==0.4.8; python_version >= "3.5" and python_full_version < "3.0.0" and pyt
|
||||
pycoingecko==1.4.0
|
||||
pymeeus==0.4.3; python_version >= "3.5"
|
||||
pyparsing==2.4.7; python_full_version >= "3.6.0" and python_version >= "3.6"
|
||||
pyportfolioopt==1.4.1
|
||||
pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
|
||||
pystan==2.19.1.1
|
||||
python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.5" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0") and (python_version >= "2.7" and python_full_version < "3.0.0" and python_version < "4" or python_version >= "2.7" and python_version < "4" and python_full_version >= "3.3.0")
|
||||
|
||||
@@ -92,6 +92,7 @@ pycparser==2.20; python_version >= "3.5" and python_full_version < "3.0.0" or py
|
||||
pygments==2.8.1; python_version >= "3.6"
|
||||
pymeeus==0.5.9; python_version >= "3.5"
|
||||
pyparsing==2.4.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
|
||||
pyportfolioopt==1.4.1
|
||||
pyrsistent==0.17.3; python_version >= "3.6"
|
||||
pysocks==1.7.1; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0"
|
||||
python-dateutil==2.8.1; python_full_version >= "3.6.1" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6") and (python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0")
|
||||
|
||||
@@ -31,6 +31,7 @@ from gamestonk_terminal.residuals_analysis import ra_controller
|
||||
from gamestonk_terminal.portfolio import port_controller
|
||||
from gamestonk_terminal.cryptocurrency import crypto_controller
|
||||
from gamestonk_terminal.screener import screener_controller
|
||||
from gamestonk_terminal.portfolio_optimization import po_controller
|
||||
|
||||
# import warnings
|
||||
# warnings.simplefilter("always")
|
||||
@@ -88,7 +89,9 @@ def main():
|
||||
"pa",
|
||||
"crypto",
|
||||
"ra",
|
||||
"po",
|
||||
]
|
||||
|
||||
menu_parser.add_argument("opt", choices=choices)
|
||||
completer = NestedCompleter.from_nested_dict({c: None for c in choices})
|
||||
|
||||
@@ -242,6 +245,9 @@ def main():
|
||||
elif ns_known_args.opt == "crypto":
|
||||
b_quit = crypto_controller.menu()
|
||||
|
||||
elif ns_known_args.opt == "po":
|
||||
b_quit = po_controller.menu([s_ticker])
|
||||
|
||||
elif ns_known_args.opt == "pred":
|
||||
|
||||
if not gtff.ENABLE_PREDICT:
|
||||
|
||||
Reference in New Issue
Block a user