mirror of
https://github.com/simonmichael/hledger.git
synced 2025-01-06 02:23:46 +03:00
fix: roi: rare bug with PnL applied on the first day of investment
This commit is contained in:
parent
25755c1ddd
commit
555a68faa5
@ -19,7 +19,7 @@ import System.Exit
|
||||
import Data.Time.Calendar
|
||||
import Text.Printf
|
||||
import Data.Bifunctor (second)
|
||||
import Data.Either (fromLeft, fromRight)
|
||||
import Data.Either (fromLeft, fromRight, isLeft)
|
||||
import Data.Function (on)
|
||||
import Data.List
|
||||
import Numeric.RootFinding
|
||||
@ -168,15 +168,33 @@ timeWeightedReturn showCashFlow prettyTables investmentsQuery trans mixedAmountV
|
||||
-- will sort PnL changes to come before cash flows (on any
|
||||
-- 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
|
||||
sort
|
||||
$ (++) (map (\(date,amt) -> (date,Left $ maNegate amt)) pnl )
|
||||
-- Aggregate all entries for a single day, assuming that intraday interest is negligible
|
||||
$ map (\date_cash -> let (dates, cash) = unzip date_cash in (head dates, Right (maSum cash)))
|
||||
$ groupBy ((==) `on` fst)
|
||||
$ sortOn fst
|
||||
$ map (second maNegate)
|
||||
$ cashFlow
|
||||
-- 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.
|
||||
zeroUnitsNeedsCashflowAtTheFront
|
||||
$ sort
|
||||
$ dailyCashflows ++ datedPnls
|
||||
where
|
||||
zeroUnitsNeedsCashflowAtTheFront changes =
|
||||
if initialUnits > 0 then changes
|
||||
else
|
||||
let (leadingPnls, rest) = span (isLeft . snd) changes
|
||||
(firstCashflow, rest') = splitAt 1 rest
|
||||
in firstCashflow ++ leadingPnls ++ rest'
|
||||
|
||||
datedPnls = map (\(date,amt) -> (date,Left $ maNegate amt)) pnl
|
||||
|
||||
dailyCashflows =
|
||||
sort
|
||||
-- Aggregate all entries for a single day, assuming that intraday interest is negligible
|
||||
$ map (\date_cash -> let (dates, cash) = unzip date_cash in (head dates, Right (maSum cash)))
|
||||
$ groupBy ((==) `on` fst)
|
||||
$ sortOn fst
|
||||
$ map (second maNegate)
|
||||
$ cashFlow
|
||||
|
||||
let units =
|
||||
tail $
|
||||
@ -195,7 +213,7 @@ timeWeightedReturn showCashFlow prettyTables investmentsQuery trans mixedAmountV
|
||||
unitPrice' = valueAfterDate/unitBalance
|
||||
in (valueOnDate, 0, unitPrice', unitBalance))
|
||||
(0, 0, initialUnitPrice, initialUnits)
|
||||
changes
|
||||
$ dbg3 "changes" changes
|
||||
|
||||
let finalUnitBalance = if null units then initialUnits else let (_,_,_,u) = last units in u
|
||||
finalUnitPrice = if finalUnitBalance == 0 then initialUnitPrice
|
||||
|
Loading…
Reference in New Issue
Block a user