Last active
October 10, 2020 16:34
-
-
Save zapstar/a888cd0fc3ca087672e05e8d2e59c1d5 to your computer and use it in GitHub Desktop.
Stock Options Calculator (For a startup's employee)
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 python3 | |
""" | |
Stock Options Calculator | |
Helps you get a fair idea of the costs involved and profits that can be gained in the future. | |
Example Usage: | |
# --option-grant 10000,0.25 10000,0.5 --exercise-fmv 1.25 --income-tax-rate 0.3 --exercise-exch-rate 73 --sale-fmv 2.5 --capital-gain-rate 0.2 --sale-exch-rate 75 --indexation-ratio 1.075 | |
""" | |
import argparse | |
from typing import List, Tuple | |
def compute(stock_options: List[Tuple[int, float]], | |
exercise_fmv: float, | |
tax_slab_rate: float, | |
exercise_exch_rate: float, | |
sale_fmv: float, | |
cap_gain_rate: float, | |
cii_ratio: float, | |
sale_exch_rate: float) -> None: | |
print('Options Grant:') | |
for num_vested_shares, strike_price in stock_options: | |
print(f'* Vested: {num_vested_shares}, Strike Price (in $): {strike_price}') | |
print('Fair Market Value at exercise (in $):', exercise_fmv) | |
print(f'Income Tax Rate at exercise: {tax_slab_rate * 100}%') | |
print('Exchange rate of $ at exercise:', exercise_exch_rate) | |
# Assume metrics to be zero | |
cur_market_value = 0.0 | |
cost_to_purchase = 0.0 | |
total_perquisite = 0.0 | |
# Compute basic metrics | |
for num_vested_shares, strike_price in stock_options: | |
cost_to_purchase += num_vested_shares * strike_price | |
cur_market_value += num_vested_shares * exercise_fmv | |
total_perquisite += (exercise_fmv - strike_price) * num_vested_shares | |
# Compute tax liability for during exercise | |
if total_perquisite > 0: | |
tax_pay_exercise = total_perquisite * tax_slab_rate | |
else: | |
tax_pay_exercise = 0 | |
total_cost_exercise = cost_to_purchase + tax_pay_exercise | |
# Print stuff related to exercise | |
print('Cost to employee to exercise options:', cost_to_purchase * exercise_exch_rate) | |
print('Current Market Value of shares:', cur_market_value * exercise_exch_rate) | |
print('Total Discount:', (cur_market_value - cost_to_purchase) * exercise_exch_rate) | |
print('Income Tax at exercise:', tax_pay_exercise * exercise_exch_rate) | |
print('Total cost to employee at exercise:', total_cost_exercise * exercise_exch_rate) | |
print('Unrealized Profit at exercise:', (cur_market_value - total_cost_exercise) * exercise_exch_rate) | |
# Compute future values if asked for | |
if sale_fmv is not None: | |
print('Fair Market value at sale (in $):', sale_fmv) | |
print(f'Capital Gains Tax Rate at sale: {cap_gain_rate * 100}%') | |
print('Indexation Ratio:', cii_ratio) | |
print('Exchange rate of $ at sale:', exercise_exch_rate) | |
fut_market_value = 0.0 | |
for num_vested_shares, _ in stock_options: | |
fut_market_value += num_vested_shares * sale_fmv | |
# Compute capital gains for the future | |
capital_gain = fut_market_value - cur_market_value * cii_ratio | |
if capital_gain > 0: | |
tax_pay_sale = capital_gain * cap_gain_rate | |
else: | |
tax_pay_sale = 0 | |
# Print stuff related to future values | |
print('Market Value of shares at sale:', fut_market_value * sale_exch_rate) | |
print('Tax to be paid on sale:', tax_pay_sale * sale_exch_rate) | |
future_total_profit = (fut_market_value - tax_pay_sale) * sale_exch_rate - total_cost_exercise * exercise_exch_rate | |
print('Future Profit on sale:', future_total_profit) | |
def option_grant_type(s): | |
try: | |
num_options, strike_price = [float(x) for x in s.split(',')] | |
except ValueError: | |
raise argparse.ArgumentTypeError('Options should be NUM_OPTIONS,STRIKE_PRICE') | |
return num_options, strike_price | |
def main(): | |
# Parse the arguments | |
parser = argparse.ArgumentParser() | |
# Exercised Options. List of (Shares, Strike Price) | |
parser.add_argument('--option-grant', | |
type=option_grant_type, | |
help='Options Grant Tuple[Number of options (> 0), Strike Price (> 0)]', | |
nargs='+') | |
# Fair market value at exercise | |
parser.add_argument('--exercise-fmv', required=True, type=float, help='Fair Market Value at exercise (> 0)') | |
# Tax rate at exercise | |
parser.add_argument('--income-tax-rate', required=True, type=float, help='Income Tax rate slab [0, 1)') | |
# Exchange rate from dollars to rupees | |
parser.add_argument('--exercise-exch-rate', type=float, help='Dollar Exchange rate on exercise', default=1) | |
# Fair Market Value at sale | |
parser.add_argument('--sale-fmv', type=float, help='Fair Market value at sale') | |
# Tax rate for capital gains during sale | |
parser.add_argument('--capital-gain-rate', type=float, help='Capital Gains Taxation rate [0, 1)') | |
# Ratio of cost inflation indices (sale date / excercise date) | |
parser.add_argument('--indexation-ratio', type=float, default=1, | |
help='Ratio of cost inflation indices (Sale Date / Exercise Date)') | |
# Exchange rate from dollars to rupees | |
parser.add_argument('--sale-exch-rate', type=float, help='Dollar Exchange rate on sale date', default=1) | |
# Parse and compute. | |
ops = parser.parse_args() | |
# If sale FMV is given, capital gains tax is a must | |
if bool(ops.sale_fmv) ^ bool(ops.capital_gain_rate): | |
print('Sale FMV and Capital Gains Tax Rate are both required or optional') | |
exit(1) | |
compute(ops.option_grant, ops.exercise_fmv, ops.income_tax_rate, ops.exercise_exch_rate, | |
ops.sale_fmv, ops.capital_gain_rate, ops.indexation_ratio, ops.sale_exch_rate) | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment