[http://www.investopedia.com/terms/s/sharperatio.asp]
A portfolio is just a set of allocations in variety of securities.
For example:
- 20% in APPL (Apple)
- 30% in FB (Facebook)
- 50% in GOOG (Google)
These percentages should add up to 100% (or if defined as weights whey should ad up to 1).
Key statistics for portfolio
The Sharpe Ratio is a measure for calculating risk-adjusted return, and this ratio has become the industry standard for such calculations.
Sharpe ratio = (Mean portfolio return − Risk-free rate)/Standard deviation of portfolio return
The original Sharpe Ratio
Annualized Sharpe Ratio = K-value * SR
K-values for various sampling rates:
Since I'm based in the USA, I will use a very low risk-free rate (the rate you would get if you just put your money in a bank, its currently very low in the USA, let's just say its ~0% return). If you are in a different country with higher rates for your trading currency, you can use this trick to convert a yearly rate with a daily rate:
daily_rate = ((1.0 + yearly_rate)**(1/252))-1
Other values people use are things like the 3-month treasury bill or LIBOR.
In [23]:
import pandas as pd
import quandl
In [24]:
aapl = pd.read_csv('AAPL_CLOSE', index_col='Date', parse_dates=True)
cisco = pd.read_csv('CISCO_CLOSE', index_col='Date', parse_dates=True)
ibm = pd.read_csv('IBM_CLOSE', index_col='Date', parse_dates=True)
amzn = pd.read_csv('AMZN_CLOSE', index_col='Date', parse_dates=True)
In [25]:
aapl.head()
Out[25]:
In [26]:
aapl.index
Out[26]:
In [27]:
cisco.head()
Out[27]:
In [28]:
ibm.head()
Out[28]:
In [29]:
amzn.head()
Out[29]:
In [30]:
for stock_df in (aapl, cisco, ibm, amzn):
stock_df['returns'] = stock_df['Adj. Close'].pct_change()
stock_df['cumulative return'] = (1 + stock_df['returns']).cumprod()
In [31]:
aapl.head()
Out[31]:
In [32]:
# Another method for calculating cumulative (directly from Adj. Close)
for stock_df in (aapl, cisco, ibm, amzn):
stock_df['Normed Return'] = stock_df['Adj. Close'] / stock_df.iloc[0]['Adj. Close']
In [33]:
aapl.head()
Out[33]:
In [34]:
list(zip((aapl, cisco, ibm, amzn), [.3,.2,.4,.1]))
Out[34]:
In [35]:
for stock_df, allo in zip((aapl, cisco, ibm, amzn), [.3,.2,.4,.1]):
stock_df['Allocation'] = stock_df['Normed Return'] * allo
In [36]:
aapl.head()
Out[36]:
In [37]:
for stock_df in (aapl, cisco, ibm, amzn):
stock_df['Position Values'] = stock_df['Allocation'] * 1000000
In [38]:
aapl.drop(['returns', 'cumulative return'], inplace=True, axis=1)
aapl.head()
Out[38]:
In [39]:
# Creating larger porfolio dataframe
all_pos_vals = [aapl['Position Values'], cisco['Position Values'], ibm['Position Values'], amzn['Position Values']]
portfolio_val = pd.concat(all_pos_vals, axis=1)
portfolio_val.columns = ['AAPL Pos', 'CISCO Pos', 'IBM Pos', 'AMZN Pos']
portfolio_val.head()
Out[39]:
In [40]:
# Summing along the row
portfolio_val['Total Pos'] = portfolio_val.sum(axis=1)
portfolio_val.head()
Out[40]:
In [41]:
import matplotlib.pyplot as plt
%matplotlib inline
In [42]:
portfolio_val['Total Pos'].plot(figsize=(10,8))
plt.title('Total Portfolio Value');
In [43]:
portfolio_val.drop('Total Pos', axis=1).plot(figsize=(10,8));
In [44]:
portfolio_val.head()
Out[44]:
In [45]:
portfolio_val['Daily Returns'] = portfolio_val['Total Pos'].pct_change()
portfolio_val.head()
Out[45]:
In [46]:
portfolio_val['Daily Returns'].mean()
Out[46]:
In [47]:
portfolio_val['Daily Returns'].std()
Out[47]:
In [48]:
portfolio_val['Daily Returns'].plot(kind='hist', bins=100, figsize=(8,5));
In [49]:
portfolio_val['Daily Returns'].plot(kind='kde', figsize=(8,5));
In [50]:
overall_cum_ret = 100 * (portfolio_val['Total Pos'][-1] / portfolio_val['Total Pos'][0] - 1)
overall_cum_ret
Out[50]:
In [51]:
portfolio_val['Total Pos'][-1]
Out[51]:
Thus we have gained over 84.74% on our initial portfolio investment till date which amounts to 1847428.51 USD
In [52]:
# Calculating sharpe ration assuming that risk_free return is zero.
sharpe_ratio = portfolio_val['Daily Returns'].mean() / portfolio_val['Daily Returns'].std()
sharpe_ratio
Out[52]:
Therefore, our sharpe ratio is 0.054196806626477189
In [53]:
# Calculating annualized sharpe ratio
annual_sharpe_ratio = (252**0.5) * sharpe_ratio
annual_sharpe_ratio
Out[53]:
Therefore, our annualized sharpe ratio is 0.81748646188585039
Sharpe ratio acceptable measures:
The basic purpose of sharpe ratio is to allow investors to analyze how much greater a return here he or she is obtaining in relation to the level of additional risk taken to generate that return.
“Modern Portfolio Theory (MPT), a hypothesis put forth by Harry Markowitz in his paper “Portfolio Selection,” (published in 1952 by the Journal of Finance) is an investment theory based on the idea that risk-averse investors can construct portfolios to optimize or maximize expected return based on a given level of market risk, emphasizing that risk is an inherent part of higher reward. It is one of the most important and influential economic theories dealing with finance and investment.
Goal: Optimize the portfolio holding to obtain the best Sharpe ratio.
[http://www.bfjlaward.com/pdf/26063/59-69_Lopez%20Color_JPM_0708.pdf]
In [54]:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
In [55]:
aapl = pd.read_csv('AAPL_CLOSE', index_col='Date', parse_dates=True)
cisco = pd.read_csv('CISCO_CLOSE', index_col='Date', parse_dates=True)
ibm = pd.read_csv('IBM_CLOSE', index_col='Date', parse_dates=True)
amzn = pd.read_csv('AMZN_CLOSE', index_col='Date', parse_dates=True)
In [56]:
aapl.head()
Out[56]:
In [57]:
stocks = pd.concat([aapl, cisco, ibm, amzn], axis=1)
stocks.columns = ['aapl', 'cisco', 'ibm', 'amzn']
stocks.head()
Out[57]:
In [58]:
stocks.pct_change().mean()
Out[58]:
In [59]:
# Pearson correlation coefficient
# [https://statistics.laerd.com/statistical-guides/pearson-correlation-coefficient-statistical-guide.php]
stocks.pct_change().corr()
Out[59]:
We will now switch over to using log returns instead of arithmetic returns, for many of our use cases they are almost the same,but most technical analyses require detrending/normalizing the time series and using log returns is a nice way to do that. Log returns are convenient to work with in many of the algorithms we will encounter.
For a full analysis of why we use log returns, check this great article.
In [60]:
# Arithmetic daily returns
stocks.pct_change().head()
Out[60]:
In [61]:
# Logarithmic daily returns
log_ret = np.log(stocks/stocks.shift(1))
log_ret.head()
Out[61]:
In [62]:
log_ret.hist(bins=100, figsize=(12,8))
plt.tight_layout()
In [63]:
# Mean of logarithmic returns
log_ret.mean()
Out[63]:
In [64]:
# Covariance of logarithmic returns
log_ret.cov()
Out[64]:
===========================================================
In [65]:
# resetting the random seed
np.random.seed(101)
# know the stocks
print(stocks.columns)
# create random allocation weights
weights = np.random.random(4)
print('Random weights: {}'.format(weights))
# rebalance the weights to add up to 1.0
weights = weights/np.sum(weights)
print('Rebalanced weights: {}'.format(weights))
# expected return
exp_ret = np.sum((log_ret.mean() * weights) * 252)
print('Expected return: {}'.format(exp_ret))
# expected volatility
exp_vol = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
print('Expected volatility: {}'.format(exp_vol))
# Sharpe Ratio
SR = exp_ret/exp_vol
print('Sharpe Ratio: {}'.format(SR))
In [66]:
# resetting the random seed
np.random.seed(101)
num_portfolios = 25000
all_weights = np.zeros((num_portfolios, len(stocks.columns)))
exp_ret_arr = np.zeros(num_portfolios)
exp_vol_arr = np.zeros(num_portfolios)
sharpe_arr = np.zeros(num_portfolios)
for i in range(num_portfolios):
weights = np.random.random(4)
weights = weights/np.sum(weights)
all_weights[i,:] = weights
exp_ret_arr[i] = np.sum((log_ret.mean() * weights) * 252)
exp_vol_arr[i] = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
sharpe_arr[i] = exp_ret_arr[i]/exp_vol_arr[i]
In [67]:
# maximum sharpe ratio obtained
sharpe_arr.max()
Out[67]:
In [68]:
# index of maximum sharpe ratio
sharpe_arr.argmax()
Out[68]:
In [69]:
# allocation weights at the maximum sharpe ratio
all_weights[1420, :]
Out[69]:
In [70]:
plt.figure(figsize=(12,8))
plt.scatter(exp_vol_arr, exp_ret_arr, c=sharpe_arr, cmap='plasma')
plt.colorbar(label='Sharpe ratio')
plt.xlabel('Volatility')
plt.ylabel('Return');
max_sr_ret = exp_ret_arr[1420]
max_sr_vol = exp_vol_arr[1420]
plt.scatter(max_sr_vol, max_sr_ret, c='red', s=50, edgecolors='black');
In [71]:
stocks.head()
Out[71]:
In [72]:
all_positions_returns = ((1 + stocks.pct_change()).cumprod() * all_weights[1420, :] * 1000000).dropna().sum(axis=1)
all_positions_returns[-1]
Out[72]:
In [73]:
100 * (all_positions_returns[-1] / all_positions_returns[0] - 1)
Out[73]:
In [74]:
all_positions_returns.plot(figsize=(10,8));
In [75]:
portfolio_val['Total Pos'].plot(figsize=(10,8))
plt.title('Total Portfolio Value');
In [76]:
((1 + stocks.pct_change()).cumprod() * all_weights[1420, :] * 1000000).plot(figsize=(10,8));
In [77]:
portfolio_val.drop(['Total Pos', 'Daily Returns'], axis=1).plot(figsize=(10,8));
In [78]:
def get_ret_vol_sr(weights):
weights = np.array(weights)
ret = np.sum(log_ret.mean() * weights) * 252
vol = np.sqrt(np.dot(weights.T, np.dot(log_ret.cov() * 252, weights)))
sr = ret/vol
return np.array([ret, vol, sr])
In [79]:
from scipy.optimize import minimize
In [80]:
help(minimize)
In [81]:
# Helper functions
def neg_sharpe(weights):
return get_ret_vol_sr(weights)[2] * -1.0
In [82]:
def check_sum(weights):
# return 0 if the sum of the weights is 1
return np.sum(weights) - 1.0
In [83]:
constraints = ({'type' : 'eq', 'fun' : check_sum})
# The (min, max) value bound of each portfolio allocation, essentially
# a number between 0 and 1
bounds = ((0,1),(0,1),(0,1),(0,1))
# initial allocation guess
init_guess = [0.25,0.25,0.25,0.25]
In [92]:
opt_results = minimize(fun=neg_sharpe, x0=init_guess, method='SLSQP', bounds=bounds, constraints=constraints)
opt_results
Out[92]:
In [85]:
# best allocation portfolio
opt_results.x
Out[85]:
In [87]:
# returns array([returns, volatility, Sharpe ratio])
get_ret_vol_sr(opt_results.x)
Out[87]:
In [93]:
# Finding the frontier between the y value (expected log returns) 0 to 0.3
frontier_y = np.linspace(0, 0.3, 100)
def minimize_vol(weights):
return get_ret_vol_sr(weights)[1] # returns volatility
In [96]:
frontier_volatility = []
for possible_return in frontier_y:
constraints = ({'type':'eq', 'fun':check_sum},
{'type':'eq', 'fun': lambda weights: get_ret_vol_sr(weights)[0] - possible_return})
result = minimize(fun=minimize_vol,x0=init_guess,method='SLSQP', constraints=constraints)
frontier_volatility.append(result['fun'])
In [98]:
plt.figure(figsize=(12,8))
plt.scatter(exp_vol_arr, exp_ret_arr, c=sharpe_arr, cmap='plasma')
plt.colorbar(label='Sharpe ratio')
plt.xlabel('Volatility')
plt.ylabel('Return');
plt.plot(frontier_volatility, frontier_y, 'g--', linewidth=3);
Three major types:
1. ETF - Exchange Traded Funds
2. Mutual Funds
3. Hedge Funds
ETFs are exchange traded funds that are constituted of a basket of funds, bonds, commodities, securities etc. Their holding are completely public and transparent and individuals can buy trade the marketable security.
Typically people investing in ETFs are more intrested in a diversified portfolio and want to keep their investment in an ETF for a longer period of time.
One of the most common ETFs is the Spider (SPY) which tracks the S&P500
A mutual fund is an investment vechicle made up of a pool of funds collected from many investors for the purpose of investing in securities such as stocks, bonds, money market instruments and similar assets.
Mutual funds are operated by money managers, whoc invest the fund's capital and attempt to produce captial gains and income for the fund's investors.
A mutual fund's portfolio is structured and maintained to match the investment objectives stated in its prospectus.
Mutual Funds disclose their holdings typically once a quarter, although this can vary by fund.
Hedge funds are alternative investments using pooled funds that employ numerous different strategies to earn active return, or alpha, for their investors.
Hedge funds may be aggressively managed or make use of deritives and leverage in both domestic and international markets with the goal of generating high returns (either in an absolute sense or over a specified market benchmark).
Essentially the goal of hedge funds is to beat the market, or beat the benchmarks.
It is important to note that hedge funds are generally only accessible to accredited investors as they require less SEC regulations than other funds.
One aspect that has set the hedge fund industry apart is the fact that hedge funds face less regulation than mutual funds and other investment vehicles.
Making an order includes the following:
Example orders:
- BUY, AAPL, 200, MARKET
- SELL, TSLA, 400, MARKET
- BUY, AMD, 2000, LIMIT, 13.95
- SELL, NVDA, 150, LIMIT, 160.99
[http://www.investopedia.com/terms/c/capm.asp]
Portfolio Returns:
Market Weights:
In [101]:
from scipy import stats
In [103]:
help(stats.linregress)
In [114]:
import pandas as pd
import pandas_datareader as web
import datetime
In [115]:
start = datetime.datetime(2010, 1, 4)
end = datetime.datetime(2017, 7, 25)
spy_etf = web.DataReader('SPY', 'google', start=start, end= end)
In [117]:
spy_etf.info()
In [119]:
spy_etf.tail()
Out[119]:
In [126]:
start = pd.to_datetime('2010-01-04')
end = pd.to_datetime('2017-07-18')
In [127]:
aapl = web.DataReader('AAPL','google',start,end)
aapl.head()
Out[127]:
In [128]:
import matplotlib.pyplot as plt
%matplotlib inline
In [130]:
aapl['Close'].plot(label='AAPL', figsize=(10,8))
spy_etf['Close'].plot(label='SPY Index')
plt.legend();
In [131]:
aapl['Cumulative'] = aapl['Close']/aapl['Close'].iloc[0]
spy_etf['Cumulative'] = spy_etf['Close']/spy_etf['Close'].iloc[0]
In [135]:
aapl.head()
Out[135]:
In [134]:
aapl['Cumulative'].plot(label='AAPL', figsize=(10,8))
spy_etf['Cumulative'].plot(label='SPY')
plt.legend();
In [136]:
aapl['Daily Return'] = aapl['Close'].pct_change()
spy_etf['Daily Return'] = spy_etf['Close'].pct_change()
In [138]:
plt.scatter(aapl['Daily Return'], spy_etf['Daily Return'], alpha=0.5);
In [140]:
beta,alpha,r_value,p_value,std_err = stats.linregress(aapl['Daily Return'].iloc[1:],spy_etf['Daily Return'].iloc[1:])
beta
Out[140]:
In [141]:
alpha
Out[141]:
In [142]:
r_value
Out[142]:
Conclusion: Since, r_value aapl and spy_etf are not a good fit.
[http://www.investopedia.com/walkthrough/corporate-finance/5/dividends/stock-dividends-splits.aspx]
Conclusion: Use the Adjusted Close instead of Close, as Adj. Close does take into account stock splits and dividends.
[http://www.investopedia.com/terms/e/efficientmarkethypothesis.asp]
EMH is an investment theory that states it is impossible to 'beat the market' because stock market efficiency causes existing share prices to always incorporate and reflect all relevant information.
According to the EMH, stocks always trade at their fair value on stock exchanges, making it impossible for investors to either purchase undervalued stocks or sell stocks for inflated prices.
As such, it should be impossible to outperform the overall market through expert stock selection or market timing, and the only way an investor can possibly obtain higher returns is by purchasing riskier investments.
The success of different strategies and hedge funds implies the strongest interpretation of EMH is probably not true.
There are also events that have shown the market to be overvalued at certain points in history (financial crises, dot com bubble, etc)
In [ ]: