Label night-session products and hide day-only symbols at night.
Mark tradable varieties with a night tag; during 21:00-02:30 filter out index futures and other products without night sessions from symbol picker and recommend list. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+62
-5
@@ -87,6 +87,38 @@ PRODUCT_CATEGORIES = ["贵金属", "有色金属", "黑色金属", "能源化工
|
||||
for _p in PRODUCTS:
|
||||
_p["category"] = PRODUCT_CATEGORY_MAP.get(_p["ths"], "其他")
|
||||
|
||||
# 无夜盘品种(日盘-only):中金所股指、大商所鸡蛋/生猪等
|
||||
NO_NIGHT_SESSION_THS = frozenset({"IF", "IH", "IC", "IM", "jd", "lh"})
|
||||
|
||||
|
||||
def product_has_night_session(ths_or_product) -> bool:
|
||||
"""品种是否参与夜盘交易。"""
|
||||
if isinstance(ths_or_product, dict):
|
||||
ths = (ths_or_product.get("ths") or "").strip()
|
||||
else:
|
||||
ths = (ths_or_product or "").strip()
|
||||
if not ths:
|
||||
return True
|
||||
m = re.match(r"^([A-Za-z]+)", ths)
|
||||
letters = m.group(1) if m else ths
|
||||
return letters not in NO_NIGHT_SESSION_THS and letters.upper() not in NO_NIGHT_SESSION_THS
|
||||
|
||||
|
||||
def filter_for_trading_session(rows: list[dict]) -> list[dict]:
|
||||
"""夜盘时段隐藏无夜盘品种。"""
|
||||
from market_sessions import is_night_trading_session
|
||||
|
||||
if not is_night_trading_session():
|
||||
return rows
|
||||
out: list[dict] = []
|
||||
for row in rows:
|
||||
if row.get("has_night_session") is False:
|
||||
continue
|
||||
ths = row.get("ths") or row.get("ths_code") or ""
|
||||
if row.get("has_night_session") is True or product_has_night_session(ths):
|
||||
out.append(row)
|
||||
return out
|
||||
|
||||
|
||||
def product_category(ths: str) -> str:
|
||||
return PRODUCT_CATEGORY_MAP.get((ths or "").strip(), "其他")
|
||||
@@ -341,15 +373,22 @@ def resolve_main_contract(product: dict) -> Optional[dict]:
|
||||
}
|
||||
|
||||
if best:
|
||||
best = _enrich_item(best, product)
|
||||
_MAIN_CACHE[cache_key] = (now, best)
|
||||
return best
|
||||
|
||||
|
||||
def _enrich_item(item: dict) -> dict:
|
||||
def _enrich_item(item: dict, product: Optional[dict] = None) -> dict:
|
||||
out = dict(item)
|
||||
if not out.get("input_label"):
|
||||
out["input_label"] = f"{out.get('name', '')} {out.get('ths_code', '')}".strip()
|
||||
out["near_expiry"] = is_near_expiry_main(out.get("ths_code", ""))
|
||||
if product is None and out.get("ths_code"):
|
||||
product = _product_for_contract_code(out["ths_code"])
|
||||
if product is not None:
|
||||
out["has_night_session"] = product_has_night_session(product)
|
||||
elif "has_night_session" not in out:
|
||||
out["has_night_session"] = product_has_night_session(out.get("ths_code") or "")
|
||||
return out
|
||||
|
||||
|
||||
@@ -365,7 +404,7 @@ def refresh_main_index():
|
||||
try:
|
||||
main = fut.result()
|
||||
if main:
|
||||
new_idx[product["sina"]] = _enrich_item(main)
|
||||
new_idx[product["sina"]] = _enrich_item(main, product)
|
||||
except Exception:
|
||||
pass
|
||||
with _main_index_lock:
|
||||
@@ -389,7 +428,7 @@ def _start_warm_thread():
|
||||
def _stub_main_contract(product: dict) -> dict:
|
||||
"""缓存未就绪时的快速占位(当月合约),避免首次打开搜索为空。"""
|
||||
today = date.today()
|
||||
return _enrich_item(_make_symbol_item(product, today.year, today.month, 0))
|
||||
return _enrich_item(_make_symbol_item(product, today.year, today.month, 0), product)
|
||||
|
||||
|
||||
def _product_matches(product: dict, q_lower: str) -> bool:
|
||||
@@ -428,12 +467,16 @@ def search_symbols(query: str) -> list:
|
||||
return []
|
||||
|
||||
q_lower = q.lower()
|
||||
from market_sessions import is_night_trading_session
|
||||
night_only = is_night_trading_session()
|
||||
with _main_index_lock:
|
||||
index = dict(_main_index)
|
||||
index_ready = bool(index)
|
||||
|
||||
scored: list[tuple[int, dict]] = []
|
||||
for p in PRODUCTS:
|
||||
if night_only and not product_has_night_session(p):
|
||||
continue
|
||||
if not _product_matches(p, q_lower):
|
||||
continue
|
||||
main = index.get(p["sina"])
|
||||
@@ -444,6 +487,7 @@ def search_symbols(query: str) -> list:
|
||||
|
||||
scored.sort(key=lambda x: -x[0])
|
||||
results = [item for _, item in scored[:12]]
|
||||
results = filter_for_trading_session(results)
|
||||
|
||||
if not results and len(q) >= 3:
|
||||
codes = ths_to_codes(q)
|
||||
@@ -460,10 +504,19 @@ def search_symbols(query: str) -> list:
|
||||
"display": f"{name} ({codes['ths_code']})",
|
||||
"volume": raw.get("volume", 0) if raw else 0,
|
||||
}))
|
||||
results = filter_for_trading_session(results)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def enrich_recommend_row(row: dict) -> dict:
|
||||
"""补全推荐行字段(含是否夜盘)。"""
|
||||
out = dict(row)
|
||||
ths = out.get("ths") or ""
|
||||
out["has_night_session"] = product_has_night_session(ths)
|
||||
return out
|
||||
|
||||
|
||||
_THS_TO_PRODUCT = {p["ths"]: p for p in PRODUCTS}
|
||||
for _p in PRODUCTS:
|
||||
_THS_TO_PRODUCT.setdefault(_p["ths"].lower(), _p)
|
||||
@@ -531,7 +584,7 @@ def _item_from_recommend_row(row: dict, product: dict) -> Optional[dict]:
|
||||
}
|
||||
if max_lots is not None:
|
||||
item["max_lots"] = max_lots
|
||||
return _enrich_item(item)
|
||||
return _enrich_item(item, product)
|
||||
|
||||
with _main_index_lock:
|
||||
main = _main_index.get(product["sina"])
|
||||
@@ -539,7 +592,7 @@ def _item_from_recommend_row(row: dict, product: dict) -> Optional[dict]:
|
||||
item = dict(main)
|
||||
if max_lots is not None:
|
||||
item["max_lots"] = max_lots
|
||||
return _enrich_item(item)
|
||||
return _enrich_item(item, product)
|
||||
|
||||
item = _stub_main_contract(product)
|
||||
if max_lots is not None:
|
||||
@@ -563,6 +616,10 @@ def list_recommended_symbols_grouped(recommend_rows: list[dict]) -> list[dict]:
|
||||
product = _product_for_ths(ths_key)
|
||||
if not product:
|
||||
continue
|
||||
if not product_has_night_session(product):
|
||||
from market_sessions import is_night_trading_session
|
||||
if is_night_trading_session():
|
||||
continue
|
||||
seen.add(ths_key)
|
||||
item = _item_from_recommend_row(row, product)
|
||||
if not item:
|
||||
|
||||
Reference in New Issue
Block a user