95 lines
2.7 KiB
Ruby
95 lines
2.7 KiB
Ruby
|
|
# frozen_string_literal: true
|
||
|
|
|
||
|
|
require "json"
|
||
|
|
require "net/http"
|
||
|
|
|
||
|
|
require "provider/adapters/adapter"
|
||
|
|
|
||
|
|
class Provider
|
||
|
|
module Adapters
|
||
|
|
# Central Bank of the Republic of Turkey.
|
||
|
|
# Uses EVDS3 bulk API with buying (A) and selling (S) rates in TRY.
|
||
|
|
# Each currency's mid-market rate is derived from the average of buy/sell.
|
||
|
|
# Requires TCMB_API_KEY environment variable.
|
||
|
|
class TCMB < Adapter
|
||
|
|
EVDS_URL = "https://evds3.tcmb.gov.tr/igmevdsms-dis"
|
||
|
|
# Buy/sell rates in TRY for each currency. Hardcoded because the EVDS3 catalog API doesn't expose
|
||
|
|
# a clean list and the series rarely change.
|
||
|
|
# Browse: https://evds3.tcmb.gov.tr > Exchange Rates > Indicative Exchange Rates
|
||
|
|
CURRENCIES = [
|
||
|
|
"AED",
|
||
|
|
"AUD",
|
||
|
|
"AZN",
|
||
|
|
"CAD",
|
||
|
|
"CHF",
|
||
|
|
"CNY",
|
||
|
|
"DKK",
|
||
|
|
"EUR",
|
||
|
|
"GBP",
|
||
|
|
"JPY",
|
||
|
|
"KRW",
|
||
|
|
"KWD",
|
||
|
|
"KZT",
|
||
|
|
"NOK",
|
||
|
|
"PKR",
|
||
|
|
"QAR",
|
||
|
|
"RON",
|
||
|
|
"RUB",
|
||
|
|
"SAR",
|
||
|
|
"SEK",
|
||
|
|
"USD",
|
||
|
|
].freeze
|
||
|
|
|
||
|
|
SERIES = CURRENCIES.flat_map do |c|
|
||
|
|
[["#{c}_BUY", "TP.DK.#{c}.A.YTL"], ["#{c}_SELL", "TP.DK.#{c}.S.YTL"]]
|
||
|
|
end.to_h.freeze
|
||
|
|
|
||
|
|
COLUMNS = SERIES.to_h { |code, series| [series.tr(".", "_"), code] }.freeze
|
||
|
|
|
||
|
|
class << self
|
||
|
|
def api_key = ENV["TCMB_API_KEY"] || raise(Adapter::Unavailable, "no API key")
|
||
|
|
def backfill_range = 730
|
||
|
|
end
|
||
|
|
|
||
|
|
def fetch(after: nil, upto: nil)
|
||
|
|
start_date = after || Date.new(2012, 1, 2)
|
||
|
|
end_date = upto || Date.today
|
||
|
|
|
||
|
|
url = URI("#{EVDS_URL}/series=#{SERIES.values.join("-")}" \
|
||
|
|
"&startDate=#{start_date.strftime("%d-%m-%Y")}" \
|
||
|
|
"&endDate=#{end_date.strftime("%d-%m-%Y")}" \
|
||
|
|
"&type=json&frequency=1")
|
||
|
|
|
||
|
|
response = Net::HTTP.get(url, "key" => self.class.api_key)
|
||
|
|
data = JSON.parse(response)
|
||
|
|
items = data["items"] || []
|
||
|
|
|
||
|
|
items.flat_map do |item|
|
||
|
|
date = Date.strptime(item["Tarih"], "%d-%m-%Y")
|
||
|
|
raw = {}
|
||
|
|
|
||
|
|
COLUMNS.each do |column, code|
|
||
|
|
value = item[column]
|
||
|
|
next if value.nil?
|
||
|
|
|
||
|
|
raw[code] = Float(value)
|
||
|
|
end
|
||
|
|
|
||
|
|
# Mid of buying and selling for each currency → X→TRY rate
|
||
|
|
# JPY is quoted per 100 units in TCMB data (confirmed via series metadata)
|
||
|
|
CURRENCIES.filter_map do |currency|
|
||
|
|
buy = raw["#{currency}_BUY"]
|
||
|
|
sell = raw["#{currency}_SELL"]
|
||
|
|
next unless buy && sell
|
||
|
|
|
||
|
|
rate = (buy + sell) / 2
|
||
|
|
rate /= 100.0 if currency == "JPY"
|
||
|
|
|
||
|
|
{ date:, base: currency, quote: "TRY", rate: rate.round(4) }
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|