feat(quant-engine): 10개 고전 기술전략 갭분석 후 7개 보조신호 채택
사용자 제시 10개 고전 기술전략(골든크로스/모멘텀/52주신고가/연속상승하락/이격도/돌파실패/ 강한종가/변동성확장/평균회귀/추세필터)을 기존 엔진과 대조한 갭분석 결과: - 이미 구현됨: 모멘텀(VELOCITY_V1/RS_MOMENTUM_V1), 이격도·평균회귀(MEAN_REVERSION_GATE_V1) - 신규 채택 7개: GOLDEN_CROSS_SIGNAL_V1, STRONG_CLOSE_SIGNAL_V1, VOLATILITY_EXPANSION_BREAKOUT_V1, FIFTY_TWO_WEEK_HIGH_TRIGGER_V1, CONSECUTIVE_STREAK_V1, BREAKOUT_FAILURE_STOP_V1, TREND_FILTER_GATE_V1 AGENTS.md 하드룰(추격매수 방지, anti-late-entry gate 필수통과)에 따라 BUY 방향 신호 전부를 STRATEGY_SCORING의 보조신호로만 편입 — BREAKOUT_QUALITY_GATE_V2/FOLLOW_THROUGH_DAY_CONFIRM_V1/ ANTI_LATE_ENTRY_GATE_V2 게이트 체인을 우회하는 독립 BUY 트리거로는 사용하지 않음. 검증: validate_specs/validate_golden_coverage_100(100%)/validate_calibration_registry_v1/ validate_schema_model_generation_v1/validate_agents_shrink_v1 전부 PASS. golden test 22/22 PASS. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
"""Golden tests for BREAKOUT_FAILURE_STOP_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-6)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_breakout_failure_stop_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_breakout_failure_stop_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_reentry_below_prior_high_within_window_triggers_sell_review() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.evaluate(100, 95, 3)
|
||||
assert result["breakout_failure"] is True
|
||||
assert result["gate"] == "SELL_RISK_EXIT_REVIEW"
|
||||
|
||||
|
||||
def test_reentry_after_window_does_not_trigger() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.evaluate(100, 95, 10)
|
||||
assert result["breakout_failure"] is False
|
||||
|
||||
|
||||
def test_missing_prior_high_returns_null() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.evaluate(None, 95, 3)
|
||||
assert result["breakout_failure"] is None
|
||||
@@ -0,0 +1,37 @@
|
||||
"""Golden tests for CONSECUTIVE_STREAK_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-5)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_consecutive_streak_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_consecutive_streak_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_trailing_three_up_days_gives_up_streak_3() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.compute_streaks([1, 2, -1, 1, 2, 3])
|
||||
assert result["up_streak"] == 3
|
||||
assert result["down_streak"] == 0
|
||||
|
||||
|
||||
def test_trailing_two_down_days_gives_down_streak_2() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.compute_streaks([5, -1, -2])
|
||||
assert result["down_streak"] == 2
|
||||
assert result["up_streak"] == 0
|
||||
|
||||
|
||||
def test_empty_changes_returns_null_not_zero() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.compute_streaks([])
|
||||
assert result["up_streak"] is None
|
||||
assert result["down_streak"] is None
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Golden tests for FIFTY_TWO_WEEK_HIGH_TRIGGER_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-4)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_fifty_two_week_high_trigger_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_fifty_two_week_high_trigger_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_close_at_or_above_52w_high_triggers() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(105, 100) is True
|
||||
|
||||
|
||||
def test_close_below_52w_high_does_not_trigger() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(95, 100) is False
|
||||
|
||||
|
||||
def test_missing_high52w_returns_null() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(105, None) is None
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Golden tests for GOLDEN_CROSS_SIGNAL_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-1)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_golden_cross_signal_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_golden_cross_signal_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_ma20_crossing_above_ma60_detected() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.golden_cross_today(105, 98, 100, 99) is True
|
||||
|
||||
|
||||
def test_no_cross_when_ma20_already_above() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.golden_cross_today(105, 101, 100, 99) is False
|
||||
|
||||
|
||||
def test_missing_prev_values_returns_null_not_false() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.golden_cross_today(105, None, 100, 99) is None
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Golden tests for STRONG_CLOSE_SIGNAL_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-2)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_strong_close_signal_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_strong_close_signal_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_close_near_high_is_strong_close() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.evaluate_strong_close(99, 100, 90)
|
||||
assert result["strong_close"] is True
|
||||
assert result["close_position_pct"] == 90.0
|
||||
|
||||
|
||||
def test_close_near_low_is_not_strong_close() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.evaluate_strong_close(91, 100, 90)
|
||||
assert result["strong_close"] is False
|
||||
|
||||
|
||||
def test_degenerate_high_equals_low_returns_null() -> None:
|
||||
mod = _load_module()
|
||||
result = mod.evaluate_strong_close(100, 100, 100)
|
||||
assert result["strong_close"] is None
|
||||
assert result["close_position_pct"] is None
|
||||
@@ -0,0 +1,36 @@
|
||||
"""Golden tests for TREND_FILTER_GATE_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-7)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_trend_filter_gate_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_trend_filter_gate_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_above_rising_ma120_passes() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(105, 100, 99) is True
|
||||
|
||||
|
||||
def test_below_ma120_fails() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(95, 100, 99) is False
|
||||
|
||||
|
||||
def test_above_but_falling_ma120_fails() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(105, 100, 101) is False
|
||||
|
||||
|
||||
def test_missing_ma120_returns_null() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.evaluate(105, None, 99) is None
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Golden tests for VOLATILITY_EXPANSION_BREAKOUT_V1 (governance/todo/technical_signals_p4_adoption_plan.yaml P4-3)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.util
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
MODULE_PATH = ROOT / "tools" / "build_volatility_expansion_breakout_v1.py"
|
||||
|
||||
|
||||
def _load_module():
|
||||
spec = importlib.util.spec_from_file_location("build_volatility_expansion_breakout_v1", MODULE_PATH)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_squeeze_then_strong_move_triggers_signal() -> None:
|
||||
mod = _load_module()
|
||||
prev_squeeze = mod.squeeze_detected(10)
|
||||
assert prev_squeeze is True
|
||||
assert mod.evaluate(prev_squeeze, 4.5) is True
|
||||
|
||||
|
||||
def test_no_squeeze_does_not_trigger_even_with_strong_move() -> None:
|
||||
mod = _load_module()
|
||||
prev_squeeze = mod.squeeze_detected(80)
|
||||
assert prev_squeeze is False
|
||||
assert mod.evaluate(prev_squeeze, 4.5) is False
|
||||
|
||||
|
||||
def test_missing_percentile_returns_null() -> None:
|
||||
mod = _load_module()
|
||||
assert mod.squeeze_detected(None) is None
|
||||
Reference in New Issue
Block a user