121 lines
3.0 KiB
Go
121 lines
3.0 KiB
Go
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)
|
|
}
|