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 <cursoragent@cursor.com>
This commit is contained in:
@@ -302,6 +302,21 @@ def list_product_recommendations(
|
|||||||
quote = quote_fn(ths) or {}
|
quote = quote_fn(ths) or {}
|
||||||
price = quote.get("price")
|
price = quote.get("price")
|
||||||
main_code = (quote.get("ths_code") or "").strip()
|
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(
|
row = assess_product_for_capital(
|
||||||
product, capital, price,
|
product, capital, price,
|
||||||
max_margin_pct=max_margin_pct,
|
max_margin_pct=max_margin_pct,
|
||||||
@@ -311,8 +326,8 @@ def list_product_recommendations(
|
|||||||
margin_used=margin_used,
|
margin_used=margin_used,
|
||||||
)
|
)
|
||||||
row["main_code"] = main_code
|
row["main_code"] = main_code
|
||||||
if main_code:
|
if daily:
|
||||||
row.update(analyze_product_daily(main_code))
|
row.update(daily)
|
||||||
_attach_turnover(row)
|
_attach_turnover(row)
|
||||||
return row
|
return row
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
|
|||||||
@@ -99,8 +99,6 @@ def recommend_cache_needs_refresh(
|
|||||||
return True
|
return True
|
||||||
if rows_missing_category(rows):
|
if rows_missing_category(rows):
|
||||||
return True
|
return True
|
||||||
if rows_missing_turnover(rows):
|
|
||||||
return True
|
|
||||||
if rows_missing_contract_spec(rows):
|
if rows_missing_contract_spec(rows):
|
||||||
return True
|
return True
|
||||||
if float(capital or 0) > 0 and not rows:
|
if float(capital or 0) > 0 and not rows:
|
||||||
@@ -294,6 +292,29 @@ def refresh_recommend_cache(
|
|||||||
margin_used=used,
|
margin_used=used,
|
||||||
)
|
)
|
||||||
rows = filter_affordable_recommendations(all_rows)
|
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:
|
if not rows and float(capital or 0) > 0:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
"recommend refresh: 0 affordable rows capital=%.2f total=%d no_price=%d blocked=%d",
|
"recommend refresh: 0 affordable rows capital=%.2f total=%d no_price=%d blocked=%d",
|
||||||
|
|||||||
Reference in New Issue
Block a user