ExLedger.Parser.Price (ex_ledger v0.6.0)

Parser for price directives (P DATE COMMODITY PRICE).

Provides extraction, parsing, and lookup functionality for price data.

Summary

Functions

Returns all available commodity pairs in the price database.

Builds a price database from a list of price directives.

Returns all commodities that can be converted to the target currency.

Extracts all price directives from input.

Looks up the price for a commodity on a given date.

Parses a single price directive line.

Types

price_db()

@type price_db() :: %{required({String.t(), String.t()}) => [{Date.t(), Decimal.t()}]}

price_directive()

@type price_directive() :: ExLedger.Parser.Core.price_directive()

Functions

available_pairs(price_db)

@spec available_pairs(price_db()) :: [{String.t(), String.t()}]

Returns all available commodity pairs in the price database.

Examples

iex> db = %{{"EUR", "CHF"} => [...], {"USD", "CHF"} => [...]}
iex> Price.available_pairs(db)
[{"EUR", "CHF"}, {"USD", "CHF"}]

build_price_db(prices)

@spec build_price_db([price_directive()]) :: price_db()

Builds a price database from a list of price directives.

The database is indexed by {source_commodity, target_commodity} tuples, with prices sorted by date descending for efficient lookup.

Examples

iex> prices = [
...>   %{date: ~D[2026-01-15], commodity: "EUR", price: %{value: 0.9432, currency: "CHF"}},
...>   %{date: ~D[2026-02-01], commodity: "EUR", price: %{value: 0.9455, currency: "CHF"}}
...> ]
iex> db = Price.build_price_db(prices)
iex> db[{"EUR", "CHF"}]
[{~D[2026-02-01], 0.9455}, {~D[2026-01-15], 0.9432}]

commodities_convertible_to(target, price_db)

@spec commodities_convertible_to(String.t(), price_db()) :: [String.t()]

Returns all commodities that can be converted to the target currency.

Examples

iex> db = %{{"EUR", "CHF"} => [...], {"USD", "CHF"} => [...], {"GBP", "EUR"} => [...]}
iex> Price.commodities_convertible_to("CHF", db)
["EUR", "USD"]

extract_price_directives(input)

@spec extract_price_directives(String.t()) :: [price_directive()]

Extracts all price directives from input.

Returns a list of price directive maps with date, commodity, and price.

Examples

iex> input = """
...> P 2026-01-15 EUR CHF 0.9432
...> P 2026-01-15 USD CHF 0.8821
...> """
iex> Price.extract_price_directives(input)
[
  %{date: ~D[2026-01-15], commodity: "EUR", price: %{value: 0.9432, currency: "CHF", currency_position: :leading}},
  %{date: ~D[2026-01-15], commodity: "USD", price: %{value: 0.8821, currency: "CHF", currency_position: :leading}}
]

lookup_price(from_commodity, to_commodity, date, price_db)

@spec lookup_price(String.t(), String.t(), Date.t(), price_db()) ::
  {:ok, Decimal.t()} | {:error, :no_price_found}

Looks up the price for a commodity on a given date.

Returns the most recent price on or before the given date.

Examples

iex> db = %{{"EUR", "CHF"} => [{~D[2026-02-01], 0.9455}, {~D[2026-01-15], 0.9432}]}
iex> Price.lookup_price("EUR", "CHF", ~D[2026-01-20], db)
{:ok, 0.9432}

iex> Price.lookup_price("EUR", "CHF", ~D[2026-02-15], db)
{:ok, 0.9455}

iex> Price.lookup_price("EUR", "CHF", ~D[2026-01-01], db)
{:error, :no_price_found}

parse_price_directive(line)

@spec parse_price_directive(String.t()) :: {:ok, price_directive()} | :skip

Parses a single price directive line.

Format: P DATE COMMODITY PRICE

  • DATE: YYYY/MM/DD or YYYY-MM-DD
  • COMMODITY: The source commodity symbol (e.g., EUR, USD, AAPL)
  • PRICE: The price amount with target currency (e.g., CHF 0.9432 or $150.00)

Examples

iex> Price.parse_price_directive("P 2026-01-15 EUR CHF 0.9432")
{:ok, %{date: ~D[2026-01-15], commodity: "EUR", price: %{value: 0.9432, currency: "CHF", currency_position: :leading}}}

iex> Price.parse_price_directive("P 2026/01/15 AAPL $150.00")
{:ok, %{date: ~D[2026-01-15], commodity: "AAPL", price: %{value: 150.0, currency: "$", currency_position: :leading}}}