Overhaul Portfolio class (#2021)

* adds pythonic portfolio class

* start calculate trades refactoring

* adds comments to portfolio model - delete afterwards

* finish calculate trades refactoring

* restore original portfolio_model.py

* implement calculate_allocations

* adapt and test controller load, show, bench, alloc and perf

* add old code that was ok

* adapt controller

* adapt portfolio_view

* run black on pythonic_portfolio.py

* fix crypto bug

* change column name in example datasets

* substitute portfolio_model.py

* update show command

* push cumulative returns calculation to model

* fix last change in cumulative returns

* add comments on possibly unused code

* run black on changes

* bring metrics from helper to model

* push rolling metrics from view to model

* Details and linting

* Fix tests

* remove empty attribute and rename class

* fix view and controller rf

* change returns calculation method

* remove CASH from code

* remove cash from tickers_list

* run black on changes

* change function name

* adapt to PortfolioModel

* fix tests

* fix tests on help

* fix linting

* call metrics from PortfolioModel

* call drawdown from model

* fix some mypy issues

* fix remaining mypy issues

* fix test

* Fix linting

* Remove unused function

* Small fixes

* Remove old code and adjust summary to simply work

* Update the Excel since CASH is no longer a thing

* Fix tests

* Update the csvs

* Updates to usage of full_shares and more details

* Fix -t flag for perf

Co-authored-by: Jeroen Bouma <jer.bouma@gmail.com>
This commit is contained in:
montezdesousa
2022-07-08 16:59:09 +01:00
committed by GitHub
parent b229ed7570
commit 2c3e10a128
20 changed files with 16075 additions and 16425 deletions

View File

@@ -30,7 +30,7 @@ def obtain_assets_allocation(benchmark_info: Dict, portfolio_trades: pd.DataFram
portfolio_assets_allocation = (
(
portfolio_trades[portfolio_trades["Type"] != "CASH"]
.groupby(by="Name")
.groupby(by="Ticker")
.agg({"Portfolio Value": "sum"})
.div(portfolio_trades["Portfolio Value"].sum())
)

View File

@@ -17,8 +17,8 @@ from openbb_terminal.helper_funcs import (
EXPORT_ONLY_FIGURES_ALLOWED,
EXPORT_ONLY_RAW_DATA_ALLOWED,
check_positive_float,
print_rich_table,
)
from openbb_terminal.menu import session
from openbb_terminal.parent_classes import BaseController
from openbb_terminal.portfolio import portfolio_model
@@ -28,7 +28,7 @@ from openbb_terminal.portfolio.portfolio_optimization import po_controller
from openbb_terminal.rich_config import console, MenuText
from openbb_terminal.common.quantitative_analysis import qa_view
# pylint: disable=R1710,E1101,C0415,W0212,too-many-function-args,C0302
# pylint: disable=R1710,E1101,C0415,W0212,too-many-function-args,C0302,too-many-instance-attributes
logger = logging.getLogger(__name__)
@@ -108,7 +108,7 @@ class PortfolioController(BaseController):
if filepath.is_file()
}
self.portfolio = pd.DataFrame(
self.portfolio_df = pd.DataFrame(
columns=[
"Date",
"Name",
@@ -126,11 +126,14 @@ class PortfolioController(BaseController):
]
)
self.portfolio_name = ""
self.benchmark_name = ""
self.portfolio_name: str = ""
self.benchmark_name: str = ""
self.original_benchmark_name = ""
self.risk_free_rate = 0
self.portlist: List[str] = os.listdir(self.DEFAULT_HOLDINGS_PATH)
self.portfolio = portfolio_model.Portfolio()
self.portfolio: portfolio_model.PortfolioModel = (
portfolio_model.PortfolioModel()
)
if session and obbff.USE_PROMPT_TOOLKIT:
choices: dict = {c: {} for c in self.controller_choices}
@@ -189,8 +192,6 @@ class PortfolioController(BaseController):
mt.add_cmd("es", self.portfolio_name and self.benchmark_name)
mt.add_cmd("os", self.portfolio_name and self.benchmark_name)
console.print(text=mt.menu_text, menu="Portfolio")
port = bool(self.portfolio_name)
port_bench = bool(self.portfolio_name) and bool(self.benchmark_name)
@@ -201,7 +202,7 @@ class PortfolioController(BaseController):
load load data into the portfolio[/cmds]
[param]Loaded orderbook:[/param] {self.portfolio_name or ""}
[param]Risk Free Rate: [/param] {self.portfolio.rf:.2%}
[param]Risk Free Rate: [/param] {self.risk_free_rate:.2%}
{("[unvl]", "[cmds]")[port]}
show show existing transactions{("[/unvl]", "[/cmds]")[port]}
{("[unvl]", "[cmds]")[port]}
@@ -261,14 +262,10 @@ class PortfolioController(BaseController):
@log_start_end(log=logger)
def call_po(self, _):
"""Process po command"""
if self.portfolio.empty:
if self.portfolio is None:
tickers = []
else:
tickers = (
self.portfolio._stock_tickers
+ self.portfolio._etf_tickers
+ self.portfolio._crypto_tickers
)
tickers = self.portfolio.tickers_list
self.queue = self.load_class(
po_controller.PortfolioOptimizationController,
tickers,
@@ -321,10 +318,10 @@ class PortfolioController(BaseController):
else:
file_location = ns_parser.file # type: ignore
if str(file_location).endswith(".csv"):
self.portfolio = portfolio_model.Portfolio.from_csv(file_location)
elif str(file_location).endswith(".xlsx"):
self.portfolio = portfolio_model.Portfolio.from_xlsx(file_location)
orderbook = portfolio_model.PortfolioModel.read_orderbook(
str(file_location)
)
self.portfolio = portfolio_model.PortfolioModel(orderbook)
if ns_parser.name:
self.portfolio_name = ns_parser.name
@@ -332,25 +329,22 @@ class PortfolioController(BaseController):
self.portfolio_name = ns_parser.file
# Generate holdings from trades
self.portfolio.generate_holdings_from_trades()
self.portfolio.generate_portfolio_data()
# Add in the Risk-free rate
self.portfolio.add_rf(ns_parser.risk_free_rate)
self.portfolio.set_risk_free_rate(ns_parser.risk_free_rate)
console.print(f"\n[bold]Portfolio:[/bold] {self.portfolio_name}")
console.print(f"[bold]Risk Free Rate:[/bold] {self.portfolio.rf}")
console.print(
f"[bold]Risk Free Rate:[/bold] {self.portfolio.risk_free_rate}"
)
console.print()
@log_start_end(log=logger)
def call_show(self, _):
"""Process show command"""
if self.portfolio.empty:
logger.warning("No portfolio loaded")
console.print("[red]No portfolio loaded.[/red]\n")
return
print_rich_table(self.portfolio.trades, show_index=False)
portfolio_view.display_orderbook(self.portfolio, show_index=False)
@log_start_end(log=logger)
def call_bench(self, other_args: List[str]):
@@ -371,13 +365,21 @@ class PortfolioController(BaseController):
required="-h" not in other_args,
help="Set the benchmark for the portfolio. By default, this is SPDR S&P 500 ETF Trust (SPY).",
)
parser.add_argument(
"-s",
"--full_shares",
action="store_true",
default=False,
dest="full_shares",
help="Whether to only make a trade with the benchmark when a full share can be bought (no partial shares).",
)
if other_args and "-" not in other_args[0][0]:
other_args.insert(0, "-b")
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
# needs to be checked since we want to use the start date of the portfolio when comparing with benchmark
if ns_parser and self.portfolio is not None:
# Needs to be checked since we want to use the start date of the portfolio when comparing with benchmark
if self.portfolio_name:
chosen_benchmark = " ".join(ns_parser.benchmark)
@@ -387,17 +389,24 @@ class PortfolioController(BaseController):
else:
benchmark_ticker = chosen_benchmark
self.portfolio.add_benchmark(benchmark_ticker)
self.portfolio.load_benchmark(benchmark_ticker, ns_parser.full_shares)
self.benchmark_name = self.portfolio.benchmark_info["longName"]
self.benchmark_name = chosen_benchmark
# Make it so that there is no chance of there being a difference in length between
# the portfolio and benchmark return DataFrames
(
self.portfolio.returns,
self.portfolio.benchmark_returns,
) = portfolio_helper.make_equal_length(
self.portfolio.returns, self.portfolio.benchmark_returns
)
console.print(
f"[bold]\nBenchmark:[/bold] {self.benchmark_name} ({benchmark_ticker})"
)
else:
console.print(
"[red]Please first define the portfolio using 'load'[/red]\n"
)
console.print("[red]Please first load orderbook using 'load'[/red]\n")
console.print()
@log_start_end(log=logger)
@@ -433,7 +442,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(parser, other_args, limit=10)
if ns_parser:
if ns_parser and self.portfolio is not None:
console.print()
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
@@ -489,14 +498,6 @@ class PortfolioController(BaseController):
Shows performance of each trade and total performance of the portfolio versus the benchmark.
""",
)
parser.add_argument(
"-s",
"--full_shares",
action="store_true",
default=False,
dest="full_shares",
help="Whether to only make a trade with the benchmark when a full share can be bought (no partial shares).",
)
parser.add_argument(
"-t",
"--show_trades",
@@ -519,13 +520,10 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
self.portfolio.mimic_portfolio_trades_for_benchmark(
full_shares=ns_parser.full_shares
)
portfolio_view.display_performance_vs_benchmark(
self.portfolio.portfolio_trades,
@@ -659,7 +657,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if ns_parser and self.portfolio is not None:
if self.portfolio_name:
if ns_parser.adjusted and ns_parser.student_t:
console.print(
@@ -723,7 +721,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if ns_parser and self.portfolio is not None:
if self.portfolio_name:
qa_view.display_es(
self.portfolio.returns,
@@ -772,7 +770,7 @@ class PortfolioController(BaseController):
""",
)
ns_parser = self.parse_known_args_and_warn(parser, other_args)
if ns_parser:
if ns_parser and self.portfolio is not None:
if self.portfolio_name:
data = self.portfolio.returns[1:]
qa_view.display_omega(
@@ -813,7 +811,7 @@ class PortfolioController(BaseController):
export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES,
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -853,7 +851,7 @@ class PortfolioController(BaseController):
export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES,
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -900,7 +898,7 @@ class PortfolioController(BaseController):
export_allowed=EXPORT_ONLY_FIGURES_ALLOWED,
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -941,7 +939,7 @@ class PortfolioController(BaseController):
export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES,
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -966,7 +964,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -995,7 +993,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -1029,7 +1027,7 @@ class PortfolioController(BaseController):
"--rfr",
type=check_positive_float,
dest="risk_free_rate",
default=self.portfolio.rf,
default=self.risk_free_rate,
help="Set risk free rate for calculations.",
)
if other_args and "-" not in other_args[0][0]:
@@ -1037,7 +1035,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -1072,7 +1070,7 @@ class PortfolioController(BaseController):
"--rfr",
type=check_positive_float,
dest="risk_free_rate",
default=self.portfolio.rf,
default=self.risk_free_rate,
help="Set risk free rate for calculations.",
)
if other_args and "-" not in other_args[0][0]:
@@ -1080,7 +1078,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -1115,7 +1113,7 @@ class PortfolioController(BaseController):
ns_parser = self.parse_known_args_and_warn(
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -1149,7 +1147,7 @@ class PortfolioController(BaseController):
"--rfr",
type=check_positive_float,
dest="risk_free_rate",
default=self.portfolio.rf,
default=self.risk_free_rate,
help="Set risk free rate for calculations.",
)
if other_args and "-" not in other_args[0][0]:
@@ -1211,11 +1209,11 @@ class PortfolioController(BaseController):
portfolio_view.display_kelly_criterion(
self.portfolio, ns_parser.export
)
elif ns_parser.metric == "payoff":
elif ns_parser.metric == "payoff" and self.portfolio is not None:
portfolio_view.display_payoff_ratio(
self.portfolio, ns_parser.export
)
elif ns_parser.metric == "profitfactor":
elif ns_parser.metric == "profitfactor" and self.portfolio is not None:
portfolio_view.display_profit_factor(
self.portfolio, ns_parser.export
)
@@ -1247,7 +1245,7 @@ class PortfolioController(BaseController):
raw=True,
export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES,
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):
@@ -1282,7 +1280,7 @@ class PortfolioController(BaseController):
"--rfr",
type=check_positive_float,
dest="risk_free_rate",
default=self.portfolio.rf,
default=self.risk_free_rate,
help="Set risk free rate for calculations.",
)
if other_args and "-" not in other_args[0][0]:
@@ -1293,7 +1291,7 @@ class PortfolioController(BaseController):
other_args,
export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED,
)
if ns_parser:
if ns_parser and self.portfolio is not None:
if check_portfolio_benchmark_defined(
self.portfolio_name, self.benchmark_name
):

