From 16d90814a16c7b536892975c4bcc6fcfb52d8f1d Mon Sep 17 00:00:00 2001 From: dekun Date: Fri, 3 Jul 2026 06:43:20 +0800 Subject: [PATCH] Fix empty recommend list when CTP ticks unavailable off-hours. Fall back to daily prev_close for margin estimates; keep previous cache when refresh gets all no_price; stop re-fetching only for missing turnover. Co-authored-by: Cursor --- modules/trading/product_recommend.py | 19 +++++++++++++++++-- modules/trading/recommend_store.py | 25 +++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/modules/trading/product_recommend.py b/modules/trading/product_recommend.py index 4623925..0ad9074 100644 --- a/modules/trading/product_recommend.py +++ b/modules/trading/product_recommend.py @@ -302,6 +302,21 @@ def list_product_recommendations( quote = quote_fn(ths) or {} price = quote.get("price") main_code = (quote.get("ths_code") or "").strip() + daily: dict = {} + if main_code: + daily = analyze_product_daily(main_code) + if not price or float(price or 0) <= 0: + for key in ("prev_close", "today_open", "price"): + val = daily.get(key) if daily else None + if val in (None, "", 0): + continue + try: + px = float(val) + except (TypeError, ValueError): + continue + if px > 0: + price = px + break row = assess_product_for_capital( product, capital, price, max_margin_pct=max_margin_pct, @@ -311,8 +326,8 @@ def list_product_recommendations( margin_used=margin_used, ) row["main_code"] = main_code - if main_code: - row.update(analyze_product_daily(main_code)) + if daily: + row.update(daily) _attach_turnover(row) return row except Exception as exc: diff --git a/modules/trading/recommend_store.py b/modules/trading/recommend_store.py index 5790399..5c4f40e 100644 --- a/modules/trading/recommend_store.py +++ b/modules/trading/recommend_store.py @@ -99,8 +99,6 @@ def recommend_cache_needs_refresh( return True if rows_missing_category(rows): return True - if rows_missing_turnover(rows): - return True if rows_missing_contract_spec(rows): return True if float(capital or 0) > 0 and not rows: @@ -294,6 +292,29 @@ def refresh_recommend_cache( margin_used=used, ) rows = filter_affordable_recommendations(all_rows) + prev_rows: list[dict] = [] + try: + prev_row = conn.execute( + "SELECT rows_json FROM product_recommend_cache WHERE id=1", + ).fetchone() + if prev_row: + prev_rows = json.loads(prev_row["rows_json"] or "[]") + if not isinstance(prev_rows, list): + prev_rows = [] + except Exception: + prev_rows = [] + if not rows and prev_rows: + no_price = sum(1 for r in all_rows if r.get("status") == "no_price") + if not all_rows or no_price >= max(1, len(all_rows) // 2): + logger.warning( + "recommend refresh: keeping previous %d rows " + "(affordable=0, no_price=%d/%d, capital=%.2f)", + len(prev_rows), + no_price, + len(all_rows), + float(capital or 0), + ) + rows = prev_rows if not rows and float(capital or 0) > 0: logger.warning( "recommend refresh: 0 affordable rows capital=%.2f total=%d no_price=%d blocked=%d",