feat(web): M8.6a 交易信号页(/signals)+ 导航与类型

- app/signals/page.tsx:因子+买卖排名阈值+时点+白名单 → BUY/WATCH/SELL 表格
  (类型 Pill 着色、得分/价格/触发理由)+ 最近信号记录回看
- app-shell 导航加入「交易信号」;lib/types.ts 增加 Signal* 类型与 universe.symbols
- 真实 MySQL 端到端:/api/signals 返回可解释信号(跌破 MA60 的 SELL 警示等);
  /signals 页面 200;tsc --noEmit 通过
This commit is contained in:
Simon
2026-09-09 00:40:22 +08:00
parent 8b2f8ac35c
commit e1ac23fa25
3 changed files with 254 additions and 2 deletions
+53 -2
View File
@@ -22,7 +22,11 @@ export interface FactorMeta {
export interface ResearchSpec {
type: "factor_test" | "backtest";
universe: { exclude_st?: boolean; min_listing_days?: number };
universe: {
exclude_st?: boolean;
min_listing_days?: number;
symbols?: string[];
};
factors: { name: string; weight: number }[];
selection: { top_n: number };
rebalance: "weekly" | "monthly";
@@ -84,7 +88,11 @@ export interface SelectionCondition {
}
export interface SelectionQuery {
universe: { exclude_st?: boolean; min_listing_days?: number };
universe: {
exclude_st?: boolean;
min_listing_days?: number;
symbols?: string[];
};
as_of?: string | null; // null/省略 = 最近交易日
method: "score" | "condition";
factors: { name: string; weight: number }[];
@@ -126,3 +134,46 @@ export interface SelectionRun {
result: SelectionResult;
}
/* ---- 交易信号(v2 §15,与 domain/entities/signal.py 对应) ---- */
export interface SignalRules {
buy_rank_threshold?: number;
buy_require_trend?: boolean;
buy_require_momentum?: boolean;
trend_ma?: number;
sell_rank_threshold?: number;
sell_on_trend_break?: boolean;
max_output_rank?: number;
}
export interface SignalEvent {
symbol: string;
signal_date: string;
signal_type: "BUY" | "WATCH" | "SELL";
score?: number | null;
price?: number | null;
trigger_reason: string[];
}
export interface SignalResult {
as_of_date: string;
rules: SignalRules;
statistics: { universe_size: number; buy: number; watch: number; sell: number };
events: SignalEvent[];
config_snapshot: Record<string, unknown>;
}
export interface SignalMeta {
id: string;
as_of: string;
buy: number;
watch: number;
sell: number;
created_at?: string | null;
}
export interface SignalRun {
signal_id: string;
result: SignalResult;
}