feat(DATA-004): add underlying instrument selector

This commit is contained in:
Bu5hm4nn
2026-03-28 16:40:18 +01:00
parent cdd091a468
commit 3b98ebae69
13 changed files with 378 additions and 15 deletions

View File

@@ -7,13 +7,16 @@ import pytest
from app.domain.backtesting_math import AssetQuantity, PricePerAsset
from app.domain.instruments import (
GC_F_OUNCES_PER_CONTRACT,
GLD_EXPENSE_DECAY_RATE,
GLD_INITIAL_OUNCES_PER_SHARE,
GLD_LAUNCH_YEAR,
Underlying,
asset_quantity_from_weight,
gld_ounces_per_share,
instrument_metadata,
price_per_weight_from_asset_price,
supported_underlyings,
weight_from_asset_quantity,
)
from app.domain.units import BaseCurrency, Weight, WeightUnit
@@ -117,3 +120,65 @@ def test_instrument_conversions_fail_closed_for_unsupported_symbols() -> None:
with pytest.raises(ValueError, match="Unsupported instrument metadata"):
asset_quantity_from_weight("SLV", Weight(amount=Decimal("1"), unit=WeightUnit.OUNCE_TROY))
# DATA-004: Underlying Instrument Selector tests
def test_underlying_enum_has_gld_and_gc_f() -> None:
"""Verify Underlying enum contains GLD and GC=F."""
assert Underlying.GLD.value == "GLD"
assert Underlying.GC_F.value == "GC=F"
def test_underlying_display_names() -> None:
"""Verify Underlying display names are descriptive."""
assert Underlying.GLD.display_name() == "SPDR Gold Shares ETF"
assert Underlying.GC_F.display_name() == "Gold Futures (COMEX)"
def test_underlying_descriptions() -> None:
"""Verify Underlying descriptions indicate data source status."""
assert Underlying.GLD.description() == "SPDR Gold Shares ETF (live data via yfinance)"
assert Underlying.GC_F.description() == "Gold Futures (coming soon)"
def test_supported_underlyings_returns_all() -> None:
"""Verify supported_underlyings() returns all available choices."""
underlyings = supported_underlyings()
assert len(underlyings) == 2
assert Underlying.GLD in underlyings
assert Underlying.GC_F in underlyings
def test_gc_f_metadata() -> None:
"""Verify GC=F instrument metadata is correct."""
gc_f_meta = instrument_metadata("GC=F")
assert gc_f_meta.symbol == "GC=F"
assert gc_f_meta.quote_currency is BaseCurrency.USD
assert gc_f_meta.weight_per_share == Weight(amount=GC_F_OUNCES_PER_CONTRACT, unit=WeightUnit.OUNCE_TROY)
assert gc_f_meta.weight_per_share.amount == Decimal("100") # 100 troy oz per contract
def test_gc_f_contract_specs() -> None:
"""Verify GC=F contract specifications."""
assert GC_F_OUNCES_PER_CONTRACT == Decimal("100")
# 1 contract = 100 oz
one_contract = AssetQuantity(amount=Decimal("1"), symbol="GC=F")
weight = weight_from_asset_quantity(one_contract)
assert weight == Weight(amount=Decimal("100"), unit=WeightUnit.OUNCE_TROY)
def test_gc_f_price_per_weight_conversion() -> None:
"""Verify GC=F price converts correctly to price per weight."""
# GC=F quoted at $2700/oz (already per ounce)
quote = PricePerAsset(amount=Decimal("270000"), currency=BaseCurrency.USD, symbol="GC=F") # $2700/oz * 100 oz
spot = price_per_weight_from_asset_price(quote, per_unit=WeightUnit.OUNCE_TROY)
# Should be $2700/oz
assert spot.amount == Decimal("2700")
assert spot.currency is BaseCurrency.USD
assert spot.per_unit is WeightUnit.OUNCE_TROY