diff --git a/doc/ledger-grammar.org b/doc/ledger-grammar.org
--- a/doc/ledger-grammar.org
+++ b/doc/ledger-grammar.org
@@ -16,6 +16,7 @@
 Letter = UpperCaseAscii ∪ LowerCaseAscii ∪ NonAscii
 Dollar = { '$' }
 Colon = { ':' }
+Comma = { ',' }
 OpenCurly = { '{' }
 CloseCurly = { '}' }
 OpenSquare = { '[' }
@@ -105,6 +106,18 @@
 <lvl3Cmdty> ::= Lvl3CmdtyChar+
 
 * Quantities
+
+<digListSpace> ::= Space Digit+
+<digListThinSpace> ::= ThinSpace Digit+
+<digListComma> ::= Comma Digit+
+<digListPeriod> ::= Period Digit+
+
+<digGrpSpace> ::= Digit+ <digListSpace>+
+<digGrpThinSpace> ::= Digit+ <digListThinSpace>+
+<digGrpComma> ::= Digit+ <digListComma>+
+<digGrpPeriod> ::= Digit+ <digListPeriod>+
+
+
 
 <digitGroup> ::= ThinSpace Digit+
 <digitSequence> ::= Digit+ <digitGroup>*
diff --git a/penny-bin.cabal b/penny-bin.cabal
--- a/penny-bin.cabal
+++ b/penny-bin.cabal
@@ -1,5 +1,5 @@
 Name: penny-bin
-Version: 0.20.0.0
+Version: 0.22.0.0
 Cabal-version: >=1.8
 Build-Type: Simple
 License: BSD3
@@ -11,162 +11,10 @@
 bug-reports: omari@smileystation.com
 Category: Console, Finance
 License-File: LICENSE
-synopsis: Extensible double-entry accounting system - binary and documentation
-
-description: Penny is a double-entry accounting system. It is inspired
-  by, but incompatible with, John Wiegley's Ledger, which is available
-  at <http://ledger-cli.org/>. Installing this package with cabal
-  install will install the executable program and the necessary
-  libraries.
-
-  .
-
-  * Penny is a double-entry accounting system. It uses traditional
-  accounting terminology, such as the terms \"Debit\" and
-  \"Credit\". If you need a refresher on the basics of double-entry
-  accounting, pick up a used accounting textbook from your favorite
-  bookseller (they can be had cheaply, for less than ten U.S. dollars
-  including shipping) or check out
-  <http://www.principlesofaccounting.com/>, a great free online text.
-
-  .
-
-  * Penny is based around "Penny.Lincoln", a core library to represent
-    transactions and postings and their components, such as their
-    amounts and whether they are debits and credits. You can use
-    Lincoln all by itself even if you don't use the other components
-    of Penny, which you may find handy if you are a Haskell
-    programmer. I wrote Penny because I wanted a precise library to
-    represent my accounting data so I could analyze it programatically
-    and verify its consistency.
-
-  .
-
-  * Penny's command line interface and its reports give you
-    great flexibility to filter and sort postings. Each posting
-    within a transaction may have its own flags assigned (e.g. to
-    indicate whether the posting is cleared) and each posting may have
-    infinite \"tags\" assigned to it, giving you another way to
-    categorize your postings. For instance, you might have vacation
-    related postings in several different accounts, but you can give
-    them all a \"vacation\" tag.
-
-  .
-
-  * You can easily build a program to process downloads of Open
-    Financial Exchange data from your financial institution. Your
-    program will merge new transactions into your ledger
-    automatically.
-
-  .
-
-  * Full Unicode support.
-
-  .
-
-  * Penny's reports have color baked in from the beginning. You do not
-    have to use color, which is handy if you are sending
-    output to a file or if, well, you just don't like color.
-
-  .
-
-  * Penny's reports automatically adjust themselves to the width of
-    your screen. You can easily specify how much or how little data to
-    see with command line options.
-
-  .
-
-  * Penny handles multiple commodities (for example, multiple
-    currencies, stocks and bonds, tracking other assets, etc.) in an
-    easy and transparent way that is consistent with double-entry
-    accounting principles. It embraces the philosophy outlined in this
-    tutorial on multiple commodity accounting:
-    <http://www.mscs.dal.ca/~selinger/accounting/tutorial.html>.
-
-  .
-
-  * Penny stores amounts using only integers.  This ensures the
-    accuracy of your data, as using floating point values to represent
-    money is a bad idea. Here is one explanation:
-    <http://stackoverflow.com/questions/3730019/why-not-use-double-or-float-to-represent-currency>. The
-    use of integer arithmetic also makes Penny simpler internally, as
-    there is no need for arbitrary rounding to compensate for the
-    bizarre and inaccurate results that sometimes arise from the use of
-    floating-point values to represent currencies.
-
-  .
-
-  * Freely licensed under the MIT license. If you take this code,
-    improve it, lock it up and make it proprietary, and sell it,
-    AWESOME! I haven't lost anything because I still have my code and,
-    what's more, then maybe I can buy your product and not have to
-    maintain this one any more!
-
-  .
-
-  * Tested using QuickCheck. The tests are available in the Git
-    repository that also contains the main library. Not everything
-    is tested, but the tests that exist so far have already rooted
-    out some strange corner-case bugs.
-
-  .
-
-  Non-features / disadvantages:
-
-  .
-
-  * Written in Haskell. Yes, I think Haskell is the best tool ever,
-    but its compiler is not as commonly installed as compilers for C
-    or C++, and non-Haskellers will probably find Penny to be more
-    difficult to install than Ledger, as the latter is written in C++.
-
-  .
-
-  * Handling commodities requires that you set up multiple accounts;
-    some might find this cumbersome.
-
-  .
-
-  * Young and not well tested yet.
-
-  .
-
-  * Runs only on Unix-like operating systems.
-
-  .
-
-  * Full Penny functionality is available without a Haskell compiler;
-    you could even use a pre-compiled binary.  However, Penny does not
-    read configuration files at runtime; instead, to change the
-    default settings, you will need to have GHC installed so that you
-    can compile a custom binary.
-
-  .
-
-  * Can be slow and memory hungry with large data sets. I have a
-    ledger file with about 28,000 lines. On my least capable machine
-    (which has an Intel Core 2 Duo at 1.6 GHz) this takes about 1.4
-    seconds to parse. Not horrible but not instantaneous
-    either. Generating a report about all these transactions can take
-    about seven seconds and a little less than 300 MB of memory. I
-    have eliminated all the obvious slowness from the code and
-    attempted a rewrite of the parser, which made no difference; other
-    ideas to speed up Penny with large data sets would involve
-    substantial changes and this is not at the top of my list because
-    the program is currently usable with relatively recent hardware.
-
-  .
+synopsis: Deprecated - use penny package instead
 
