roi: use MixedAmount more and keep styles when reporting commodities

This commit is contained in:
Dmitry Astapov 2021-01-12 22:51:59 +00:00 committed by Simon Michael
parent 14a3b9833c
commit 07a9f11962
2 changed files with 44 additions and 43 deletions

View File

@ -49,10 +49,10 @@ roimode = hledgerCommandMode
data OneSpan = OneSpan data OneSpan = OneSpan
Day -- start date, inclusive Day -- start date, inclusive
Day -- end date, exclusive Day -- end date, exclusive
Quantity -- value of investment at the beginning of day on spanBegin_ MixedAmount -- value of investment at the beginning of day on spanBegin_
Quantity -- value of investment at the end of day on spanEnd_ MixedAmount -- value of investment at the end of day on spanEnd_
[(Day,Quantity)] -- all deposits and withdrawals (but not changes of value) in the DateSpan [spanBegin_,spanEnd_) [(Day,MixedAmount)] -- all deposits and withdrawals (but not changes of value) in the DateSpan [spanBegin_,spanEnd_)
[(Day,Quantity)] -- all PnL changes of the value of investment in the DateSpan [spanBegin_,spanEnd_) [(Day,MixedAmount)] -- all PnL changes of the value of investment in the DateSpan [spanBegin_,spanEnd_)
deriving (Show) deriving (Show)
@ -131,10 +131,10 @@ roi CliOpts{rawopts_=rawopts, reportspec_=rspec@ReportSpec{rsOpts=ReportOpts{..}
let smallIsZero x = if abs x < 0.01 then 0.0 else x let smallIsZero x = if abs x < 0.01 then 0.0 else x
return [ showDate spanBegin return [ showDate spanBegin
, showDate (addDays (-1) spanEnd) , showDate (addDays (-1) spanEnd)
, T.pack $ showDecimal valueBefore , T.pack $ showMixedAmount valueBefore
, T.pack $ showDecimal cashFlowAmt , T.pack $ showMixedAmount cashFlowAmt
, T.pack $ showDecimal valueAfter , T.pack $ showMixedAmount valueAfter
, T.pack $ showDecimal (valueAfter - (valueBefore + cashFlowAmt)) , T.pack $ showMixedAmount (valueAfter - (valueBefore + cashFlowAmt))
, T.pack $ printf "%0.2f%%" $ smallIsZero irr , T.pack $ printf "%0.2f%%" $ smallIsZero irr
, T.pack $ printf "%0.2f%%" $ smallIsZero twr ] , T.pack $ printf "%0.2f%%" $ smallIsZero twr ]
@ -148,8 +148,9 @@ roi CliOpts{rawopts_=rawopts, reportspec_=rspec@ReportSpec{rsOpts=ReportOpts{..}
TL.putStrLn $ Ascii.render prettyTables id id id table TL.putStrLn $ Ascii.render prettyTables id id id table
timeWeightedReturn showCashFlow prettyTables investmentsQuery trans (OneSpan spanBegin spanEnd valueBefore valueAfter cashFlow pnl) = do timeWeightedReturn showCashFlow prettyTables investmentsQuery trans (OneSpan spanBegin spanEnd valueBeforeAmt valueAfter cashFlow pnl) = do
let initialUnitPrice = 100 let valueBefore = unMix valueBeforeAmt
let initialUnitPrice = 100 :: Decimal
let initialUnits = valueBefore / initialUnitPrice let initialUnits = valueBefore / initialUnitPrice
let changes = let changes =
-- If cash flow and PnL changes happen on the same day, this -- If cash flow and PnL changes happen on the same day, this
@ -170,16 +171,16 @@ timeWeightedReturn showCashFlow prettyTables investmentsQuery trans (OneSpan spa
tail $ tail $
scanl scanl
(\(_, _, unitPrice, unitBalance) (date, amt) -> (\(_, _, unitPrice, unitBalance) (date, amt) ->
let valueOnDate = total trans (And [investmentsQuery, Date (DateSpan Nothing (Just date))]) let valueOnDate = unMix $ total trans (And [investmentsQuery, Date (DateSpan Nothing (Just date))])
in in
case amt of case amt of
Right amt -> Right amt ->
-- we are buying or selling -- we are buying or selling
let unitsBoughtOrSold = amt / unitPrice let unitsBoughtOrSold = unMix amt / unitPrice
in (valueOnDate, unitsBoughtOrSold, unitPrice, unitBalance + unitsBoughtOrSold) in (valueOnDate, unitsBoughtOrSold, unitPrice, unitBalance + unitsBoughtOrSold)
Left pnl -> Left pnl ->
-- PnL change -- PnL change
let valueAfterDate = valueOnDate + pnl let valueAfterDate = valueOnDate + unMix pnl
unitPrice' = valueAfterDate/unitBalance unitPrice' = valueAfterDate/unitBalance
in (valueOnDate, 0, unitPrice', unitBalance)) in (valueOnDate, 0, unitPrice', unitBalance))
(0, 0, initialUnitPrice, initialUnits) (0, 0, initialUnitPrice, initialUnits)
@ -187,7 +188,7 @@ timeWeightedReturn showCashFlow prettyTables investmentsQuery trans (OneSpan spa
let finalUnitBalance = if null units then initialUnits else let (_,_,_,u) = last units in u let finalUnitBalance = if null units then initialUnits else let (_,_,_,u) = last units in u
finalUnitPrice = if finalUnitBalance == 0 then initialUnitPrice finalUnitPrice = if finalUnitBalance == 0 then initialUnitPrice
else valueAfter / finalUnitBalance else (unMix valueAfter) / finalUnitBalance
-- Technically, totalTWR should be (100*(finalUnitPrice - initialUnitPrice) / initialUnitPrice), but initalUnitPrice is 100, so 100/100 == 1 -- Technically, totalTWR should be (100*(finalUnitPrice - initialUnitPrice) / initialUnitPrice), but initalUnitPrice is 100, so 100/100 == 1
totalTWR = roundTo 2 $ (finalUnitPrice - initialUnitPrice) totalTWR = roundTo 2 $ (finalUnitPrice - initialUnitPrice)
years = fromIntegral (diffDays spanEnd spanBegin) / 365 :: Double years = fromIntegral (diffDays spanEnd spanBegin) / 365 :: Double
@ -201,7 +202,7 @@ timeWeightedReturn showCashFlow prettyTables investmentsQuery trans (OneSpan spa
(valuesOnDate',unitsBoughtOrSold', unitPrices', unitBalances') = unzip4 units (valuesOnDate',unitsBoughtOrSold', unitPrices', unitBalances') = unzip4 units
add x lst = if valueBefore/=0 then x:lst else lst add x lst = if valueBefore/=0 then x:lst else lst
dates = add spanBegin dates' dates = add spanBegin dates'
cashflows = add valueBefore cashflows' cashflows = add valueBeforeAmt cashflows'
pnls = add 0 pnls' pnls = add 0 pnls'
unitsBoughtOrSold = add initialUnits unitsBoughtOrSold' unitsBoughtOrSold = add initialUnits unitsBoughtOrSold'
unitPrices = add initialUnitPrice unitPrices' unitPrices = add initialUnitPrice unitPrices'
@ -218,13 +219,13 @@ timeWeightedReturn showCashFlow prettyTables investmentsQuery trans (OneSpan spa
| value <- map showDecimal valuesOnDate | value <- map showDecimal valuesOnDate
| oldBalance <- map showDecimal (0:unitBalances) | oldBalance <- map showDecimal (0:unitBalances)
| balance <- map showDecimal unitBalances | balance <- map showDecimal unitBalances
| pnl <- map showDecimal pnls | pnl <- map showMixedAmount pnls
| cashflow <- map showDecimal cashflows | cashflow <- map showMixedAmount cashflows
| prc <- map showDecimal unitPrices | prc <- map showDecimal unitPrices
| udelta <- map showDecimal unitsBoughtOrSold ]) | udelta <- map showDecimal unitsBoughtOrSold ])
printf "Final unit price: %s/%s units = %s\nTotal TWR: %s%%.\nPeriod: %.2f years.\nAnnualized TWR: %.2f%%\n\n" printf "Final unit price: %s/%s units = %s\nTotal TWR: %s%%.\nPeriod: %.2f years.\nAnnualized TWR: %.2f%%\n\n"
(showDecimal valueAfter) (showDecimal finalUnitBalance) (showDecimal finalUnitPrice) (showDecimal totalTWR) years annualizedTWR (showMixedAmount valueAfter) (showDecimal finalUnitBalance) (showDecimal finalUnitPrice) (showDecimal totalTWR) years annualizedTWR
return annualizedTWR return annualizedTWR
@ -242,7 +243,7 @@ internalRateOfReturn showCashFlow prettyTables (OneSpan spanBegin spanEnd valueB
(Table (Table
(Tbl.Group NoLine (map (Header . showDate) dates)) (Tbl.Group NoLine (map (Header . showDate) dates))
(Tbl.Group SingleLine [Header "Amount"]) (Tbl.Group SingleLine [Header "Amount"])
(map ((:[]) . T.pack . showDecimal) amounts)) (map ((:[]) . T.pack . showMixedAmount) amounts))
-- 0% is always a solution, so require at least something here -- 0% is always a solution, so require at least something here
case totalCF of case totalCF of
@ -256,11 +257,11 @@ internalRateOfReturn showCashFlow prettyTables (OneSpan spanBegin spanEnd valueB
SearchFailed -> error' $ "Error (SearchFailed): Failed to find solution for Internal Rate of Return (IRR).\n" SearchFailed -> error' $ "Error (SearchFailed): Failed to find solution for Internal Rate of Return (IRR).\n"
++ " Either search does not converge to a solution, or converges too slowly." ++ " Either search does not converge to a solution, or converges too slowly."
type CashFlow = [(Day, Quantity)] type CashFlow = [(Day, MixedAmount)]
interestSum :: Day -> CashFlow -> Double -> Double interestSum :: Day -> CashFlow -> Double -> Double
interestSum referenceDay cf rate = sum $ map go cf interestSum referenceDay cf rate = sum $ map go cf
where go (t,m) = fromRational (toRational m) * (rate ** (fromIntegral (referenceDay `diffDays` t) / 365)) where go (t,m) = fromRational (toRational (unMix m)) * (rate ** (fromIntegral (referenceDay `diffDays` t) / 365))
calculateCashFlow :: [Transaction] -> Query -> CashFlow calculateCashFlow :: [Transaction] -> Query -> CashFlow
@ -268,8 +269,8 @@ calculateCashFlow trans query = filter ((/=0).snd) $ map go trans
where where
go t = (transactionDate2 t, total [t] query) go t = (transactionDate2 t, total [t] query)
total :: [Transaction] -> Query -> Quantity total :: [Transaction] -> Query -> MixedAmount
total trans query = unMix $ sumPostings $ filter (matchesPosting query) $ concatMap realPostings trans total trans query = sumPostings $ filter (matchesPosting query) $ concatMap realPostings trans
unMix :: MixedAmount -> Quantity unMix :: MixedAmount -> Quantity
unMix a = unMix a =

View File

@ -12,7 +12,7 @@ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Y
+---++------------+------------++---------------+----------+-------------+-----++-------+-------+ +---++------------+------------++---------------+----------+-------------+-----++-------+-------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++=======+=======+ +===++============+============++===============+==========+=============+=====++=======+=======+
| 1 || 2017-01-01 | 2017-12-31 || 0 | 200 | 200 | 0 || 0.00% | 0.00% | | 1 || 2017-01-01 | 2017-12-31 || 0 | $200 | $200 | 0 || 0.00% | 0.00% |
+---++------------+------------++---------------+----------+-------------+-----++-------+-------+ +---++------------+------------++---------------+----------+-------------+-----++-------+-------+
>>>=0 >>>=0
@ -31,7 +31,7 @@ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Y
+---++------------+------------++---------------+----------+-------------+-----++--------+--------+ +---++------------+------------++---------------+----------+-------------+-----++--------+--------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++========+========+ +===++============+============++===============+==========+=============+=====++========+========+
| 1 || 2017-01-01 | 2017-12-31 || 0 | 100 | 112 | 12 || 12.00% | 12.00% | | 1 || 2017-01-01 | 2017-12-31 || 0 | $100 | $112 | $12 || 12.00% | 12.00% |
+---++------------+------------++---------------+----------+-------------+-----++--------+--------+ +---++------------+------------++---------------+----------+-------------+-----++--------+--------+
>>>=0 >>>=0
@ -95,7 +95,7 @@ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Y
+---++------------+------------++---------------+----------+-------------+-----++--------+--------+ +---++------------+------------++---------------+----------+-------------+-----++--------+--------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++========+========+ +===++============+============++===============+==========+=============+=====++========+========+
| 1 || 2017-01-01 | 2017-12-31 || 0 | 100 | 112 | 12 || 12.00% | 12.00% | | 1 || 2017-01-01 | 2017-12-31 || 0 | $100 | $112 | $12 || 12.00% | 12.00% |
+---++------------+------------++---------------+----------+-------------+-----++--------+--------+ +---++------------+------------++---------------+----------+-------------+-----++--------+--------+
>>>=0 >>>=0
@ -118,7 +118,7 @@ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Y
+---++------------+------------++---------------+----------+-------------+-----++--------+--------+ +---++------------+------------++---------------+----------+-------------+-----++--------+--------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++========+========+ +===++============+============++===============+==========+=============+=====++========+========+
| 1 || 2017-01-01 | 2017-12-31 || 0 | 200 | 220 | 20 || 12.72% | 10.00% | | 1 || 2017-01-01 | 2017-12-31 || 0 | $200 | $220 | $20 || 12.72% | 10.00% |
+---++------------+------------++---------------+----------+-------------+-----++--------+--------+ +---++------------+------------++---------------+----------+-------------+-----++--------+--------+
>>>=0 >>>=0
@ -138,11 +138,11 @@ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Y
investment = $20 investment = $20
pnl pnl
>>> >>>
+---++------------+------------++---------------+----------+-------------+------++---------+---------+ +---++------------+------------++---------------+----------+-------------+-------++---------+---------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+======++=========+=========+ +===++============+============++===============+==========+=============+=======++=========+=========+
| 1 || 2017-01-01 | 2017-12-31 || 0 | 200 | 20 | -180 || -95.73% | -90.00% | | 1 || 2017-01-01 | 2017-12-31 || 0 | $200 | $20 | $-180 || -95.73% | -90.00% |
+---++------------+------------++---------------+----------+-------------+------++---------+---------+ +---++------------+------------++---------------+----------+-------------+-------++---------+---------+
>>>=0 >>>=0
@ -172,10 +172,10 @@ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Q
+---++------------+------------++---------------+----------+-------------+-----++---------+---------+ +---++------------+------------++---------------+----------+-------------+-----++---------+---------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++=========+=========+ +===++============+============++===============+==========+=============+=====++=========+=========+
| 1 || 2017-01-01 | 2017-03-31 || 0 | 100 | 100 | 0 || 0.00% | 0.00% | | 1 || 2017-01-01 | 2017-03-31 || 0 | $100 | $100 | 0 || 0.00% | 0.00% |
| 2 || 2017-04-01 | 2017-06-30 || 100 | 0 | 110 | 10 || 46.56% | 46.56% | | 2 || 2017-04-01 | 2017-06-30 || $100 | 0 | $110 | $10 || 46.56% | 46.56% |
| 3 || 2017-07-01 | 2017-09-30 || 110 | 100 | 210 | 0 || 0.00% | 0.00% | | 3 || 2017-07-01 | 2017-09-30 || $110 | $100 | $210 | 0 || 0.00% | 0.00% |
| 4 || 2017-10-01 | 2017-12-31 || 210 | -50 | 155 | -5 || -11.83% | -11.82% | | 4 || 2017-10-01 | 2017-12-31 || $210 | $-50 | $155 | $-5 || -11.83% | -11.82% |
+---++------------+------------++---------------+----------+-------------+-----++---------+---------+ +---++------------+------------++---------------+----------+-------------+-----++---------+---------+
>>>=0 >>>=0
@ -206,7 +206,7 @@ hledger -f- roi --inv investment --pnl pnl -b 2017-06 -e 2018
+---++------------+------------++---------------+----------+-------------+-----++-------+--------+ +---++------------+------------++---------------+----------+-------------+-----++-------+--------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++=======+========+ +===++============+============++===============+==========+=============+=====++=======+========+
| 1 || 2017-06-01 | 2017-12-31 || 100 | 50 | 155 | 5 || 5.24% | 11.45% | | 1 || 2017-06-01 | 2017-12-31 || $100 | $50 | $155 | $5 || 5.24% | 11.45% |
+---++------------+------------++---------------+----------+-------------+-----++-------+--------+ +---++------------+------------++---------------+----------+-------------+-----++-------+--------+
>>>=0 >>>=0
@ -258,7 +258,7 @@ hledger -f- roi -p 2019-11 --inv Investment --pnl PnL --value cost,A --infer-val
+---++------------+------------++---------------+----------+-------------+-----++----------+-------+ +---++------------+------------++---------------+----------+-------------+-----++----------+-------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=====++==========+=======+ +===++============+============++===============+==========+=============+=====++==========+=======+
| 1 || 2019-11-01 | 2019-11-30 || 0 | -1 | 0 | 1 || 3678.34% | 0.00% | | 1 || 2019-11-01 | 2019-11-30 || 0 | -1 A | 0 | 1 A || 3678.34% | 0.00% |
+---++------------+------------++---------------+----------+-------------+-----++----------+-------+ +---++------------+------------++---------------+----------+-------------+-----++----------+-------+
>>>=0 >>>=0
@ -277,10 +277,10 @@ P 2021-01-01 $ 73.88
assets:investment =11000 assets:investment =11000
income:investment income:investment
>>> >>>
+---++------------+------------++---------------+----------+-------------+-------++---------+---------+ +---++------------+------------++---------------+----------+-------------+-----++---------+---------+
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR | | || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR | TWR |
+===++============+============++===============+==========+=============+=======++=========+=========+ +===++============+============++===============+==========+=============+=====++=========+=========+
| 1 || 2020-12-02 | 2021-01-02 || 0 | 135.35 | 148.89 | 13.54 || 196.58% | 196.58% | | 1 || 2020-12-02 | 2021-01-02 || 0 | $135 | $149 | $14 || 196.58% | 196.58% |
+---++------------+------------++---------------+----------+-------------+-------++---------+---------+ +---++------------+------------++---------------+----------+-------------+-----++---------+---------+
>>>=0 >>>=0