mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 14:31:25 +00:00
Fix Yahoo minor unit handling (#335)
Some security prices get returned in non-standard minor units (eg. `GBp` for Great British Pence, instead of `GBP` for Great British Pounds), leading to incorrect handling of the returned price data. This commit adds conversion logic for the two currencies I've been able to find examples of this affecting.
This commit is contained in:
committed by
GitHub
parent
4475d85ab2
commit
ff43e71dec
@@ -248,18 +248,21 @@ class Provider::YahooFinance < Provider
|
||||
closes = quotes["close"] || []
|
||||
|
||||
# Get currency from metadata
|
||||
currency = chart_data.dig("meta", "currency") || "USD"
|
||||
raw_currency = chart_data.dig("meta", "currency") || "USD"
|
||||
|
||||
prices = []
|
||||
timestamps.each_with_index do |timestamp, index|
|
||||
close_price = closes[index]
|
||||
next if close_price.nil? # Skip days with no data (weekends, holidays)
|
||||
|
||||
# Normalize currency and price to handle minor units
|
||||
normalized_currency, normalized_price = normalize_currency_and_price(raw_currency, close_price.to_f)
|
||||
|
||||
prices << Price.new(
|
||||
symbol: symbol,
|
||||
date: Time.at(timestamp).to_date,
|
||||
price: close_price.to_f,
|
||||
currency: currency,
|
||||
price: normalized_price,
|
||||
currency: normalized_currency,
|
||||
exchange_operating_mic: exchange_operating_mic
|
||||
)
|
||||
end
|
||||
@@ -277,6 +280,29 @@ class Provider::YahooFinance < Provider
|
||||
ENV["YAHOO_FINANCE_URL"] || "https://query1.finance.yahoo.com"
|
||||
end
|
||||
|
||||
# ================================
|
||||
# Currency Normalization
|
||||
# ================================
|
||||
|
||||
# Yahoo Finance sometimes returns currencies in minor units (pence, cents)
|
||||
# This is not part of ISO 4217 but is a convention used by financial data providers
|
||||
# Mapping of Yahoo Finance minor unit codes to standard currency codes and conversion multipliers
|
||||
MINOR_CURRENCY_CONVERSIONS = {
|
||||
"GBp" => { currency: "GBP", multiplier: 0.01 }, # British pence to pounds (eg. https://finance.yahoo.com/quote/IITU.L/)
|
||||
"ZAc" => { currency: "ZAR", multiplier: 0.01 } # South African cents to rand (eg. https://finance.yahoo.com/quote/JSE.JO)
|
||||
}.freeze
|
||||
|
||||
# Normalizes Yahoo Finance currency codes and prices
|
||||
# Returns [currency_code, price] with currency converted to standard ISO code
|
||||
# and price converted from minor units to major units if applicable
|
||||
def normalize_currency_and_price(currency, price)
|
||||
if conversion = MINOR_CURRENCY_CONVERSIONS[currency]
|
||||
[ conversion[:currency], price * conversion[:multiplier] ]
|
||||
else
|
||||
[ currency, price ]
|
||||
end
|
||||
end
|
||||
|
||||
# ================================
|
||||
# Validation
|
||||
# ================================
|
||||
|
||||
@@ -215,4 +215,34 @@ class Provider::YahooFinanceTest < ActiveSupport::TestCase
|
||||
@provider.send(:validate_date_range!, Date.current - 5.years, Date.current)
|
||||
end
|
||||
end
|
||||
|
||||
# ================================
|
||||
# Currency Normalization Tests
|
||||
# ================================
|
||||
|
||||
test "normalize_currency_and_price converts GBp to GBP" do
|
||||
currency, price = @provider.send(:normalize_currency_and_price, "GBp", 1234.56)
|
||||
assert_equal "GBP", currency
|
||||
assert_equal 12.3456, price
|
||||
end
|
||||
|
||||
test "normalize_currency_and_price converts ZAc to ZAR" do
|
||||
currency, price = @provider.send(:normalize_currency_and_price, "ZAc", 5000.0)
|
||||
assert_equal "ZAR", currency
|
||||
assert_equal 50.0, price
|
||||
end
|
||||
|
||||
test "normalize_currency_and_price leaves standard currencies unchanged" do
|
||||
currency, price = @provider.send(:normalize_currency_and_price, "USD", 100.50)
|
||||
assert_equal "USD", currency
|
||||
assert_equal 100.50, price
|
||||
|
||||
currency, price = @provider.send(:normalize_currency_and_price, "GBP", 50.25)
|
||||
assert_equal "GBP", currency
|
||||
assert_equal 50.25, price
|
||||
|
||||
currency, price = @provider.send(:normalize_currency_and_price, "EUR", 75.75)
|
||||
assert_equal "EUR", currency
|
||||
assert_equal 75.75, price
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user