-  You can install this binary simply by typing "cabal install
-  penny-bin", which will install this binary along with all the
-  necessary dependencies. The penny-bin package also has all
-  documentation. Unfortunately running "cabal install" will not
-  install the documentation, so you will need to find the downloaded
-  archive (usually in
-  "$HOME/.cabal/packages/hackage.haskell.org/penny-bin") and unpack it
-  to see the documentation. You will want to start by reading the
-  README file, which will point you to additional documentation and
-  how to install it if you wish.
+description:
+  This package is now deprecated.  Use the penny package instead.
 
 extra-source-files:
     install-docs
@@ -186,7 +34,7 @@
 Executable penny
   Build-depends:
     base ==4.*,
-    penny-lib ==0.20.*
+    penny-lib ==0.22.*
 
   Main-is: penny-main.hs
   Other-modules: Paths_penny_bin
@@ -200,13 +48,13 @@
 Executable penny-selloff
   Build-depends:
     base == 4.*,
-    penny-lib ==0.20.*,
+    penny-lib ==0.22.*,
     explicit-exception ==0.1.*,
     containers ==0.5.*,
     semigroups ==0.9.*,
     text ==0.11.*,
     parsec ==3.1.*,
-    multiarg ==0.16.*,
+    multiarg ==0.18.*,
     transformers ==0.3.*
 
   Main-is: penny-selloff.hs
@@ -220,9 +68,9 @@
 Executable penny-diff
   Build-depends:
     base ==4.*,
-    penny-lib ==0.20.*,
+    penny-lib ==0.22.*,
     text ==0.11.*,
-    multiarg ==0.16.*,
+    multiarg ==0.18.*,
     explicit-exception == 0.1.*
 
   Main-is: penny-diff.hs
@@ -237,8 +85,8 @@
 Executable penny-reprint
   Build-depends:
       base ==4.*
-    , multiarg ==0.16.*
-    , penny-lib ==0.20.*
+    , multiarg ==0.18.*
+    , penny-lib ==0.22.*
     , pretty-show ==1.5.*
     , text ==0.11.*
 
@@ -251,9 +99,9 @@
 Executable penny-reconcile
   Build-depends:
       base ==4.*
