Expected Returns¶
Meanvariance optimisation 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 where the main flaw in efficient frontier lies – the optimisation 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 optimiser.
Caution
In my experience, supplying expected returns often does more harm than good. If predicting stock returns were as easy as calcualting 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)
The expected_returns
module provides functions for estimating the expected returns of
the assets, which is a required input in meanvariance optimisation.
By convention, the output of these methods are 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:
 mean historical return
 exponentially weighted mean historical return
Additionally, we provide utility functions to convert from returns to prices and viceversa.

pypfopt.expected_returns.
mean_historical_return
(prices, frequency=252)¶ Calculate annualised mean (daily) historical return from input (daily) asset prices.
Parameters:  prices (pd.DataFrame) – adjusted closing prices of the asset, each row is a date and each column is a ticker/id.
 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 unlikely to be accurate. This is a problem especially in the context of a quadratic optimiser, which will maximise the erroneous inputs, In some informal backtests, I’ve found that vanilla efficient frontier portfolios (using mean historical returns and sample covariance) actually do have a statistically significant outperformance over the S&P500 (in the order of 35%), though the same isn’t true for cryptoasset portfolios. At some stage, I may redo these backtests rigorously and add them to the repo (see the Roadmap and Changelog page for more).

pypfopt.expected_returns.
ema_historical_return
(prices, frequency=252, span=500)¶ 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.
 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.
returns_from_prices
(prices)¶ 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. Returns: (daily) returns Return type: pd.DataFrame

pypfopt.expected_returns.
prices_from_returns
(returns)¶ 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 Returns: (daily) pseudoprices. Return type: pd.DataFrame