frankfurter/lib/provider/adapters/tcmb.rb
BadMark d0a43e357a
Some checks failed
CI/CD Pipeline / test (push) Has been cancelled
CI/CD Pipeline / publish (push) Has been cancelled
CI/CD Pipeline / release (push) Has been cancelled
up
2026-05-21 01:38:14 -06:00

94 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