Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
Add differential equation model
  • Loading branch information
agentydragon committed Sep 2, 2019
1 parent 3089023 commit c48ded40640cda8e3851fd0b2a9512f95ae89997
Showing with 125 additions and 6 deletions.
  1. +95 −0 model/differential.go
  2. +28 −5 model/model.go
  3. +1 −0 worthy/asset.go
  4. +1 −1 worthy/worthy.go
@@ -0,0 +1,95 @@
// f(x) = C*(1+i) ^ x + (c / (log(1 + i)))
//
// C = c/i' - f(0)

package model

import (
"fmt"
"math"
)

func YearsUntilSavedUpExp(
total float64,
yearlyYield float64,
targetNumber float64,
monthlySaving float64) float64 {
// Calculate how much longer do we need to save to get that number.
c := -monthlySaving * 12 // monthly savings (minus - because they're plus, not costs)
f_0 := total // initial savings
i := yearlyYield // yearly yield, e.g. 0.04 = 4%
i_prime := math.Log(1 + i)
C := f_0 - (c / i_prime)

timeToReach := func(f_x float64) float64 {
// Derivation:
// f_x = C*math.Exp(1+i, x) + c/i_prime
// 0 = C*math.Exp(1+i, x) + c/i_prime - f_x
// -C*math.Exp(1+i, x) = c/i_prime - f_x
// math.Exp(1+i, x) = (c/i_prime - f_x) / (-C)
// x = math.Log((c/i_prime - f_x) / (-C)) / i_prime
return math.Log((c/i_prime-f_x)/(-C)) / i_prime
}
return timeToReach(targetNumber)
}

func YearsUntilIndependent(
total float64,
yearlyYield float64,
monthlyGoal float64,
monthlySaving float64) {
// How much you need to have to be financially independent.
// TODO(prvak): This number should also depend on how long you need it to last.
targetNumber := (monthlyGoal * 12) / yearlyYield

if targetNumber > total {
fmt.Println("time to reach", targetNumber, " in years:", YearsUntilSavedUpExp(total, yearlyYield, targetNumber, monthlySaving))
} else {
fmt.Println("already reached")
}
}

func PrintFiInfoExp(
total float64,
yearlyYield float64,
monthlyGoal float64,
monthlySaving float64) {
fmt.Println("total=", total, "yearlyYield=", yearlyYield, "monthlyGoal=", monthlyGoal, "monthlySaving=", monthlySaving)
/// c := monthlyGoal * 12 // yearly costs
/// f_0 := total // initial savings
/// i := yearlyYield // yearly yield, e.g. 0.04 = 4%

/// i_prime := math.Log(1 + i)

/// f := func(x float64) float64 {
/// return C*math.Pow(1+i, x) + (c / i_prime)
/// }

/// for i = 0; i < 10; i++ {
/// fmt.Println("after", i, "years:", f(i))
/// }

targetNumber := (monthlyGoal * 12) / yearlyYield

fmt.Println("you need to have", targetNumber)

// Calculate how much longer do we need to save to get that number.
c := -monthlySaving * 12 // monthly savings (minus - because they're plus, not costs)
f_0 := total // initial savings
i := yearlyYield // yearly yield, e.g. 0.04 = 4%
i_prime := math.Log(1 + i)
C := f_0 - (c / i_prime)

timeToReach := func(f_x float64) float64 {
// f_x = C*math.Exp(1+i, x) + c/i_prime
// 0 = C*math.Exp(1+i, x) + c/i_prime - f_x
// -C*math.Exp(1+i, x) = c/i_prime - f_x
// math.Exp(1+i, x) = (c/i_prime - f_x) / (-C)
return math.Log((c/i_prime-f_x)/(-C)) / i_prime
}
if targetNumber > total {
fmt.Println("time to reach", targetNumber, " in years:", timeToReach(targetNumber))
} else {
fmt.Println("already reached")
}
}
@@ -3,26 +3,49 @@ package model
import (
"fmt"
"math"
"time"
)

func yearsToMonths(years float64) float64 {
return years * 12
}

func durationDescription(now time.Time, years float64) string {
oneYear := float64(time.Hour) * 24 * 365.24
goalTime := now.Add(time.Duration(math.Round(oneYear * years)))
return fmt.Sprintf("%.1f months (%.2f years) = %s",
yearsToMonths(years), years, goalTime.Format("2006-01-02"))
}

// Yearly yield: 0.03 means assumed yearly yield of 3%.
func PrintFiInfo(
total float64,
yearlyYield float64,
monthlyGoal float64,
monthlySaving float64) {
yield := 1.0 + yearlyYield
fmt.Printf("Model inputs: yearly yield %.2f%%, monthly goal %.2f\n", yearlyYield*100.0, monthlyGoal)
//fmt.Printf("Model inputs: yearly yield %.2f%%, monthly goal %.2f\n", yearlyYield*100.0, monthlyGoal)

monthlyDividend := math.Pow(yield, 1.0/12)

monthlyDivP := monthlyDividend - 1.0
perpetuityTarget := monthlyGoal / monthlyDivP
fmt.Printf("Perpetuity model: spend ≤%.2f%% monthly → need ≥%.2f.", monthlyDivP*100, perpetuityTarget)
fmt.Printf("Perpetuity model: spend ≤%.2g%% monthly → need ≥%.2f.", monthlyDivP*100, perpetuityTarget)
now := time.Now()
if perpetuityTarget > total {
needMonths := (perpetuityTarget - total) / monthlySaving
needYears := needMonths / 12
fmt.Printf(" Saving %.2f per month, need %.2f months (%.2f years).", monthlySaving, needMonths, needYears)
fmt.Printf("\nSaving %.2f per month:\n", monthlySaving)
var needYears float64 = YearsUntilSavedUpLinear(total, perpetuityTarget, monthlySaving)
fmt.Printf("Linear: need %s.\n", durationDescription(now, needYears))
needYears = YearsUntilSavedUpExp(total, yearlyYield, perpetuityTarget, monthlySaving)
fmt.Printf(" Exp: need %s.\n", durationDescription(now, needYears))
} else {
fmt.Printf(" %.0f%% ✓\n", (total/perpetuityTarget)*100)
}
fmt.Println()
}

func YearsUntilSavedUpLinear(
total float64, targetNumber float64, monthlySaving float64) float64 {
needMonths := (targetNumber - total) / monthlySaving
return needMonths / 12
}
@@ -11,6 +11,7 @@ type Asset struct {
}

func (self Asset) String() string {
// TODO(prvak): Do not represent unnecessary ".00" numbers.
if self.Type == money.Currency || self.Type == money.Crypto {
return fmt.Sprintf("%s %.2f", self.Symbol, self.Amount)
} else {
@@ -47,7 +47,7 @@ func printFiInfo(
monthlyGoal Asset,
monthlySaving Asset) {

fmt.Printf("Model inputs: yearly yield %.2f%%, monthly goal %s\n", yearlyYield*100.0, monthlyGoal)
fmt.Printf("Model inputs: yearly yield %.2g%%, monthly goal %s\n", yearlyYield*100.0, monthlyGoal)

toCommon := func(x Asset) float64 {
return commonPrices[x.Denomination] * x.Amount

0 comments on commit c48ded4

Please sign in to comment.