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