121 lines
3.2 KiB
Ruby
121 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "net/http"
|
|
require "ox"
|
|
|
|
require "provider/adapters/adapter"
|
|
|
|
class Provider
|
|
module Adapters
|
|
# Seðlabanki Íslands (Central Bank of Iceland). Publishes daily reference
|
|
# exchange rates for 30+ currencies against the Icelandic króna (ISK).
|
|
# Two GroupIDs are needed: 9 (official reference, 10 currencies from 1981)
|
|
# and 7 (registered mid-rate, 22 currencies from 2006).
|
|
class SBI < Adapter
|
|
BASE_URL = "https://sedlabanki.is/xmltimeseries/Default.aspx"
|
|
# TimeSeries ID to ISO currency code mapping
|
|
# GroupID=9 (official reference rates)
|
|
GROUP9_CURRENCIES = {
|
|
"4055" => "USD",
|
|
"4061" => "DKK",
|
|
"4064" => "EUR",
|
|
"4085" => "JPY",
|
|
"4088" => "CAD",
|
|
"4091" => "NOK",
|
|
"4097" => "XDR",
|
|
"4103" => "GBP",
|
|
"4106" => "CHF",
|
|
"4109" => "SEK",
|
|
}.freeze
|
|
|
|
# GroupID=7 (registered mid-rates)
|
|
GROUP7_CURRENCIES = {
|
|
"29" => "CNY",
|
|
"31" => "PLN",
|
|
"35" => "NGN",
|
|
"36" => "TWD",
|
|
"37" => "KRW",
|
|
"38" => "SRD",
|
|
"39" => "AUD",
|
|
"40" => "NZD",
|
|
"41" => "HKD",
|
|
"42" => "HUF",
|
|
"43" => "ILS",
|
|
"44" => "ZAR",
|
|
"45" => "SGD",
|
|
"46" => "MXN",
|
|
"48" => "TRY",
|
|
"50" => "INR",
|
|
"52" => "CZK",
|
|
"53" => "BRL",
|
|
"74" => "THB",
|
|
"288" => "JMD",
|
|
"3503" => "SAR",
|
|
"19254" => "KWD",
|
|
}.freeze
|
|
|
|
class << self
|
|
def backfill_range = 365
|
|
end
|
|
|
|
def fetch(after: nil, upto: nil)
|
|
end_date = upto || Date.today
|
|
|
|
xml9 = fetch_group(after, end_date, 9)
|
|
xml7 = fetch_group(after, end_date, 7)
|
|
|
|
parse(xml9, GROUP9_CURRENCIES) + parse(xml7, GROUP7_CURRENCIES)
|
|
end
|
|
|
|
def parse(xml, currency_map = GROUP9_CURRENCIES.merge(GROUP7_CURRENCIES))
|
|
doc = Ox.load(xml, mode: :generic)
|
|
records = []
|
|
|
|
doc.locate("*/TimeSeries").each do |ts|
|
|
id = ts.attributes[:ID]
|
|
currency = currency_map[id]
|
|
next unless currency
|
|
|
|
ts.locate("TimeSeriesData/Entry").each do |entry|
|
|
date_node = entry.locate("Date").first
|
|
value_node = entry.locate("Value").first
|
|
next unless date_node && value_node
|
|
|
|
date_str = date_node.text
|
|
rate_str = value_node.text
|
|
next unless date_str && rate_str
|
|
|
|
rate = Float(rate_str)
|
|
next if rate.zero?
|
|
|
|
date = parse_date(date_str)
|
|
records << { date:, base: currency, quote: "ISK", rate: }
|
|
end
|
|
end
|
|
|
|
records
|
|
end
|
|
|
|
private
|
|
|
|
def fetch_group(start_date, end_date, group_id)
|
|
url = URI(BASE_URL)
|
|
url.query = URI.encode_www_form(
|
|
"DagsFra" => start_date.to_s,
|
|
"DagsTil" => end_date.to_s,
|
|
"GroupID" => group_id,
|
|
"Type" => "xml",
|
|
)
|
|
|
|
Net::HTTP.get(url)
|
|
end
|
|
|
|
def parse_date(str)
|
|
# Format: "M/D/YYYY 12:00:00 AM"
|
|
date_part = str.split(" ").first
|
|
month, day, year = date_part.split("/").map(&:to_i)
|
|
Date.new(year, month, day)
|
|
end
|
|
end
|
|
end
|
|
end
|