calc.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. function mortgage_calc(d) {
  2. var app_rate;
  3. console.log(d);
  4. if(d.appreciation_mode == 'rate') {
  5. app_rate = d.app_rate;
  6. }
  7. else {
  8. app_rate = Math.pow((d.resell_price - d.resell_closing_costs) / d.purchase_price, 1 / d.resell_months) - 1;
  9. app_rate *= 12;
  10. }
  11. d.monthly_app_rate = annualToMonthlyInterest(app_rate);
  12. d.inflation_rate_monthly = annualToMonthlyInterest(d.inflation_rate);
  13. d.monthly_interest = d.apr / 12; // this works with APR's
  14. var num_payments = d.loan_term * 12;
  15. d.property_tax_rate_monthly = annualToMonthlyInterest(d.property_tax_rate);
  16. d.reference_rate_monthly = annualToMonthlyInterest(d.reference_rate);
  17. var downpayment = d.purchase_price - d.loan_amount;
  18. var pmi_amount = d.loan_amount * d.pmi_rate / 12 / 100;
  19. var data = amortTable(d.loan_amount, d.apr, num_payments);
  20. // appreciation and house value
  21. data.house_value = data.period.map(function(i) {
  22. return d.purchase_price * cmpInt(d.monthly_app_rate, i);
  23. });
  24. // perhaps these have backwards names
  25. data.inflation = data.period.map(function(i) {
  26. return 1 / cmpInt(d.inflation_rate_monthly, i);
  27. });
  28. data.inv_inflation = data.period.map(function(i) {
  29. return cmpInt(d.inflation_rate_monthly, i);
  30. });
  31. data.reference_line = data.period.map(function(i) {
  32. return downpayment * cmpInt(d.reference_rate_monthly, i);
  33. });
  34. data.acc_appreciation = data.period.map(function(i) {
  35. return d.purchase_price * (cmpInt(d.monthly_app_rate, i) - 1);
  36. });
  37. data.appreciation = data.period.map(function(i) {
  38. return d.purchase_price * (cmpInt(d.monthly_app_rate, i+1) - cmpInt(d.monthly_app_rate, i));
  39. });
  40. data.equity = data.house_value.map(function(hv, i) {
  41. return hv - data.loan_balance[i];
  42. });
  43. data.acc_interest = accumulate(data.interest, 0, true);
  44. data.commute_costs = data.period.map(constantValue(d.commute_costs));
  45. // renting as an alternative
  46. // TODO: factor inflation
  47. data.cheapest_rent = data.period.map(constantValue(d.cheapest_rent))
  48. //monthly_income_before_housing
  49. console.log(data);
  50. function cmpInt(periodic_rate, period) {
  51. // A = P(1 + r/n) ^ tn
  52. return Math.pow(1 + periodic_rate, period);
  53. }
  54. var insurance_rate = .0035;
  55. var insurance = d.purchase_price * insurance_rate / 12;
  56. data.insurance = data.period.map(constantValue(insurance));
  57. var overhead = d.bills_monthly + d.maint_monthly + (d.maint_yearly / 12) + d.other_fees_monthly;
  58. data.overhead = data.period.map(constantValue(overhead));
  59. data.property_tax = data.house_value.map(function(hv) {
  60. // BUG: APR or compound interest? should go off assessment
  61. return (d.property_tax_rate_monthly) * hv;
  62. });
  63. data.ltv = [];
  64. data.pmi = [];
  65. for(var i = 0; i <= num_payments; i++) {
  66. // prolly a bug here: loan balance is the ending balance
  67. var ltv = parseFloat((data.loan_balance[i] / d.purchase_price).toFixed(2));
  68. data.ltv[i] = ltv;
  69. // mortgage insurance
  70. data.pmi[i] = ltv > 0.80 ? pmi_amount : 0;
  71. data.payment[i] += data.pmi[i];
  72. }
  73. data.direct_costs = data.period.map(function(i) {
  74. return data.insurance[i] +
  75. data.pmi[i] +
  76. data.interest[i] +
  77. data.property_tax[i];
  78. });
  79. data.acc_direct_costs = accumulate(data.direct_costs, d.closing_costs, true);
  80. data.cash_spent = accumulate(sum(data.direct_costs, data.principle), d.closing_costs + downpayment, true);
  81. data.total_costs = sum(data.commute_costs, sum(data.direct_costs, data.overhead));
  82. data.acc_total_costs = accumulate(data.total_costs, d.closing_costs, true);
  83. data.net_gain = data.period.map(function(i) {
  84. return data.equity[i] + d.monthly_rental_income - downpayment - data.acc_total_costs[i];
  85. });
  86. // inflation adjustments.
  87. // note that some values stay constant relative to inflation
  88. data.inf_pmi = mul(data.pmi, data.inflation);
  89. data.inf_payment = mul(data.payment, data.inflation);
  90. data.inf_insurance = data.insurance;
  91. data.inf_house_value = mul(data.house_value, data.inflation);
  92. data.inf_equity = mul(data.equity, data.inflation);
  93. data.inf_principle = mul(data.principle, data.inflation);
  94. data.inf_interest = mul(data.interest, data.inflation);
  95. data.inf_loan_balance = mul(data.loan_balance, data.inflation);
  96. data.inf_reference_line = mul(data.reference_line, data.inflation);
  97. data.inf_overhead = data.overhead;
  98. data.inf_commute_costs = data.commute_costs;
  99. // can't just multiply this one like above because some part of it isn't commutative
  100. data.inf_acc_appreciation = data.period.map(function(i) {
  101. return d.purchase_price * (cmpInt(d.monthly_app_rate - d.inflation_rate_monthly, i) - 1);
  102. });
  103. data.inf_appreciation = data.period.map(function(i) {
  104. return d.purchase_price *
  105. (cmpInt(d.monthly_app_rate - d.inflation_rate_monthly, i+1) -
  106. cmpInt(d.monthly_app_rate - d.inflation_rate_monthly, i));
  107. });
  108. data.inf_direct_costs = data.period.map(function(i) {
  109. return data.insurance[i] +
  110. data.pmi[i] * data.inflation[i] + // BUG: does pmi rise?
  111. data.interest[i] * data.inflation[i] + // interest payments are chiseled into the contract
  112. data.property_tax[i];
  113. });
  114. data.inf_acc_interest = accumulate(data.inf_interest, 0, true);
  115. data.inf_acc_direct_costs = accumulate(data.inf_direct_costs, d.closing_costs, true);
  116. data.inf_total_costs = sum(data.inf_commute_costs, sum(data.inf_direct_costs, data.inf_overhead));
  117. data.inf_acc_total_costs = accumulate(data.inf_total_costs, d.closing_costs, true);
  118. data.inf_cash_spent = accumulate(sum(data.inf_direct_costs, data.inf_principle), d.closing_costs + downpayment, true);
  119. data.inf_gross_gain = data.period.map(function(i) {
  120. return data.inf_equity[i] - downpayment - data.inf_acc_direct_costs[i];
  121. });
  122. data.inf_net_gain = data.period.map(function(i) {
  123. return data.inf_equity[i] - downpayment - data.inf_acc_total_costs[i];
  124. });
  125. return {
  126. time_series: data,
  127. };
  128. }