;valuation: refactor, update, clean up
This commit is contained in:
		
							parent
							
								
									e143ad2695
								
							
						
					
					
						commit
						467210c796
					
				| @ -186,7 +186,7 @@ instance Semigroup Journal where | |||||||
|     ,jcommodities               = jcommodities               j1 <> jcommodities               j2 |     ,jcommodities               = jcommodities               j1 <> jcommodities               j2 | ||||||
|     ,jinferredcommodities       = jinferredcommodities       j1 <> jinferredcommodities       j2 |     ,jinferredcommodities       = jinferredcommodities       j1 <> jinferredcommodities       j2 | ||||||
|     ,jpricedirectives           = jpricedirectives           j1 <> jpricedirectives           j2 |     ,jpricedirectives           = jpricedirectives           j1 <> jpricedirectives           j2 | ||||||
|     ,jimpliedmarketprices       = jimpliedmarketprices       j1 <> jimpliedmarketprices       j2 |     ,jinferredmarketprices      = jinferredmarketprices      j1 <> jinferredmarketprices      j2 | ||||||
|     ,jtxnmodifiers              = jtxnmodifiers              j1 <> jtxnmodifiers              j2 |     ,jtxnmodifiers              = jtxnmodifiers              j1 <> jtxnmodifiers              j2 | ||||||
|     ,jperiodictxns              = jperiodictxns              j1 <> jperiodictxns              j2 |     ,jperiodictxns              = jperiodictxns              j1 <> jperiodictxns              j2 | ||||||
|     ,jtxns                      = jtxns                      j1 <> jtxns                      j2 |     ,jtxns                      = jtxns                      j1 <> jtxns                      j2 | ||||||
| @ -212,7 +212,7 @@ nulljournal = Journal { | |||||||
|   ,jcommodities               = M.empty |   ,jcommodities               = M.empty | ||||||
|   ,jinferredcommodities       = M.empty |   ,jinferredcommodities       = M.empty | ||||||
|   ,jpricedirectives           = [] |   ,jpricedirectives           = [] | ||||||
|   ,jimpliedmarketprices       = [] |   ,jinferredmarketprices      = [] | ||||||
|   ,jtxnmodifiers              = [] |   ,jtxnmodifiers              = [] | ||||||
|   ,jperiodictxns              = [] |   ,jperiodictxns              = [] | ||||||
|   ,jtxns                      = [] |   ,jtxns                      = [] | ||||||
| @ -1044,16 +1044,16 @@ canonicalStyleFrom ss@(s:_) = | |||||||
| -- been balanced and posting amounts have appropriate prices attached. | -- been balanced and posting amounts have appropriate prices attached. | ||||||
| journalInferMarketPricesFromTransactions :: Journal -> Journal | journalInferMarketPricesFromTransactions :: Journal -> Journal | ||||||
| journalInferMarketPricesFromTransactions j = | journalInferMarketPricesFromTransactions j = | ||||||
|   j{jimpliedmarketprices = |   j{jinferredmarketprices = | ||||||
|        dbg4 "jimpliedmarketprices" $ |        dbg4 "jinferredmarketprices" $ | ||||||
|        mapMaybe postingImpliedMarketPrice $ journalPostings j |        mapMaybe postingInferredmarketPrice $ journalPostings j | ||||||
|    } |    } | ||||||
| 
 | 
 | ||||||
| -- | Make a market price equivalent to this posting's amount's unit | -- | Make a market price equivalent to this posting's amount's unit | ||||||
| -- price, if any. If the posting amount is multicommodity, only the | -- price, if any. If the posting amount is multicommodity, only the | ||||||
| -- first commodity amount is considered. | -- first commodity amount is considered. | ||||||
| postingImpliedMarketPrice :: Posting -> Maybe MarketPrice | postingInferredmarketPrice :: Posting -> Maybe MarketPrice | ||||||
| postingImpliedMarketPrice p@Posting{pamount} = | postingInferredmarketPrice p@Posting{pamount} = | ||||||
|   -- convert any total prices to unit prices |   -- convert any total prices to unit prices | ||||||
|   case mixedAmountTotalPriceToUnitPrice pamount of |   case mixedAmountTotalPriceToUnitPrice pamount of | ||||||
|     Mixed ( Amount{acommodity=fromcomm, aprice = Just (UnitPrice Amount{acommodity=tocomm, aquantity=rate})} : _) -> |     Mixed ( Amount{acommodity=fromcomm, aprice = Just (UnitPrice Amount{acommodity=tocomm, aquantity=rate})} : _) -> | ||||||
|  | |||||||
| @ -470,9 +470,9 @@ data Journal = Journal { | |||||||
|   ,jdeclaredaccounts      :: [(AccountName,AccountDeclarationInfo)] -- ^ Accounts declared by account directives, in parse order (after journal finalisation) |   ,jdeclaredaccounts      :: [(AccountName,AccountDeclarationInfo)] -- ^ Accounts declared by account directives, in parse order (after journal finalisation) | ||||||
|   ,jdeclaredaccounttypes  :: M.Map AccountType [AccountName]        -- ^ Accounts whose type has been declared in account directives (usually 5 top-level accounts) |   ,jdeclaredaccounttypes  :: M.Map AccountType [AccountName]        -- ^ Accounts whose type has been declared in account directives (usually 5 top-level accounts) | ||||||
|   ,jcommodities           :: M.Map CommoditySymbol Commodity        -- ^ commodities and formats declared by commodity directives |   ,jcommodities           :: M.Map CommoditySymbol Commodity        -- ^ commodities and formats declared by commodity directives | ||||||
|   ,jinferredcommodities   :: M.Map CommoditySymbol AmountStyle      -- ^ commodities and formats inferred from journal amounts  TODO misnamed - jusedstyles |   ,jinferredcommodities   :: M.Map CommoditySymbol AmountStyle      -- ^ commodities and formats inferred from journal amounts  TODO misnamed, should be eg jusedstyles | ||||||
|   ,jpricedirectives       :: [PriceDirective]                       -- ^ Declarations of market prices by P directives, in parse order (after journal finalisation) |   ,jpricedirectives       :: [PriceDirective]                       -- ^ Declarations of market prices by P directives, in parse order (after journal finalisation) | ||||||
|   ,jimpliedmarketprices   :: [MarketPrice]                          -- ^ Market prices implied by transactions, in parse order (after journal finalisation) |   ,jinferredmarketprices  :: [MarketPrice]                          -- ^ Market prices implied by transactions, in parse order (after journal finalisation) | ||||||
|   ,jtxnmodifiers          :: [TransactionModifier] |   ,jtxnmodifiers          :: [TransactionModifier] | ||||||
|   ,jperiodictxns          :: [PeriodicTransaction] |   ,jperiodictxns          :: [PeriodicTransaction] | ||||||
|   ,jtxns                  :: [Transaction] |   ,jtxns                  :: [Transaction] | ||||||
|  | |||||||
| @ -52,38 +52,6 @@ import Hledger.Data.Dates (parsedate) | |||||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ||||||
| -- Types | -- Types | ||||||
| 
 | 
 | ||||||
| -- | A snapshot of the known exchange rates between commodity pairs at a given date, |  | ||||||
| -- as a graph allowing fast lookup and path finding, along with some helper data. |  | ||||||
| data PriceGraph = PriceGraph { |  | ||||||
|    prGraph   :: Gr CommoditySymbol Quantity |  | ||||||
|     -- ^ A directed graph of exchange rates between commodity pairs. |  | ||||||
|     -- Node labels are commodities and edge labels are exchange rates, |  | ||||||
|     -- which were either: |  | ||||||
|     -- declared by P directives, |  | ||||||
|     -- implied by transaction prices, |  | ||||||
|     -- inferred by reversing a declared rate, |  | ||||||
|     -- or inferred by reversing a transaction-implied rate. |  | ||||||
|     -- There will be at most one edge between each directed pair of commodities, |  | ||||||
|     -- eg there can be one USD->EUR and one EUR->USD. |  | ||||||
|   ,prNodemap :: NodeMap CommoditySymbol |  | ||||||
|     -- ^ Mapping of graph node ids to commodity symbols. |  | ||||||
|   ,prDefaultValuationCommodities :: M.Map CommoditySymbol CommoditySymbol |  | ||||||
|     -- ^ The default valuation commodity for each source commodity. |  | ||||||
|     --   These are used when a valuation commodity is not specified |  | ||||||
|     --   (-V). They are the destination commodity of the latest |  | ||||||
|     --   (declared or transaction-implied, but not reverse) each |  | ||||||
|     --   source commodity's latest market price (on the date of this |  | ||||||
|     --   graph). |  | ||||||
|   } |  | ||||||
|   deriving (Show,Generic) |  | ||||||
| 
 |  | ||||||
| instance NFData PriceGraph |  | ||||||
| 
 |  | ||||||
| -- | A price oracle is a magic function that looks up market prices |  | ||||||
| -- (exchange rates) from one commodity to another (or if unspecified, |  | ||||||
| -- to a default valuation commodity) on a given date, somewhat efficiently. |  | ||||||
| type PriceOracle = (Day, CommoditySymbol, Maybe CommoditySymbol) -> Maybe (CommoditySymbol, Quantity) |  | ||||||
| 
 |  | ||||||
| -- | What kind of value conversion should be done on amounts ? | -- | What kind of value conversion should be done on amounts ? | ||||||
| -- CLI: --value=cost|then|end|now|DATE[,COMM] | -- CLI: --value=cost|then|end|now|DATE[,COMM] | ||||||
| data ValuationType = | data ValuationType = | ||||||
| @ -95,9 +63,62 @@ data ValuationType = | |||||||
|   | AtDefault  (Maybe CommoditySymbol)  -- ^ works like AtNow in single period reports, like AtEnd in multiperiod reports |   | AtDefault  (Maybe CommoditySymbol)  -- ^ works like AtNow in single period reports, like AtEnd in multiperiod reports | ||||||
|   deriving (Show,Data,Eq) -- Typeable |   deriving (Show,Data,Eq) -- Typeable | ||||||
| 
 | 
 | ||||||
|  | -- | A snapshot of the known exchange rates between commodity pairs at a given date, | ||||||
|  | -- as a graph allowing fast lookup and path finding, along with some helper data. | ||||||
|  | data PriceGraph = PriceGraph { | ||||||
|  |    prGraph   :: Gr CommoditySymbol Quantity | ||||||
|  |     -- ^ A directed graph of exchange rates between commodity pairs. | ||||||
|  |     -- Node labels are commodities and edge labels are exchange rates, | ||||||
|  |     -- which were either: | ||||||
|  |     -- declared by P directives, | ||||||
|  |     -- inferred from transaction prices, | ||||||
|  |     -- inferred by reversing a declared rate, | ||||||
|  |     -- or inferred by reversing a transaction-inferred rate. | ||||||
|  |     -- There will be at most one edge between each directed pair of commodities, | ||||||
|  |     -- eg there can be one USD->EUR and one EUR->USD. | ||||||
|  |   ,prNodemap :: NodeMap CommoditySymbol | ||||||
|  |     -- ^ Mapping of graph node ids to commodity symbols. | ||||||
|  |   ,prDefaultValuationCommodities :: M.Map CommoditySymbol CommoditySymbol | ||||||
|  |     -- ^ The default valuation commodity for each source commodity. | ||||||
|  |     --   These are used when a valuation commodity is not specified | ||||||
|  |     --   (-V). They are the destination commodity of the latest | ||||||
|  |     --   (declared or inferred, but not reverse) each | ||||||
|  |     --   source commodity's latest market price (on the date of this | ||||||
|  |     --   graph). | ||||||
|  |   } | ||||||
|  |   deriving (Show,Generic) | ||||||
|  | 
 | ||||||
