diff --git a/Ledger/Balance.hs b/Ledger/Balance.hs
--- a/Ledger/Balance.hs
+++ b/Ledger/Balance.hs
@@ -20,10 +20,12 @@
     , insert
     , delete
     , balanceStore
+    , BalanceError(..)
     ) where
 
 import           Control.Applicative
 import "comonad" Control.Comonad.Trans.Store
+import           Control.Exception
 import           Control.Lens hiding (from, to)
 import qualified Control.Lens.Internal as Lens
 import           Control.Monad hiding (forM)
@@ -34,6 +36,7 @@
 import qualified Data.IntMap as IntMap
 import qualified Data.Key as K
 import           Data.Semigroup
+import           Data.Text (Text)
 import           Data.Traversable
 import           Ledger.Commodity
 import           Linear.Vector
@@ -52,8 +55,8 @@
 
 makePrisms ''Balance
 
-non' :: a -> Iso' (Maybe a) a
-non' = flip anon (const False)
+non'' :: a -> Iso' (Maybe a) a
+non'' = flip anon (const False)
 
 -- instance Num a => Num (Balance a) where
 --     x + y = x ^+^ y
@@ -87,8 +90,8 @@
     Amount cx qx ^+^ Amount cy qy
         | cx == cy  = Amount cx (qx + qy)
         | otherwise = Balance $ IntMap.fromList [(cx,qx), (cy,qy)]
