From fd5337f163786ebb4c9b9e3ba657e3e5113762c8 Mon Sep 17 00:00:00 2001 From: "WIN-ALB39P9B6BU\\Docker" Date: Wed, 24 Jun 2026 17:22:24 +0800 Subject: [PATCH] update --- core/scoring/config.py | 2 +- core/scoring/features/validator.py | 12 +++++++++ core/ui/flet/app_v2.py | 39 ++++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/core/scoring/config.py b/core/scoring/config.py index 7a9da38..9b1c7d9 100644 --- a/core/scoring/config.py +++ b/core/scoring/config.py @@ -10,7 +10,7 @@ GRID_HIGH = 11 # 网格上限 GRID_STEP = 1 # 网格间距(整数格) # ---- 候选股过滤 ---- -FILTER_MIN_CLOSE = 8 # 最低收盘价 +FILTER_MIN_CLOSE = 5 # 最低收盘价(覆盖网格策略持仓股) FILTER_MAX_CLOSE = 13 # 最高收盘价 REQUIRE_DAYS = 120 # 最少交易日数 diff --git a/core/scoring/features/validator.py b/core/scoring/features/validator.py index 2f47c5c..3ad9220 100644 --- a/core/scoring/features/validator.py +++ b/core/scoring/features/validator.py @@ -128,6 +128,18 @@ def load_candidates(trade_date: date) -> DataContext: PrintLog(LogLevel.INFO, f'[validator] 候选: {len(candidates)} 通过, {len(excluded)} 排除') + # 3.5 补充:强制加入网格持仓股(不受价格过滤限制) + from core.sfgrid.model import SFGridTradeTarget + pos_rows = list(SFGridTradeTarget + .select(SFGridTradeTarget.stock_code) + .where(SFGridTradeTarget.enabled == True) + .dicts()) + pos_codes = [r['stock_code'].split('.')[0] for r in pos_rows] + forced = [c for c in pos_codes if c not in candidates and c not in excluded] + if forced: + PrintLog(LogLevel.INFO, f'[validator] 强制加入网格持仓股: {forced}') + candidates.extend(forced) + # 4. 裁剪K线到只含候选股 (保留最近180日) kline_df = kline_df[kline_df['stock_code'].isin(candidates)].copy() cut_date = trade_date - timedelta(days=365) diff --git a/core/ui/flet/app_v2.py b/core/ui/flet/app_v2.py index dd98071..125b165 100644 --- a/core/ui/flet/app_v2.py +++ b/core/ui/flet/app_v2.py @@ -302,6 +302,22 @@ class _DataStore: elif ot == 24 and '空' not in tags: tags.append('空') return tags + def pool_action_tags(self, stock_code: str) -> list[str]: + """某标的的股票池待执行标记(eliminate/liquidate)""" + plain = _plain(stock_code) + from core.scoring.models import PendingPoolAction + rows = list(PendingPoolAction + .select(PendingPoolAction.action_type) + .where(PendingPoolAction.stock_code == plain) + .dicts()) + tags = [] + for r in rows: + if r['action_type'] == 'eliminate' and '淘汰' not in tags: + tags.append('淘汰') + elif r['action_type'] == 'liquidate' and '沉寂' not in tags: + tags.append('沉寂') + return tags + # ══════════════════════════════════════════════════════════════ # GridPanel — 网格策略持仓表格 @@ -326,7 +342,7 @@ class _GridPanel: def _rebuild(self): # (width, expand): 0=固定宽, >0=弹性比重; 新增排名列(50px) - _C = [(35, 0), (0, 2), (70, 1), (0, 2), (50, 1), (55, 1), (50, 0), (60, 1), (0, 1)] + _C = [(35, 0), (0, 2), (70, 1), (80, 1), (50, 1), (55, 1), (50, 0), (60, 1), (0, 1)] H = ["ID", "股票", "市场价", "网格基准", "持仓", "成本", "排名", "状态", "操作"] header = [] for h, (w, e) in zip(H, _C): @@ -414,7 +430,26 @@ class _GridPanel: on_click=lambda e, tt=t: self._dialogs.confirm_remove(tt)), ] - cells_text = [str(tid), f'{t.stock_code} {t.stock_name}', + # 股票列:名称 + 标记标签 + pool_tags = self._data.pool_action_tags(t.stock_code) + tag_chips = [] + if pool_tags: + # 有淘汰/沉寂时,显示操作标签,不显示默认标签 + for tag in pool_tags: + color = '#E67E22' if tag == '淘汰' else '#E74C3C' + tag_chips.append(ft.Container( + _text(tag, color='white', bold=True, size=10), + bgcolor=color, border_radius=4, padding=ft.Padding(2, 1, 2, 1), + )) + else: + # 无操作标签时,显示默认"网格"标签 + tag_chips.append(ft.Container( + _text('网格', color='white', bold=True, size=10), + bgcolor='#2E7D32', border_radius=4, padding=ft.Padding(2, 1, 2, 1), + )) + name_cell = ft.Row([_text(f'{t.stock_code} {t.stock_name}')] + tag_chips, spacing=4) + + cells_text = [str(tid), name_cell, f'{mp:.3f}', gcell, str(t.current_position), f'{self._data.avgPrices.get(tid, 0):.3f}',