|  | instance NFData PriceGraph | ||||||
|  | 
 | ||||||
|  | -- | A price oracle is a magic memoising function that efficiently | ||||||
|  | -- looks up market prices (exchange rates) from one commodity to | ||||||
|  | -- another (or if unspecified, to a default valuation commodity) on a | ||||||
|  | -- given date. | ||||||
|  | type PriceOracle = (Day, CommoditySymbol, Maybe CommoditySymbol) -> Maybe (CommoditySymbol, Quantity) | ||||||
|  | 
 | ||||||
|  | -- | Generate a price oracle (memoising price lookup function) from a | ||||||
|  | -- journal's directive-declared and transaction-inferred market | ||||||
|  | -- prices. For best performance, generate this only once per journal, | ||||||
|  | -- reusing it across reports if there are more than one, as | ||||||
|  | -- compoundBalanceCommand does. | ||||||
|  | journalPriceOracle :: Journal -> PriceOracle | ||||||
|  | journalPriceOracle Journal{jpricedirectives, jinferredmarketprices} = | ||||||
|  |   let | ||||||
|  |     declaredprices = map priceDirectiveToMarketPrice jpricedirectives | ||||||
|  |     makepricegraph = memo $ makePriceGraph declaredprices jinferredmarketprices | ||||||
|  |   in | ||||||
|  |     memo $ uncurry3 $ priceLookup makepricegraph | ||||||
|  | 
 | ||||||
