Public
Edited
May 23, 2024
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
contribution_narrative = function(year, contributions, inflate=false) {
const contributions_per_kid = contributions.map(d => inflate ? d[year + 1 - min_start_year].yearly_contribution_inflated : d[year + 1 - min_start_year].yearly_contribution);
const total_contribution = d3.sum(contributions_per_kid);
const target_529 = (1 - (utma_fraction / 100) ) * total_contribution;
const target_UTMA = total_contribution - target_529;
const target_UTMA_per_kid = contributions_per_kid.map(d => money(d / total_contribution * target_UTMA));
return `total contributions of ${money(total_contribution)} with 529 contribution of ${money(target_529)}, total UTMA contribution of ${money(target_UTMA)}, and per kid's UTMA of ${target_UTMA_per_kid.join(", ")}`
}
Insert cell
catchup_529 = catchup_total - catchup_UTMA
Insert cell
catchup_UTMA = catchup_total * (utma_fraction / 100)
Insert cell
catchup_total = d3.max([total_contributions_current_year - yearend_savings, 0])
Insert cell
target_529_current = (1 - (utma_fraction / 100) ) * yearend_savings
Insert cell
target_UTMA_current = yearend_savings - target_529_current
Insert cell
target_UTMA = total_contributions_current_year - target_529
Insert cell
target_529 = (1 - (utma_fraction / 100) ) * total_contributions_current_year
Insert cell
nextyear_withdrawals_by_kids = contributions_per_kid_starting_now.map(d => d[current_year + 1 - min_start_year].withdrawal)
Insert cell
nextyear_adjusted_contribution_by_kids = contributions_per_kid_starting_now.map(d => d[current_year + 1 - min_start_year].yearly_contribution)
Insert cell
nextyear_target_by_kids = contributions_per_kid.map(d => d[current_year + 1 - min_start_year].balance)
Insert cell
thisyear_withdrawals_by_kids = contributions_per_kid_starting_now.map(d => d[current_year - min_start_year].withdrawal)
Insert cell
yearend_target_by_kids = contributions_per_kid.map(d => d[current_year-min_start_year].balance)
Insert cell
kid_balances = yearend_target_by_kids.map(d => d / total_contributions_current_year)
Insert cell
total_contributions_current_year = d3.sum(yearend_target_by_kids)
Insert cell
viewof college_years = Inputs.input(4)
Insert cell
Insert cell
money = d3.format("$,.0d")
Insert cell
Insert cell
kids = {
var tmp = {};
for (var i=0; i<kids_dynamic.elements.length; i++) {
if (i % 2 == 0) {
tmp[`Kid ${i/2+1}`] = {birth_year: kids_dynamic.elements[i]};
tmp[`Kid ${i/2+1}`].preschool_start = kids_dynamic.elements[i] + college_years + ( kids_dynamic.elements[i+1] ? -1 : 0 );
}
}
return tmp;
}
Insert cell
precollege_school_years = 15;
Insert cell
Insert cell
min_start_year = d3.min(start_years)
Insert cell
start_years = Object.values(kids).map(d => d.birth_year)
Insert cell
end_years = Object.values(kids).map(d => d.preschool_start + precollege_school_years + college_years - 1)
Insert cell
kid_faces = ["🧒","🧒","🧒","🧒","🧒","🧒","🧒","🧒"]
Insert cell
max_end_year = d3.max(end_years)
Insert cell
Inputs.table(tuition_inflated)
Insert cell
tuition_inflated = {
const net_tuition_inflation = tuition_inflation - inflation_rate;
var tmp = Array(max_end_year - min_start_year + 1);
for (var i=0; i < tmp.length; i++) {
const cal_year = i + min_start_year;
const year_diff = cal_year - current_year;
tmp[i] = {year: cal_year, tuition: Math.pow(1 + net_tuition_inflation/100, year_diff) * tuition};
}
return tmp;
}
Insert cell
viewof rows = Inputs.table(contributions_per_kid[0])
Insert cell
contributions_per_kid = Object.values(kids).map((d, j) => compute_contributions(d, j, true, NaN, NaN))
Insert cell
contributions_per_kid_starting_now = Object.values(kids).map((d, j) => compute_contributions(d, j, false, yearend_savings, kid_balances))
Insert cell
net_return = return_rate - inflation_rate
Insert cell
compute_contributions = function(d, j, since_birth, yearend_savings_, kid_balances_) {
const start_year = d.birth_year;
const college_start = d.preschool_start + precollege_school_years;
const end_year = college_start + college_years - 1;
// const total_college_cost = tuition * college_years;
const total_college_cost = d3.sum(tuition_inflated.slice(college_start-min_start_year, end_year-min_start_year+1), d => d.tuition);
const yearly_contribution = total_college_cost / (end_year - start_year + 1);
var tmp = Array(max_end_year - min_start_year + 1);
var balanced = false;
var multiplier = 1;
var heat = .9;
var iteration = 0;
const max_iterations = 100
while (!balanced & (iteration < max_iterations)) {
// console.log('iteration', iteration)
for (var i=0; i < tmp.length; i++) {
const cal_year = i + min_start_year;
const year_diff = cal_year - current_year;
const total_inflation = Math.pow(1 + inflation_rate/100, year_diff);
const total_deflation = Math.pow(1 - inflation_rate/100, year_diff);
const inflation_adjusted_contribution = contributions_match_inflation ? yearly_contribution : total_deflation * yearly_contribution;
const started_college = cal_year >= college_start; // this will turn during the cal year they start
const finished_college = cal_year > end_year; // this will turn on the year they graduate
const in_college = (started_college) & (!finished_college);
const born = cal_year >= d.birth_year;
const age = born ? cal_year - d.birth_year : 0;
tmp[i] = {
name: Object.keys(kids)[j],
year: cal_year,
age_last_bday: age,
yearly_contribution: ((cal_year > current_year) | (since_birth & born)) & (!finished_college) ? inflation_adjusted_contribution * multiplier : 0,
withdrawal: in_college ? tuition_inflated[i].tuition : 0
}
if ((cal_year == current_year) & !since_birth) {
tmp[i].yearly_contribution = yearend_savings_ * kid_balances_[j];
}
tmp[i].monthly_contribution = tmp[i].yearly_contribution / 12;
tmp[i].net = tmp[i].yearly_contribution - tmp[i].withdrawal;
tmp[i].balance = (1 + (net_return / 100)) * (i > 0 ? tmp[i-1].balance + tmp[i].net : tmp[i].net);
tmp[i].yearly_contribution_inflated = tmp[i].yearly_contribution * total_inflation;
}
if (Math.abs(tmp[tmp.length-1].balance) > 20) {
if (tmp[tmp.length-1].balance > 0) {
multiplier = multiplier * (1 - heat);
} else {
multiplier = multiplier * (1 + heat);
}
heat = heat * .9;
} else {
balanced = true;
}
iteration++;
}
return tmp;
}
Insert cell
Insert cell
require("d3")
Insert cell
import { clone } from "@mbostock/html-in-markdown"
Insert cell
import { view } from "@tomlarkworthy/view"
Insert cell
import { Tangle } from "@mbostock/tangle"
Insert cell
import { Plot } from "@mkfreeman/plot-tooltip"
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more