frankfurter/lib/provider/adapters/bccr.rb
BadMark d0a43e357a
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / publish (push) Has been cancelled
CI/CD Pipeline / release (push) Has been cancelled
up
2026-05-21 01:38:14 -06:00

87 lines
2.6 KiB
Ruby

# frozen_string_literal: true
require "json"
require "net/http"
require "provider/adapters/adapter"
class Provider
module Adapters
# Banco Central de Costa Rica (BCCR). Fetches the daily reference exchange
# rate for the US dollar against the Costa Rican colón via the SDDE API.
# The API returns a pivoted table (dates as columns) with a maximum of 100
# date columns per request, so backfill is chunked in 90-day periods.
class BCCR < Adapter
BASE_URL = "https://apim.bccr.fi.cr/SDDE/api"
TOKEN_URL = "#{BASE_URL}/Bccr.GE.SDDE.IndicadoresSitioExterno.ServiciosUsuario.API/Token/GenereCSRF"
DATA_URL = "#{BASE_URL}/Bccr.GE.SDDE.IndicadoresSitioExterno.GrupoVariables.API/CuadroGrupoVariables/ObtenerDatosCuadro"
# IdGrupoVariable=1 is the USD buy/sell reference rate group
GROUP_ID = 1
# Indicator 318 is the sell (venta) rate — CRC per 1 USD
SELL_INDICATOR = 318
class << self
def backfill_range = 90
end
def fetch(after: nil, upto: nil)
token = fetch_token
url = URI(DATA_URL)
url.query = URI.encode_www_form(
"IdGrupoVariable" => GROUP_ID,
"FechaInicio" => "#{after}T00:00:00",
"FechaFin" => (upto || Date.today).to_s,
"CantidadSeriesAMostrar" => 100,
)
response = Net::HTTP.start(url.host, url.port, use_ssl: true, open_timeout: 15, read_timeout: 30) do |http|
req = Net::HTTP::Get.new(url)
req["token_csrf"] = token
req["Origin"] = "https://sdd.bccr.fi.cr"
http.request(req)
end
parse(response.body)
end
def parse(json)
data = json.is_a?(String) ? JSON.parse(json) : json
columns = data["columnas"]
indicators = data["indicadoresRaiz"]
return [] unless columns && indicators
sell = indicators.find { |i| i["idIndicador"] == SELL_INDICATOR }
return [] unless sell
series = sell["series"]
columns.drop(1).filter_map do |col|
index = col["field"].delete_prefix("serie")
value_str = series["serie#{index}Ingles"]
next if value_str.nil? || value_str.empty?
date = parse_date(col["tituloIngles"])
next unless date
rate_value = Float(value_str)
next if rate_value.zero?
{ date:, base: "USD", quote: "CRC", rate: rate_value }
end
end
private
def fetch_token
uri = URI(TOKEN_URL)
Net::HTTP.get(uri)
end
def parse_date(date_str)
Date.parse(date_str)
end
end
end
end