|  | priceDirectiveToMarketPrice :: PriceDirective -> MarketPrice | ||||||
|  | priceDirectiveToMarketPrice PriceDirective{..} = | ||||||
|  |   MarketPrice{ mpdate = pddate | ||||||
|  |              , mpfrom = pdcommodity | ||||||
|  |              , mpto   = acommodity pdamount | ||||||
|  |              , mprate = aquantity pdamount | ||||||
|  |              } | ||||||
| 
 | 
 | ||||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ||||||
| -- Valuation | -- Converting things to value | ||||||
| 
 | 
 | ||||||
| -- | Apply a specified valuation to this mixed amount, using the | -- | Apply a specified valuation to this mixed amount, using the | ||||||
| -- provided price oracle, commodity styles, reference dates, and | -- provided price oracle, commodity styles, reference dates, and | ||||||
| @ -189,72 +210,28 @@ amountValueAtDate priceoracle styles mto d a = | |||||||
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | ||||||
| -- Market price lookup | -- Market price lookup | ||||||
| 
 | 
 | ||||||
| -- From a journal's directive-declared and transaction-implied market | -- | Given a memoising price graph generator, a valuation date, a | ||||||
| -- prices, generate a memoising function that efficiently looks up | -- source commodity and an optional valuation commodity, find the | ||||||
| -- exchange rates between commodities on any date. For best performance, | -- value on that date of one unit of the source commodity in the | ||||||
| -- you should generate this only once per journal, reusing it across | -- valuation commodity, or in a default valuation commodity. Returns | ||||||
| -- reports if there are more than one (as in compoundBalanceCommand). | -- the valuation commodity that was specified or chosen, and the | ||||||
| journalPriceOracle :: Journal -> PriceOracle | -- quantity of it that one unit of the source commodity is worth. Or | ||||||
| journalPriceOracle Journal{jpricedirectives, jimpliedmarketprices} = | -- if no applicable market price can be found or calculated, or if the | ||||||
|   -- traceStack "journalPriceOracle" $ | -- source commodity and the valuation commodity are the same, returns | ||||||
|   let | -- Nothing. | ||||||
|     pricesatdate = |  | ||||||
|       memo $ |  | ||||||
|       pricesAtDate jpricedirectives jimpliedmarketprices |  | ||||||
|   in |  | ||||||
|     memo $ |  | ||||||
|     uncurry3 $ |  | ||||||
|     priceLookup pricesatdate |  | ||||||
| 
 |  | ||||||
