<jupyter_start><jupyter_text>Monte Carlo Simulations with the Efficient Frontier Summary of Efficient FrontierThe Efficient fronter is a set of optimal portfolios that offer the highest expected return for a defined level of risk. It provides a great visualization on how to choose an optimal portfolio mathematically. _*Risk is defined as the assests actual return differing from our expected return.*_"The efficient frontier is the set of optimal portfolios that offer the highest expected return for a defined level of risk or the lowest risk for a given level of expected return. Portfolios that lie below the efficient frontier are sub-optimal because they do not provide enough return for the level of risk." - Investopedia Founder: <NAME>                                               ![image info](Harry_Markowitz.jfif)<NAME> introduced the efficient frontier theory in 1952 and later won a Nobel Memorial Prize in economics for the Modern Portfolio Theory in 1990. This theory is widely taught in every introductory Financial Course throughout the United States. His theory is written in detail in a paper: *Portfolio Selection* (1952). SummaryI will simulate weights on individual companies within a given portfolio to obtain an understanding on what return to risk is desired by the individual.I picked 10 or so companies that are spread out in their corresponding Industries such that we have a relatively "low" correlation with each other. Companies Google | NVIDIA | Facebook  Wells Fargo | Pfizer | COKE   Disney | IMAX | Catepillar Southwest Airlines<jupyter_code>import re
from io import StringIO
from datetime import datetime, timedelta
import requests
import pandas as pd
import numpy as np<jupyter_output><empty_output><jupyter_text>Obtaining the Data Companies of Interest (with their associated ticker)| Technology | Finance | Health | Consumer | Entertainment | Industrials | Transportation || --- | --- | --- |--- | --- | --- | --- || (GOOG) Google | (WFC) Wells Fargo | (PFE) Pfizer | (COKE) Coke |(DIS) Disney | (CAT) Catepillar |(LUV) Southwest Airlines|| (NVDA) NVIDIA | --- | --- | --- | (IMAX) IMAX | --- | --- || (FB) Facebook | --- | --- | --- | --- | --- | --- |<jupyter_code># Getting Data from 6 years back
# I will use the most recent 1 year to determine how well I would have done if I follow the efficient frontier.
# The market is open 252 times in a given year.
# I will get the adjusted close as my main data.
import pandas_datareader as pdr
from datetime import datetime


def get_historical_Data(tickers):
    """
    This function returns a pd dataframe with all of the adjusted closing information
    """
    data = pd.DataFrame()
    names = list()
    for i in tickers:
        data = pd.concat([data,pdr.get_data_yahoo(symbols=i, start=datetime(2013, 10, 11), end=datetime(2020, 10, 11)).iloc[:,5]], axis = 1)
        names.append(i)
    data.columns = names
    return data

#    The ticker names of the companies that we will be looking at.
ticks = ["GOOG", "NVDA", "FB", "WFC","DIS", "IMAX", "LUV", "PFE", "COKE", "CAT"]
d = get_historical_Data(ticks)
print(d.shape)
# Most Recent Data
d.tail()
# Saving the most recent year data such that we can compare...
# Called dT (DataTest)
dT = d.iloc[d.shape[0] - 252:,:] # Data test

# Update the "Training" or "data full"
d = d.iloc[:d.shape[0] - 252,:] # Data Train for the Simulation

print("Testing Data dimensions: ", dT.shape)
print("Training Data dimensions:", d.shape)
dT # Test
d # Train<jupyter_output><empty_output><jupyter_text>Understanding Returns<jupyter_code>from scipy import stats
expected_returns_a = d.pct_change() # Daily returns from trading day to day...
expected_returns_a.columns = ticks # Setting the Column names 
expected_returns_aA = pd.DataFrame(expected_returns_a.mean()*250) # Annualizing the average rate of return
expected_returns_aA = expected_returns_aA.T # Transpose the values 

dar = d.pct_change().iloc[1:,:]+1 # dar = portfolio returns for each period (in this case day to day)
# 6 is the number of years I am working with (Note: Remember that earlier I've took out a year for training purposes.)
gar = pd.DataFrame(np.prod(dar)**(1/float(6)) - 1) # Geometric Average Rate of Return
# print(gar)
full_return_annual = (pd.concat([expected_returns_aA.T, gar], axis = 1))
# DO NOTE that Arithmetic Average Return is not usually an appropriate method
# for calculating the average return and telling others...

# Example: Returns are the following (50%, 30%, -50%) on a yearly basis (jan 1st to dec 31st)
# Average: (50 + 30 - 50) / 3 = 10% average rate of return. This is not a great "representation of how well you done"
# Example
# Start with initial value of $ 100 Dollars: 
# First year becomes 150. 
# Second Year becomes 190. 
# Third year becomes 97.5. You LOST money.

