diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,62 @@
+# Revision history for hledger-interest
+
+## 1.7.0 (2026-11-11)
+
+### Changes that affect computed interest
+
+Several bugs have been fixed that made hledger-interest compute wrong
+amounts. Re-running it on existing journals may produce different
+results in the following cases:
+
+- `--bgb288` applied 5.37% to every date after 2011-06-30, because a
+  stray entry in the table of base rates shadowed all later rates. The
+  table has been fixed, checked against the rates published by the
+  Deutsche Bundesbank, and extended up to 2026-12-31. Dates after the
+  end of the table now produce a clear error message instead of a
+  crash.
+- `--annual-schedule` used the rate in effect at the start of a period
+  until the next transaction or year end, ignoring any rate changes in
+  between, and a new rate took effect only on the day after its start
+  date. Rates now take effect exactly on their start date
+  ([#20](https://github.com/peti/hledger-interest/issues/20),
+  [#23](https://github.com/peti/hledger-interest/pull/23)). Dates
+  before the first entry of the schedule earn no interest instead of
+  crashing.
+- A transaction on the first day of a rate period, e.g. on January 1st
+  with `--annual`, earned one day of interest too many
+  ([#21](https://github.com/peti/hledger-interest/issues/21)).
+- `--30E-360isda` counted the days of a period that starts on the 31st
+  of a month wrongly, e.g. 30 instead of 15 days from 2023-01-31 to
+  2023-02-15 ([#28](https://github.com/peti/hledger-interest/issues/28)).
+- Interest on balances without decimal places is now shown with at
+  least two decimal places instead of being rounded to whole units.
+
+### New features
+
+- The new `--post-on-transactions` flag posts interest only when the
+  balance changes (and on the final day with `--today`), rather than at
+  the end of every year and rate period. Interest is still computed
+  separately for every year and rate, but no longer compounds at the end
+  of each year
+  ([#16](https://github.com/peti/hledger-interest/issues/16),
+  [#26](https://github.com/peti/hledger-interest/issues/26)).
+- Without `--file`, hledger-interest now reads `$LEDGER_FILE` or
+  `~/.hledger.journal` like hledger does, instead of silently reading
+  nothing ([#24](https://github.com/peti/hledger-interest/issues/24)).
+  Errors while reading the journal, such as a missing file, are
+  reported without the "Uncaught exception" noise.
+
+### Other changes
+
+- Descriptions of interest on multi-currency balances no longer contain
+  line breaks ([#11](https://github.com/peti/hledger-interest/issues/11),
+  [#13](https://github.com/peti/hledger-interest/pull/13)).
+- The README explains how interest is computed with each day counting
+  convention, and that `--act` corresponds to Actual/Actual ISDA
+  ([#25](https://github.com/peti/hledger-interest/issues/25)).
+- Support hledger-lib up to version 1.52.x and GHC 9.6 to 9.14.
+- Drop the obsolete dependency on Cabal and the `Setup.hs` file.
+
+## 1.6.7 and earlier
+
+See the [git history](https://github.com/peti/hledger-interest/commits/master).
diff --git a/Hledger/Interest.hs b/Hledger/Interest.hs
--- a/Hledger/Interest.hs
+++ b/Hledger/Interest.hs
@@ -33,17 +33,24 @@
   , targetAccount :: AccountName
   , dayCountConvention :: DayCountConvention
   , interestRate :: Rate
+  , postOnTransactions :: Bool
+    -- ^ Post accrued interest only on transaction dates rather than at the
+    -- end of every rate period (e.g. at every year end with 'perAnno').
   }
 
 data InterestState = InterestState
   { balancedUntil :: Day
   , balance :: MixedAmount
+  , accruedDays :: Integer          -- ^ interest accrued but not posted yet
+  , accruedInterest :: MixedAmount
   }
 
 nullInterestState :: InterestState
 nullInterestState = InterestState
   { balancedUntil = nulldate
   , balance = nullmixedamt
+  , accruedDays = 0
+  , accruedInterest = nullmixedamt
   }
 
 processTransaction :: Transaction -> Computer ()
@@ -56,8 +63,12 @@
     bal <- gets balance
     modify (\st -> st { balance = bal + pamount p })
 
+-- | Compute and post all interest up to and including the given day.
 computeInterest :: Day -> Computer ()
-computeInterest day = do
+computeInterest day = accrueInterest day >> postInterest day Nothing
+
+accrueInterest :: Day -> Computer ()
+accrueInterest day = do
   from <- gets balancedUntil
   bal <- gets balance
   rate <- asks interestRate
@@ -67,30 +78,51 @@
   modify (\st -> st { balancedUntil = newFrom })
   when (to >= from && not (mixedAmountIsZero bal)) $ do
     diff <- asks dayCountConvention
-    t <- mkTrans to ((from `diff` to) + 1) ratePerAnno
+    yearDays <- daysInYear to
+    let days = (from `diff` to) + 1
+        interest = mixed [ a { aquantity = (aquantity a * ratePerAnno) / fromInteger yearDays * fromInteger days } | a <- amounts bal ]
+    modify (\st -> st { accruedDays = accruedDays st + days
+                      , accruedInterest = accruedInterest st + interest
+                      })
+    onTransactionsOnly <- asks postOnTransactions
+    unless onTransactionsOnly (postInterest to (Just ratePerAnno))
+  when (newFrom <= day) (accrueInterest day)
+
+-- | Post all accrued interest as one transaction on the given day. The
+-- description mentions the rate only if one is given, i.e. if the interest
+-- was accrued over a single rate period.
+postInterest :: Day -> Maybe Decimal -> Computer ()
+postInterest day ratePerAnno = do
+  st <- get
+  when (accruedDays st > 0) $ do
+    put st { accruedDays = 0, accruedInterest = nullmixedamt }
+    t <- mkTrans day (accruedDays st) (accruedInterest st) ratePerAnno
     tell [t]
     processTransaction t
-  when (newFrom < day) (computeInterest day)
 
 daysInYear :: Day -> Computer Integer
 daysInYear now = asks dayCountConvention >>= \diff -> return (day1 `diff` day2)
   where day1 = fromGregorian (fst (toOrdinalDate now)) 1 1
         day2 = fromGregorian (succ (fst (toOrdinalDate now))) 1 1
 
-mkTrans :: Day -> Integer -> Decimal -> Computer Transaction
-mkTrans day days ratePerAnno = do
+-- | Create the transaction for the given interest, which was accrued over
+-- the given number of days.
+mkTrans :: Day -> Integer -> MixedAmount -> Maybe Decimal -> Computer Transaction
+mkTrans day days interest ratePerAnno = do
   bal <- gets balance
   srcAcc <- asks sourceAccount
   targetAcc <- asks targetAccount
-  perDayScalar <- daysInYear day
   let t = nulltransaction
           { tdate          = day
-          , tdescription   = T.pack $ showPercent ratePerAnno ++ " interest for " ++ showMixedAmount bal ++ " over " ++ show days ++ " days"
+          , tdescription   = T.pack $ maybe "" (\r -> showPercent r ++ " ") ratePerAnno ++ "interest for " ++ showMixedAmountOneLine bal ++ " over " ++ show days ++ " days"
           , tpostings      = [pTarget,pSource]
           }
+      -- balance might have been an exact amount, in which case its precision might
+      -- be set to 0 decimal places. We ensure that interest is displayed with at least
+      -- two decimal points
       pTarget = nullposting
           { paccount       = targetAcc
-          , pamount        = mixed [ a { aquantity = (aquantity a * ratePerAnno) / fromInteger perDayScalar * fromInteger days } | a <- amounts bal ]
+          , pamount        = mixed [ setMinPrecision 2 a | a <- amounts interest ]
           , ptype          = RegularPosting
           , ptransaction   = Just t
           }
@@ -101,6 +133,16 @@
           , ptransaction   = Just t
           }
   return t
+  where
+    -- Copy of amountSetPrecisionMin (with amountDisplayPrecision inlined) from
+    -- hledger-lib >= 1.32, which older hledger-lib versions (still allowed by our
+    -- lower bound) do not provide
+    setMinPrecision minp a = amountSetPrecision (Precision $ max minp displayPrecision) a
+      where
+        displayPrecision =
+          case asprecision $ astyle a of
+            Precision n      -> n
+            NaturalPrecision -> decimalPlaces (normalizeDecimal (aquantity a))
 
 showPercent :: Decimal -> String
 showPercent r = shows (r * 100) "%"
diff --git a/Hledger/Interest/DayCountConvention.hs b/Hledger/Interest/DayCountConvention.hs
--- a/Hledger/Interest/DayCountConvention.hs
+++ b/Hledger/Interest/DayCountConvention.hs
@@ -33,13 +33,21 @@
     (y1,m1,d1) = toGregorian date1
     (y2,m2,d2) = toGregorian date2
 
--- This variant additionally normalizes end-of-months to 30, i.e. there
--- are 30 days between 2003-02-28 and 2003-03-31.
-
+-- | This variant additionally normalizes end-of-months to 30, i.e. there
+-- are 30 days between 2003-02-28 and 2003-03-31. (Strict 30E/360 ISDA
+-- exempts a February month-end that is the maturity date; we have no
+-- notion of maturity, so that exception is not applied.)
+--
+-- >>> diff30E_360isda (fromGregorian 2003 2 28) (fromGregorian 2003 3 31)
+-- 30
+-- >>> diff30E_360isda (fromGregorian 2023 1 31) (fromGregorian 2023 2 15)
+-- 15
+-- >>> diff30E_360isda (fromGregorian 2023 1 15) (fromGregorian 2023 2 28)
+-- 45
 diff30E_360isda :: DayCountConvention
 diff30E_360isda date1 date2 = assert (date1 <= date2) $ mkDiff30_360 (y1, m1, d1') (y2, m2, d2')
   where
     (y1,m1,d1) = toGregorian date1
     (y2,m2,d2) = toGregorian date2
-    d1' = if d1 > 30 || d1 == gregorianMonthLength y1 m1 then 30 else d1
-    d2' = if d1 > 30 || d2 == gregorianMonthLength y2 m2 then 30 else d2
+    d1' = if d1 == gregorianMonthLength y1 m1 then 30 else d1
+    d2' = if d2 == gregorianMonthLength y2 m2 then 30 else d2
diff --git a/Hledger/Interest/Rate.hs b/Hledger/Interest/Rate.hs
--- a/Hledger/Interest/Rate.hs
+++ b/Hledger/Interest/Rate.hs
@@ -3,7 +3,8 @@
 import Data.Time.Calendar
 import Data.Time.Calendar.OrdinalDate
 import Data.Decimal
-import Data.List (sortOn)
+import Data.List (sortOn, find)
+import Data.Maybe (fromMaybe)
 
 type Rate = Day -> (Day,Decimal)
 
@@ -14,9 +15,28 @@
 perAnno rate date = (yearEnd date, rate)
 
 perAnnoSchedule :: [(Day,Decimal)] -> Rate
-perAnnoSchedule schedule date = (yearEnd date, effectiveRate)
+perAnnoSchedule schedule date = (effectiveUntil, effectiveRate)
   where
-    (_, effectiveRate) = last $ takeWhile (\(fromDate, _) -> fromDate<date) sortedSchedule
+    effectiveUntil =
+      -- make sure that end date we report does not cross the year boundary
+      min (yearEnd date) endOfPeriod
+    (endOfPeriod, effectiveRate) =
+      case find (\(fromDate, toDate,  _) -> fromDate<=date && date<=toDate) fromToSchedule of
+        -- date precedes the schedule: no interest until the first rate takes effect
+        Nothing -> (beforeSchedule, 0)
+        Just (_,toDate,rate) -> (toDate, rate)
+    beforeSchedule =
+      case sortedSchedule of
+        (firstDate, _):_ -> pred firstDate
+        []               -> yearEnd date
+    fromToSchedule =
+      -- Convert the list of (startDate,rate) to (startDate,endDate,rate) intervals, where
+      -- the end date of the interval is the day before the start of the next interval,
+      -- and the last interval goes to infinity
+      zipWith
+        (\(from, rate) (to, _) -> (from, pred to, rate))
+        sortedSchedule
+        (drop 1 sortedSchedule ++ [(day 999999 1 1,0)])
     sortedSchedule = sortOn fst schedule
 
 day :: Integer -> Int -> Int -> Day
@@ -25,69 +45,113 @@
 yearEnd :: Day -> Day
 yearEnd date = day (fst (toOrdinalDate date)) 12 31
 
+-- | Interest according to German BGB §288: the base rate published by the
+-- Deutsche Bundesbank (BGB §247) plus 5 percentage points.
+--
+-- >>> bgb288 (day 2011 08 15)
+-- (2011-12-31,0.0537)
+-- >>> bgb288 (day 2018 07 01)
+-- (2018-12-31,0.0412)
+-- >>> bgb288 (day 2023 06 01)
+-- (2023-06-30,0.0662)
+-- >>> bgb288 (day 2026 07 01)
+-- (2026-12-31,0.0652)
 bgb288 :: Rate
 bgb288 = basiszins (5/100)
 
 basiszins :: Decimal -> Rate
-basiszins r date = (to, r + p)
+basiszins r date =
+  case tableRate basiszinsPeriods date of
+    Just (to, p) -> (to, r + p)
+    Nothing      -> error ("hledger-interest: the BGB base rate after " ++ show lastEnd ++
+                           " is not known yet; please update basiszinsTable in Hledger/Interest/Rate.hs")
   where
-    (_,to,p) = head (dropWhile (\(_,to',_) -> to' < date) basiszinsTable)
+    lastEnd = case reverse basiszinsPeriods of
+                (_,to,_) : _ -> to
+                []           -> error "basiszinsTable is empty"
 
-basiszinsTable :: [(Day, Day, Decimal)]
+-- | Find the period of a (from, to, rate) table that has not ended yet at
+-- the given date, and return its end date and rate.
+tableRate :: [(Day, Day, Decimal)] -> Day -> Maybe (Day, Decimal)
+tableRate table date =
+  case dropWhile (\(_,to,_) -> to < date) table of
+    (_,to,p) : _ -> Just (to, p)
+    []           -> Nothing
+
+-- | Like 'tableRate', but fail with an error message if the table does not
+-- cover the given date.
+tableRate' :: String -> [(Day, Day, Decimal)] -> Rate
+tableRate' name table date =
+  fromMaybe (error ("hledger-interest: no " ++ name ++ " interest rate is known for " ++ show date))
+            (tableRate table date)
+
+-- | Turn the start dates of 'basiszinsTable' into (from, to, rate) periods.
+-- Each period ends the day before the next one starts; the last one is
+-- valid for six months.
+basiszinsPeriods :: [(Day, Day, Decimal)]
+basiszinsPeriods = zipWith (\(from, p) to -> (from, to, p)) basiszinsTable ends
+  where
+    starts = map fst basiszinsTable
+    ends   = map pred (drop 1 starts ++ map (addGregorianMonthsClip 6) (take 1 (reverse starts)))
+
+-- | Basiszinssatz according to BGB §247, keyed by the date it takes effect.
+-- Source: https://www.bundesbank.de/de/bundesbank/organisation/agb-und-regelungen/basiszinssatz-607820
+basiszinsTable :: [(Day, Decimal)]
 basiszinsTable =
-  [ (day 2002 01 01, day 2002 06 30, 257 / 10000)
-  , (day 2002 07 01, day 2002 12 31, 247 / 10000)
-  , (day 2003 01 01, day 2003 06 30, 197 / 10000)
-  , (day 2003 07 01, day 2003 12 31, 122 / 10000)
-  , (day 2004 01 01, day 2004 06 30, 114 / 10000)
-  , (day 2004 07 01, day 2004 12 31, 113 / 10000)
-  , (day 2005 01 01, day 2005 06 30, 121 / 10000)
-  , (day 2005 07 01, day 2005 12 31, 117 / 10000)
-  , (day 2006 01 01, day 2006 06 30, 137 / 10000)
-  , (day 2006 07 01, day 2006 12 31, 195 / 10000)
-  , (day 2007 01 01, day 2007 06 30, 270 / 10000)
-  , (day 2007 07 01, day 2007 12 31, 319 / 10000)
-  , (day 2008 01 01, day 2008 06 30, 332 / 10000)
-  , (day 2008 07 01, day 2008 12 31, 319 / 10000)
-  , (day 2009 01 01, day 2009 06 30, 162 / 10000)
-  , (day 2009 07 01, day 2009 12 31,  12 / 10000)
-  , (day 2010 01 01, day 2010 06 30,  12 / 10000)
-  , (day 2010 07 01, day 2010 12 31,  12 / 10000)
-  , (day 2011 01 01, day 2011 06 30,  12 / 10000)
-  , (day 2011 07 01, day 2999 12 31,  37 / 10000)
-  , (day 2011 01 01, day 2011 06 30,  12 / 10000)
-  , (day 2011 07 01, day 2011 12 31,  37 / 10000)
-  , (day 2012 01 01, day 2012 06 30,  12 / 10000)
-  , (day 2012 07 01, day 2012 12 31,  12 / 10000)
-  , (day 2013 01 01, day 2013 06 30, (-13) / 10000)
-  , (day 2013 07 01, day 2013 12 31, (-38) / 10000)
-  , (day 2014 01 01, day 2014 06 30, (-63) / 10000)
-  , (day 2014 07 01, day 2014 12 31, (-73) / 10000)
-  , (day 2015 01 01, day 2015 06 30, (-83) / 10000)
-  , (day 2015 07 01, day 2015 12 31, (-83) / 10000)
-  , (day 2016 01 01, day 2016 06 30, (-83) / 10000)
-  , (day 2016 07 01, day 2016 12 31, (-88) / 10000)
-  , (day 2017 01 01, day 2017 06 30, (-88) / 10000)
-  , (day 2017 07 01, day 2017 12 31, (-88) / 10000)
-  , (day 2018 01 01, day 2018 06 30, (-88) / 10000)
-  , (day 2019 01 01, day 2019 06 30, (-88) / 10000)
-  , (day 2019 07 01, day 2019 12 31, (-88) / 10000)
-  , (day 2020 01 01, day 2020 06 30, (-88) / 10000)
-  , (day 2020 07 01, day 2020 12 31, (-88) / 10000)
-  , (day 2021 01 01, day 2021 06 30, (-88) / 10000)
-  , (day 2021 07 01, day 2021 12 31, (-88) / 10000)
-  , (day 2022 01 01, day 2022 06 30, (-88) / 10000)
-  , (day 2022 07 01, day 2022 12 31, (-88) / 10000)
-  , (day 2023 01 01, day 2023 06 30, 162 / 10000)
-  , (day 2023 07 01, day 2023 12 31, 312 / 10000)
-  , (day 2024 01 01, day 2024 06 30, 362 / 10000)
-  , (day 2024 07 01, day 2024 12 31, 337 / 10000)
+  [ (day 2002 01 01,  257 / 10000)
+  , (day 2002 07 01,  247 / 10000)
+  , (day 2003 01 01,  197 / 10000)
+  , (day 2003 07 01,  122 / 10000)
+  , (day 2004 01 01,  114 / 10000)
+  , (day 2004 07 01,  113 / 10000)
+  , (day 2005 01 01,  121 / 10000)
+  , (day 2005 07 01,  117 / 10000)
+  , (day 2006 01 01,  137 / 10000)
+  , (day 2006 07 01,  195 / 10000)
+  , (day 2007 01 01,  270 / 10000)
+  , (day 2007 07 01,  319 / 10000)
+  , (day 2008 01 01,  332 / 10000)
+  , (day 2008 07 01,  319 / 10000)
+  , (day 2009 01 01,  162 / 10000)
+  , (day 2009 07 01,   12 / 10000)
+  , (day 2010 01 01,   12 / 10000)
+  , (day 2010 07 01,   12 / 10000)
+  , (day 2011 01 01,   12 / 10000)
+  , (day 2011 07 01,   37 / 10000)
+  , (day 2012 01 01,   12 / 10000)
+  , (day 2012 07 01,   12 / 10000)
+  , (day 2013 01 01, (-13) / 10000)
+  , (day 2013 07 01, (-38) / 10000)
+  , (day 2014 01 01, (-63) / 10000)
+  , (day 2014 07 01, (-73) / 10000)
+  , (day 2015 01 01, (-83) / 10000)
+  , (day 2015 07 01, (-83) / 10000)
+  , (day 2016 01 01, (-83) / 10000)
+  , (day 2016 07 01, (-88) / 10000)
+  , (day 2017 01 01, (-88) / 10000)
+  , (day 2017 07 01, (-88) / 10000)
+  , (day 2018 01 01, (-88) / 10000)
+  , (day 2018 07 01, (-88) / 10000)
+  , (day 2019 01 01, (-88) / 10000)
+  , (day 2019 07 01, (-88) / 10000)
+  , (day 2020 01 01, (-88) / 10000)
+  , (day 2020 07 01, (-88) / 10000)
+  , (day 2021 01 01, (-88) / 10000)
+  , (day 2021 07 01, (-88) / 10000)
+  , (day 2022 01 01, (-88) / 10000)
+  , (day 2022 07 01, (-88) / 10000)
+  , (day 2023 01 01,  162 / 10000)
+  , (day 2023 07 01,  312 / 10000)
+  , (day 2024 01 01,  362 / 10000)
+  , (day 2024 07 01,  337 / 10000)
+  , (day 2025 01 01,  227 / 10000)
+  , (day 2025 07 01,  127 / 10000)
+  , (day 2026 01 01,  127 / 10000)
+  , (day 2026 07 01,  152 / 10000)
   ]
 
 ingDiba :: Rate
-ingDiba date = (to, p)
-  where
-    (_,to,p) = head (dropWhile (\(_,to',_) -> to' < date) ingDibaTable)
+ingDiba = tableRate' "ING-DiBa" ingDibaTable
 
 ingDibaTable :: [(Day, Day, Decimal)]
 ingDibaTable =
@@ -98,9 +162,7 @@
   ]
 
 db24 :: Rate
-db24 date = (to, p)
-  where
-    (_,to,p) = head (dropWhile (\(_,to',_) -> to' < date) db24Table)
+db24 = tableRate' "Deutsche Bank 24" db24Table
 
 db24Table :: [(Day, Day, Decimal)]
 db24Table =
diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -5,7 +5,7 @@
 import Hledger.Read
 import Hledger.Utils ( toRegex' )
 
-import Control.Exception ( bracket )
+import Control.Exception ( ErrorCall(..), bracket, handle )
 import Control.Monad
 import Data.List ( sortOn )
 import Data.Maybe
@@ -28,6 +28,7 @@
   , optDCC              :: Maybe DayCountConvention
   , optRate             :: Maybe Rate
   , optBalanceToday     :: Bool
+  , optPostOnTxns       :: Bool
   , optIgnoreAssertions :: Bool
   }
 
@@ -42,6 +43,7 @@
   , optDCC              = Nothing
   , optRate             = Nothing
   , optBalanceToday     = False
+  , optPostOnTxns       = False
   , optIgnoreAssertions = False
   }
 
@@ -52,11 +54,12 @@
  , Option ['v'] ["verbose"]           (NoArg (\o -> o { optVerbose = True }))                              "echo input ledger to stdout (default)"
  , Option ['q'] ["quiet"]             (NoArg (\o -> o { optVerbose = False }))                             "don't echo input ledger to stdout"
  , Option []    ["today"]             (NoArg (\o -> o { optBalanceToday = True }))                         "compute interest up until today"
- , Option ['f'] ["file"]              (ReqArg (\f o -> o { optInput = f : optInput o }) "FILE")          "input ledger file (pass '-' for stdin)"
+ , Option []    ["post-on-transactions"] (NoArg (\o -> o { optPostOnTxns = True }))                        "post interest only when the balance changes\n(and with --today), not at the end of\nevery year or rate period"
+ , Option ['f'] ["file"]              (ReqArg (\f o -> o { optInput = f : optInput o }) "FILE")            "input ledger file (pass '-' for stdin)\ndefault: $LEDGER_FILE or ~/.hledger.journal"
  , Option ['s'] ["source"]            (ReqArg (\a o -> o { optSourceAcc = a }) "ACCOUNT")                  "interest source account"
  , Option ['t'] ["target"]            (ReqArg (\a o -> o { optTargetAcc = a }) "ACCOUNT")                  "interest target account"
  , Option ['I'] ["ignore-assertions"] (NoArg (\o -> o { optIgnoreAssertions = True }))                     "ignore any failing balance assertions"
- , Option []    ["act"]               (NoArg (\o -> o { optDCC = Just diffAct }))                          "use 'act' day counting convention"
+ , Option []    ["act"]               (NoArg (\o -> o { optDCC = Just diffAct }))                          "use 'act/act' day counting convention\n(Actual/Actual ISDA; see README)"
  , Option []    ["30-360"]            (NoArg (\o -> o { optDCC = Just diff30_360 }))                       "use '30/360' day counting convention"
  , Option []    ["30E-360"]           (NoArg (\o -> o { optDCC = Just diff30E_360 }))                      "use '30E/360' day counting convention"
  , Option []    ["30E-360isda"]       (NoArg (\o -> o { optDCC = Just diff30E_360isda }))                  "use '30E/360isda' day counting convention"
@@ -92,7 +95,11 @@
   when (isNothing (optDCC opts)) (commandLineError "no day counting convention specified\n")
   when (isNothing (optRate opts)) (commandLineError "no interest rate specified\n")
   let ledgerInputOptions = definputopts { balancingopts_ = (balancingopts_ definputopts) { ignore_assertions_ = optIgnoreAssertions opts } }
-  jnl' <- runExceptT (readJournalFiles ledgerInputOptions (reverse (optInput opts))) >>= either fail return
+  inputFiles <- case optInput opts of
+                  [] -> pure <$> defaultJournalPath
+                  fs -> return (reverse fs)
+  jnl' <- handle (\(ErrorCall msg) -> hPutStrLn stderr msg >> exitFailure) $
+            runExceptT (readJournalFiles ledgerInputOptions inputFiles) >>= either fail return
   interestAcc <- case args of
                    []    -> commandLineError "required argument ACCOUNT is missing\n"
                    [acc] -> return $ T.pack acc
@@ -105,6 +112,7 @@
             , targetAccount = T.pack (optTargetAcc opts)
             , dayCountConvention = fromJust (optDCC opts)
             , interestRate = fromJust (optRate opts)
+            , postOnTransactions = optPostOnTxns opts
             }
   thisDay <- getCurrentDay
   let finalize
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,8 +1,13 @@
 hledger-interest
 ================
 
+[![hackage release](https://img.shields.io/hackage/v/hledger-interest.svg?label=hackage)](http://hackage.haskell.org/package/hledger-interest)
+[![stackage LTS package](http://stackage.org/package/hledger-interest/badge/lts)](http://stackage.org/lts/package/hledger-interest)
+[![stackage Nightly package](http://stackage.org/package/hledger-interest/badge/nightly)](http://stackage.org/nightly/package/hledger-interest)
+[![CI Status](https://github.com/peti/hledger-interest/actions/workflows/haskell-ci.yml/badge.svg)](https://github.com/peti/hledger-interest/actions/workflows/haskell-ci.yml)
+
 hledger-interest is a small command-line utility based on [Simon
-Michael's hlegder library](http://hledger.org/). Its purpose is to
+Michael's hledger library](http://hledger.org/). Its purpose is to
 compute interest for a given ledger account. Using command line flags,
 the program can be configured to use various [day counting
 conventions](http://en.wikipedia.org/wiki/Day_count_convention), such as
@@ -14,16 +19,21 @@
 the source code.
 
 An overview over the available run-time options can be displayed by
-running "`hleder-interest --help`":
+running "`hledger-interest --help`":
 
     Usage: hledger-interest [OPTION...] ACCOUNT
       -v          --verbose                   echo input ledger to stdout (default)
       -q          --quiet                     don't echo input ledger to stdout
                   --today                     compute interest up until today
+                  --post-on-transactions      post interest only when the balance changes
+                                              (and with --today), not at the end of
+                                              every year or rate period
       -f FILE     --file=FILE                 input ledger file (pass '-' for stdin)
+                                              default: $LEDGER_FILE or ~/.hledger.journal
       -s ACCOUNT  --source=ACCOUNT            interest source account
       -t ACCOUNT  --target=ACCOUNT            interest target account
-                  --act                       use 'act' day counting convention
+                  --act                       use 'act/act' day counting convention
+                                              (Actual/Actual ISDA; see README)
                   --30-360                    use '30/360' day counting convention
                   --30E-360                   use '30E/360' day counting convention
                   --30E-360isda               use '30E/360isda' day counting convention
@@ -42,6 +52,47 @@
 transfers the interest that accrued so far. Interest will be debited
 from the account designed by the `--source` flag and credited to the
 account designed by the `--target` flag.
+
+## Day counting conventions
+
+For every period, hledger-interest computes
+
+    interest = balance * rate * days / days-in-year
+
+where `days` is the length of the period, both ends included, and
+`days-in-year` is the length of the calendar year in which the period
+ends. Both are counted with the selected convention:
+
+- `--act` counts actual calendar days, so a year has 365 or 366 days.
+  `--annual`, `--annual-schedule`, and `--bgb288` never let a period
+  cross a year boundary, which makes this the [Actual/Actual
+  ISDA](https://en.wikipedia.org/wiki/Day_count_convention#Actual/Actual_ISDA)
+  method. It is also the reason why these rates post interest on
+  December 31st of every year (see below). With `--constant`, a period can span
+  several years and is divided by the length of the year it ends in, so
+  the result is only an approximation of Actual/Actual ISDA in that case.
+- `--30-360`, `--30E-360`, and `--30E-360isda` count every month as 30
+  days and every year as 360 days. They differ in how they treat the
+  31st and the end of February; see the [Wikipedia
+  article](https://en.wikipedia.org/wiki/Day_count_convention#30/360_methods)
+  for details. `--30E-360isda` does not apply the February exception
+  for the maturity date, because hledger-interest has no notion of
+  maturity.
+
+By default, hledger-interest posts the interest at the end of every
+period: on every transaction date, but also on December 31st and on
+every date at which the rate changes. If the interest is credited to the
+interest-bearing account itself, it compounds from then on. Many loans
+and savings accounts compound less often, though, e.g. only when a
+payment is made. With `--post-on-transactions`, interest is still
+computed separately for every year and rate, but posted only on
+transaction dates (and on the final day with `--today`). Since such a
+posting may cover several rates, its description does not mention a
+rate, e.g. for a rate change from 5% to 7% on 2021-12-20:
+
+    2022-01-10 interest for -9841.10 USD over 31 days
+        Liabilities:Loan       -53.65 USD
+        Expenses:Interest       53.65 USD
 
 ## Examples
 
diff --git a/Setup.hs b/Setup.hs
deleted file mode 100644
--- a/Setup.hs
+++ /dev/null
@@ -1,2 +0,0 @@
-import Distribution.Simple
-main = defaultMain
diff --git a/hledger-interest.cabal b/hledger-interest.cabal
--- a/hledger-interest.cabal
+++ b/hledger-interest.cabal
@@ -1,5 +1,5 @@
 Name:                   hledger-interest
-Version:                1.6.7
+Version:                1.7.0
 Synopsis:               computes interest for a given account
 License:                BSD3
 License-file:           LICENSE
@@ -10,14 +10,14 @@
 Build-type:             Simple
 Cabal-version:          >= 1.10
 Extra-source-files:     README.md
+                        CHANGELOG.md
 Stability:              stable
-tested-with:            GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.7,
-                        GHC == 9.0.2, GHC == 9.2.8, GHC == 9.4.8, GHC == 9.6.3,
-                        GHC == 9.8.4, GHC == 9.10.1
+tested-with:            GHC == 9.6.7, GHC == 9.8.4, GHC == 9.10.3,
+                        GHC == 9.12.2, GHC == 9.14.1
 
 Description:
  hledger-interest is a small command-line utility based on Simon
- Michael's hleder library. Its purpose is to compute interest for a
+ Michael's hledger library. Its purpose is to compute interest for a
  given ledger account. Using command line flags, the program can be
  configured to use various schemes for day-counting, such as act\/act,
  30\/360, 30E\/360, and 30\/360isda. Furthermore, it supports a (small)
@@ -50,45 +50,41 @@
  ledger would look like this:
  .
  > $ hledger-interest --file=test.ledger --source=Expenses:Interest --target=Liabilities:Bank --30-360 --annual=0.05 Liabilities:Bank
- > 2008/09/26 Loan
- >     Assets:Bank              EUR  10000.00
- >     Liabilities:Bank
- >
- > 2008/11/27 Payment
- >     Assets:Bank              EUR  -3771.12
- >     Liabilities:Bank
+ > 2008-09-26 Loan
+ >     Assets:Bank          EUR 10000.00
+ >     Liabilities:Bank    EUR -10000.00
  >
- > 2008/11/27 5.00% interest for EUR -10000.00 over 61 days
- >     Liabilities:Bank         EUR    -84.72
- >     Expenses:Interest
+ > 2008-11-27 5% interest for EUR -10000.00 over 61 days
+ >     Liabilities:Bank       EUR -84.72
+ >     Expenses:Interest       EUR 84.72
  >
- > 2008/12/31 5.00% interest for EUR -6313.60 over 34 days
- >     Liabilities:Bank         EUR    -29.81
- >     Expenses:Interest
+ > 2008-11-27 Payment
+ >     Assets:Bank         EUR -3771.12
+ >     Liabilities:Bank     EUR 3771.12
  >
- > 2009/05/03 Payment
- >     Assets:Bank              EUR  -1200.00
- >     Liabilities:Bank
+ > 2008-12-31 5% interest for EUR -6313.60 over 34 days
+ >     Liabilities:Bank       EUR -29.81
+ >     Expenses:Interest       EUR 29.81
  >
- > 2009/05/03 5.00% interest for EUR -6343.42 over 123 days
- >     Liabilities:Bank         EUR   -108.37
- >     Expenses:Interest
+ > 2009-05-03 5% interest for EUR -6343.42 over 123 days
+ >     Liabilities:Bank      EUR -108.37
+ >     Expenses:Interest      EUR 108.37
  >
- > 2009/12/31 5.00% interest for EUR -5251.78 over 238 days
- >     Liabilities:Bank         EUR   -173.60
- >     Expenses:Interest
+ > 2009-05-03 Payment
+ >     Assets:Bank         EUR -1200.00
+ >     Liabilities:Bank     EUR 1200.00
  >
- > 2010/12/10 Payment
- >     Assets:Bank              EUR  -3700.00
- >     Liabilities:Bank
+ > 2009-12-31 5% interest for EUR -5251.78 over 238 days
+ >     Liabilities:Bank      EUR -173.60
+ >     Expenses:Interest      EUR 173.60
  >
- > 2010/12/10 5.00% interest for EUR -5425.38 over 340 days
- >     Liabilities:Bank         EUR   -256.20
- >     Expenses:Interest
+ > 2010-12-10 5% interest for EUR -5425.38 over 340 days
+ >     Liabilities:Bank      EUR -256.20
+ >     Expenses:Interest      EUR 256.20
  >
- > 2010/12/31 5.00% interest for EUR -1981.58 over 21 days
- >     Liabilities:Bank         EUR     -5.78
- >     Expenses:Interest
+ > 2010-12-10 Payment
+ >     Assets:Bank         EUR -3700.00
+ >     Liabilities:Bank     EUR 3700.00
  .
  Running the utility with @--help@ gives a brief overview over the
  available options:
@@ -99,19 +95,26 @@
  >   -v          --verbose                   echo input ledger to stdout (default)
  >   -q          --quiet                     don't echo input ledger to stdout
  >               --today                     compute interest up until today
+ >               --post-on-transactions      post interest only when the balance changes
+ >                                           (and with --today), not at the end of
+ >                                           every year or rate period
  >   -f FILE     --file=FILE                 input ledger file (pass '-' for stdin)
+ >                                           default: $LEDGER_FILE or ~/.hledger.journal
  >   -s ACCOUNT  --source=ACCOUNT            interest source account
  >   -t ACCOUNT  --target=ACCOUNT            interest target account
  >   -I          --ignore-assertions         ignore any failing balance assertions
- >               --act                       use 'act' day counting convention
+ >               --act                       use 'act/act' day counting convention
+ >                                           (Actual/Actual ISDA; see README)
  >               --30-360                    use '30/360' day counting convention
  >               --30E-360                   use '30E/360' day counting convention
  >               --30E-360isda               use '30E/360isda' day counting convention
  >               --constant=RATE             constant interest rate
+ >               --annual=RATE               annual interest rate
  >               --annual-schedule=SCHEDULE  schedule of annual interest rates.
  >                                           syntax: '[(Date1,Rate1),(Date2,Rate2),...]'
- >               --annual=RATE               annual interest rate
  >               --bgb288                    compute interest according to German BGB288
+ >               --db24                      HACK: Deutsche Bank 24
+ >               --ing-diba                  HACK: compute interest according for Ing-Diba Tagesgeld account
 
 Source-Repository head
   Type:                 git
@@ -120,7 +123,7 @@
 Executable hledger-interest
   default-language:     Haskell2010
   Main-is:              Main.hs
-  Build-depends:        base >= 3 && < 5, hledger-lib >= 1.26 && < 1.42, time, mtl, Cabal, Decimal, text
+  Build-depends:        base >= 3 && < 5, hledger-lib >= 1.26 && < 1.53, time, mtl, Decimal, text
   other-modules:        Hledger.Interest
                         Hledger.Interest.DayCountConvention
                         Hledger.Interest.Rate