| -- | Given a list of price directives in parse order, find the market |  | ||||||
| -- value at the given date of one unit of a given source commodity, in |  | ||||||
| -- a different specified valuation commodity, or a default valuation |  | ||||||
| -- commodity. |  | ||||||
| -- | -- | ||||||
| -- When the valuation commodity is specified, this looks for an | -- See makePriceGraph for how prices are determined. | ||||||
| -- exchange rate (market price) calculated in any of the following | -- Note that both market prices and default valuation commodities can | ||||||
| -- ways, in order of preference: | -- vary with valuation date, since that determines which market prices | ||||||
| -- | -- are visible. | ||||||
| -- 1. a declared market price (DMP) - a P directive giving the |  | ||||||
| --    exchange rate from source commodity to valuation commodity |  | ||||||
| -- |  | ||||||
| -- 2. a transaction-implied market price (TMP) - a market price |  | ||||||
| --    equivalent to the transaction price used in the latest |  | ||||||
| --    transaction from source commodity to valuation commodity |  | ||||||
| --    (on or before the valuation date) |  | ||||||
| -- |  | ||||||
| -- 3. a reverse declared market price (RDMP) - calculated by inverting |  | ||||||
| --    a DMP |  | ||||||
| -- |  | ||||||
| -- 4. a reverse transaction-implied market price (RTMP) - calculated |  | ||||||
| --    by inverting a TMP |  | ||||||
| -- |  | ||||||
| -- 5. an indirect market price (IMP) - calculated by combining the |  | ||||||
| --    shortest chain of market prices (any of the above types) leading |  | ||||||
| --    from source commodity to valuation commodity. |  | ||||||
| -- |  | ||||||
| -- When the valuation commodity is not specified, this looks for the |  | ||||||
| -- latest applicable declared or transaction-implied price, and |  | ||||||
| -- converts to the commodity mentioned in that price (the default |  | ||||||
| -- valuation commodity). |  | ||||||
| -- |  | ||||||
| -- Note this default valuation commodity can vary across successive |  | ||||||
| -- calls for different dates, since it depends on the price |  | ||||||
| -- declarations in each period. |  | ||||||
| -- |  | ||||||
| -- This returns the valuation commodity that was specified or |  | ||||||
| -- inferred, and the quantity of it that one unit of the source |  | ||||||
| -- commodity is worth. Or if no applicable market price or chain of |  | ||||||
| -- prices can be found, or the source commodity and the valuation |  | ||||||
| -- commodity are the same, returns Nothing. |  | ||||||
| -- | -- | ||||||
| priceLookup :: (Day -> PriceGraph) -> Day -> CommoditySymbol -> Maybe CommoditySymbol -> Maybe (CommoditySymbol, Quantity) | priceLookup :: (Day -> PriceGraph) -> Day -> CommoditySymbol -> Maybe CommoditySymbol -> Maybe (CommoditySymbol, Quantity) | ||||||
| priceLookup pricesatdate d from mto = | priceLookup makepricegraph d from mto = | ||||||
|   -- trace ("priceLookup ("++show d++", "++show from++", "++show mto++")") $ |   -- trace ("priceLookup ("++show d++", "++show from++", "++show mto++")") $ | ||||||
|   let |   let | ||||||
|     -- build a graph of the commodity exchange rates in effect on this day |     -- build a graph of the commodity exchange rates in effect on this day | ||||||
|     -- XXX should hide these fgl details better |     -- XXX should hide these fgl details better | ||||||
|     PriceGraph{prGraph=g, prNodemap=m, prDefaultValuationCommodities=defaultdests} = pricesatdate d |     PriceGraph{prGraph=g, prNodemap=m, prDefaultValuationCommodities=defaultdests} = makepricegraph d | ||||||
|     fromnode = node m from |     fromnode = node m from | ||||||
|     mto' = mto <|> mdefaultto |     mto' = mto <|> mdefaultto | ||||||
|       where |       where | ||||||
| @ -286,8 +263,7 @@ priceLookup pricesatdate d from mto = | |||||||
| tests_priceLookup = | tests_priceLookup = | ||||||
|   let |   let | ||||||
|     d = parsedate |     d = parsedate | ||||||
|     a q c = amount{acommodity=c, aquantity=q} |     p date from q to = MarketPrice{mpdate=d date, mpfrom=from, mpto=to, mprate=q} | ||||||
|     p date from q to = PriceDirective{pddate=d date, pdcommodity=from, pdamount=a q to} |  | ||||||
|     ps1 = [ |     ps1 = [ | ||||||
|        p "2000/01/01" "A" 10 "B" |        p "2000/01/01" "A" 10 "B" | ||||||
|       ,p "2000/01/01" "B" 10 "C" |       ,p "2000/01/01" "B" 10 "C" | ||||||
| @ -295,32 +271,62 @@ tests_priceLookup = | |||||||
|       ,p "2000/01/01" "E"  2 "D" |       ,p "2000/01/01" "E"  2 "D" | ||||||
|       ,p "2001/01/01" "A" 11 "B" |       ,p "2001/01/01" "A" 11 "B" | ||||||
|       ] |       ] | ||||||
|     pricesatdate = pricesAtDate ps1 [] |     makepricegraph = makePriceGraph ps1 [] | ||||||
|   in test "priceLookup" $ do |   in test "priceLookup" $ do | ||||||
|     priceLookup pricesatdate (d "1999/01/01") "A" Nothing    @?= Nothing |     priceLookup makepricegraph (d "1999/01/01") "A" Nothing    @?= Nothing | ||||||
|     priceLookup pricesatdate (d "2000/01/01") "A" Nothing    @?= Just ("B",10) |     priceLookup makepricegraph (d "2000/01/01") "A" Nothing    @?= Just ("B",10) | ||||||
|     priceLookup pricesatdate (d "2000/01/01") "B" (Just "A") @?= Just ("A",0.1) |     priceLookup makepricegraph (d "2000/01/01") "B" (Just "A") @?= Just ("A",0.1) | ||||||
|     priceLookup pricesatdate (d "2000/01/01") "A" (Just "E") @?= Just ("E",500) |     priceLookup makepricegraph (d "2000/01/01") "A" (Just "E") @?= Just ("E",500) | ||||||
| 
 | 
 | ||||||
| ------------------------------------------------------------------------------ | -- | Build the graph of commodity conversion prices for a given day. | ||||||
| -- Building the price graph (network of commodity conversions) on a given day. | -- Converts a list of declared market prices in parse order, and a | ||||||
| 
 | -- list of transaction-inferred market prices in parse order, to a | ||||||
| -- | Convert a list of market price directives in parse order, and a | -- graph of all known exchange rates between commodity pairs in effect | ||||||
| -- list of transaction-implied market prices in parse order, to a | -- on that day. Cf hledger.m4.md -> Valuation: | ||||||
| -- graph of the effective exchange rates between commodity pairs on | -- | ||||||
| -- the given day. | -- hledger looks for a market price (exchange rate) from commodity A | ||||||
| pricesAtDate :: [PriceDirective] -> [MarketPrice] -> Day -> PriceGraph | -- to commodity B in one or more of these ways, in this order of | ||||||
| pricesAtDate pricedirectives impliedmarketprices d = | -- preference: | ||||||
|   dbg9 ("pricesAtDate "++show d) $ | -- | ||||||
|  | -- 1. A *declared market price* or *inferred market price*: | ||||||
|  | --    A's latest market price in B on or before the valuation date | ||||||
|  | --    as declared by a P directive, or (with the `--value-infer` flag) | ||||||
|  | --    inferred from transaction prices. | ||||||
|  | --    | ||||||
|  | -- 2. A *reverse market price*: | ||||||
|  | --    the inverse of a declared or inferred market price from B to A. | ||||||
|  | --  | ||||||
|  | -- 3. A *chained market price*: | ||||||
|  | --    a synthetic price formed by combining the shortest chain of market | ||||||
|  | --    prices (any of the above types) leading from A to B. | ||||||
|  | -- | ||||||
|  | -- 1 and 2 form the edges of the price graph, and we can query it for | ||||||
|  | -- 3 (which is the reason we use a graph). | ||||||
|  | -- | ||||||
|  | -- We also identify each commodity's default valuation commodity, if | ||||||
|  | -- any. For each commodity A, hledger picks a default valuation | ||||||
|  | -- commodity as follows: | ||||||
|  | -- | ||||||
|  | -- 1. The price commodity from the latest (on or before valuation | ||||||
|  | --    date) declared market price for A. | ||||||
|  | -- | ||||||
|  | -- 2. If there are no P directives at all (any commodity, any date), | ||||||
|  | --    and the `--value-infer` flag is used, then the price commodity | ||||||
|  | --    from the latest (on or before valuation date) transaction price | ||||||
|  | --    for A. | ||||||
|  | -- | ||||||
|  | makePriceGraph :: [MarketPrice] -> [MarketPrice] -> Day -> PriceGraph | ||||||
|  | makePriceGraph alldeclaredprices allinferredprices d = | ||||||
|  |   dbg9 ("makePriceGraph "++show d) $ | ||||||
|   PriceGraph{prGraph=g, prNodemap=m, prDefaultValuationCommodities=defaultdests} |   PriceGraph{prGraph=g, prNodemap=m, prDefaultValuationCommodities=defaultdests} | ||||||
|   where |   where | ||||||
|     -- prices in effect on date d, either declared or implied |     -- prices in effect on date d, either declared or inferred | ||||||
|     currentdeclaredandimpliedprices = dbg2 "currentdeclaredandimpliedprices" $ |     declaredandinferredprices = dbg2 "declaredandinferredprices" $ | ||||||
|       latestPriceForEachPairOn pricedirectives impliedmarketprices d |       declaredOrInferredPricesOn alldeclaredprices allinferredprices d | ||||||
| 
 | 
 | ||||||
