101 lines
2.4 KiB
Ruby
101 lines
2.4 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "json"
|
|
require "net/http"
|
|
|
|
require "provider/adapters/adapter"
|
|
|
|
class Provider
|
|
module Adapters
|
|
# Hong Kong Monetary Authority. Publishes daily HKD exchange rates for 17
|
|
# currencies via a public JSON API. Rates are expressed as HKD per 1 unit
|
|
# of foreign currency (base = foreign currency, quote = HKD). Data is
|
|
# published monthly with approximately a 1-month lag.
|
|
# Historical data available from 1981-01-02.
|
|
class HKMA < Adapter
|
|
BASE_URL = "https://api.hkma.gov.hk/public/market-data-and-statistics/" \
|
|
"monthly-statistical-bulletin/er-ir/er-eeri-daily"
|
|
PAGE_SIZE = 100
|
|
|
|
CURRENCY_FIELDS = [
|
|
"usd",
|
|
"eur",
|
|
"gbp",
|
|
"jpy",
|
|
"cad",
|
|
"aud",
|
|
"sgd",
|
|
"twd",
|
|
"chf",
|
|
"cny",
|
|
"krw",
|
|
"thb",
|
|
"myr",
|
|
"php",
|
|
"inr",
|
|
"idr",
|
|
"zar",
|
|
].freeze
|
|
|
|
class << self
|
|
def backfill_range = 365
|
|
end
|
|
|
|
def fetch(after: nil, upto: nil)
|
|
end_date = upto || Date.today
|
|
|
|
fetch_pages(after, end_date)
|
|
end
|
|
|
|
def parse(records)
|
|
records.each_with_object([]) do |record, result|
|
|
date_str = record["end_of_day"]
|
|
next unless date_str
|
|
|
|
date = Date.parse(date_str)
|
|
CURRENCY_FIELDS.each do |field|
|
|
rate_value = record[field]
|
|
next unless rate_value.is_a?(Numeric) && rate_value.positive?
|
|
|
|
result << { date:, base: field.upcase, quote: "HKD", rate: rate_value.to_f }
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def fetch_pages(start_date, end_date)
|
|
raw_records = []
|
|
offset = 0
|
|
|
|
loop do
|
|
page = fetch_page(start_date, end_date, offset)
|
|
break if page.empty?
|
|
|
|
raw_records.concat(page)
|
|
break if page.size < PAGE_SIZE
|
|
|
|
offset += PAGE_SIZE
|
|
end
|
|
|
|
parse(raw_records)
|
|
end
|
|
|
|
def fetch_page(start_date, end_date, offset)
|
|
uri = URI(BASE_URL)
|
|
uri.query = URI.encode_www_form(
|
|
"choose" => "end_of_day",
|
|
"from" => start_date.to_s,
|
|
"to" => end_date.to_s,
|
|
"pagesize" => PAGE_SIZE,
|
|
"offset" => offset,
|
|
"sortby" => "end_of_day",
|
|
"sortorder" => "desc",
|
|
)
|
|
response = Net::HTTP.get(uri)
|
|
data = JSON.parse(response)
|
|
data.dig("result", "records") || []
|
|
end
|
|
end
|
|
end
|
|
end
|