January 1, 0001

title: “vestibulum vitghgae dictum aliqua phasellus magnis at” date: 2018-05-30 tags: [“aliquam incididunt”, “notes”, “velit”] categories: [“aliquam auctor”, “do”] description: “vulputate sem sapien felis ullamcorper risus mi eu metus integer posuere fermentum” draft: false

import pandas as pd
import os
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
# Constants
data_folder='data_2020_09_25'
variable='Adj Close'
start_date = '2010-09-25'
end_date = '2020-09-25'
n_stocks=len(os.listdir(data_folder))
def get_stocks_data_from_folder(data_folder, variable, start_date, end_date):
    files_list=os.listdir(data_folder)
    df=pd.DataFrame()
    for filename in files_list:
        file_path=os.path.join(data_folder,filename)
        col_name=filename.replace('.NS.csv','')
        df[col_name]=read_stock_variable_from_CSV(file_path,variable,start_date, end_date)[variable]

    return df

def read_stock_variable_from_CSV(file_path,variable,start_date, end_date):
    data=pd.read_csv(file_path)
    data['Date'] = data['Date'].apply(pd.to_datetime)
    data.set_index('Date',inplace=True)
    mask = (data.index >= start_date) & (data.index <= end_date)
    return data.loc[mask][['Adj Close']]

Adjusted closing is closing price but it includes stock splitting, dividends, etc.

data_adj_close=get_stocks_data_from_folder(data_folder, 'Adj Close', start_date, end_date)
missing_values_count = data_adj_close.isnull().sum()
print(missing_values_count)
data_adj_close.dropna(inplace=True)
AXISBANK     8
DABUR        8
DRREDDY      8
ICICIBANK    8
INFY         8
RELIANCE     8
SBIN         8
SUNPHARMA    8
TCS          8
dtype: int64
data_adj_close.plot(figsize=(16,8))
<matplotlib.axes._subplots.AxesSubplot at 0x1219cbdf708>

png

daily_returns= data_adj_close.pct_change(1)
daily_returns.dropna(inplace=True)
daily_logreturns=np.log(1+daily_returns)
daily_logreturns['AXISBANK'].plot(figsize=(16,8))
<matplotlib.axes._subplots.AxesSubplot at 0x1219d46c048>

png

daily_logreturns['DABUR'].plot(figsize=(16,8))
<matplotlib.axes._subplots.AxesSubplot at 0x1219d4fcbc8>

png

daily_logreturns.plot(figsize=(16,8))
<matplotlib.axes._subplots.AxesSubplot at 0x1bc3ef49a08>

png

fig = plt.figure(figsize = (15,10))
ax = fig.gca()
daily_logreturns.hist(ax = ax,bins=100)
C:\Users\bharath\Anaconda3\lib\site-packages\ipykernel_launcher.py:3: UserWarning: To output multiple subplots, the figure containing the passed axes is being cleared
  This is separate from the ipykernel package so we can avoid doing imports until





array([[<matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D1DBA88>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D39A888>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D3D4308>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D5BA0C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D5EEF08>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D623888>],
       [<matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D65E6C8>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D695548>,
        <matplotlib.axes._subplots.AxesSubplot object at 0x000001BC3D6A0C88>]],
      dtype=object)

png

daily_logreturns.mean()*252 # per year
AXISBANK     0.059558
DABUR        0.168364
DRREDDY      0.133080
ICICIBANK    0.107442
INFY         0.184585
RELIANCE     0.164989
SBIN         0.014902
SUNPHARMA    0.100159
TCS          0.193752
dtype: float64
daily_logreturns.cov()*252 # per year
.dataframe tbody tr th {
    vertical-align: top;
}

.dataframe thead th {
    text-align: right;
}

def get_portfolio_return(weights, daily_logreturns):
    return np.sum(daily_logreturns.mean()*weights)*252 # per year - 252 trading days
def get_portfolio_variance(weights, daily_logreturns):
    return np.sqrt(np.dot(weights.T, np.dot(daily_logreturns.cov()*252,weights)))
def generate_porfolios_stats_randomly(daily_logreturns, n_portfolios):
    n_stocks=len(daily_logreturns.columns)
    pf_returns=[]
    pf_variances=[]

    for i in range(n_portfolios):
        weights=np.random.random(n_stocks)
        weights /= np.sum(weights)
        pf_returns.append(get_portfolio_return(weights, daily_logreturns))
        pf_variances.append(get_portfolio_variance(weights, daily_logreturns))

    pf_returns=np.array(pf_returns)
    pf_variances=np.array(pf_variances)
    return pf_returns, pf_variances
returns, variances = generate_porfolios_stats_randomly(daily_logreturns, 10000)
def plot_portfolios(pfreturns,pfvariances):
    plt.figure(figsize = (15,8))
    plt.scatter(pfvariances, pfreturns,c=pfreturns/pfvariances, marker='o' , edgecolor='black')
    plt.grid(True)
    plt.xlabel('Expected Variance')
    plt.ylabel('Expected Return')
    plt.colorbar(label= 'Sharp Ratio')
    plt.show()
plot_portfolios(returns,variances)

png

def get_portfolio_stats(weights, daily_logreturns):
    pf_return=get_portfolio_return(weights, daily_logreturns)
    pf_variance=get_portfolio_variance(weights, daily_logreturns)
    return np.array([pf_return, pf_variance, pf_return/pf_variance])
def get_portfolio_sharp_ration(weights, daily_logreturns):
    return -get_portfolio_stats(weights, daily_logreturns)[2]
from scipy.optimize import minimize
def optimize_portfolio(daily_logreturns):
    n_stocks=len(daily_logreturns.columns)
    bounds = tuple((0,1) for x in range(n_stocks))
    con=({'type':'eq','fun': lambda x: np.sum(x)-1})
    weights=np.random.random(n_stocks)
    weights /= np.sum(weights)
    optimum_pf=minimize(fun=get_portfolio_sharp_ration, x0=weights, args=daily_logreturns, bounds=bounds, method='SLSQP', constraints=con)
    return optimum_pf
opt_pf=optimize_portfolio(daily_logreturns)
print('Optimal weights:',opt_pf['x'].round(3))
Optimal weights: [0.    0.299 0.152 0.    0.131 0.129 0.    0.    0.29 ]
AXISBANK 	DABUR 	DRREDDY 	ICICIBANK 	INFY 	RELIANCE 	SBIN 	SUNPHARMA 	TCS
# optimum portfolio statistics
opt_pf_stats=get_portfolio_stats(opt_pf['x'],daily_logreturns)
print('Expected return, Volatility and Sharp ratio:',opt_pf_stats)
Expected return, Volatility and Sharp ratio: [0.17205126 0.16731228 1.02832414]
def plot_optimum_portfolios(pfreturns,pfvariances):
    plt.figure(figsize = (15,8))
    plt.scatter(pfvariances, pfreturns,c=pfreturns/pfvariances, marker='o' , edgecolor='black')
    plt.plot(opt_pf_stats[1],opt_pf_stats[0],'g*', markersize=20)
    plt.grid(True)
    plt.xlabel('Expected Variance')
    plt.ylabel('Expected Return')
    plt.colorbar(label= 'Sharp Ratio')
    plt.show()
plot_optimum_portfolios(returns,variances)

png