227 lines
8.1 KiB
Python
227 lines
8.1 KiB
Python
"""Pydantic 数据模型"""
|
||
|
||
from pydantic import BaseModel
|
||
from datetime import datetime
|
||
|
||
|
||
class StockQuote(BaseModel):
|
||
ts_code: str
|
||
name: str
|
||
price: float
|
||
pct_chg: float
|
||
volume: float # 成交量(手)
|
||
amount: float # 成交额(万元)
|
||
turnover_rate: float # 换手率 %
|
||
pe: float | None = None
|
||
pb: float | None = None
|
||
circ_mv: float | None = None # 流通市值(亿)
|
||
total_mv: float | None = None # 总市值(亿)
|
||
volume_ratio: float | None = None # 量比
|
||
high: float | None = None
|
||
low: float | None = None
|
||
open: float | None = None
|
||
pre_close: float | None = None
|
||
limit_up: float | None = None
|
||
limit_down: float | None = None
|
||
amplitude: float | None = None # 振幅 %
|
||
|
||
|
||
class CapitalFlow(BaseModel):
|
||
ts_code: str
|
||
trade_date: str
|
||
buy_elg_amount: float = 0 # 特大单买入(万)
|
||
sell_elg_amount: float = 0
|
||
buy_lg_amount: float = 0 # 大单买入(万)
|
||
sell_lg_amount: float = 0
|
||
buy_md_amount: float = 0 # 中单买入(万)
|
||
sell_md_amount: float = 0
|
||
buy_sm_amount: float = 0 # 小单买入(万)
|
||
sell_sm_amount: float = 0
|
||
net_mf_amount: float = 0 # 主力净流入(万)
|
||
|
||
@property
|
||
def main_net_inflow(self) -> float:
|
||
"""主力净流入 = 特大单净买 + 大单净买"""
|
||
return (self.buy_elg_amount - self.sell_elg_amount +
|
||
self.buy_lg_amount - self.sell_lg_amount)
|
||
|
||
@property
|
||
def total_amount(self) -> float:
|
||
return (self.buy_elg_amount + self.sell_elg_amount +
|
||
self.buy_lg_amount + self.sell_lg_amount +
|
||
self.buy_md_amount + self.sell_md_amount +
|
||
self.buy_sm_amount + self.sell_sm_amount)
|
||
|
||
|
||
class SectorInfo(BaseModel):
|
||
sector_code: str
|
||
sector_name: str
|
||
board_type: str = "snapshot" # industry / concept / snapshot
|
||
theme_id: str = ""
|
||
theme_name: str = ""
|
||
theme_aliases: list[str] = []
|
||
trade_date: str = ""
|
||
pct_change: float = 0 # 涨跌幅 %
|
||
capital_inflow: float = 0 # 主力净流入(万元,原始数据来自Tushare亿元×10000)
|
||
limit_up_count: int = 0 # 涨停数
|
||
days_continuous: int = 0 # 连续资金流入天数
|
||
heat_score: float = 0 # 热度综合评分
|
||
stage: str = "mid" # 板块阶段: early/mid/late/end
|
||
|
||
# ── 板块分析增强字段 ──
|
||
member_count: int = 0 # 成分股数量
|
||
leading_stocks: list[dict] = [] # 领涨股 [{ts_code, name, pct_chg, amount}]
|
||
capital_trend: list[float] = [] # 近5日资金净流入趋势(万)
|
||
pct_trend: list[float] = [] # 近5日涨跌幅趋势
|
||
turnover_avg: float = 0 # 板块平均换手率
|
||
main_force_ratio: float = 0 # 主力资金占比(主力净流入/总成交额)
|
||
realtime_pct_change: float | None = None
|
||
realtime_limit_up_count: int | None = None
|
||
realtime_amount: float | None = None
|
||
realtime_turnover_rate: float | None = None
|
||
realtime_up_count: int | None = None
|
||
realtime_down_count: int | None = None
|
||
leading_stocks_realtime: list[dict] = []
|
||
is_realtime: bool = False
|
||
data_mode: str = "daily_snapshot"
|
||
source: str = "snapshot"
|
||
data_status: str = "fresh" # fresh / stale / fallback / snapshot
|
||
source_detail: str = ""
|
||
catalyst_score: float = 0
|
||
catalyst_count: int = 0
|
||
catalyst_reasons: list[str] = []
|
||
|
||
|
||
class MarketTemperature(BaseModel):
|
||
trade_date: str
|
||
up_count: int = 0 # 上涨家数
|
||
down_count: int = 0 # 下跌家数
|
||
limit_up_count: int = 0 # 涨停数(自然涨停)
|
||
limit_down_count: int = 0 # 跌停数
|
||
max_streak: int = 0 # 最高连板
|
||
broken_rate: float = 0 # 炸板率 %
|
||
index_above_ma20: bool = False # 上证在 MA20 上方
|
||
temperature: float = 0 # 综合温度 0-100
|
||
source: str = "snapshot"
|
||
data_status: str = "fresh" # fresh / estimated / degraded / snapshot
|
||
source_detail: str = ""
|
||
limit_counts_reliable: bool = False
|
||
|
||
|
||
class MarketBreadth(BaseModel):
|
||
trade_date: str
|
||
up_count: int = 0
|
||
down_count: int = 0
|
||
flat_count: int = 0
|
||
limit_up_count: int = 0
|
||
limit_down_count: int = 0
|
||
total_count: int = 0
|
||
sample_count: int = 0
|
||
source: str = "snapshot"
|
||
reliable: bool = False
|
||
limit_counts_reliable: bool = False
|
||
|
||
|
||
class TechnicalSignal(BaseModel):
|
||
ts_code: str
|
||
name: str = ""
|
||
ma_bullish: bool = False # 均线多头排列
|
||
volume_breakout: bool = False # 放量突破
|
||
macd_golden: bool = False # MACD金叉
|
||
rsi_healthy: bool = False # RSI节奏区间(辅助参考)
|
||
pullback_support: bool = False # 缩量回踩支撑
|
||
big_yang: bool = False # 底部放量长阳
|
||
boll_support: bool = False # 布林带下轨支撑
|
||
score: float = 0 # 信号触发计数分
|
||
trend_score: float = 0 # 趋势评分(推荐体系用的技术面分数)
|
||
signal_count: int = 0 # 满足的信号数量
|
||
|
||
# 位置安全评估(防追高)
|
||
rally_pct_5d: float = 0 # 近5日累计涨幅
|
||
rally_pct_10d: float = 0 # 近10日累计涨幅
|
||
distance_from_high: float = 0 # 距离60日高点 %(负=已回调)
|
||
position_score: float = 50 # 位置安全得分 0-100
|
||
|
||
# 价格参考
|
||
support_price: float | None = None # 支撑位
|
||
resist_price: float | None = None # 压力位
|
||
stop_loss_price: float | None = None # 止损价
|
||
|
||
|
||
class Recommendation(BaseModel):
|
||
ts_code: str
|
||
name: str
|
||
sector: str
|
||
score: float # 综合评分
|
||
market_temp_score: float
|
||
sector_score: float
|
||
capital_score: float
|
||
technical_score: float
|
||
supply_demand_score: float = 0 # 供需评分
|
||
price_action_score: float = 0 # 价格行为评分
|
||
position_score: float = 50 # 位置安全得分
|
||
valuation_score: float = 50 # 估值安全得分
|
||
signal: str # BUY / SELL / HOLD
|
||
entry_price: float | None = None
|
||
target_price: float | None = None
|
||
stop_loss: float | None = None
|
||
reasons: list[str] = []
|
||
risk_note: str = ""
|
||
level: str = "" # 强烈推荐/推荐/观望/回避
|
||
strategy: str = "trend_breakout" # trend_breakout / momentum(旧) / potential(旧)
|
||
entry_signal_type: str = "none" # breakout / pullback / launch / none
|
||
entry_timing: str = "" # 进场时机建议(盘中适用)
|
||
action_plan: str = "观察" # 可操作 / 重点关注 / 观察
|
||
trigger_condition: str = "" # 触发条件
|
||
invalidation_condition: str = "" # 失效条件
|
||
suggested_position_pct: float = 0 # 建议仓位 %
|
||
review_after_days: int = 3 # 建议复盘天数
|
||
lifecycle_status: str = "candidate" # candidate / actionable / tracking / closed
|
||
data_freshness: str = "" # 数据新鲜度说明
|
||
llm_analysis: str = "" # LLM 深度分析
|
||
llm_score: float | None = None # AI 评分 1-10
|
||
recall_tags: list[str] = []
|
||
prefilter_decision: str = ""
|
||
prefilter_reason: str = ""
|
||
focus_points: list[str] = []
|
||
decision_trace: dict = {}
|
||
scan_session: str = ""
|
||
created_at: datetime | None = None
|
||
|
||
|
||
class StrategyFocus(BaseModel):
|
||
label: str
|
||
description: str
|
||
|
||
|
||
class StrategySectorFocus(BaseModel):
|
||
sector_name: str
|
||
stage: str = "mid"
|
||
heat_score: float = 0
|
||
pct_change: float = 0
|
||
limit_up_count: int = 0
|
||
turnover_rate: float = 0
|
||
up_count: int = 0
|
||
down_count: int = 0
|
||
data_mode: str = "daily_snapshot"
|
||
view: str = ""
|
||
|
||
|
||
class StrategyBoard(BaseModel):
|
||
trade_date: str
|
||
data_mode: str = "daily_snapshot"
|
||
market_regime: str
|
||
risk_level: str
|
||
action_bias: str
|
||
position_suggestion: str
|
||
summary: str
|
||
recommended_mode: str
|
||
strategy_focus: list[StrategyFocus] = []
|
||
watch_sectors: list[StrategySectorFocus] = []
|
||
avoid_rules: list[str] = []
|
||
iteration_notes: list[str] = []
|
||
iteration_report: dict = {}
|
||
metrics: dict = {}
|
||
ai_review: str = ""
|
||
generated_by: str = "rules"
|