-    Amount cx qx ^+^ Balance ys = Balance $ ys & at cx.non' 0 +~ qx
-    Balance xs ^+^ Amount cy qy = Balance $ xs & at cy.non' 0 +~ qy
+    Amount cx qx ^+^ Balance ys = Balance $ ys & at cx.non'' 0 +~ qx
+    Balance xs ^+^ Amount cy qy = Balance $ xs & at cy.non'' 0 +~ qy
 
     Balance xs ^+^ Balance ys = Balance $ xs ^+^ ys
     {-# INLINE (^+^) #-}
@@ -198,34 +201,34 @@
 
 type instance Index (Balance a) = Int
 type instance IxValue (Balance a) = a
-instance Applicative f => Ixed f (Balance a) where
-    ix _k _f Zero = pure Zero
-    ix _k _f pl@(Plain _) = pure pl
-    ix k f amt@(Amount c q)
-        | k == c    = Amount c <$> (Lens.indexed f k q <&> id)
-        | otherwise = pure amt
-    ix k f bal@(Balance xs) = case IntMap.lookup k xs of
-        Just v  -> Balance
-            <$> (Lens.indexed f k v <&> \v' -> IntMap.insert k v' xs)
-        Nothing -> pure bal
+-- instance Applicative f => Ixed f (Balance a) where
+--     ix _k _f Zero = pure Zero
+--     ix _k _f pl@(Plain _) = pure pl
+--     ix k f amt@(Amount c q)
+--         | k == c    = Amount c <$> (Lens.indexed f k q <&> id)
+--         | otherwise = pure amt
+--     ix k f bal@(Balance xs) = case IntMap.lookup k xs of
+--         Just v  -> Balance
+--             <$> (Lens.indexed f k v <&> \v' -> IntMap.insert k v' xs)
+--         Nothing -> pure bal
 
-instance At (Balance a) where
-    at k f m = Lens.indexed f k mv <&> \r -> case r of
-        Nothing -> maybe m (const (delete k m)) mv
-        Just v' -> insert k v' m
-      where mv = K.lookup k m
+-- instance At (Balance a) where
+--     at k f m = Lens.indexed f k mv <&> \r -> case r of
+--         Nothing -> maybe m (const (delete k m)) mv
+--         Just v' -> insert k v' m
+--       where mv = K.lookup k m
 
-instance (Contravariant f, Functor f) => Contains f (Balance a) where
-  contains = containsLookup K.lookup
-  {-# INLINE contains #-}
+-- instance (Contravariant f, Functor f) => Contains f (Balance a) where
+--   contains = containsLookup K.lookup
+--   {-# INLINE contains #-}
 
-instance Applicative f => Each f (Balance a) (Balance b) a b where
-  each _ Zero = pure Zero
-  each f (Plain q) = Plain <$> Lens.indexed f noCommodity q
-  each f (Amount c q) = Amount c <$> Lens.indexed f c q
-  each f (Balance m) = sequenceA $ Balance $ IntMap.mapWithKey f' m
-    where f' = Lens.indexed f
-  {-# INLINE each #-}
+-- instance Applicative f => Each f (Balance a) (Balance b) a b where
+--   each _ Zero = pure Zero
+--   each f (Plain q) = Plain <$> Lens.indexed f noCommodity q
+--   each f (Amount c q) = Amount c <$> Lens.indexed f c q
+--   each f (Balance m) = sequenceA $ Balance $ IntMap.mapWithKey f' m
+--     where f' = Lens.indexed f
+--   {-# INLINE each #-}
 
 instance FunctorWithIndex Int Balance where
   imap = iover itraversed
@@ -301,3 +304,8 @@
 
 balanceSum :: Num a => [Balance a] -> Balance a
 balanceSum = Foldable.foldr (^+^) Zero
+
+data BalanceError = BalanceParseError Text
+    deriving (Show, Typeable)
+
+instance Exception BalanceError
diff --git a/Ledger/Commodity.hs b/Ledger/Commodity.hs
--- a/Ledger/Commodity.hs
+++ b/Ledger/Commodity.hs
@@ -10,7 +10,7 @@
 module Ledger.Commodity
        ( Commodity
        , CommodityInfo(..), HasCommodityInfo(..)
-       , defaultCommodityInfo
+       , defaultCommodityInfo, defaultPrimaryCommodityInfo
        , CommodityMap(..), HasCommodityMap(..)
        , extendByDigits
        ) where
@@ -49,6 +49,24 @@
 
 makeClassy ''CommodityInfo
 
+instance Semigroup CommodityInfo where
+    x <> y = x
+        & commSymbol       .~ y^.commSymbol
+        & commPrecision    .~ max (x^.commPrecision) (y^.commPrecision)
+        & commSuffixed     .~ (x^.commSuffixed     || y^.commSuffixed)
+        & commSeparated    .~ (x^.commSeparated    || y^.commSeparated)
+        & commThousands    .~ (x^.commThousands    || y^.commThousands)
+        & commDecimalComma .~ (x^.commDecimalComma || y^.commDecimalComma)
+        & commNoMarket     .~ (x^.commNoMarket     || y^.commNoMarket)
+        & commBuiltin      .~ (x^.commBuiltin      || y^.commBuiltin)
+        & commKnown        .~ (x^.commKnown        || y^.commKnown)
+        & commPrimary      .~ (x^.commPrimary      || y^.commPrimary)
+        & commHistory      .~ (x^.commHistory <> y^.commHistory)
+
+instance Monoid CommodityInfo where
+    mempty = defaultCommodityInfo
+    x `mappend` y = x <> y
+
 -- | Return a 'CommodityInfo' with defaults selected for all fields.  It is
 --   intended that at least one field of the result will be modified
 --   immediately.
@@ -57,7 +75,7 @@
     { _commSymbol       = ""
     , _commPrecision    = 0
     , _commSuffixed     = False
-    , _commSeparated    = False
+    , _commSeparated    = True
     , _commThousands    = True
     , _commDecimalComma = False
     , _commNoMarket     = False
@@ -67,6 +85,14 @@
     , _commHistory      = IntMap.empty
     }
 
+defaultPrimaryCommodityInfo :: Text -> CommodityInfo
+defaultPrimaryCommodityInfo sym = defaultCommodityInfo
+    & commSymbol    .~ sym
+    & commPrecision .~ 2
+    & commNoMarket  .~ True
+    & commKnown     .~ True
+    & commPrimary   .~ True
+
 -- | A commodities map, relating commodity indices to information about
 --   those commodities.
 data CommodityMap = CommodityMap
@@ -77,7 +103,8 @@
 makeClassy ''CommodityMap
 
 instance Semigroup CommodityMap where
-    CommodityMap x <> CommodityMap y = CommodityMap (x <> y)
+    CommodityMap x <> CommodityMap y =
+        CommodityMap (IntMap.unionWith (<>) x y)
 
 instance Monoid CommodityMap where
     mempty = CommodityMap mempty
diff --git a/Ledger/Commodity/History.hs b/Ledger/Commodity/History.hs
--- a/Ledger/Commodity/History.hs
+++ b/Ledger/Commodity/History.hs
@@ -6,7 +6,6 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
-{-# LANGUAGE ViewPatterns #-}
 
 module Ledger.Commodity.History
        ( findConversion
@@ -165,9 +164,9 @@
 
     h _goal = return 0
 
-    go vm ks = (\(!x, !y, _) -> (x, y)) $ foldl' h (time, 1, f) ks
+    go vm ks = (\(!x, !y, _) -> (x, y)) $ foldl' o (time, 1, f) ks
       where
-        h (!w, !r, !s) u = let (w', r') = vm IntMap.! s IntMap.! u
+        o (!w, !r, !s) u = let (w', r') = vm IntMap.! s IntMap.! u
                            in (min w w', r / r', u)
 
 -- | Add a price conversion in the form of a ratio between two commodities at
@@ -178,9 +177,9 @@
     commodities.at t %= fmap (addconv (1/ratio) ?? f)
     commodities.at f %= fmap (addconv ratio ?? t)
   where
-    addconv r s t =
+    addconv r s t' =
         let c  = s^.commHistory
-            rm = case IntMap.lookup t c of
+            rm = case IntMap.lookup t' c of
                 Nothing -> Map.singleton time r
                 Just m  -> Map.insert time r m
-        in s & commHistory .~ IntMap.insert t rm c
+        in s & commHistory .~ IntMap.insert t' rm c
diff --git a/Ledger/Commodity/Parse.hs b/Ledger/Commodity/Parse.hs
--- a/Ledger/Commodity/Parse.hs
+++ b/Ledger/Commodity/Parse.hs
@@ -1,12 +1,34 @@
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE PackageImports #-}
+{-# LANGUAGE TypeFamilies #-}
 
 module Ledger.Commodity.Parse where
 
-import "mtl" Control.Monad.State.Class
-import       Data.Text.Lazy
-import       Ledger.Balance
-import       Ledger.Commodity
+import           Control.Applicative
+import           Control.Lens
+import "mtl"     Control.Monad.State.Class
+import qualified Data.IntMap.Strict as IntMap
+import           Data.Semigroup
+import qualified Data.Text as T
+import           Data.Text.Lazy (Text, unpack)
+--import qualified Data.Text.Lazy as TL
+import           Ledger.Balance
+import           Ledger.Commodity
+--import           Text.Parser.Char
+--import           Text.Parser.Combinators
+import           Text.Trifecta.Parser
+import           Text.Trifecta.Result
 
-parseBalance :: MonadState CommodityMap m => Text -> m (Balance a)
-parseBalance _str = undefined
+parseBalance :: (MonadState CommodityMap m, Functor m, a ~ Rational)
+             => Text -> m (Either BalanceError (Balance a))
+parseBalance str =
+    case parseString balanceParser mempty (unpack str) of
+        Success (b, c) -> do
+            len <- IntMap.size <$> use commodities
+            commodities.at len .= Just c
+            return . Right $ b
+        Failure e ->
+            return . Left $ BalanceParseError (T.pack (show e))
+
+balanceParser :: a ~ Rational => Parser (Balance a, CommodityInfo)
+balanceParser = undefined
diff --git a/Ledger/Commodity/Print.hs b/Ledger/Commodity/Print.hs
--- a/Ledger/Commodity/Print.hs
+++ b/Ledger/Commodity/Print.hs
@@ -2,6 +2,8 @@
 {-# LANGUAGE ImpredicativeTypes #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE PackageImports #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
 
 module Ledger.Commodity.Print
     ( printBalance
@@ -10,41 +12,79 @@
 
 import           Control.Applicative
 import           Control.Lens
+import           Control.Monad
 import "mtl"     Control.Monad.Reader.Class
 import           Control.Monad.Trans.Reader (runReader)
 import           Control.Monad.Trans.State (evalState)
 import           Control.Monad.Trans.Writer
 import qualified Data.IntMap.Strict as IntMap
+import           Data.List
+import           Data.List.Split
 import           Data.Maybe (fromMaybe)
-import           Data.Text.Lazy
+import           Data.Number.CReal
+import           Data.Text.Lazy (Text, pack)
+--import qualified Data.Text.Lazy as TL
 import           Data.Text.Lazy.Builder
 import           Ledger.Balance
 import           Ledger.Commodity
 import           Ledger.Commodity.Parse
 
-printBalance :: (MonadReader CommodityMap m, Functor m, Show a)
+printBalance :: (MonadReader CommodityMap m, Functor m, a ~ Rational)
              => Balance a
              -> m Text
-printBalance Zero = return "0"
+printBalance Zero      = return "0"
 printBalance (Plain x) = return $ pack (show x)
-printBalance x = toLazyText <$> execWriterT (buildBalance x)
+printBalance x         = toLazyText <$> execWriterT (buildBalance x)
 
-buildBalance :: (MonadReader CommodityMap m, Show a)
+buildBalance :: (MonadReader CommodityMap m, Functor m, a ~ Rational)
              => Balance a
              -> WriterT Builder m ()
 buildBalance (Amount c q) = do
-    mcomm <- view (commodities.at c)
-    let comm = fromMaybe defaultCommodityInfo mcomm
-    tell $ fromText (comm^.commSymbol)
-    tell $ fromString (show q)
+    cm <- fromMaybe defaultCommodityInfo <$> view (commodities.at c)
 
+    unless (cm^.commSuffixed) $ do
+        outputSymbol cm
+        when (cm^.commSeparated) $
+            tell $ fromLazyText " "
+
+    tell $ fromString (formatAmount cm)
+
+    when (cm^.commSuffixed) $ do
+        when (cm^.commSeparated) $
+            tell $ fromLazyText " "
+        outputSymbol cm
+  where
+    outputSymbol cm = tell $ fromText (cm^.commSymbol)
+
+    formatAmount cm =
+        let prec   = cm^.commPrecision
+            str    = showCReal prec (fromRational q)
+            (n, m) = case break (== '.') str of
+                (xs, '.':ys) -> (xs, ys)
+                (xs, ys)     -> (xs, ys)
+            len    = length m
+            (com, per) = if cm^.commDecimalComma
+                         then (".", ",")
+                         else (",", ".")
+            n' = if cm^.commThousands
+                 then reverse . intercalate com . chunksOf 3 . reverse $ n
+                 else n
+            m' = if len < prec
+                 then m ++ replicate (prec - len) '0'
+                 else m
+        in intercalate per [n', m']
+
 buildBalance (Balance xs) =
     mapM_ (buildBalance . uncurry Amount) $ IntMap.toList xs
 
 buildBalance _ = return ()
 
-balance :: Show a => CommodityMap -> Iso' (Balance a) Text
-balance pool = iso toBalance fromBalance
+balance :: a ~ Rational => CommodityMap -> Iso' (Balance a) Text
+balance pool = iso fromBalance toBalance
   where
-    toBalance   = flip runReader pool . printBalance
-    fromBalance = flip evalState pool . parseBalance
+    toBalance str = flip evalState pool $ do
+        eb <- parseBalance str
+        return $ case eb of
+            Left (_ :: BalanceError) -> Zero
+            Right b -> b
+    fromBalance = flip runReader pool . printBalance
diff --git a/Setup.hs b/Setup.hs
--- a/Setup.hs
+++ b/Setup.hs
diff --git a/commodities.cabal b/commodities.cabal
--- a/commodities.cabal
+++ b/commodities.cabal
@@ -1,5 +1,5 @@
 Name:          commodities
-Version:       0.2.0
+Version:       0.2.0.1
 Synopsis:      Library for working with commoditized amounts and price histories
 Description:   Library for working with commoditized amounts and price histories
 License-file:  LICENSE
@@ -25,20 +25,24 @@
         Ledger.Commodity.Print
     build-depends:
         base                   >= 3 && < 5
-      , PSQueue
+      , PSQueue                >= 1.1
       , comonad                >= 4.0
-      , containers             >= 0.4
-      , distributive           >= 0.3
-      , keys                   >= 3.0.2
+      , containers             >= 0.5.0.0
+      , distributive           >= 0.3.2
+      , failure                >= 0.2
+      , keys                   >= 3.10
       , lens                   >= 3.10
-      , linear                 >= 0.7
-      , mtl
-      , numbers                >= 2009.8.9
-      , semigroups
+      , linear                 >= 1.3.1
+      , mtl                    >= 2.1.2
+      , numbers                >= 3000.2.0.0
+      , parsers                >= 0.10.1.1
+      , semigroups             >= 0.12
       , semigroupoids          >= 4.0
-      , text                   >= 0.11.2
-      , thyme
+      , split                  >= 0.2.2
+      , text                   >= 0.11.3.1
+      , thyme                  >= 0.3.1.0
       , transformers
+      , trifecta               >= 1.2.1.1
 
 test-suite doctests
     default-language: Haskell98
@@ -48,9 +52,9 @@
     hs-source-dirs:   test
     build-depends:
         base      >= 3   && < 5
-      , directory >= 1.0 && < 1.3
-      , doctest   >= 0.8 && <= 0.10
-      , filepath  >= 1.3 && < 1.4
+      , directory >= 1.0 && < 1.4
+      , doctest   >= 0.8 && < 0.12
+      , filepath  >= 1.3 && < 1.5
 
 Test-suite test
     default-language: Haskell98
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -52,17 +52,17 @@
     let oneHourAgo = addUTCTime (-3600) now
         oneDayAgo  = addUTCTime (-(24 * 3600)) now
 
-        usd = (defaultPrimary "USD")
+        usd = (defaultPrimaryCommodityInfo "USD")
             & commHistory .~
                    IntMap.fromList [ (2, Map.fromList [(oneHourAgo, 0.75)])
                                    , (3, Map.fromList [(oneDayAgo, 0.66)])
                                    ]
-        cad = (defaultPrimary "CAD")
+        cad = (defaultPrimaryCommodityInfo "CAD")
             & commHistory .~
                 IntMap.fromList [ (1, Map.fromList [(oneHourAgo, 1.33)])
                                 , (3, Map.fromList [(oneHourAgo, 0.83)])
                                 ]
-        eur = (defaultPrimary "EUR")
+        eur = (defaultPrimaryCommodityInfo "EUR")
             & commHistory .~
                 IntMap.fromList [ (1, Map.fromList [(oneDayAgo, 1.5)])
                                 , (2, Map.fromList [(oneHourAgo, 1.2)])
@@ -72,19 +72,12 @@
         , (2, cad)
         , (3, eur)
         ]
-  where
-    defaultPrimary sym = defaultCommodityInfo
-        & commSymbol    .~ sym
-        & commPrecision .~ 2
-        & commNoMarket  .~ True
-        & commKnown     .~ True
-        & commPrimary   .~ True
 
 testMap' :: UTCTime -> CommodityMap
 testMap' now =
-    let usd = defaultPrimary "USD"
-        cad = defaultPrimary "CAD"
-        eur = defaultPrimary "EUR"
+    let usd = defaultPrimaryCommodityInfo "USD"
+        cad = defaultPrimaryCommodityInfo "CAD"
+        eur = defaultPrimaryCommodityInfo "EUR"
 
         cm = CommodityMap $ IntMap.fromList
             [ (1, usd)
@@ -97,10 +90,3 @@
         addConversion 1 2 oneHourAgo (3 % 4)
         addConversion 1 3 oneDayAgo  (2 % 3)
         addConversion 2 3 oneHourAgo (5 % 6)
-  where
-    defaultPrimary sym = defaultCommodityInfo
-        & commSymbol    .~ sym
-        & commPrecision .~ 2
-        & commNoMarket  .~ True
-        & commKnown     .~ True
-        & commPrimary   .~ True
