mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-02 15:32:04 +00:00
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:
@@ -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())
|
||||
)
|
||||
|
||||
@@ -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
|
||||
):
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
BIN
portfolio/holdings/Public_Equity_Orderbook.xlsx
vendored
BIN
portfolio/holdings/Public_Equity_Orderbook.xlsx
vendored
Binary file not shown.
7
portfolio/holdings/bitcoin.csv
vendored
7
portfolio/holdings/bitcoin.csv
vendored
@@ -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
|
||||
|
||||
|
11
portfolio/holdings/example.csv
vendored
11
portfolio/holdings/example.csv
vendored
@@ -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
|
||||
|
||||
|
5
portfolio/holdings/market.csv
vendored
5
portfolio/holdings/market.csv
vendored
@@ -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
|
||||
|
||||
|
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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",
|
||||
[],
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user