parsing: infer a conversion price in unpriced two-commodity transactions
This commit is contained in:
		
							parent
							
								
									c8614b9a15
								
							
						
					
					
						commit
						33bedcbab0
					
				@ -360,20 +360,28 @@ Or, you can write `@@ TOTALPRICE`, which is sometimes more convenient:
 | 
			
		||||
     assets:cash
 | 
			
		||||
 | 
			
		||||
Or, you can set the price for this commodity as of a certain date, using a
 | 
			
		||||
historical price directive as shown here:
 | 
			
		||||
historical price directive (P) as shown:
 | 
			
		||||
 | 
			
		||||
    ; the exchange rate for euro is $1.35 on 2009/1/1 (and thereafter, until a newer price directive is found)
 | 
			
		||||
    ; four space-separated fields: P, date, commodity symbol, unit price in 2nd commodity
 | 
			
		||||
    P 2009/1/1 € $1.35  
 | 
			
		||||
    
 | 
			
		||||
    2009/1/2 x
 | 
			
		||||
    2009/1/2
 | 
			
		||||
     expenses:foreign currency       €100
 | 
			
		||||
     assets
 | 
			
		||||
 | 
			
		||||
The print command shows any prices in effect. Either example above will show:
 | 
			
		||||
Or, you can write a transaction in two commodities, without prices but
 | 
			
		||||
with all amounts specified, and a conversion price will be inferred so as
 | 
			
		||||
to balance the transaction:
 | 
			
		||||
 | 
			
		||||
    2009/1/2
 | 
			
		||||
     expenses:foreign currency       €100
 | 
			
		||||
     assets                         $-135
 | 
			
		||||
 | 
			
		||||
The print command shows any prices in effect. So the first example above gives:
 | 
			
		||||
 | 
			
		||||
    $ hledger print
 | 
			
		||||
    2009/01/02 x
 | 
			
		||||
    2009/01/02
 | 
			
		||||
        expenses:foreign currency  €100 @ $1.35
 | 
			
		||||
        assets                     €-100 @ $1.35
 | 
			
		||||
 | 
			
		||||
@ -385,8 +393,8 @@ with any command:
 | 
			
		||||
        expenses:foreign currency       $135.00
 | 
			
		||||
        assets                         $-135.00
 | 
			
		||||
 | 
			
		||||
The `--cost/-B` flag does only one lookup step, ie it will not look up the
 | 
			
		||||
price of a price's commodity.
 | 
			
		||||
In other words the `--cost/-B` flag converts amounts to their price's
 | 
			
		||||
commodity. (It will not look up the price of a price.)
 | 
			
		||||
 | 
			
		||||
Note hledger handles prices differently from c++ ledger in this respect:
 | 
			
		||||
we assume unit prices do not vary over time.  This is good for simple
 | 
			
		||||
@ -1110,8 +1118,9 @@ entries, and the following c++ ledger options and commands:
 | 
			
		||||
    it does not print multi-commodity transactions in valid journal format.)
 | 
			
		||||
 | 
			
		||||
  - hledger's default commodity directive (D) sets the commodity for
 | 
			
		||||
    subsequent commodityless amounts. ledger uses it only to set commodity
 | 
			
		||||
    display settings and for the entry command.
 | 
			
		||||
    subsequent commodityless amounts, and contributes to that commodity's
 | 
			
		||||
    display settings. ledger uses D only for commodity display settings
 | 
			
		||||
    and for the entry command.
 | 
			
		||||
 | 
			
		||||
## Troubleshooting
 | 
			
		||||
 | 
			
		||||
