Current section

Files

Jump to
ptax lib ptax.ex
Raw

lib/ptax.ex

defmodule PTAX do
@moduledoc """
Converts between currencies using the Brazilian Central Bank's PTAX rates.
Rates are computed as the mid-point between BCB's closing bid and ask quotes
for each currency pair.
"""
alias Money.ExchangeRates.Retriever
@doc """
Exchanges a `Money` amount to the given currency using the latest known PTAX rates.
Returns `{:ok, Money.t()}` on success, or `{:error, reason}` if the rates
are unavailable or the currency is not supported.
## Examples
iex> {:ok, %Money{}} = PTAX.exchange(Money.new(:USD, "100"), :BRL)
iex> PTAX.exchange(Money.new(:BRL, "100"), :XYZ)
{:error, {Money.UnknownCurrencyError, "The currency :XYZ is not known."}}
"""
@spec exchange(Money.t(), Money.currency_reference()) ::
{:ok, Money.t()} | {:error, {Exception.t(), String.t()}}
def exchange(%Money{} = money, currency) do
rates = Retriever.latest_rates(PTAX.Retriever)
with {:ok, money} <- Money.to_currency(money, currency, rates) do
{:ok, Money.round(money, currency_digits: :cash)}
end
end
@doc """
Exchanges a `Money` amount to the given currency using the latest known PTAX rates.
Raises if the rates are unavailable or the currency is not supported.
## Examples
iex> %Money{} = PTAX.exchange!(Money.new(:USD, "100"), :BRL)
iex> PTAX.exchange!(Money.new(:BRL, "100"), :XYZ)
** (Money.UnknownCurrencyError) The currency :XYZ is not known.
"""
@spec exchange!(Money.t(), Money.currency_reference()) :: Money.t()
def exchange!(%Money{} = money, currency) do
rates = Retriever.latest_rates(PTAX.Retriever)
money
|> Money.to_currency!(currency, rates)
|> Money.round(currency_digits: :cash)
end
@doc """
Exchanges a `Money` amount to the given currency using PTAX rates for the given date.
Returns `{:ok, Money.t()}` on success, or `{:error, reason}` if the rates
are unavailable or the currency is not supported.
## Examples
iex> PTAX.exchange(Money.new(:GBP, "50"), :BRL, ~D[2026-05-15])
{:ok, Money.new(:BRL, "337.63")}
iex> PTAX.exchange(Money.new(:USD, "100"), :BRL, ~D[2025-12-25])
{:error, {Money.ExchangeRateError, "no exchange rates available for 2025-12-25"}}
"""
@spec exchange(Money.t(), Money.currency_reference(), Date.t()) ::
{:ok, Money.t()} | {:error, {Exception.t(), String.t()}}
def exchange(%Money{} = money, currency, date) do
rates = Retriever.historic_rates(PTAX.Retriever, date)
with {:ok, money} <- Money.to_currency(money, currency, rates) do
{:ok, Money.round(money, currency_digits: :cash)}
end
end
@doc """
Exchanges a `Money` amount to the given currency using PTAX rates for the given date.
Raises if the rates are unavailable or the currency is not supported.
## Examples
iex> PTAX.exchange!(Money.new(:GBP, "50"), :BRL, ~D[2026-05-15])
Money.new(:BRL, "337.63")
iex> PTAX.exchange!(Money.new(:USD, "100"), :BRL, ~D[2025-12-25])
** (Money.ExchangeRateError) no exchange rates available for 2025-12-25
"""
@spec exchange!(Money.t(), Money.currency_reference(), Date.t()) :: Money.t()
def exchange!(%Money{} = money, currency, date) do
rates = Retriever.historic_rates(PTAX.Retriever, date)
money
|> Money.to_currency!(currency, rates)
|> Money.round(currency_digits: :cash)
end
end