-    , penny-lib ==0.20.*
+    , penny-lib ==0.22.*
     , text ==0.11.*
-    , multiarg ==0.16.*
+    , multiarg ==0.18.*
     , explicit-exception ==0.1.*
 
   main-is: penny-reconcile.hs
diff --git a/penny-diff.hs b/penny-diff.hs
--- a/penny-diff.hs
+++ b/penny-diff.hs
@@ -1,7 +1,6 @@
 module Main where
 
 import Control.Arrow (first, second)
-import Data.Either (partitionEithers)
 import Data.Maybe (fromJust)
 import Data.List (deleteFirstsBy)
 import qualified System.Console.MultiArg as M
@@ -19,11 +18,8 @@
 
 import qualified Paths_penny_bin as PPB
 
-groupingSpecs :: CR.GroupSpecs
-groupingSpecs = CR.GroupSpecs CR.NoGrouping CR.NoGrouping
-
 main :: IO ()
-main = runPennyDiff groupingSpecs
+main = runPennyDiff
 
 help :: String -> String
 help pn = unlines
@@ -49,11 +45,8 @@
 optFile2 :: M.OptSpec Args
 optFile2 = M.OptSpec [] "2" (M.NoArg (ArgFile File2))
 
-allOpts :: [M.OptSpec (Either (IO ()) Args)]
-allOpts = [ fmap Right optFile1
-          , fmap Right optFile2
-          , fmap Left (Ly.version PPB.version)
-          ]
+allOpts :: [M.OptSpec Args]
+allOpts = [ optFile1 , optFile2 ]
 
 data File = File1 | File2
   deriving (Eq, Show)
@@ -94,11 +87,10 @@
 -- the line number for the top line for that; otherwise, shows the
 -- line number for the TopLine.
 renderTransaction
-  :: CR.GroupSpecs
-  -> File
+  :: File
   -> L.Transaction
   -> Maybe X.Text
-renderTransaction gs f t = fmap addHeader $ CR.transaction gs (noMeta t)
+renderTransaction f t = fmap addHeader $ CR.transaction Nothing (noMeta t)
   where
     lin = case L.tMemo . L.tlCore . fst . L.unTransaction $ t of
       Nothing -> L.unTopLineLine . L.tTopLineLine . fromJust
@@ -109,56 +101,60 @@
     noMeta txn = let (tl, es) = L.unTransaction txn
                  in (L.tlCore tl, fmap L.pdCore es)
 
-renderPrice :: CR.GroupSpecs -> File -> L.PricePoint -> Maybe X.Text
-renderPrice gs f p = fmap addHeader $ CR.price gs p
+renderPrice :: File -> L.PricePoint -> Maybe X.Text
+renderPrice f p = fmap addHeader $ CR.price p
   where
     lin = L.unPriceLine . fromJust . L.priceLine $ p
     addHeader x = (showLineNum f lin) `X.append` x
 
-renderNonBlankItem :: CR.GroupSpecs -> File -> NonBlankItem -> Maybe X.Text
-renderNonBlankItem gs f =
-  S.caseS3 (renderTransaction gs f) (renderPrice gs f) CR.comment
+renderNonBlankItem
+  :: File
+  -> NonBlankItem
+  -> Maybe X.Text
+renderNonBlankItem f =
+  S.caseS3 (renderTransaction f) (renderPrice f) CR.comment
 
-runPennyDiff :: CR.GroupSpecs -> IO ()
-runPennyDiff co = do
+runPennyDiff :: IO ()
+runPennyDiff = do
   (f1, f2, dts) <- parseCommandLine
   l1 <- C.open [f1]
   l2 <- C.open [f2]
   let (r1, r2) = doDiffs l1 l2
-  showDiffs co dts (r1, r2)
+  showDiffs dts (r1, r2)
   case (r1, r2) of
     ([], []) -> E.exitSuccess
     _ -> E.exitWith (E.ExitFailure 1)
 
 showDiffs
-  :: CR.GroupSpecs
-  -> DiffsToShow
+  :: DiffsToShow
   -> ([NonBlankItem], [NonBlankItem])
   -> IO ()
-showDiffs co dts (l1, l2) =
+showDiffs dts (l1, l2) =
   case dts of
     File1Only -> showFile1
     File2Only -> showFile2
     BothFiles -> showFile1 >> showFile2
   where