@ -1241,10 +1250,8 @@ Here are some issues you might encounter when you run hledger:
 | 
			
		||||
    the current list of things we know we don't parse (see also
 | 
			
		||||
    [file format compatibility](#file-format-compatibility):
 | 
			
		||||
 | 
			
		||||
    - AMOUNT1 = AMOUNT2 (balance assertion ? price specification ?)
 | 
			
		||||
    - specifying prices via postings in different commodities
 | 
			
		||||
    - comma decimal point and period thousands separator, or any number
 | 
			
		||||
      format other than the US standard
 | 
			
		||||
    - AMOUNT1 = AMOUNT2 (balance assertion/price specification ?)
 | 
			
		||||
    - ... AMOUNT {...}
 | 
			
		||||
 | 
			
		||||
## Examples and recipes
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ import Hledger.Data.Types
 | 
			
		||||
import Hledger.Data.Dates
 | 
			
		||||
import Hledger.Data.Posting
 | 
			
		||||
import Hledger.Data.Amount
 | 
			
		||||
import Hledger.Data.Commodity (dollars, dollar, unknown)
 | 
			
		||||
import Hledger.Data.Commodity
 | 
			
		||||
 | 
			
		||||
instance Show Transaction where show = showTransactionUnelided
 | 
			
		||||
 | 
			
		||||
@ -133,29 +133,87 @@ isTransactionBalanced canonicalcommoditymap t =
 | 
			
		||||
      rsum'  = canonicaliseMixedAmount canonicalcommoditymap $ costOfMixedAmount rsum
 | 
			
		||||
      bvsum' = canonicaliseMixedAmount canonicalcommoditymap $ costOfMixedAmount bvsum
 | 
			
		||||
 | 
			
		||||
-- | Ensure that this entry is balanced, possibly auto-filling a missing
 | 
			
		||||
-- amount first. We can auto-fill if there is just one non-virtual
 | 
			
		||||
-- transaction without an amount. The auto-filled balance will be
 | 
			
		||||
-- converted to cost basis if possible. If the entry can not be balanced,
 | 
			
		||||
-- return an error message instead.
 | 
			
		||||
-- | Ensure this transaction is balanced, possibly inferring a missing
 | 
			
		||||
-- amount or a conversion price first, or return an error message.
 | 
			
		||||
--
 | 
			
		||||
-- Balancing is affected by the provided commodities' display precisions.
 | 
			
		||||
--
 | 
			
		||||
-- We can infer an amount when there are multiple real postings and
 | 
			
		||||
-- exactly one of them is amountless; likewise for balanced virtual
 | 
			
		||||
-- postings. Inferred amounts are converted to cost basis when possible.
 | 
			
		||||
--
 | 
			
		||||
-- We can infer a price when all amounts were specified and the sum of
 | 
			
		||||
-- real postings' amounts is exactly two non-explicitly-priced amounts in
 | 
			
		||||
-- different commodities; likewise for balanced virtual postings.
 | 
			
		||||
balanceTransaction :: Maybe (Map.Map String Commodity) -> Transaction -> Either String Transaction
 | 
			
		||||
balanceTransaction canonicalcommoditymap t@Transaction{tpostings=ps}
 | 
			
		||||
    | length rwithoutamounts > 1 || length bvwithoutamounts > 1
 | 
			
		||||
        = Left $ printerr "could not balance this transaction (too many missing amounts)"
 | 
			
		||||
    | not $ isTransactionBalanced canonicalcommoditymap t' = Left $ printerr $ nonzerobalanceerror t'
 | 
			
		||||
    | otherwise = Right t'
 | 
			
		||||
    | not $ isTransactionBalanced canonicalcommoditymap t''' = Left $ printerr $ nonzerobalanceerror t'''
 | 
			
		||||
    | otherwise = Right t'''
 | 
			
		||||
    where
 | 
			
		||||
      rps = filter isReal ps
 | 
			
		||||
      bvps = filter isBalancedVirtual ps
 | 
			
		||||
      (rwithamounts, rwithoutamounts) = partition hasAmount rps
 | 
			
		||||
      (bvwithamounts, bvwithoutamounts) = partition hasAmount bvps
 | 
			
		||||
      t' = t{tpostings=map balance ps}
 | 
			
		||||
      -- maybe infer missing amounts
 | 
			
		||||
      (rwithamounts, rwithoutamounts)   = partition hasAmount $ realPostings t
 | 
			
		||||
      (bvwithamounts, bvwithoutamounts) = partition hasAmount $ balancedVirtualPostings t
 | 
			
		||||
      ramounts  = map pamount rwithamounts
 | 
			
		||||
      bvamounts = map pamount bvwithamounts
 | 
			
		||||
      t' = t{tpostings=map inferamount ps}
 | 
			
		||||
          where 
 | 
			
		||||
            balance p | not (hasAmount p) && isReal p
 | 
			
		||||
                          = p{pamount = (-(sum $ map pamount rwithamounts))}
 | 
			
		||||
                      | not (hasAmount p) && isBalancedVirtual p
 | 
			
		||||
                          = p{pamount = (-(sum $ map pamount bvwithamounts))}
 | 
			
		||||
                      | otherwise = p
 | 
			
		||||
            inferamount p | not (hasAmount p) && isReal p            = p{pamount = (- sum ramounts)}
 | 
			
		||||
                          | not (hasAmount p) && isBalancedVirtual p = p{pamount = (- sum bvamounts)}
 | 
			
		||||
                          | otherwise                             = p
 | 
			
		||||
 | 
			
		||||
      -- maybe infer conversion prices, for real postings
 | 
			
		||||
      rmixedamountsinorder = map pamount $ realPostings t'
 | 
			
		||||
      ramountsinorder = concatMap amounts rmixedamountsinorder
 | 
			
		||||
      rcommoditiesinorder  = map commodity ramountsinorder
 | 
			
		||||
      rsumamounts  = amounts $ sum rmixedamountsinorder
 | 
			
		||||
      -- assumption: the sum of mixed amounts is normalised (one simple amount per commodity)
 | 
			
		||||
      t'' = if length rsumamounts == 2 && all (isNothing.price) rsumamounts && t'==t
 | 
			
		||||
             then t'{tpostings=map inferprice ps}
 | 
			
		||||
             else t'
 | 
			
		||||
          where
 | 
			
		||||
            -- assumption: a posting's mixed amount contains one simple amount
 | 
			
		||||
            inferprice p@Posting{pamount=Mixed [a@Amount{commodity=c,price=Nothing}], ptype=RegularPosting}
 | 
			
		||||
                = p{pamount=Mixed [a{price=conversionprice c}]}
 | 
			
		||||
                where
 | 
			
		||||
                  conversionprice c | c == unpricedcommodity
 | 
			
		||||
                                        -- assign a balancing price. Use @@ for more exact output when possible.
 | 
			
		||||
                                        = if length ramountsinunpricedcommodity == 1
 | 
			
		||||
                                           then Just $ TotalPrice $ Mixed [setAmountPrecision maxprecision $ negate $ targetcommodityamount]
 | 
			
		||||
                                           else Just $ UnitPrice $ Mixed [setAmountPrecision maxprecision $ negate $ targetcommodityamount `divideAmount` (quantity unpricedamount)]
 | 
			
		||||
                                    | otherwise = Nothing
 | 
			
		||||
                      where
 | 
			
		||||
                        unpricedcommodity     = head $ filter (`elem` (map commodity rsumamounts)) rcommoditiesinorder
 | 
			
		||||
                        unpricedamount        = head $ filter ((==unpricedcommodity).commodity) rsumamounts
 | 
			
		||||
                        targetcommodityamount = head $ filter ((/=unpricedcommodity).commodity) rsumamounts
 | 
			
		||||
                        ramountsinunpricedcommodity = filter ((==unpricedcommodity).commodity) ramountsinorder
 | 
			
		||||
            inferprice p = p
 | 
			
		||||
 | 
			
		||||
      -- maybe infer prices for balanced virtual postings. Just duplicates the above for now.
 | 
			
		||||
      bvmixedamountsinorder = map pamount $ balancedVirtualPostings t''
 | 
			
		||||
      bvamountsinorder = concatMap amounts bvmixedamountsinorder
 | 
			
		||||
      bvcommoditiesinorder  = map commodity bvamountsinorder
 | 
			
		||||
      bvsumamounts  = amounts $ sum bvmixedamountsinorder
 | 
			
		||||
      t''' = if length bvsumamounts == 2 && all (isNothing.price) bvsumamounts && t'==t -- XXX could check specifically for bv amount inferring
 | 
			
		||||
             then t''{tpostings=map inferprice ps}
 | 
			
		||||
             else t''
 | 
			
		||||
          where
 | 
			
		||||
            inferprice p@Posting{pamount=Mixed [a@Amount{commodity=c,price=Nothing}], ptype=BalancedVirtualPosting}
 | 
			
		||||
                = p{pamount=Mixed [a{price=conversionprice c}]}
 | 
			
		||||
                where
 | 
			
		||||
                  conversionprice c | c == unpricedcommodity
 | 
			
		||||
                                        = if length bvamountsinunpricedcommodity == 1
 | 
			
		||||
                                           then Just $ TotalPrice $ Mixed [setAmountPrecision maxprecision $ negate $ targetcommodityamount]
 | 
			
		||||
                                           else Just $ UnitPrice $ Mixed [setAmountPrecision maxprecision $ negate $ targetcommodityamount `divideAmount` (quantity unpricedamount)]
 | 
			
		||||
                                    | otherwise = Nothing
 | 
			
		||||
                      where
 | 
			
		||||
                        unpricedcommodity     = head $ filter (`elem` (map commodity bvsumamounts)) bvcommoditiesinorder
 | 
			
		||||
                        unpricedamount        = head $ filter ((==unpricedcommodity).commodity) bvsumamounts
 | 
			
		||||
                        targetcommodityamount = head $ filter ((/=unpricedcommodity).commodity) bvsumamounts
 | 
			
		||||
                        bvamountsinunpricedcommodity = filter ((==unpricedcommodity).commodity) bvamountsinorder
 | 
			
		||||
            inferprice p = p
 | 
			
		||||
 | 
			
		||||
      printerr s = intercalate "\n" [s, showTransactionUnelided t]
 | 
			
		||||
 | 
			
		||||
nonzerobalanceerror :: Transaction -> String
 | 
			
		||||
@ -173,7 +231,6 @@ journalTransactionWithDate :: WhichDate -> Transaction -> Transaction
 | 
			
		||||
journalTransactionWithDate ActualDate t = t
 | 
			
		||||
journalTransactionWithDate EffectiveDate t = txnTieKnot t{tdate=fromMaybe (tdate t) (teffectivedate t)}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
-- | Ensure a transaction's postings refer back to it.
 | 
			
		||||
txnTieKnot :: Transaction -> Transaction
 | 
			
		||||
txnTieKnot t@Transaction{tpostings=ps} = t{tpostings=map (settxn t) ps}
 | 
			
		||||
@ -277,16 +334,30 @@ tests_Hledger_Data_Transaction = TestList [
 | 
			
		||||
                            [Posting False "a" missingamt "" RegularPosting [] Nothing,
 | 
			
		||||
                             Posting False "b" missingamt "" RegularPosting [] Nothing
 | 
			
		||||
                            ] ""))
 | 
			
		||||
     let e = balanceTransaction Nothing (Transaction (parsedate "2007/01/28") Nothing False "" "test" "" []
 | 
			
		||||
     let e = balanceTransaction Nothing (Transaction (parsedate "2007/01/28") Nothing False "" "" "" []
 | 
			
		||||
                           [Posting False "a" (Mixed [dollars 1]) "" RegularPosting [] Nothing,
 | 
			
		||||
                            Posting False "b" missingamt "" RegularPosting [] Nothing
 | 
			
		||||
                           ] "")
 | 
			
		||||
     assertBool "one missing amount should be ok" (isRight e)
 | 
			
		||||
     assertEqual "balancing amount is added" 
 | 
			
		||||
     assertBool "balanceTransaction allows one missing amount" (isRight e)
 | 
			
		||||
     assertEqual "balancing amount is inferred"
 | 
			
		||||
                     (Mixed [dollars (-1)])
 | 
			
		||||
                     (case e of
 | 
			
		||||
                        Right e' -> (pamount $ last $ tpostings e')
 | 
			
		||||
                        Left _ -> error' "should not happen")
 | 
			
		||||
     let e = balanceTransaction Nothing (Transaction (parsedate "2011/01/01") Nothing False "" "" "" []
 | 
			
		||||
                           [Posting False "a" (Mixed [dollars 1.35]) "" RegularPosting [] Nothing,
 | 
			
		||||
                            Posting False "b" (Mixed [euros   (-1)]) "" RegularPosting [] Nothing
 | 
			
		||||
                           ] "")
 | 
			
		||||
     assertBool "balanceTransaction can infer conversion price" (isRight e)
 | 
			
		||||
     assertEqual "balancing conversion price is inferred"
 | 
			
		||||
                     (Mixed [Amount{commodity=dollar{precision=2},
 | 
			
		||||
                                    quantity=1.35,
 | 
			
		||||
                                    price=(Just $ TotalPrice $ Mixed [Amount{commodity=euro{precision=maxprecision},
 | 
			
		||||
                                                                             quantity=1,
 | 
			
		||||
                                                                             price=Nothing}])}])
 | 
			
		||||
                     (case e of
 | 
			
		||||
                        Right e' -> (pamount $ head $ tpostings e')
 | 
			
		||||
                        Left _ -> error' "should not happen")
 | 
			
		||||
 | 
			
		||||
  ,"isTransactionBalanced" ~: do
 | 
			
		||||
     let t = Transaction (parsedate "2009/01/01") Nothing False "" "a" "" []
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										123
									
								
								tests/prices.test
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								tests/prices.test
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,123 @@
 | 
			
		||||
# price-related tests
 | 
			
		||||
# 1. print a transaction with an explicit unit price
 | 
			
		||||
bin/hledger -f- print
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100 @ $1.35
 | 
			
		||||
    assets
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency  €100 @ $1.35
 | 
			
		||||
    assets                     €-100 @ $1.35
 | 
			
		||||
 | 
			
		||||
# 2. convert to cost basis
 | 
			
		||||
bin/hledger -f- print -B
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100 @ $1.35
 | 
			
		||||
    assets
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       $135.00
 | 
			
		||||
    assets                         $-135.00
 | 
			
		||||
 | 
			
		||||
# 3. with a historical price directive
 | 
			
		||||
bin/hledger -f- print -B
 | 
			
		||||
<<<
 | 
			
		||||
P 2010/12/31 € $1.34
 | 
			
		||||
P 2011/01/01 € $1.35
 | 
			
		||||
P 2011/01/02 € $1.36
 | 
			
		||||
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100
 | 
			
		||||
    assets
 | 
			
		||||
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       $135.00
 | 
			
		||||
    assets                         $-135.00
 | 
			
		||||
 | 
			
		||||
# 4. with a total price
 | 
			
		||||
bin/hledger -f - print
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100 @@ $135
 | 
			
		||||
    assets
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency  €100 @@ $135
 | 
			
		||||
    assets                     €-100 @@ $135
 | 
			
		||||
 | 
			
		||||
# 5. when the balance has exactly two commodities, both unpriced, infer an
 | 
			
		||||
# implicit conversion price for the first one in terms of the second.
 | 
			
		||||
bin/hledger -f - print
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100
 | 
			
		||||
    misc                              $2.1
 | 
			
		||||
    assets                         $-135.00
 | 
			
		||||
    misc                              €1
 | 
			
		||||
    misc                             €-1
 | 
			
		||||
    misc                             $-2.1
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency  €100 @ $1.35
 | 
			
		||||
    misc                              $2.10
 | 
			
		||||
    assets                         $-135.00
 | 
			
		||||
    misc                         €1 @ $1.35
 | 
			
		||||
    misc                        €-1 @ $1.35
 | 
			
		||||
    misc                             $-2.10
 | 
			
		||||
 | 
			
		||||
# # 6. when the *cost-basis* balance has exactly two commodities, both
 | 
			
		||||
# # unpriced, infer an implicit conversion price for the first one in terms
 | 
			
		||||
# # of the second.
 | 
			
		||||
# bin/hledger -f - print
 | 
			
		||||
# <<<
 | 
			
		||||
# 2011/01/01
 | 
			
		||||
#     expenses:foreign currency       €100
 | 
			
		||||
#     assets                         $-135.00
 | 
			
		||||
#     misc                              $3.1 @ 2 bob
 | 
			
		||||
#     misc                             $-3.1 @ 2 bob
 | 
			
		||||
#     misc                              £1 @@ 2 shekels
 | 
			
		||||
#     misc                             £-1 @@ 2 shekels
 | 
			
		||||
# >>>
 | 
			
		||||
# 2011/01/01
 | 
			
		||||
#     expenses:foreign currency   €100 @ $1.35
 | 
			
		||||
#     assets                     €-100 @ $1.35
 | 
			
		||||
#     misc                        $3.1 @ 2 bob
 | 
			
		||||
#     misc                       $-3.1 @ 2 bob
 | 
			
		||||
#     misc                          £1 @@ 2 shekels
 | 
			
		||||
#     misc                         £-1 @@ 2 shekels
 | 
			
		||||
#
 | 
			
		||||
## 7. another, from ledger tests. Just one posting to price so uses @@.
 | 
			
		||||
bin/hledger -f - print
 | 
			
		||||
<<<
 | 
			
		||||
2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be
 | 
			
		||||
    c56a21d23a6535184e7152ee138c28974f14280c  866.231000 GGGGG
 | 
			
		||||
    a35e82730cf91569c302b313780e5895f75a62b9   $-17,783.72
 | 
			
		||||
>>>
 | 
			
		||||
2002/09/30 * 1a1a6305d06ce4b284dba0d267c23f69d70c20be
 | 
			
		||||
    c56a21d23a6535184e7152ee138c28974f14280c  866.231000 GGGGG @@ $17,783.72
 | 
			
		||||
    a35e82730cf91569c302b313780e5895f75a62b9   $-17,783.72
 | 
			
		||||
 | 
			
		||||
# 8. when the balance has more than two commodities, don't bother
 | 
			
		||||
bin/hledger -f - print
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100
 | 
			
		||||
    assets                         $-135
 | 
			
		||||
    expenses:other                  £200
 | 
			
		||||
>>>= !0
 | 
			
		||||
# 9. another
 | 
			
		||||
bin/hledger -f - balance -B
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency        €99
 | 
			
		||||
    assets                         $-130
 | 
			
		||||
    expenses:foreign currency         €1
 | 
			
		||||
    assets                           $-5
 | 
			
		||||
>>>
 | 
			
		||||
               $-135  assets
 | 
			
		||||
                $135  expenses:foreign currency
 | 
			
		||||
--------------------
 | 
			
		||||
                  $0
 | 
			
		||||
@ -1,66 +0,0 @@
 | 
			
		||||
# price-related tests
 | 
			
		||||
# 1. print a transaction with an explicit unit price
 | 
			
		||||
bin/hledger -f- print
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100 @ $1.35
 | 
			
		||||
    assets
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency  €100 @ $1.35
 | 
			
		||||
    assets                     €-100 @ $1.35
 | 
			
		||||
 | 
			
		||||
# 2. convert to cost basis
 | 
			
		||||
bin/hledger -f- print -B
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100 @ $1.35
 | 
			
		||||
    assets
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       $135.00
 | 
			
		||||
    assets                         $-135.00
 | 
			
		||||
 | 
			
		||||
# 2. with a historical price directive
 | 
			
		||||
bin/hledger -f- print -B
 | 
			
		||||
<<<
 | 
			
		||||
P 2010/12/31 € $1.34
 | 
			
		||||
P 2011/01/01 € $1.35
 | 
			
		||||
P 2011/01/02 € $1.36
 | 
			
		||||
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100
 | 
			
		||||
    assets
 | 
			
		||||
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       $135.00
 | 
			
		||||
    assets                         $-135.00
 | 
			
		||||
 | 
			
		||||
# 3. with a total price
 | 
			
		||||
bin/hledger -f - print
 | 
			
		||||
<<<
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency       €100 @@ $135
 | 
			
		||||
    assets
 | 
			
		||||
>>>
 | 
			
		||||
2011/01/01
 | 
			
		||||
    expenses:foreign currency  €100 @@ $135
 | 
			
		||||
    assets                     €-100 @@ $135
 | 
			
		||||
 | 
			
		||||
# 4. with an implicit price
 | 
			
		||||
# bin/hledger -f - print
 | 
			
		||||
# <<<
 | 
			
		||||
# 2011/01/01
 | 
			
		||||
#     expenses:foreign currency       €100 @ $1.35
 | 
			
		||||
#     assets                              $-135.00
 | 
			
		||||
# >>>
 | 
			
		||||
# 2011/01/01
 | 
			
		||||
#     expenses:foreign currency  €100 @ $1.35
 | 
			
		||||
#     assets
 | 
			
		||||
#
 | 
			
		||||
# 2009/1/1 opening balance
 | 
			
		||||
#     Assets:Brokerage        1 AAPL
 | 
			
		||||
#     Assets:Checking        $-20.00
 | 
			
		||||
#
 | 
			
		||||
# >>>= 0
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user