diff options
Diffstat (limited to 'hledger-fire.hs')
-rwxr-xr-x | hledger-fire.hs | 86 |
1 files changed, 0 insertions, 86 deletions
diff --git a/hledger-fire.hs b/hledger-fire.hs index 68ea5a8..8b13789 100755 --- a/hledger-fire.hs +++ b/hledger-fire.hs @@ -1,87 +1 @@ -#!/usr/bin/env runhaskell --- | Calculations for FIRE (financial independence, retire early) -module Main where -import Data.Decimal (Decimal(..), DecimalRaw(..), roundTo, divide) -import Data.Either (fromRight) -import qualified Data.List as List -import Data.Text (pack) -import Data.Time.Calendar (Day, toGregorian) -import Data.Time.Clock (UTCTime(utctDay), getCurrentTime) -import Hledger -data Config = Config - { age :: Decimal - } - -main = do - j <- getJournal - today <- getCurrentTime >>= return . utctDay - let (thisyear, _, _) = toGregorian today - let cfg = Config { age = (fromInteger thisyear) - 1992 } - say [ "savings rate:", show $ savingsRate j today ] - say [ "target fund:", show $ targetFund j today ] - let n = whenFreedom j today - say [ "when free:", show $ n, "months" - , "(I'll be", show $ roundTo 1 $ (n/12) + (age cfg), "years old)" - ] - -say = putStrLn . unwords - -getJournal :: IO Journal -getJournal = do - jp <- defaultJournalPath - let opts = definputopts { auto_ = True } - ej <- readJournalFile opts jp - return $ fromRight undefined ej - --- | Helper for getting the total out of a balance report. -getTotal :: Journal -> Day -> String -> Quantity -getTotal j d q = head $ map aquantity $ total - where - opts = defreportopts { balancetype_ = CumulativeChange } - (query, _) = parseQuery d $ pack q - (_, (Mixed total)) = balanceReport opts query j - --- | These are the accounts that I consider a part of my savings and not my --- cash-spending accounts. -savingsAccounts :: [String] -savingsAccounts = - [ "as:me:save", "as:me:vest" ] - --- | Savings rate is a FIRE staple. Basically take your savings and divide it by --- your income on a monthly basis. --- -savingsRate :: Journal -> Day -> Quantity -savingsRate j d = roundTo 2 $ allSavings / (- allIncome) - -- gotta flip the sign because income is negative - where - allSavings = getTotal j d query - query = List.intercalate " " $ savingsAccounts ++ ["cur:USD", "-p 'from 2019-11-01'"] - allIncome = getTotal j d "^in" - --- | The target fund is simply 25x your annual expenditure. --- --- This is going to be incomplete until I have a full year of --- expenses.. currently, I just use my most recent quarter times 4 as a proxy --- for the yearly expenses. --- --- Assumptions: 4% withdrawal rate, 3-5% return on investments. --- -targetFund :: Journal -> Day -> Quantity -targetFund j d = 25 * yearlyExpenses - where - yearlyExpenses = 4 * quarterlyExpenses - quarterlyExpenses = sum $ map aquantity $ total - (query, _) = parseQuery d $ pack "^ex -p lastquarter cur:USD" - (_, (Mixed total)) = balanceReport opts query j - opts = defreportopts - --- | How long until I can live off of my savings and investment returns? --- --- Return integer is number of months until I'm free. --- -whenFreedom :: Journal -> Day -> Quantity -whenFreedom j d = roundTo 1 $ targetFund j d / monthlySavings - where - monthlySavings = sum $ map (getTotal j d) $ map appendMonthly savingsAccounts - appendMonthly s = s ++ " --monthly" |