Expected Returns¶
Meanvariance optimization requires knowledge of the expected returns. In practice, these are rather difficult to know with any certainty. Thus the best we can do is to come up with estimates, for example by extrapolating historical data, This is the main flaw in meanvariance optimization – the optimization procedure is sound, and provides strong mathematical guarantees, given the correct inputs. This is one of the reasons why I have emphasised modularity: users should be able to come up with their own superior models and feed them into the optimizer.
Caution
Supplying expected returns can do more harm than good. If predicting stock returns were as easy as calculating the mean historical return, we’d all be rich! For most usecases, I would suggest that you focus your efforts on choosing an appropriate risk model (see Risk Models).
As of v0.5.0, you can use BlackLitterman Allocation to significantly improve the quality of your estimate of the expected returns.
The expected_returns
module provides functions for estimating the expected returns of
the assets, which is a required input in meanvariance optimization.
By convention, the output of these methods is expected annual returns. It is assumed that
daily prices are provided, though in reality the functions are agnostic
to the time period (just change the frequency
parameter). Asset prices must be given as
a pandas dataframe, as per the format described in the User Guide.
All of the functions process the price data into percentage returns data, before calculating their respective estimates of expected returns.
Currently implemented:
 general return model function, allowing you to run any return model from one function.
 mean historical return
 exponentially weighted mean historical return
 CAPM estimate of returns
Additionally, we provide utility functions to convert from returns to prices and viceversa.
Note
For any of these methods, if you would prefer to pass returns (the default is prices),
set the boolean flag returns_data=True

pypfopt.expected_returns.
mean_historical_return
(prices, returns_data=False, compounding=True, frequency=252)[source]¶ Calculate annualised mean (daily) historical return from input (daily) asset prices. Use
compounding
to toggle between the default geometric mean (CAGR) and the arithmetic mean.Parameters:  prices (pd.DataFrame) – adjusted closing prices of the asset, each row is a date and each column is a ticker/id.
 returns_data (bool, defaults to False.) – if true, the first argument is returns instead of prices. These should not be log returns.
 compounding (bool, defaults to True) – computes geometric mean returns if True, arithmetic otherwise, optional.
 frequency (int, optional) – number of time periods in a year, defaults to 252 (the number of trading days in a year)
Returns: annualised mean (daily) return for each asset
Return type: pd.Series
This is probably the default textbook approach. It is intuitive and easily interpretable, however the estimates are subject to large uncertainty. This is a problem especially in the context of a meanvariance optimizer, which will maximise the erroneous inputs.

pypfopt.expected_returns.
ema_historical_return
(prices, returns_data=False, compounding=True, span=500, frequency=252)[source]¶ Calculate the exponentiallyweighted mean of (daily) historical returns, giving higher weight to more recent data.
Parameters:  prices (pd.DataFrame) – adjusted closing prices of the asset, each row is a date and each column is a ticker/id.
 returns_data (bool, defaults to False.) – if true, the first argument is returns instead of prices. These should not be log returns.
 compounding (bool, defaults to True) – computes geometric mean returns if True, arithmetic otherwise, optional.
 frequency (int, optional) – number of time periods in a year, defaults to 252 (the number of trading days in a year)
 span (int, optional) – the timespan for the EMA, defaults to 500day EMA.
Returns: annualised exponentiallyweighted mean (daily) return of each asset
Return type: pd.Series
The exponential moving average is a simple improvement over the mean historical return; it gives more credence to recent returns and thus aims to increase the relevance of the estimates. This is parameterised by the
span
parameter, which gives users the ability to decide exactly how much more weight is given to recent data. Generally, I would err on the side of a higher span – in the limit, this tends towards the mean historical return. However, if you plan on rebalancing much more frequently, there is a case to be made for lowering the span in order to capture recent trends.

pypfopt.expected_returns.
capm_return
(prices, market_prices=None, returns_data=False, risk_free_rate=0.02, compounding=True, frequency=252)[source]¶ Compute a return estimate using the Capital Asset Pricing Model. Under the CAPM, asset returns are equal to market returns plus a \(eta\) term encoding the relative risk of the asset.
\[R_i = R_f + \beta_i (E(R_m)  R_f)\]Parameters:  prices (pd.DataFrame) – adjusted closing prices of the asset, each row is a date and each column is a ticker/id.
 market_prices (pd.DataFrame, optional) – adjusted closing prices of the benchmark, defaults to None
 returns_data (bool, defaults to False.) – if true, the first arguments are returns instead of prices.
 risk_free_rate (float, optional) – riskfree rate of borrowing/lending, defaults to 0.02. You should use the appropriate time period, corresponding to the frequency parameter.
 compounding (bool, defaults to True) – computes geometric mean returns if True, arithmetic otherwise, optional.
 frequency (int, optional) – number of time periods in a year, defaults to 252 (the number of trading days in a year)
Returns: annualised return estimate
Return type: pd.Series

pypfopt.expected_returns.
returns_from_prices
(prices, log_returns=False)[source]¶ Calculate the returns given prices.
Parameters:  prices (pd.DataFrame) – adjusted (daily) closing prices of the asset, each row is a date and each column is a ticker/id.
 log_returns (bool, defaults to False) – whether to compute using log returns
Returns: (daily) returns
Return type: pd.DataFrame

pypfopt.expected_returns.
prices_from_returns
(returns, log_returns=False)[source]¶ Calculate the pseudoprices given returns. These are not true prices because the initial prices are all set to 1, but it behaves as intended when passed to any PyPortfolioOpt method.
Parameters:  returns (pd.DataFrame) – (daily) percentage returns of the assets
 log_returns (bool, defaults to False) – whether to compute using log returns
Returns: (daily) pseudoprices.
Return type: pd.DataFrame