forked from QuantSoftware/QuantSoftwareToolkit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtutorial8.py
More file actions
141 lines (108 loc) · 5.39 KB
/
tutorial8.py
File metadata and controls
141 lines (108 loc) · 5.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
'''
(c) 2011, 2012 Georgia Tech Research Corporation
This source code is released under the New BSD license. Please see
http://wiki.quantsoftware.org/index.php?title=QSTK_License
for license details.
Created on January, 24, 2013
@author: Sourabh Bajaj
@contact: [email protected]
@summary: Demonstrates the use of the CVXOPT portfolio optimization call.
'''
# QSTK Imports
import QSTK.qstkutil.qsdateutil as du
import QSTK.qstkutil.tsutil as tsu
import QSTK.qstkutil.DataAccess as da
# Third Party Imports
import datetime as dt
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
def getFrontier(na_data):
'''Function gets a 100 sample point frontier for given returns'''
# Special Case with fTarget=None, just returns average rets.
(na_avgrets, na_std, b_error) = tsu.OptPort(na_data, None)
# Declaring bounds on the optimized portfolio
na_lower = np.zeros(na_data.shape[1])
na_upper = np.ones(na_data.shape[1])
# Getting the range of possible returns with these bounds
(f_min, f_max) = tsu.getRetRange(na_data, na_lower, na_upper,
na_avgrets, s_type="long")
# Getting the step size and list of returns to optimize for.
f_step = (f_max - f_min) / 100.0
lf_returns = [f_min + x * f_step for x in range(101)]
# Declaring empty lists
lf_std = []
lna_portfolios = []
# Calling the optimization for all returns
for f_target in lf_returns:
(na_weights, f_std, b_error) = tsu.OptPort(na_data, f_target,
na_lower, na_upper, s_type="long")
lf_std.append(f_std)
lna_portfolios.append(na_weights)
return (lf_returns, lf_std, lna_portfolios, na_avgrets, na_std)
def main():
'''Main Function'''
# S&P 100
ls_symbols = ['AAPL', 'ABT', 'ACN', 'AEP', 'ALL', 'AMGN', 'AMZN', 'APC', 'AXP', 'BA', 'BAC', 'BAX', 'BHI', 'BK', 'BMY', 'BRK.B', 'CAT', 'C', 'CL', 'CMCSA', 'COF', 'COP', 'COST', 'CPB', 'CSCO', 'CVS', 'CVX', 'DD', 'DELL', 'DIS', 'DOW', 'DVN', 'EBAY', 'EMC', 'EXC', 'F', 'FCX', 'FDX', 'GD', 'GE', 'GILD', 'GOOG', 'GS', 'HAL', 'HD', 'HNZ', 'HON', 'HPQ', 'IBM', 'INTC', 'JNJ', 'JPM', 'KFT', 'KO', 'LLY', 'LMT', 'LOW', 'MA', 'MCD', 'MDT', 'MET', 'MMM', 'MO', 'MON', 'MRK', 'MS', 'MSFT', 'NKE', 'NOV', 'NSC', 'NWSA', 'NYX', 'ORCL', 'OXY', 'PEP', 'PFE', 'PG', 'PM', 'QCOM', 'RF', 'RTN', 'SBUX', 'SLB', 'HSH', 'SO', 'SPG', 'T', 'TGT', 'TWX', 'TXN', 'UNH', 'UPS', 'USB', 'UTX', 'VZ', 'WAG', 'WFC', 'WMB', 'WMT', 'XOM']
# Creating an object of the dataaccess class with Yahoo as the source.
c_dataobj = da.DataAccess('Yahoo')
ls_all_syms = c_dataobj.get_all_symbols()
# Bad symbols are symbols present in portfolio but not in all syms
ls_bad_syms = list(set(ls_symbols) - set(ls_all_syms))
for s_sym in ls_bad_syms:
i_index = ls_symbols.index(s_sym)
ls_symbols.pop(i_index)
# Start and End date of the charts
dt_end = dt.datetime(2010, 1, 1)
dt_start = dt_end - dt.timedelta(days=365)
dt_test = dt_end + dt.timedelta(days=365)
# We need closing prices so the timestamp should be hours=16.
dt_timeofday = dt.timedelta(hours=16)
# Get a list of trading days between the start and the end.
ldt_timestamps = du.getNYSEdays(dt_start, dt_end, dt_timeofday)
ldt_timestamps_test = du.getNYSEdays(dt_end, dt_test, dt_timeofday)
# Reading just the close prices
df_close = c_dataobj.get_data(ldt_timestamps, ls_symbols, "close")
df_close_test = c_dataobj.get_data(ldt_timestamps_test, ls_symbols, "close")
# Filling the data for missing NAN values
df_close = df_close.fillna(method='ffill')
df_close = df_close.fillna(method='bfill')
df_close_test = df_close_test.fillna(method='ffill')
df_close_test = df_close_test.fillna(method='bfill')
# Copying the data values to a numpy array to get returns
na_data = df_close.values.copy()
na_data_test = df_close_test.values.copy()
# Getting the daily returns
tsu.returnize0(na_data)
tsu.returnize0(na_data_test)
# Calculating the frontier.
(lf_returns, lf_std, lna_portfolios, na_avgrets, na_std) = getFrontier(na_data)
(lf_returns_test, lf_std_test, unused, unused, unused) = getFrontier(na_data_test)
# Plotting the efficient frontier
plt.clf()
plt.plot(lf_std, lf_returns, 'b')
plt.plot(lf_std_test, lf_returns_test, 'r')
# Plot where the efficient frontier would be the following year
lf_ret_port_test = []
lf_std_port_test = []
for na_portfolio in lna_portfolios:
na_port_rets = np.dot(na_data_test, na_portfolio)
lf_std_port_test.append(np.std(na_port_rets))
lf_ret_port_test.append(np.average(na_port_rets))
plt.plot(lf_std_port_test, lf_ret_port_test, 'k')
# Plot indivisual stock risk/return as green +
for i, f_ret in enumerate(na_avgrets):
plt.plot(na_std[i], f_ret, 'g+')
# # Plot some arrows showing transistion of efficient frontier
# for i in range(0, 101, 10):
# plt.arrow(lf_std[i], lf_returns[i], lf_std_port_test[i] - lf_std[i],
# lf_ret_port_test[i] - lf_returns[i], color='k')
# Labels and Axis
plt.legend(['2009 Frontier', '2010 Frontier',
'Performance of \'09 Frontier in 2010'], loc='lower right')
plt.title('Efficient Frontier For S&P 100 ')
plt.ylabel('Expected Return')
plt.xlabel('StDev')
plt.savefig('tutorial8.pdf', format='pdf')
if __name__ == '__main__':
main()