Skip to content

Instantly share code, notes, and snippets.

@anowell
Last active April 29, 2018 08:13
Show Gist options
  • Save anowell/ebea243a93b38097c73860133958b729 to your computer and use it in GitHub Desktop.
Save anowell/ebea243a93b38097c73860133958b729 to your computer and use it in GitHub Desktop.
Calculating equity positions
[dependencies]
csv = { git = "https://github.com/BurntSushi/rust-csv.git" } #"1.0.0-beta.6"
serde = "1.0.38"
serde_derive = "1.0.38"
chrono = { verstion = "0.4.2", features = ["serde"] }
decimal = "2.0.4"
#[macro_use]
extern crate serde_derive;
extern crate chrono;
extern crate csv;
extern crate decimal;
use chrono::{Datelike, NaiveDate};
use csv::ReaderBuilder;
use csv::Trim;
use decimal::d128; // cuz finance with floating point is bad
use std::env;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
struct Grant {
issue: NaiveDate,
amount: u32,
exercise: d128,
}
#[derive(Default, Debug)]
struct Aggregate {
total_vested: u32,
total_issued: u32,
total_vested_exercise: d128,
total_granted_exercise: d128,
}
fn vested(g: &Grant, on: NaiveDate) -> u32 {
// vesting cliff
if g.issue > on {
return 0;
}
// If days of month indicate full month didn't pass, we'll have to subract an extra month
let extra = if g.issue.day() > on.day() { 1 } else { 0 };
let months = (on.year() - g.issue.year()) as u32 * 12 + on.month() - g.issue.month() - extra;
match months {
m if m < 12 => 0, // nothing vested
m if m >= 48 => g.amount, // fully vested
m => m as u32 * g.amount / 48, // vested 1/48 per month
}
}
fn main() {
let path = env::args().skip(1).next().expect("Missing arg. Must specify CSV file containing grants");
let mut rdr = ReaderBuilder::new()
.trim(Trim::All)
.from_path(path)
.unwrap();
let mut grants = Vec::new();
for record in rdr.deserialize() {
let record: Grant = record.unwrap();
grants.push(record);
}
println!("Date, Percent Vested, Total Vested, Total Granted, Exercise Vested Cost, Exercise Granted Cost");
for y in 2015..=2019 {
for m in 1..=12 {
let totals: Aggregate = grants.iter().fold(Aggregate::default(), |agg, ref g| {
let mut agg = agg;
let date = NaiveDate::from_ymd(y, m, 1);
if date > g.issue {
let vested = vested(&g, date);
agg.total_vested += vested;
agg.total_issued += g.amount;
agg.total_vested_exercise += g.exercise * d128::from(vested);
agg.total_granted_exercise += g.exercise * d128::from(g.amount);
}
agg
});
if totals.total_issued > 0 {
let percent = 100 * totals.total_vested / totals.total_issued;
println!(
"{}-{}-01, {}%, {}, {}, {}, {}",
y, m, percent, totals.total_vested, totals.total_issued, totals.total_vested_exercise, totals.total_granted_exercise
);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment