1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-04 21:15:19 +02:00

Add security prices provider (Synth integration) (#1039)

* User tickers as primary lookup symbol instead of isin

* Add security price provider

* Fetch security prices in bulk to improve sync performance

* Fetch prices in bulk, better mocking for tests
This commit is contained in:
Zach Gollwitzer 2024-08-01 19:43:23 -04:00 committed by GitHub
parent c70c8b6d86
commit 453a54e5e6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 584 additions and 118 deletions

View file

@ -5,6 +5,27 @@ class Provider::Synth
@api_key = api_key
end
def fetch_security_prices(ticker:, start_date:, end_date:)
prices = paginate(
"#{base_url}/tickers/#{ticker}/open-close",
start_date: start_date,
end_date: end_date
) do |body|
body.dig("prices").map do |price|
{
date: price.dig("date"),
price: price.dig("close")&.to_f || price.dig("open")&.to_f,
currency: "USD"
}
end
end
SecurityPriceResponse.new \
prices: prices,
success?: true,
raw_response: prices.to_json
end
def fetch_exchange_rate(from:, to:, date:)
retrying Provider::Base.known_transient_errors do |on_last_attempt|
response = Faraday.get("#{base_url}/rates/historical") do |req|
@ -33,9 +54,11 @@ class Provider::Synth
end
private
attr_reader :api_key
ExchangeRateResponse = Struct.new :rate, :success?, :error, :raw_response, keyword_init: true
SecurityPriceResponse = Struct.new :prices, :success?, :error, :raw_response, keyword_init: true
def base_url
"https://api.synthfinance.com"
@ -43,9 +66,43 @@ class Provider::Synth
def build_error(response)
Provider::Base::ProviderError.new(<<~ERROR)
Failed to fetch exchange rate from #{self.class}
Failed to fetch data from #{self.class}
Status: #{response.status}
Body: #{response.body.inspect}
ERROR
end
def fetch_page(url, page, params = {})
Faraday.get(url) do |req|
req.headers["Authorization"] = "Bearer #{api_key}"
params.each { |k, v| req.params[k.to_s] = v.to_s }
req.params["page"] = page
end
end
def paginate(url, params = {})
results = []
page = 1
current_page = 0
total_pages = 1
while current_page < total_pages
response = fetch_page(url, page, params)
if response.success?
body = JSON.parse(response.body)
page_results = yield(body)
results.concat(page_results)
current_page = body.dig("paging", "current_page")
total_pages = body.dig("paging", "total_pages")
page += 1
else
raise build_error(response)
end
end
results
end
end