100 lines
3.1 KiB
Ruby
100 lines
3.1 KiB
Ruby
|
|
# frozen_string_literal: true
|
||
|
|
|
||
|
|
require "net/http"
|
||
|
|
require "ox"
|
||
|
|
|
||
|
|
require "provider/adapters/adapter"
|
||
|
|
|
||
|
|
class Provider
|
||
|
|
module Adapters
|
||
|
|
# Central Bank of Armenia. Publishes daily rates for ~30 currencies against AMD.
|
||
|
|
class CBA < Adapter
|
||
|
|
URL = URI("https://api.cba.am/exchangerates.asmx")
|
||
|
|
CHUNK_SIZE = 365
|
||
|
|
TROY_OUNCE_GRAMS = 31.1035
|
||
|
|
PRECIOUS_METALS = ["XAU", "XAG"].freeze
|
||
|
|
|
||
|
|
def fetch(after: nil, upto: nil)
|
||
|
|
end_date = upto || Date.today
|
||
|
|
iso_codes = current_currency_codes
|
||
|
|
records = []
|
||
|
|
chunk_start = after
|
||
|
|
|
||
|
|
while chunk_start <= end_date
|
||
|
|
chunk_end = [chunk_start + CHUNK_SIZE - 1, end_date].min
|
||
|
|
records.concat(range(chunk_start, chunk_end, iso_codes))
|
||
|
|
chunk_start = chunk_end + 1
|
||
|
|
end
|
||
|
|
|
||
|
|
records
|
||
|
|
end
|
||
|
|
|
||
|
|
private
|
||
|
|
|
||
|
|
def current_currency_codes
|
||
|
|
response = request("ExchangeRatesLatest", <<~XML)
|
||
|
|
<ExchangeRatesLatest xmlns="http://www.cba.am/" />
|
||
|
|
XML
|
||
|
|
|
||
|
|
result = response.locate("soap:Envelope/soap:Body/ExchangeRatesLatestResponse/ExchangeRatesLatestResult").first
|
||
|
|
return "" unless result
|
||
|
|
|
||
|
|
result.locate("Rates/ExchangeRate").filter_map do |node|
|
||
|
|
node.locate("ISO").first&.text
|
||
|
|
end.join(",")
|
||
|
|
end
|
||
|
|
|
||
|
|
def range(start_date, end_date, iso_codes)
|
||
|
|
response = request("ExchangeRatesByDateRangeByISO", <<~XML)
|
||
|
|
<ExchangeRatesByDateRangeByISO xmlns="http://www.cba.am/">
|
||
|
|
<ISOCodes>#{iso_codes}</ISOCodes>
|
||
|
|
<DateFrom>#{start_date}</DateFrom>
|
||
|
|
<DateTo>#{end_date}</DateTo>
|
||
|
|
</ExchangeRatesByDateRangeByISO>
|
||
|
|
XML
|
||
|
|
|
||
|
|
response
|
||
|
|
.locate("soap:Envelope/soap:Body/ExchangeRatesByDateRangeByISOResponse/ExchangeRatesByDateRangeByISOResult/diffgr:diffgram/DocumentElement/ExchangeRatesByRange")
|
||
|
|
.filter_map do |row|
|
||
|
|
iso = row.locate("ISO").first&.text
|
||
|
|
next unless iso
|
||
|
|
|
||
|
|
{ date: Date.parse(row.locate("RateDate").first.text), base: iso, quote: "AMD", rate: extract_rate(row) }
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
def request(action, payload)
|
||
|
|
xml = <<~XML
|
||
|
|
<?xml version="1.0" encoding="utf-8"?>
|
||
|
|
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||
|
|
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||
|
|
<soap:Body>
|
||
|
|
#{payload.strip}
|
||
|
|
</soap:Body>
|
||
|
|
</soap:Envelope>
|
||
|
|
XML
|
||
|
|
|
||
|
|
Ox.load(
|
||
|
|
Net::HTTP.post(
|
||
|
|
URL,
|
||
|
|
xml,
|
||
|
|
{
|
||
|
|
"Content-Type" => "text/xml; charset=utf-8",
|
||
|
|
"SOAPAction" => "\"http://www.cba.am/#{action}\"",
|
||
|
|
},
|
||
|
|
).body,
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
def extract_rate(node)
|
||
|
|
iso = node.locate("ISO").first&.text
|
||
|
|
amount = Integer(node.locate("Amount").first.text)
|
||
|
|
rate = Float(node.locate("Rate").first.text)
|
||
|
|
rate *= TROY_OUNCE_GRAMS if PRECIOUS_METALS.include?(iso)
|
||
|
|
rate / amount
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|