"""v1.7.6 新增特征检测和信号淘汰机制测试""" import os import sys import pandas as pd import numpy as np PROJECT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) if PROJECT_DIR not in sys.path: sys.path.insert(0, PROJECT_DIR) def make_df(highs, lows, closes, opens=None, volumes=None): df = pd.DataFrame({ "high": highs, "low": lows, "close": closes, "open": opens or closes, "volume": volumes or [1000] * len(closes), }) return df def test_detect_higher_lows_finds_clear_pattern(): """验证底部抬高检测能识别清晰的逐步抬高低点""" from app.services.altcoin_screener import detect_higher_lows lows, highs, closes, volumes = [], [], [], [] for seg in range(6): for _ in range(4): low = seg * 5 + np.random.uniform(-0.3, 0.3) high = low + np.random.uniform(2, 4) lows.append(low) highs.append(high) closes.append((low + high) / 2) volumes.append(np.random.uniform(800, 1200)) df = make_df(highs, lows, closes, volumes=volumes) result = detect_higher_lows(df) assert result["found"] is True assert result["hl_count"] >= 4 assert "底部抬高" in result["signal"] def test_detect_higher_lows_rejects_declining_trend(): """验证下降趋势不会被误判为底部抬高""" from app.services.altcoin_screener import detect_higher_lows lows, highs, closes, volumes = [], [], [], [] for seg in range(6): for _ in range(4): low = 100 - seg * 3 + np.random.uniform(-0.3, 0.3) high = low + np.random.uniform(1, 3) lows.append(low) highs.append(high) closes.append((low + high) / 2) volumes.append(np.random.uniform(800, 1200)) df = make_df(highs, lows, closes, volumes=volumes) result = detect_higher_lows(df) assert result["found"] is False def test_detect_compression_surge_detects_tight_then_volume(): """验证压缩后放量模式能被检测""" from app.services.altcoin_screener import detect_compression_surge lows = [100 + np.random.uniform(-0.5, 0.5) for _ in range(24)] highs = [l + np.random.uniform(1, 2.5) for l in lows] closes = [(h + l) / 2 for h, l in zip(highs, lows)] volumes = [np.random.uniform(500, 800) for _ in range(21)] + [np.random.uniform(3000, 5000) for _ in range(3)] df = make_df(highs, lows, closes, volumes=volumes) result = detect_compression_surge(df) assert result["found"] is True assert result["range_pct"] < 20 assert result["vol_ratio"] > 2.0 assert "压缩放量" in result["signal"] def test_detect_compression_surge_rejects_wide_range(): """验证宽幅震荡不触发压缩检测""" from app.services.altcoin_screener import detect_compression_surge lows = [np.random.uniform(80, 120) for _ in range(24)] highs = [l + np.random.uniform(5, 15) for l in lows] closes = [(h + l) / 2 for h, l in zip(highs, lows)] volumes = [np.random.uniform(500, 800) for _ in range(21)] + [np.random.uniform(3000, 5000) for _ in range(3)] df = make_df(highs, lows, closes, volumes=volumes) result = detect_compression_surge(df) assert result["found"] is False def test_signal_deprecation_config_exists(): """验证信号淘汰机制配置可正确读取""" from app.config.config_loader import get_review_params dep = get_review_params().get("signal_deprecation", {}) assert dep.get("enabled") is True assert dep.get("min_samples", 0) >= 5 assert 0 < dep.get("hit_rate_deprecate_threshold", 0) < 1