View File

@@ -152,6 +152,7 @@ def is_ticker(ticker: str) -> bool:
return "previousClose" in item.info
# TODO: Is this being used anywhere?
def beta_word(beta: float) -> str:
"""Describe a beta
@@ -244,61 +245,25 @@ def filter_df_by_period(df: pd.DataFrame, period: str = "all") -> pd.DataFrame:
return df
def sharpe_ratio(return_series: pd.Series, risk_free_rate: float) -> float:
"""Get sharpe ratio
def make_equal_length(df1: pd.DataFrame, df2: pd.DataFrame):
"""Filter dataframe by selected period
Parameters
----------
return_series : pd.Series
Returns of the portfolio
risk_free_rate: float
Value to use for risk free rate
Parameters
----------
df1: pd.DataFrame
The first DataFrame that needs to be compared.
df2: pd.DataFrame
The second DataFrame that needs to be compared.
Returns
-------
float
Sharpe ratio
Returns
----------
df1 and df2
Both DataFrames returned
"""
mean = return_series.mean() - risk_free_rate
sigma = return_series.std()
# Match the DataFrames so they share a similar length
if len(df1.index) > len(df2.index):
df1 = df1.loc[df2.index]
elif len(df2.index) > len(df1.index):
df2 = df2.loc[df1.index]
return mean / sigma
def sortino_ratio(return_series: pd.Series, risk_free_rate: float) -> float:
"""Get sortino ratio
Parameters
----------
return_series : pd.Series
Returns of the portfolio
risk_free_rate: float
Value to use for risk free rate
Returns
-------
float
Sortino ratio
"""
mean = return_series.mean() - risk_free_rate
std_neg = return_series[return_series < 0].std()
return mean / std_neg
def get_maximum_drawdown(return_series: pd.Series) -> float:
"""Get maximum drawdown
Parameters
----------
return_series : pd.Series
Returns of the portfolio
Returns
-------
float
Maximum drawdown
"""
comp_ret = (return_series + 1).cumprod()
peak = comp_ret.expanding(min_periods=1).max()
dd = (comp_ret / peak) - 1
return dd.min()
return df1, df2

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ from openbb_terminal.helper_funcs import (
from openbb_terminal.decorators import log_start_end
from openbb_terminal.rich_config import console
# pylint: disable=C0302
# pylint: disable=C0302,redefined-outer-name
# from reportlab.lib.pagesizes import letter
# from reportlab.pdfgen import canvas
@@ -64,6 +64,25 @@ In order to load a CSV do the following:
console.print(text)
@log_start_end(log=logger)
def display_orderbook(portfolio=None, show_index=False):
"""Display portfolio orderbook
Parameters
----------
portfolio: Portfolio
Instance of Portfolio class
show_index: bool
Defaults to False.
"""
if portfolio is None:
logger.warning("No orderbook loaded")
console.print("[red]No orderbook loaded.[/red]\n")
else:
print_rich_table(portfolio.get_orderbook(), show_index)
@log_start_end(log=logger)
def display_assets_allocation(
portfolio_allocation: pd.DataFrame,
@@ -243,81 +262,21 @@ def display_performance_vs_benchmark(
benchmark_trades.index = pd.to_datetime(benchmark_trades["Date"].values)
benchmark_trades = portfolio_helper.filter_df_by_period(benchmark_trades, period)
# Calculate total value and return
total_investment_difference = (
portfolio_trades["Portfolio Investment"].sum()
- benchmark_trades["Benchmark Investment"].sum()
)
total_value_difference = (
portfolio_trades["Portfolio Value"].sum()
- benchmark_trades["Benchmark Value"].sum()
)
total_portfolio_return = (
portfolio_trades["Portfolio Value"].sum()
/ portfolio_trades["Portfolio Investment"].sum()
) - 1
total_benchmark_return = (
benchmark_trades["Benchmark Value"].sum()
/ benchmark_trades["Benchmark Investment"].sum()
) - 1
total_abs_return_difference = (
portfolio_trades["Portfolio Value"].sum()
- portfolio_trades["Portfolio Investment"].sum()
) - (
benchmark_trades["Benchmark Value"].sum()
- benchmark_trades["Benchmark Investment"].sum()
)
totals = pd.DataFrame.from_dict(
{
"Total Investment": [
portfolio_trades["Portfolio Investment"].sum(),
benchmark_trades["Benchmark Investment"].sum(),
total_investment_difference,
],
"Total Value": [
portfolio_trades["Portfolio Value"].sum(),
benchmark_trades["Benchmark Value"].sum(),
total_value_difference,
],
"Total % Return": [
f"{total_portfolio_return:.2%}",
f"{total_benchmark_return:.2%}",
f"{total_portfolio_return - total_benchmark_return:.2%}",
],
"Total Abs Return": [
portfolio_trades["Portfolio Value"].sum()
- portfolio_trades["Portfolio Investment"].sum(),
benchmark_trades["Benchmark Value"].sum()
- benchmark_trades["Benchmark Investment"].sum(),
total_abs_return_difference,
],
},
orient="index",
columns=["Portfolio", "Benchmark", "Difference"],
)
print_rich_table(
totals.replace(0, "-"),
title=f"Portfolio vs. Benchmark - Totals in period: {period}",
headers=list(totals.columns),
show_index=True,
)
if show_all_trades:
# Combine DataFrames
combined = pd.concat(
[
portfolio_trades[
["Date", "Name", "Portfolio Value", "% Portfolio Return"]
["Date", "Ticker", "Portfolio Value", "% Portfolio Return"]
],
benchmark_trades[["Benchmark Value", "% Benchmark Return"]],
benchmark_trades[["Benchmark Value", "Benchmark % Return"]],
],
axis=1,
)
# Calculate alpha
combined["Alpha"] = (
combined["% Portfolio Return"] - combined["% Benchmark Return"]
combined["% Portfolio Return"] - combined["Benchmark % Return"]
)
combined["Date"] = pd.to_datetime(combined["Date"]).dt.date
@@ -329,6 +288,66 @@ def display_performance_vs_benchmark(
show_index=False,
floatfmt=[".2f", ".2f", ".2f", ".2%", ".2f", ".2%", ".2%"],
)
else:
# Calculate total value and return
total_investment_difference = (
portfolio_trades["Portfolio Investment"].sum()
- benchmark_trades["Benchmark Investment"].sum()
)
total_value_difference = (
portfolio_trades["Portfolio Value"].sum()
- benchmark_trades["Benchmark Value"].sum()
)
total_portfolio_return = (
portfolio_trades["Portfolio Value"].sum()
/ portfolio_trades["Portfolio Investment"].sum()
) - 1
total_benchmark_return = (
benchmark_trades["Benchmark Value"].sum()
/ benchmark_trades["Benchmark Investment"].sum()
) - 1
total_abs_return_difference = (
portfolio_trades["Portfolio Value"].sum()
- portfolio_trades["Portfolio Investment"].sum()
) - (
benchmark_trades["Benchmark Value"].sum()
- benchmark_trades["Benchmark Investment"].sum()
)
totals = pd.DataFrame.from_dict(
{
"Total Investment": [
portfolio_trades["Portfolio Investment"].sum(),
benchmark_trades["Benchmark Investment"].sum(),
total_investment_difference,
],
"Total Value": [
portfolio_trades["Portfolio Value"].sum(),
benchmark_trades["Benchmark Value"].sum(),
total_value_difference,
],
"Total % Return": [
f"{total_portfolio_return:.2%}",
f"{total_benchmark_return:.2%}",
f"{total_portfolio_return - total_benchmark_return:.2%}",
],
"Total Abs Return": [
portfolio_trades["Portfolio Value"].sum()
- portfolio_trades["Portfolio Investment"].sum(),
benchmark_trades["Benchmark Value"].sum()
- benchmark_trades["Benchmark Investment"].sum(),
total_abs_return_difference,
],
},
orient="index",
columns=["Portfolio", "Benchmark", "Difference"],
)
print_rich_table(
totals.replace(0, "-"),
title=f"Portfolio vs. Benchmark - Totals in period: {period}",
headers=list(totals.columns),
show_index=True,
)
@log_start_end(log=logger)
@@ -360,15 +379,12 @@ def display_cumulative_returns(
external_axes: plt.Axes
Optional axes to display plot on
"""
portfolio_returns = portfolio_helper.filter_df_by_period(portfolio_returns, period)
benchmark_returns = portfolio_helper.filter_df_by_period(benchmark_returns, period)
cumulative_returns = 100 * (
(1 + portfolio_returns.shift(periods=1, fill_value=0)).cumprod() - 1
)
benchmark_c_returns = 100 * (
(1 + benchmark_returns.shift(periods=1, fill_value=0)).cumprod() - 1
)
cumulative_returns = 100 * portfolio_model.cumulative_returns(portfolio_returns)
benchmark_c_returns = 100 * portfolio_model.cumulative_returns(benchmark_returns)
if raw:
last_cumulative_returns = cumulative_returns.to_frame()
@@ -440,17 +456,13 @@ def display_yearly_returns(
for year in sorted(set(portfolio_returns.index.year)):
creturns_year = portfolio_returns[portfolio_returns.index.year == year]
cumulative_returns = 100 * (
(1 + creturns_year.shift(periods=1, fill_value=0)).cumprod() - 1
)
cumulative_returns = 100 * portfolio_model.cumulative_returns(creturns_year)
creturns_year_idx.append(datetime.strptime(f"{year}-04-15", "%Y-%m-%d"))
creturns_year_val.append(cumulative_returns.values[-1])
breturns_year = benchmark_returns[benchmark_returns.index.year == year]
benchmark_c_returns = 100 * (
(1 + breturns_year.shift(periods=1, fill_value=0)).cumprod() - 1
)
benchmark_c_returns = 100 * portfolio_model.cumulative_returns(breturns_year)
breturns_year_idx.append(datetime.strptime(f"{year}-08-15", "%Y-%m-%d"))
breturns_year_val.append(benchmark_c_returns.values[-1])
@@ -552,8 +564,8 @@ def display_monthly_returns(
creturns_val = list()
for i in range(1, 13):
creturns_year_month = creturns_year[creturns_year.index.month == i]
creturns_year_month_val = 100 * (
(1 + creturns_year_month.shift(periods=1, fill_value=0)).cumprod() - 1
creturns_year_month_val = 100 * portfolio_model.cumulative_returns(
creturns_year_month
)
if creturns_year_month.empty:
@@ -566,8 +578,8 @@ def display_monthly_returns(
breturns_val = list()
for i in range(1, 13):
breturns_year_month = breturns_year[breturns_year.index.month == i]
breturns_year_month_val = 100 * (
(1 + breturns_year_month.shift(periods=1, fill_value=0)).cumprod() - 1
breturns_year_month_val = 100 * portfolio_model.cumulative_returns(
breturns_year_month
)
if breturns_year_month.empty:
@@ -821,7 +833,7 @@ def display_distribution_returns(
@log_start_end(log=logger)
def display_holdings_value(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
sum_assets: bool = False,
raw: bool = False,
limit: int = 10,
@@ -845,15 +857,8 @@ def display_holdings_value(
external_axes: plt.Axes
Optional axes to display plot on
"""
all_holdings = pd.concat(
[
portfolio.portfolio["StockHoldings"],
portfolio.portfolio["ETFHoldings"],
portfolio.portfolio["CryptoHoldings"],
],
axis=1,
)
all_holdings = all_holdings.drop(columns=["temp"])
all_holdings = portfolio.historical_trade_data["End Value"][portfolio.tickers_list]
if raw:
all_holdings["Total Value"] = all_holdings.sum(axis=1)
@@ -906,7 +911,7 @@ def display_holdings_value(
@log_start_end(log=logger)
def display_holdings_percentage(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
sum_assets: bool = False,
raw: bool = False,
limit: int = 10,
@@ -930,15 +935,8 @@ def display_holdings_percentage(
external_axes: plt.Axes
Optional axes to display plot on
"""
all_holdings = pd.concat(
[
portfolio.portfolio["StockHoldings"],
portfolio.portfolio["ETFHoldings"],
portfolio.portfolio["CryptoHoldings"],
],
axis=1,
)
all_holdings = all_holdings.drop(columns=["temp"])
all_holdings = portfolio.historical_trade_data["End Value"][portfolio.tickers_list]
all_holdings = all_holdings.divide(all_holdings.sum(axis=1), axis=0) * 100
@@ -1029,8 +1027,10 @@ def display_rolling_volatility(
length = portfolio_helper.PERIODS_DAYS[period]
rolling_volatility = portfolio_returns.rolling(length).std()
rolling_volatility_bench = benchmark_returns.rolling(length).std()
rolling_volatility = portfolio_model.rolling_volatility(portfolio_returns, length)
rolling_volatility_bench = portfolio_model.rolling_volatility(
benchmark_returns, length
)
rolling_volatility.plot(ax=ax)
rolling_volatility_bench.plot(ax=ax)
@@ -1087,11 +1087,11 @@ def display_rolling_sharpe(
length = portfolio_helper.PERIODS_DAYS[period]
rolling_sharpe = portfolio_returns.rolling(length).apply(
lambda x: (x.mean() - risk_free_rate) / x.std()
rolling_sharpe = portfolio_model.rolling_sharpe(
portfolio_returns, risk_free_rate, length
)
rolling_sharpe_bench = benchmark_returns.rolling(length).apply(
lambda x: (x.mean() - risk_free_rate) / x.std()
rolling_sharpe_bench = portfolio_model.rolling_sharpe(
benchmark_returns, risk_free_rate, length
)
rolling_sharpe.plot(ax=ax)
@@ -1149,11 +1149,11 @@ def display_rolling_sortino(
length = portfolio_helper.PERIODS_DAYS[period]
rolling_sortino = portfolio_returns.rolling(length).apply(
lambda x: (x.mean() - risk_free_rate) / x[x < 0].std()
rolling_sortino = portfolio_model.rolling_sortino(
portfolio_returns, risk_free_rate, length
)
rolling_sortino_bench = benchmark_returns.rolling(length).apply(
lambda x: (x.mean() - risk_free_rate) / x[x < 0].std()
rolling_sortino_bench = portfolio_model.rolling_sortino(
benchmark_returns, risk_free_rate, length
)
rolling_sortino.plot(ax=ax)
@@ -1284,7 +1284,7 @@ def display_maximum_drawdown(
@log_start_end(log=logger)
def display_rsquare(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display R-square
@@ -1311,7 +1311,7 @@ def display_rsquare(
@log_start_end(log=logger)
def display_skewness(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display skewness
@@ -1338,7 +1338,7 @@ def display_skewness(
@log_start_end(log=logger)
def display_kurtosis(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display kurtosis
@@ -1365,7 +1365,7 @@ def display_kurtosis(
@log_start_end(log=logger)
def display_stats(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
period: str = "all",
export: str = "",
):
@@ -1395,7 +1395,7 @@ def display_stats(
@log_start_end(log=logger)
def display_volatility(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display volatility for multiple periods
@@ -1421,7 +1421,7 @@ def display_volatility(
@log_start_end(log=logger)
def display_sharpe_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
risk_free_rate: float,
export: str = "",
):
@@ -1453,7 +1453,7 @@ def display_sharpe_ratio(
@log_start_end(log=logger)
def display_sortino_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
risk_free_rate: float,
export: str = "",
):
@@ -1485,7 +1485,7 @@ def display_sortino_ratio(
@log_start_end(log=logger)
def display_maximum_drawdown_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display maximum drawdown for multiple periods
@@ -1511,7 +1511,7 @@ def display_maximum_drawdown_ratio(
@log_start_end(log=logger)
def display_gaintopain_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display gain-to-pain ratio for multiple periods
@@ -1540,7 +1540,7 @@ def display_gaintopain_ratio(
@log_start_end(log=logger)
def display_tracking_error(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display tracking error for multiple periods
@@ -1566,7 +1566,7 @@ def display_tracking_error(
@log_start_end(log=logger)
def display_information_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display information ratio for multiple periods
@@ -1595,7 +1595,7 @@ def display_information_ratio(
@log_start_end(log=logger)
def display_tail_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display tail ratio for multiple periods
@@ -1621,7 +1621,7 @@ def display_tail_ratio(
@log_start_end(log=logger)
def display_common_sense_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display common sense ratio for multiple periods
@@ -1650,7 +1650,7 @@ def display_common_sense_ratio(
@log_start_end(log=logger)
def display_jensens_alpha(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
rf: float = 0,
export: str = "",
):
@@ -1679,7 +1679,7 @@ def display_jensens_alpha(
@log_start_end(log=logger)
def display_calmar_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display calmar ratio for multiple periods
@@ -1705,7 +1705,7 @@ def display_calmar_ratio(
@log_start_end(log=logger)
def display_kelly_criterion(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display kelly criterion for multiple periods
@@ -1730,7 +1730,7 @@ def display_kelly_criterion(
def display_payoff_ratio(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display payoff ratio for multiple periods
@@ -1755,7 +1755,7 @@ def display_payoff_ratio(
def display_profit_factor(
portfolio: portfolio_model.Portfolio,
portfolio: portfolio_model.PortfolioModel,
export: str = "",
):
"""Display profit factor for multiple periods
@@ -1805,32 +1805,33 @@ def display_summary_portfolio_benchmark(
portfolio_returns = portfolio_helper.filter_df_by_period(portfolio_returns, period)
benchmark_returns = portfolio_helper.filter_df_by_period(benchmark_returns, period)
metrics = {}
metrics["Volatility"] = [portfolio_returns.std(), benchmark_returns.std()]
metrics["Skew"] = [
scipy.stats.skew(portfolio_returns),
scipy.stats.skew(benchmark_returns),
]
metrics["Kurtosis"] = [
scipy.stats.kurtosis(portfolio_returns),
scipy.stats.kurtosis(benchmark_returns),
]
metrics["Maximum Drawdowwn"] = [
portfolio_helper.get_maximum_drawdown(portfolio_returns),
portfolio_helper.get_maximum_drawdown(benchmark_returns),
]
metrics["Sharpe ratio"] = [
portfolio_helper.sharpe_ratio(portfolio_returns, risk_free_rate),
portfolio_helper.sharpe_ratio(benchmark_returns, risk_free_rate),
]
metrics["Sortino ratio"] = [
portfolio_helper.sortino_ratio(portfolio_returns, risk_free_rate),
portfolio_helper.sortino_ratio(benchmark_returns, risk_free_rate),
]
metrics["R2 Score"] = [
r2_score(portfolio_returns, benchmark_returns),
r2_score(portfolio_returns, benchmark_returns),
]
metrics = {
"Volatility": [portfolio_returns.std(), benchmark_returns.std()],
"Skew": [
scipy.stats.skew(portfolio_returns),
scipy.stats.skew(benchmark_returns),
],
"Kurtosis": [
scipy.stats.kurtosis(portfolio_returns),
scipy.stats.kurtosis(benchmark_returns),
],
"Maximum Drawdowwn": [
portfolio_model.get_maximum_drawdown(portfolio_returns),
portfolio_model.get_maximum_drawdown(benchmark_returns),
],
"Sharpe ratio": [
portfolio_model.sharpe_ratio(portfolio_returns, risk_free_rate),
portfolio_model.sharpe_ratio(benchmark_returns, risk_free_rate),
],
"Sortino ratio": [
portfolio_model.sortino_ratio(portfolio_returns, risk_free_rate),
portfolio_model.sortino_ratio(benchmark_returns, risk_free_rate),
],
"R2 Score": [
r2_score(portfolio_returns, benchmark_returns),
r2_score(portfolio_returns, benchmark_returns),
],
}
summary = pd.DataFrame(
metrics.values(), index=metrics.keys(), columns=["Portfolio", "Benchmark"]
@@ -1848,307 +1849,3 @@ def display_summary_portfolio_benchmark(
"summary",
summary,
)
#
# @log_start_end(log=logger)
# def plot_overall_return(
# comb: pd.DataFrame, m_tick: str, plot: bool = False
# ) -> ImageReader:
# """Generates overall return graph
#
# Parameters
# ----------
# comb : pd.DataFrame
# Dataframe with returns
# m_tick : str
# The ticker for the market asset
# plot : bool
# Whether to plot the graph or return it for PDF
#
# Returns
# ----------
# img : ImageReader
# Overal return graph
# """
# fig, ax = plt.subplots(figsize=(10, 5))
# ax.plot(comb.index, comb["return"], color="tab:blue", label="Portfolio")
# ax.plot(comb.index, comb[("Market", "Return")], color="orange", label=m_tick)
#
# ax.set_ylabel("", fontweight="bold", fontsize=12, color="black")
# ax.set_xlabel("")
# ax.yaxis.set_label_coords(-0.1, 0.5)
# ax.grid(True)
# ax.spines["top"].set_visible(False)
# ax.spines["right"].set_visible(False)
# ax.spines["bottom"].set_visible(False)
# ax.spines["left"].set_visible(False)
# fig.suptitle(
# "Cumulative Performance", y=0.99, fontweight="bold", fontsize=14, color="black"
# )
# ax.axhline(0, ls="-", lw=1, color="gray", zorder=1)
# ax.axhline(0, ls="--", lw=1, color="black", zorder=2)
# fig.set_facecolor("white")
# ax.set_title(
# f'{comb.index[:1][0].strftime("%Y/%m/%d")} - {comb.index[-1:][0].strftime("%Y/%m/%d")}',
# fontsize=12,
# color="gray",
# )
# ax.yaxis.set_major_formatter(mtick.PercentFormatter(1.0))
# ax.set_facecolor("white")
# ax.legend()
# fig.autofmt_xdate()
# if plot:
# plt.show()
# console.print("")
# return None
# imgdata = BytesIO()
# fig.savefig(imgdata, format="png")
# plt.close("all")
# imgdata.seek(0)
# return ImageReader(imgdata)
#
#
# @log_start_end(log=logger)
# def plot_rolling_beta(df: pd.DataFrame) -> ImageReader:
# """Returns a chart with the portfolio's rolling beta
#
# Parameters
# ----------
# df : pd.DataFrame
# The dataframe to be analyzed
#
# Returns
# ----------
# img : ImageReader
# Rolling beta graph
# """
#
# fig, ax = plt.subplots(figsize=(10, 5))
# ax.plot(
# df.index,
# df["total"],
# color="tab:blue",
# )
#
# ax.set_ylabel("", fontweight="bold", fontsize=12, color="black")
# ax.set_xlabel("")
# ax.yaxis.set_label_coords(-0.1, 0.5)
# ax.grid(True)
# ax.spines["top"].set_visible(False)
# ax.spines["right"].set_visible(False)
# ax.spines["bottom"].set_visible(False)
# ax.spines["left"].set_visible(False)
# fig.suptitle(
# "Rolling Beta of Stocks", y=0.99, fontweight="bold", fontsize=14, color="black"
# )
# ax.axhline(0, ls="-", lw=1, color="gray", zorder=1)
# ax.axhline(0, ls="--", lw=1, color="black", zorder=2)
# fig.set_facecolor("white")
# ax.set_title(
# f'{df.index[:1][0].strftime("%Y-%m-%d")} - {df.index[-1:][0].strftime("%Y-%m-%d")}',
# color="gray",
# )
# ax.set_facecolor("white")
# fig.autofmt_xdate()
# imgdata = BytesIO()
# fig.savefig(imgdata, format="png")
# plt.close("all")
# imgdata.seek(0)
# return ImageReader(imgdata)
#
#
# @log_start_end(log=logger)
# def plot_ef(
# stocks: List[str],
# variance: float,
# per_ret: float,
# rf_rate: float,
# period: str = "3mo",
# n_portfolios: int = 300,
# risk_free: bool = False,
# ):
# """Display efficient frontier
#
# Parameters
# ----------
# stocks : List[str]
# List of the stocks to be included in the weights
# variance : float
# The variance for the portfolio
# per_ret : float
# The portfolio's return for the portfolio
# rf_rate : float
# The risk free rate
# period : str
# The period to track
# n_portfolios : int
# The number of portfolios to generate
# risk_free : bool
# Include the risk-free asset
# """
# fig, ax = plt.subplots(figsize=(10, 5), dpi=PLOT_DPI)
# ef, rets, stds = optimizer_model.generate_random_portfolios(
# [x.upper() for x in stocks], period, n_portfolios
# )
# 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
# ret_sharpe, std_sharpe, _ = ef.portfolio_performance(risk_free_rate=rf_rate)
# ax.scatter(std_sharpe, ret_sharpe, marker="*", s=100, c="r", label="Max Sharpe")
# plt.plot(variance, per_ret, "ro", label="Portfolio")
# # Add risk free line
# if risk_free:
# y = ret_sharpe * 1.2
# m = (ret_sharpe - rf_rate) / std_sharpe
# x2 = (y - rf_rate) / m
# x = [0, x2]
# y = [rf_rate, y]
# line = Line2D(x, y, color="#FF0000", label="Capital Allocation Line")
# ax.set_xlim(xmin=min(stds) * 0.8)
# ax.add_line(line)
# ax.set_title(f"Efficient Frontier simulating {n_portfolios} portfolios")
# ax.legend()
# fig.tight_layout()
# ax.grid(b=True, which="major", color="#666666", linestyle="-")
#
# if obbff.USE_ION:
# plt.ion()
#
# imgdata = BytesIO()
# fig.savefig(imgdata, format="png")
# plt.close("all")
# imgdata.seek(0)
# return ImageReader(imgdata)
# @log_start_end(log=logger)
# def display_allocation2(data: pd.DataFrame, graph: bool):
# """Displays allocation
# Parameters
# ----------
# data: pd.DataFrame
# The portfolio allocation dataframe
# graph: bool
# If pie chart shall be displayed with table"""
#
# print_rich_table(data, headers=list(data.columns), title="Allocation")
# console.print("")
#
# if graph:
# graph_data = data[data["pct_allocation"] >= 5].copy()
# if not graph_data.empty:
# graph_data.loc["Other"] = [
# "NA",
# data["value"].sum() - graph_data["value"].sum(),
# 100 - graph_data["value"].sum(),
# ]
# labels = graph_data.index.values
# sizes = graph_data["value"].to_list()
# else:
# labels = data.index.values
# sizes = data["value"].to_list()
# fig, ax = plt.subplots()
# ax.pie(sizes, labels=labels, autopct="%1.1f%%", startangle=90)
# ax.axis("equal")
# ax.set_title("Portfolio Allocation")
# fig.set_tight_layout(True)
#
# plt.show()
#
# class Report:
# @log_start_end(log=logger)
# def __init__(self, df: pd.DataFrame, hist: pd.DataFrame, m_tick: str):
# """Generate financial reports.
# Financial reports allow users to show the how they have been performing in
# trades. This allows for a simple way to show progress and analyze metrics
# that track portfolio performance
#
# Parameters
# ----------
# df : pd.DataFrame
# The dataframe with previous holdings information
# hist : pd.DataFrame
# The dataframe with previous prices for stocks in the portfolio
# df_m : pd.DataFrame
# Dataframe of benchmark
# n : int
# The number of days to analyze
#
# Attributes
# ----------
# generate_report : None
# Generates a report with the given parameters
# generate_pg1 : None
# Creates the first page of the PDF report
# generate_pg2 : None
# Creates the second page of the PDF report
#
# """
# self.df = df
# self.hist = hist
# self.m_tick = m_tick
# self.df_m = yfinance_model.get_market(self.df.index[0], self.m_tick)
# # self.returns, self.variance = portfolio_model.get_return(df, self.df_m, n)
# self.returns = pd.DataFrame()
# self.rf = get_rf()
# self.betas = portfolio_model.get_rolling_beta(
# self.df, self.hist, self.df_m, 365
# )
#
# @log_start_end(log=logger)
# def generate_report(self) -> None:
# d = path.dirname(path.abspath(__file__)).replace(
# "openbb_terminal", "exports"
# )
# loc = path.abspath(
# path.join(
# d,
# f"ar_{datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf",
# )
# )
# report = canvas.Canvas(loc, pagesize=letter)
# reportlab_helpers.base_format(report, "Overview")
# self.generate_pg1(report)
# self.generate_pg2(report)
# report.save()
# console.print("File save in:\n", loc, "\n")
#
# @log_start_end(log=logger)
# def generate_pg1(self, report: canvas.Canvas) -> None:
# report.drawImage(
# plot_overall_return(self.returns, self.m_tick, False), 15, 400, 600, 300
# )
# main_text = portfolio_model.get_main_text(self.returns)
# reportlab_helpers.draw_paragraph(report, main_text, 30, 410, 550, 200)
# current_return = self.returns["return"][-1]
# beta = self.betas["total"][-1]
# market_return = self.returns[("Market", "Return")][-1]
# sharpe = f"{(current_return - self.rf)/ np.std(self.returns['return']):.2f}"
# treynor = f"{(current_return - self.rf)/ beta:.2f}" if beta > 0 else "N/A"
# alpha = f"{current_return - (self.rf + beta * (market_return - self.rf)):.2f}"
# information = (
# f"{float(alpha)/ (np.std(self.returns['return'] - market_return)):.2f}"
# )
# perf = [
# ["Sharpe", sharpe],
# ["Treynor", treynor],
# ["Alpha", alpha],
# ["Information", information],
# ]
# reportlab_helpers.draw_table(report, "Performance", 540, 300, 30, perf)
# reportlab_helpers.draw_paragraph(
# report, portfolio_model.performance_text, 140, 290, 460, 200
# )
# report.showPage()
#
# @log_start_end(log=logger)
# def generate_pg2(self, report: canvas.Canvas) -> None:
# reportlab_helpers.base_format(report, "Portfolio Analysis")
# if "Holding" in self.df.columns:
# report.drawImage(plot_rolling_beta(self.betas), 15, 400, 600, 300)
# main_t = portfolio_model.get_beta_text(self.betas)
# reportlab_helpers.draw_paragraph(report, main_t, 30, 410, 550, 200)
# # report.drawImage(plot_ef(uniques, self.variance, self.returns["return"][-1], self.rf), 15, 65, 600, 300)

Binary file not shown.

View File

@@ -1,4 +1,3 @@
Date,Name,Type,Sector,Industry,Country,Price,Quantity,Fees,Premium,Investment,Side,Currency
07/20/2021,CASH,CASH,CASH,CASH,CASH,30000,1,0,0,30000,Deposit,USD
07/20/2021,btc,CRYPTO,Digital Currency,Large,United States,29790,1,0,0,29790,Buy,USD
07/25/2021,eth,CRYPTO,Digital Asset,Mid,United States,29799,1,0,0,29799,Buy,USD
Date,Ticker,Type,Sector,Industry,Country,Price,Quantity,Fees,Premium,Investment,Side,Currency
7/20/2021,btc,CRYPTO,Digital Currency,Large,United States,29790,1,0,0,29790,Buy,USD
7/25/2021,eth,CRYPTO,Digital Asset,Mid,United States,29799,1,0,0,29799,Buy,USD
1 Date Name Ticker Type Sector Industry Country Price Quantity Fees Premium Investment Side Currency
2 07/20/2021 7/20/2021 CASH btc CASH CRYPTO CASH Digital Currency CASH Large CASH United States 30000 29790 1 0 0 30000 29790 Deposit Buy USD
3 07/20/2021 7/25/2021 btc eth CRYPTO Digital Currency Digital Asset Large Mid United States 29790 29799 1 0 0 29790 29799 Buy USD
07/25/2021 eth CRYPTO Digital Asset Mid United States 29799 1 0 0 29799 Buy USD

View File

@@ -1,6 +1,5 @@
Date,Name,Type,Sector,Industry,Country,Price,Quantity,Fees,Premium,Investment,Side,Currency
2021/3/24,CASH,CASH,CASH,CASH,CASH,2000,1,0,0,2000,Deposit,USD
2021/3/24,aapl,stock,Technology,Communication Services,United States,120.7,300,0,0,0,Buy,USD
2021/3/26,msft,stock,Technology,Communication Services,United States,232.8,200,0,0,0,Buy,USD
2021/5/21,btc,crypto,Digital Assets,Digital Assets,United States,37430,1,0,0,0,Buy,USD
2021/7/2,aapl,stock,Technology,Communication Services,United States,139.8,300,0,0,0,Buy,USD
Date,Ticker,Type,Sector,Industry,Country,Price,Quantity,Fees,Premium,Investment,Side,Currency
3/24/2021,aapl,stock,Technology,Communication Services,United States,120.7,300,0,0,0,Buy,USD
3/26/2021,msft,stock,Technology,Communication Services,United States,232.8,200,0,0,0,Buy,USD
5/21/2021,btc,crypto,Digital Assets,Digital Assets,United States,37430,1,0,0,0,Buy,USD
7/2/2021,aapl,stock,Technology,Communication Services,United States,139.8,300,0,0,0,Buy,USD
1 Date Name Ticker Type Sector Industry Country Price Quantity Fees Premium Investment Side Currency
2 2021/3/24 3/24/2021 CASH aapl CASH stock CASH Technology CASH Communication Services CASH United States 2000 120.7 1 300 0 0 2000 0 Deposit Buy USD
3 2021/3/24 3/26/2021 aapl msft stock Technology Communication Services United States 120.7 232.8 300 200 0 0 0 Buy USD
4 2021/3/26 5/21/2021 msft btc stock crypto Technology Digital Assets Communication Services Digital Assets United States 232.8 37430 200 1 0 0 0 Buy USD
5 2021/5/21 7/2/2021 btc aapl crypto stock Digital Assets Technology Digital Assets Communication Services United States 37430 139.8 1 300 0 0 0 Buy USD
2021/7/2 aapl stock Technology Communication Services United States 139.8 300 0 0 0 Buy USD

View File

@@ -1,4 +1,3 @@
Date,Name,Type,Sector,Industry,Country,Price,Quantity,Fees,Premium,Investment,Side,Currency
10/05/2017,CASH,CASH,CASH,CASH,CASH,8523,1,0,0,8423,Deposit,USD
Date,Ticker,Type,Sector,Industry,Country,Price,Quantity,Fees,Premium,Investment,Side,Currency
10/12/2018,QQQ,ETF,American Technology,Large,United States,174.32,30,0,0,0,Buy,USD
06/05/2020,SPY,ETF,American Market,Large,United States,319.34,10,0,0,0,Buy,USD
6/5/2020,SPY,ETF,American Market,Large,United States,319.34,10,0,0,0,Buy,USD
1 Date Name Ticker Type Sector Industry Country Price Quantity Fees Premium Investment Side Currency
10/05/2017 CASH CASH CASH CASH CASH 8523 1 0 0 8423 Deposit USD
2 10/12/2018 QQQ QQQ ETF American Technology Large United States 174.32 30 0 0 0 Buy USD
3 06/05/2020 6/5/2020 SPY SPY ETF American Market Large United States 319.34 10 0 0 0 Buy USD

View File

@@ -39,7 +39,7 @@ interactions:
CF-Cache-Status:
- MISS
CF-RAY:
- 726fd15b7ea0b78a-AMS
- 727750f15ff5b78a-AMS
Cache-Control:
- no-store
Connection:
@@ -49,7 +49,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 07 Jul 2022 10:15:14 GMT
- Fri, 08 Jul 2022 08:05:40 GMT
Etag:
- W/"1d-mmRSeO9uba2rhQtGfy4YjixIkt4"
Expect-CT:

View File

@@ -39,7 +39,7 @@ interactions:
CF-Cache-Status:
- MISS
CF-RAY:
- 726fd15c7a72b784-AMS
- 727750f278c3b930-AMS
Cache-Control:
- no-store
Connection:
@@ -49,7 +49,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 07 Jul 2022 10:15:14 GMT
- Fri, 08 Jul 2022 08:05:40 GMT
Etag:
- W/"1d-mmRSeO9uba2rhQtGfy4YjixIkt4"
Expect-CT:

View File

@@ -39,7 +39,7 @@ interactions:
CF-Cache-Status:
- MISS
CF-RAY:
- 726fd15d89da0b6f-AMS
- 727750f39c4fb89a-AMS
Cache-Control:
- no-store
Connection:
@@ -49,7 +49,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 07 Jul 2022 10:15:14 GMT
- Fri, 08 Jul 2022 08:05:41 GMT
Etag:
- W/"1d-mmRSeO9uba2rhQtGfy4YjixIkt4"
Expect-CT:

View File

@@ -39,7 +39,7 @@ interactions:
CF-Cache-Status:
- MISS
CF-RAY:
- 726fd15ec954fa4c-AMS
- 727750f4ce961ee7-AMS
Cache-Control:
- no-store
Connection:
@@ -49,7 +49,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 07 Jul 2022 10:15:15 GMT
- Fri, 08 Jul 2022 08:05:41 GMT
Etag:
- W/"1d-mmRSeO9uba2rhQtGfy4YjixIkt4"
Expect-CT:

View File

@@ -39,7 +39,7 @@ interactions:
CF-Cache-Status:
- MISS
CF-RAY:
- 726fd15ffad5b948-AMS
- 727750f5dd37b784-AMS
Cache-Control:
- no-store
Connection:
@@ -49,7 +49,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 07 Jul 2022 10:15:15 GMT
- Fri, 08 Jul 2022 08:05:41 GMT
Etag:
- W/"1d-mmRSeO9uba2rhQtGfy4YjixIkt4"
Expect-CT:

View File

@@ -39,7 +39,7 @@ interactions:
CF-Cache-Status:
- MISS
CF-RAY:
- 726fd160fc7fb891-AMS
- 727750f6ed5f4242-AMS
Cache-Control:
- no-store
Connection:
@@ -49,7 +49,7 @@ interactions:
Content-Type:
- application/json; charset=utf-8
Date:
- Thu, 07 Jul 2022 10:15:15 GMT
- Fri, 08 Jul 2022 08:05:41 GMT
Etag:
- W/"1d-mmRSeO9uba2rhQtGfy4YjixIkt4"
Expect-CT:

View File

@@ -8,17 +8,17 @@ dtype: float64"
ytd,"TotalHoldings 0.759
dtype: float64","Adj Close 0.722
dtype: float64"
3m,"TotalHoldings 0.693
dtype: float64","Adj Close 0.606
3m,"TotalHoldings 0.68
dtype: float64","Adj Close 0.613
dtype: float64"
6m,"TotalHoldings 0.731
dtype: float64","Adj Close 0.699
dtype: float64"
1y,"TotalHoldings 0.588
dtype: float64","Adj Close 0.481
1y,"TotalHoldings 0.571
dtype: float64","Adj Close 0.452
dtype: float64"
3y,"TotalHoldings -1.153
dtype: float64","Adj Close -0.774
3y,"TotalHoldings -1.17
dtype: float64","Adj Close -0.791
dtype: float64"
5y,"TotalHoldings -1.308
dtype: float64","Adj Close -1.483
1 Portfolio Benchmark
8 3y TotalHoldings -1.153 dtype: float64 TotalHoldings -1.17 dtype: float64 Adj Close -0.774 dtype: float64 Adj Close -0.791 dtype: float64
9 5y TotalHoldings -1.308 dtype: float64 Adj Close -1.483 dtype: float64
10 10y TotalHoldings -1.308 dtype: float64 Adj Close -1.483 dtype: float64
11 all TotalHoldings -1.308 dtype: float64 Adj Close -1.483 dtype: float64
12
13
14
15
16
17
18
19
20
21
22
23
24

View File

@@ -8,8 +8,8 @@ dtype: float64"
ytd,"TotalHoldings 0.845
dtype: float64","Adj Close 0.831
dtype: float64"
3m,"TotalHoldings 0.759
dtype: float64","Adj Close 0.704
3m,"TotalHoldings 0.758
dtype: float64","Adj Close 0.703
dtype: float64"
6m,"TotalHoldings 0.847
dtype: float64","Adj Close 0.83
@@ -17,7 +17,7 @@ dtype: float64"
1y,"TotalHoldings 0.923
dtype: float64","Adj Close 0.927
dtype: float64"
3y,"TotalHoldings 0.911
3y,"TotalHoldings 0.91
dtype: float64","Adj Close 0.798
dtype: float64"
5y,"TotalHoldings 0.882
1 Portfolio Benchmark
8 3y TotalHoldings 0.911 dtype: float64 TotalHoldings 0.91 dtype: float64 Adj Close 0.798 dtype: float64
9 5y TotalHoldings 0.882 dtype: float64 Adj Close 0.81 dtype: float64
10 10y TotalHoldings 0.882 dtype: float64 Adj Close 0.81 dtype: float64
11 all TotalHoldings 0.882 dtype: float64 Adj Close 0.81 dtype: float64
12
13
14
15
17
18
19
20
21
22
23

View File

@@ -205,13 +205,6 @@ def test_call_func_expect_queue(expected_queue, func, queue):
[],
dict(),
),
(
"call_load",
["Public_Equity_Orderbook.xlsx"],
"portfolio_model.Portfolio.from_xlsx",
[],
dict(),
),
(
"call_show",
[],

View File

@@ -1,40 +1,4 @@
> bro brokers holdings, supports robinhood, ally, degiro, coinbase
> po portfolio optimization, optimal your portfolio weights efficiently
load load data into the portfolio
Loaded orderbook:
Risk Free Rate:
show show existing transactions
bench define the benchmark
Benchmark:
Graphs:
holdv holdings of assets (absolute value)
holdp portfolio holdings of assets (in percentage)
cret cumulative returns
yret yearly returns
mret monthly returns
dret daily returns
distr distribution of daily returns
maxdd maximum drawdown
rvol rolling volatility
rsharpe rolling sharpe
rsort rolling sortino
rbeta rolling beta
Metrics:
alloc allocation on an asset, sector, countries or regions basis
summary all portfolio vs benchmark metrics for a certain period of choice
metric portfolio vs benchmark metric for all different periods
perf performance of the portfolio versus benchmark
Risk Metrics:
var display value at risk
es display expected shortfall
os portfolio/os
Could not preprocess orderbook.
> bro brokers holdings, supports: robinhood, ally, degiro, coinbase
> po portfolio optimization, optimal your portfolio weights efficiently