Add GTL price-traffic chart diagnostic

This commit is contained in:
2026-07-13 12:08:10 +02:00
parent dc1570877c
commit 4730d19694
14 changed files with 433 additions and 44 deletions
+70 -4
View File
@@ -3,7 +3,6 @@
from datetime import datetime
from unittest.mock import AsyncMock, patch
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
@@ -29,10 +28,12 @@ class _FakeLevel:
class _FakeOHLCV:
"""Mimics an OHLCVRecord with a close attribute."""
"""Mimics an OHLCVRecord with price attributes."""
def __init__(self, close: float):
def __init__(self, close: float, high: float | None = None, low: float | None = None):
self.close = close
self.high = high if high is not None else close + 1.0
self.low = low if low is not None else close - 1.0
def _make_app() -> FastAPI:
@@ -62,6 +63,71 @@ SAMPLE_LEVELS = [
SAMPLE_OHLCV = [_FakeOHLCV(100.0)]
class TestGateTargetLadderRouter:
@patch("app.routers.sr_levels.detect_gate_target_ladder")
@patch("app.routers.sr_levels.query_ohlcv", new_callable=AsyncMock)
def test_returns_transient_price_traffic_proposals(self, mock_ohlcv, mock_detect):
mock_ohlcv.return_value = [
_FakeOHLCV(100.0, high=101.0, low=99.0),
_FakeOHLCV(102.0, high=103.0, low=100.0),
]
mock_detect.return_value = [
{
"price_level": 105.0,
"type": "resistance",
"strength": 80,
"detection_method": "merged",
"sources": ["pivot_point", "range_grid"],
"rejection_count": 7,
},
{
"price_level": 95.0,
"type": "support",
"strength": 60,
"detection_method": "range_grid",
"sources": ["range_grid"],
"rejection_count": 4,
},
]
response = TestClient(_make_app()).get("/api/v1/gate-target-ladder/aapl")
assert response.status_code == 200
data = response.json()["data"]
assert data["symbol"] == "AAPL"
assert data["lookback_bars"] == 2
assert [level["price_level"] for level in data["levels"]] == [95.0, 105.0]
assert data["levels"][1] == {
"price_level": 105.0,
"type": "resistance",
"strength": 80,
"detection_method": "merged",
"sources": ["pivot_point", "range_grid"],
"traffic_count": 7,
}
mock_detect.assert_called_once_with(
[101.0, 103.0],
[99.0, 100.0],
[100.0, 102.0],
)
@patch("app.routers.sr_levels.detect_gate_target_ladder")
@patch("app.routers.sr_levels.query_ohlcv", new_callable=AsyncMock)
def test_empty_history_returns_empty_ladder(self, mock_ohlcv, mock_detect):
mock_ohlcv.return_value = []
response = TestClient(_make_app()).get("/api/v1/gate-target-ladder/AAPL")
assert response.status_code == 200
assert response.json()["data"] == {
"symbol": "AAPL",
"levels": [],
"count": 0,
"lookback_bars": 0,
}
mock_detect.assert_not_called()
class TestSRLevelsRouterZones:
"""Tests for max_zones parameter and zone inclusion in response."""
@@ -207,7 +273,7 @@ class TestSRLevelsRouterVisibleLevels:
), f"visible level price {price} not within any zone bounds"
# visible_levels must be a subset of levels (by id)
level_ids = {l["id"] for l in data["levels"]}
level_ids = {level["id"] for level in data["levels"]}
for lvl in visible:
assert lvl["id"] in level_ids