Files
OpenBB/openbb_terminal/portfolio/portfolio_model.py
montezdesousa 570e579067 Portfolio docstring example fixes and some bugs (#3431)
* fix doctest

* fix some type hints

* reorganize portfolio folders

* fix rfr

* fix vol bug

* fix more docs

* fix failing test

* doc typo

* numpy fix

* pydocstyle

* regenerate website docs
2022-11-18 19:30:55 +00:00

1873 lines
57 KiB
Python

"""Portfolio Model"""
__docformat__ = "numpy"
import logging
from typing import Tuple, Union
import numpy as np
import scipy
import pandas as pd
from sklearn.metrics import r2_score
from openbb_terminal.common.quantitative_analysis import qa_model
from openbb_terminal.decorators import log_start_end
from openbb_terminal.portfolio.statics import PERIODS
from openbb_terminal.portfolio import portfolio_helper, metrics_model
from openbb_terminal.portfolio.portfolio_engine import PortfolioEngine
# pylint: disable=E1136,W0201,R0902,C0302
# pylint: disable=unsupported-assignment-operation,redefined-outer-name,too-many-public-methods, consider-using-f-string
logger = logging.getLogger(__name__)
pd.options.mode.chained_assignment = None
@log_start_end(log=logger)
def generate_portfolio(
transactions_file_path: str,
benchmark_symbol: str = "SPY",
full_shares: bool = False,
risk_free_rate: float = 0,
) -> PortfolioEngine:
"""Get PortfolioEngine object
Parameters
----------
transactions_file_path : str
Path to transactions file
benchmark_symbol : str
Benchmark ticker to download data
full_shares : bool
Whether to mimic the portfolio trades exactly (partial shares) or round down the
quantity to the nearest number
risk_free_rate : float
Risk free rate in float format
Returns
-------
PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
"""
transactions = PortfolioEngine.read_transactions(transactions_file_path)
portfolio_engine = PortfolioEngine(transactions)
portfolio_engine.generate_portfolio_data()
portfolio_engine.set_benchmark(symbol=benchmark_symbol, full_shares=full_shares)
portfolio_engine.set_risk_free_rate(risk_free_rate)
return portfolio_engine
@log_start_end(log=logger)
def get_transactions(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get portfolio transactions
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
Portfolio transactions
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.show(p)
"""
return portfolio_engine.get_transactions()
@log_start_end(log=logger)
def set_benchmark(
portfolio_engine: PortfolioEngine, symbol: str, full_shares: bool = False
):
"""Load benchmark into portfolio
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
symbol: str
Benchmark symbol to download data
full_shares: bool
Whether to mimic the portfolio trades exactly (partial shares) or round down the
quantity to the nearest number
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.bench(p, symbol="SPY")
"""
portfolio_engine.set_benchmark(symbol=symbol, full_shares=full_shares)
@log_start_end(log=logger)
def set_risk_free_rate(portfolio_engine: PortfolioEngine, risk_free_rate: float):
"""Set risk-free rate
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
risk_free_rate: float
Risk free rate in float format
"""
portfolio_engine.set_risk_free_rate(risk_free_rate=risk_free_rate)
def get_holdings_value(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get holdings of assets (absolute value)
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of holdings value
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.holdv(p)
"""
all_holdings = portfolio_engine.historical_trade_data["End Value"][
portfolio_engine.tickers_list
]
all_holdings["Total Value"] = all_holdings.sum(axis=1)
# No need to account for time since this is daily data
all_holdings.index = all_holdings.index.date
return all_holdings
def get_holdings_percentage(
portfolio_engine: PortfolioEngine,
) -> pd.DataFrame:
"""Get holdings of assets (in percentage)
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of holdings percentage
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.holdp(p)
"""
all_holdings = portfolio_engine.historical_trade_data["End Value"][
portfolio_engine.tickers_list
]
all_holdings = all_holdings.divide(all_holdings.sum(axis=1), axis=0) * 100
# order it a bit more in terms of magnitude
all_holdings = all_holdings[all_holdings.sum().sort_values(ascending=False).index]
return all_holdings
@log_start_end(log=logger)
def get_yearly_returns(
portfolio_engine: PortfolioEngine,
window: str = "all",
) -> pd.DataFrame:
"""Get yearly returns
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
interval to compare cumulative returns and benchmark
Returns
-------
pd.DataFrame
DataFrame with yearly returns
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.yret(p)
"""
portfolio_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.returns, window
)
benchmark_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, window
)
creturns_year_val = list()
breturns_year_val = list()
for year in sorted(set(portfolio_returns.index.year)):
creturns_year = portfolio_returns[portfolio_returns.index.year == year]
cumulative_returns = 100 * metrics_model.cumulative_returns(creturns_year)
creturns_year_val.append(cumulative_returns.values[-1])
breturns_year = benchmark_returns[benchmark_returns.index.year == year]
benchmark_c_returns = 100 * metrics_model.cumulative_returns(breturns_year)
breturns_year_val.append(benchmark_c_returns.values[-1])
df = pd.DataFrame(
{
"Portfolio": pd.Series(
creturns_year_val, index=list(set(portfolio_returns.index.year))
),
"Benchmark": pd.Series(
breturns_year_val, index=list(set(portfolio_returns.index.year))
),
"Difference": pd.Series(
np.array(creturns_year_val) - np.array(breturns_year_val),
index=list(set(portfolio_returns.index.year)),
),
}
)
return df
@log_start_end(log=logger)
def get_monthly_returns(
portfolio_engine: PortfolioEngine,
window: str = "all",
) -> pd.DataFrame:
"""Get monthly returns
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
interval to compare cumulative returns and benchmark
Returns
-------
pd.DataFrame
DataFrame with monthly returns
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.mret(p)
"""
portfolio_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.returns, window
)
benchmark_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, window
)
creturns_month_val = list()
breturns_month_val = list()
for year in sorted(list(set(portfolio_returns.index.year))):
creturns_year = portfolio_returns[portfolio_returns.index.year == year]
creturns_val = list()
for i in range(1, 13):
creturns_year_month = creturns_year[creturns_year.index.month == i]
creturns_year_month_val = 100 * metrics_model.cumulative_returns(
creturns_year_month
)
if creturns_year_month.empty:
creturns_val.append(0)
else:
creturns_val.append(creturns_year_month_val.values[-1])
creturns_month_val.append(creturns_val)
breturns_year = benchmark_returns[benchmark_returns.index.year == year]
breturns_val = list()
for i in range(1, 13):
breturns_year_month = breturns_year[breturns_year.index.month == i]
breturns_year_month_val = 100 * metrics_model.cumulative_returns(
breturns_year_month
)
if breturns_year_month.empty:
breturns_val.append(0)
else:
breturns_val.append(breturns_year_month_val.values[-1])
breturns_month_val.append(breturns_val)
monthly_returns = pd.DataFrame(
creturns_month_val,
index=sorted(list(set(portfolio_returns.index.year))),
columns=[
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
],
)
bench_monthly_returns = pd.DataFrame(
breturns_month_val,
index=sorted(list(set(benchmark_returns.index.year))),
columns=[
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec",
],
)
return monthly_returns, bench_monthly_returns
@log_start_end(log=logger)
def get_daily_returns(
portfolio_engine: PortfolioEngine,
window: str = "all",
) -> pd.DataFrame:
"""Get daily returns
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
interval to compare cumulative returns and benchmark
Returns
-------
pd.DataFrame
DataFrame with daily returns
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.dret(p)
"""
portfolio_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.returns, window
)
benchmark_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, window
)
df = portfolio_returns.to_frame()
df = df.join(benchmark_returns)
df.index = df.index.date
df.columns = ["portfolio", "benchmark"]
return df
def join_allocation(
portfolio: pd.DataFrame, benchmark: pd.DataFrame, column: str
) -> pd.DataFrame:
"""Help method to join portfolio and benchmark allocation by column
Parameters
----------
portfolio: pd.DataFrame
Portfolio allocation
benchmark: pd.DataFrame
Benchmark allocation
column: str
Column to join DataFrames
Returns
-------
pd.DataFrame
DataFrame with portfolio and benchmark allocations
"""
combined = pd.merge(portfolio, benchmark, on=column, how="left")
combined["Difference"] = combined["Portfolio"] - combined["Benchmark"]
combined = combined.replace(np.nan, "-")
combined = combined.replace(0, "-")
return combined
def get_distribution_returns(
portfolio_engine: PortfolioEngine,
window: str = "all",
) -> pd.DataFrame:
"""Display daily returns
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
interval to compare cumulative returns and benchmark
Returns
-------
pd.DataFrame
DataFrame of returns distribution
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.distr(p)
"""
portfolio_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.returns, window
)
benchmark_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, window
)
df = pd.DataFrame(portfolio_returns).join(pd.DataFrame(benchmark_returns))
df.columns.values[0] = "portfolio"
df.columns.values[1] = "benchmark"
return df
@log_start_end(log=logger)
def get_maximum_drawdown(
portfolio_engine: PortfolioEngine, is_returns: bool = False
) -> Tuple[pd.DataFrame, pd.Series]:
"""Calculate the drawdown (MDD) of historical series. Note that the calculation is done
on cumulative returns (or prices). The definition of drawdown is
DD = (current value - rolling maximum) / rolling maximum
Parameters
----------
data: pd.Series
Series of input values
is_returns: bool
Flag to indicate inputs are returns
Returns
-------
pd.Series
Holdings series
pd.Series
Drawdown series
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.maxdd(p)
"""
holdings: pd.Series = portfolio_engine.portfolio_value
if is_returns:
holdings = (1 + holdings).cumprod()
rolling_max = holdings.cummax()
drawdown = (holdings - rolling_max) / rolling_max
return holdings, drawdown
def get_rolling_volatility(
portfolio_engine: PortfolioEngine, window: str = "1y"
) -> pd.DataFrame:
"""Get rolling volatility
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
Rolling window size to use
Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y
Returns
-------
pd.DataFrame
Rolling volatility DataFrame
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.rvol(p)
"""
portfolio_rvol = metrics_model.rolling_volatility(portfolio_engine.returns, window)
if portfolio_rvol.empty:
return pd.DataFrame()
benchmark_rvol = metrics_model.rolling_volatility(
portfolio_engine.benchmark_returns, window
)
if benchmark_rvol.empty:
return pd.DataFrame()
df = pd.DataFrame(portfolio_rvol).join(pd.DataFrame(benchmark_rvol))
df.columns.values[0] = "portfolio"
df.columns.values[1] = "benchmark"
return df
def get_rolling_sharpe(
portfolio_engine: pd.DataFrame, risk_free_rate: float = 0, window: str = "1y"
) -> pd.DataFrame:
"""Get rolling sharpe ratio
Parameters
----------
portfolio_returns : pd.Series
Series of portfolio returns
risk_free_rate : float
Risk free rate
window : str
Rolling window to use
Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y
Returns
-------
pd.DataFrame
Rolling sharpe ratio DataFrame
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.rsharpe(p)
"""
portfolio_rsharpe = metrics_model.rolling_sharpe(
portfolio_engine.returns, risk_free_rate, window
)
if portfolio_rsharpe.empty:
return pd.DataFrame()
benchmark_rsharpe = metrics_model.rolling_sharpe(
portfolio_engine.benchmark_returns, risk_free_rate, window
)
if benchmark_rsharpe.empty:
return pd.DataFrame()
df = pd.DataFrame(portfolio_rsharpe).join(pd.DataFrame(benchmark_rsharpe))
df.columns.values[0] = "portfolio"
df.columns.values[1] = "benchmark"
return df
def get_rolling_sortino(
portfolio_engine: PortfolioEngine,
risk_free_rate: float = 0,
window: str = "1y",
) -> pd.DataFrame:
"""Get rolling sortino
Parameters
----------
portfolio : PortfolioEngine
PortfolioEngine object
window: str
interval for window to consider
Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y
risk_free_rate: float
Value to use for risk free rate in sharpe/other calculations
Returns
-------
pd.DataFrame
Rolling sortino ratio DataFrame
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.rsort(p)
"""
portfolio_rsortino = metrics_model.rolling_sortino(
portfolio_engine.returns, risk_free_rate, window
)
if portfolio_rsortino.empty:
return pd.DataFrame()
benchmark_rsortino = metrics_model.rolling_sortino(
portfolio_engine.benchmark_returns, risk_free_rate, window
)
if benchmark_rsortino.empty:
return pd.DataFrame()
df = pd.DataFrame(portfolio_rsortino).join(pd.DataFrame(benchmark_rsortino))
df.columns.values[0] = "portfolio"
df.columns.values[1] = "benchmark"
return df
@log_start_end(log=logger)
def get_rolling_beta(
portfolio_engine: PortfolioEngine,
window: str = "1y",
) -> pd.DataFrame:
"""Get rolling beta using portfolio and benchmark returns
Parameters
----------
portfolio : PortfolioEngine
PortfolioEngine object
window: string
Interval used for rolling values.
Possible options: mtd, qtd, ytd, 1d, 5d, 10d, 1m, 3m, 6m, 1y, 3y, 5y, 10y.
Returns
-------
pd.DataFrame
DataFrame of the portfolio's rolling beta
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.rbeta(p)
"""
df = metrics_model.rolling_beta(
portfolio_engine.returns, portfolio_engine.benchmark_returns, window
)
return df
def get_summary(
portfolio_engine: PortfolioEngine,
window: str = "all",
risk_free_rate: float = 0,
) -> pd.DataFrame:
"""Get portfolio and benchmark returns summary
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
interval to compare cumulative returns and benchmark
risk_free_rate : float
Risk free rate for calculations
Returns
-------
pd.DataFrame
DataFrame with portfolio and benchmark returns summary
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.summary(p)
"""
portfolio_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.returns, window
)
benchmark_returns = portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, window
)
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 Drawdown": [
metrics_model.maximum_drawdown(portfolio_returns),
metrics_model.maximum_drawdown(benchmark_returns),
],
"Sharpe ratio": [
metrics_model.sharpe_ratio(portfolio_returns, risk_free_rate),
metrics_model.sharpe_ratio(benchmark_returns, risk_free_rate),
],
"Sortino ratio": [
metrics_model.sortino_ratio(portfolio_returns, risk_free_rate),
metrics_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"]
)
summary["Difference"] = summary["Portfolio"] - summary["Benchmark"]
summary.loc["Volatility"] = summary.loc["Volatility"].apply("{:.2%}".format)
summary.loc["Maximum Drawdown"] = summary.loc["Maximum Drawdown"].apply(
"{:.2%}".format
)
summary.loc["R2 Score"] = summary.loc["R2 Score"].apply("{:.2%}".format)
return summary
@log_start_end(log=logger)
def get_assets_allocation(
portfolio_engine: PortfolioEngine,
tables: bool = False,
limit: int = 10,
recalculate: bool = False,
) -> Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]]:
"""Display portfolio asset allocation compared to the benchmark
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
tables: bool
Whether to include separate allocation tables
limit: int
The amount of assets you wish to show, by default this is set to 10
recalculate: bool
Flag to force recalculate allocation if already exists
Returns
-------
Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]]
DataFrame with combined allocation plus individual allocation if tables is `True`.
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.alloc.assets(p)
"""
portfolio_engine.calculate_allocation(category="Asset", recalculate=recalculate)
benchmark_allocation = portfolio_engine.benchmark_assets_allocation.iloc[:limit]
portfolio_allocation = portfolio_engine.portfolio_assets_allocation.iloc[:limit]
combined = join_allocation(portfolio_allocation, benchmark_allocation, "Symbol")
if tables:
return combined, portfolio_allocation, benchmark_allocation
return combined
def get_sectors_allocation(
portfolio_engine: PortfolioEngine,
limit: int = 10,
tables: bool = False,
recalculate: bool = False,
):
"""Display portfolio sector allocation compared to the benchmark
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
tables: bool
Whether to include separate allocation tables
limit: int
The amount of assets you wish to show, by default this is set to 10
recalculate: bool
Flag to force recalculate allocation if already exists
Returns
-------
Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]]
DataFrame with combined allocation plus individual allocation if tables is `True`.
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.alloc.sectors(p)
"""
portfolio_engine.calculate_allocation(category="Sector", recalculate=recalculate)
benchmark_allocation = portfolio_engine.benchmark_sectors_allocation.iloc[:limit]
portfolio_allocation = portfolio_engine.portfolio_sectors_allocation.iloc[:limit]
combined = join_allocation(portfolio_allocation, benchmark_allocation, "Sector")
if tables:
return combined, portfolio_allocation, benchmark_allocation
return combined
def get_countries_allocation(
portfolio_engine: PortfolioEngine,
limit: int = 10,
tables: bool = False,
recalculate: bool = False,
):
"""Display portfolio country allocation compared to the benchmark
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
tables: bool
Whether to include separate allocation tables
limit: int
The amount of assets you wish to show, by default this is set to 10
recalculate: bool
Flag to force recalculate allocation if already exists
Returns
-------
Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]]
DataFrame with combined allocation plus individual allocation if tables is `True`.
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.alloc.countries(p)
"""
portfolio_engine.calculate_allocation(category="Country", recalculate=recalculate)
benchmark_allocation = portfolio_engine.benchmark_countries_allocation.iloc[:limit]
portfolio_allocation = portfolio_engine.portfolio_countries_allocation.iloc[:limit]
combined = join_allocation(portfolio_allocation, benchmark_allocation, "Country")
if tables:
return combined, portfolio_allocation, benchmark_allocation
return combined
def get_regions_allocation(
portfolio_engine: PortfolioEngine,
limit: int = 10,
tables: bool = False,
recalculate: bool = False,
):
"""Display portfolio region allocation compared to the benchmark
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
tables: bool
Whether to include separate allocation tables
limit: int
The amount of assets you wish to show, by default this is set to 10
recalculate: bool
Flag to force recalculate allocation if already exists
Returns
-------
Union[pd.DataFrame, Tuple[pd.DataFrame, pd.DataFrame, pd.DataFrame]]
DataFrame with combined allocation plus individual allocation if tables is `True`.
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.alloc.regions(p)
"""
portfolio_engine.calculate_allocation(category="Region", recalculate=recalculate)
benchmark_allocation = portfolio_engine.benchmark_regions_allocation.iloc[:limit]
portfolio_allocation = portfolio_engine.portfolio_regions_allocation.iloc[:limit]
combined = join_allocation(portfolio_allocation, benchmark_allocation, "Region")
if tables:
return combined, portfolio_allocation, benchmark_allocation
return combined
@log_start_end(log=logger)
def get_r2_score(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get R2 Score for portfolio and benchmark selected
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame with R2 Score between portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.rsquare(p)
"""
vals = list()
for period in PERIODS:
vals.append(
round(
r2_score(
portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
),
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
),
),
3,
)
)
return pd.DataFrame(vals, index=PERIODS, columns=["R2 Score"])
@log_start_end(log=logger)
def get_skewness(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get skewness for portfolio and benchmark selected
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame with skewness for portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.skew(p)
"""
vals = list()
for period in PERIODS:
vals.append(
[
round(
scipy.stats.skew(
portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
)
),
3,
),
round(
scipy.stats.skew(
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
)
),
3,
),
]
)
return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"])
@log_start_end(log=logger)
def get_kurtosis(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get kurtosis for portfolio and benchmark selected
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame with kurtosis for portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.kurtosis(p)
"""
vals = list()
for period in PERIODS:
vals.append(
[
round(
scipy.stats.kurtosis(
portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
)
),
3,
),
round(
scipy.stats.skew(
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
)
),
3,
),
]
)
return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"])
@log_start_end(log=logger)
def get_stats(portfolio_engine: PortfolioEngine, window: str = "all") -> pd.DataFrame:
"""Get stats for portfolio and benchmark selected based on a certain interval
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window : str
interval to consider. Choices are: mtd, qtd, ytd, 3m, 6m, 1y, 3y, 5y, 10y, all
Returns
-------
pd.DataFrame
DataFrame with overall stats for portfolio and benchmark for a certain period
"""
df = (
portfolio_helper.filter_df_by_period(portfolio_engine.returns, window)
.describe()
.to_frame()
.join(
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, window
).describe()
)
)
df.columns = ["Portfolio", "Benchmark"]
return df
@log_start_end(log=logger)
def get_volatility(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get volatility for portfolio and benchmark selected
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame with volatility for portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.volatility(p)
"""
vals = list()
for period in PERIODS:
port_rets = portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
)
bench_rets = portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
)
vals.append(
[
round(
port_rets.std() * (len(port_rets) ** 0.5),
3,
),
round(
bench_rets.std() * (len(bench_rets) ** 0.5),
3,
),
]
)
return pd.DataFrame(
vals,
index=PERIODS,
columns=["Portfolio [%]", "Benchmark [%]"],
)
@log_start_end(log=logger)
def get_sharpe_ratio(
portfolio_engine: PortfolioEngine, risk_free_rate: float = 0
) -> pd.DataFrame:
"""Get sharpe ratio for portfolio and benchmark selected
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
risk_free_rate: float
Risk free rate value
Returns
-------
pd.DataFrame
DataFrame with sharpe ratio for portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.sharpe(p)
"""
vals = list()
for period in PERIODS:
vals.append(
[
round(
metrics_model.sharpe_ratio(
portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
),
risk_free_rate,
),
3,
),
round(
metrics_model.sharpe_ratio(
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
),
risk_free_rate,
),
3,
),
]
)
return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"])
@log_start_end(log=logger)
def get_sortino_ratio(
portfolio_engine: PortfolioEngine, risk_free_rate: float = 0
) -> pd.DataFrame:
"""Get sortino ratio for portfolio and benchmark selected
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
risk_free_rate: float
Risk free rate value
Returns
-------
pd.DataFrame
DataFrame with sortino ratio for portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.sortino(p)
"""
vals = list()
for period in PERIODS:
vals.append(
[
round(
metrics_model.sortino_ratio(
portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
),
risk_free_rate,
),
3,
),
round(
metrics_model.sortino_ratio(
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
),
risk_free_rate,
),
3,
),
]
)
return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"])
@log_start_end(log=logger)
def get_maximum_drawdown_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get maximum drawdown ratio for portfolio and benchmark selected
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame with maximum drawdown for portfolio and benchmark for different periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.maxdrawdown(p)
"""
vals = list()
for period in PERIODS:
vals.append(
[
round(
metrics_model.maximum_drawdown(
portfolio_helper.filter_df_by_period(
portfolio_engine.returns, period
)
),
3,
),
round(
metrics_model.maximum_drawdown(
portfolio_helper.filter_df_by_period(
portfolio_engine.benchmark_returns, period
)
),
3,
),
]
)
return pd.DataFrame(vals, index=PERIODS, columns=["Portfolio", "Benchmark"])
@log_start_end(log=logger)
def get_gaintopain_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get Pain-to-Gain ratio based on historical data
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of the portfolio's gain-to-pain ratio
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.gaintopain(p)
"""
gtp_period_df = metrics_model.get_gaintopain_ratio(
portfolio_engine.historical_trade_data,
portfolio_engine.benchmark_trades,
portfolio_engine.benchmark_returns,
)
return gtp_period_df
@log_start_end(log=logger)
def get_tracking_error(
portfolio_engine: PortfolioEngine, window: int = 252
) -> Tuple[pd.DataFrame, pd.Series]:
"""Get tracking error
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window: int
Interval used for rolling values
Returns
-------
pd.DataFrame
DataFrame of tracking errors during different time windows
pd.Series
Series of rolling tracking error
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.trackerr(p)
"""
trackr_period_df, trackr_rolling = metrics_model.get_tracking_error(
portfolio_engine.returns, portfolio_engine.benchmark_returns, window
)
return trackr_period_df, trackr_rolling
@log_start_end(log=logger)
def get_information_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get information ratio
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of the information ratio during different time periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.information(p)
"""
ir_period_df = metrics_model.get_information_ratio(
portfolio_engine.returns,
portfolio_engine.historical_trade_data,
portfolio_engine.benchmark_trades,
portfolio_engine.benchmark_returns,
)
return ir_period_df
@log_start_end(log=logger)
def get_tail_ratio(
portfolio_engine: PortfolioEngine, window: int = 252
) -> Tuple[pd.DataFrame, pd.Series, pd.Series]:
"""Get tail ratio
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window: int
Interval used for rolling values
Returns
-------
pd.DataFrame
DataFrame of the portfolios and the benchmarks tail ratio during different time windows
pd.Series
Series of the portfolios rolling tail ratio
pd.Series
Series of the benchmarks rolling tail ratio
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.tail(p)
"""
tailr_period_df, portfolio_tr, benchmark_tr = metrics_model.get_tail_ratio(
portfolio_engine.returns, portfolio_engine.benchmark_returns, window
)
return tailr_period_df, portfolio_tr, benchmark_tr
@log_start_end(log=logger)
def get_common_sense_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get common sense ratio
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of the portfolios and the benchmarks common sense ratio during different time periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.commonsense(p)
"""
csr_period_df = metrics_model.get_common_sense_ratio(
portfolio_engine.returns,
portfolio_engine.historical_trade_data,
portfolio_engine.benchmark_trades,
portfolio_engine.benchmark_returns,
)
return csr_period_df
@log_start_end(log=logger)
def get_jensens_alpha(
portfolio_engine: PortfolioEngine, risk_free_rate: float = 0, window: str = "1y"
) -> Tuple[pd.DataFrame, pd.Series]:
"""Get jensen's alpha
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window: str
Interval used for rolling values
risk_free_rate: float
Risk free rate
Returns
-------
pd.DataFrame
DataFrame of jensens's alpha during different time windows
pd.Series
Series of jensens's alpha data
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.jensens(p)
"""
ja_period_df, ja_rolling = metrics_model.jensens_alpha(
portfolio_engine.returns,
portfolio_engine.historical_trade_data,
portfolio_engine.benchmark_trades,
portfolio_engine.benchmark_returns,
risk_free_rate,
window,
)
return ja_period_df, ja_rolling
@log_start_end(log=logger)
def get_calmar_ratio(
portfolio_engine: PortfolioEngine, window: int = 756
) -> Tuple[pd.DataFrame, pd.Series]:
"""Get calmar ratio
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
window: int
Interval used for rolling values
Returns
-------
pd.DataFrame
DataFrame of calmar ratio of the benchmark and portfolio during different time periods
pd.Series
Series of calmar ratio data
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.calmar(p)
"""
cr_period_df, cr_rolling = metrics_model.get_calmar_ratio(
portfolio_engine.returns,
portfolio_engine.historical_trade_data,
portfolio_engine.benchmark_trades,
portfolio_engine.benchmark_returns,
window,
)
return cr_period_df, cr_rolling
@log_start_end(log=logger)
def get_kelly_criterion(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get kelly criterion
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of kelly criterion of the portfolio during different time periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.kelly(p)
"""
kc_period_df = metrics_model.get_kelly_criterion(
portfolio_engine.returns, portfolio_engine.portfolio_trades
)
return kc_period_df
@log_start_end(log=logger)
def get_payoff_ratio(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get payoff ratio
Returns
-------
pd.DataFrame
DataFrame of payoff ratio of the portfolio during different time periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.payoff(p)
During some time periods there were no losing trades. Thus some values could not be calculated.
"""
pr_period_ratio = metrics_model.get_payoff_ratio(portfolio_engine.portfolio_trades)
return pr_period_ratio
@log_start_end(log=logger)
def get_profit_factor(portfolio_engine: PortfolioEngine) -> pd.DataFrame:
"""Get profit factor
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
Returns
-------
pd.DataFrame
DataFrame of profit factor of the portfolio during different time periods
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.metric.profitfactor(p)
During some time periods there were no losing trades. Thus some values could not be calculated.
"""
pf_period_df = metrics_model.get_profit_factor(portfolio_engine.portfolio_trades)
return pf_period_df
@log_start_end(log=logger)
def get_performance_vs_benchmark(
portfolio_engine: PortfolioEngine,
show_all_trades: bool = False,
) -> pd.DataFrame:
"""Get portfolio performance vs the benchmark
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
show_all_trades: bool
Whether to also show all trades made and their performance (default is False)
Returns
-------
pd.DataFrame
DataFrame with portfolio performance vs the benchmark
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.perf(p)
"""
portfolio_trades = portfolio_engine.portfolio_trades
benchmark_trades = portfolio_engine.benchmark_trades
portfolio_trades.index = pd.to_datetime(portfolio_trades["Date"].values)
benchmark_trades.index = pd.to_datetime(benchmark_trades["Date"].values)
if show_all_trades:
# Combine DataFrames
combined = pd.concat(
[
portfolio_trades[
["Date", "Ticker", "Portfolio Value", "Portfolio % Return"]
],
benchmark_trades[["Benchmark Value", "Benchmark % Return"]],
],
axis=1,
)
# Calculate alpha
combined["Alpha"] = (
combined["Portfolio % Return"] - combined["Benchmark % Return"]
)
combined["Date"] = pd.to_datetime(combined["Date"]).dt.date
return combined
# 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"],
)
return totals.replace(0, "-")
@log_start_end(log=logger)
def get_var(
portfolio_engine: PortfolioEngine,
use_mean: bool = False,
adjusted_var: bool = False,
student_t: bool = False,
percentile: float = 99.9,
) -> pd.DataFrame:
"""Get portfolio VaR
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
use_mean: bool
if one should use the data mean return
adjusted_var: bool
if one should have VaR adjusted for skew and kurtosis (Cornish-Fisher-Expansion)
student_t: bool
If one should use the student-t distribution
percentile: float
var percentile (%)
Returns
-------
pd.DataFrame
DataFrame with portfolio VaR
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.var(p)
"""
return qa_model.get_var(
data=portfolio_engine.returns,
use_mean=use_mean,
adjusted_var=adjusted_var,
student_t=student_t,
percentile=percentile,
portfolio=True,
)
@log_start_end(log=logger)
def get_es(
portfolio_engine: PortfolioEngine,
use_mean: bool = False,
distribution: str = "normal",
percentile: float = 99.9,
) -> pd.DataFrame:
"""Get portfolio expected shortfall
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
use_mean:
if one should use the data mean return
distribution: str
choose distribution to use: logistic, laplace, normal
percentile: float
es percentile (%)
Returns
-------
pd.DataFrame
DataFrame with portfolio expected shortfall
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.es(p)
"""
return qa_model.get_es(
data=portfolio_engine.returns,
use_mean=use_mean,
distribution=distribution,
percentile=percentile,
portfolio=True,
)
@log_start_end(log=logger)
def get_omega(
portfolio_engine: PortfolioEngine,
threshold_start: float = 0,
threshold_end: float = 1.5,
) -> pd.DataFrame:
"""Get omega ratio
Parameters
----------
portfolio_engine: PortfolioEngine
PortfolioEngine class instance, this will hold transactions and perform calculations.
Use `portfolio.load` to create a PortfolioEngine.
threshold_start: float
annualized target return threshold start of plotted threshold range
threshold_end: float
annualized target return threshold end of plotted threshold range
Returns
-------
pd.DataFrame
DataFrame with portfolio omega ratio
Examples
--------
>>> from openbb_terminal.sdk import openbb
>>> p = openbb.portfolio.load("openbb_terminal/miscellaneous/portfolio_examples/holdings/example.csv")
>>> output = openbb.portfolio.om(p)
"""
return qa_model.get_omega(
data=portfolio_engine.returns,
threshold_start=threshold_start,
threshold_end=threshold_end,
)