update pay ui
This commit is contained in:
198
app.py
198
app.py
@@ -12536,113 +12536,113 @@ def get_hotspot_overview():
|
||||
'change_pct': change_pct
|
||||
})
|
||||
|
||||
# 2. 获取概念异动数据(从 concept_anomaly_hybrid 表)
|
||||
# 2. 获取概念异动数据(优先从 V2 表,fallback 到旧表)
|
||||
alerts = []
|
||||
|
||||
# 首先确保表存在(使用 begin() 来自动提交)
|
||||
try:
|
||||
with engine.begin() as conn:
|
||||
conn.execute(text("""
|
||||
CREATE TABLE IF NOT EXISTS concept_anomaly_hybrid (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
concept_id VARCHAR(64) NOT NULL,
|
||||
alert_time DATETIME NOT NULL,
|
||||
trade_date DATE NOT NULL,
|
||||
alert_type VARCHAR(32) NOT NULL,
|
||||
final_score FLOAT NOT NULL,
|
||||
rule_score FLOAT NOT NULL,
|
||||
ml_score FLOAT NOT NULL,
|
||||
trigger_reason VARCHAR(64),
|
||||
alpha FLOAT,
|
||||
alpha_delta FLOAT,
|
||||
amt_ratio FLOAT,
|
||||
amt_delta FLOAT,
|
||||
rank_pct FLOAT,
|
||||
limit_up_ratio FLOAT,
|
||||
stock_count INT,
|
||||
total_amt FLOAT,
|
||||
triggered_rules JSON,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uk_concept_time (concept_id, alert_time, trade_date),
|
||||
INDEX idx_trade_date (trade_date),
|
||||
INDEX idx_concept_id (concept_id),
|
||||
INDEX idx_final_score (final_score),
|
||||
INDEX idx_alert_type (alert_type)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='概念异动检测结果(融合版)'
|
||||
"""))
|
||||
except Exception as create_err:
|
||||
logger.debug(f"创建表检查: {create_err}")
|
||||
use_v2 = False
|
||||
|
||||
with engine.connect() as conn:
|
||||
# 查询 concept_anomaly_hybrid 表
|
||||
alert_result = conn.execute(text("""
|
||||
SELECT
|
||||
a.concept_id,
|
||||
a.alert_time,
|
||||
a.trade_date,
|
||||
a.alert_type,
|
||||
a.final_score,
|
||||
a.rule_score,
|
||||
a.ml_score,
|
||||
a.trigger_reason,
|
||||
a.alpha,
|
||||
a.alpha_delta,
|
||||
a.amt_ratio,
|
||||
a.amt_delta,
|
||||
a.rank_pct,
|
||||
a.limit_up_ratio,
|
||||
a.stock_count,
|
||||
a.total_amt,
|
||||
a.triggered_rules
|
||||
FROM concept_anomaly_hybrid a
|
||||
WHERE a.trade_date = :trade_date
|
||||
ORDER BY a.alert_time
|
||||
"""), {'trade_date': trade_date})
|
||||
# 尝试查询 V2 表(时间片对齐 + 持续确认版本)
|
||||
try:
|
||||
v2_result = conn.execute(text("""
|
||||
SELECT
|
||||
concept_id, alert_time, trade_date, alert_type,
|
||||
final_score, rule_score, ml_score, trigger_reason, confirm_ratio,
|
||||
alpha, alpha_zscore, amt_zscore, rank_zscore,
|
||||
momentum_3m, momentum_5m, limit_up_ratio, triggered_rules
|
||||
FROM concept_anomaly_v2
|
||||
WHERE trade_date = :trade_date
|
||||
ORDER BY alert_time
|
||||
"""), {'trade_date': trade_date})
|
||||
v2_rows = v2_result.fetchall()
|
||||
if v2_rows:
|
||||
use_v2 = True
|
||||
for row in v2_rows:
|
||||
triggered_rules = None
|
||||
if row[16]:
|
||||
try:
|
||||
triggered_rules = json.loads(row[16]) if isinstance(row[16], str) else row[16]
|
||||
except:
|
||||
pass
|
||||
|
||||
# 获取概念名称映射(从 ES 或缓存)
|
||||
concept_names = {}
|
||||
alerts.append({
|
||||
'concept_id': row[0],
|
||||
'concept_name': row[0], # 后面会填充
|
||||
'time': row[1].strftime('%H:%M') if row[1] else None,
|
||||
'timestamp': row[1].isoformat() if row[1] else None,
|
||||
'alert_type': row[3],
|
||||
'final_score': float(row[4]) if row[4] else None,
|
||||
'rule_score': float(row[5]) if row[5] else None,
|
||||
'ml_score': float(row[6]) if row[6] else None,
|
||||
'trigger_reason': row[7],
|
||||
# V2 新增字段
|
||||
'confirm_ratio': float(row[8]) if row[8] else None,
|
||||
'alpha': float(row[9]) if row[9] else None,
|
||||
'alpha_zscore': float(row[10]) if row[10] else None,
|
||||
'amt_zscore': float(row[11]) if row[11] else None,
|
||||
'rank_zscore': float(row[12]) if row[12] else None,
|
||||
'momentum_3m': float(row[13]) if row[13] else None,
|
||||
'momentum_5m': float(row[14]) if row[14] else None,
|
||||
'limit_up_ratio': float(row[15]) if row[15] else 0,
|
||||
'triggered_rules': triggered_rules,
|
||||
# 兼容字段
|
||||
'importance_score': float(row[4]) / 100 if row[4] else None,
|
||||
'is_v2': True,
|
||||
})
|
||||
except Exception as v2_err:
|
||||
logger.debug(f"V2 表查询失败,使用旧表: {v2_err}")
|
||||
|
||||
for row in alert_result:
|
||||
concept_id = row[0]
|
||||
alert_time = row[1]
|
||||
triggered_rules = None
|
||||
if row[16]:
|
||||
try:
|
||||
triggered_rules = json.loads(row[16]) if isinstance(row[16], str) else row[16]
|
||||
except:
|
||||
pass
|
||||
# Fallback: 查询旧表
|
||||
if not use_v2:
|
||||
try:
|
||||
alert_result = conn.execute(text("""
|
||||
SELECT
|
||||
a.concept_id, a.alert_time, a.trade_date, a.alert_type,
|
||||
a.final_score, a.rule_score, a.ml_score, a.trigger_reason,
|
||||
a.alpha, a.alpha_delta, a.amt_ratio, a.amt_delta,
|
||||
a.rank_pct, a.limit_up_ratio, a.stock_count, a.total_amt,
|
||||
a.triggered_rules
|
||||
FROM concept_anomaly_hybrid a
|
||||
WHERE a.trade_date = :trade_date
|
||||
ORDER BY a.alert_time
|
||||
"""), {'trade_date': trade_date})
|
||||
|
||||
# 获取概念名称(优先从缓存,否则使用 concept_id)
|
||||
concept_name = concept_names.get(concept_id) or concept_id
|
||||
for row in alert_result:
|
||||
triggered_rules = None
|
||||
if row[16]:
|
||||
try:
|
||||
triggered_rules = json.loads(row[16]) if isinstance(row[16], str) else row[16]
|
||||
except:
|
||||
pass
|
||||
|
||||
# 计算涨停数量(从 limit_up_ratio 和 stock_count 估算)
|
||||
limit_up_ratio = float(row[13]) if row[13] else 0
|
||||
stock_count = int(row[14]) if row[14] else 0
|
||||
limit_up_count = int(limit_up_ratio * stock_count) if stock_count > 0 else 0
|
||||
limit_up_ratio = float(row[13]) if row[13] else 0
|
||||
stock_count = int(row[14]) if row[14] else 0
|
||||
limit_up_count = int(limit_up_ratio * stock_count) if stock_count > 0 else 0
|
||||
|
||||
alerts.append({
|
||||
'concept_id': concept_id,
|
||||
'concept_name': concept_name,
|
||||
'time': alert_time.strftime('%H:%M') if alert_time else None,
|
||||
'timestamp': alert_time.isoformat() if alert_time else None,
|
||||
'alert_type': row[3],
|
||||
'final_score': float(row[4]) if row[4] else None,
|
||||
'rule_score': float(row[5]) if row[5] else None,
|
||||
'ml_score': float(row[6]) if row[6] else None,
|
||||
'trigger_reason': row[7],
|
||||
'alpha': float(row[8]) if row[8] else None,
|
||||
'alpha_delta': float(row[9]) if row[9] else None,
|
||||
'amt_ratio': float(row[10]) if row[10] else None,
|
||||
'amt_delta': float(row[11]) if row[11] else None,
|
||||
'rank_pct': float(row[12]) if row[12] else None,
|
||||
'limit_up_ratio': limit_up_ratio,
|
||||
'limit_up_count': limit_up_count,
|
||||
'stock_count': stock_count,
|
||||
'total_amt': float(row[15]) if row[15] else None,
|
||||
'triggered_rules': triggered_rules,
|
||||
# 兼容旧字段
|
||||
'importance_score': float(row[4]) / 100 if row[4] else None,
|
||||
})
|
||||
alerts.append({
|
||||
'concept_id': row[0],
|
||||
'concept_name': row[0],
|
||||
'time': row[1].strftime('%H:%M') if row[1] else None,
|
||||
'timestamp': row[1].isoformat() if row[1] else None,
|
||||
'alert_type': row[3],
|
||||
'final_score': float(row[4]) if row[4] else None,
|
||||
'rule_score': float(row[5]) if row[5] else None,
|
||||
'ml_score': float(row[6]) if row[6] else None,
|
||||
'trigger_reason': row[7],
|
||||
'alpha': float(row[8]) if row[8] else None,
|
||||
'alpha_delta': float(row[9]) if row[9] else None,
|
||||
'amt_ratio': float(row[10]) if row[10] else None,
|
||||
'amt_delta': float(row[11]) if row[11] else None,
|
||||
'rank_pct': float(row[12]) if row[12] else None,
|
||||
'limit_up_ratio': limit_up_ratio,
|
||||
'limit_up_count': limit_up_count,
|
||||
'stock_count': stock_count,
|
||||
'total_amt': float(row[15]) if row[15] else None,
|
||||
'triggered_rules': triggered_rules,
|
||||
'importance_score': float(row[4]) / 100 if row[4] else None,
|
||||
'is_v2': False,
|
||||
})
|
||||
except Exception as old_err:
|
||||
logger.debug(f"旧表查询也失败: {old_err}")
|
||||
|
||||
# 尝试批量获取概念名称
|
||||
if alerts:
|
||||
|
||||
Reference in New Issue
Block a user