140 lines
4.8 KiB
Python
140 lines
4.8 KiB
Python
"""MC1-P2: Optimize a portfolio.
|
|
|
|
Copyright 2018, Georgia Institute of Technology (Georgia Tech)
|
|
Atlanta, Georgia 30332
|
|
All Rights Reserved
|
|
|
|
Template code for CS 4646/7646
|
|
|
|
Georgia Tech asserts copyright ownership of this template and all derivative
|
|
works, including solutions to the projects assigned in this course. Students
|
|
and other users of this template code are advised not to share it with others
|
|
or to make it available on publicly viewable websites including repositories
|
|
such as github and gitlab. This copyright statement should not be removed
|
|
or edited.
|
|
|
|
We do grant permission to share solutions privately with non-students such
|
|
as potential employers. However, sharing with other current or future
|
|
students of CS 7646 is prohibited and subject to being investigated as a
|
|
GT honor code violation.
|
|
|
|
-----do not edit anything above this line---
|
|
|
|
Student Name: Tucker Balch (replace with your name)
|
|
GT User ID: tb34 (replace with your User ID)
|
|
GT ID: 900897987 (replace with your GT ID)
|
|
"""
|
|
|
|
|
|
import pandas as pd
|
|
import matplotlib.pyplot as plt
|
|
import numpy as np
|
|
import datetime as dt
|
|
import math
|
|
import scipy.optimize as opt
|
|
from util import get_data, plot_data
|
|
|
|
|
|
def get_sharpe_ratio(daily_returns):
|
|
SR = daily_returns.mean() / daily_returns.std()
|
|
k = math.sqrt(252) # daily sampling
|
|
SR = k * SR
|
|
return SR
|
|
|
|
|
|
def get_daily_returns(df):
|
|
daily_returns = df.copy()
|
|
daily_returns[1:] = (df[1:] / df[:-1].values) - 1
|
|
daily_returns = daily_returns.iloc[1:]
|
|
return daily_returns
|
|
|
|
|
|
def get_daily_portfolio_value(prices, allocs):
|
|
normed = prices / prices.iloc[0, :]
|
|
alloced = normed * allocs
|
|
port_values = alloced.sum(axis=1)
|
|
return port_values
|
|
|
|
|
|
def calculate_stats(prices, allocs):
|
|
port_value = get_daily_portfolio_value(prices, allocs)
|
|
cum_ret = (port_value.iloc[-1] / port_value.iloc[0]) - 1
|
|
daily_returns = get_daily_returns(port_value)
|
|
avg_daily_ret = daily_returns.mean()
|
|
std_daily_ret = daily_returns.std()
|
|
sharpe_ratio = get_sharpe_ratio(daily_returns)
|
|
return [cum_ret, avg_daily_ret, std_daily_ret, sharpe_ratio]
|
|
|
|
|
|
# This is the function that will be tested by the autograder
|
|
# The student must update this code to properly implement the functionality
|
|
def optimize_portfolio(sd=dt.datetime(2008,1,1), ed=dt.datetime(2009,1,1), \
|
|
syms=['GOOG','AAPL','GLD','XOM'], gen_plot=False):
|
|
|
|
# Read in adjusted closing prices for given symbols, date range
|
|
dates = pd.date_range(sd, ed)
|
|
prices_all = get_data(syms, dates) # automatically adds SPY
|
|
prices = prices_all[syms] # only portfolio symbols
|
|
prices_SPY = prices_all['SPY'] # only SPY, for comparison later
|
|
|
|
def calc_sr(allocs):
|
|
port_value = get_daily_portfolio_value(prices, allocs)
|
|
cum_ret = (port_value.iloc[-1] / port_value.iloc[0]) - 1
|
|
daily_returns = get_daily_returns(port_value)
|
|
sharpe_ratio = get_sharpe_ratio(daily_returns)
|
|
return sharpe_ratio * -1
|
|
|
|
# find the allocations for the optimal portfolio
|
|
len_syms = len(syms)
|
|
allocs = np.asarray([1 / len_syms for _ in range(len_syms)])
|
|
bounds = [(0, 1) for _ in range(len_syms)]
|
|
consts = ({'type': 'eq', 'fun': lambda inputs: 1.0 - np.sum(inputs)})
|
|
result = opt.minimize(calc_sr, x0=allocs, bounds=bounds, constraints=consts)
|
|
print(result)
|
|
allocs = result.x
|
|
cr, adr, sddr, sr = calculate_stats(prices, allocs)
|
|
|
|
# Compare daily portfolio value with SPY using a normalized plot
|
|
if gen_plot:
|
|
port_val = get_daily_portfolio_value(prices, allocs)
|
|
prices_SPY_normed = prices_SPY / prices_SPY.iloc[0]
|
|
df_temp = pd.concat([port_val, prices_SPY_normed], keys=['Portfolio', 'SPY'], axis=1)
|
|
ax = df_temp.plot(title="SPY and Portfolio Normed", fontsize=12)
|
|
plt.savefig('plot.png')
|
|
plt.show()
|
|
|
|
return allocs, cr, adr, sddr, sr
|
|
|
|
|
|
def report():
|
|
start_date = dt.datetime(2008,6,1)
|
|
end_date = dt.datetime(2009,6,1)
|
|
symbols = ['IBM', 'X', 'GLD', 'JPM']
|
|
allocations, cr, adr, sddr, sr = optimize_portfolio(sd = start_date, ed = end_date,\
|
|
syms = symbols, gen_plot = True)
|
|
|
|
|
|
def test_code():
|
|
# Define input parameters
|
|
# Note that ALL of these values will be set to different values by
|
|
# the autograder!
|
|
start_date = dt.datetime(2009,1,1)
|
|
end_date = dt.datetime(2010,1,1)
|
|
symbols = ['GOOG', 'AAPL', 'GLD', 'XOM', 'IBM']
|
|
allocations, cr, adr, sddr, sr = optimize_portfolio(sd = start_date, ed = end_date,\
|
|
syms = symbols, gen_plot = True)
|
|
|
|
# Print statistics
|
|
print(f"Start Date: {start_date}")
|
|
print(f"End Date: {end_date}")
|
|
print(f"Symbols: {symbols}")
|
|
print(f"Allocations:{allocations}")
|
|
print(f"Sharpe Ratio: {sr}")
|
|
print(f"Volatility (stdev of daily returns): {sddr}")
|
|
print(f"Average Daily Return: {adr}")
|
|
print(f"Cumulative Return: {cr}")
|
|
|
|
if __name__ == "__main__":
|
|
# test_code()
|
|
report()
|