diff --git a/ledger/predict.go b/ana/interest.go similarity index 56% rename from ledger/predict.go rename to ana/interest.go index 0a1291d..8433841 100644 --- a/ledger/predict.go +++ b/ana/interest.go @@ -1,15 +1,16 @@ -package ledger +package ana import ( - "fmt" "maps" "math" "regexp" "time" + + "gogs.inhome.blapointe.com/ana-ledger/ledger" ) -func RegisterWithCompoundingInterestPrediction(reg Register, window time.Duration, pattern string, apy float64) (Register, error) { - lastBalances := make(Balances) +func RegisterWithCompoundingInterestPrediction(reg ledger.Register, window time.Duration, pattern string, apy float64) (ledger.Register, error) { + lastBalances := make(ledger.Balances) p := regexp.MustCompile(pattern) for _, d := range reg.Dates() { if t, _ := dateToTime(d); time.Now().Before(t) { @@ -27,7 +28,7 @@ func RegisterWithCompoundingInterestPrediction(reg Register, window time.Duratio for _, predictionTime := range predictedTimes { k := predictionTime.Format("2006-01") if _, ok := result[k]; !ok { - result[k] = make(Balances) + result[k] = make(ledger.Balances) } for k2, v2 := range lastBalances { if _, ok := result[k][k2]; !ok { @@ -36,12 +37,12 @@ func RegisterWithCompoundingInterestPrediction(reg Register, window time.Duratio } } - addedSoFar := make(Balances) + addedSoFar := make(ledger.Balances) for _, predictionTime := range predictedTimes { k := predictionTime.Format("2006-01") for name := range lastBalances { if _, ok := addedSoFar[name]; !ok { - addedSoFar[name] = make(Balance) + addedSoFar[name] = make(ledger.Balance) } for currency := range result[k][name] { // A = P(1 + r/n)**nt @@ -57,25 +58,3 @@ func RegisterWithCompoundingInterestPrediction(reg Register, window time.Duratio 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) -} diff --git a/ana/interest_test.go b/ana/interest_test.go new file mode 100644 index 0000000..26be7a3 --- /dev/null +++ b/ana/interest_test.go @@ -0,0 +1,26 @@ +package ana + +import ( + "testing" + "time" +) + +func TestInterest(t *testing.T) { + input := newTestRegister() + + got, err := RegisterWithCompoundingInterestPrediction(input, time.Hour*24*365, "X", .04) + if err != nil { + t.Fatal(err) + } + t.Logf("%+v", got) + + if len(got) <= len(input) { + t.Error(len(got)) + } + + for _, date := range got.Dates() { + for name, balance := range got[date] { + t.Logf("%s | %s %s", date, name, balance.Debug()) + } + } +} diff --git a/ledger/predict_test.go b/ledger/predict_test.go deleted file mode 100644 index f92c5d8..0000000 --- a/ledger/predict_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package ledger - -import ( - "testing" - "time" -) - -func TestRegisterPrediction(t *testing.T) { - t.Run("compounding interest", func(t *testing.T) { - input := newTestRegister() - - got, err := RegisterWithCompoundingInterestPrediction(input, time.Hour*24*365, "X", .04) - if err != nil { - t.Fatal(err) - } - t.Logf("%+v", got) - - if len(got) <= len(input) { - t.Error(len(got)) - } - - for _, date := range got.Dates() { - for name, balance := range got[date] { - t.Logf("%s | %s %s", date, name, balance.Debug()) - } - } - }) -} - -func newTestRegister() map[string]Balances { - s := func(t time.Time) string { - return t.Format("2006-01") - } - day := time.Hour * 24 - lastYear := time.Now().Add(-1 * day * time.Duration(time.Now().YearDay())) - return map[string]Balances{ - s(lastYear.Add(day * 0)): Balances{"X": Balance{USD: 1}}, - s(lastYear.Add(day * 32)): Balances{"X": Balance{USD: 2}}, - s(lastYear.Add(day * 64)): Balances{"X": Balance{USD: 3}}, - s(lastYear.Add(day * 94)): Balances{"X": Balance{USD: 4}}, - s(lastYear.Add(day * 124)): Balances{"X": Balance{USD: 5}}, - s(lastYear.Add(day * 154)): Balances{"X": Balance{USD: 6}}, - s(lastYear.Add(day * 184)): Balances{"X": Balance{USD: 8}}, - s(lastYear.Add(day * 214)): Balances{"X": Balance{USD: 10}}, - s(lastYear.Add(day * 244)): Balances{"X": Balance{USD: 12}}, - s(lastYear.Add(day * 274)): Balances{"X": Balance{USD: 16, "XYZ": 1}}, - } -} diff --git a/ledger/register.go b/ledger/register.go index c090bc3..f207104 100644 --- a/ledger/register.go +++ b/ledger/register.go @@ -1,6 +1,7 @@ package ledger import ( + "fmt" "slices" "time" ) @@ -53,8 +54,27 @@ func (register Register) Times() []time.Time { dates := register.Dates() result := make([]time.Time, len(dates)) for i := range dates { - v, _ := dateToTime(dates[i]) - result[i] = v + result[i] = mustDateToTime(dates[i]) } return result } + +func mustDateToTime(s string) time.Time { + result, err := dateToTime(s) + if err != nil { + panic(err) + } + 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) +}