87 lines
2.6 KiB
Ruby
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
|