提交
This commit is contained in:
@@ -710,3 +710,150 @@ class IndexWeightsResponse(BaseModel):
|
||||
total: int # 成分股总数(返回 items 为按权重降序的子集)
|
||||
items: list[IndexWeightItemOut] = []
|
||||
|
||||
|
||||
# ---------- 买卖点实验室(/signals:手动标注共同点分析 + 条件扫描) ----------
|
||||
class SignalPlanCreate(BaseModel):
|
||||
ts_code: str = Field(min_length=6, max_length=12)
|
||||
name: str = Field(default="", max_length=64) # 空则后端默认「{code} 买点方案」
|
||||
|
||||
|
||||
class SignalPlanUpdate(BaseModel):
|
||||
name: str | None = Field(default=None, min_length=1, max_length=64)
|
||||
note: str | None = None
|
||||
conditions: list[IndicatorCondition] | None = None # None=不改;[] = 清空
|
||||
|
||||
|
||||
class SignalPointIn(BaseModel):
|
||||
kind: Literal["buy", "sell"]
|
||||
trade_date: date
|
||||
source: Literal["manual", "scan"] = "manual"
|
||||
|
||||
|
||||
class SignalPointOut(BaseModel):
|
||||
kind: str # buy | sell
|
||||
trade_date: date
|
||||
source: str # manual | scan
|
||||
|
||||
model_config = {"from_attributes": True}
|
||||
|
||||
|
||||
class SignalPlanOut(BaseModel):
|
||||
id: int
|
||||
ts_code: str
|
||||
name: str
|
||||
note: str | None = None
|
||||
conditions: list[IndicatorCondition] = Field(default_factory=list)
|
||||
points: list[SignalPointOut] = Field(default_factory=list)
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class SignalPlanListResponse(BaseModel):
|
||||
items: list[SignalPlanOut]
|
||||
|
||||
|
||||
class SignalsAnalyzeRequest(BaseModel):
|
||||
ts_code: str = Field(min_length=6, max_length=12)
|
||||
buy_dates: list[date] = Field(min_length=1, max_length=200)
|
||||
|
||||
|
||||
class FeatureFindingOut(BaseModel):
|
||||
"""共同特征发现:特征在买点日的命中率 vs 全期基线比例(纯算法统计)。"""
|
||||
key: str
|
||||
label: str
|
||||
samples_hit: int # 命中特征的买点数
|
||||
samples: int # 有效买点数
|
||||
hit_rate: float # 买点命中率 %(0-100)
|
||||
base_rate: float # 全期基线比例 %(0-100)
|
||||
lift: float # 命中率 / 基线
|
||||
mappable: bool # 是否可转成 IndicatorCondition(进扫描/回测)
|
||||
condition: IndicatorCondition | None = None
|
||||
|
||||
|
||||
class ValueSummaryOut(BaseModel):
|
||||
"""连续值摘要:买点日中位数 vs 全期中位数(直觉对照用)。"""
|
||||
key: str
|
||||
label: str
|
||||
at_points: float | None
|
||||
baseline: float | None
|
||||
|
||||
|
||||
class SignalsAnalyzeResponse(BaseModel):
|
||||
ts_code: str
|
||||
samples: int # 有效买点数(落在该股交易日上)
|
||||
total_bars: int
|
||||
findings: list[FeatureFindingOut]
|
||||
summaries: list[ValueSummaryOut]
|
||||
|
||||
|
||||
class WindowStatOut(BaseModel):
|
||||
window: int # 持有 N 交易日
|
||||
samples: int
|
||||
mean: float # 平均收益 %
|
||||
median: float
|
||||
win_rate: float # 收益>0 占比 %
|
||||
p10: float
|
||||
p90: float
|
||||
|
||||
|
||||
class EvalPointOut(BaseModel):
|
||||
"""单个买点的评估:未来 N 交易日收益矩阵(复权校正)。"""
|
||||
date: date # 买点(信号)日
|
||||
entry_date: date | None = None # 实际入场日(默认信号次日)
|
||||
entry_price: float | None = None
|
||||
rets: dict[str, float | None] = Field(default_factory=dict) # "5" -> 收益%(越界/缺数据 None)
|
||||
max_gain: dict[str, float | None] = Field(default_factory=dict) # 窗口内最大涨幅 %
|
||||
max_dd: dict[str, float | None] = Field(default_factory=dict) # 窗口内最大回撤 %
|
||||
|
||||
|
||||
class EvalPairOut(BaseModel):
|
||||
"""手动配对(买点 -> 其后最近卖点)的一笔评估。"""
|
||||
buy_date: date
|
||||
sell_date: date
|
||||
entry_date: date | None = None
|
||||
entry_price: float | None = None
|
||||
exit_price: float | None = None
|
||||
ret_pct: float | None = None # 复权校正收益 %
|
||||
|
||||
|
||||
class PairStatsOut(BaseModel):
|
||||
samples: int
|
||||
mean: float
|
||||
median: float
|
||||
win_rate: float
|
||||
max: float
|
||||
min: float
|
||||
|
||||
|
||||
class SignalsEvaluateResult(BaseModel):
|
||||
samples: int
|
||||
points: list[EvalPointOut] = Field(default_factory=list)
|
||||
stats: list[WindowStatOut] = Field(default_factory=list)
|
||||
pairs: list[EvalPairOut] = Field(default_factory=list)
|
||||
pair_stats: PairStatsOut | None = None
|
||||
|
||||
|
||||
class SignalsScanRequest(BaseModel):
|
||||
ts_code: str = Field(min_length=6, max_length=12)
|
||||
conditions: list[IndicatorCondition] = Field(min_length=1, max_length=10)
|
||||
windows: list[int] = Field(default_factory=lambda: [1, 3, 5, 10, 20, 60])
|
||||
start: date | None = None
|
||||
end: date | None = None
|
||||
|
||||
|
||||
class SignalsScanResponse(BaseModel):
|
||||
ts_code: str
|
||||
dates: list[date] # 命中信号日(升序,已丢弃指标预热区)
|
||||
total: int
|
||||
evaluate: SignalsEvaluateResult
|
||||
|
||||
|
||||
class SignalsEvaluateRequest(BaseModel):
|
||||
ts_code: str = Field(min_length=6, max_length=12)
|
||||
buy_dates: list[date] = Field(min_length=1, max_length=500)
|
||||
sell_dates: list[date] = Field(default_factory=list, max_length=500)
|
||||
windows: list[int] = Field(default_factory=lambda: [1, 3, 5, 10, 20, 60])
|
||||
# 进出场时机默认与 /api/backtest/event 对齐:信号次日入场、到期收盘卖出
|
||||
entry_timing: Literal["next_open", "next_close"] = "next_open"
|
||||
exit_timing: Literal["close", "open"] = "close"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user