feat(ECR-009): Crypto Wyckoff Screener 独立页(D/W/M)
移植 A_Share_DP 引擎;本地缓存与 60s tip;月线由日线 UTC 聚合;不碰主站 analyze/缠论叠层。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
"""Wyckoff Screener domain models — Architecture v1.0 frozen contracts."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import date, datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Optional
|
||||
|
||||
|
||||
class WyckoffCycle(str, Enum):
|
||||
ACCUMULATION = "Accumulation"
|
||||
RE_ACCUMULATION = "ReAccumulation"
|
||||
MARKUP = "Markup"
|
||||
DISTRIBUTION = "Distribution"
|
||||
RE_DISTRIBUTION = "ReDistribution"
|
||||
MARKDOWN = "Markdown"
|
||||
UNKNOWN = "Unknown"
|
||||
|
||||
|
||||
class WyckoffPhase(str, Enum):
|
||||
A = "A"
|
||||
B = "B"
|
||||
C = "C"
|
||||
D = "D"
|
||||
E = "E"
|
||||
NONE = "None"
|
||||
|
||||
|
||||
class WyckoffEvent(str, Enum):
|
||||
PS = "PS"
|
||||
SC = "SC"
|
||||
AR = "AR"
|
||||
ST = "ST"
|
||||
SPRING = "Spring"
|
||||
TEST = "Test"
|
||||
SOS = "SOS"
|
||||
LPS = "LPS"
|
||||
JUMP = "Jump"
|
||||
BACKUP = "Backup"
|
||||
BC = "BC"
|
||||
UTAD = "UTAD"
|
||||
SOW = "SOW"
|
||||
LPSY = "LPSY"
|
||||
NONE = "None"
|
||||
|
||||
|
||||
class DecisionSignal(str, Enum):
|
||||
STRONG_BUY = "StrongBuy"
|
||||
BUY = "Buy"
|
||||
WATCH = "Watch"
|
||||
AVOID = "Avoid"
|
||||
SELL = "Sell"
|
||||
|
||||
|
||||
class RiskLevel(str, Enum):
|
||||
LOW = "Low"
|
||||
MEDIUM = "Medium"
|
||||
HIGH = "High"
|
||||
|
||||
|
||||
@dataclass
|
||||
class EngineResult:
|
||||
"""Unified result envelope for every Wyckoff engine (v1.0 contract)."""
|
||||
|
||||
name: str
|
||||
version: str = "1.0.0"
|
||||
confidence: float = 0.0
|
||||
score: float = 0.0
|
||||
reasons: list[str] = field(default_factory=list)
|
||||
warnings: list[str] = field(default_factory=list)
|
||||
metrics: dict[str, Any] = field(default_factory=dict)
|
||||
payload: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict[str, Any]:
|
||||
return {
|
||||
"name": self.name,
|
||||
"version": self.version,
|
||||
"confidence": self.confidence,
|
||||
"score": self.score,
|
||||
"reasons": self.reasons,
|
||||
"warnings": self.warnings,
|
||||
"metrics": self.metrics,
|
||||
"payload": self.payload,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class OHLCVFrame:
|
||||
"""In-memory OHLCV for one symbol one timeframe. Engines never touch DB."""
|
||||
|
||||
ts_code: str
|
||||
timeframe: str # "1d" | "1w" | "1M"
|
||||
trade_dates: list[date]
|
||||
open: list[float]
|
||||
high: list[float]
|
||||
low: list[float]
|
||||
close: list[float]
|
||||
volume: list[float]
|
||||
amount: list[float] = field(default_factory=list)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.close)
|
||||
|
||||
@property
|
||||
def empty(self) -> bool:
|
||||
return len(self.close) == 0
|
||||
|
||||
|
||||
@dataclass
|
||||
class WyckoffScanRow:
|
||||
"""Persisted scan row for wyckoff_scan table."""
|
||||
|
||||
trade_date: date
|
||||
ts_code: str
|
||||
name: str = ""
|
||||
industry: str = ""
|
||||
engine_version: str = "v1.0.0"
|
||||
|
||||
m_cycle: str = WyckoffCycle.UNKNOWN.value
|
||||
cycle_confidence: float = 0.0
|
||||
trend_score: float = 0.0
|
||||
|
||||
w_cycle: str = WyckoffCycle.UNKNOWN.value
|
||||
w_phase: str = WyckoffPhase.NONE.value
|
||||
w_current_event: str = WyckoffEvent.NONE.value
|
||||
w_recent_events_json: str = "[]"
|
||||
phase_confidence: float = 0.0
|
||||
structure_score: float = 0.0
|
||||
|
||||
d_current_event: str = WyckoffEvent.NONE.value
|
||||
d_recent_events_json: str = "[]"
|
||||
event_confidence: float = 0.0
|
||||
entry_score: float = 0.0
|
||||
|
||||
entry: Optional[float] = None
|
||||
stop: Optional[float] = None
|
||||
target1: Optional[float] = None
|
||||
target2: Optional[float] = None
|
||||
rr: Optional[float] = None
|
||||
|
||||
alignment: float = 0.0
|
||||
stars: int = 1
|
||||
decision_signal: str = DecisionSignal.WATCH.value
|
||||
signal_confidence: float = 0.0
|
||||
overall_confidence: float = 0.0
|
||||
overall_score: float = 0.0
|
||||
risk: str = RiskLevel.MEDIUM.value
|
||||
reasons_json: str = "[]"
|
||||
|
||||
feature_snapshot_json: str = "{}"
|
||||
markers_json: str = "[]"
|
||||
scanned_at: datetime = field(default_factory=datetime.now)
|
||||
Reference in New Issue
Block a user