Last active
January 4, 2016 03:26
-
-
Save aresnick/af342f19c3d2102ddeb1 to your computer and use it in GitHub Desktop.
This file contains 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
var baseline_coc = 0.06; // What baseline investment should we compare this program to? | |
var annual_interest = 0.08; // What interest rate will we charge residents if they abandon their initiative? | |
var initial_loan = 50000.0; // Assume everyone begins with a $50K debt | |
var minimum_payment = { 'active': 500, 'inactive': 1000 } // If someone's initiative is active, offer a reduced minimum payment ($500/m) compared to $1000/m otherwise | |
var annual_deactivation_probability = 0.3; // How likely is it in any given year than an initiative shuts down? | |
var yearly_enrollment = [5, 10, 15, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20]; // Over the first 15 years, how many residents are enrolled per year? | |
// Variables of interest we'll be measuring over the course of runs | |
var total_paid = 0; | |
var lost_interest = 0; | |
var profit_over_baseline = 0; | |
var t = 0; | |
var Member = function() { | |
var self = this; | |
self.t_born = t; | |
self.t_dead = null; | |
self.age_at_death = null; | |
self.active = true; | |
self.balance = initial_loan; | |
self.minimum_annual_payment = self.active ? minimum_payment['active'] : minimum_payment['inactive']; | |
self.pay = function(amount) { // For a given Member, pay back `amount` of their loan | |
total_paid += amount; | |
self.balance -= amount; | |
}; | |
self.grow_balance = function() { // Increment the balance each year | |
if (self.active) { | |
self.balance += 0; | |
lost_interest += annual_interest * self.balance; | |
} else { | |
self.balance *= (1 + annual_interest); | |
profit_over_baseline += (annual_interest - baseline_coc) * self.balance; | |
} | |
}; | |
self.increment_year = function() { // Increment the year | |
if (self.active) { | |
if (Math.random() < annual_deactivation_probability) { // Simulate deactivation and record timeline if deactivating | |
self.active = false; | |
self.t_dead = t; | |
self.age_at_death = self.t_dead - self.t_born; | |
} | |
} | |
self.pay(self.minimum_annual_payment); | |
self.grow_balance(); | |
}; | |
self.get_age = function () { | |
return t - self.t_born; | |
} | |
}; | |
var run = function() { // Simulate a run | |
t = 0; | |
total_paid = 0; | |
lost_interest = 0; | |
profit_over_baseline = 0; | |
var members = []; | |
// For each of the 15y of the program, simulate members' growth over time | |
yearly_enrollment.forEach(function(count) { | |
for (var i = 0; i < count; ++i) { | |
members.push(new Member()); | |
}; | |
members.forEach(function(member) { | |
member.increment_year(); | |
}); | |
t += 1; | |
}); | |
// Log results | |
var results = {}; | |
results["Total alumni"] = members.length; | |
var active_alumni = members.filter(function(m) { | |
return m.active; | |
}); | |
results["Total active alumni"] = active_alumni.length; | |
var inactive_alumni = members.filter(function(m) { return !m.active; }); | |
results["age_at_death"] = inactive_alumni.map(function(m) { return m.age_at_death; }).reduce(function(a,b) { return a + b; })/inactive_alumni.length; | |
results["Average living age"] = active_alumni.map(function(m) { return m.get_age(); }).reduce(function(a,b) { return a + b; })/active_alumni.length; | |
results["Invested principal"] = 50000 * members.length; | |
results["Outstanding balances"] = members.map(function(m) { | |
return m.balance; | |
}).reduce(function(a, b) { | |
return a + b; | |
}); | |
results["Total paid"] = total_paid; | |
results["Lost interest"] = lost_interest; | |
results["Profit from inactive members"] = profit_over_baseline; | |
results["Net cost"] = profit_over_baseline - lost_interest; | |
return results; | |
}; | |
// Simulate over num_runs (10K) runs | |
var num_runs = 10000; | |
var runs = []; | |
for (var i = 0; i < num_runs; ++i) { | |
runs.push(run()); | |
}; | |
// Average simulation results | |
var average = {}; | |
Object.keys(runs[0]).forEach(function(key) { | |
average[key] = runs.map(function(r) { | |
return r[key]; | |
}).reduce(function(a, b) { | |
return a + b; | |
}) / runs.length; | |
}); | |
// Log out results | |
console.log(average); | |
/////////////////////////////////////////////////////////////////////////////// | |
// Sample run results (10K) | |
// | |
// { 'Total alumni': 270, | |
// 'Total active alumni': 46.1633, | |
// age_at_death: 1.6657111417978643, | |
// 'Average living age': 3.213380739663558, | |
// 'Invested principal': 13500000, | |
// 'Outstanding balances': 20044032.82097633, | |
// 'Total paid': 985000, | |
// 'Lost interest': 2030810.98, | |
// 'Profit from inactive members': 2032838.8616636116, | |
// 'Net cost': 2027.8816636019956 } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment