diff --git a/cmd/cli/config.go b/cmd/cli/config.go index eefb45c..da8c73b 100644 --- a/cmd/cli/config.go +++ b/cmd/cli/config.go @@ -1,9 +1,11 @@ package cli type Config struct { - Files FileList - BPI string - Query struct { + Files FileList + BPI string + CPI string + CPIYear int + Query struct { Period Period Sort string NoRounding bool diff --git a/cmd/cli/main.go b/cmd/cli/main.go index 868164d..9d3c680 100644 --- a/cmd/cli/main.go +++ b/cmd/cli/main.go @@ -30,6 +30,8 @@ func Main() { fs.BoolVar(&config.Query.USDOnly, "usd", false, "filter to usd") fs.BoolVar(&config.Query.NoExchanging, "no-exchanging", true, "omit currency exchanges") fs.StringVar(&config.BPI, "bpi", "", "path to bpi") + fs.StringVar(&config.CPI, "cpi", "", "path to cpi") + fs.IntVar(&config.CPIYear, "cpiy", 0, "use cpi to convert usd to this year's value") if err := fs.Parse(os.Args[1:]); err != nil { panic(err) } @@ -89,6 +91,23 @@ func Main() { bpis = b } + cpiNormalizer := ana.NewNormalizer() + if config.CPI != "" && config.CPIYear > 0 { + c, err := ledger.NewBPIs(config.CPI) + if err != nil { + panic(err) + } + cpi := c["CPI"] + cpiy := cpi.Lookup(fmt.Sprintf("%d-06-01", config.CPIYear)) + if cpiy == nil { + panic(fmt.Errorf("no cpi for year %d", config.CPIYear)) + } + + for date, value := range cpi { + cpiNormalizer = cpiNormalizer.With(".*", date, value/(*cpiy)) + } + } + if config.Query.Normalize { deltas = ana.NewDefaultNormalizer().Normalize(deltas) } @@ -113,13 +132,14 @@ func Main() { WithBPIs(bpis). KindaLike(q). KindaGroup(group). - Nonzero() + Nonzero(). + Normalize(cpiNormalizer, "9") FPrintBalances(w, "", balances, nil, config.Query.USDOnly, config.Query.Normalize, time.Now().Format("2006-01-02"), false, maxAccW) case "reg": transactions := deltas.Transactions() cumulative := make(ledger.Balances) for _, transaction := range transactions { - balances := ledger.Deltas(transaction).Like(q).Group(group).Balances().WithBPIsAt(bpis, transaction[0].Date).Nonzero() + balances := ledger.Deltas(transaction).Like(q).Group(group).Balances().WithBPIsAt(bpis, transaction[0].Date).Nonzero().Normalize(cpiNormalizer, transaction[0].Date) shouldPrint := false shouldPrint = shouldPrint || len(balances) > 2 if config.Query.NoExchanging { @@ -220,7 +240,7 @@ func FPrintBalances(w io.Writer, linePrefix string, balances, cumulatives ledger if !normalized { fmt.Fprintf(w, format, printableKey, printableCurrency, balances[key][currency], printableCurrency, cumulative) } else { - factor := normalizer.NormalizeFactor(ledger.Delta{Name: key, Date: date}) + factor := normalizer.NormalizeFactor(key, date) trailingMax := maxes[currency] - math.Abs(balances[key][currency]) fmt.Fprintf(w, format, printableKey, printableCurrency, balances[key][currency], printableCurrency, cumulative, cumulative*factor, factor, printableCurrency, factor*trailingMax) } diff --git a/cmd/http/router.go b/cmd/http/router.go index e1a18c4..aa6ce18 100644 --- a/cmd/http/router.go +++ b/cmd/http/router.go @@ -135,7 +135,7 @@ func (router Router) APITransactions(w http.ResponseWriter, r *http.Request) { v := normalized[k] if v := math.Abs(v["$"]); v < biggest { normalizedDelta := biggest - v - normalizedFactor := normalizer.NormalizeFactor(ledger.Delta{Name: k, Date: time.Now().Format("2006-01-02")}) + normalizedFactor := normalizer.NormalizeFactor(k, time.Now().Format("2006-01-02")) normalized[fmt.Sprintf(`(%s trailing $)`, k)] = ledger.Balance{"$": normalizedDelta * normalizedFactor} } } diff --git a/src/ana/normalize.go b/src/ana/normalize.go index ebbe6c8..0bbb578 100644 --- a/src/ana/normalize.go +++ b/src/ana/normalize.go @@ -68,15 +68,15 @@ func (n Normalizer) Normalize(deltas ledger.Deltas) ledger.Deltas { } func (n Normalizer) NormalizeDelta(delta ledger.Delta) ledger.Delta { - delta.Value /= n.NormalizeFactor(delta) + delta.Value /= n.NormalizeFactor(delta.Name, delta.Date) return delta } -func (n Normalizer) NormalizeFactor(delta ledger.Delta) float64 { +func (n Normalizer) NormalizeFactor(name, date string) float64 { for pattern := range n.m { - if regexp.MustCompile(pattern).MatchString(delta.Name) { + if regexp.MustCompile(pattern).MatchString(name) { for _, normalize := range n.m[pattern] { - if normalize.startDate < delta.Date { + if normalize.startDate < date { return normalize.factor } } diff --git a/src/ledger/balances.go b/src/ledger/balances.go index 8ad1eb0..d2f374e 100644 --- a/src/ledger/balances.go +++ b/src/ledger/balances.go @@ -9,6 +9,10 @@ import ( "time" ) +type Normalizer interface { + NormalizeFactor(string, string) float64 +} + type Balances map[string]Balance type Balance map[Currency]float64 @@ -246,3 +250,14 @@ func (balance Balance) Debug() string { } return strings.Join(result, " + ") } + +func (balances Balances) Normalize(n Normalizer, date string) Balances { + result := make(Balances) + for name, balance := range balances { + result[name] = make(Balance) + for currency, value := range balance { + result[name][currency] = value / n.NormalizeFactor(name, date) + } + } + return result +}