frankfurter/lib/provider/adapters/bnm.rb

79 lines
2.3 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
# Bank Negara Malaysia daily exchange rates. Data available from 2006-01-03.
# Historical rates are fetched per-currency per-month.
class BNM < Adapter
BASE_URL = "https://api.bnm.gov.my/public/exchange-rate"
SESSION = "0900"
HEADERS = { "Accept" => "application/vnd.BNM.API.v1+json" }.freeze
class << self
def backfill_range = 30
end
def fetch(after: nil, upto: nil)
start_date = Date.parse(after.to_s)
end_date = upto || Date.today
currencies = fetch_currencies
currencies.flat_map { |code| fetch_currency(code, start_date, end_date) }
end
private
def fetch_currencies
uri = URI("#{BASE_URL}?session=#{SESSION}")
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.get("#{uri.path}?#{uri.query}", HEADERS)
end
data = JSON.parse(response.body)
data["data"].map { |item| item["currency_code"] }
end
def fetch_currency(code, start_date, end_date)
records = []
each_month(start_date, end_date) do |year, month|
uri = URI("#{BASE_URL}/#{code}/year/#{year}/month/#{month}?session=#{SESSION}")
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
http.get("#{uri.path}?#{uri.query}", HEADERS)
end
data = JSON.parse(response.body)
currency_data = data["data"]
next if currency_data.nil? || currency_data.is_a?(Array)
unit = currency_data["unit"] || 1
rates = currency_data["rate"]
rates = [rates] unless rates.is_a?(Array)
rates.each do |rate|
mid = rate["middle_rate"]
next unless mid
date = Date.parse(rate["date"])
next if date > end_date
records << { date:, base: code, quote: "MYR", rate: mid / unit }
end
sleep(1)
end
records
end
def each_month(start_date, end_date)
date = Date.new(start_date.year, start_date.month, 1)
while date <= end_date
yield date.year, date.month
date = date.next_month
end
end
end
end
end