# Geometric Average: (also known as the Compounded annual growth rate)
# Using the example from above...
# ((1+ 0.5) * (1 + 0.3) * (0.5))^(1/3) - 1
# ((1.5)*(1.3)*(0.5))^(1/3) - 1
# .9916 - 1
# -0.0084
# or (-0.84) % average ANNUAL rate of return (more accurate gauge as to how well you've done.)

full_return_annual.columns = ["Average Arithmetic Returns", "Average Geometric Returns"] 
print("Expected Annual Returns ", expected_returns_aA)
print("dar", dar)
print("Full Annual Return", full_return_annual)<jupyter_output>Expected Annual Returns         GOOG      NVDA        FB       WFC       DIS      IMAX       LUV  \
0  0.198358  0.500861  0.260655  0.076283  0.144291  0.007926  0.256635   

        PFE      COKE       CAT  
0  0.087328  0.319925  0.122425  
dar                 GOOG      NVDA        FB       WFC       DIS      IMAX  \
2013-10-14  1.004725  1.015072  1.008145  1.007724  1.009364  1.014739   
2013-10-15  1.006734  0.994835  0.999798  0.994970  0.994164  0.982868   
2013-10-16  1.018163  1.012330  1.033131  1.016129  0.998645  1.011747   
2013-10-17  0.989711  1.001923  1.020923  1.011135  1.001055  0.998502   
2013-10-18  1.137963  1.011516  1.038498  1.000000  1.010991  0.994749   
...              ...       ...       ...       ...       ...       ...   
2019-10-04  1.017822  1.003640  1.005965  1.015058  1.016543  0.990278   
2019-10-07  0.998908  1.012969  0.995733  0.991872  1.004836  0.985040   
2019-10-08  0.984640  0.961482  0.989259  0.979717  0.981436  0.990508   
2019-10-09  [...]<jupyter_text>Equations Utilized  Measuring the Adjusted Risk of ReturnMeasures the risk adjusted rate of return of a portfolio.$$\begin{aligned}Sharpe Ratio = \frac{R_p - R_f}{\sigma_p}\end{aligned}$$$\sigma_p$ = Standard Deviation of Portfolio \$R_p$ = Return of Portfolio \$R_f$ = Return of Risk Free Instrument\Rule of Thumb: Sharpe Ratio < 1 sub-optimal... There is most likely a better option \Sharpe Ratio > 1 is acceptable \Sharpe Ratio > 2 is VERY good \Sharpe Ratio > 3 is EXCELLENT!  Volatility$$\begin{aligned}\sum_{i=0}^N \sum_{j=0}^N {\sigma_{ij}}{X_i X}\end{aligned}$$$X$ = Weights in Portfolio \$\sigma_{ij}$ = Variance - Covariance Matrix Expected Return$$\begin{aligned}\sum_{i=0}^N X_i \mu_i\end{aligned}$$\$X$ = Weights in Porfolio \$\mu_i$ = Arithmetic Average Rate of Return for $i^{th}$ security<jupyter_code># Storing lists that retain returns, volatility, and weights of the Simulated portfolios
portfolio_returns = []
portfolio_volatility = []
sharpe_ratio = []

# This is what is going to be randomized
stock_weights = []

# Number of Indiviudal securities that will be a part of the portfolio
num_assets = len(ticks)
# Number of simulated iterations
num_portfolios = 100000

# Getting the covariance matrix
# Gets a percentage change one day to the next
daily_returns = d.pct_change()
# Converting daily returns to annual returns (standardizing to a year)
annual_returns = (daily_returns.mean() * 250) + 1

# Obtaining the covariance of annual
cov_daily = daily_returns.cov() # Covariance
cov_annual = cov_daily*250 # Covariance Annualized

print(annual_returns)
# Setting seed of interpretability
np.random.seed(3)
# Filling in the lists with a simulated return, risk, and a given weight
# num_portfolios
for i in range(num_portfolios):
    # Randomly assign weights
    weights = np.random.random(num_assets)
    # Standardize the weights
    weights /= np.sum(weights)
    returns = (np.dot(weights, (annual_returns)))
    volatility = np.sqrt(np.dot(weights.T, np.dot(cov_annual, weights)))
    """
    sharpe ratio: This calculates the risk adjusted return
    It suggests that adding assets to a portfolio that have low correlation can decrease portfolio risk without 
    sacrificing return 
    """
    sharpe = ((returns-1) / volatility)
    sharpe_ratio.append(sharpe)
    portfolio_returns.append(returns-1)
    portfolio_volatility.append(volatility)
    stock_weights.append(weights)

# Storing the portfolio values
portfolio = {'Returns': portfolio_returns,
             'Volatility': portfolio_volatility,
             'Sharpe Ratio': sharpe_ratio}

# Add an additional entry to the portfolio such that each indivudal weight is incorporated for its corresponding company
for counter,symbol in enumerate(ticks):
    portfolio[symbol+' Weight'] = [Weight[counter] for Weight in stock_weights]