-    showFile1 = showNonBlankItems co File1 l1
-    showFile2 = showNonBlankItems co File2 l2
+    showFile1 = showNonBlankItems File1 l1
+    showFile2 = showNonBlankItems File2 l2
 
 failure :: String -> IO a
 failure s = IO.hPutStrLn IO.stderr s
   >> E.exitWith (E.ExitFailure 2)
 
 showNonBlankItems
-  :: CR.GroupSpecs
-  -> File
+  :: File
   -> [NonBlankItem]
   -> IO ()
-showNonBlankItems o f ls =
-  mapM_ (showNonBlankItem o f) ls
+showNonBlankItems f ls =
+  mapM_ (showNonBlankItem f) ls
 
-showNonBlankItem :: CR.GroupSpecs -> File -> NonBlankItem -> IO ()
-showNonBlankItem co f nbi = maybe e TIO.putStr
-  (renderNonBlankItem co f nbi)
+showNonBlankItem
+  :: File
+  -> NonBlankItem
+  -> IO ()
+showNonBlankItem f nbi = maybe e TIO.putStr
+  (renderNonBlankItem f nbi)
   where
     e = failure $ "could not render item: " ++ show nbi
 
@@ -181,22 +177,19 @@
 -- an indication of which differences to show.
 parseCommandLine :: IO (String, String, DiffsToShow)
 parseCommandLine = do
-  as <- M.simpleWithHelp help M.Intersperse allOpts
-        (return . Right . Filename)
-  let (doVer, args) = partitionEithers as
-      toFilename a = case a of
+  as <- M.simpleHelpVersion help (Ly.version PPB.version)
+        allOpts M.Intersperse
+        (return . Filename)
+  let toFilename a = case a of
         Filename s -> Just s
         _ -> Nothing
-  case doVer of
-    [] -> return ()
-    x:_ -> x
-  (fn1, fn2) <- case mapMaybe toFilename args of
+  (fn1, fn2) <- case mapMaybe toFilename as of
     x:y:[] -> return (x, y)
     _ -> failure "penny-diff: error: you must supply two filenames."
   let getDiffs
-        | ((ArgFile File1) `elem` args)
-          && ((ArgFile File2) `elem` args) = BothFiles
-        | ((ArgFile File1) `elem` args) = File1Only
-        | ((ArgFile File2) `elem` args) = File2Only
+        | ((ArgFile File1) `elem` as)
+          && ((ArgFile File2) `elem` as) = BothFiles
+        | ((ArgFile File1) `elem` as) = File1Only
+        | ((ArgFile File2) `elem` as) = File2Only
         | otherwise = BothFiles
   return (fn1, fn2, getDiffs)
diff --git a/penny-main.hs b/penny-main.hs
--- a/penny-main.hs
+++ b/penny-main.hs
@@ -46,10 +46,10 @@
     -- If this list is empty, then by default postings are left in the
     -- same order as they appear in the ledger files.
 
-  , balanceFormat = const defaultQtyFormat
-    -- ^ How to format balances in the balance report. Change this
-    -- function if, for example, you want to allow for digit
-    -- grouping. The default function does not perform digit grouping.
+  , formatQty = qtyFormatter $ S3a Period
+    -- ^ How to format quantities. This affects only quantities that
+    -- are not parsed from the ledger.  Examples include calculated
+    -- totals and inferred quantities.  Affects all reports.
 
   , balanceShowZeroBalances = False
     -- ^ Show zero balances in the balance report? If True, show them;
@@ -86,11 +86,6 @@
     -- SortByQty
     -- SortByName
 
-  , convertFormat = const defaultQtyFormat
-    -- ^ How to format balances in the convert report. For instance,
-    -- this function might perform digit grouping. The default
-    -- function does not perform any digit grouping.
-
   , postingsFields = fields
     -- ^ Fields to show by default in the postings report.
 
@@ -105,16 +100,6 @@
 
   , postingsDateFormat = yearMonthDay
     -- ^ How to format dates in the postings report.
-
-  , postingsQtyFormat = qtyAsIs
-    -- ^ How to format quantities in the balance report. This function
-    -- is used when showing the quantity for the posting itself, and
-    -- not the quantity for the totals columns (for that, see
-    -- postingsBalanceFormat.) For example this function might perform
-    -- digit grouping.
-
-  , postingsBalanceFormat = balanceAsIs
-    -- ^ How to format balance totals in the postings report.
 
   , postingsSubAccountLength = 2
     -- ^ Account names in the postings report are shortened if
diff --git a/penny-reconcile.hs b/penny-reconcile.hs
--- a/penny-reconcile.hs
+++ b/penny-reconcile.hs
@@ -1,5 +1,6 @@
 module Main where
 
