feat: add market data architecture and complete migration through build 039

This commit is contained in:
2026-07-14 09:58:16 +03:00
parent 26deb861bc
commit a996f2f797
443 changed files with 80452 additions and 1335 deletions

View File

@@ -0,0 +1,83 @@
# app/tests/unit/trading/auto/test_execution_quality.py
from __future__ import annotations
from types import SimpleNamespace
import pytest
import src.trading.auto.execution_quality as module
from src.integrations.exchange.models import ExecutionPriceSnapshot
from src.trading.auto.execution_quality import AutoExecutionQualityMixin
class Harness(AutoExecutionQualityMixin):
_spread_thresholds_by_asset = {}
_default_spread_thresholds = {
"warning_enter": 1.0,
"warning_exit": 0.8,
"block_enter": 2.0,
"block_exit": 1.5,
}
_max_snapshot_age_seconds = 5.0
_warning_snapshot_age_seconds = 2.0
_last_logged_execution_quality_key = None
def _log_execution_quality_if_changed(self, **_: object) -> None:
return None
def _apply_exchange_block_state(self, **_: object) -> None:
raise AssertionError("exchange block must not be applied")
def _state() -> SimpleNamespace:
return SimpleNamespace(
market_is_open=True,
symbol="BTC/USD_LEVERAGE",
strategy="trend",
status="RUNNING",
execution_quality=None,
execution_quality_reason=None,
execution_quality_message=None,
execution_block_reason=None,
market_runtime_degraded=False,
snapshot_age_seconds=None,
spread_percent=None,
execution_price_age_seconds=None,
execution_bid_price=None,
execution_ask_price=None,
execution_last_price=None,
execution_price_freshness=None,
)
def test_execution_quality_uses_typed_execution_snapshot(
monkeypatch: pytest.MonkeyPatch,
) -> None:
snapshot = ExecutionPriceSnapshot(
symbol="BTC/USD_LEVERAGE",
last_price=100.5,
bid_price=100.0,
ask_price=101.0,
updated_at="13.07.2026 15:00:00",
source="dzengi:fresh_cache",
is_fresh=True,
age_seconds=0.5,
)
class Service:
def get_execution_snapshot(self, *_: object, **__: object) -> ExecutionPriceSnapshot:
return snapshot
monkeypatch.setattr(module, "ExchangeService", Service)
state = _state()
Harness()._sync_execution_quality_state(state) # type: ignore[arg-type]
assert state.execution_bid_price == 100.0
assert state.execution_ask_price == 101.0
assert state.execution_last_price == 100.5
assert state.execution_price_source == "dzengi:fresh_cache"
assert state.snapshot_age_seconds == 0.5

View File

@@ -0,0 +1,75 @@
# app/tests/unit/trading/auto/test_signal_runtime_quote.py
from __future__ import annotations
import inspect
from datetime import datetime, timezone
from decimal import Decimal
from types import SimpleNamespace
import pytest
import src.trading.auto.signal_runtime as module
from src.market_data.acquisition.models.quote import Quote
from src.trading.auto.signal_runtime import AutoSignalRuntimeMixin
def _quote() -> Quote:
return Quote(
symbol="BTC/USD_LEVERAGE",
last_price=Decimal("100.5"),
bid_price=Decimal("100.0"),
ask_price=Decimal("101.0"),
exchange_timestamp=None,
received_at=datetime.now(timezone.utc),
source="dzengi",
)
class Harness(AutoSignalRuntimeMixin):
_ready_confidence = 0.3
def test_ready_signal_uses_canonical_quote(
monkeypatch: pytest.MonkeyPatch,
) -> None:
quote = _quote()
requested: list[tuple[str, str]] = []
captured: dict[str, object] = {}
class Service:
def get_quote(self, symbol: str, *, runtime_key: str) -> Quote:
requested.append((symbol, runtime_key))
return quote
class Journal:
def log_ui_info(self, **kwargs: object) -> None:
captured.update(kwargs)
harness = Harness()
def build_payload(**kwargs: object) -> dict[str, object]:
captured["quote"] = kwargs["quote"]
return {"ok": True}
monkeypatch.setattr(module, "ExchangeService", Service)
monkeypatch.setattr(module, "JournalService", Journal)
monkeypatch.setattr(harness, "_build_ready_signal_payload", build_payload)
state = SimpleNamespace(symbol="BTC/USD_LEVERAGE")
harness._log_ready_signal(
state=state, # type: ignore[arg-type]
signal="BUY",
reason="test",
confidence=0.9,
signal_intent="ENTRY",
)
assert requested == [("BTC/USD_LEVERAGE", "auto")]
assert captured["quote"] is quote
def test_signal_runtime_has_no_legacy_market_snapshot_call() -> None:
source = inspect.getsource(module.AutoSignalRuntimeMixin)
assert "get_quote(" in source