roi: simplify/speed up (no longer checks every day with P directive)
This commit is contained in:
parent
c6f84b31c0
commit
dde5a59049
@ -20,14 +20,13 @@ import System.Exit
|
|||||||
import Data.Time.Calendar
|
import Data.Time.Calendar
|
||||||
import Text.Printf
|
import Text.Printf
|
||||||
import Data.Bifunctor (second)
|
import Data.Bifunctor (second)
|
||||||
import Data.Either (fromLeft, fromRight, isLeft)
|
|
||||||
import Data.Function (on)
|
import Data.Function (on)
|
||||||
import Data.List
|
import Data.List
|
||||||
import Numeric.RootFinding
|
import Numeric.RootFinding
|
||||||
import Data.Decimal
|
import Data.Decimal
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import qualified Data.Text.Lazy.IO as TL
|
import qualified Data.Text.Lazy.IO as TL
|
||||||
import Safe (headDef, tailDef)
|
import Safe (headDef, lastMay)
|
||||||
import System.Console.CmdArgs.Explicit as CmdArgs
|
import System.Console.CmdArgs.Explicit as CmdArgs
|
||||||
|
|
||||||
import Text.Tabular.AsciiWide as Tab
|
import Text.Tabular.AsciiWide as Tab
|
||||||
@ -99,8 +98,6 @@ roi CliOpts{rawopts_=rawopts, reportspec_=rspec@ReportSpec{_rsReportOpts=ReportO
|
|||||||
|
|
||||||
let (fullPeriod, spans) = reportSpan filteredj rspec
|
let (fullPeriod, spans) = reportSpan filteredj rspec
|
||||||
|
|
||||||
let priceDirectiveDates = dbg3 "priceDirectiveDates" $ map pddate $ jpricedirectives j
|
|
||||||
|
|
||||||
let processSpan (DateSpan Nothing _) = error "Undefined start of the period - will be unable to compute the rates of return"
|
let processSpan (DateSpan Nothing _) = error "Undefined start of the period - will be unable to compute the rates of return"
|
||||||
processSpan (DateSpan _ Nothing) = error "Undefined end of the period - will be unable to compute the rates of return"
|
processSpan (DateSpan _ Nothing) = error "Undefined end of the period - will be unable to compute the rates of return"
|
||||||
processSpan spn@(DateSpan (Just begin) (Just end)) = do
|
processSpan spn@(DateSpan (Just begin) (Just end)) = do
|
||||||
@ -120,9 +117,7 @@ roi CliOpts{rawopts_=rawopts, reportspec_=rspec@ReportSpec{_rsReportOpts=ReportO
|
|||||||
total trans (And [investmentsQuery
|
total trans (And [investmentsQuery
|
||||||
, Date (DateSpan Nothing (Just end))])
|
, Date (DateSpan Nothing (Just end))])
|
||||||
|
|
||||||
priceDates = dbg3 "priceDates" $ nub $ filter (spanContainsDate spn) priceDirectiveDates
|
cashFlow = dbg3 "cashFlow" $
|
||||||
cashFlow =
|
|
||||||
((map (,nullmixedamt) priceDates)++) $
|
|
||||||
cashFlowApplyCostValue $
|
cashFlowApplyCostValue $
|
||||||
calculateCashFlow wd trans (And [ Not investmentsQuery
|
calculateCashFlow wd trans (And [ Not investmentsQuery
|
||||||
, Not pnlQuery
|
, Not pnlQuery
|
||||||
@ -179,116 +174,116 @@ roi CliOpts{rawopts_=rawopts, reportspec_=rspec@ReportSpec{_rsReportOpts=ReportO
|
|||||||
|
|
||||||
TL.putStrLn $ Tab.render prettyTables id id id table
|
TL.putStrLn $ Tab.render prettyTables id id id table
|
||||||
|
|
||||||
timeWeightedReturn styles showCashFlow prettyTables investmentsQuery trans mixedAmountValue (OneSpan begin end valueBeforeAmt valueAfter cashFlow pnl) = do
|
-- Entry for TWR computation, capturing all cashflows that are potentially accompanied by pnl change on the same day (if not, it is zero)
|
||||||
let valueBefore = unMix valueBeforeAmt
|
data TwrEntry = TwrEntry { twrDate :: Day, twrCashflow :: Decimal, twrValueAfter :: Decimal, twrPnl :: Decimal } deriving (Eq, Show)
|
||||||
let initialUnitCost = 100 :: Decimal
|
|
||||||
let initialUnits = valueBefore / initialUnitCost
|
timeWeightedReturn _styles showCashFlow prettyTables investmentsQuery trans mixedAmountValue (OneSpan begin end valueBeforeAmt valueAfterAmt cashflows pnls) = do
|
||||||
let changes =
|
let datedCashflows =
|
||||||
-- If cash flow and PnL changes happen on the same day, this
|
-- Aggregate all entries for a single day, assuming that intraday interest is negligible
|
||||||
-- will sort PnL changes to come before cash flows (on any
|
dbg3 "datedCashflows"
|
||||||
-- given day), so that we will have better unit price computed
|
|
||||||
-- first for processing cash flow. This is why pnl changes are Left
|
|
||||||
-- and cashflows are Right.
|
|
||||||
-- However, if the very first date in the changes list has both
|
|
||||||
-- PnL and CashFlow, we would not be able to apply pnl change to 0 unit,
|
|
||||||
-- which would lead to an error. We make sure that we have at least one
|
|
||||||
-- cashflow entry at the front, and we know that there would be at most
|
|
||||||
-- one for the given date, by construction. Empty CashFlows added
|
|
||||||
-- because of a begin date before the first transaction are not seen as
|
|
||||||
-- a valid cashflow entry at the front.
|
|
||||||
zeroUnitsNeedsCashflowAtTheFront
|
|
||||||
$ sort
|
$ sort
|
||||||
$ datedCashflows ++ datedPnls
|
$ map (\datecashes -> let (dates, cash) = unzip datecashes in (headDef (error' "Roi.hs: datecashes was null, please report a bug") dates, maSum cash))
|
||||||
where
|
$ groupBy ((==) `on` fst)
|
||||||
zeroUnitsNeedsCashflowAtTheFront changes1 =
|
$ sortOn fst
|
||||||
if initialUnits > 0 then changes1
|
$ map (second maNegate)
|
||||||
else
|
$ cashflows
|
||||||
let (leadingEmptyCashFlows, rest) = span isEmptyCashflow changes1
|
|
||||||
(leadingPnls, rest') = span (isLeft . snd) rest
|
|
||||||
(firstCashflow, rest'') = splitAt 1 rest'
|
|
||||||
in leadingEmptyCashFlows ++ firstCashflow ++ leadingPnls ++ rest''
|
|
||||||
|
|
||||||
isEmptyCashflow (_date, amt) = case amt of
|
valueBefore = unMix valueBeforeAmt
|
||||||
Right amt' -> mixedAmountIsZero amt'
|
valueAfter = unMix valueAfterAmt
|
||||||
Left _ -> False
|
|
||||||
|
|
||||||
datedPnls = map (second Left) $ aggregateByDate pnl
|
investmentPostings = concatMap (filter (matchesPosting investmentsQuery) . realPostings) trans
|
||||||
|
|
||||||
datedCashflows = map (second Right) $ aggregateByDate cashFlow
|
totalInvestmentPostingsTill date = sumPostings $ filter (matchesPosting (Date (DateSpan Nothing (Just $ Exact date)))) investmentPostings
|
||||||
|
|
||||||
aggregateByDate datedAmounts =
|
-- filter span is (-infinity, date+1), which gives us effectively (-infinity, date]
|
||||||
-- Aggregate all entries for a single day, assuming that intraday interest is negligible
|
valueAfterDate date = unMix $ mixedAmountValue end date $ totalInvestmentPostingsTill (addDays 1 date)
|
||||||
sort
|
|
||||||
$ map (\datecashes -> let (dates, cash) = unzip datecashes in (headDef (error' "Roi.hs: datecashes was null, please report a bug") dates, maSum cash))
|
|
||||||
$ groupBy ((==) `on` fst)
|
|
||||||
$ sortOn fst
|
|
||||||
$ map (second maNegate)
|
|
||||||
$ datedAmounts
|
|
||||||
|
|
||||||
let units =
|
-- We are dividing the period [begin, end) into subperiods on each cashflow, and then compute
|
||||||
tailDef (error' "Roi.hs units was null, please report a bug") $
|
-- the rate of return for each subperiod. For this we need to know the value of the investment
|
||||||
scanl
|
-- at the beginning and end of each subperiod, adjusted for cashflow.
|
||||||
(\(_, _, unitCost, unitBalance) (date, amt) ->
|
--
|
||||||
let valueOnDate = unMix $ mixedAmountValue end date $ total trans (And [investmentsQuery, Date (DateSpan Nothing (Just $ Exact date))])
|
-- Subperiods are going to be [valueBefore ... (c_0,v_0)][... (c_1, v_1)][... (c_2,v_2)] ... [... (c_n,v_n)][... valueAfter]
|
||||||
in
|
-- , where v_i is the value of investment computed immediately after cashflow c_i
|
||||||
case amt of
|
addEnd cflows =
|
||||||
Right amt' ->
|
case lastMay cflows of
|
||||||
-- we are buying or selling
|
Nothing -> cflows
|
||||||
let unitsBoughtOrSold = unMix amt' / unitCost
|
Just entry ->
|
||||||
in (valueOnDate, unitsBoughtOrSold, unitCost, unitBalance + unitsBoughtOrSold)
|
let end_ = addDays (-1) end in
|
||||||
Left pnl' ->
|
if twrDate entry < end_ then cflows++[TwrEntry end_ 0 valueAfter (pnlOn end_)] else cflows
|
||||||
-- PnL change
|
|
||||||
let valueAfterDate = valueOnDate + unMix pnl'
|
|
||||||
unitCost' =
|
|
||||||
if unitBalance == 0 then initialUnitCost -- everything was sold, let's reset the cost to initial cost
|
|
||||||
else valueAfterDate/unitBalance
|
|
||||||
in (valueOnDate, 0, unitCost', unitBalance))
|
|
||||||
(0, 0, initialUnitCost, initialUnits)
|
|
||||||
$ dbg3 "changes" changes
|
|
||||||
|
|
||||||
let finalUnitBalance = if null units then initialUnits else let (_,_,_,u) = last units in u
|
pnlOn date = unMix $ maNegate $ sum $ map snd $ filter ((==date).fst) pnls
|
||||||
finalUnitCost = if finalUnitBalance == 0 then
|
|
||||||
if null units then initialUnitCost
|
twrEntries =
|
||||||
else let (_,_,lastUnitCost,_) = last units in lastUnitCost
|
dbg3 "twrEntries"
|
||||||
else (unMix valueAfter) / finalUnitBalance
|
$ addEnd
|
||||||
-- Technically, totalTWR should be (100*(finalUnitCost - initialUnitCost) / initialUnitCost), but initalUnitCost is 100, so 100/100 == 1
|
$ concatMap (\(date,cashflow) ->
|
||||||
totalTWR = roundTo 2 $ (finalUnitCost - initialUnitCost)
|
let pnl = pnlOn date
|
||||||
|
cash = unMix cashflow
|
||||||
|
value_ = valueAfterDate date - pnl - cash -- valueAfterDate includes both cashflow and pnl on date, if any
|
||||||
|
in
|
||||||
|
-- if we had PnL postings on the same day as cashflow,
|
||||||
|
-- we want to account for them separately. If pnl is positive, we apply pnl first, and if pnl was negative
|
||||||
|
-- we apply cashflow first, in an attempt to avoid having negative valuations and ugly debug output (and
|
||||||
|
-- computations as well)
|
||||||
|
if pnl == 0 then [TwrEntry date cash (value_ + cash) 0]
|
||||||
|
else if pnl > 0
|
||||||
|
then [TwrEntry date 0 (value_ + pnl) pnl, TwrEntry date cash (value_ + cash + pnl) 0]
|
||||||
|
else [TwrEntry date cash (value_ + cash) 0, TwrEntry date 0 (value_ + cash + pnl) pnl]
|
||||||
|
) datedCashflows
|
||||||
|
|
||||||
|
-- Calculate interest for each subperiod, adjusting the value at the start of the period by the cashflow
|
||||||
|
-- For subperiods [valueBefore ... (c_0,v_0)][... (c_1, v_1)][... (c_2,v_2)] ... [... (c_n,v_n)][... valueAfter], the computation is going to be
|
||||||
|
-- 1 + twr = (v_0 - c_0)/valueBefore + (v_1 - c_1) / v_0 + ... + valueAfter/v_n
|
||||||
|
-- See https://en.wikipedia.org/wiki/Time-weighted_return#Time-weighted_return_compensating_for_external_flows
|
||||||
|
let calculateSubPeriods _ [] = []
|
||||||
|
calculateSubPeriods prev (curr:rest) =
|
||||||
|
let adjustedEnd = twrValueAfter curr - twrCashflow curr in
|
||||||
|
let subPeriodReturn =
|
||||||
|
if twrValueAfter prev == 0 || adjustedEnd == 0
|
||||||
|
then 1
|
||||||
|
else adjustedEnd / (twrValueAfter prev)
|
||||||
|
in (subPeriodReturn, (prev, curr)) : calculateSubPeriods curr rest
|
||||||
|
|
||||||
|
let subPeriods = dbg3 "subPeriods" $ calculateSubPeriods (TwrEntry begin 0 valueBefore (pnlOn begin)) twrEntries
|
||||||
|
|
||||||
|
-- Compute overall time-weighted rate of return
|
||||||
|
let twr =
|
||||||
|
dbg3 "twr" $
|
||||||
|
if subPeriods == []
|
||||||
|
then if valueBefore == 0 then 0 else (valueAfter - valueBefore)/valueBefore
|
||||||
|
else (product $ map fst subPeriods) - 1
|
||||||
(startYear, _, _) = toGregorian begin
|
(startYear, _, _) = toGregorian begin
|
||||||
years = fromIntegral (diffDays end begin) / (if isLeapYear startYear then 366 else 365) :: Double
|
years = fromIntegral (diffDays end begin) / (if isLeapYear startYear then 366 else 365) :: Double
|
||||||
annualizedTWR = 100*((1+(realToFrac totalTWR/100))**(1/years)-1) :: Double
|
periodTWR = roundTo 2 $ 100 * twr
|
||||||
|
annualizedTWR = 100*((1+(realToFrac twr))**(1/years)-1) :: Double
|
||||||
|
|
||||||
when showCashFlow $ do
|
when showCashFlow $ do
|
||||||
printf "\nTWR cash flow for %s - %s\n" (showDate begin) (showDate (addDays (-1) end))
|
printf "\nTWR cash flow entries and subperiod rates for period %s - %s\n" (showDate begin) (showDate (addDays (-1) end))
|
||||||
let (dates', amts) = unzip changes
|
let showDecimalT = T.pack . showDecimal
|
||||||
cashflows' = map (fromRight nullmixedamt) amts
|
let dates = map twrDate twrEntries
|
||||||
pnls = map (fromLeft nullmixedamt) amts
|
TL.putStrLn $ Tab.render prettyTables id id id
|
||||||
(valuesOnDate,unitsBoughtOrSold', unitPrices', unitBalances') = unzip4 units
|
|
||||||
add x lst = if valueBefore/=0 then x:lst else lst
|
|
||||||
dates = add begin dates'
|
|
||||||
cashflows = add valueBeforeAmt cashflows'
|
|
||||||
unitsBoughtOrSold = add initialUnits unitsBoughtOrSold'
|
|
||||||
unitPrices = add initialUnitCost unitPrices'
|
|
||||||
unitBalances = add initialUnits unitBalances'
|
|
||||||
|
|
||||||
TL.putStr $ Tab.render prettyTables id id T.pack
|
|
||||||
(Table
|
(Table
|
||||||
(Tab.Group NoLine (map (Header . showDate) dates))
|
(Tab.Group Tab.NoLine (map (Header . showDate) dates))
|
||||||
(Tab.Group DoubleLine [ Tab.Group Tab.SingleLine [Tab.Header "Portfolio value", Tab.Header "Unit balance"]
|
(Tab.Group Tab.SingleLine [Header "Amount", Header "PnL on this day", Header "Value afterwards" ])
|
||||||
, Tab.Group Tab.SingleLine [Tab.Header "Pnl", Tab.Header "Cashflow", Tab.Header "Unit price", Tab.Header "Units"]
|
( [ [ showDecimalT (twrCashflow e), showDecimalT (twrPnl e), showDecimalT (twrValueAfter e) ]
|
||||||
, Tab.Group Tab.SingleLine [Tab.Header "New Unit Balance"]])
|
| e <- twrEntries ]))
|
||||||
[ [val, oldBalance, pnl', cashflow, prc, udelta, balance]
|
|
||||||
| val <- map showDecimal valuesOnDate
|
|
||||||
| oldBalance <- map showDecimal (0:unitBalances)
|
|
||||||
| balance <- map showDecimal unitBalances
|
|
||||||
| pnl' <- map (showMixedAmountOneLineWithoutCost False . styleAmounts styles) pnls
|
|
||||||
| cashflow <- map (showMixedAmountOneLineWithoutCost False . styleAmounts styles) cashflows
|
|
||||||
| prc <- map showDecimal unitPrices
|
|
||||||
| udelta <- map showDecimal unitsBoughtOrSold ])
|
|
||||||
|
|
||||||
printf "Final unit price: %s/%s units = %s\nTotal TWR: %s%%.\nPeriod: %.2f years.\nAnnualized TWR: %.2f%%\n\n"
|
TL.putStr $ Tab.render prettyTables T.pack id id
|
||||||
(showMixedAmountOneLineWithoutCost False $ styleAmounts styles valueAfter) (showDecimal finalUnitBalance) (showDecimal finalUnitCost) (showDecimal totalTWR) years annualizedTWR
|
(Table
|
||||||
|
(Tab.Group Tab.NoLine [ Header (show n) | n <-[1..length subPeriods]])
|
||||||
|
(Tab.Group DoubleLine [ Tab.Group Tab.SingleLine [Tab.Header "Subperiod start", Tab.Header "Subperiod end"]
|
||||||
|
, Tab.Group Tab.SingleLine [Tab.Header "Value at start", Tab.Header "Cashflow", Tab.Header "PnL postings", Tab.Header "Value at end"]
|
||||||
|
, Tab.Group Tab.SingleLine [Tab.Header "Subperiod return rate"]])
|
||||||
|
[ [ showDate (twrDate prev), showDate (twrDate curr)
|
||||||
|
, showDecimalT (twrValueAfter prev - twrCashflow prev), showDecimalT (twrCashflow prev), showDecimalT (twrPnl prev), showDecimalT (twrValueAfter curr - twrCashflow curr)
|
||||||
|
, showDecimalT rate ]
|
||||||
|
| (rate, (prev, curr)) <- subPeriods
|
||||||
|
])
|
||||||
|
|
||||||
return ((realToFrac totalTWR) :: Double, annualizedTWR)
|
printf "Total period TWR: %s%%.\nPeriod: %.2f years.\nAnnualized TWR: %.2f%%\n\n"
|
||||||
|
(showDecimal periodTWR) years annualizedTWR
|
||||||
|
|
||||||
|
return ((realToFrac periodTWR) :: Double, annualizedTWR)
|
||||||
|
|
||||||
internalRateOfReturn styles showCashFlow prettyTables (OneSpan begin end valueBefore valueAfter cashFlow _pnl) = do
|
internalRateOfReturn styles showCashFlow prettyTables (OneSpan begin end valueBefore valueAfter cashFlow _pnl) = do
|
||||||
let prefix = (begin, maNegate valueBefore)
|
let prefix = (begin, maNegate valueBefore)
|
||||||
|
|||||||
@ -170,7 +170,7 @@ $ hledger -f- roi --inv investment --pnl pnl -b 2017 -e 2018 -Q
|
|||||||
| 1 || 2017-01-01 | 2017-03-31 || 0 | $100 | $100 | 0 || 0.00% || 0.00% | 0.00% |
|
| 1 || 2017-01-01 | 2017-03-31 || 0 | $100 | $100 | 0 || 0.00% || 0.00% | 0.00% |
|
||||||
| 2 || 2017-04-01 | 2017-06-30 || $100 | 0 | $110 | $10 || 46.56% || 10.00% | 46.56% |
|
| 2 || 2017-04-01 | 2017-06-30 || $100 | 0 | $110 | $10 || 46.56% || 10.00% | 46.56% |
|
||||||
| 3 || 2017-07-01 | 2017-09-30 || $110 | $100 | $210 | 0 || 0.00% || 0.00% | 0.00% |
|
| 3 || 2017-07-01 | 2017-09-30 || $110 | $100 | $210 | 0 || 0.00% || 0.00% | 0.00% |
|
||||||
| 4 || 2017-10-01 | 2017-12-31 || $210 | $-50 | $155 | $-5 || -11.83% || -3.12% | -11.82% |
|
| 4 || 2017-10-01 | 2017-12-31 || $210 | $-50 | $155 | $-5 || -11.83% || -3.12% | -11.83% |
|
||||||
+-------++------------+------------++---------------+----------+-------------+-----++---------++------------+----------+
|
+-------++------------+------------++---------------+----------+-------------+-----++---------++------------+----------+
|
||||||
| Total || 2017-01-01 | 2017-12-31 || 0 | $150 | $155 | $5 || 3.64% || 6.56% | 6.56% |
|
| Total || 2017-01-01 | 2017-12-31 || 0 | $150 | $155 | $5 || 3.64% || 6.56% | 6.56% |
|
||||||
+-------++------------+------------++---------------+----------+-------------+-----++---------++------------+----------+
|
+-------++------------+------------++---------------+----------+-------------+-----++---------++------------+----------+
|
||||||
@ -277,7 +277,7 @@ $ hledger -f - roi --inv assets:investment --pnl income:investment --value=then,
|
|||||||
+---++------------+------------++---------------+---------------+---------------+--------------++---------++------------+----------+
|
+---++------------+------------++---------------+---------------+---------------+--------------++---------++------------+----------+
|
||||||
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
|
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
|
||||||
+===++============+============++===============+===============+===============+==============++=========++============+==========+
|
+===++============+============++===============+===============+===============+==============++=========++============+==========+
|
||||||
| 1 || 2020-12-02 | 2021-01-02 || 0 | $131.23359580 | $148.89009204 | $17.65649624 || 321.99% || 13.45% | 323.47% |
|
| 1 || 2020-12-02 | 2021-01-02 || 0 | $131.23359580 | $148.89009204 | $17.65649624 || 321.99% || 13.45% | 323.66% |
|
||||||
+---++------------+------------++---------------+---------------+---------------+--------------++---------++------------+----------+
|
+---++------------+------------++---------------+---------------+---------------+--------------++---------++------------+----------+
|
||||||
|
|
||||||
>=
|
>=
|
||||||
@ -376,7 +376,7 @@ P 2023-01-01 C 1B
|
|||||||
|
|
||||||
P 2023-12-31 C 2B
|
P 2023-12-31 C 2B
|
||||||
|
|
||||||
$ hledger -f - roi --inv investment --pnl income --value='end,B' -b2023 -e2024
|
$ hledger -f - roi --inv investment --pnl income --value='then,B' -b2023 -e2024
|
||||||
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
|
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
|
||||||
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
|
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
|
||||||
+===++============+============++===============+==========+=============+=====++========++============+==========+
|
+===++============+============++===============+==========+=============+=====++========++============+==========+
|
||||||
@ -384,3 +384,32 @@ $ hledger -f - roi --inv investment --pnl income --value='end,B' -b2023 -e2024
|
|||||||
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
|
+---++------------+------------++---------------+----------+-------------+-----++--------++------------+----------+
|
||||||
|
|
||||||
>= 0
|
>= 0
|
||||||
|
|
||||||
|
# ** 16. Correcly process dates with just pricing changes
|
||||||
|
<
|
||||||
|
D 1,000.00 EUR
|
||||||
|
|
||||||
|
2018-07-01 investment
|
||||||
|
assets:bank
|
||||||
|
investments:iShares Core MSCI World 1 "IE00B4L5Y983"
|
||||||
|
|
||||||
|
P 2018-12-28 "IE00B4L5Y983" 43.11000000 "EUR"
|
||||||
|
P 2019-06-28 "IE00B4L5Y983" 50.93000000 "EUR"
|
||||||
|
|
||||||
|
2019-07-01 investment
|
||||||
|
assets:bank
|
||||||
|
investments:iShares Core MSCI World 10 "IE00B4L5Y983"
|
||||||
|
|
||||||
|
P 2019-12-30 "IE00B4L5Y983" 56.59000000 "EUR"
|
||||||
|
|
||||||
|
$ hledger -f - roi --value then --begin 2019 --end 2020 --inv investmen --pnl '"profit and loss"' -p 'every 2 quarters'
|
||||||
|
+-------++------------+------------++---------------+------------+-------------+-----------++--------++------------+----------+
|
||||||
|
| || Begin | End || Value (begin) | Cashflow | Value (end) | PnL || IRR || TWR/period | TWR/year |
|
||||||
|
+=======++============+============++===============+============+=============+===========++========++============+==========+
|
||||||
|
| 1 || 2019-01-01 | 2019-06-30 || 43.11 EUR | 0 | 50.93 EUR | 7.82 EUR || 39.96% || 18.14% | 39.96% |
|
||||||
|
| 2 || 2019-07-01 | 2019-12-31 || 50.93 EUR | 509.30 EUR | 622.49 EUR | 62.26 EUR || 23.25% || 11.11% | 23.25% |
|
||||||
|
+-------++------------+------------++---------------+------------+-------------+-----------++--------++------------+----------+
|
||||||
|
| Total || 2019-01-01 | 2019-12-31 || 43.11 EUR | 509.30 EUR | 622.49 EUR | 70.08 EUR || 24.51% || 31.27% | 31.27% |
|
||||||
|
+-------++------------+------------++---------------+------------+-------------+-----------++--------++------------+----------+
|
||||||
|
|
||||||
|
>= 0
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user