+import Data.Either (partitionEithers)
 import Data.Maybe (fromMaybe, fromJust)
 import qualified Data.Text as X
 import Control.Monad (guard)
@@ -56,27 +57,20 @@
   , "  --version  - Show version and exit"
   ]
 
-groupSpecs :: C.GroupSpecs
-groupSpecs = C.GroupSpecs C.NoGrouping C.NoGrouping
-
-type ShowVer = IO ()
 type Printer = X.Text -> IO ()
 type PosArg = String
-type Arg = S.S3 ShowVer Printer PosArg
+type Arg = Either Printer PosArg
 
 allOpts :: [MA.OptSpec Arg]
-allOpts =
-  [ fmap S.S3a $ Ly.version PPB.version
-  , fmap S.S3b Ly.output
-  ]
+allOpts = [ fmap Left Ly.output ]
 
 main :: IO ()
 main = do
-  as <- MA.simpleWithHelp help MA.Intersperse allOpts (fmap return S.S3c)
-  let (showVer, printers, posArgs) = S.partitionS3 as
-  sequence_ showVer
+  as <- MA.simpleHelpVersion help (Ly.version PPB.version)
+        allOpts MA.Intersperse (return . Right)
+  let (printers, posArgs) = partitionEithers as
   led <- C.open posArgs
   let led' = map (S.mapS4 changeTransaction id id id) led
