Welcome to Perfana’s documentation!¶
Performance Analytics (Perfana) is a toolbox to calculate various analytics and statistics for financial engineering. It also contains shorthands for plotting common charts.
Perfana package¶
API Reference¶
The exact API of all functions and classes, as given by the docstrings. The API documents expected types and allowed features for all functions, and all parameters available for the algorithms.
Core¶
Core API applies to a series of methods that apply to a pandas DataFrame
or Series
or iterable TimeSeries like object.
Core API¶
Core API applies to a series of methods that apply to a pandas DataFrame or Series or iterable TimeSeries like object.
Relative¶
Relative API contains a series of functions that apply to a pandas DataFrame or Series or iterable TimeSeries like object to calculate various forms of relative comparison measures.
-
perfana.core.relative.
correlation_measure
(portfolio, benchmark, duration='monthly', *, is_returns=False, date_as_index=True)[source]¶ Computes the correlation measure through time. The data is assumed to be daily. If the benchmark is a single series, a single TimeSeriesData will be returned. Otherwise, a dictionary of TimeSeries will be returned where the keys are each individual benchmark
- Parameters
portfolio (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The portfolio values vector or matrixbenchmark (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The benchmark values vector or matrixduration (
Union
[str
,int
]) – Duration to calculate the relative price index with. Either a string or positive integer value can be specified. Supported string values are ‘day’, ‘week’, ‘month’, ‘quarter’, ‘semi-annual’ and ‘year’is_returns – Set this to true if the portfolio and benchmark values are in “returns” instead of raw values (i.e. prices or raw index value)
date_as_index – If true, returns the date as the dataframe’s index. Otherwise, the date is placed as a column in the dataframe
- Returns
A DataFrame of the correlation measure between the assets in the portfolio against the benchmark If multiple series are included in the benchmark, returns a dictionary where the keys are the benchmarks’ name and the values are the correlation measure of the portfolio against that particular benchmark
- Return type
TimeSeriesData or dict of TimeSeriesData
Examples
>>> from perfana.datasets import load_etf >>> from perfana.core import correlation_measure >>> etf = load_etf().dropna() >>> returns = etf.iloc[:, 1:] >>> benchmark = etf.iloc[:, 0] >>> correlation_measure(returns, benchmark, 'monthly').head() BND VTI VWO Date 2007-05-10 -0.384576 0.890783 0.846000 2007-05-11 -0.525299 0.911693 0.857288 2007-05-14 -0.482180 0.912002 0.855114 2007-05-15 -0.439073 0.913992 0.842561 2007-05-16 -0.487110 0.899859 0.837781
-
perfana.core.relative.
relative_price_index
(portfolio, benchmark, duration='monthly', *, is_returns=False, date_as_index=True)[source]¶ Computes the relative price index through time. The data is assumed to be daily. If the benchmark is a single series, a single TimeSeriesData will be returned. Otherwise, a dictionary of TimeSeries will be returned where the keys are each individual benchmark
Notes
The relative price index at a particular time \(t\) for an asset \(a\) against its benchmark \(b\) is given by
\[RP_{a, t} = r_{a, t - d} - r_{b, t - d}\]where d is the duration. For example, if the duration is ‘monthly’, \(d\) will be 22 days.
- Parameters
portfolio (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The portfolio values vector or matrixbenchmark (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The benchmark values vector or matrixduration (
Union
[str
,int
]) – Duration to calculate the relative price index with. Either a string or positive integer value can be specified. Supported string values are ‘day’, ‘week’, ‘month’, ‘quarter’, ‘semi-annual’ and ‘year’is_returns – Set this to true if the portfolio and benchmark values are in “returns” instead of raw values (i.e. prices or raw index value)
date_as_index – If true, returns the date as the dataframe’s index. Otherwise, the date is placed as a column in the dataframe
- Returns
A DataFrame of the relative price index between the assets in the portfolio against the benchmark If multiple series are included in the benchmark, returns a dictionary where the keys are the benchmarks’ name and the values are the relative price index of the portfolio against that particular benchmark
- Return type
TimeSeriesData or dict of TimeSeriesData
Examples
>>> from perfana.datasets import load_etf >>> from perfana.core import relative_price_index >>> etf = load_etf().dropna() >>> returns = etf.iloc[:, 1:] >>> benchmark = etf.iloc[:, 0] >>> relative_price_index(returns, benchmark, 'monthly').head() BND VTI VWO Date 2007-05-10 -0.016000 0.009433 0.000458 2007-05-11 -0.031772 0.008626 0.013009 2007-05-14 -0.016945 0.014056 0.008658 2007-05-15 -0.002772 0.020824 0.018758 2007-05-16 0.002791 0.025402 0.028448
Returns¶
Returns API contains a series of functions that apply to a pandas DataFrame or Series or iterable TimeSeries like object to calculate various forms of returns.
-
perfana.core.returns.
annualized_returns
(r, freq=None, geometric=True)[source]¶ Calculates the annualized returns from the data
The formula for annualized geometric returns is formulated by raising the compound return to the number of periods in a year, and taking the root to the number of total observations:
\[\prod_i^N(1 + r_i)^{\frac{s}{N}} - 1\]where \(s\) is the number of observations in a year, and \(N\) is the total number of observations.
For simple returns (geometric=FALSE), the formula is:
\[\frac{s}{N} \sum^N_i r_i\]- Parameters
r (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Numeric returns series or data framefreq (
Optional
[str
]) – Frequency of the data. Use one of daily, weekly, monthly, quarterly, semi-annually, yearlygeometric – If True, calculates the geometric returns. Otherwise, calculates the arithmetic returns
- Returns
Annualized returns
- Return type
float or Series
Examples
>>> from perfana.datasets import load_etf >>> from perfana.core import annualized_returns # Get returns starting from the date where all etf has data >>> etf = load_etf().dropna().pa.to_returns().dropna() VBK 0.091609 BND 0.036224 VTI 0.081203 VWO 0.027670 dtype: float64 >>> annualized_returns(etf.VWO) 0.02767037698144148
-
perfana.core.returns.
excess_returns
(ra, rb, freq=None, geometric=True)[source]¶ An average annualized excess return is convenient for comparing excess returns
Excess returns is calculated by first annualizing the asset returns and benchmark returns stream. See the docs for annualized_returns() for more details. The geometric returns formula is:
\[r_g = \frac{r_a - r_b}{1 + r_b}\]The arithmetic excess returns formula is:
\[r_g = r_a - r_b\]Returns calculation will be truncated by the one with the shorter length. Also, annualized returns are calculated by the geometric annualized returns in both cases
- Parameters
ra (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The assets returns vector or matrixrb (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The benchmark returns. If this is a vector and the asset returns is a matrix, then all assets returns (columns) will be compared against this single benchmark. Otherwise, if this is a matrix, then assets will be compared to each individual benchmark (i.e. column for column)freq (
Optional
[str
]) – Frequency of the data. Use one of [daily, weekly, monthly, quarterly, semi-annually, yearly]geometric – If True, calculates the geometric excess returns. Otherwise, calculates the arithmetic excess returns
- Returns
Excess returns of each strategy against benchmark
- Return type
TimeSeriesData
Examples
>>> from perfana.datasets import load_etf >>> from perfana.core import excess_returns # Get returns starting from the date where all etf has data >>> etf = load_etf().dropna().pa.to_returns().dropna() >>> excess_returns(etf, etf.VBK) VBK 0.000000 BND -0.050737 VTI -0.009533 VWO -0.058573 dtype: float64
-
perfana.core.returns.
relative_returns
(ra, rb, prefixes=('PRT', 'BMK'))[source]¶ Calculates the ratio of the cumulative performance for two assets through time
- Parameters
ra (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The assets returns vector or matrixrb (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The benchmark returnsprefixes – Prefix to apply to overlapping column names in the left and right side, respectively. This is also applied when the column name is an integer (i.e. 0 -> PRT_0). It is the default name of the Series data if there are no name to the Series
- Returns
Returns a DataFrame of the cumulative returns ratio between 2 asset classes. Returns a Series if there is only 2 compared classes.
- Return type
TimeSeriesData
Examples
>>> from perfana.datasets import load_etf >>> from perfana.core import relative_returns # Get returns starting from the date where all etf has data >>> etf = load_etf().dropna().pa.to_returns().dropna() >>> relative_returns(etf.tail(), etf.VBK.tail()) VBK/VBK BND/VBK VTI/VBK VWO/VBK Date 2019-02-25 1.0 0.996027 0.997856 1.009737 2019-02-26 1.0 1.004013 1.002591 1.013318 2019-02-27 1.0 0.997005 0.997934 1.000389 2019-02-28 1.0 1.001492 1.001461 0.998348 2019-03-01 1.0 0.987385 0.997042 0.988521
Risk¶
Risk API contains a series of functions that apply to a pandas DataFrame or Series or iterable TimeSeries like object to calculate various forms of risk.
-
perfana.core.risk.
drawdown
(data, weights=None, geometric=True, rebalance=True)[source]¶ Calculates the drawdown at each time instance.
If data is DataFrame-like, weights must be specified. If data is Series-like, weights can be left empty.
- Parameters
data (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The assets returns vector or matrixweights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
,None
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.geometric – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
- Returns
Drawdown at each time instance
- Return type
Series
Examples
>>> from perfana.datasets import load_hist >>> from perfana.core import drawdown >>> hist = load_hist().iloc[:, :7] >>> weights = [0.25, 0.18, 0.24, 0.05, 0.04, 0.13, 0.11] >>> drawdown(hist, weights).min() -0.4007984968456346 >>> drawdown(hist.iloc[:, 0]).min() -0.5491340502573534
import matplotlib.pyplot as plt from perfana.core import drawdown from perfana.datasets import load_hist data = load_hist().iloc[:, :7] weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] dd = drawdown(data, weights) plt.plot(dd.index, dd) plt.title("Drawdown") plt.xlabel("Time") plt.ylabel("Drawdown Depth") plt.show()
-
perfana.core.risk.
drawdown_summary
(data, weights=None, geometric=True, rebalance=True, *, top=5)[source]¶ A summary of each drawdown instance. Output is ranked by depth of the drawdown.
If data is DataFrame-like, weights must be specified. If data is Series-like, weights can be left empty.
- Parameters
data (
Union
[DataFrame
,Iterable
[Union
[int
,float
]],ndarray
,Series
]) – The assets returns vector or matrixweights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
,None
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.geometric – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
top (
Optional
[int
]) – If None, returns all episodes. If specified, returns the top n episodes ranked by the depth of drawdown.
- Returns
A data frame summarizing each drawdown episode
- Return type
DataFrame
Examples
>>> from perfana.datasets import load_hist >>> from perfana.core import drawdown_summary >>> hist = load_hist().iloc[:, :7] >>> weights = [0.25, 0.18, 0.24, 0.05, 0.04, 0.13, 0.11] >>> drawdown_summary(hist, weights) Start Trough End Drawdown Length ToTrough Recovery 0 2007-11-30 2009-02-28 2014-02-28 -0.400798 76 16 60 1 2000-04-30 2003-03-31 2004-02-29 -0.203652 47 36 11 2 1990-01-31 1990-11-30 1991-05-31 -0.150328 17 11 6 3 1998-04-30 1998-10-31 1999-06-30 -0.149830 15 7 8 4 1994-02-28 1995-03-31 1996-01-31 -0.132766 24 14 10 >>> drawdown_summary(hist.iloc[:, 0]) Start Trough End Drawdown Length ToTrough Recovery 0 2007-11-30 2009-02-28 2014-05-31 -0.549134 79 16 63 1 2000-04-30 2003-03-31 2006-12-31 -0.474198 81 36 45 2 1990-01-31 1990-09-30 1994-01-31 -0.286489 49 9 40 3 1998-08-31 1998-09-30 1999-01-31 -0.148913 6 2 4 4 2018-10-31 2018-12-31 2019-03-31 -0.130014 6 3 3
Monte Carlo¶
Monte Carlo API applies to a series of methods that apply to a 3 dimensional data cube where the dimensions represent the time, trials and assets respectively. For example, if the simulated cube projects 10 years of monthly data for 8 assets for 10000 trials (that is 10000 simulations), the cube will be a numpy array with shape (120, 10000, 8).
Monte Carlo API¶
Monte Carlo API applies to a series of methods that apply to a 3 dimensional data cube where the dimensions represent the time, trials and assets respectively. For example, if the simulated cube projects 10 years of monthly data for 8 assets for 10000 trials (that is 10000 simulations), the cube will be a numpy array with shape (120, 10000, 8).
Returns¶
-
perfana.monte_carlo.returns.
annualized_returns_m
(data, weights, freq, geometric=True, rebalance=True)[source]¶ Calculates the annualized returns from the Monte Carlo simulation
The formula for annualized geometric returns is formulated by raising the compound return to the number of periods in a year, and taking the root to the number of total observations. For the rebalance, geometric returns, the annualized returns is derived by:
\[\begin{split}&y = M / s \\ &\frac{1}{N}\sum^N_i \left[\prod_j^T \left(1 + \sum^A_k (r_{ijk} \cdot w_k \right) \right]^{\frac{1}{y}} - 1\end{split}\]where s is the number of observations in a year, and M is the total number of observations, N is the number of trials in the simulation, T is the number of trials in the simulation and A is the number of assets in the simulation.
For simple returns (geometric=FALSE), the formula for the rebalanced case is:
\[\frac{s}{NM} \left[\sum^N_i \sum^T_j \sum^A_k (r_{ijk} \cdot w_k) \right]\]- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.geometric (
bool
) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.
- Returns
Annualized returns of the portfolio
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import annualized_returns_m >>> cube = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> annualized_returns_m(cube, weights, 'month') 0.02111728739277985
-
perfana.monte_carlo.returns.
annualized_bmk_returns_m
(data, weights, bmk_weights, freq, geometric=True, rebalance=True)[source]¶ Calculates the returns of the portfolio relative to a benchmark portfolio.
The benchmark components must be placed after the portfolio components in the simulated returns cube.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.bmk_weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the benchmark portfolio.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.geometric (
bool
) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.
- Returns
The portfolio returns relative to the benchmark
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import annualized_bmk_returns_m >>> cube = load_cube() >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> bmk_weights = [0.65, 0.35] >>> freq = "quarterly" >>> annualized_bmk_returns_m(cube, weights, bmk_weights, freq) -0.006819613944426206
-
perfana.monte_carlo.returns.
annualized_quantile_returns_m
(data, weights, quantile, freq, geometric=True, rebalance=True, interpolation='midpoint')[source]¶ Compute the q-th quantile of the returns in the simulated data cube.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.quantile (
Union
[float
,Iterable
[float
]]) – Quantile or sequence of quantiles to compute, which must be between 0 and 1 inclusivefreq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.geometric (
bool
) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.interpolation –
This optional parameter specifies the interpolation method to use when the desired quantile lies between two data points
i < j
:linear:
i + (j - i) * fraction
, wherefraction
is the fractional part of the index surrounded byi
andj
.lower:
i
.higher:
j
.nearest:
i
orj
, whichever is nearest.midpoint:
(i + j) / 2
.
- Returns
The returns of the portfolio relative to the benchmark at the specified quantile
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import annualized_quantile_returns_m >>> cube = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> freq = "quarterly" >>> q = 0.25 >>> annualized_quantile_returns_m(cube, weights, q, freq) 0.005468353416130167 >>> q = [0.25, 0.75] >>> annualized_quantile_returns_m(cube, weights, q, freq) array([0.00546835, 0.03845033])
-
perfana.monte_carlo.returns.
annualized_bmk_quantile_returns_m
(data, weights, bmk_weights, quantile, freq, geometric=True, rebalance=True, interpolation='midpoint')[source]¶ Compares the annualized returns against a benchmark at the specified quantiles.
The benchmark components must be placed after the portfolio components in the simulated returns cube.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.bmk_weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the benchmark portfolio.quantile (
Union
[float
,Iterable
[float
]]) – Quantile or sequence of quantiles to compute, which must be between 0 and 1 inclusivefreq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.geometric (
bool
) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.interpolation –
This optional parameter specifies the interpolation method to use when the desired quantile lies between two data points
i < j
:linear:
i + (j - i) * fraction
, wherefraction
is the fractional part of the index surrounded byi
andj
.lower:
i
.higher:
j
.nearest:
i
orj
, whichever is nearest.midpoint:
(i + j) / 2
.
- Returns
The returns of the portfolio over the benchmark at the specified quantiles
- Return type
float or array_like of floats
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import annualized_bmk_quantile_returns_m >>> cube = load_cube() >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> bmk_weights = [0.65, 0.35] >>> freq = "quarterly" >>> q = 0.25 >>> annualized_bmk_quantile_returns_m(cube, weights, bmk_weights, q, freq) -0.010792419409674459 >>> q = [0.25, 0.75] >>> annualized_bmk_quantile_returns_m(cube, weights, bmk_weights, q, freq) array([-0.01079242, -0.0025487 ])
-
perfana.monte_carlo.returns.
returns_attr
(data, weights, freq, geometric=True, rebalance=True)[source]¶ Derives the returns attribution given a data cube and weights.
Notes
The return values are defined as follows:
- marginal
The absolute marginal contribution of the asset class towards the portfolio returns. It is essentially the percentage attribution multiplied by the portfolio returns.
- percentage
The percentage contribution of the asset class towards the portfolio returns. This number though named in percentage is actually in decimals. Thus 0.01 represents a 1% contribution.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.geometric (
bool
) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.
- Returns
A named tuple of marginal and percentage returns attribution respectively. The marginal attribution is the returns of the simulated data over time multiplied by the percentage attribution.
- Return type
Attribution
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import returns_attr >>> cube = load_cube()[..., :3] >>> weights = [0.33, 0.34, 0.33] >>> freq = "quarterly" >>> attr = returns_attr(cube, weights, freq) >>> attr.marginal array([0.00996204, 0.00733369, 0.00963802]) >>> attr.percentage array([0.36987203, 0.27228623, 0.35784174]) >>> attr.marginal is attr[0] True >>> attr.percentage is attr[1] True
-
perfana.monte_carlo.returns.
returns_distribution
(data, weights, freq=None, annualize=True, geometric=True, rebalance=True)[source]¶ Calculates the returns distribution of the simulation cube
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.freq (
Union
[str
,int
,None
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1. Ifannualize
is False,freq
can be ignored.annualize – If true, the returns distribution values are annualized
geometric (
bool
) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.
- Returns
A vector of the distribution of returns
- Return type
Array
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import returns_distribution >>> cube = load_cube()[..., :3] >>> weights = [0.33, 0.34, 0.33] >>> freq = "quarterly" >>> returns_distribution(cube, weights, freq).shape (1000,)
-
perfana.monte_carlo.returns.
returns_path
(data, weights, rebalance=True, quantile=None)[source]¶ Returns a matrix of the returns path of the portfolio.
The first axis represents the time and the second axis represents the trials.
If the
quantile
argument is specified, the specific quantile for each time period will be returned. Thus, if the 0.75 quantile is specified, it is the 75th quantile for each time period and not the path the 75th quantile in the terminal period took.- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.rebalance (
bool
) – If True, portfolio is assumed to be rebalanced at every step.quantile (
Union
[int
,float
,Iterable
[Union
[int
,float
]],ndarray
,Series
,None
]) – Quantile or sequence of quantiles to compute, which must be between 0 and 1 inclusive
- Returns
The returns path for the portfolio
- Return type
ndarray
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import returns_path >>> cube = load_cube()[..., :3] >>> weights = [0.33, 0.34, 0.33] >>> returns_path(cube, weights).shape (1000, 81) >>> returns_path(cube, weights, quantile=0.75).shape # 75th quantile (81, ) >>> returns_path(cube, weights, quantile=[0.25, 0.5, 0.75]).shape # 25th, 50th and 75th quantile (3, 81)
import matplotlib.pyplot as plt import numpy as np from perfana.datasets import load_cube from perfana.monte_carlo import returns_path data = load_cube()[..., :7] weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] quantile = [0.05, 0.25, 0.5, 0.75, 0.95] color = np.array([ (200, 200, 200), (143, 143, 143), (196, 8, 8), (143, 143, 143), (200, 200, 200), ]) / 255 paths = returns_path(data, weights, quantile=quantile) n, t = paths.shape x = np.arange(t) fig = plt.figure(figsize=(8, 6)) sp = fig.add_subplot(111) for i in reversed(range(n)): label = f"{int(quantile[i] * 100)}" sp.plot(x, paths[i], figure=fig, label=label, color=color[i]) sp.fill_between(x, paths[0], paths[-1], color=color[0]) sp.fill_between(x, paths[1], paths[-2], color=color[1]) sp.set_title("Cumulative returns path") sp.set_xlabel("Time Period") sp.set_ylabel("Returns") sp.grid() sp.legend(title="Percentile") fig.show()
Risk¶
-
perfana.monte_carlo.risk.
beta_m
(cov_or_data, weights, freq=None, aid=0)[source]¶ Derives the portfolio beta with respect to the specified asset class
Notes
The asset is identified by its index (aid) on the covariance matrix / simulated returns cube / weight vector. If a simulated returns data cube is given, the frequency of the data must be specified. In this case, the empirical covariance matrix would be used to derive the volatility.
- Parameters
cov_or_data (
ndarray
) – Covariance matrix or simulated returns data cube.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the covariance matrix shape or the simulated data’s last axis.freq (
Union
[str
,int
,None
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.aid – Asset index
- Returns
Portfolio beta with respect to asset class.
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import portfolio_cov, beta_m >>> data = load_cube()[..., :3] # first 3 asset classes only >>> weights = [0.33, 0.34, 0.33] >>> freq = 'quarterly' >>> dm_eq_id = 0 # calculate correlation with respect to developing markets equity >>> beta_m(data, weights, freq, dm_eq_id) 1.3047194776321622
-
perfana.monte_carlo.risk.
correlation_m
(cov_or_data, weights, freq=None, aid=0)[source]¶ Derives the portfolio correlation with respect to the specified asset class
Notes
The asset is identified by its index (aid) on the covariance matrix / simulated returns cube / weight vector. If a simulated returns data cube is given, the frequency of the data must be specified. In this case, the empirical covariance matrix would be used to derive the volatility.
- Parameters
cov_or_data (
ndarray
) – Covariance matrix or simulated returns data cube.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the covariance matrix shape or the simulated data’s last axis.freq (
Union
[str
,int
,None
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.aid – Asset index
- Returns
Portfolio correlation with respect to asset class
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import portfolio_cov, correlation_m >>> data = load_cube()[..., :3] # first 3 asset classes only >>> weights = [0.33, 0.34, 0.33] >>> freq = 'quarterly' >>> dm_eq_id = 0 # calculate correlation with respect to developing markets equity >>> correlation_m(data, weights, freq, dm_eq_id) 0.9642297301278216
-
perfana.monte_carlo.risk.
cvar_attr
(data, weights, alpha=0.95, rebalance=True, invert=True)[source]¶ Calculates the CVaR (Expected Shortfall) attribution for each asset class in the portfolio.
Notes
From a mathematical point of view, the alpha value (confidence level for calculation) should be taken at the negative extreme of the distribution. However, the default is set to ease the practitioner.
The return values are defined as follows:
- marginal
The absolute marginal contribution of the asset class towards the portfolio CVaR. It is essentially the percentage attribution multiplied by the portfolio CVaR.
- percentage
The percentage contribution of the asset class towards the portfolio CVaR. This number though named in percentage is actually in decimals. Thus 0.01 represents a 1% contribution.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.alpha – Confidence level for calculation.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
invert – Whether to invert the confidence interval level. See Notes.
- Returns
A named tuple of relative and absolute CVaR (expected shortfall) attribution respectively. The absolute attribution is the CVaR of the simulated data over time multiplied by the percentage attribution.
- Return type
Attribution
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import cvar_attr >>> cube = load_cube()[..., :3] >>> weights = [0.33, 0.34, 0.33] >>> attr = cvar_attr(cube, weights, alpha=0.95) >>> attr.marginal array([-0.186001 , -0.35758411, -0.20281477]) >>> attr.percentage array([0.24919752, 0.47907847, 0.27172401]) >>> attr.marginal is attr[0] True >>> attr.percentage is attr[1] True
-
perfana.monte_carlo.risk.
cvar_div_ratio
(data, weights, alpha=0.95, rebalance=True, invert=True)[source]¶ Calculates the CVaR (Expected Shortfall) tail diversification ratio of the portfolio
Notes
From a mathematical point of view, the alpha value (confidence level for calculation) should be taken at the negative extreme of the distribution. However, the default is set to ease the practitioner.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.alpha – Confidence level for calculation.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
invert – Whether to invert the confidence interval level. See Notes.
- Returns
CVaR (Expected Shortfall) tail diversification ratio
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import cvar_div_ratio >>> cube = load_cube()[..., :3] >>> weights = [0.33, 0.34, 0.33] >>> cvar_div_ratio(cube, weights) 0.8965390850633622
-
perfana.monte_carlo.risk.
cvar_m
(data, weights, alpha=0.95, rebalance=True, invert=True)[source]¶ Calculates the Conditional Value at Risk (Expected Shortfall) of the portfolio.
Notes
From a mathematical point of view, the alpha value (confidence level for calculation) should be taken at the negative extreme of the distribution. However, the default is set to ease the practitioner.
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.alpha – Confidence level for calculation.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
invert – Whether to invert the confidence interval level. See Notes.
- Returns
CVaR (Expected Shortfall) of the portfolio
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import cvar_m >>> cube = load_cube()[..., :3] >>> weights = [0.33, 0.34, 0.33] >>> cvar_m(cube, weights) -0.7463998716846179
-
perfana.monte_carlo.risk.
diversification_m
(cov_or_data, weights, freq)[source]¶ Derives the diversification ratio of the portfolio
- Parameters
cov_or_data (
ndarray
) – Covariance matrix or simulated returns data cube.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
- Returns
Tracking error of the portfolio
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import portfolio_cov, diversification_m >>> data = load_cube()[..., :7] # first 7 asset classes >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> freq = 'quarterly' >>> diversification_m(data, weights, freq)
-
perfana.monte_carlo.risk.
drawdown_m
(data, weights, geometric=True, rebalance=True)[source]¶ Calculates the drawdown statistics
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.geometric – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
- Returns
A named tuple containing the average maximum drawdown and the drawdown path for each simulation instance.
- Return type
Drawdown
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import drawdown_m >>> data = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> dd = drawdown_m(data, weights) >>> dd.average -0.3198714473717889 >>> dd.paths.shape (80, 1000)
-
perfana.monte_carlo.risk.
portfolio_cov
(data, freq)[source]¶ Forms the empirical portfolio covariance matrix from the simulation data cube
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
- Returns
Empirical portfolio covariance matrix
- Return type
array_like of float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import portfolio_cov >>> data = load_cube()[..., :3] # first 3 asset classes only >>> portfolio_cov(data, 'quarterly').round(4) array([[0.0195, 0.0356, 0.021 ], [0.0356, 0.0808, 0.0407], [0.021 , 0.0407, 0.0239]])
-
perfana.monte_carlo.risk.
prob_loss
(data, weights, rebalance=True, terminal=False)[source]¶ Calculates the probability of the portfolio suffering a loss。
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.rebalance – If True, portfolio is assumed to be rebalanced at every step.
terminal – If True, this only compares the probability of a loss at the last stage. If False (default), the calculation will take into account if the portfolio was “ruined” and count it as a loss even though the terminal value is positive.
- Returns
A named tuple containing the probability of underperformance and loss
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import prob_loss >>> data = load_cube() >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> prob_loss(data, weights) 0.198
-
perfana.monte_carlo.risk.
prob_under_perf
(data, weights, bmk_weights, rebalance=True, terminal=False)[source]¶ Calculates the probability of the portfolio underperforming the benchmark at the terminal state
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.bmk_weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the benchmark portfolio.rebalance – If True, portfolio is assumed to be rebalanced at every step.
terminal – If True, this only compares the probability of underperformance at the last stage. If False (default), the calculation will take into account if the portfolio was “ruined” and count it as an underperformance against the benchmark even though the terminal value is higher than the benchmark. If both portfolios are “ruined”, then it underperforms if it is ruined earlier.
- Returns
A named tuple containing the probability of underperformance and loss
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import prob_under_perf >>> data = load_cube() >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> bmk_weights = [0.65, 0.35] >>> prob_under_perf(data, weights, bmk_weights) 0.863
-
perfana.monte_carlo.risk.
tail_loss
(data, weights, threshold=-0.3, rebalance=True)[source]¶ Calculates the probability and expectation of a tail loss beyond a threshold
Threshold by default is set at -0.3, which means find the probability that the portfolio loses more than 30% of its value and the expected loss.
Notes
The return values are defined as follows:
- prob
Probability of having a tail loss exceeding the threshold
- expected_loss
Value of the expected loss for the portfolio at the threshold
- Parameters
data (
ndarray
) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.threshold – Portfolio loss threshold.
rebalance – If True, portfolio is assumed to be rebalanced at every step.
- Returns
A named tuple containing the probability and expected loss of the portfolio exceeding the threshold.
- Return type
TailLoss
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import tail_loss >>> data = load_cube()[..., :3] # first 3 asset classes only >>> weights = [0.33, 0.34, 0.33] >>> loss = tail_loss(data, weights, -0.3) >>> loss.prob 0.241 >>> loss.expected_loss -0.3978210273894446
-
perfana.monte_carlo.risk.
tracking_error_m
(cov_or_data, weights, bmk_weights, freq)[source]¶ Calculates the tracking error with respect to the benchmark.
If a covariance matrix is used as the data, the benchmark components must be placed after the portfolio components. If a simulated returns cube is used as the data, the benchmark components must be placed after the portfolio components.
- Parameters
cov_or_data (
ndarray
) – Covariance matrix or simulated returns data cube.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.bmk_weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the benchmark portfolio.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
- Returns
Tracking error of the portfolio
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import portfolio_cov, tracking_error_m >>> data = load_cube() >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> bmk_weights = [0.65, 0.35] >>> freq = 'quarterly' >>> tracking_error_m(data, weights, bmk_weights, freq) 0.031183281273726802
-
perfana.monte_carlo.risk.
vol_attr
(cov_or_data, weights, freq)[source]¶ Derives the volatility attribution given a data cube and weights.
Notes
The return values are defined as follows:
- marginal
The absolute marginal contribution of the asset class towards the portfolio volatility. It is essentially the percentage attribution multiplied by the portfolio volatility.
- percentage
The percentage contribution of the asset class towards the portfolio volatility. This number though named in percentage is actually in decimals. Thus 0.01 represents a 1% contribution.
- Parameters
cov_or_data (
ndarray
) – Covariance matrix or simulated returns data cube.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.freq (
Union
[str
,int
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
- Returns
A named tuple of relative and absolute volatility attribution respectively. The absolute attribution is the volatility of the simulated data over time multiplied by the percentage attribution.
- Return type
Attribution
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import vol_attr >>> data = load_cube()[..., :3] # first 3 asset classes only >>> weights = [0.33, 0.34, 0.33] >>> freq = 'quarterly' >>> attr = vol_attr(data, weights, freq) >>> attr.marginal.round(4) array([0.2352, 0.5006, 0.2643]) >>> attr.percentage.round(4) array([0.0445, 0.0947, 0.05 ]) >>> attr.marginal is attr[0] True >>> attr.percentage is attr[1] True
-
perfana.monte_carlo.risk.
volatility_m
(cov_or_data, weights, freq=None)[source]¶ Calculates the portfolio volatility given a simulated returns cube or a covariance matrix
Notes
If a simulated returns data cube is given, the frequency of the data must be specified. In this case, the empirical covariance matrix would be used to derive the volatility.
- Parameters
cov_or_data (
ndarray
) – Covariance matrix or simulated returns data cube.weights (
Union
[Iterable
[Union
[int
,float
]],ndarray
,Series
]) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the covariance matrix shape or the simulated data’s last axis.freq (
Union
[str
,int
,None
]) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
- Returns
Portfolio volatility.
- Return type
float
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import portfolio_cov, volatility_m >>> data = load_cube()[..., :3] # first 3 asset classes only >>> weights = [0.33, 0.34, 0.33] >>> freq = 'quarterly' >>> cov_mat = portfolio_cov(data, freq).round(4) # empirical covariance matrix >>> # Using covariance matrix >>> volatility_m(cov_mat, weights) 0.1891091219375734 >>> # Using the simulated returns data cube >>> volatility_m(data, weights, freq) 0.1891091219375734
Sensitivity¶
-
perfana.monte_carlo.sensitivity.
sensitivity_m
(data, weights, freq, shock=0.05, geometric=True, rebalance=True, cov=None, cvar_cutoff=3, cvar_data=None, alpha=0.95, invert=True, names=None, leveraged=False, distribute=True)[source]¶ Calculates the sensitivity of adding and removing from the asset class on the portfolio.
This is a wrapper function for the 3 sensitivity calculations. For more granular usages, use the base functions instead.
Notes
When given a positive shock and a “proportionate” distribution strategy, each asset class is given an additional amount by removing from the other asset classes proportionately. For example, given a portfolio with weights
[0.1, 0.2, 0.3, 0.4]
, a shock of 5% to the first asset in the portfolio will result in weights[0.15, 0.19, 0.28, 0.38]
. A negative shock works by removing from the asset class and adding to the other asset classes proportionately.If the distribution strategy is set to
False
, the asset class’ weight is increased without removing from the other asset classes. Thus the sum of the portfolio weights will not equal 1.By default, the portfolio is not leveraged. This means that the asset class be shorted (negative shock) to go below 0 and levered (positive shock) to go above 1. The asset class weight is thus capped between 0 and 1 by default. If the
leverage
option is set toTrue
, then this value is no longer capped.- Parameters
data (ndarray) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.
weights (array_like) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.
freq (Frequency) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
shock (float) – The amount to shock each asset class by. A positive number represents adding to the asset class by proportionately removing from the other asset class. A negative number represents removing from the asset class and adding to the other asset class proportionately.
geometric (bool) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.
cov (ndarray) – Asset covariance matrix
cvar_cutoff (int) – Number of years to trim the data cube by for cvar calculation.
cvar_data (np.ndarray) – If specified, will use this data cube instead of the main data cube for cvar calculations.
alpha (float) – Confidence level for calculation.
invert (bool) – Whether to invert the confidence interval level
rebalance (bool) – If True, portfolio is assumed to be rebalanced at every step.
names (list of str) – Asset class names
leveraged (bool) – If True, asset weights are allowed to go below 0 and above 1. This represents that the asset class can be shorted or levered.
distribute (bool) – If True, asset value changes are distributed proportionately to all other asset classes. See Notes for more information.
- Returns
A dataframe with the asset names as the indices and with columns (ret, vol, cvar) representing returns, volatility and CVaR respectively.
- Return type
DataFrame
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import sensitivity_m >>> data = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> freq = 'quarterly' >>> shock = 0.05 # 5% absolute shock >>> sensitivity_m(data, weights, freq, shock) ret vol cvar Asset_1 0.022403 0.113284 -0.485220 Asset_2 0.020484 0.121786 -0.542988 Asset_3 0.022046 0.113964 -0.492411 Asset_4 0.020854 0.109301 -0.478581 Asset_5 0.020190 0.104626 -0.459786 Asset_6 0.020335 0.106652 -0.467798 Asset_7 0.020220 0.106140 -0.468692
-
perfana.monte_carlo.sensitivity.
sensitivity_returns_m
(data, weights, freq, shock=0.05, geometric=True, rebalance=True, names=None, leveraged=False, distribute=True)[source]¶ Calculates the sensitivity of a shock to the annualized returns of the portfolio
Notes
When given a positive shock and a “proportionate” distribution strategy, each asset class is given an additional amount by removing from the other asset classes proportionately. For example, given a portfolio with weights
[0.1, 0.2, 0.3, 0.4]
, a shock of 5% to the first asset in the portfolio will result in weights[0.15, 0.19, 0.28, 0.38]
. A negative shock works by removing from the asset class and adding to the other asset classes proportionately.If the distribution strategy is set to
False
, the asset class’ weight is increased without removing from the other asset classes. Thus the sum of the portfolio weights will not equal 1.By default, the portfolio is not leveraged. This means that the asset class be shorted (negative shock) to go below 0 and levered (positive shock) to go above 1. The asset class weight is thus capped between 0 and 1 by default. If the
leverage
option is set toTrue
, then this value is no longer capped.- Parameters
data (ndarray) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.
weights (array_like) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.
shock (float) – The amount to shock each asset class by. A positive number represents adding to the asset class by proportionately removing from the other asset class. A negative number represents removing from the asset class and adding to the other asset class proportionately.
freq (Frequency) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
geometric (bool) – If True, calculates the geometric mean, otherwise, calculates the arithmetic mean.
rebalance (bool) – If True, portfolio is assumed to be rebalanced at every step.
names (list of str) – Asset class names
leveraged (bool) – If True, asset weights are allowed to go below 0 and above 1. This represents that the asset class can be shorted or levered.
distribute (bool) – If True, asset value changes are distributed proportionately to all other asset classes. See Notes for more information.
- Returns
A series with asset names as the index and annualized returns as its value
- Return type
Series
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import sensitivity_returns_m >>> data = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> freq = 'quarterly' >>> shock = 0.05 # 5% absolute shock >>> sensitivity_returns_m(data, weights, freq, shock) Asset_1 0.022403 Asset_2 0.020484 Asset_3 0.022046 Asset_4 0.020854 Asset_5 0.020190 Asset_6 0.020335 Asset_7 0.020220 Name: ret, dtype: float64
-
perfana.monte_carlo.sensitivity.
sensitivity_vol_m
(cov_or_data, weights, freq=None, shock=0.05, names=None, leveraged=False, distribute=True)[source]¶ Calculates the sensitivity of a shock to the annualized volatility of the portfolio
- Parameters
cov_or_data (
ndarray
) – Monte carlo simulation data or covariance matrix. If simulation cube, this must be 3 dimensional with the axis representing time, trial and asset respectively and frequency will also need to be specified.weights (array_like) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.
freq (Frequency) – Frequency of the data. Can either be a string (‘week’, ‘month’, ‘quarter’, ‘semi-annual’, ‘year’) or an integer specifying the number of units per year. Week: 52, Month: 12, Quarter: 4, Semi-annual: 6, Year: 1.
shock (float) – The amount to shock each asset class by. A positive number represents adding to the asset class by proportionately removing from the other asset class. A negative number represents removing from the asset class and adding to the other asset class proportionately.
names (list of str) – Asset class names
leveraged (bool) – If True, asset weights are allowed to go below 0 and above 1. This represents that the asset class can be shorted or levered.
distribute (bool) – If True, asset value changes are distributed proportionately to all other asset classes. See Notes for more information.
- Returns
A series with asset names as the index and annualized volatility as its value
- Return type
Series
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import sensitivity_vol_m >>> data = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> freq = 'quarterly' >>> shock = 0.05 # 5% absolute shock >>> sensitivity_vol_m(data, weights, freq, shock) Asset_1 0.113284 Asset_2 0.121786 Asset_3 0.113964 Asset_4 0.109301 Asset_5 0.104626 Asset_6 0.106652 Asset_7 0.106140 Name: vol, dtype: float64
-
perfana.monte_carlo.sensitivity.
sensitivity_cvar_m
(data, weights, shock=0.05, alpha=0.95, rebalance=True, invert=True, names=None, leveraged=False, distribute=True)[source]¶ Calculates the sensitivity of a shock to the CVaR of the portfolio
Notes
When given a positive shock and a “proportionate” distribution strategy, each asset class is given an additional amount by removing from the other asset classes proportionately. For example, given a portfolio with weights
[0.1, 0.2, 0.3, 0.4]
, a shock of 5% to the first asset in the portfolio will result in weights[0.15, 0.19, 0.28, 0.38]
. A negative shock works by removing from the asset class and adding to the other asset classes proportionately.If the distribution strategy is set to
False
, the asset class’ weight is increased without removing from the other asset classes. Thus the sum of the portfolio weights will not equal 1.By default, the portfolio is not leveraged. This means that the asset class be shorted (negative shock) to go below 0 and levered (positive shock) to go above 1. The asset class weight is thus capped between 0 and 1 by default. If the
leverage
option is set toTrue
, then this value is no longer capped.- Parameters
data (ndarray) – Monte carlo simulation data. This must be 3 dimensional with the axis representing time, trial and asset respectively.
weights (array_like) – Weights of the portfolio. This must be 1 dimensional and must match the dimension of the data’s last axis.
shock (float) – The amount to shock each asset class by. A positive number represents adding to the asset class by proportionately removing from the other asset class. A negative number represents removing from the asset class and adding to the other asset class proportionately.
alpha (float) – Confidence level for calculation.
invert (bool) – Whether to invert the confidence interval level
rebalance (bool) – If True, portfolio is assumed to be rebalanced at every step.
names (list of str) – Asset class names
leveraged (bool) – If True, asset weights are allowed to go below 0 and above 1. This represents that the asset class can be shorted or levered.
distribute (bool) – If True, asset value changes are distributed proportionately to all other asset classes. See Notes for more information.
- Returns
A series with asset names as the index and CVaR as its value
- Return type
Series
Examples
>>> from perfana.datasets import load_cube >>> from perfana.monte_carlo import sensitivity_cvar_m >>> data = load_cube()[..., :7] >>> weights = [0.25, 0.18, 0.13, 0.11, 0.24, 0.05, 0.04] >>> freq = 'quarterly' >>> shock = 0.05 # 5% absolute shock >>> sensitivity_cvar_m(data, weights, shock) Asset_1 -0.485220 Asset_2 -0.542988 Asset_3 -0.492411 Asset_4 -0.478581 Asset_5 -0.459786 Asset_6 -0.467798 Asset_7 -0.468692 Name: cvar, dtype: float64
Datasets¶
Data sets contain a series of python data objects to help get the user started on the package.
Datasets API¶
Data sets contain a series of python data objects to help get the user started on the package.
Load Sample Simulation Data Cube¶
-
perfana.datasets.base.
load_cube
(*, download=False)[source]¶ Loads a sample Monte Carlo simulation of 9 asset classes.
The dimension of the cube is 80 * 1000 * 9. The first axis represents the time, the second represents the number of trials (simulations) and the third represents each asset class.
- Parameters
download (bool) – If True, forces the data to be downloaded again from the repository. Otherwise, loads the data from the stash folder
- Returns
A data cube of simulated returns
- Return type
ndarray
Load ETF Data¶
-
perfana.datasets.base.
load_etf
(*, date_as_index=True, download=False)[source]¶ Dataset contains prices of 4 ETF ranging from 2001-06-15 to 2019-03-01.
- Parameters
date_as_index (
bool
) – If True, sets the first column as the index of the DataFramedownload (bool) – If True, forces the data to be downloaded again from the repository. Otherwise, loads the data from the stash folder
- Returns
A data frame containing the prices of 4 ETF
- Return type
DataFrame
Load Swiss Market Index¶
-
perfana.datasets.base.
load_smi
(*, as_returns=False, download=False)[source]¶ Dataset contains the close prices of all 20 constituents of the Swiss Market Index (SMI) from 2011-09-09 to 2012-03-28.
- Parameters
as_returns (bool) – If true, transforms the price data to returns data
download (bool) – If True, forces the data to be downloaded again from the repository. Otherwise, loads the data from the stash folder
- Returns
A data frame of the closing prices of all 20 constituents of the Swiss Market Index
- Return type
DataFrame