forked from akkana/scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfincompare.py
executable file
·181 lines (146 loc) · 6.64 KB
/
fincompare.py
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/usr/bin/env python
# Graph a bunch of financial assets (stocks or mutual funds)
# specified on the commandline by ticker symbols.
# (Downloads data from Yahoo finance.)
# Usage: fincompare.py LDLAX SRCMX VFIAX FUSVX VSCGX IRCAX SCALX
# You can also specify modules that do custom loading,
# in case you need to parse CSV or Excel files or any other local files.
# Just add those on the commandline, e.g. fincompare mybank.py IRCAX SCALX
# The name of the module must end in .py, e.g. mycsv.py
# It may include a full path to the file, e.g. ~/mydata/parse_my_data.py
# Your module must provide a function with this signature:
# plot_fund(color='b', marker='o')
# (of course you can choose your own defaults for color and marker).
# The function should return a triplet initial, start, end
# where initial is the initial value of the investment in dollars,
# and start and end are datetimes.
# You can't currently specify the date range unless you use a custom module.
# Copyright 2013 by Akkana Peck.
# Share and enjoy under the terms of the GPL v2 or later.
import sys, os
import datetime
import math
import numpy
# http://blog.mafr.de/2012/03/11/time-series-data-with-matplotlib/
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.finance import quotes_historical_yahoo
#
# Read the list of funds to plot.
# If any of these ends in .py, assume it's a custom module;
# we'll try to load the module, which should include this function:
# plot_fund(color='b', marker='o')
#
imported_modules = {}
if len(sys.argv) < 2 :
funds = ['VFIAX', 'FUSVX', 'LDLAX', 'VSCGX', 'IRCAX', 'SCALX', 'SRCMX']
else :
funds = []
for f in sys.argv[1:] :
if f.endswith('.py') :
# First split off any pathname included,
# since python isn't smart about importing from a pathname.
fpath, ffile = os.path.split(f)
if fpath :
sys.path.append(fpath)
try :
imported_modules[f] = __import__(ffile[:-3])
except Exception, e :
print "Couldn't import", f
print e
else :
funds.append(f)
# Set up the plots:
fig = plt.figure(figsize=(12, 8)) # width, height in inches
ax1 = plt.subplot(111)
# Pick a different color and style for each plot.
# Sure would be nice if matplotlib could handle stuff like this for us.
# The impossible-to-find documentation for line styles is at:
# http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.plot
# For colors, to use anything beyond the standard list, see
# http://matplotlib.org/api/colors_api.html
colors = [ 'b', 'r', 'g', 'c', 'y', 'k' ]
styles = [ '-', '--', ':', '-.' ]
markers = [ 'o', '*', 's', '^', 'p', '+', 'D', 'x', '|', 'h' ]
def pick_color(i) :
'''Pick a color that tries to be reasonably different
from other colors so far picked.
'''
return colors[i % len(colors)] \
+ styles[int(i / len(colors))]
# + markers[i%len(markers)]
def plot_funds(tickerlist, initial, start, end) :
'''Plot a fund by its ticker symbol,
normalized to a given initial value.
'''
numdays = (end - start).days
daysinyear = 365.0
print '%9s %9s %9s %9s' % ('Ticker', 'daily', 'CC', 'abs')
# For testing, use something like
# FUSVX = quotes_historical_yahoo('FUSVX', datetime.datetime(2012, 10, 1),
# datetime.datetime(2013, 4, 1),
# asobject=True)
for i, ticker in enumerate(tickerlist) :
# This gives a runtime warning for SCAL, and all the aclose vals
# come out zero. Catching a RuntimeWarning isn't as simple as try;
# http://stackoverflow.com/questions/10519237/python-how-to-avoid-runtimewarning-in-function-definition
# http://stackoverflow.com/questions/9349434/how-do-i-check-for-numeric-overflow-without-getting-a-warning-in-python
fund_data = quotes_historical_yahoo(ticker, start, end, asobject=True)
# Guard against failures of quotes_historical_yahoo;
# without this check you'll see more uncatchable RuntimeWarnings.
if fund_data['aclose'][0] == 0 :
print ticker, ": First adjusted close is 0!"
continue
# Calculate effective daily-compounded interest rate
fixed_pct = fund_data['aclose'][-1]/fund_data['aclose'][0] - 1.
Rcc = daysinyear / numdays * \
numpy.log(fund_data['aclose'][-1] / fund_data['aclose'][0])
# Convert CC return to daily-compounded return:
Rdaily = daysinyear * (math.exp(Rcc / daysinyear) - 1.)
# Another attempt to compute the daily rate, but it's wrong.
# Reff = daysinyear * (math.exp(math.log(fund_data['aclose'][-1]
# - fund_data['aclose'][0])
# /numdays) - 1)
print "%9s %9.2f %9.2f %9.2f" % (ticker,
Rdaily*100, Rcc*100, fixed_pct*100)
# Normalize to the initial investment:
fund_data['aclose'] *= initial / fund_data['aclose'][0]
# and plot
ax1.plot_date(x=fund_data['date'], y=fund_data['aclose'],
fmt=pick_color(i), label=ticker)
initial = None
for i, f in enumerate(imported_modules.keys()) :
try :
initial, start, end = imported_modules[f].plot_fund(color='k',
marker=markers[i%len(markers)])
except Exception, e:
print "Couldn't plot", f
print e
if not initial :
initial = 100000
start = datetime.datetime(2011, 1, 1)
end = datetime.datetime.now()
# Baseline at the initial investment:
plt.axhline(y=initial, color='k')
# This used to happen automatically, but then matplotlib started
# starting at 2000 rather than following the data. So better be sure:
ax1.set_xlim(start, end)
plot_funds(funds, initial, start, end)
ax1.set_ylabel("Value")
plt.grid(True)
plt.legend(loc='upper left')
# Rotate the X date labels. I wonder if there's any way to do this
# only for some subplots? Not that I'd really want to.
# In typical matplotlib style, it's a completely different syntax
# depending on whether you have one or multiple plots.
# http://stackoverflow.com/questions/8010549/subplots-with-dates-on-the-x-axis
# There is apparently no way to do this through the subplot.
# ax1.set_xticklabels(rotation=20)
# fig = plt.figure(1)
plt.xticks(rotation=30)
ax1.set_title("Investment options")
# This is intended for grouping muultiple plots, but it's also the only
# way I've found to get rid of all the extra blank space around the outsides
# of the plot and devote more space to the content itself.
plt.tight_layout()
plt.show()