WIP: Fix Aggregazione market Product #67
@@ -34,33 +34,28 @@ class ProductInfo(BaseModel):
|
||||
product.provider = provider_name
|
||||
symbols_infos.setdefault(product.symbol, []).append(product)
|
||||
|
||||
# Aggregazione per ogni symbol
|
||||
# Aggregazione per ogni symbol usando aggregate_single_asset
|
||||
aggregated_products: list[ProductInfo] = []
|
||||
for symbol, product_list in symbols_infos.items():
|
||||
product = ProductInfo()
|
||||
|
||||
product.id = f"{symbol}_AGGREGATED"
|
||||
product.symbol = symbol
|
||||
product.currency = next((p.currency for p in product_list if p.currency), "")
|
||||
|
||||
# Raccogliamo i provider che hanno fornito dati
|
||||
providers = [p.provider for p in product_list if p.provider]
|
||||
product.provider = ", ".join(set(providers)) if providers else "AGGREGATED"
|
||||
|
||||
# Calcolo del volume medio
|
||||
volume_sum = sum(p.volume_24h for p in product_list if p.volume_24h > 0)
|
||||
product.volume_24h = volume_sum / len(product_list) if product_list else 0.0
|
||||
|
||||
# Calcolo del prezzo pesato per volume (VWAP - Volume Weighted Average Price)
|
||||
if volume_sum > 0:
|
||||
prices_weighted = sum(p.price * p.volume_24h for p in product_list if p.volume_24h > 0)
|
||||
product.price = prices_weighted / volume_sum
|
||||
else:
|
||||
# Se non c'è volume, facciamo una media semplice dei prezzi
|
||||
valid_prices = [p.price for p in product_list if p.price > 0]
|
||||
product.price = sum(valid_prices) / len(valid_prices) if valid_prices else 0.0
|
||||
|
||||
aggregated_products.append(product)
|
||||
try:
|
||||
# Usa aggregate_single_asset per aggregare ogni simbolo
|
||||
aggregated = ProductInfo.aggregate_single_asset(product_list)
|
||||
|
||||
# aggregate_single_asset calcola il volume medio, ma per multi_assets
|
||||
# vogliamo il volume totale. Ricalcoliamo il volume come somma dopo il filtro USD
|
||||
# Dobbiamo rifare il filtro USD per contare correttamente
|
||||
currencies = set(p.currency for p in product_list if p.currency)
|
||||
if len(currencies) > 1:
|
||||
product_list = [p for p in product_list if p.currency.upper() == "USD"]
|
||||
|
||||
# Volume totale
|
||||
aggregated.volume_24h = sum(p.volume_24h for p in product_list if p.volume_24h > 0)
|
||||
|
||||
aggregated_products.append(aggregated)
|
||||
except ValueError:
|
||||
# Se aggregate_single_asset fallisce (es. no USD when currencies differ), salta
|
||||
continue
|
||||
|
||||
return aggregated_products
|
||||
|
||||
@staticmethod
|
||||
@@ -100,6 +95,14 @@ class ProductInfo(BaseModel):
|
||||
if not assets_list:
|
||||
raise ValueError("aggregate_single_asset requires at least one ProductInfo")
|
||||
|
||||
# Controllo valuta: se non sono tutte uguali, filtra solo USD
|
||||
currencies = set(p.currency for p in assets_list if p.currency)
|
||||
if len(currencies) > 1:
|
||||
# Valute diverse: filtra solo USD
|
||||
assets_list = [p for p in assets_list if p.currency.upper() == "USD"]
|
||||
if not assets_list:
|
||||
raise ValueError("aggregate_single_asset: no USD products available when currencies differ")
|
||||
|
||||
# Aggregazione per ogni Exchange
|
||||
aggregated: ProductInfo = ProductInfo()
|
||||
first = assets_list[0]
|
||||
|
||||
@@ -66,13 +66,13 @@ class TestMarketDataAggregator:
|
||||
assert btc_info is not None
|
||||
avg_weighted_price_btc = (50000.0 * 1000.0 + 50100.0 * 1100.0) / (1000.0 + 1100.0)
|
||||
assert btc_info.price == pytest.approx(avg_weighted_price_btc, rel=1e-3) # type: ignore
|
||||
assert btc_info.volume_24h == pytest.approx(1050.0, rel=1e-3) # type: ignore
|
||||
assert btc_info.volume_24h == pytest.approx(2100.0, rel=1e-3) # type: ignore # Total volume (1000 + 1100)
|
||||
assert btc_info.currency == "USD"
|
||||
|
||||
assert eth_info is not None
|
||||
avg_weighted_price_eth = (4000.0 * 2000.0 + 4050.0 * 2100.0) / (2000.0 + 2100.0)
|
||||
assert eth_info.price == pytest.approx(avg_weighted_price_eth, rel=1e-3) # type: ignore
|
||||
assert eth_info.volume_24h == pytest.approx(2050.0, rel=1e-3) # type: ignore
|
||||
assert eth_info.volume_24h == pytest.approx(4100.0, rel=1e-3) # type: ignore # Total volume (2000 + 2100)
|
||||
assert eth_info.currency == "USD"
|
||||
|
||||
def test_aggregate_product_info_with_no_data(self):
|
||||
@@ -141,12 +141,10 @@ class TestMarketDataAggregator:
|
||||
assert info is not None
|
||||
assert info.id == "BTC_AGGREGATED"
|
||||
assert info.symbol == "BTC"
|
||||
assert info.currency in ["USD", "EUR"] # Can be either, depending on which is found first
|
||||
# When aggregating different currencies, VWAP is calculated
|
||||
# (100000.0 * 1000.0 + 70000.0 * 800.0) / (1000.0 + 800.0)
|
||||
expected_price = (100000.0 * 1000.0 + 70000.0 * 800.0) / (1000.0 + 800.0)
|
||||
assert info.price == pytest.approx(expected_price, rel=1e-3) # type: ignore
|
||||
assert info.volume_24h == pytest.approx(900.0, rel=1e-3) # type: ignore # Average of volumes
|
||||
assert info.currency == "USD" # Only USD products are kept
|
||||
# When currencies differ, only USD is aggregated (only Provider1 in this case)
|
||||
assert info.price == pytest.approx(100000.0, rel=1e-3) # type: ignore
|
||||
assert info.volume_24h == pytest.approx(1000.0, rel=1e-3) # type: ignore # Only USD volume
|
||||
|
||||
# ===== Tests for aggregate_single_asset =====
|
||||
|
||||
|
||||
Reference in New Issue
Block a user