Hello World (original)

This example script is available in the repository. See the docstring below for its explanation.

This is a translation of the original IPython notebook using Cvxportfolio’s stable API.

"""This is a simple example of back-tests with Cvxportfolio.

This is a close translation of what was done in `this notebook
<https://github.com/cvxgrp/cvxportfolio/blob/0.0.X/examples/HelloWorld.ipynb>`_.
In fact, you can see that the results are identical.

The approach used here is not recommended; in particular we download
data externally (it is done better now by the automatic data download
and cleaning code we include in Cvxportfolio). The returns used here
are close-to-close total returns, while our interface computes correctly
the open-to-open total returns.

In this example returns and covariances are forecasted externally,
while today this can be done automatically using the default forecasters
used by :class:`cvxportfolio.ReturnsForecast` and
:class:`cvxportfolio.FullCovariance`.

Nevertheless, you can see by running this that we are still able to
reproduce exactly the behavior of the early development versions
of the library.

.. note::

    To run this, you need to install ``yfinance`` and
    ``pandas_datareader``.
"""

import matplotlib.pyplot as plt
import pandas as pd
import pandas_datareader as pdr
import yfinance

import cvxportfolio as cvx

# Download market data
tickers = ['AMZN', 'GOOGL', 'TSLA', 'NKE']

returns = pd.DataFrame(dict([(ticker,
    yfinance.download(ticker)['Adj Close'].pct_change())
                for ticker in tickers]))

returns["USDOLLAR"] = pdr.get_data_fred(
    'DFF', start="1900-01-01",
    end=pd.Timestamp.today())['DFF']/(252*100)

returns = returns.fillna(method='ffill').iloc[1:]

print('Returns')
print(returns)

# Create market data server.
market_data = cvx.UserProvidedMarketData(
    returns = returns,
    cash_key = 'USDOLLAR')


# Today we'd do all the above by (no external packages needed):
# market_data = cvx.DownloadedMarketData(
#     universe = ['AMZN', 'GOOGL', 'TSLA', 'NKE'],
#     cash_key = 'USDOLLAR')


print('Historical returns:')
print(market_data.returns)

# Build forecasts of expected returns and covariances.
# Note that we shift so that each day we use ones built
# using past returns only. This is done automatically
# by the forecasters used by default in the stable versions
# of Cvxportfolio.
r_hat_with_cash = market_data.returns.rolling(
    window=250).mean().shift(1).dropna()
Sigma_hat_without_cash = market_data.returns.iloc[:, :-1
    ].rolling(window=250).cov().shift(4).dropna()

r_hat = r_hat_with_cash.iloc[:, :-1]
r_hat_cash = r_hat_with_cash.iloc[:, -1]
print('Expected returns forecast:')
print(r_hat_with_cash)

# Define transaction and holding cost models.

# Half spread.
HALF_SPREAD = 10E-4

# In the 2016 development code borrow fees were expressed per-period.
# In the stable version we require annualized percent.
# This value corresponds to 1 basis point per period, which was in the
# original example.
BORROW_FEE = 2.552

tcost_model = cvx.TcostModel(a=HALF_SPREAD, b=None)
hcost_model = cvx.HcostModel(short_fees=BORROW_FEE)

# As risk model, we use the historical covariances computed above.
# Note that the stable version of Cvxportfolio requires the covariance
# matrix to not include cash (as it shouldn't). In the development versions
# it was there. It doesn't make any difference in numerical terms.
risk_model = cvx.FullSigma(Sigma_hat_without_cash)

# Constraint.
leverage_limit = cvx.LeverageLimit(3)

# Define a single-period optimization policy; its objective function is
# maximized.
gamma_risk, gamma_trade, gamma_hold = 5., 1., 1.
spo_policy = cvx.SinglePeriodOpt(
    objective = cvx.ReturnsForecast(r_hat) + cvx.CashReturn(r_hat_cash)
        - gamma_risk * risk_model
        - gamma_trade * tcost_model
        - gamma_hold * hcost_model,
    constraints=[leverage_limit],
    include_cash_return=False)

# Define the market simulator.
market_sim = cvx.MarketSimulator(
    market_data = market_data,
    costs = [
        cvx.TcostModel(a=HALF_SPREAD, b=None),
        cvx.HcostModel(short_fees=BORROW_FEE)])

# Initial portfolio, uniform on non-cash assets.
init_portfolio = pd.Series(
    index=market_data.returns.columns, data=250000.)
init_portfolio.USDOLLAR = 0

# Run two back-tests.
results = market_sim.run_multiple_backtest(
    h=[init_portfolio]*2,
    start_time='2013-01-03',  end_time='2016-12-31',
    policies=[spo_policy, cvx.Hold()])

print('Back-test result, single-period optimization policy:')
print(results[0])

print('Back-test result, Hold policy:')
print(results[1])

results[0].v.plot(label='SPO')
results[1].v.plot(label='Hold policy')
plt.title('Portfolio total value in time (USD)')
plt.legend()
plt.show()

results[0].w.plot()
plt.title('SPO weights in time')
plt.show()