package ledger import ( "fmt" "io" "maps" "regexp" "time" ) func RegisterWithContributionPrediction(reg Register, window time.Duration) (Register, error) { result := make(Register) result.PushAll(reg) for _, name := range result.Names() { subregister, err := registerWithContributionPredictionForName(result, window, name) if err != nil { return nil, err } result.PushAll(subregister) } return result, nil } func registerWithContributionPredictionForName(reg Register, window time.Duration, name string) (Register, error) { return nil, io.EOF } func registerWithContributionPredictionForNameForCurrency(reg Register, window time.Duration, name string, currency Currency) (Register, error) { return nil, io.EOF // find median contribution value+frequency in most recent half // find median contribution value+frequency in most recent quarter // find median contribution value+frequency in most recent eighth // weighted averages of medians // project } func BPIsWithFixedGrowthPrediction(bpis BPIs, window time.Duration, pattern string, apy float64) (BPIs, error) { last := map[Currency]struct { t string v float64 }{} for currency, bpi := range bpis { for date, value := range bpi { if date > last[currency].t { was := last[currency] was.t = date was.v = value last[currency] = was } } } result := make(BPIs) p := regexp.MustCompile(pattern) for currency, v := range bpis { result[currency] = maps.Clone(v) if p.MatchString(string(currency)) { for _, predictionTime := range predictionTimes(window) { k2 := predictionTime.Format("2006-01") was := last[currency] was.v *= 1.0 + apy result[currency][k2] = was.v last[currency] = was } } } return result, nil } func RegisterWithCompoundingInterestPrediction(reg Register, window time.Duration, pattern string, apy float64) (Register, error) { lastBalances := make(Balances) p := regexp.MustCompile(pattern) for _, date := range reg.Dates() { for name := range reg[date] { if p.MatchString(name) { lastBalances[name] = reg[date][name] } } } result := maps.Clone(reg) for _, predictionTime := range predictionTimes(window) { k := predictionTime.Format("2006-01") if _, ok := result[k]; !ok { result[k] = make(Balances) } for name, balance := range lastBalances { balance2 := maps.Clone(balance) for k := range balance2 { balance2[k] *= 1.0 + (apy / 12) } result[k][name] = balance2 lastBalances[name] = balance2 } } 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) }