# make a nice dataframe of the extended dictionary
df = pd.DataFrame(portfolio)
df
# PLotting the efficient frontier.
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
df.plot.scatter(x='Volatility', y='Returns', c='Sharpe Ratio',
                cmap='RdYlGn', edgecolors='black', figsize=(10, 8), grid=True)
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Returns')
plt.title('Efficient Frontier')
plt.show()
# Finding the Optimal Portfolio
min_volatility = df['Volatility'].min()
max_sharpe = df['Sharpe Ratio'].max()

# use the min, max values to locate and create the two special portfolios
sharpe_portfolio = df.loc[df['Sharpe Ratio'] == max_sharpe]
min_variance_port = df.loc[df['Volatility'] == min_volatility]

# plot frontier, max sharpe & min Volatility values with a scatterplot
plt.style.use('fivethirtyeight')
df.plot.scatter(x='Volatility', y='Returns', c='Sharpe Ratio',
                cmap='RdYlGn', edgecolors='black', figsize=(10, 8), grid=True)
plt.scatter(x=sharpe_portfolio['Volatility'], y=sharpe_portfolio['Returns'], c='red', marker='D', s=200)
plt.scatter(x=min_variance_port['Volatility'], y=min_variance_port['Returns'], c='blue', marker='D', s=200 )
plt.xlabel('Volatility (Std. Deviation)')
plt.ylabel('Expected Returns')
plt.title('Efficient Frontier')
plt.show()
# Additional Details
r_ef = pd.concat([min_variance_port.T,sharpe_portfolio.T], axis = 1)
r_ef.columns = ["Minimum Risk Adjusted Values", "Max Risk Adjusted Values"]
print(r_ef)<jupyter_output>Minimum Risk Adjusted Values  Max Risk Adjusted Values
Returns                           0.150794                  0.309052
Volatility                        0.141734                  0.197737
Sharpe Ratio                      1.063918                  1.562948
GOOG Weight                       0.035359                  0.106809
NVDA Weight                       0.018026                  0.250944
FB Weight                         0.011483                  0.038666
WFC Weight                        0.089905                  0.007228
DIS Weight                        0.197421                  0.060073
IMAX Weight                       0.040720                  0.005087
LUV Weight                        0.103566                  0.206534
PFE Weight                        0.299035                  0.006567
COKE Weight                       0.092985                  0.254782
CAT Weight                        0.111500                  0.063310<jupyter_text>If I were to invest 1,000 USD last year... what would I have now?<jupyter_code>amount_invest = 1000
expected_return = pd.DataFrame(amount_invest * (1+r_ef.iloc[0,:]))
print("----------------------------------------------------------------")
print("                Expected Returns on my Portfolio")
print("----------------------------------------------------------------")
print(expected_return.T)
print("")
print("----------------------------------------------------------------")
print("If I invested", amount_invest,"USD on |", dT.index[0],"| I would have...")
actual_return = (dT.iloc[dT.shape[0]-1,:] - dT.iloc[0,:]) / ( dT.iloc[0,:])
# Multipling the weights to the price at the beginning of the year
beg_price = (dT.iloc[0,:])
end_price = dT.iloc[dT.shape[0]-1,:]
print("----------------------------------------------------------------")
# Weights derived from the Efficient Frontier Portfolio
# Weights for Minimum Risk
w = np.array(r_ef.iloc[3:,0])

percentage_change = (end_price - beg_price)/(beg_price)+1
print("Using the Portfolio Weights for Minimum Risk Return Portfolio")
money_left = sum(w * percentage_change* amount_invest)
print("")
print("    Starting balance $ 1000 : Ending with $ ",round(money_left, 2))
print("")
print("----------------------------------------------------------------")
print("Using the Portfolio Weights Maximized Risk-Return Portfolio")
# Weights for Maxmimum Risk
w1 = np.array(r_ef.iloc[3:,1])

money_left1 = sum(w1 * percentage_change* amount_invest)
print("")
print("    Starting balance $ 1000 : Ending with $ ", round(money_left1,2))
print("")

# Other models to take a look at...
# That try to predict a securities rate of return
# CAPM
# CCAPM
# ICAPM
# Fama French 3 factor, 4 factor, and 5 factor model.<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1277798438081071, 'fraction_numerical': 0.06366679062848643, 'mean_word_length': 2.980461811722913, 'pattern_counts': {'":': 0, '<': 22, '<?xml version=': 0, '>': 24, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 7, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '5', 'dir': 'jupyter-structured-clean-dedup', 'id': '25321', 'license': 'MIT', 'n_tokens_mistral': 4721, 'n_tokens_neox': 4058, 'n_words': 1383, 'path': 'Monte Carlo Efficient Frontier/Monte Carlo Simulation on the Efficient Frontier.ipynb', 'repo_name': 'SpencerPao/Quantitative_Strategies', 'size': '13463'}