diff --git a/app/apps/currencies/exchange_rates/fetcher.py b/app/apps/currencies/exchange_rates/fetcher.py index 4810a18..00b5786 100644 --- a/app/apps/currencies/exchange_rates/fetcher.py +++ b/app/apps/currencies/exchange_rates/fetcher.py @@ -6,6 +6,7 @@ from django.utils import timezone from apps.currencies.exchange_rates.providers import ( SynthFinanceProvider, + SynthFinanceStockProvider, CoinGeckoFreeProvider, CoinGeckoProProvider, ) @@ -17,6 +18,7 @@ logger = logging.getLogger(__name__) # Map service types to provider classes PROVIDER_MAPPING = { "synth_finance": SynthFinanceProvider, + "synth_finance_stock": SynthFinanceStockProvider, "coingecko_free": CoinGeckoFreeProvider, "coingecko_pro": CoinGeckoProProvider, } diff --git a/app/apps/currencies/exchange_rates/providers.py b/app/apps/currencies/exchange_rates/providers.py index fb61aa7..243f521 100644 --- a/app/apps/currencies/exchange_rates/providers.py +++ b/app/apps/currencies/exchange_rates/providers.py @@ -150,3 +150,68 @@ class CoinGeckoProProvider(CoinGeckoFreeProvider): super().__init__(api_key) self.session = requests.Session() self.session.headers.update({"x-cg-pro-api-key": api_key}) + + +class SynthFinanceStockProvider(ExchangeRateProvider): + """Implementation for Synth Finance API Real-Time Prices endpoint (synthfinance.com)""" + + BASE_URL = "https://api.synthfinance.com/tickers" + rates_inverted = True + + def __init__(self, api_key: str = None): + super().__init__(api_key) + self.session = requests.Session() + self.session.headers.update( + {"Authorization": f"Bearer {self.api_key}", "accept": "application/json"} + ) + + def get_rates( + self, target_currencies: QuerySet, exchange_currencies: set + ) -> List[Tuple[Currency, Currency, Decimal]]: + results = [] + + for currency in target_currencies: + if currency.exchange_currency not in exchange_currencies: + continue + + try: + # Same currency has rate of 1 + if currency.code == currency.exchange_currency.code: + rate = Decimal("1") + results.append((currency.exchange_currency, currency, rate)) + continue + + # Fetch real-time price for this ticker + response = self.session.get( + f"{self.BASE_URL}/{currency.code}/real-time" + ) + response.raise_for_status() + data = response.json() + + # Use fair market value as the rate + rate = Decimal(data["data"]["fair_market_value"]) + results.append((currency.exchange_currency, currency, rate)) + + # Log API usage + credits_used = data["meta"]["credits_used"] + credits_remaining = data["meta"]["credits_remaining"] + logger.info( + f"Synth Finance API call for {currency.code}: {credits_used} credits used, {credits_remaining} remaining" + ) + except requests.RequestException as e: + logger.error( + f"Error fetching rate from Synth Finance API for ticker {currency.code}: {e}", + exc_info=True, + ) + except KeyError as e: + logger.error( + f"Unexpected response structure from Synth Finance API for ticker {currency.code}: {e}", + exc_info=True, + ) + except Exception as e: + logger.error( + f"Unexpected error processing Synth Finance data for ticker {currency.code}: {e}", + exc_info=True, + ) + + return results diff --git a/app/apps/currencies/migrations/0012_alter_exchangerateservice_service_type.py b/app/apps/currencies/migrations/0012_alter_exchangerateservice_service_type.py new file mode 100644 index 0000000..75319c3 --- /dev/null +++ b/app/apps/currencies/migrations/0012_alter_exchangerateservice_service_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.6 on 2025-03-02 01:28 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('currencies', '0011_remove_exchangerateservice_fetch_interval_hours_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='exchangerateservice', + name='service_type', + field=models.CharField(choices=[('synth_finance', 'Synth Finance'), ('synth_finance_stock', 'Synth Finance Stock'), ('coingecko_free', 'CoinGecko (Demo/Free)'), ('coingecko_pro', 'CoinGecko (Pro)')], max_length=255, verbose_name='Service Type'), + ), + ] diff --git a/app/apps/currencies/models.py b/app/apps/currencies/models.py index d58b9f4..b871281 100644 --- a/app/apps/currencies/models.py +++ b/app/apps/currencies/models.py @@ -92,6 +92,7 @@ class ExchangeRateService(models.Model): class ServiceType(models.TextChoices): SYNTH_FINANCE = "synth_finance", "Synth Finance" + SYNTH_FINANCE_STOCK = "synth_finance_stock", "Synth Finance Stock" COINGECKO_FREE = "coingecko_free", "CoinGecko (Demo/Free)" COINGECKO_PRO = "coingecko_pro", "CoinGecko (Pro)"