1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-19 05:09:38 +02:00

Use Synth for exchange rates (#514)

* Switch currency seeding over to Synth

* Switch all exchange rates over to Synth
This commit is contained in:
Josh Pigford 2024-03-04 10:26:20 -06:00 committed by GitHub
parent 0f2c41477d
commit 4843cf22c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 29 additions and 84 deletions

View file

@ -6,8 +6,8 @@ HOSTED=false
APP_DOMAIN=
# Exchange Rate API
# This is used to convert between different currencies in the app. We're currently using Open Exchange Rates (openexchangerates.org) to sync exchange rate data and, at the moment, that requies a $12/mo subscription. This is NOT required to run Maybe with one currency, but it is required to sync exchange rate data if you need multiple currencies. In the future we'll be adding support for other exchange rate APIs that are cheaper/free.
OPEN_EXCHANGE_APP_ID=
# This is used to convert between different currencies in the app. We use Synth, which is a Maybe product. You can sign up for a free account at synthfinance.com.
SYNTH_API_KEY=
# Currency Configuration
# A list of currencies that you want to support. This is used to generate the list of currencies that users can select from when creating a new account.

View file

@ -2,8 +2,6 @@ class DailyExchangeRateJob < ApplicationJob
queue_as :default
def perform
app_id = ENV["OPEN_EXCHANGE_APP_ID"]
# Get the last date for which exchange rates were fetched for each currency
last_fetched_dates = ExchangeRate.group(:base_currency).maximum(:date)
@ -11,10 +9,12 @@ class DailyExchangeRateJob < ApplicationJob
Currency.all.each do |currency|
last_fetched_date = last_fetched_dates[currency.iso_code] || Date.yesterday
next_day = last_fetched_date + 1.day
response = Faraday.get("https://openexchangerates.org/api/historical/#{next_day}.json") do |req|
req.params["app_id"] = app_id
req.params["base"] = currency.iso_code
req.params["symbols"] = Currency.where.not(iso_code: currency.iso_code).pluck(:iso_code).join(",")
response = Faraday.get("https://api.synthfinance.com/rates/historical") do |req|
req.headers["Authorization"] = "Bearer #{ENV["SYNTH_API_KEY"]}"
req.params["date"] = next_day.to_s
req.params["from"] = currency.iso_code
req.params["to"] = Currency.where.not(iso_code: currency.iso_code).pluck(:iso_code).join(",")
end
if response.success?

View file

@ -3,18 +3,18 @@ namespace :currencies do
task seed: :environment do
currencies = ENV["CURRENCIES"].split(",")
if currencies.count > 1 && ENV["OPEN_EXCHANGE_APP_ID"].present?
url = "https://openexchangerates.org/api/currencies.json"
if currencies.count > 1 && ENV["SYNTH_API_KEY"].present?
url = "https://api.synthfinance.com/currencies"
response = Faraday.get(url) do |req|
req.params["app_id"] = ENV["OPEN_EXCHANGE_APP_ID"]
req.headers["Authorization"] = "Bearer #{ENV["SYNTH_API_KEY"]}"
end
oxr_currencies = JSON.parse(response.body)
synth_currencies = JSON.parse(response.body)
currencies.each do |iso_code|
Currency.find_or_create_by(iso_code: iso_code) do |c|
c.name = oxr_currencies[iso_code]
c.name = synth_currencies["data"].find { |currency| currency["iso_code"] == iso_code.downcase }["name"]
end
end

View file

@ -1,30 +1,17 @@
namespace :exchange_rates do
desc "Fetch exchange rates from openexchangerates.org"
desc "Fetch exchange rates from Synth API"
task sync: :environment do
app_id = ENV["OPEN_EXCHANGE_APP_ID"]
MININUM_DATE_RANGE = 30.days
MAXIMUM_DATE_RANGE = 120.days
# First, check what plan the user is on at OXR
# If the user is on the Developer plan, we can only fetch exchange rates for the past 30 days and must use the historical endpoint
account_check = Faraday.get("https://openexchangerates.org/api/usage.json") do |req|
req.params["app_id"] = app_id
end
account_details = JSON.parse(account_check.body)
quota_limit = account_details["data"]["usage"]["requests_quota"]
if quota_limit <= 10000
# First loop through all Currency records and fetch exchange rates for each, but use the historical endpoint, which only allows fetching one day at a time. For each currency, set the base currency to the currency's iso_code and the symbols to all other currencies' iso_codes
Currency.all.each do |currency|
(Date.today - MININUM_DATE_RANGE).upto(Date.today) do |date|
response = Faraday.get("https://openexchangerates.org/api/historical/#{date}.json") do |req|
req.params["app_id"] = app_id
req.params["base"] = currency.iso_code
req.params["symbols"] = Currency.where.not(iso_code: currency.iso_code).pluck(:iso_code).join(",")
(Date.today - 30.days).upto(Date.today) do |date|
response = Faraday.get("https://api.synthfinance.com/rates/historical") do |req|
req.headers["Authorization"] = "Bearer #{ENV["SYNTH_API_KEY"]}"
req.params["date"] = date.to_s
req.params["from"] = currency.iso_code
req.params["to"] = Currency.where.not(iso_code: currency.iso_code).pluck(:iso_code).join(",")
end
if response.success?
rates = JSON.parse(response.body)["rates"]
rates = JSON.parse(response.body)["data"]["rates"]
rates.each do |currency_iso_code, value|
ExchangeRate.find_or_create_by(date: date, base_currency: currency.iso_code, converted_currency: currency_iso_code) do |exchange_rate|
@ -37,47 +24,5 @@ namespace :exchange_rates do
end
end
end
else
# Use Faraday to make a request openexchangerates.org time series endpoint
# Use the response to create or update exchange rates in the database
url = "https://openexchangerates.org/api/time-series.json"
start_date = (Date.today - MAXIMUM_DATE_RANGE).to_s
end_date = Date.today.to_s
# Loop through all Currency records and fetch exchange rates for each
Currency.all.each do |currency|
start_period = Date.parse(start_date)
end_period = Date.parse(end_date)
while start_period < end_period
current_end_date = [ start_period + 30.days, end_period ].min
response = Faraday.get(url) do |req|
req.params["app_id"] = app_id
req.params["start"] = start_period.to_s
req.params["end"] = current_end_date.to_s
req.params["base"] = currency.iso_code
req.params["symbols"] = Currency.where.not(iso_code: currency.iso_code).pluck(:iso_code).join(",")
end
if response.success?
rates = JSON.parse(response.body)["rates"]
rates.each do |date, rate|
rate.each do |currency_iso_code, value|
ExchangeRate.find_or_create_by(date: date, base_currency: currency.iso_code, converted_currency: currency_iso_code) do |exchange_rate|
exchange_rate.rate = value
end
puts "#{currency.iso_code} to #{currency_iso_code} on #{date}: #{value}"
end
end
else
puts "Failed to fetch exchange rates for period #{start_period} to #{current_end_date}: #{response.status}"
end
start_period = current_end_date + 1.day
end
end
end
end
end