{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveGeneric #-}
module Hledger.Data.Valuation (
ValuationType(..)
,PriceOracle
,journalPriceOracle
,unsupportedValueThenError
,mixedAmountApplyValuation
,mixedAmountValueAtDate
,marketPriceReverse
,priceDirectiveToMarketPrice
,tests_Valuation
)
where
import Control.Applicative ((<|>))
import Data.Decimal (roundTo)
import Data.Function ((&), on)
import Data.Graph.Inductive (Gr, Node, NodeMap, mkMapGraph, mkNode, lab, out, sp)
import Data.List
import Data.List.Extra (nubSortBy)
import qualified Data.Map as M
import Data.Maybe
import qualified Data.Text as T
import Data.Time.Calendar (Day, fromGregorian)
import Data.MemoUgly (memo)
import GHC.Generics (Generic)
import Safe (headMay)
import Hledger.Utils
import Hledger.Data.Types
import Hledger.Data.Amount
data ValuationType =
AtCost (Maybe CommoditySymbol)
| AtThen (Maybe CommoditySymbol)
| AtEnd (Maybe CommoditySymbol)
| AtNow (Maybe CommoditySymbol)
| AtDate Day (Maybe CommoditySymbol)
| AtDefault (Maybe CommoditySymbol)
deriving (Int -> ValuationType -> ShowS
[ValuationType] -> ShowS
ValuationType -> String
(Int -> ValuationType -> ShowS)
-> (ValuationType -> String)
-> ([ValuationType] -> ShowS)
-> Show ValuationType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ValuationType] -> ShowS
$cshowList :: [ValuationType] -> ShowS
show :: ValuationType -> String
$cshow :: ValuationType -> String
showsPrec :: Int -> ValuationType -> ShowS
$cshowsPrec :: Int -> ValuationType -> ShowS
Show,ValuationType -> ValuationType -> Bool
(ValuationType -> ValuationType -> Bool)
-> (ValuationType -> ValuationType -> Bool) -> Eq ValuationType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ValuationType -> ValuationType -> Bool
$c/= :: ValuationType -> ValuationType -> Bool
== :: ValuationType -> ValuationType -> Bool
$c== :: ValuationType -> ValuationType -> Bool
Eq)
data PriceGraph = PriceGraph {
PriceGraph -> Gr CommoditySymbol Quantity
prGraph :: Gr CommoditySymbol Quantity
,PriceGraph -> NodeMap CommoditySymbol
prNodemap :: NodeMap CommoditySymbol
,PriceGraph -> Map CommoditySymbol CommoditySymbol
prDefaultValuationCommodities :: M.Map CommoditySymbol CommoditySymbol
}
deriving (Int -> PriceGraph -> ShowS
[PriceGraph] -> ShowS
PriceGraph -> String
(Int -> PriceGraph -> ShowS)
-> (PriceGraph -> String)
-> ([PriceGraph] -> ShowS)
-> Show PriceGraph
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PriceGraph] -> ShowS
$cshowList :: [PriceGraph] -> ShowS
show :: PriceGraph -> String
$cshow :: PriceGraph -> String
showsPrec :: Int -> PriceGraph -> ShowS
$cshowsPrec :: Int -> PriceGraph -> ShowS
Show,(forall x. PriceGraph -> Rep PriceGraph x)
-> (forall x. Rep PriceGraph x -> PriceGraph) -> Generic PriceGraph
forall x. Rep PriceGraph x -> PriceGraph
forall x. PriceGraph -> Rep PriceGraph x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep PriceGraph x -> PriceGraph
$cfrom :: forall x. PriceGraph -> Rep PriceGraph x
Generic)
type PriceOracle = (Day, CommoditySymbol, Maybe CommoditySymbol) -> Maybe (CommoditySymbol, Quantity)
journalPriceOracle :: Bool -> Journal -> PriceOracle
journalPriceOracle :: Bool -> Journal -> PriceOracle
journalPriceOracle infer :: Bool
infer Journal{[PriceDirective]
jpricedirectives :: Journal -> [PriceDirective]
jpricedirectives :: [PriceDirective]
jpricedirectives, [MarketPrice]
jinferredmarketprices :: Journal -> [MarketPrice]
jinferredmarketprices :: [MarketPrice]
jinferredmarketprices} =
let
declaredprices :: [MarketPrice]
declaredprices = (PriceDirective -> MarketPrice)
-> [PriceDirective] -> [MarketPrice]
forall a b. (a -> b) -> [a] -> [b]
map PriceDirective -> MarketPrice
priceDirectiveToMarketPrice [PriceDirective]
jpricedirectives
inferredprices :: [MarketPrice]
inferredprices = if Bool
infer then [MarketPrice]
jinferredmarketprices else []
makepricegraph :: Day -> PriceGraph
makepricegraph = (Day -> PriceGraph) -> Day -> PriceGraph
forall a b. Ord a => (a -> b) -> a -> b
memo ((Day -> PriceGraph) -> Day -> PriceGraph)
-> (Day -> PriceGraph) -> Day -> PriceGraph
forall a b. (a -> b) -> a -> b
$ [MarketPrice] -> [MarketPrice] -> Day -> PriceGraph
makePriceGraph [MarketPrice]
declaredprices [MarketPrice]
inferredprices
in
PriceOracle -> PriceOracle
forall a b. Ord a => (a -> b) -> a -> b
memo (PriceOracle -> PriceOracle) -> PriceOracle -> PriceOracle
forall a b. (a -> b) -> a -> b
$ (Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity))
-> PriceOracle
forall a b c d. (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 ((Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity))
-> PriceOracle)
-> (Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity))
-> PriceOracle
forall a b. (a -> b) -> a -> b
$ (Day -> PriceGraph)
-> Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity)
priceLookup Day -> PriceGraph
makepricegraph
priceDirectiveToMarketPrice :: PriceDirective -> MarketPrice
priceDirectiveToMarketPrice :: PriceDirective -> MarketPrice
priceDirectiveToMarketPrice PriceDirective{..} =
MarketPrice :: Day
-> CommoditySymbol -> CommoditySymbol -> Quantity -> MarketPrice
MarketPrice{ mpdate :: Day
mpdate = Day
pddate
, mpfrom :: CommoditySymbol
mpfrom = CommoditySymbol
pdcommodity
, mpto :: CommoditySymbol
mpto = Amount -> CommoditySymbol
acommodity Amount
pdamount
, mprate :: Quantity
mprate = Amount -> Quantity
aquantity Amount
pdamount
}
mixedAmountApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Maybe Day -> Day -> Bool -> ValuationType -> MixedAmount -> MixedAmount
mixedAmountApplyValuation :: PriceOracle
-> Map CommoditySymbol AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> ValuationType
-> MixedAmount
-> MixedAmount
mixedAmountApplyValuation priceoracle :: PriceOracle
priceoracle styles :: Map CommoditySymbol AmountStyle
styles periodlast :: Day
periodlast mreportlast :: Maybe Day
mreportlast today :: Day
today ismultiperiod :: Bool
ismultiperiod v :: ValuationType
v (Mixed as :: [Amount]
as) =
[Amount] -> MixedAmount
Mixed ([Amount] -> MixedAmount) -> [Amount] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ (Amount -> Amount) -> [Amount] -> [Amount]
forall a b. (a -> b) -> [a] -> [b]
map (PriceOracle
-> Map CommoditySymbol AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> ValuationType
-> Amount
-> Amount
amountApplyValuation PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Day
periodlast Maybe Day
mreportlast Day
today Bool
ismultiperiod ValuationType
v) [Amount]
as
amountApplyValuation :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Day -> Maybe Day -> Day -> Bool -> ValuationType -> Amount -> Amount
amountApplyValuation :: PriceOracle
-> Map CommoditySymbol AmountStyle
-> Day
-> Maybe Day
-> Day
-> Bool
-> ValuationType
-> Amount
-> Amount
amountApplyValuation priceoracle :: PriceOracle
priceoracle styles :: Map CommoditySymbol AmountStyle
styles periodlast :: Day
periodlast mreportlast :: Maybe Day
mreportlast today :: Day
today ismultiperiod :: Bool
ismultiperiod v :: ValuationType
v a :: Amount
a =
case ValuationType
v of
AtCost Nothing -> Map CommoditySymbol AmountStyle -> Amount -> Amount
styleAmount Map CommoditySymbol AmountStyle
styles (Amount -> Amount) -> Amount -> Amount
forall a b. (a -> b) -> a -> b
$ Amount -> Amount
amountCost Amount
a
AtCost mc :: Maybe CommoditySymbol
mc -> PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc Day
periodlast (Amount -> Amount) -> Amount -> Amount
forall a b. (a -> b) -> a -> b
$ Map CommoditySymbol AmountStyle -> Amount -> Amount
styleAmount Map CommoditySymbol AmountStyle
styles (Amount -> Amount) -> Amount -> Amount
forall a b. (a -> b) -> a -> b
$ Amount -> Amount
amountCost Amount
a
AtThen _mc :: Maybe CommoditySymbol
_mc -> String -> Amount
forall a. String -> a
error' String
unsupportedValueThenError
AtEnd mc :: Maybe CommoditySymbol
mc -> PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc Day
periodlast Amount
a
AtNow mc :: Maybe CommoditySymbol
mc -> PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc Day
today Amount
a
AtDefault mc :: Maybe CommoditySymbol
mc | Bool
ismultiperiod -> PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc Day
periodlast Amount
a
AtDefault mc :: Maybe CommoditySymbol
mc -> PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc (Day -> Maybe Day -> Day
forall a. a -> Maybe a -> a
fromMaybe Day
today Maybe Day
mreportlast) Amount
a
AtDate d :: Day
d mc :: Maybe CommoditySymbol
mc -> PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc Day
d Amount
a
unsupportedValueThenError :: String
unsupportedValueThenError :: String
unsupportedValueThenError = "Sorry, --value=then is not yet implemented for this kind of report."
mixedAmountValueAtDate :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Maybe CommoditySymbol -> Day -> MixedAmount -> MixedAmount
mixedAmountValueAtDate :: PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> MixedAmount
-> MixedAmount
mixedAmountValueAtDate priceoracle :: PriceOracle
priceoracle styles :: Map CommoditySymbol AmountStyle
styles mc :: Maybe CommoditySymbol
mc d :: Day
d (Mixed as :: [Amount]
as) = [Amount] -> MixedAmount
Mixed ([Amount] -> MixedAmount) -> [Amount] -> MixedAmount
forall a b. (a -> b) -> a -> b
$ (Amount -> Amount) -> [Amount] -> [Amount]
forall a b. (a -> b) -> [a] -> [b]
map (PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate PriceOracle
priceoracle Map CommoditySymbol AmountStyle
styles Maybe CommoditySymbol
mc Day
d) [Amount]
as
amountValueAtDate :: PriceOracle -> M.Map CommoditySymbol AmountStyle -> Maybe CommoditySymbol -> Day -> Amount -> Amount
amountValueAtDate :: PriceOracle
-> Map CommoditySymbol AmountStyle
-> Maybe CommoditySymbol
-> Day
-> Amount
-> Amount
amountValueAtDate priceoracle :: PriceOracle
priceoracle styles :: Map CommoditySymbol AmountStyle
styles mto :: Maybe CommoditySymbol
mto d :: Day
d a :: Amount
a =
case PriceOracle
priceoracle (Day
d, Amount -> CommoditySymbol
acommodity Amount
a, Maybe CommoditySymbol
mto) of
Nothing -> Amount
a
Just (comm :: CommoditySymbol
comm, rate :: Quantity
rate) ->
Map CommoditySymbol AmountStyle -> Amount -> Amount
styleAmount Map CommoditySymbol AmountStyle
styles
Amount
amount{acommodity :: CommoditySymbol
acommodity=CommoditySymbol
comm, aquantity :: Quantity
aquantity=Quantity
rate Quantity -> Quantity -> Quantity
forall a. Num a => a -> a -> a
* Amount -> Quantity
aquantity Amount
a}
priceLookup :: (Day -> PriceGraph) -> Day -> CommoditySymbol -> Maybe CommoditySymbol -> Maybe (CommoditySymbol, Quantity)
priceLookup :: (Day -> PriceGraph)
-> Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity)
priceLookup makepricegraph :: Day -> PriceGraph
makepricegraph d :: Day
d from :: CommoditySymbol
from mto :: Maybe CommoditySymbol
mto =
let
PriceGraph{prGraph :: PriceGraph -> Gr CommoditySymbol Quantity
prGraph=Gr CommoditySymbol Quantity
g, prNodemap :: PriceGraph -> NodeMap CommoditySymbol
prNodemap=NodeMap CommoditySymbol
m, prDefaultValuationCommodities :: PriceGraph -> Map CommoditySymbol CommoditySymbol
prDefaultValuationCommodities=Map CommoditySymbol CommoditySymbol
defaultdests} =
Int -> String -> PriceGraph -> PriceGraph
forall a. Int -> String -> a -> a
traceAt 1 ("valuation date: "String -> ShowS
forall a. [a] -> [a] -> [a]
++Day -> String
forall a. Show a => a -> String
show Day
d) (PriceGraph -> PriceGraph) -> PriceGraph -> PriceGraph
forall a b. (a -> b) -> a -> b
$ Day -> PriceGraph
makepricegraph Day
d
fromnode :: Int
fromnode = NodeMap CommoditySymbol -> CommoditySymbol -> Int
forall a. Ord a => NodeMap a -> a -> Int
node NodeMap CommoditySymbol
m CommoditySymbol
from
mto' :: Maybe CommoditySymbol
mto' = Maybe CommoditySymbol
mto Maybe CommoditySymbol
-> Maybe CommoditySymbol -> Maybe CommoditySymbol
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Maybe CommoditySymbol
mdefaultto
where
mdefaultto :: Maybe CommoditySymbol
mdefaultto = String -> Maybe CommoditySymbol -> Maybe CommoditySymbol
forall a. Show a => String -> a -> a
dbg1 ("default valuation commodity for "String -> ShowS
forall a. [a] -> [a] -> [a]
++CommoditySymbol -> String
T.unpack CommoditySymbol
from) (Maybe CommoditySymbol -> Maybe CommoditySymbol)
-> Maybe CommoditySymbol -> Maybe CommoditySymbol
forall a b. (a -> b) -> a -> b
$
CommoditySymbol
-> Map CommoditySymbol CommoditySymbol -> Maybe CommoditySymbol
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup CommoditySymbol
from Map CommoditySymbol CommoditySymbol
defaultdests
in
case Maybe CommoditySymbol
mto' of
Nothing -> Maybe (CommoditySymbol, Quantity)
forall a. Maybe a
Nothing
Just to :: CommoditySymbol
to | CommoditySymbol
toCommoditySymbol -> CommoditySymbol -> Bool
forall a. Eq a => a -> a -> Bool
==CommoditySymbol
from -> Maybe (CommoditySymbol, Quantity)
forall a. Maybe a
Nothing
Just to :: CommoditySymbol
to ->
case Maybe Quantity
mindirectprice of
Nothing -> Maybe (CommoditySymbol, Quantity)
forall a. Maybe a
Nothing
Just q :: Quantity
q -> (CommoditySymbol, Quantity) -> Maybe (CommoditySymbol, Quantity)
forall a. a -> Maybe a
Just (CommoditySymbol
to, Quantity
q)
where
tonode :: Int
tonode = NodeMap CommoditySymbol -> CommoditySymbol -> Int
forall a. Ord a => NodeMap a -> a -> Int
node NodeMap CommoditySymbol
m CommoditySymbol
to
Maybe Quantity
mindirectprice :: Maybe Quantity =
case Int -> Int -> Gr CommoditySymbol Quantity -> Maybe Path
forall (gr :: * -> * -> *) b a.
(Graph gr, Real b) =>
Int -> Int -> gr a b -> Maybe Path
sp Int
fromnode Int
tonode Gr CommoditySymbol Quantity
g :: Maybe [Node] of
Nothing -> Maybe Quantity
forall a. Maybe a
Nothing
Just nodes :: Path
nodes ->
String -> Maybe Quantity -> Maybe Quantity
forall i.
(Integral i, Show i) =>
String -> Maybe (DecimalRaw i) -> Maybe (DecimalRaw i)
dbg ("market price for "String -> ShowS
forall a. [a] -> [a] -> [a]
++String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate " -> " ((CommoditySymbol -> String) -> [CommoditySymbol] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map CommoditySymbol -> String
T.unpack [CommoditySymbol]
comms)) (Maybe Quantity -> Maybe Quantity)
-> Maybe Quantity -> Maybe Quantity
forall a b. (a -> b) -> a -> b
$
Quantity -> Maybe Quantity
forall a. a -> Maybe a
Just (Quantity -> Maybe Quantity) -> Quantity -> Maybe Quantity
forall a b. (a -> b) -> a -> b
$ [Quantity] -> Quantity
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
product ([Quantity] -> Quantity) -> [Quantity] -> Quantity
forall a b. (a -> b) -> a -> b
$ Gr CommoditySymbol Quantity -> Path -> [Quantity]
forall b a. (Show b, Ord b) => Gr a b -> Path -> [b]
pathEdgeLabels Gr CommoditySymbol Quantity
g Path
nodes
where comms :: [CommoditySymbol]
comms = [Maybe CommoditySymbol] -> [CommoditySymbol]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe CommoditySymbol] -> [CommoditySymbol])
-> [Maybe CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ (Int -> Maybe CommoditySymbol) -> Path -> [Maybe CommoditySymbol]
forall a b. (a -> b) -> [a] -> [b]
map (Gr CommoditySymbol Quantity -> Int -> Maybe CommoditySymbol
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Int -> Maybe a
lab Gr CommoditySymbol Quantity
g) Path
nodes
dbg :: String -> Maybe (DecimalRaw i) -> Maybe (DecimalRaw i)
dbg msg :: String
msg = (Maybe (DecimalRaw i) -> String)
-> Maybe (DecimalRaw i) -> Maybe (DecimalRaw i)
forall a. Show a => (a -> String) -> a -> a
dbg1With (((String
msgString -> ShowS
forall a. [a] -> [a] -> [a]
++": ")String -> ShowS
forall a. [a] -> [a] -> [a]
++) ShowS
-> (Maybe (DecimalRaw i) -> String)
-> Maybe (DecimalRaw i)
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String
-> (DecimalRaw i -> String) -> Maybe (DecimalRaw i) -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe "" (DecimalRaw i -> String
forall a. Show a => a -> String
show (DecimalRaw i -> String)
-> (DecimalRaw i -> DecimalRaw i) -> DecimalRaw i -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> DecimalRaw i -> DecimalRaw i
forall i. Integral i => Word8 -> DecimalRaw i -> DecimalRaw i
roundTo 8))
tests_priceLookup :: TestTree
tests_priceLookup =
let
p :: Integer
-> Int
-> Int
-> CommoditySymbol
-> Quantity
-> CommoditySymbol
-> MarketPrice
p y :: Integer
y m :: Int
m d :: Int
d from :: CommoditySymbol
from q :: Quantity
q to :: CommoditySymbol
to = MarketPrice :: Day
-> CommoditySymbol -> CommoditySymbol -> Quantity -> MarketPrice
MarketPrice{mpdate :: Day
mpdate=Integer -> Int -> Int -> Day
fromGregorian Integer
y Int
m Int
d, mpfrom :: CommoditySymbol
mpfrom=CommoditySymbol
from, mpto :: CommoditySymbol
mpto=CommoditySymbol
to, mprate :: Quantity
mprate=Quantity
q}
ps1 :: [MarketPrice]
ps1 = [
Integer
-> Int
-> Int
-> CommoditySymbol
-> Quantity
-> CommoditySymbol
-> MarketPrice
p 2000 01 01 "A" 10 "B"
,Integer
-> Int
-> Int
-> CommoditySymbol
-> Quantity
-> CommoditySymbol
-> MarketPrice
p 2000 01 01 "B" 10 "C"
,Integer
-> Int
-> Int
-> CommoditySymbol
-> Quantity
-> CommoditySymbol
-> MarketPrice
p 2000 01 01 "C" 10 "D"
,Integer
-> Int
-> Int
-> CommoditySymbol
-> Quantity
-> CommoditySymbol
-> MarketPrice
p 2000 01 01 "E" 2 "D"
,Integer
-> Int
-> Int
-> CommoditySymbol
-> Quantity
-> CommoditySymbol
-> MarketPrice
p 2001 01 01 "A" 11 "B"
]
makepricegraph :: Day -> PriceGraph
makepricegraph = [MarketPrice] -> [MarketPrice] -> Day -> PriceGraph
makePriceGraph [MarketPrice]
ps1 []
in String -> Assertion -> TestTree
test "priceLookup" (Assertion -> TestTree) -> Assertion -> TestTree
forall a b. (a -> b) -> a -> b
$ do
(Day -> PriceGraph)
-> Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity)
priceLookup Day -> PriceGraph
makepricegraph (Integer -> Int -> Int -> Day
fromGregorian 1999 01 01) "A" Maybe CommoditySymbol
forall a. Maybe a
Nothing Maybe (CommoditySymbol, Quantity)
-> Maybe (CommoditySymbol, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= Maybe (CommoditySymbol, Quantity)
forall a. Maybe a
Nothing
(Day -> PriceGraph)
-> Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity)
priceLookup Day -> PriceGraph
makepricegraph (Integer -> Int -> Int -> Day
fromGregorian 2000 01 01) "A" Maybe CommoditySymbol
forall a. Maybe a
Nothing Maybe (CommoditySymbol, Quantity)
-> Maybe (CommoditySymbol, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (CommoditySymbol, Quantity) -> Maybe (CommoditySymbol, Quantity)
forall a. a -> Maybe a
Just ("B",10)
(Day -> PriceGraph)
-> Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity)
priceLookup Day -> PriceGraph
makepricegraph (Integer -> Int -> Int -> Day
fromGregorian 2000 01 01) "B" (CommoditySymbol -> Maybe CommoditySymbol
forall a. a -> Maybe a
Just "A") Maybe (CommoditySymbol, Quantity)
-> Maybe (CommoditySymbol, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (CommoditySymbol, Quantity) -> Maybe (CommoditySymbol, Quantity)
forall a. a -> Maybe a
Just ("A",0.1)
(Day -> PriceGraph)
-> Day
-> CommoditySymbol
-> Maybe CommoditySymbol
-> Maybe (CommoditySymbol, Quantity)
priceLookup Day -> PriceGraph
makepricegraph (Integer -> Int -> Int -> Day
fromGregorian 2000 01 01) "A" (CommoditySymbol -> Maybe CommoditySymbol
forall a. a -> Maybe a
Just "E") Maybe (CommoditySymbol, Quantity)
-> Maybe (CommoditySymbol, Quantity) -> Assertion
forall a. (Eq a, Show a, HasCallStack) => a -> a -> Assertion
@?= (CommoditySymbol, Quantity) -> Maybe (CommoditySymbol, Quantity)
forall a. a -> Maybe a
Just ("E",500)
makePriceGraph :: [MarketPrice] -> [MarketPrice] -> Day -> PriceGraph
makePriceGraph :: [MarketPrice] -> [MarketPrice] -> Day -> PriceGraph
makePriceGraph alldeclaredprices :: [MarketPrice]
alldeclaredprices allinferredprices :: [MarketPrice]
allinferredprices d :: Day
d =
String -> PriceGraph -> PriceGraph
forall a. Show a => String -> a -> a
dbg9 ("makePriceGraph "String -> ShowS
forall a. [a] -> [a] -> [a]
++Day -> String
forall a. Show a => a -> String
show Day
d) (PriceGraph -> PriceGraph) -> PriceGraph -> PriceGraph
forall a b. (a -> b) -> a -> b
$
PriceGraph :: Gr CommoditySymbol Quantity
-> NodeMap CommoditySymbol
-> Map CommoditySymbol CommoditySymbol
-> PriceGraph
PriceGraph{prGraph :: Gr CommoditySymbol Quantity
prGraph=Gr CommoditySymbol Quantity
g, prNodemap :: NodeMap CommoditySymbol
prNodemap=NodeMap CommoditySymbol
m, prDefaultValuationCommodities :: Map CommoditySymbol CommoditySymbol
prDefaultValuationCommodities=Map CommoditySymbol CommoditySymbol
defaultdests}
where
visibledeclaredprices :: [MarketPrice]
visibledeclaredprices = (MarketPrice -> Bool) -> [MarketPrice] -> [MarketPrice]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Day -> Day -> Bool
forall a. Ord a => a -> a -> Bool
<=Day
d)(Day -> Bool) -> (MarketPrice -> Day) -> MarketPrice -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.MarketPrice -> Day
mpdate) [MarketPrice]
alldeclaredprices
visibleinferredprices :: [MarketPrice]
visibleinferredprices = (MarketPrice -> Bool) -> [MarketPrice] -> [MarketPrice]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Day -> Day -> Bool
forall a. Ord a => a -> a -> Bool
<=Day
d)(Day -> Bool) -> (MarketPrice -> Day) -> MarketPrice -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
.MarketPrice -> Day
mpdate) [MarketPrice]
allinferredprices
declaredandinferredprices :: [MarketPrice]
declaredandinferredprices = String -> [MarketPrice] -> [MarketPrice]
forall a. Show a => String -> a -> a
dbg2 "declaredandinferredprices" ([MarketPrice] -> [MarketPrice]) -> [MarketPrice] -> [MarketPrice]
forall a b. (a -> b) -> a -> b
$
[MarketPrice] -> [MarketPrice] -> [MarketPrice]
effectiveMarketPrices [MarketPrice]
visibledeclaredprices [MarketPrice]
visibleinferredprices
reverseprices :: [MarketPrice]
reverseprices = String -> [MarketPrice] -> [MarketPrice]
forall a. Show a => String -> a -> a
dbg2 "reverseprices" ([MarketPrice] -> [MarketPrice]) -> [MarketPrice] -> [MarketPrice]
forall a b. (a -> b) -> a -> b
$
(MarketPrice -> MarketPrice) -> [MarketPrice] -> [MarketPrice]
forall a b. (a -> b) -> [a] -> [b]
map MarketPrice -> MarketPrice
marketPriceReverse [MarketPrice]
declaredandinferredprices [MarketPrice] -> [MarketPrice] -> [MarketPrice]
forall a. Eq a => [a] -> [a] -> [a]
\\ [MarketPrice]
declaredandinferredprices
(g :: Gr CommoditySymbol Quantity
g, m :: NodeMap CommoditySymbol
m) =
[CommoditySymbol]
-> [(CommoditySymbol, CommoditySymbol, Quantity)]
-> (Gr CommoditySymbol Quantity, NodeMap CommoditySymbol)
forall a (g :: * -> * -> *) b.
(Ord a, DynGraph g) =>
[a] -> [(a, a, b)] -> (g a b, NodeMap a)
mkMapGraph
(String -> [CommoditySymbol] -> [CommoditySymbol]
forall a. Show a => String -> a -> a
dbg9 "price graph labels" ([CommoditySymbol] -> [CommoditySymbol])
-> [CommoditySymbol] -> [CommoditySymbol]
forall a b. (a -> b) -> a -> b
$ [CommoditySymbol] -> [CommoditySymbol]
forall a. Ord a => [a] -> [a]
sort [CommoditySymbol]
allcomms)
(String
-> [(CommoditySymbol, CommoditySymbol, Quantity)]
-> [(CommoditySymbol, CommoditySymbol, Quantity)]
forall a. Show a => String -> a -> a
dbg9 "price graph edges" ([(CommoditySymbol, CommoditySymbol, Quantity)]
-> [(CommoditySymbol, CommoditySymbol, Quantity)])
-> [(CommoditySymbol, CommoditySymbol, Quantity)]
-> [(CommoditySymbol, CommoditySymbol, Quantity)]
forall a b. (a -> b) -> a -> b
$ [(CommoditySymbol
mpfrom, CommoditySymbol
mpto, Quantity
mprate) | MarketPrice{..} <- [MarketPrice]
prices])
:: (Gr CommoditySymbol Quantity, NodeMap CommoditySymbol)
where
prices :: [MarketPrice]
prices = [MarketPrice]
declaredandinferredprices [MarketPrice] -> [MarketPrice] -> [MarketPrice]
forall a. [a] -> [a] -> [a]
++ [MarketPrice]
reverseprices
allcomms :: [CommoditySymbol]
allcomms = (MarketPrice -> CommoditySymbol)
-> [MarketPrice] -> [CommoditySymbol]
forall a b. (a -> b) -> [a] -> [b]
map MarketPrice -> CommoditySymbol
mpfrom [MarketPrice]
prices
defaultdests :: Map CommoditySymbol CommoditySymbol
defaultdests = [(CommoditySymbol, CommoditySymbol)]
-> Map CommoditySymbol CommoditySymbol
forall k a. Ord k => [(k, a)] -> Map k a
M.fromList [(CommoditySymbol
mpfrom,CommoditySymbol
mpto) | MarketPrice{..} <- [MarketPrice]
pricesfordefaultcomms]
where
pricesfordefaultcomms :: [MarketPrice]
pricesfordefaultcomms = String -> [MarketPrice] -> [MarketPrice]
forall a. Show a => String -> a -> a
dbg2 "prices for choosing default valuation commodities, by date then parse order" ([MarketPrice] -> [MarketPrice]) -> [MarketPrice] -> [MarketPrice]
forall a b. (a -> b) -> a -> b
$
[MarketPrice]
ps
[MarketPrice]
-> ([MarketPrice] -> [(Integer, MarketPrice)])
-> [(Integer, MarketPrice)]
forall a b. a -> (a -> b) -> b
& [Integer] -> [MarketPrice] -> [(Integer, MarketPrice)]
forall a b. [a] -> [b] -> [(a, b)]
zip [1..]
[(Integer, MarketPrice)]
-> ([(Integer, MarketPrice)] -> [(Integer, MarketPrice)])
-> [(Integer, MarketPrice)]
forall a b. a -> (a -> b) -> b
& ((Integer, MarketPrice) -> (Integer, MarketPrice) -> Ordering)
-> [(Integer, MarketPrice)] -> [(Integer, MarketPrice)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy ((Day, Integer) -> (Day, Integer) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ((Day, Integer) -> (Day, Integer) -> Ordering)
-> ((Integer, MarketPrice) -> (Day, Integer))
-> (Integer, MarketPrice)
-> (Integer, MarketPrice)
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (\(parseorder :: Integer
parseorder,MarketPrice{..})->(Day
mpdate,Integer
parseorder)))
[(Integer, MarketPrice)]
-> ([(Integer, MarketPrice)] -> [MarketPrice]) -> [MarketPrice]
forall a b. a -> (a -> b) -> b
& ((Integer, MarketPrice) -> MarketPrice)
-> [(Integer, MarketPrice)] -> [MarketPrice]
forall a b. (a -> b) -> [a] -> [b]
map (Integer, MarketPrice) -> MarketPrice
forall a b. (a, b) -> b
snd
where
ps :: [MarketPrice]
ps | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [MarketPrice] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [MarketPrice]
visibledeclaredprices = [MarketPrice]
visibledeclaredprices
| Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [MarketPrice] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [MarketPrice]
alldeclaredprices = [MarketPrice]
alldeclaredprices
| Bool
otherwise = [MarketPrice]
visibleinferredprices
effectiveMarketPrices :: [MarketPrice] -> [MarketPrice] -> [MarketPrice]
effectiveMarketPrices :: [MarketPrice] -> [MarketPrice] -> [MarketPrice]
effectiveMarketPrices declaredprices :: [MarketPrice]
declaredprices inferredprices :: [MarketPrice]
inferredprices =
let
declaredprices' :: [(Integer, Integer, MarketPrice)]
declaredprices' = [(1, Integer
i, MarketPrice
p) | (i :: Integer
i,p :: MarketPrice
p) <- [Integer] -> [MarketPrice] -> [(Integer, MarketPrice)]
forall a b. [a] -> [b] -> [(a, b)]
zip [1..] [MarketPrice]
declaredprices]
inferredprices' :: [(Integer, Integer, MarketPrice)]
inferredprices' = [(0, Integer
i, MarketPrice
p) | (i :: Integer
i,p :: MarketPrice
p) <- [Integer] -> [MarketPrice] -> [(Integer, MarketPrice)]
forall a b. [a] -> [b] -> [(a, b)]
zip [1..] [MarketPrice]
inferredprices]
in
[(Integer, Integer, MarketPrice)]
declaredprices' [(Integer, Integer, MarketPrice)]
-> [(Integer, Integer, MarketPrice)]
-> [(Integer, Integer, MarketPrice)]
forall a. [a] -> [a] -> [a]
++ [(Integer, Integer, MarketPrice)]
inferredprices'
[(Integer, Integer, MarketPrice)]
-> ([(Integer, Integer, MarketPrice)]
-> [(Integer, Integer, MarketPrice)])
-> [(Integer, Integer, MarketPrice)]
forall a b. a -> (a -> b) -> b
& ((Integer, Integer, MarketPrice)
-> (Integer, Integer, MarketPrice) -> Ordering)
-> [(Integer, Integer, MarketPrice)]
-> [(Integer, Integer, MarketPrice)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (((Day, Integer, Integer) -> (Day, Integer, Integer) -> Ordering)
-> (Day, Integer, Integer) -> (Day, Integer, Integer) -> Ordering
forall a b c. (a -> b -> c) -> b -> a -> c
flip (Day, Integer, Integer) -> (Day, Integer, Integer) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ((Day, Integer, Integer) -> (Day, Integer, Integer) -> Ordering)
-> ((Integer, Integer, MarketPrice) -> (Day, Integer, Integer))
-> (Integer, Integer, MarketPrice)
-> (Integer, Integer, MarketPrice)
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (\(precedence :: Integer
precedence,parseorder :: Integer
parseorder,mp :: MarketPrice
mp)->(MarketPrice -> Day
mpdate MarketPrice
mp,Integer
precedence,Integer
parseorder)))
[(Integer, Integer, MarketPrice)]
-> ([(Integer, Integer, MarketPrice)] -> [MarketPrice])
-> [MarketPrice]
forall a b. a -> (a -> b) -> b
& ((Integer, Integer, MarketPrice) -> MarketPrice)
-> [(Integer, Integer, MarketPrice)] -> [MarketPrice]
forall a b. (a -> b) -> [a] -> [b]
map (Integer, Integer, MarketPrice) -> MarketPrice
forall a b c. (a, b, c) -> c
third3
[MarketPrice] -> ([MarketPrice] -> [MarketPrice]) -> [MarketPrice]
forall a b. a -> (a -> b) -> b
& (MarketPrice -> MarketPrice -> Ordering)
-> [MarketPrice] -> [MarketPrice]
forall a. (a -> a -> Ordering) -> [a] -> [a]
nubSortBy ((CommoditySymbol, CommoditySymbol)
-> (CommoditySymbol, CommoditySymbol) -> Ordering
forall a. Ord a => a -> a -> Ordering
compare ((CommoditySymbol, CommoditySymbol)
-> (CommoditySymbol, CommoditySymbol) -> Ordering)
-> (MarketPrice -> (CommoditySymbol, CommoditySymbol))
-> MarketPrice
-> MarketPrice
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (\(MarketPrice{..})->(CommoditySymbol
mpfrom,CommoditySymbol
mpto)))
marketPriceReverse :: MarketPrice -> MarketPrice
marketPriceReverse :: MarketPrice -> MarketPrice
marketPriceReverse mp :: MarketPrice
mp@MarketPrice{..} = MarketPrice
mp{mpfrom :: CommoditySymbol
mpfrom=CommoditySymbol
mpto, mpto :: CommoditySymbol
mpto=CommoditySymbol
mpfrom, mprate :: Quantity
mprate=1Quantity -> Quantity -> Quantity
forall a. Fractional a => a -> a -> a
/Quantity
mprate}
node :: Ord a => NodeMap a -> a -> Node
node :: NodeMap a -> a -> Int
node m :: NodeMap a
m = (Int, a) -> Int
forall a b. (a, b) -> a
fst ((Int, a) -> Int) -> (a -> (Int, a)) -> a -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, a), NodeMap a) -> (Int, a)
forall a b. (a, b) -> a
fst (((Int, a), NodeMap a) -> (Int, a))
-> (a -> ((Int, a), NodeMap a)) -> a -> (Int, a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. NodeMap a -> a -> ((Int, a), NodeMap a)
forall a. Ord a => NodeMap a -> a -> (LNode a, NodeMap a)
mkNode NodeMap a
m
pathEdgeLabels :: (Show b, Ord b) => Gr a b -> [Node] -> [b]
pathEdgeLabels :: Gr a b -> Path -> [b]
pathEdgeLabels g :: Gr a b
g = (Maybe b -> b) -> [Maybe b] -> [b]
forall a b. (a -> b) -> [a] -> [b]
map Maybe b -> b
forall a. Maybe a -> a
frommaybe ([Maybe b] -> [b]) -> (Path -> [Maybe b]) -> Path -> [b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Int, Int) -> Maybe b) -> [(Int, Int)] -> [Maybe b]
forall a b. (a -> b) -> [a] -> [b]
map (Gr a b -> (Int, Int) -> Maybe b
forall b a. Ord b => Gr a b -> (Int, Int) -> Maybe b
nodesEdgeLabel Gr a b
g) ([(Int, Int)] -> [Maybe b])
-> (Path -> [(Int, Int)]) -> Path -> [Maybe b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Path -> [(Int, Int)]
pathEdges
where frommaybe :: Maybe a -> a
frommaybe = a -> Maybe a -> a
forall a. a -> Maybe a -> a
fromMaybe (String -> a
forall a. String -> a
error' "pathEdgeLabels: expected no Nothings here")
pathEdges :: [Node] -> [(Node,Node)]
pathEdges :: Path -> [(Int, Int)]
pathEdges p :: Path
p = [(Int
f,Int
t) | f :: Int
f:t :: Int
t:_ <- Path -> [Path]
forall a. [a] -> [[a]]
tails Path
p]
nodesEdgeLabel :: Ord b => Gr a b -> (Node, Node) -> Maybe b
nodesEdgeLabel :: Gr a b -> (Int, Int) -> Maybe b
nodesEdgeLabel g :: Gr a b
g (from :: Int
from,to :: Int
to) = [b] -> Maybe b
forall a. [a] -> Maybe a
headMay ([b] -> Maybe b) -> [b] -> Maybe b
forall a b. (a -> b) -> a -> b
$ [b] -> [b]
forall a. Ord a => [a] -> [a]
sort [b
l | (_,t :: Int
t,l :: b
l) <- Gr a b -> Int -> [(Int, Int, b)]
forall (gr :: * -> * -> *) a b.
Graph gr =>
gr a b -> Int -> [LEdge b]
out Gr a b
g Int
from, Int
tInt -> Int -> Bool
forall a. Eq a => a -> a -> Bool
==Int
to]
tests_Valuation :: TestTree
tests_Valuation = String -> [TestTree] -> TestTree
tests "Valuation" [
TestTree
tests_priceLookup
]