<jupyter_start><jupyter_text>Finance notebooks  Some basics: Portfolio finance  Let's make some portfolio math, as usual with **python** (https://www.python.org/) and **pandas** (http://pandas.pydata.org/).<jupyter_code>import pandas as pd
import pandas_datareader.data as web
import matplotlib.pyplot as plt
import numpy as np

# Defines the chart color scheme using Tableu's Tableau10 
plt.style.use('https://gist.githubusercontent.com/mbonix/8478091db6a2e6836341c2bb3f55b9fc/raw/7155235ed03e235c38b66c160d402192ad4d94d9/tableau10.mplstyle')

%matplotlib inline<jupyter_output><empty_output><jupyter_text>We are going to download some prices, just as an example. We'll work on **Apple** (AAPL), **Alphabet** (former Google, GOOGL), **Microsoft** (MSFT), **McDonald's** (MCD), **Coca-Cola** (KO) over a ten-year period.<jupyter_code># List of stocks tickers
STOCKS = ['NASDAQ:AAPL', 'NASDAQ:GOOGL', 'NASDAQ:MSFT', 'NYSE:MCD', 'NYSE:KO']

# Analysis period
START = '12-30-2006'
END = '12-31-2016'

data = web.DataReader(STOCKS, 'google', pd.to_datetime(START), pd.to_datetime(END))

# Only Close prices are relevant for analysis
prices = data.loc['Close', :, :]
prices.tail(10)<jupyter_output><empty_output><jupyter_text>Now, let's calculate an **annualized return** for each of the stocks over the entire period.<jupyter_code>compound_returns = (1 + (prices.iloc[-1, :]).to_frame().T / (prices.iloc[0, :])) ** 0.1 - 1
compound_returns.index = ['Compound Annual Return']
compound_returns<jupyter_output><empty_output><jupyter_text>Then we could compute an **annual volatility** over the period, daily sampled.<jupyter_code>annual_volatility = prices.fillna(method='pad').pct_change().std().apply(lambda x: x * 260 ** 0.5).to_frame().T
annual_volatility.index = ['Annual Volatility']
annual_volatility<jupyter_output><empty_output><jupyter_text>Let's join these stats into a single dataframe.<jupyter_code>stock_moments = pd.concat((compound_returns, annual_volatility))
stock_moments<jupyter_output><empty_output><jupyter_text>And let's make a chart, plotting volatities on X-axis and returns on Y-axis.<jupyter_code>f = plt.figure(figsize=(10, 8));
ax = f.add_subplot(111);
for n in range(len(stock_moments.columns)):
    ax.plot(stock_moments.loc['Annual Volatility', stock_moments.columns[n]], stock_moments.loc['Compound Annual Return', stock_moments.columns[n]], ls='', marker='o', label=stock_moments.columns[n]);
