#!/usr/bin/env python # coding: utf-8 # # Riskfolio-Lib Tutorial: #
#
# #
#
# #
Buy Me a Coffee at ko-fi.com #
#
__[Financionerioncios](https://financioneroncios.wordpress.com)__ #
__[Orenji](https://www.linkedin.com/company/orenj-i)__ #
__[Riskfolio-Lib](https://riskfolio-lib.readthedocs.io/en/latest/)__ #
__[Dany Cajas](https://www.linkedin.com/in/dany-cajas/)__ # ## Tutorial 23: Dollar Neutral Portfolios # # ## 1. Downloading the data: # In[ ]: import numpy as np import pandas as pd import matplotlib.pyplot as plt import yfinance as yf import warnings warnings.filterwarnings("ignore") pd.options.display.float_format = '{:.4%}'.format # Date range start = '2016-01-01' end = '2019-12-30' # Tickers of assets assets = ['JCI', 'TGT', 'CMCSA', 'CPB', 'MO', 'APA', 'MMC', 'JPM', 'ZION', 'PSA', 'BAX', 'BMY', 'LUV', 'PCAR', 'TXT', 'TMO', 'DE', 'MSFT', 'HPQ', 'SEE', 'VZ', 'CNP', 'NI', 'T', 'BA'] assets.sort() # Downloading data data = yf.download(assets, start = start, end = end, auto_adjust=False) data = data.loc[:,('Adj Close', slice(None))] data.columns = assets # In[2]: # Calculating returns Y = data[assets].pct_change().dropna() display(Y.head()) # ## 2. Mean Risk Dollar Neutral Portfolios # # To calculate dollar neutral portfolios we have to solve the following problem: # # $$ # \begin{aligned} # & \max_{w} & & \mu w \\ # & & & \sum^{N}_{i=1} w_{i} = 0 \\ # & & & \phi(w) \leq \overline{\phi} \\ # & & & \sum^{N}_{i=1} \max(w_{i},0) \leq W^{L} \\ # & & & \sum^{N}_{i=1} -\min(w_{i},0) \leq W^{S} \\ # & & & W^{S} \leq w \leq W^{L}\\ # \end{aligned} # $$ # # Where $\mu$ is the mean vector, $w$ are the portfolio weights, $\phi(w)$ is a convex risk function, $\overline{\phi}$ is an upper bound for the risk function, $W^{L}$ is the upper bound of positive weights and $W^{S}$ is the upper bound of negative weights. # # ## 3. Dollar Neutral Portfolio with a Constraint on Standard Deviation # # ### 3.1 Calculating Dollar Neutral Portfolio # In[3]: import riskfolio as rp # Building the portfolio object port = rp.Portfolio(returns=Y) # Calculating optimal portfolio # Select method and estimate input parameters: method_mu='hist' # Method to estimate expected returns based on historical data. method_cov='hist' # Method to estimate covariance matrix based on historical data. port.assets_stats(method_mu=method_mu, method_cov=method_cov) # Market neutral constraints: port.sht = True # Allows short positions port.uppersht = 1 # Upper bound for sum of negative weights port.upperlng = 1 # Upper bound for sum of positive weights port.budget = 0 # Sum of all weights port.upperdev = 0.20/252**0.5 # Upper bound for daily standard deviation # Estimate optimal portfolio: model='Classic' # Could be Classic (historical), BL (Black Litterman), FM (Factor Model) # or BL_FM (Black Litterman with Factor Model) rm = 'MV' # Risk measure used, this time will be variance obj = 'MaxRet' # For Market Neutral the objective must be hist = True # Use historical scenarios for risk measures that depend on scenarios rf = 0 # Risk free rate l = 3 # Risk aversion factor, only useful when obj is 'Utility' w = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist) print("Sum weights : ", np.round(np.sum(w.to_numpy()),4)) display(w.T) # ### 3.2 Plotting portfolio composition # In[4]: # Plotting the composition of the portfolio title = "Max Return Dollar Neutral with Variance Constraint" ax = rp.plot_pie(w=w, title=title, others=0.05, nrow=25, cmap = "tab20", height=7, width=10, ax=None) # In[5]: # Plotting the composition of the portfolio using bar chart ax = rp.plot_bar(w, title="Max Return Dollar Neutral with Variance Constraint", kind="v", others=0.05, nrow=25, height=6, width=10) # ### 3.3 Calculating efficient frontier # In[6]: points = 50 # Number of points of the frontier port.upperdev = None # Deleting the upper bound for daily standard deviation frontier = port.efficient_frontier(model=model, rm=rm, points=points, rf=rf, hist=hist) display(frontier) # In[7]: # Plotting the efficient frontier label = "Max Return Dollar Neutral with Variance Constraint" # Title of point mu = port.mu # Expected returns cov = port.cov # Covariance matrix returns = port.returns # Returns of the assets ax = rp.plot_frontier(w_frontier=frontier, mu=mu, cov=cov, returns=returns, rm=rm, rf=rf, alpha=0.05, cmap='viridis', w=w, label=label, marker='*', s=16, c='r', height=6, width=10, ax=None) # In[8]: # Plotting efficient frontier composition in absolute values ax = rp.plot_frontier_area(w_frontier=np.abs(frontier), cmap="tab20", height=6, width=10, ax=None) # ## 4. Dollar Neutral Portfolio with a Constraint on CVaR # # ### 4.1 Calculating Dollar Neutral Portfolio # In[9]: rm = 'CVaR' # Risk measure port.upperCVaR = 0.40/252**0.5 # Creating an upper bound for daily CVaR w = port.optimization(model=model, rm=rm, obj=obj, rf=rf, l=l, hist=hist) print("Sum weights : ", np.round(np.sum(w.to_numpy()),4)) display(w.T) # ### 4.2 Plotting portfolio composition # In[10]: # Plotting the composition of the portfolio title = "Max Return Dollar Neutral with CVaR Constraint" ax = rp.plot_pie(w=w, title=title, others=0.05, nrow=25, cmap = "tab20", height=7, width=10, ax=None) # In[11]: # Plotting the composition of the portfolio using bar chart ax = rp.plot_bar(w, title="Max Return Dollar Neutral with CVaR Constraint", kind="v", others=0.05, nrow=25, height=6, width=10) # ### 4.3 Calculate efficient frontier # In[12]: points = 50 # Number of points of the frontier port.upperCVaR = None # Deleting the upper bound for daily CVaR frontier = port.efficient_frontier(model=model, rm=rm, points=points, rf=rf, hist=hist) display(frontier.T.head()) # In[13]: label = "Max Return Dollar Neutral with CVaR Constraint" # Title of point ax = rp.plot_frontier(w_frontier=frontier, mu=mu, cov=cov, returns=returns, rm=rm, rf=rf, alpha=0.05, cmap='viridis', w=w, label=label, marker='*', s=16, c='r', height=6, width=10, ax=None) # In[14]: # Plotting efficient frontier composition in absolute values ax = rp.plot_frontier_area(w_frontier=np.abs(frontier), cmap="tab20", height=6, width=10, ax=None)