Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

💶 Euro Exchange Rates History

euro_exchange.livemd

💶 Euro Exchange Rates History

Mix.install([
  {:briefly, "~> 0.4.1"},
  {:req, "~> 0.3.10"},
  {:kino_explorer, "~> 0.1.8"},
  {:kino_vega_lite, "~> 0.1.9"}
])

Fetch historic exchange data from European Central Bank

Run in Livebook

alias Explorer.DataFrame, as: D
alias Explorer.Series, as: S
alias VegaLite, as: V

path = Briefly.create!()

"https://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist.zip"
|> Req.get!()
|> then(&(&1.body |> hd() |> elem(1)))
|> then(&File.write!(path, &1))

history = D.from_csv!(path, header: true, parse_dates: true, null_character: "N/A")
currencies =
  history
  |> D.dtypes()
  |> Map.keys()
  |> Kernel.--(["Date"])
  |> Enum.sort()
  |> Enum.map(&{&1, &1})

currency =
  "Currency"
  |> Kino.Input.select(currencies, default: "USD")
  |> Kino.render()
  |> Kino.Input.read()

from_to =
  "Exchange"
  |> Kino.Input.select([
    {"from", "Currency to EUR"},
    {"to", "EUR to Currency"}
  ])
  |> Kino.render()
  |> Kino.Input.read()

time_filter =
  "Show"
  |> Kino.Input.select(
    [
      {"all", "All"},
      {"1825", "Last 5 years"},
      {"730", "Last 2 years"},
      {"365", "Last 1 year"},
      {"ytd", "Year to date"},
      {"90", "Last 90 days"},
      {"60", "Last 60 days"},
      {"30", "Last 30 days"},
      {"15", "Last 15 days"},
      {"7", "Last 7 days"}
    ],
    default: "all"
  )
  |> Kino.render()
  |> Kino.Input.read()

df =
  history
  |> D.select(["Date", currency])
  |> D.rename(%{currency => "to"})
  |> D.mutate_with(fn entry ->
    case entry["to"] do
      nil -> []
      to -> [from: S.divide(1, to)]
    end
  end)
  |> D.select(["Date", from_to])
  |> D.rename(%{"Date" => "date", from_to => "value"})

df =
  case time_filter do
    "all" ->
      df

    "ytd" ->
      cut = %Date{Date.utc_today() | month: 1, day: 1}
      D.filter_with(df, &S.greater(&1["date"], cut))

    days ->
      cut = Date.add(Date.utc_today(), -1 * String.to_integer(days))
      D.filter_with(df, &S.greater(&1["date"], cut))
  end

:ok
label =
  case from_to do
    "from" -> "1 #{currency} to EUR"
    "to" -> "1 EUR to #{currency}"
  end

V.new(
  width: 750,
  height: 400,
  title: ["date", label]
)
|> V.data_from_values(df, only: ["date", "value"])
|> V.mark(:point, tooltip: true)
|> V.encode_field(:x, "date",
  title: "date",
  type: :temporal
)
|> V.encode_field(:y, "value",
  title: label,
  type: :quantitative
)