DpExchange.Core.Types.FxRate (DpExchangeCore v0.3.14)

Copy Markdown View Source

A foreign-exchange reference rate a venue publishes for a past instant.

This is not a price the venue traded at

Every other rate in this contract is the venue's own market. This one is not: Gemini's documentation says plainly that it "does not offer foreign exchange services" and that the endpoint is "for historical reference only". The number comes from a third-party source the venue names, and the venue is relaying it.

So :source and :benchmark are carried and are not decoration. %{source: "bcb", benchmark: "Spot"} says which institution published the rate and which of its series it came from. Two venues relaying the same pair at the same instant can legitimately disagree, and a caller reconciling them needs to know it is comparing two sources rather than finding a bug.

:as_of is the rate's instant, not when it was fetched

The endpoint takes a timestamp and answers for it. :as_of is that instant, echoed by the venue, and it is the whole content of the request — a rate without it is a number with no time attached, which is not a rate.

:provider is the venue; :source is whoever computed the rate

Two different facts that both look like "where this came from". Collapsing them would make a Gemini-relayed BCB rate indistinguishable from one Gemini computed itself, and only the second would be the venue's own claim.

Summary

Functions

Converts amount at this rate.

Builds a t/0, failing closed if a required field is absent or nil.

Types

t()

@type t() :: %DpExchange.Core.Types.FxRate{
  as_of: DateTime.t(),
  benchmark: String.t() | nil,
  pair: String.t(),
  provider: atom() | String.t(),
  rate: Decimal.t(),
  source: String.t() | nil
}

Functions

convert(fx_rate, amount)

@spec convert(t(), Decimal.t()) :: Decimal.t()

Converts amount at this rate.

Straight multiplication, unrounded: Decimal is exact and the currency's precision is the caller's to apply, knowing which currency it holds.

This does not check the pair. A caller converting AUD with a GBPUSD rate gets a number, and no type can tell it that was wrong without knowing which side of the pair the amount is in — which the venue's fxPair string alone does not say.

new(attrs)

@spec new(keyword() | map()) :: t()

Builds a t/0, failing closed if a required field is absent or nil.

@enforce_keys guards presence, not nil — see DpExchange.Core.Types.Validate.