diff options
author | Ben Sima <ben@bsima.me> | 2020-12-30 15:27:31 -0500 |
---|---|---|
committer | Ben Sima <ben@bsima.me> | 2020-12-30 15:27:31 -0500 |
commit | a7508431554f817d07963f1feed071dfe5fbcf28 (patch) | |
tree | c4a6d280fa9e5a6119bf4d58b2726ed1cf3a07f8 | |
parent | 85f23894a6ef696af4d0f27368e757cdca887ce2 (diff) |
hledger levels, graph, and networth
-rwxr-xr-x | hledger-level | 133 | ||||
-rwxr-xr-x | hledger-networth | 4 | ||||
-rwxr-xr-x | hledger-overview.hs | 20 |
3 files changed, 153 insertions, 4 deletions
diff --git a/hledger-level b/hledger-level new file mode 100755 index 0000000..4c2643b --- /dev/null +++ b/hledger-level @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +set -exo pipefail + +query='^as ^li' + +function floor { + printf %.f $1 +} + +function ceil { + printf %.f $(bc <<< "$1+1") +} + +function dec { + bc <<< "$1-1" +} + +function inc { + bc <<< "$1+1" +} + +function exp10 { + bc <<< "10^$1" +} + +function log10 { + bc -l <<< "l($1)/l(10)" +} + +function add { + bc -l <<< "$1 + $2" +} + +# colors +purple="#6961ff" +green="#61ff69" +red="#ff6961" + +datefmt="%Y-%m-%d" +today=$(date +$datefmt) +xstart=$(date -d"2 years ago" +$datefmt) + +# basically just calculates the log10 and removes some columns +process_row=$(cat <<EOF +import csv, sys, math +from datetime import datetime +data = sys.stdin.readlines() +rdr = csv.reader(data) +next(rdr, None) # skip header +prev=None +for row in rdr: + date = row[1] + networth = float(row[6][:-3].replace(",", "").strip()) + if prev: + x1 = datetime.strptime(prev[0], "$datefmt") + x2 = datetime.strptime(date, "$datefmt") + y1 = prev[1] + y2 = networth + change = (y1-y2) / max((x1-x2).days, 1) + change = ((networth / prev[1]) - 1) * 100 + else: + change = 1 + level = math.log10(networth) + print(date) + print(f"{date},{level:.2f},{networth:.2f},{change}") + prev = (date, networth) +EOF +) + +networth=$(hledger bal $query --depth=1 -V --infer-value \ + | tail -n1 \ + | awk '{ print $1 }' \ + | tr -d ',' +) +level=$(log10 $networth) +flevel=$(floor $level) + +data=$(mktemp --suffix=-data) +hledger reg $query -V --infer-value -O csv \ + | python -c "$process_row" \ + > $data + +plot=$(mktemp --suffix=-plot) + +# here's the actual plotting code +cat <<EOF > $plot +set datafile separator "," + +#set term dumb + +set multiplot +set size 1, 0.3 +set grid + +# x axis +set xdata time +set timefmt '$datefmt' +set xrange ['$xstart':'$today'] +set format x '$datefmt' +set xtics rotate by 60 offset 0,-1.5 out nomirror + +# Level +set origin 0, 0.6 +set ytics 0.2 +set yrange [$(floor $(dec $level)):$(ceil $level)] +set ylabel "Overall Level" textcolor rgb "green" +plot '$data' using 1:2 linecolor rgb "red" smooth bezier with lines title "Level ($level)", \ + $(inc $flevel) linecolor rgb "$purple" with lines title "Level $(inc $flevel)", \ + $flevel linecolor rgb "$green" with lines title "Level $flevel", \ + $(dec $flevel) linecolor rgb "$red" with lines title "Level $(dec $flevel)" + + +# Net worth, within current level +set origin 0, 0.3 +set ytics $(exp10 $level) +set yrange [$(exp10 $(dec $flevel)):$(exp10 $(inc $flevel))] +set ylabel "Net worth within current level" textcolor rgb "green" +plot '$data' using 1:3 linecolor rgb "green" with filledcurves x1 title "Net Worth ($networth)", \ + $(exp10 $(inc $flevel)) linecolor rgb "$purple" with lines title "Level $(inc $flevel)", \ + $(exp10 $flevel) linecolor rgb "$green" with lines title "Level $flevel", \ + $(exp10 $(dec $flevel)) linecolor rgb "$red" with lines title "Level $(dec $flevel)" + +# Rate of change +set origin 0, 0 +set ytics 10 +set yrange [-100:100] +set ylabel "Rate of change" +plot '$data' using 1:4 linecolor rgb "green" smooth csplines with lines title "rate of change" + +unset multiplot +EOF + +gnuplot -p $plot diff --git a/hledger-networth b/hledger-networth new file mode 100755 index 0000000..67c8aa8 --- /dev/null +++ b/hledger-networth @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +# +# pipe to tplot for a quick plot +hledger reg ^as ^li -V | awk -F'[[:space:]][[:space:]]+' '{print $4}' | cut -d' ' -f1 | tr -d ',' diff --git a/hledger-overview.hs b/hledger-overview.hs index 46a4f70..2d7e8e8 100755 --- a/hledger-overview.hs +++ b/hledger-overview.hs @@ -5,7 +5,7 @@ -- | Calculates and displays an overview of my finances. module Main where -import Data.Decimal (Decimal (..), DecimalRaw (..), divide, roundTo) +import Data.Decimal (Decimal (..), DecimalRaw (..), divide, roundTo, roundTo', realFracToDecimal) import Data.Either (fromRight) import Data.Function ((&)) import qualified Data.List as List @@ -46,7 +46,11 @@ main = do row " in - ex" (prn $ bal "^in ^ex cur:USD") $ Just "keep this negative to make progress" row "cred load" (prn netLiquid) $ Just "net liquid: credit spending minus puren cash assets. keep it positive" row "net worth" (prn netWorth) Nothing - row " level" (pr $ level netWorth) Nothing + row " level" (pr $ level netWorth) (Just $ "+" <> (prn $ netWorth - (unlevel $ roundTo' floor 1 $ level netWorth))) + let levelup n = level netWorth & (+n) & roundTo' floor 1 & unlevel & \target -> target - netWorth + row " next" (prn $ levelup 0.1) (Just $ prn $ roundTo' floor 1 $ level netWorth + 0.1) + row " nnext" (prn $ levelup 0.2) (Just $ prn $ roundTo' floor 1 $ level netWorth + 0.2) + row " nnnext" (prn $ levelup 0.3) (Just $ prn $ roundTo' floor 1 $ level netWorth + 0.3) sec "trivials" let trivialWorth = roundTo 2 $ trivial * netWorth @@ -101,8 +105,16 @@ prn d = T.intercalate "." $ case T.splitOn "." $ T.pack $ show $ roundTo 2 d of -- | There's levels to life, a proxy metric for what level you're at is your net -- worth rounded down to the nearest power of 10. -level :: Decimal -> Integer -level = floor . logBase 10 . realToFrac +level :: Decimal -> Decimal +level = realFracToDecimal 2 . logBase 10 . realToFrac + +-- | Given a level, return the net worth required to achieve that level. +unlevel :: Decimal -> Decimal +unlevel = realFracToDecimal 2 . (10**) . realToFrac + +-- Shows the steps between levels +steps = let lvls = [5.0, 5.2 .. 7.0]; ls = map (realToFrac . unlevel) lvls in zip (map realToFrac lvls) (zipWith (-) (ls++[0]) (0:ls)) + -- | A trivial decision is one that is between 0.01% of the total. -- |