diff --git a/cmd/clitest/main.go b/cmd/clitest/main.go index c25d3f3..504bd85 100644 --- a/cmd/clitest/main.go +++ b/cmd/clitest/main.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "log" + "maps" "net/http" "os" "slices" @@ -77,40 +78,50 @@ func main() { } register := deltas.Like(foolike...).Register() + bpis := maps.Clone(bpis) // MODIFIERS + if predictionMonths, err := strconv.ParseInt(r.URL.Query().Get("predictionMonths"), 10, 8); err == nil && predictionMonths > 0 { + predictionDuration := time.Hour * 24 * 365 / 12 * time.Duration(predictionMonths) + if r.URL.Query().Get("predictContributions") != "" { + register, err = ledger.RegisterWithContributionPrediction(register, predictionDuration) + if err != nil { + panic(err) + } + } + for _, nameRate := range r.URL.Query()["predictCompoundingInterest"] { + name := strings.Split(nameRate, "=")[0] + rate, err := strconv.ParseFloat(strings.Split(nameRate, "=")[1], 64) + if err != nil { + panic(err) + } + register, err = ledger.RegisterWithCompoundingInterestPrediction(register, predictionDuration, name, rate) + if err != nil { + panic(err) + } + } + for _, currencyRate := range r.URL.Query()["predictFixedGrowth"] { + currency := strings.Split(currencyRate, "=")[0] + rate, err := strconv.ParseFloat(strings.Split(currencyRate, "=")[1], 64) + if err != nil { + panic(err) + } + bpis, err = ledger.BPIsWithFixedGrowthPrediction(bpis, predictionDuration, currency, rate) + if err != nil { + panic(err) + } + } + } if r.URL.Query().Get("bpi") != "" { for date := range register { register[date] = register[date].WithBPIs(bpis) } } - if predictionMonths, err := strconv.ParseInt(r.URL.Query().Get("predictionMonths"), 10, 8); err == nil && predictionMonths > 0 { - predictionDuration := time.Hour * 24 * 365 / 12 * time.Duration(predictionMonths) - if r.URL.Query().Get("predictContributions") != "" { - register = ledger.RegisterWithContributionPrediction(register, predictionDuration) - } - for _, nameRate := range r.URL.Query()["predictCompoundingInterest"] { - splits := strings.Split(nameRate, "=") - if len(splits) != 2 { - panic(splits) - } - name := splits[0] - rate, err := strconv.ParseFloat(splits[1], 64) - if err != nil { - panic(err) - } - register = ledger.RegisterWithCompoundingInterestPrediction(register, predictionDuration, name, rate) - } - } // /MODIFIERS nameCurrencyDateValue := map[string]map[ledger.Currency]map[string]float64{} - dates := []string{} - names := []string{} for date, balances := range register { - dates = append(dates, date) for name, balance := range balances { - names = append(names, name) for currency, value := range balance { if _, ok := nameCurrencyDateValue[name]; !ok { nameCurrencyDateValue[name] = make(map[ledger.Currency]map[string]float64) @@ -122,13 +133,14 @@ func main() { } } } - slices.Sort(dates) - slices.Sort(names) chart := NewChart("line") if v := r.URL.Query().Get("chart"); v != "" { chart = NewChart(v) } + + dates := register.Dates() + names := register.Names() chart.AddX(dates) switch r.URL.Path { diff --git a/ledger/predict.go b/ledger/predict.go index 9450c38..5bffcf6 100644 --- a/ledger/predict.go +++ b/ledger/predict.go @@ -55,6 +55,9 @@ func registerWithContributionPredictionForNameForCurrency(reg Register, window t sort.Slice(contributions, func(i, j int) bool { return contributions[i].t.Before(contributions[j].t) }) + if len(contributions) < 2 { + return nil + } getMedianValueDelta := func(contributions []contribution) float64 { values := make([]float64, len(contributions)) @@ -72,10 +75,17 @@ func registerWithContributionPredictionForNameForCurrency(reg Register, window t slices.Sort(lapses) return lapses[len(lapses)/2] } + contributsSlice := func(percent float64) []contribution { + wouldBe := int(percent * float64(len(contributions))) + if wouldBe == 0 { + wouldBe = 2 + } + return contributions[len(contributions)-wouldBe:] + } - eighth := contributions[int(7.0*len(contributions)/8.0):] - quarter := contributions[int(3.0*len(contributions)/4.0):] - half := contributions[int(1.0*len(contributions)/2.0):] + eighth := contributsSlice(7.0 / 8.0) + quarter := contributsSlice(3.0 / 4.0) + half := contributsSlice(1.0 / 2.0) medianValueDelta := func() float64 { return (4.0*getMedianValueDelta(eighth) + 2.0*getMedianValueDelta(quarter) + 1.0*getMedianValueDelta(half)) / (4.0 + 2.0 + 1.0) }()