3 market api #8
@@ -6,9 +6,6 @@
|
|||||||
# Vedi https://docs.agno.com/examples/models per vedere tutti i modelli supportati
|
# Vedi https://docs.agno.com/examples/models per vedere tutti i modelli supportati
|
||||||
GOOGLE_API_KEY=
|
GOOGLE_API_KEY=
|
||||||
|
|
||||||
# Inserire il percorso di installazione di ollama (es. /usr/share/ollama/.ollama)
|
|
||||||
# attenzione che fra Linux nativo e WSL il percorso è diverso
|
|
||||||
OLLAMA_MODELS_PATH=
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Configurazioni per gli agenti di mercato
|
# Configurazioni per gli agenti di mercato
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -20,9 +17,8 @@ COINBASE_API_SECRET=
|
|||||||
# https://www.cryptocompare.com/cryptopian/api-keys
|
# https://www.cryptocompare.com/cryptopian/api-keys
|
||||||
CRYPTOCOMPARE_API_KEY=
|
CRYPTOCOMPARE_API_KEY=
|
||||||
|
|
||||||
# Binance API per Market Agent
|
# https://www.binance.com/en/my/settings/api-management
|
||||||
# Ottenibili da: https://www.binance.com/en/my/settings/api-management
|
# Non necessario per operazioni in sola lettura
|
||||||
# Supporta sia API autenticate che pubbliche (PublicBinance)
|
|
||||||
BINANCE_API_KEY=
|
BINANCE_API_KEY=
|
||||||
BINANCE_API_SECRET=
|
BINANCE_API_SECRET=
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ from app.markets import (
|
|||||||
CoinBaseWrapper,
|
CoinBaseWrapper,
|
||||||
CryptoCompareWrapper,
|
CryptoCompareWrapper,
|
||||||
BinanceWrapper,
|
BinanceWrapper,
|
||||||
PublicBinanceAgent,
|
|
||||||
BaseWrapper
|
BaseWrapper
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -239,13 +238,6 @@ def initialize_providers() -> Dict[str, BaseWrapper]:
|
|||||||
providers = {}
|
providers = {}
|
||||||
env_vars = check_environment_variables()
|
env_vars = check_environment_variables()
|
||||||
|
|
||||||
# PublicBinanceAgent (sempre disponibile)
|
|
||||||
try:
|
|
||||||
providers["PublicBinance"] = PublicBinanceAgent()
|
|
||||||
print("✅ PublicBinanceAgent inizializzato con successo")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"❌ Errore nell'inizializzazione di PublicBinanceAgent: {e}")
|
|
||||||
|
|
||||||
# CryptoCompareWrapper
|
# CryptoCompareWrapper
|
||||||
if env_vars["CRYPTOCOMPARE_API_KEY"]:
|
if env_vars["CRYPTOCOMPARE_API_KEY"]:
|
||||||
try:
|
try:
|
||||||
@@ -267,14 +259,12 @@ def initialize_providers() -> Dict[str, BaseWrapper]:
|
|||||||
print("⚠️ CoinBaseWrapper saltato: credenziali Coinbase non complete")
|
print("⚠️ CoinBaseWrapper saltato: credenziali Coinbase non complete")
|
||||||
|
|
||||||
# BinanceWrapper
|
# BinanceWrapper
|
||||||
if env_vars["BINANCE_API_KEY"] and env_vars["BINANCE_API_SECRET"]:
|
|
||||||
try:
|
try:
|
||||||
providers["Binance"] = BinanceWrapper()
|
providers["Binance"] = BinanceWrapper()
|
||||||
print("✅ BinanceWrapper inizializzato con successo")
|
print("✅ BinanceWrapper inizializzato con successo")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Errore nell'inizializzazione di BinanceWrapper: {e}")
|
print(f"❌ Errore nell'inizializzazione di BinanceWrapper: {e}")
|
||||||
else:
|
|
||||||
print("⚠️ BinanceWrapper saltato: credenziali Binance non complete")
|
|
||||||
|
|
||||||
return providers
|
return providers
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,47 @@ from pydantic import BaseModel
|
|||||||
|
|
||||||
class BaseWrapper:
|
class BaseWrapper:
|
||||||
"""
|
"""
|
||||||
Interfaccia per i wrapper delle API di mercato.
|
Base class for market API wrappers.
|
||||||
Implementa i metodi di base che ogni wrapper deve avere.
|
All market API wrappers should inherit from this class and implement the methods.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_product(self, asset_id: str) -> 'ProductInfo':
|
def get_product(self, asset_id: str) -> 'ProductInfo':
|
||||||
|
"""
|
||||||
|
Get product information for a specific asset ID.
|
||||||
|
Args:
|
||||||
|
asset_id (str): The asset ID to retrieve information for.
|
||||||
|
Returns:
|
||||||
|
ProductInfo: An object containing product information.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_products(self, asset_ids: list[str]) -> list['ProductInfo']:
|
def get_products(self, asset_ids: list[str]) -> list['ProductInfo']:
|
||||||
|
"""
|
||||||
|
Get product information for multiple asset IDs.
|
||||||
|
Args:
|
||||||
|
asset_ids (list[str]): The list of asset IDs to retrieve information for.
|
||||||
|
Returns:
|
||||||
|
list[ProductInfo]: A list of objects containing product information.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_all_products(self) -> list['ProductInfo']:
|
def get_all_products(self) -> list['ProductInfo']:
|
||||||
|
"""
|
||||||
|
Get product information for all available assets.
|
||||||
|
Returns:
|
||||||
|
list[ProductInfo]: A list of objects containing product information.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_historical_prices(self, asset_id: str = "BTC", limit: int = 100) -> list['Price']:
|
def get_historical_prices(self, asset_id: str = "BTC", limit: int = 100) -> list['Price']:
|
||||||
|
"""
|
||||||
|
Get historical price data for a specific asset ID.
|
||||||
|
Args:
|
||||||
|
asset_id (str): The asset ID to retrieve price data for.
|
||||||
|
limit (int): The maximum number of price data points to return.
|
||||||
|
Returns:
|
||||||
|
list[Price]: A list of Price objects.
|
||||||
|
"""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class ProductInfo(BaseModel):
|
class ProductInfo(BaseModel):
|
||||||
|
|||||||
@@ -23,10 +23,7 @@ class BinanceWrapper(BaseWrapper):
|
|||||||
|
|
||||||
def __init__(self, currency: str = "USDT"):
|
def __init__(self, currency: str = "USDT"):
|
||||||
api_key = os.getenv("BINANCE_API_KEY")
|
api_key = os.getenv("BINANCE_API_KEY")
|
||||||
assert api_key is not None, "API key is required"
|
|
||||||
|
|
||||||
api_secret = os.getenv("BINANCE_API_SECRET")
|
api_secret = os.getenv("BINANCE_API_SECRET")
|
||||||
assert api_secret is not None, "API secret is required"
|
|
||||||
|
|
||||||
self.currency = currency
|
self.currency = currency
|
||||||
self.client = Client(api_key=api_key, api_secret=api_secret)
|
self.client = Client(api_key=api_key, api_secret=api_secret)
|
||||||
|
|||||||
@@ -1,218 +0,0 @@
|
|||||||
"""
|
|
||||||
Versione pubblica di Binance per accesso ai dati pubblici senza autenticazione.
|
|
||||||
|
|
||||||
Questa implementazione estende BaseWrapper per mantenere coerenza
|
|
||||||
con l'architettura del modulo markets.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from typing import Optional, Dict, Any
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
from binance.client import Client
|
|
||||||
from .base import BaseWrapper, ProductInfo, Price
|
|
||||||
|
|
||||||
|
|
||||||
class PublicBinanceAgent(BaseWrapper):
|
|
||||||
"""
|
|
||||||
Agent per l'accesso ai dati pubblici di Binance.
|
|
||||||
|
|
||||||
Utilizza l'API pubblica di Binance per ottenere informazioni
|
|
||||||
sui prezzi e sui mercati senza richiedere autenticazione.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
"""Inizializza il client pubblico senza credenziali."""
|
|
||||||
self.client = Client()
|
|
||||||
|
|
||||||
def __format_symbol(self, asset_id: str) -> str:
|
|
||||||
"""
|
|
||||||
Formatta l'asset_id per Binance (es. BTC -> BTCUSDT).
|
|
||||||
|
|
||||||
Args:
|
|
||||||
asset_id: ID dell'asset (es. "BTC", "ETH")
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Simbolo formattato per Binance
|
|
||||||
"""
|
|
||||||
if asset_id.endswith("USDT") or asset_id.endswith("BUSD"):
|
|
||||||
return asset_id
|
|
||||||
return f"{asset_id}USDT"
|
|
||||||
|
|
||||||
def get_product(self, asset_id: str) -> ProductInfo:
|
|
||||||
"""
|
|
||||||
Ottiene informazioni su un singolo prodotto.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
asset_id: ID dell'asset (es. "BTC")
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Oggetto ProductInfo con le informazioni del prodotto
|
|
||||||
"""
|
|
||||||
symbol = self.__format_symbol(asset_id)
|
|
||||||
try:
|
|
||||||
ticker = self.client.get_symbol_ticker(symbol=symbol)
|
|
||||||
ticker_24h = self.client.get_ticker(symbol=symbol)
|
|
||||||
return ProductInfo.from_binance(ticker, ticker_24h)
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Errore nel recupero del prodotto {asset_id}: {e}")
|
|
||||||
return ProductInfo(id=asset_id, symbol=asset_id)
|
|
||||||
|
|
||||||
def get_products(self, asset_ids: list[str]) -> list[ProductInfo]:
|
|
||||||
"""
|
|
||||||
Ottiene informazioni su più prodotti.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
asset_ids: Lista di ID degli asset
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Lista di oggetti ProductInfo
|
|
||||||
"""
|
|
||||||
products = []
|
|
||||||
for asset_id in asset_ids:
|
|
||||||
product = self.get_product(asset_id)
|
|
||||||
products.append(product)
|
|
||||||
return products
|
|
||||||
|
|
||||||
def get_all_products(self) -> list[ProductInfo]:
|
|
||||||
"""
|
|
||||||
Ottiene informazioni su tutti i prodotti disponibili.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Lista di oggetti ProductInfo per i principali asset
|
|
||||||
"""
|
|
||||||
# Per la versione pubblica, restituiamo solo i principali asset
|
|
||||||
major_assets = ["BTC", "ETH", "BNB", "ADA", "DOT", "LINK", "LTC", "XRP"]
|
|
||||||
return self.get_products(major_assets)
|
|
||||||
|
|
||||||
def get_historical_prices(self, asset_id: str = "BTC") -> list[Price]:
|
|
||||||
"""
|
|
||||||
Ottiene i prezzi storici per un asset.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
asset_id: ID dell'asset (default: "BTC")
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Lista di oggetti Price con i dati storici
|
|
||||||
"""
|
|
||||||
symbol = self.__format_symbol(asset_id)
|
|
||||||
try:
|
|
||||||
# Ottieni candele degli ultimi 30 giorni
|
|
||||||
end_time = datetime.now()
|
|
||||||
start_time = end_time - timedelta(days=30)
|
|
||||||
|
|
||||||
klines = self.client.get_historical_klines(
|
|
||||||
symbol,
|
|
||||||
Client.KLINE_INTERVAL_1DAY,
|
|
||||||
start_time.strftime("%d %b %Y %H:%M:%S"),
|
|
||||||
end_time.strftime("%d %b %Y %H:%M:%S")
|
|
||||||
)
|
|
||||||
|
|
||||||
prices = []
|
|
||||||
for kline in klines:
|
|
||||||
price = Price(
|
|
||||||
open=float(kline[1]),
|
|
||||||
high=float(kline[2]),
|
|
||||||
low=float(kline[3]),
|
|
||||||
close=float(kline[4]),
|
|
||||||
volume=float(kline[5]),
|
|
||||||
time=str(datetime.fromtimestamp(kline[0] / 1000))
|
|
||||||
)
|
|
||||||
prices.append(price)
|
|
||||||
|
|
||||||
return prices
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Errore nel recupero dei prezzi storici per {asset_id}: {e}")
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_public_prices(self, symbols: Optional[list[str]] = None) -> Optional[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Ottiene i prezzi pubblici per i simboli specificati.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
symbols: Lista di simboli da recuperare (es. ["BTCUSDT", "ETHUSDT"]).
|
|
||||||
Se None, recupera BTC e ETH di default.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dizionario con i prezzi e informazioni sulla fonte, o None in caso di errore.
|
|
||||||
"""
|
|
||||||
if symbols is None:
|
|
||||||
symbols = ["BTCUSDT", "ETHUSDT"]
|
|
||||||
|
|
||||||
try:
|
|
||||||
prices = {}
|
|
||||||
for symbol in symbols:
|
|
||||||
ticker = self.client.get_symbol_ticker(symbol=symbol)
|
|
||||||
# Converte BTCUSDT -> BTC_USD per consistenza
|
|
||||||
clean_symbol = symbol.replace("USDT", "_USD").replace("BUSD", "_USD")
|
|
||||||
prices[clean_symbol] = float(ticker['price'])
|
|
||||||
|
|
||||||
return {
|
|
||||||
**prices,
|
|
||||||
'source': 'binance_public',
|
|
||||||
'timestamp': self.client.get_server_time()['serverTime']
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Errore nel recupero dei prezzi pubblici: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_24hr_ticker(self, symbol: str) -> Optional[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Ottiene le statistiche 24h per un simbolo specifico.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
symbol: Simbolo del trading pair (es. "BTCUSDT")
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dizionario con le statistiche 24h o None in caso di errore.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
ticker = self.client.get_ticker(symbol=symbol)
|
|
||||||
return {
|
|
||||||
'symbol': ticker['symbol'],
|
|
||||||
'price': float(ticker['lastPrice']),
|
|
||||||
'price_change': float(ticker['priceChange']),
|
|
||||||
'price_change_percent': float(ticker['priceChangePercent']),
|
|
||||||
'high_24h': float(ticker['highPrice']),
|
|
||||||
'low_24h': float(ticker['lowPrice']),
|
|
||||||
'volume_24h': float(ticker['volume']),
|
|
||||||
'source': 'binance_public'
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Errore nel recupero del ticker 24h per {symbol}: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_exchange_info(self) -> Optional[Dict[str, Any]]:
|
|
||||||
"""
|
|
||||||
Ottiene informazioni generali sull'exchange.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dizionario con informazioni sull'exchange o None in caso di errore.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
info = self.client.get_exchange_info()
|
|
||||||
return {
|
|
||||||
'timezone': info['timezone'],
|
|
||||||
'server_time': info['serverTime'],
|
|
||||||
'symbols_count': len(info['symbols']),
|
|
||||||
'source': 'binance_public'
|
|
||||||
}
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Errore nel recupero delle informazioni exchange: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
# Esempio di utilizzo
|
|
||||||
if __name__ == "__main__":
|
|
||||||
# Uso senza credenziali
|
|
||||||
public_agent = PublicBinanceAgent()
|
|
||||||
|
|
||||||
# Ottieni prezzi di default (BTC e ETH)
|
|
||||||
public_prices = public_agent.get_public_prices()
|
|
||||||
print("Prezzi pubblici:", public_prices)
|
|
||||||
|
|
||||||
# Ottieni statistiche 24h per BTC
|
|
||||||
btc_stats = public_agent.get_24hr_ticker("BTCUSDT")
|
|
||||||
print("Statistiche BTC 24h:", btc_stats)
|
|
||||||
|
|
||||||
# Ottieni informazioni exchange
|
|
||||||
exchange_info = public_agent.get_exchange_info()
|
|
||||||
print("Info exchange:", exchange_info)
|
|
||||||
@@ -23,6 +23,7 @@ def pytest_configure(config:pytest.Config):
|
|||||||
("social", "marks tests that use social media"),
|
("social", "marks tests that use social media"),
|
||||||
("limited", "marks tests that have limited execution due to API constraints"),
|
("limited", "marks tests that have limited execution due to API constraints"),
|
||||||
("wrapper", "marks tests for wrapper handler"),
|
("wrapper", "marks tests for wrapper handler"),
|
||||||
|
("tools", "marks tests for tools"),
|
||||||
]
|
]
|
||||||
for marker in markers:
|
for marker in markers:
|
||||||
line = f"{marker[0]}: {marker[1]}"
|
line = f"{marker[0]}: {marker[1]}"
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ from app.agents.market_agent import MarketToolkit
|
|||||||
from app.markets import MarketAPIsTool
|
from app.markets import MarketAPIsTool
|
||||||
|
|
||||||
@pytest.mark.limited # usa molte api calls e non voglio esaurire le chiavi api
|
@pytest.mark.limited # usa molte api calls e non voglio esaurire le chiavi api
|
||||||
|
@pytest.mark.tools
|
||||||
|
@pytest.mark.api
|
||||||
class TestMarketAPIsTool:
|
class TestMarketAPIsTool:
|
||||||
def test_wrapper_initialization(self):
|
def test_wrapper_initialization(self):
|
||||||
market_wrapper = MarketAPIsTool("USD")
|
market_wrapper = MarketAPIsTool("USD")
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.utils.market_data_aggregator import MarketDataAggregator
|
from app.utils.market_data_aggregator import MarketDataAggregator
|
||||||
from app.utils.aggregated_models import AggregatedProductInfo
|
from app.utils.aggregated_models import AggregatedProductInfo
|
||||||
from app.markets.base import ProductInfo, Price
|
from app.markets.base import ProductInfo, Price
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.limited
|
||||||
|
@pytest.mark.market
|
||||||
|
@pytest.mark.api
|
||||||
class TestMarketDataAggregator:
|
class TestMarketDataAggregator:
|
||||||
|
|
||||||
def test_initialization(self):
|
def test_initialization(self):
|
||||||
@@ -84,7 +86,3 @@ class TestMarketDataAggregator:
|
|||||||
assert len(aggregated._metadata.sources_used) > 0
|
assert len(aggregated._metadata.sources_used) > 0
|
||||||
assert aggregated._metadata.aggregation_timestamp != ""
|
assert aggregated._metadata.aggregation_timestamp != ""
|
||||||
# La confidence può essere 0.0 se ci sono fonti "unknown"
|
# La confidence può essere 0.0 se ci sono fonti "unknown"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
pytest.main([__file__])
|
|
||||||
Reference in New Issue
Block a user