fix: align production defaults and close review parity gaps
Ship greenfield min_rr=2.0 and conf=0, read-only Structural S/R, indicator cache invalidation, and UI/gate language that treats GTL as screening not exit. Align strategy_rank missing-vol fallback live vs backtest, single-source PRIMARY_TARGET_MIN_RR, expand prod parity tests, and drop dead FE clients.
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
"""Integration tests for R:R scanner full flow with quality-based target selection.
|
||||
"""Integration tests for R:R scanner full flow with probability-based primary.
|
||||
|
||||
Verifies the complete scan_ticker pipeline: quality-based S/R level selection,
|
||||
correct TradeSetup field population, and database persistence.
|
||||
Verifies scan_ticker → enhance_trade_setup: headline target is the primary
|
||||
selected by probability floors (not the pre-enhance quality candidate loop),
|
||||
TradeSetup fields, and persistence.
|
||||
|
||||
**Validates: Requirements 2.1, 2.2, 2.3, 2.4, 3.4**
|
||||
"""
|
||||
@@ -63,35 +64,52 @@ def _make_ohlcv_bars(
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# 8.1 Integration test: full scan_ticker flow with quality-based selection,
|
||||
# 8.1 Integration test: full scan_ticker flow with probability primary,
|
||||
# correct TradeSetup fields, and database persistence
|
||||
# ===========================================================================
|
||||
|
||||
def _assert_headline_is_probability_primary(setup: TradeSetup) -> None:
|
||||
"""Headline target/rr must match the starred primary from _select_primary_target."""
|
||||
targets = setup.targets or []
|
||||
assert targets, "expected generated targets after enhance"
|
||||
primaries = [t for t in targets if t.get("is_primary")]
|
||||
assert len(primaries) == 1, "exactly one primary target expected"
|
||||
primary = primaries[0]
|
||||
assert setup.target == pytest.approx(float(primary["price"]), abs=0.01)
|
||||
assert setup.rr_ratio == pytest.approx(float(primary["rr_ratio"]), abs=0.01)
|
||||
worthwhile = [
|
||||
t for t in targets
|
||||
if float(t.get("rr_ratio", 0.0)) >= 1.5 and float(t.get("probability", 0.0)) >= 20.0
|
||||
]
|
||||
pool = worthwhile or targets
|
||||
best = max(pool, key=lambda t: (float(t["probability"]), float(t["rr_ratio"])))
|
||||
assert primary["price"] == pytest.approx(float(best["price"]), abs=0.01)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_scan_ticker_full_flow_quality_selection_and_persistence(
|
||||
async def test_scan_ticker_full_flow_probability_primary_and_persistence(
|
||||
scan_session: AsyncSession,
|
||||
):
|
||||
"""Integration test for the complete scan_ticker pipeline.
|
||||
"""Integration test for the complete scan_ticker → enhance pipeline.
|
||||
|
||||
Scenario:
|
||||
- Entry ≈ 100, ATR ≈ 2.0, risk ≈ 3.0 (atr_multiplier=1.5)
|
||||
- 3 resistance levels above (long candidates):
|
||||
A: price=105, strength=90 (strong, near) → highest quality
|
||||
A: price=105, strength=90 (strong, near) → typically highest reach-prob
|
||||
B: price=115, strength=40 (medium, mid)
|
||||
C: price=135, strength=5 (weak, far)
|
||||
C: price=135, strength=5 (weak, far / lottery)
|
||||
- 3 support levels below (short candidates):
|
||||
D: price=95, strength=85 (strong, near) → highest quality
|
||||
D: price=95, strength=85 (strong, near)
|
||||
E: price=85, strength=35 (medium, mid)
|
||||
F: price=65, strength=8 (weak, far)
|
||||
- CompositeScore: 72.5
|
||||
|
||||
Verifies:
|
||||
1. Both long and short setups are produced
|
||||
2. Long target = Level A (highest quality, not most distant)
|
||||
3. Short target = Level D (highest quality, not most distant)
|
||||
4. All TradeSetup fields are correct and rounded to 4 decimals
|
||||
5. rr_ratio is the actual R:R of the selected level
|
||||
6. Old setups are deleted, new ones persisted
|
||||
2. Headline is the probability-based primary (not a distant lottery)
|
||||
3. Near/strong levels win over far/weak when they clear floors
|
||||
4. rr_ratio matches the selected primary's R:R
|
||||
5. Old setups are deleted, new ones persisted
|
||||
"""
|
||||
# -- Setup: create ticker --
|
||||
ticker = Ticker(symbol="INTEG")
|
||||
@@ -172,16 +190,14 @@ async def test_scan_ticker_full_flow_quality_selection_and_persistence(
|
||||
long_setup = long_setups[0]
|
||||
short_setup = short_setups[0]
|
||||
|
||||
# -- Assert: long target is Level A (highest quality, not most distant) --
|
||||
# Level A: price=105 (strong, near) should beat Level C: price=135 (weak, far)
|
||||
# -- Assert: headline is probability primary; near/strong beats far lottery --
|
||||
_assert_headline_is_probability_primary(long_setup)
|
||||
_assert_headline_is_probability_primary(short_setup)
|
||||
assert long_setup.target == pytest.approx(105.0, abs=0.01), (
|
||||
f"Long target should be 105.0 (highest quality), got {long_setup.target}"
|
||||
f"Long primary should be 105.0 (near, high reach-prob), got {long_setup.target}"
|
||||
)
|
||||
|
||||
# -- Assert: short target is Level D (highest quality, not most distant) --
|
||||
# Level D: price=95 (strong, near) should beat Level F: price=65 (weak, far)
|
||||
assert short_setup.target == pytest.approx(95.0, abs=0.01), (
|
||||
f"Short target should be 95.0 (highest quality), got {short_setup.target}"
|
||||
f"Short primary should be 95.0 (near, high reach-prob), got {short_setup.target}"
|
||||
)
|
||||
|
||||
# -- Assert: entry_price is the last close (≈ 100) --
|
||||
|
||||
Reference in New Issue
Block a user