frankfurter/lib/provider/adapters/fred.rb

88 lines
2.5 KiB
Ruby
Raw Normal View History

2026-05-21 01:38:14 -06:00
# frozen_string_literal: true
require "json"
require "net/http"
require "provider/adapters/adapter"
class Provider
module Adapters
# Federal Reserve Economic Data (FRED). Publishes daily H.10 exchange rates.
# Most series are quoted as foreign currency per USD. A few (AUD, EUR, GBP, NZD)
# are quoted as USD per foreign currency and stored with the foreign currency as base.
# The H.10 is a curated policy publication — these are all the series available.
class FRED < Adapter
API_URL = "https://api.stlouisfed.org/fred/series/observations"
# series_id => [quote, base]
SERIES = {
"DEXBZUS" => ["BRL", "USD"],
"DEXCAUS" => ["CAD", "USD"],
"DEXCHUS" => ["CNY", "USD"],
"DEXDNUS" => ["DKK", "USD"],
"DEXHKUS" => ["HKD", "USD"],
"DEXINUS" => ["INR", "USD"],
"DEXJPUS" => ["JPY", "USD"],
"DEXKOUS" => ["KRW", "USD"],
"DEXMAUS" => ["MYR", "USD"],
"DEXMXUS" => ["MXN", "USD"],
"DEXNOUS" => ["NOK", "USD"],
"DEXSDUS" => ["SEK", "USD"],
"DEXSFUS" => ["ZAR", "USD"],
"DEXSIUS" => ["SGD", "USD"],
"DEXSLUS" => ["LKR", "USD"],
"DEXSZUS" => ["CHF", "USD"],
"DEXTAUS" => ["TWD", "USD"],
"DEXTHUS" => ["THB", "USD"],
"DEXUSAL" => ["USD", "AUD"],
"DEXUSEU" => ["USD", "EUR"],
"DEXUSNZ" => ["USD", "NZD"],
"DEXUSUK" => ["USD", "GBP"],
}.freeze
class << self
def api_key = ENV["FRED_API_KEY"] || raise(Adapter::Unavailable, "no API key")
end
def fetch(after: nil, upto: nil)
dataset = []
params = {}
params[:observation_start] = after.to_s if after
SERIES.each do |series_id, (quote, series_base)|
sleep(0.2)
dataset.concat(fetch_series(series_id, quote, series_base, **params))
end
dataset
end
private
def fetch_series(series_id, quote, series_base, **params)
url = URI(API_URL)
url.query = URI.encode_www_form(
series_id: series_id,
api_key: self.class.api_key,
file_type: "json",
**params,
)
response = Net::HTTP.get(url)
data = JSON.parse(response)
(data["observations"] || []).filter_map do |obs|
value = obs["value"]
next if value == "."
rate = Float(value)
next if rate.zero?
date = Date.parse(obs["date"])
{ date:, base: series_base, quote:, rate: }
end
end
end
end
end