|     -- infer any additional reverse prices not already declared or implied |     -- infer any additional reverse prices not already declared or inferred | ||||||
|     reverseprices = dbg2 "reverseprices" $ |     reverseprices = dbg2 "reverseprices" $ | ||||||
|       map marketPriceReverse currentdeclaredandimpliedprices \\ currentdeclaredandimpliedprices |       map marketPriceReverse declaredandinferredprices \\ declaredandinferredprices | ||||||
| 
 | 
 | ||||||
|     -- build the graph and associated node map |     -- build the graph and associated node map | ||||||
|     (g, m) = |     (g, m) = | ||||||
| @ -329,47 +335,38 @@ pricesAtDate pricedirectives impliedmarketprices d = | |||||||
|       (dbg9 "price graph edges" $ [(mpfrom, mpto, mprate) | MarketPrice{..} <- prices]) |       (dbg9 "price graph edges" $ [(mpfrom, mpto, mprate) | MarketPrice{..} <- prices]) | ||||||
|       :: (Gr CommoditySymbol Quantity, NodeMap CommoditySymbol) |       :: (Gr CommoditySymbol Quantity, NodeMap CommoditySymbol) | ||||||
|       where |       where | ||||||
|         prices   = currentdeclaredandimpliedprices ++ reverseprices |         prices   = declaredandinferredprices ++ reverseprices | ||||||
|         allcomms = map mpfrom prices |         allcomms = map mpfrom prices | ||||||
| 
 | 
 | ||||||
