diff --git a/ana/predictor.go b/ana/predictor.go index 7605845..6b1087a 100644 --- a/ana/predictor.go +++ b/ana/predictor.go @@ -4,11 +4,16 @@ import ( "maps" "math" "regexp" + "slices" "time" "gogs.inhome.blapointe.com/ana-ledger/ledger" ) +const ( + month = time.Hour * 24 * 365 / 12 +) + type Predictor func(ledger.Balances, time.Duration) ledger.Balances func NewInterestPredictor(namePattern string, currencyPattern string, apy float64) Predictor { @@ -20,7 +25,7 @@ func NewInterestPredictor(namePattern string, currencyPattern string, apy float6 result[k] = maps.Clone(v) } - monthsPassed := float64(delta) / float64(time.Hour*24.0*365.0/12.0) + monthsPassed := float64(delta) / float64(month) scalar := math.Pow(1.0+apy/12.0, monthsPassed) for name := range result { if !nameMatcher.MatchString(name) { @@ -44,7 +49,6 @@ func NewContributionPredictor(reg ledger.Register) Predictor { func newContributionPredictor(monthlyRate map[string]ledger.Balance) Predictor { return func(given ledger.Balances, delta time.Duration) ledger.Balances { - month := time.Hour * 24 * 365 / 12 months := float64(delta) / float64(month) result := make(ledger.Balances) for k, v := range given { @@ -63,7 +67,8 @@ func newContributionPredictor(monthlyRate map[string]ledger.Balance) Predictor { } func getMonthlyContributionRates(reg ledger.Register) map[string]ledger.Balance { - window := time.Hour * 24 * 365 / 2 + window := 6 * month + window = 12 * month contributions := getRecentContributions(reg, window) result := make(map[string]ledger.Balance) for name := range contributions { @@ -88,11 +93,27 @@ func getMonthlyContributionRate(contributions []ledger.Balance, window time.Dura // TODO better than average func getMonthlyContributionRateForCurrency(contributions []ledger.Balance, window time.Duration, currency ledger.Currency) float64 { - sum := 0.0 - for _, balance := range contributions { - sum += balance[currency] + values := []float64{} + for i := range contributions { + if v, ok := contributions[i][currency]; ok { + values = append(values, v) + } } - return sum / (float64(window) / float64(time.Hour*24.0*365.0/12.0)) + slices.Sort(values) + + start := int(len(values) / 4) + end := min(1+max(0, len(values)-len(values)/4), len(values)) + subvalues := values[start:end] + sum := 0.0 + for _, v := range subvalues { + sum += v + } + standard := sum / float64(end-start) + standard = subvalues[len(subvalues)/2] + totalIfAllWereStandard := standard * float64(len(contributions)) + result := totalIfAllWereStandard / float64(window) * float64(month) + //log.Printf("%s: %v contributions of about %.2f over %v can TLDR as %.2f per month from %v", currency, len(contributions), standard, window, result, values[start:end]) + return result } func getRecentContributions(reg ledger.Register, window time.Duration) map[string][]ledger.Balance {