-      rend = fromJust $ mapM (C.item groupSpecs) (map C.stripMeta led')
+      rend = fromJust $ mapM (C.item Nothing) (map C.stripMeta led')
   let txt = X.concat rend in txt `seq` (Ly.processOutput printers txt)
 
diff --git a/penny-reprint.hs b/penny-reprint.hs
--- a/penny-reprint.hs
+++ b/penny-reprint.hs
@@ -1,11 +1,11 @@
 module Main where
 
+import Data.Either (partitionEithers)
 import qualified Penny.Copper as C
 import qualified Penny.Copper.Render as R
 import qualified Penny.Liberty as Ly
 import qualified Data.Text as X
 import qualified System.Console.MultiArg as MA
-import qualified Penny.Steel.Sums as S
 
 import qualified Paths_penny_bin as PPB
 
@@ -30,28 +30,22 @@
   , "  --version  - show version and exit"
   ]
 
-groupSpecs :: R.GroupSpecs
-groupSpecs = R.GroupSpecs R.NoGrouping R.NoGrouping
-
-type MaybeShowVer = IO ()
 type Printer = X.Text -> IO ()
 type PosArg = String
 
-type Arg = S.S3 MaybeShowVer Printer PosArg
+type Arg = Either Printer PosArg
 
 allOpts :: [MA.OptSpec Arg]
-allOpts =
-  [ fmap S.S3a $ Ly.version PPB.version
-  , fmap S.S3b $ Ly.output
-  ]
+allOpts = [ fmap Left Ly.output ]
 
 main :: IO ()
 main = do
-  as <- MA.simpleWithHelp help MA.Intersperse allOpts (fmap return S.S3c)
-  let (showVers, printers, posArgs) = S.partitionS3 as
-  sequence_ showVers
+  as <- MA.simpleHelpVersion help (Ly.version PPB.version)
+        allOpts MA.Intersperse
+        (return . Right)
+  let (printers, posArgs) = partitionEithers as
   l <- C.open posArgs
-  case mapM (R.item groupSpecs) (map C.stripMeta l) of
+  case mapM (R.item Nothing) (map C.stripMeta l) of
     Nothing -> error "could not render final ledger."
     Just x ->
       let txt = X.concat x
diff --git a/penny-selloff.hs b/penny-selloff.hs
--- a/penny-selloff.hs
+++ b/penny-selloff.hs
@@ -117,9 +117,7 @@
 import qualified System.Console.MultiArg as MA
 import qualified Text.Parsec as Parsec
 import qualified Paths_penny_bin as PPB
-
-groupingSpec :: CR.GroupSpecs
-groupingSpec = CR.GroupSpecs CR.NoGrouping CR.NoGrouping
+import qualified Penny as P
 
 type Err = Ex.Exceptional Error
 
@@ -146,24 +144,13 @@
 newtype InputFilename = InputFilename { _unInputFilename :: String }
   deriving (Eq, Show)
 
-type MaybeShowVer = IO ()
-type Opts = (MaybeShowVer, [String])
-
-posArg :: String -> Opts -> Opts
-posArg s (a, ss) = (a, s:ss)
-
-allOpts :: [MA.OptSpec (Opts -> Opts)]
-allOpts = [ fmap (\a (_, ss) -> (a, ss)) $ Ly.version PPB.version ]
-
 data ParseResult = ParseResult ProceedsAcct [Cop.LedgerItem]
 
 parseCommandLine :: IO ParseResult
 parseCommandLine = do
-  as <- MA.simpleWithHelp help MA.Intersperse allOpts
-        (fmap return posArg)
-  let opts = foldr ($) (return (), []) as
-  fst opts
-  x:xs <- case snd opts of
+  as <- MA.simpleHelpVersion help (Ly.version PPB.version)
+        [] MA.Intersperse return
+  x:xs <- case as of
     [] -> fail (show NoInputArgs)
     r -> return r
   a <- Ex.switch (fail . show . ProceedsParseFailed) return
@@ -197,11 +184,11 @@
 newtype SaleDate = SaleDate { unSaleDate :: L.DateTime }
   deriving (Show, Eq)
 
-newtype SelloffStock = SelloffStock { unSelloffStock :: L.Amount }
+newtype SelloffStock = SelloffStock { unSelloffStock :: L.Amount L.Qty }
   deriving (Show, Eq)
 
 newtype SelloffCurrency
-  = SelloffCurrency { unSelloffCurrency :: L.Amount }
+  = SelloffCurrency { unSelloffCurrency :: L.Amount L.Qty }
   deriving (Show, Eq)
 
 data SelloffInfo = SelloffInfo
@@ -481,7 +468,7 @@
   :: SelloffInfo
   -> PurchaseDate
   -> BasisRealiztn
-  -> ((L.Entry, L.PostingData), (L.Entry, L.PostingData))
+  -> ((L.Entry L.Qty, L.PostingData), (L.Entry L.Qty, L.PostingData))
 basisOffsets s pd p = (po enDr, po enCr)
   where
     ac = L.Account [basis, grp, dt]
@@ -538,7 +525,7 @@
   :: GainOrLoss
   -> SelloffCurrency
   -> CapitalChange
-  -> L.Entry
+  -> L.Entry L.Qty
 capChangeEntry gl sc cc = L.Entry dc (L.Amount qt cy)
   where
     dc = case gl of
@@ -552,7 +539,7 @@
   -> GainOrLoss
   -> CapitalChange
   -> PurchaseInfo
-  -> (L.Entry, L.PostingData)
+  -> (L.Entry L.Qty, L.PostingData)
 capChangePstg si gl cc p = (en, emptyPostingData ac)
   where
     ac = capChangeAcct gl si p
@@ -563,7 +550,7 @@
 
 proceedsPstgs
   :: SelloffInfo
-  -> ((L.Entry, L.PostingData), (L.Entry, L.PostingData))
+  -> ((L.Entry L.Qty, L.PostingData), (L.Entry L.Qty, L.PostingData))
 proceedsPstgs si = (po dr, po cr)
   where
     po en = (en, emptyPostingData ac)
@@ -597,7 +584,7 @@
             where
               (b1, b2) = basisOffsets si (piDate p) br
               c = capChangePstg si gl cc p
-    entInputs = map (first Just) (p1:p2:ps)
+    entInputs = map (first (Just . Right)) (p1:p2:ps)
 
 makeOutput
   :: ProceedsAcct
@@ -605,6 +592,7 @@
   -> Err X.Text
 makeOutput pa ldgr = do
   let bals = calcBalances ldgr
+      formatter = P.getQtyFormat defaultRadGroup ldgr
   si <- selloffInfo pa bals
   let basisAccts = findBasisAccounts (siGroup si) bals
   purchInfos <- mapM (purchaseInfo (siStock si) (siCurrency si))
@@ -614,7 +602,7 @@
   return
     . (`X.snoc` '\n')
     . fromMaybe (error "makeOutput: transaction did not render")
-    . CR.transaction groupingSpec
+    . (CR.transaction (Just formatter))
     . (\t -> let (tl, es) = L.unTransaction t
              in (L.tlCore tl, fmap L.pdCore es))
     . mkTxn si
@@ -628,3 +616,6 @@
 handleParseResult :: ParseResult -> IO ()
 handleParseResult (ParseResult pa ldgr) =
   Ex.switch (error . show) TIO.putStr . makeOutput pa $ ldgr
+
+defaultRadGroup :: S.S3 L.Radix L.PeriodGrp L.CommaGrp
+defaultRadGroup = S.S3a L.Period