|     -- determine a default valuation commodity D for each source commodity S: |     -- determine a default valuation commodity D for each source commodity S: | ||||||
|     -- the price commodity in the latest declared market price for S (on any date) |     -- the price commodity in the latest declared market price for S (on any date) | ||||||
|     defaultdests = M.fromList [(mpfrom,mpto) | MarketPrice{..} <- alldeclaredprices] |     defaultdests = M.fromList [(mpfrom,mpto) | MarketPrice{..} <- alldeclaredprices] | ||||||
|       where |  | ||||||
|         alldeclaredprices = dbg2 "alldeclaredprices" $ map priceDirectiveToMarketPrice pricedirectives |  | ||||||
| 
 | 
 | ||||||
| -- From a list of price directives in parse order, and a list of | -- | From a list of directive-declared market prices in parse order, | ||||||
| -- transaction-implied market prices in parse order, get the effective | -- and a list of transaction-inferred market prices in parse order, | ||||||
| -- price on the given date for each commodity pair. That is, the | -- get the effective price on the given date for each commodity pair. | ||||||
| -- latest declared or transaction-implied price dated on or before | -- That is, the latest (by date then parse order) declared price or | ||||||
| -- that day, with declared prices taking precedence. | -- inferred price, on or before that date, If there is both a declared | ||||||
| latestPriceForEachPairOn :: [PriceDirective] -> [MarketPrice] -> Day -> [MarketPrice] | -- and inferred price on the same day, declared takes precedence. | ||||||
| latestPriceForEachPairOn pricedirectives impliedmarketprices d = | declaredOrInferredPricesOn :: [MarketPrice] -> [MarketPrice] -> Day -> [MarketPrice] | ||||||
|  | declaredOrInferredPricesOn declaredprices inferredprices d = | ||||||
|   let |   let | ||||||
|     -- consider only declarations/transactions before the valuation date |     -- keeping only prices on or before the valuation date, label each | ||||||
|     declaredprices = map priceDirectiveToMarketPrice $ filter ((<=d).pddate) pricedirectives |     -- item with its same-day precedence (declared above inferred) and | ||||||
|     impliedmarketprices' = filter ((<=d).mpdate) impliedmarketprices |     -- then parse order | ||||||
|     -- label the items with their precedence and then their parse order |     declaredprices' = [(1, i, p) | (i,p@MarketPrice{mpdate}) <- zip [1..] declaredprices, mpdate<=d] | ||||||
|     declaredprices'                  = [(1, i, p) | (i,p) <- zip [1..] declaredprices] |     inferredprices' = [(0, i, p) | (i,p@MarketPrice{mpdate}) <- zip [1..] inferredprices, mpdate<=d] | ||||||
|     impliedmarketprices'' = [(0, i, p) | (i,p) <- zip [1..] impliedmarketprices'] |  | ||||||
|   in |   in | ||||||
|     -- combine |     -- combine | ||||||
|     declaredprices' ++ impliedmarketprices'' |     declaredprices' ++ inferredprices' | ||||||
|     -- sort by newest date then highest precedence then latest parse order |     -- sort by newest date then highest precedence then latest parse order | ||||||
|     & sortBy (flip compare `on` (\(precedence,parseorder,mp)->(mpdate mp,precedence,parseorder))) |     & sortBy (flip compare `on` (\(precedence,parseorder,mp)->(mpdate mp,precedence,parseorder))) | ||||||
|     -- discard the sorting labels |     -- discard the sorting labels | ||||||
|     & map third3 |     & map third3 | ||||||
|     -- keep only the first (ie the newest, highest precedence and latest parsed) price for each pair |     -- keep only the first (ie the newest, highest precedence, latest parsed) price for each pair | ||||||
|  |     -- XXX or use a Map ? | ||||||
|     & nubSortBy (compare `on` (\(MarketPrice{..})->(mpfrom,mpto))) |     & nubSortBy (compare `on` (\(MarketPrice{..})->(mpfrom,mpto))) | ||||||
| 
 | 
 | ||||||
| priceDirectiveToMarketPrice :: PriceDirective -> MarketPrice |  | ||||||
| priceDirectiveToMarketPrice PriceDirective{..} = |  | ||||||
|   MarketPrice{ mpdate = pddate |  | ||||||
|              , mpfrom = pdcommodity |  | ||||||
|              , mpto   = acommodity pdamount |  | ||||||
|              , mprate = aquantity pdamount |  | ||||||
|              } |  | ||||||
| 
 |  | ||||||
| marketPriceReverse :: MarketPrice -> MarketPrice | marketPriceReverse :: MarketPrice -> MarketPrice | ||||||
| marketPriceReverse mp@MarketPrice{..} = mp{mpfrom=mpto, mpto=mpfrom, mprate=1/mprate} | marketPriceReverse mp@MarketPrice{..} = mp{mpfrom=mpto, mpto=mpfrom, mprate=1/mprate} | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user