Last active
August 29, 2015 14:12
-
-
Save logic/f3062554ee5fd2ba0477 to your computer and use it in GitHub Desktop.
Calculate how much money you'd leave on the table, if you quit your (stock-vesting) job right now.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
"""Calculate how much money you'd leave on the table if you quit right now. | |
Place a file named TICKER-stock.csv (where TICKER is the ticker symbol you | |
want to track) in the same directory as this script, formatted as: | |
MM/DD/YYYY,shares | |
Where MM is the month, DD is the date, and YYYY is the year that "shares" | |
number of your TICKER shares will vest. | |
A file named "TICKER-cache.txt" (again, where TICKER is the ticker symbol | |
you looked up) will be created in the same directory as the script, caching | |
the last price retrieved from Yahoo Finance for up to an hour so you don't | |
keep requesting it (handy for running this when you logging in). | |
Run as "remaining.py <stock>", ie. "remaining.py MSFT". | |
""" | |
from __future__ import print_function | |
import argparse | |
import csv | |
import datetime | |
import json | |
import os | |
import urllib | |
DATADIR = os.path.dirname(__file__) | |
SHARES = os.path.join(DATADIR, "%s-shares.csv") | |
QUOTE = ("https://query.yahooapis.com/v1/public/yql?q=select%%20*%%20from" | |
"%%20yahoo.finance.quote%%20where%%20symbol%%3D%%22%s%%22&" | |
"format=json&diagnostics=true&env=store%%3A%%2F%%2Fdatatables.org" | |
"%%2Falltableswithkeys&callback=") | |
CACHED_QUOTE = os.path.join(DATADIR, "%s-cache.txt") | |
def get_stock(ticker): | |
"""Retrieve the current stock price for a given ticker symbol.""" | |
cached = False | |
result = None | |
cachedate = 0 | |
# Check if there's a cache. | |
try: | |
oldquote = os.stat(CACHED_QUOTE % ticker) | |
cachedate = datetime.datetime.fromtimestamp(oldquote.st_mtime) | |
result = open(CACHED_QUOTE % ticker, "r").read().strip() | |
cached = True | |
except (OSError, IOError): | |
pass | |
# If we didn't get a result from cache, or if the date on the cache is too | |
# old, force a reload from network. | |
cachelimit = datetime.datetime.now() - datetime.timedelta(hours=1) | |
if result is None or cachedate < cachelimit: | |
try: | |
query = json.load(urllib.urlopen(QUOTE % ticker)) | |
result = query["query"]["results"]["quote"]["LastTradePriceOnly"] | |
with open(CACHED_QUOTE % ticker, "w") as oldquote: | |
print(result, file=oldquote) | |
cached = False | |
except: # pylint: disable=bare-except | |
# If we got this far and still don't have a result, bail out. | |
if result is None: | |
raise | |
return float(result), cached | |
def import_vesting(ticker): | |
"""Given a ticker symbol, parse a csv of vesting dates.""" | |
vested = 0 | |
vesting = {} | |
with open(SHARES % ticker, "r") as csvfh: | |
for date, amount in csv.reader(csvfh): | |
date = datetime.datetime.strptime(date, "%m/%d/%Y") | |
amount = int(amount) | |
if date > datetime.datetime.now(): | |
vesting[date] = amount | |
else: | |
vested += amount | |
return vested, vesting | |
def output_upcoming(vesting, total, stockval): | |
"""For a set of upcoming vesting events, output a summary.""" | |
shrwidth = max(len(str(x)) for x in vesting.itervalues()) | |
for nextvest in sorted(vesting.keys()): | |
nextvestamt = vesting[nextvest] | |
nextshares = ("%%%dd" % shrwidth) % nextvestamt | |
nextvestpct = (float(nextvestamt) / total) * 100 | |
nextvestval = nextvestamt * stockval | |
print(" On %s, %s (%.2f%%) shares will vest, worth $%.2f." % | |
(nextvest.strftime("%b %d %Y"), nextshares, nextvestpct, | |
nextvestval)) | |
def output_remaining(ticker, verbose=False): | |
"""For the given ticker, output a summary of how much is left.""" | |
vested, vesting = import_vesting(ticker) | |
unvested = sum(vesting.values()) | |
total = vested + unvested | |
unvestpct = (float(unvested) / total) * 100 | |
stockval, cached = get_stock(ticker) | |
unvestval = unvested * stockval | |
stock = "%.2f%s" % (stockval, " (cached)" if cached else "") | |
print("%d/%d (%.2f%%) shares, worth $%.2f at $%s/share" % | |
(unvested, total, unvestpct, unvestval, stock)) | |
if verbose: | |
output_upcoming(vesting, total, stockval) | |
def main(): | |
"""__main__""" | |
parser = argparse.ArgumentParser() | |
parser.add_argument("ticker", help="Stock ticker symbol to look up") | |
parser.add_argument("-v", "--verbose", action="store_true", | |
help="Show all upcoming vesting events") | |
args = parser.parse_args() | |
output_remaining(args.ticker, args.verbose) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment