- SignalRules(买入 rank 阈值/趋势 MA/动量 + 卖出区间/破位警示)+ SignalEvent (BUY/WATCH/SELL,score/price/trigger_reason 可解释)+ SignalResult/Meta - quant/signal.generate_signals:与选股同一评分引擎取全市场 rank,按规则分类输出 - signal_snapshot/signal_event 表(migration d8e0b2f3c4d5,MySQL 已应用)+ Repo - SignalService + POST /api/signals(同步+落库)、GET 详情/列表 - tests/test_signals.py(引擎分类/排序/破位不 BUY、service、API 提交读回);全量 pytest 通过
58 lines
2.0 KiB
Python
58 lines
2.0 KiB
Python
"""交易信号领域实体(M8.1,v2 §15 Signal Engine)。
|
||
|
||
Signal 输入 = Selection 排序(score)+ 价格/技术条件 + 规则;输出事件可解释:
|
||
BUY / WATCH / SELL(破位警示),每条带 trigger_reason —— 回答
|
||
「某日为什么对该股票给 BUY/SELL」(v2 §8)。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import date, datetime
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
class SignalRules(BaseModel):
|
||
"""规则(结构化,MVP):买入区间 + 趋势/动量条件 + 卖出/警示区间。"""
|
||
|
||
buy_rank_threshold: int = Field(default=20, ge=1, le=500, description="rank<=此值进入买入候选")
|
||
buy_require_trend: bool = Field(default=True, description="买入需 close > MA(trend_ma)")
|
||
buy_require_momentum: bool = Field(default=False, description="买入需 close > 20 日前 close")
|
||
trend_ma: int = Field(default=60, ge=10, le=250)
|
||
sell_rank_threshold: int = Field(default=50, ge=1, le=1000, description="rank>此值或破位 → SELL 警示")
|
||
sell_on_trend_break: bool = Field(default=True, description="买入区间内 close < MA(trend_ma) → SELL")
|
||
max_output_rank: int = Field(default=80, ge=1, le=2000, description="仅输出排名前 N 的信号")
|
||
|
||
|
||
class SignalEvent(BaseModel):
|
||
symbol: str
|
||
signal_date: date
|
||
signal_type: str = Field(pattern="^(BUY|WATCH|SELL)$")
|
||
score: float | None = None
|
||
price: float | None = None
|
||
trigger_reason: list[str] = Field(default_factory=list)
|
||
|
||
|
||
class SignalStatistics(BaseModel):
|
||
universe_size: int = 0
|
||
buy: int = 0
|
||
watch: int = 0
|
||
sell: int = 0
|
||
|
||
|
||
class SignalResult(BaseModel):
|
||
as_of_date: date
|
||
rules: SignalRules
|
||
statistics: SignalStatistics
|
||
events: list[SignalEvent] = Field(default_factory=list)
|
||
config_snapshot: dict = Field(default_factory=dict)
|
||
|
||
|
||
class SignalMeta(BaseModel):
|
||
id: str
|
||
as_of: date
|
||
buy: int = 0
|
||
watch: int = 0
|
||
sell: int = 0
|
||
created_at: datetime | None = None
|