Case-Shiller multi-period¶
We test the Multi-Period Optimization model on a real estate portfolio.
This is an example that shows that Cvxportfolio can work as well with different asset classes. We use the Case-Shiller index as proxy for the price of housing units in various metropolitan areas in the USA. We impose realistic transaction costs, which are comparable to the annual return on the asset, and we show that multi-period optimization is useful to correctly balance transaction cost and expected risk-adjusted return.
We present the (Cvxportfolio native) plots of the results of various back-tests, and also create an “efficient frontier” plot obtained by sweeping over choices of the risk aversion hyper-parameter.
import matplotlib.pyplot as plt
import numpy as np
import cvxportfolio as cvx
# These are monthly time serieses of the Case-Shiller index for
# a selection of US metropolitan areas. You can find them on the
# website of FRED https://fred.stlouisfed.org/
UNIVERSE = [
'SFXRSA', # San Francisco
'LXXRSA', # Los Angeles
'SEXRSA', # Seattle
'DAXRSA', # Dallas
'SDXRSA', # San Diego
'MIXRSA', # Miami
'PHXRSA', # Phoenix
'NYXRSA', # New York
'CHXRSA', # Chicago
'ATXRSA', # Atlanta
'LVXRSA', # Las Vegas
'POXRSA', # Portland
'WDXRSA', # Washington D.C.
'TPXRSA', # Tampa
'CRXRSA', # Charlotte
'MNXRSA', # Minneapolis
'DEXRSA', # Detroit
'CEXRSA', # Cleveland
'DNXRSA', # Denver
'BOXRSA', # Boston
]
# we assume that the cost of transacting
# residential real estate is about 5%
LINEAR_TCOST = 0.05
simulator = cvx.MarketSimulator(
universe = UNIVERSE,
# we enabled the default data interface to download index
# prices from FRED
datasource='Fred',
costs = [cvx.TransactionCost(
a=LINEAR_TCOST,
b=None, # since we don't have market volumes, we can't use the
# market impact term of the transaction cost model
)]
)
# let's see what a uniform allocation does
result_uniform = simulator.backtest(cvx.Uniform())
print('BACK-TEST RESULT OF UNIFORM (1/N) ALLOCATION')
print(result_uniform)
# plot the result
figure_uniform = result_uniform.plot()
# These are risk model coefficients. They don't seem to have
# a strong effect on this example.
NUM_FACTORS = 5
KAPPA = 0.1
# This is the multi-period planning horizon. We plan for 6 months
# in the future.
HORIZON = 6
policies = []
# sweep over risk aversion
for gamma_risk in np.logspace(0, 3, 10):
policies.append(cvx.MultiPeriodOptimization(
cvx.ReturnsForecast() - gamma_risk * (
cvx.FactorModelCovariance(num_factors=NUM_FACTORS)
+ KAPPA * cvx.RiskForecastError())
- cvx.TransactionCost(a=LINEAR_TCOST, b=None),
[cvx.LongOnly(applies_to_cash=True)],
planning_horizon=HORIZON,
)
)
# run parallel back-tests for all the policies defined above
results = simulator.backtest_many(policies)
print('BACK-TEST RESULT OF MPO WITH HIGHEST (OUT-OF-SAMPLE) PROFIT')
print(results[np.argmax([el.profit for el in results])])
# back-test result with the highest profit
top_profit_fig = results[np.argmax([el.profit for el in results])].plot()
# multi-period optimization efficient frontier
efficient_frontier_figure = plt.figure()
plt.plot(
[result.excess_returns.std() * np.sqrt(12) for result in results],
[result.excess_returns.mean() * 12 for result in results],
'r*-',
label='Multi-period optimization frontier'
)
plt.scatter([result_uniform.excess_returns.std() * np.sqrt(12)],
[result_uniform.excess_returns.mean() * 12],
label='Uniform (1/n) allocation'
)
plt.legend()
plt.title('Back-Test Result (Out-Of-Sample) for Real Estate Portfolio')
plt.xlabel('Excess risk (annualized)')
plt.ylabel('Excess return (annualized)')
This is the output printed to screen when executing this script. You can see many statistics of the back-tests.
Updating data....................
BACK-TEST RESULT OF UNIFORM (1/N) ALLOCATION
#################################################################
Universe size 21
Initial timestamp 1988-01-01 00:00:00+00:00
Final timestamp 2023-12-01 00:00:00+00:00
Number of periods 432
Initial value (USDOLLAR) 1.000e+06
Final value (USDOLLAR) 3.944e+06
Profit (USDOLLAR) 2.944e+06
Avg. return (annualized) 3.9%
Volatility (annualized) 2.5%
Avg. excess return (annualized) 0.8%
Excess volatility (annualized) 2.7%
Avg. growth rate (annualized) 3.8%
Avg. excess growth rate (annualized) 0.8%
Avg. TransactionCost 4bp
Max. TransactionCost 500bp
Sharpe ratio 0.30
Avg. drawdown -6.8%
Min. drawdown -36.1%
Avg. leverage 99.8%
Max. leverage 105.3%
Avg. turnover 0.4%
Max. turnover 50.0%
Avg. policy time 0.000s
Avg. simulator time 0.003s
Of which: market data 0.000s
Total time 1.187s
#################################################################
BACK-TEST RESULT OF MPO WITH HIGHEST (OUT-OF-SAMPLE) PROFIT
#################################################################
Universe size 21
Initial timestamp 1988-01-01 00:00:00+00:00
Final timestamp 2023-12-01 00:00:00+00:00
Number of periods 432
Initial value (USDOLLAR) 1.000e+06
Final value (USDOLLAR) 4.988e+06
Profit (USDOLLAR) 3.988e+06
Avg. return (annualized) 4.5%
Volatility (annualized) 3.6%
Avg. excess return (annualized) 1.5%
Avg. active return (annualized) 1.5%
Excess volatility (annualized) 3.7%
Active volatility (annualized) 3.7%
Avg. growth rate (annualized) 4.5%
Avg. excess growth rate (annualized) 1.4%
Avg. active growth rate (annualized) 1.4%
Avg. TransactionCost 1bp
Max. TransactionCost 300bp
Sharpe ratio 0.40
Information ratio 0.40
Avg. drawdown -12.1%
Min. drawdown -42.2%
Avg. leverage 97.8%
Max. leverage 100.1%
Avg. turnover 0.1%
Max. turnover 30.0%
Avg. policy time 0.011s
Avg. simulator time 0.005s
Of which: market data 0.001s
Total time 7.199s
#################################################################
And these are the figure that are plotted.
The result of the cvxportfolio.Uniform
policy, which allocates equal
weight to all non-cash assets:
And result of the cvxportfolio.MultiPeriodOptimization
policy, selected
among the efficient frontier below as the one with highest back-tested profit:
And finally, the efficient frontier, which shows that the
cvxportfolio.MultiPeriodOptimization
policies out-perform the
cvxportfolio.Uniform
allocation in both risk and reward, including
the transaction costs.