package ledger import ( "fmt" "maps" "math" "regexp" "time" ) func RegisterWithCompoundingInterestPrediction(reg Register, window time.Duration, pattern string, apy float64) (Register, error) { lastBalances := make(Balances) p := regexp.MustCompile(pattern) for _, d := range reg.Dates() { if t, _ := dateToTime(d); time.Now().Before(t) { continue } for name := range reg[d] { if p.MatchString(name) { lastBalances[name] = reg[d][name] } } } predictedTimes := predictionTimes(window) result := maps.Clone(reg) for _, predictionTime := range predictedTimes { k := predictionTime.Format("2006-01") if _, ok := result[k]; !ok { result[k] = make(Balances) } for k2, v2 := range lastBalances { if _, ok := result[k][k2]; !ok { result[k][k2] = maps.Clone(v2) } } } addedSoFar := make(Balances) for _, predictionTime := range predictedTimes { k := predictionTime.Format("2006-01") for name := range lastBalances { if _, ok := addedSoFar[name]; !ok { addedSoFar[name] = make(Balance) } for currency := range result[k][name] { // A = P(1 + r/n)**nt p := result[k][name][currency] + addedSoFar[name][currency] r := apy n := 12.0 t := 1.0 / 12.0 result[k][name][currency] = p * math.Pow(1.0+(r/n), n*t) addedSoFar[name][currency] += (result[k][name][currency] - p) } } } return result, nil } func predictionTimes(window time.Duration) []time.Time { result := []time.Time{} last := time.Now() for last.Before(time.Now().Add(window)) { last = last.Add(-1 * time.Hour * 24 * time.Duration(last.Day())).Add(time.Hour * 24 * 33) result = append(result, last) } return result } func dateToTime(s string) (time.Time, error) { for _, layout := range []string{ "2006-01-02", "2006-01", } { if t, err := time.ParseInLocation(layout, s, time.Local); err == nil { return t, err } } return time.Time{}, fmt.Errorf("no layout matching %q", s) }