# frozen_string_literal: true require "json" require "net/http" require "provider/adapters/adapter" class Provider module Adapters # Banco Central de Chile. Fetches daily exchange rates for 8 currencies # against the Chilean peso (CLP) via the BDE REST API. Requires registered # credentials (email + password) passed as query parameters. class BCCH < Adapter API_URL = "https://si3.bcentral.cl/SieteRestWS/SieteRestWS.ashx" # series_id => base currency (all quoted against CLP) SERIES = { "F073.TCO.PRE.Z.D" => "USD", "F072.CLP.EUR.N.O.D" => "EUR", "F072.CLP.GBP.N.O.D" => "GBP", "F072.CLP.JPY.N.O.D" => "JPY", "F072.CLP.CAD.N.O.D" => "CAD", "F072.CLP.AUD.N.O.D" => "AUD", "F072.CLP.CNY.N.O.D" => "CNY", "F072.CLP.BRL.N.O.D" => "BRL", }.freeze class << self def api_key = ENV["BCCH_USER"] || raise(Adapter::Unavailable, "no API key") end def fetch(after: nil, upto: nil) dataset = [] params = {} params[:firstdate] = after.to_s if after params[:lastdate] = (upto || Date.today).to_s if after SERIES.each do |series_id, base| sleep(0.2) dataset.concat(fetch_series(series_id, base, **params)) end dataset end def parse(json, base) data = JSON.parse(json) observations = data.dig("Series", "Obs") || [] observations.filter_map do |obs| next if obs["statusCode"] != "OK" value = obs["value"]&.tr(",", "") next unless value rate = Float(value) next if rate.zero? date = Date.strptime(obs["indexDateString"], "%d-%m-%Y") { date:, base:, quote: "CLP", rate: } end end private def fetch_series(series_id, base, **params) url = URI(API_URL) url.query = URI.encode_www_form( user: ENV["BCCH_USER"], pass: ENV["BCCH_PASS"], function: "GetSeries", timeseries: series_id, **params, ) response = Net::HTTP.get(url) parse(response, base) end end end end