ax.legend(loc='best');
ax.set_xlabel(stock_moments.index[1]);
ax.set_ylabel(stock_moments.index[0]);
ax.set_title('Stock risk reward profile');<jupyter_output><empty_output><jupyter_text>A few considerations: **Apple** (AAPL) has delivered a superb return over the period, but at the cost of the highest volatility (risk), while the *old-economy* stocks (**Coca-Cola**: KO e **McDonald's**: MCD) have been characterized by a lower risk and a lower return. Now, let's calculate the risk-reward profile of a **equally-weighted portfolio** composed by these five stocks. First of all, the return of the portfolio, which is the average of the stock returns, equally-weighted.<jupyter_code>WEIGHTS = [0.2, 0.2, 0.2, 0.2, 0.2]
port_moments = pd.DataFrame(index=stock_moments.index, columns=['Equally-weighted Portfolio'])
port_moments.loc['Compound Annual Return', 'Equally-weighted Portfolio'] = (stock_moments.loc['Compound Annual Return', :] * WEIGHTS).sum()
port_moments<jupyter_output><empty_output><jupyter_text>In order to calculate volatility, we need a **covariance matrix**. Luckily enough, Pandas has a function to do exactly that!<jupyter_code>cov_mat = prices.fillna(method='pad').pct_change().cov().apply(lambda x: x * 260)
cov_mat<jupyter_output><empty_output><jupyter_text>Let's check that the roots of the diagonal of the matrix are the (annualized) standard deviations of the stocks.<jupyter_code>pd.DataFrame(data=[cov_mat.iloc[n, n] ** 0.5 for n in range(len(cov_mat.columns))], index=cov_mat.columns, columns=['Annual Volatility']).T<jupyter_output><empty_output><jupyter_text>The volatility of the portfolio is easily computed.<jupyter_code>port_moments.loc['Annual Volatility', 'Equally-weighted Portfolio'] = np.array(WEIGHTS).dot(cov_mat.values).dot(np.array(WEIGHTS).T) ** 0.5
port_moments<jupyter_output><empty_output><jupyter_text>So, we can plot the risk-reward profile of the **portfolio** on the previous chart.<jupyter_code>f = plt.figure(figsize=(10, 8));
ax = f.add_subplot(111);
for n in range(len(stock_moments.columns)):
    ax.plot(stock_moments.loc['Annual Volatility', stock_moments.columns[n]], stock_moments.loc['Compound Annual Return', stock_moments.columns[n]], ls='', marker='o', label=stock_moments.columns[n]);
ax.plot(port_moments.loc['Annual Volatility','Equally-weighted Portfolio'], port_moments.loc['Compound Annual Return', 'Equally-weighted Portfolio'], ls='', marker='d', label='Equally-weighted Portfolio');
ax.legend(loc='best');
ax.set_xlabel(stock_moments.index[1]);
ax.set_ylabel(stock_moments.index[0]);
ax.set_title('Stock risk reward profile');<jupyter_output><empty_output><jupyter_text>We can notice that the portfolio risk profile is similar to *old economy* stocks, but the historical return benefits from the higher returns of *tech stocks*. **Diversification** at work! Now, let's build some **random portfolios** and calculate their risk-reward profile. First of all, the weights matrix.<jupyter_code>wgts = pd.DataFrame(np.array(np.random.uniform(0, 1, (5000, 5))), columns=prices.columns)
w = wgts.apply(lambda x: x / wgts.sum(axis=1))
w<jupyter_output><empty_output><jupyter_text>Let's compute **first two moments** of the simulated portfolios.<jupyter_code>sim_ports = pd.DataFrame(index=w.index, columns=port_moments.index)
sim_ports.loc[:, 'Compound Annual Return'] = (stock_moments.loc['Compound Annual Return', :] * w).sum(axis=1)
sim_ports.loc[:, 'Annual Volatility'] = np.diag(w.values.dot(cov_mat.values).dot(w.values.T)) ** 0.5
sim_ports<jupyter_output><empty_output><jupyter_text>And let's plot those fancy random portfolios on the **risk-reward chart**.<jupyter_code>f = plt.figure(figsize=(10, 8));
ax = f.add_subplot(111);
ax.plot(sim_ports.loc[0, 'Annual Volatility'], sim_ports.loc[0, 'Compound Annual Return'], ls='', marker='.', color='#cccccc', label='Random-weighted Portfolios');
for m in range(len(sim_ports.index)):
    ax.plot(sim_ports.loc[m, 'Annual Volatility'], sim_ports.loc[m, 'Compound Annual Return'], ls='', marker='.', color='#cccccc');
for n in range(len(stock_moments.columns)):
    ax.plot(stock_moments.loc['Annual Volatility', stock_moments.columns[n]], stock_moments.loc['Compound Annual Return', stock_moments.columns[n]], ls='', marker='o', label=stock_moments.columns[n]);
ax.plot(port_moments.loc['Annual Volatility','Equally-weighted Portfolio'], port_moments.loc['Compound Annual Return', 'Equally-weighted Portfolio'], ls='', marker='d', label='Equally-weighted Portfolio');
ax.legend(loc='best');
ax.set_xlabel(stock_moments.index[1]);
ax.set_ylabel(stock_moments.index[0]);
ax.set_title('Stock risk reward profile');<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.14925162383507484, 'fraction_numerical': 0.019344817848065517, 'mean_word_length': 7.533734939759036, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 57, 'https://': 2, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '304284', 'license': 'MIT', 'n_tokens_mistral': 2620, 'n_tokens_neox': 2428, 'n_words': 556, 'path': 'Portfolio.ipynb', 'repo_name': 'mbonix/finance-notebooks', 'size': '7082'}