prediction COMPLETE

main
Bel LaPointe 2023-10-26 05:56:36 -06:00
parent bd4e0a913a
commit eda1c564d4
3 changed files with 96 additions and 17 deletions

View File

@ -2,9 +2,10 @@ package ledger
import (
"fmt"
"io"
"maps"
"regexp"
"slices"
"sort"
"time"
)
@ -12,26 +13,97 @@ func RegisterWithContributionPrediction(reg Register, window time.Duration) (Reg
result := make(Register)
result.PushAll(reg)
for _, name := range result.Names() {
subregister, err := registerWithContributionPredictionForName(result, window, name)
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 registerWithContributionPredictionForName(reg Register, window time.Duration, name string) error {
currencies := make(map[Currency]int)
for d := range reg {
for c := range reg[d][name] {
currencies[c] = 1
}
}
for c := range currencies {
err := registerWithContributionPredictionForNameForCurrency(reg, window, name, c)
if err != nil {
return err
}
}
return nil
}
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 registerWithContributionPredictionForNameForCurrency(reg Register, window time.Duration, name string, currency Currency) error {
type contribution struct {
t time.Time
v float64
}
contributions := make([]contribution, 0)
for d := range reg {
t, err := dateToTime(d)
if err != nil {
return err
}
if v, ok := reg[d][name][currency]; ok {
contributions = append(contributions, contribution{t: t, v: v})
}
}
sort.Slice(contributions, func(i, j int) bool {
return contributions[i].t.Before(contributions[j].t)
})
getMedianValueDelta := func(contributions []contribution) float64 {
values := make([]float64, len(contributions))
for i := 1; i < len(contributions); i++ {
values[i] = contributions[i].v - contributions[i-1].v
}
slices.Sort(values)
return values[len(values)/2]
}
getMedianLapse := func(contributions []contribution) time.Duration {
lapses := make([]time.Duration, len(contributions)-1)
for i := 1; i < len(contributions); i++ {
lapses = append(lapses, contributions[i].t.Sub(contributions[i-1].t))
}
slices.Sort(lapses)
return lapses[len(lapses)/2]
}
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):]
medianValueDelta := func() float64 {
return (4.0*getMedianValueDelta(eighth) + 2.0*getMedianValueDelta(quarter) + 1.0*getMedianValueDelta(half)) / (4.0 + 2.0 + 1.0)
}()
medianLapse := func() time.Duration {
return (4.0*getMedianLapse(eighth) + 2.0*getMedianLapse(quarter) + 1.0*getMedianLapse(half)) / (4.0 + 2.0 + 1.0)
}()
latest := func() float64 {
max := 0.0
for d := range reg {
if other := reg[d][name][currency]; other > max {
max = other
}
}
return max
}()
for _, predictionTime := range predictionTimes(window) {
k := predictionTime.Format("2006-01")
if _, ok := reg[k]; !ok {
reg[k] = make(Balances)
}
if _, ok := reg[k][name]; !ok {
reg[k][name] = make(Balance)
}
expectedDelta := float64(predictionTime.Sub(time.Now())) * medianValueDelta / float64(medianLapse)
reg[k][name][currency] = latest + expectedDelta
}
return nil
}
func BPIsWithFixedGrowthPrediction(bpis BPIs, window time.Duration, pattern string, apy float64) (BPIs, error) {

View File

@ -16,13 +16,15 @@ func TestRegisterPrediction(t *testing.T) {
}
t.Logf("%+v", got)
if len(got) != len(input)+1 {
if len(got) != len(input)+13 {
t.Error(len(got))
}
if _, ok := got["2001-11"]; !ok {
t.Error(ok)
for _, date := range got.Dates() {
for _, name := range got.Names() {
t.Logf("%s | %s = %+v", date, name, got[date][name])
}
}
t.Error("not impl")
})
t.Run("compounding interest", func(t *testing.T) {

View File

@ -8,7 +8,12 @@ import (
type Register map[string]Balances
func (register Register) PushAll(other Register) {
TODO
for date := range other {
if _, ok := register[date]; !ok {
register[date] = make(Balances)
}
register[date].PushAll(other[date])
}
}
func (register Register) Names() []string {