feat: 威科夫多周期选股引擎与中文图表界面
新增规则驱动的月/周/日结构识别、决策融合与交易计划,提供扫描 API、本地 K 线(成交量/MACD/吸筹区间标注)及回填调度;K 线无起始日时默认取最近 N 根。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
"""Decision Engine contract tests — MTF facts must not be overwritten."""
|
||||
|
||||
from ashare_dp.domain.wyckoff import DecisionSignal, EngineResult, WyckoffCycle, WyckoffEvent, WyckoffPhase
|
||||
from ashare_dp.wyckoff.decision import DecisionEngine
|
||||
|
||||
|
||||
def _er(name, payload, confidence=80.0, score=80.0, reasons=None):
|
||||
return EngineResult(
|
||||
name=name,
|
||||
confidence=confidence,
|
||||
score=score,
|
||||
reasons=reasons or [],
|
||||
payload=payload,
|
||||
)
|
||||
|
||||
|
||||
def test_monthly_distribution_daily_spring_is_watch():
|
||||
eng = DecisionEngine()
|
||||
monthly = _er("Cycle", {"cycle": WyckoffCycle.DISTRIBUTION.value, "trend_score": 40}, score=40)
|
||||
weekly_c = _er("Cycle", {"cycle": WyckoffCycle.ACCUMULATION.value, "trend_score": 70}, score=70)
|
||||
weekly_p = _er("Phase", {"phase": WyckoffPhase.B.value, "cycle": WyckoffCycle.ACCUMULATION.value, "structure_score": 65}, score=65)
|
||||
weekly_e = _er("Event", {"current_event": WyckoffEvent.ST.value, "recent_events": ["SC", "AR", "ST"]}, score=60)
|
||||
daily_e = _er(
|
||||
"Event",
|
||||
{"current_event": WyckoffEvent.SPRING.value, "recent_events": ["SC", "AR", "ST", "Spring"], "entry_score": 92},
|
||||
confidence=92,
|
||||
score=92,
|
||||
)
|
||||
daily_s = _er("Signal", {"signal_label": "Spring", "current_event": "Spring"}, confidence=92, score=92)
|
||||
|
||||
out = eng.run(monthly, weekly_c, weekly_p, weekly_e, daily_e, daily_s)
|
||||
|
||||
# Facts preserved
|
||||
assert out.payload["facts"]["monthly"]["cycle"] == WyckoffCycle.DISTRIBUTION.value
|
||||
assert out.payload["m_cycle"] == WyckoffCycle.DISTRIBUTION.value
|
||||
assert out.payload["d_event"] == WyckoffEvent.SPRING.value
|
||||
# Decision gated
|
||||
assert out.payload["decision_signal"] == DecisionSignal.WATCH.value
|
||||
assert out.payload["overall_score"] <= 55.0
|
||||
|
||||
|
||||
def test_bullish_alignment_can_strong_buy():
|
||||
eng = DecisionEngine()
|
||||
monthly = _er("Cycle", {"cycle": WyckoffCycle.MARKUP.value, "trend_score": 90}, score=90, confidence=90)
|
||||
weekly_c = _er("Cycle", {"cycle": WyckoffCycle.ACCUMULATION.value, "trend_score": 85}, score=85, confidence=85)
|
||||
weekly_p = _er(
|
||||
"Phase",
|
||||
{"phase": WyckoffPhase.D.value, "cycle": WyckoffCycle.ACCUMULATION.value, "structure_score": 88},
|
||||
score=88,
|
||||
confidence=88,
|
||||
)
|
||||
weekly_e = _er("Event", {"current_event": WyckoffEvent.SOS.value, "recent_events": ["SOS"]}, score=85, confidence=85)
|
||||
daily_e = _er(
|
||||
"Event",
|
||||
{"current_event": WyckoffEvent.SPRING.value, "recent_events": ["SC", "AR", "ST", "Spring", "Test"], "entry_score": 92},
|
||||
confidence=92,
|
||||
score=92,
|
||||
)
|
||||
daily_s = _er("Signal", {"signal_label": "Spring"}, confidence=92, score=92)
|
||||
|
||||
out = eng.run(monthly, weekly_c, weekly_p, weekly_e, daily_e, daily_s)
|
||||
assert out.payload["decision_signal"] == DecisionSignal.STRONG_BUY.value
|
||||
assert out.payload["stars"] >= 4
|
||||
Reference in New Issue
Block a user