+53
-23
@@ -71,6 +71,7 @@ _CACHE_TTL = 300
|
||||
_main_index_lock = threading.Lock()
|
||||
_main_index: dict[str, dict] = {}
|
||||
_main_index_ts = 0.0
|
||||
_index_refresh_lock = threading.Lock()
|
||||
|
||||
|
||||
def build_ths_code(product: dict, year: int, month: int) -> str:
|
||||
@@ -210,7 +211,20 @@ def is_near_expiry_main(ths_code: str) -> bool:
|
||||
return months_ahead <= 1
|
||||
|
||||
|
||||
def _make_symbol_item(product: dict, year: int, month: int, volume: float) -> dict:
|
||||
def _main_contract_score(raw: dict) -> float:
|
||||
"""主力判定:优先持仓量,其次成交量。"""
|
||||
oi = float(raw.get("open_interest") or 0)
|
||||
vol = float(raw.get("volume") or 0)
|
||||
return oi if oi > 0 else vol
|
||||
|
||||
|
||||
def _make_symbol_item(
|
||||
product: dict,
|
||||
year: int,
|
||||
month: int,
|
||||
volume: float,
|
||||
open_interest: float = 0,
|
||||
) -> dict:
|
||||
ths = build_ths_code(product, year, month)
|
||||
name = product["name"]
|
||||
return {
|
||||
@@ -223,6 +237,7 @@ def _make_symbol_item(product: dict, year: int, month: int, volume: float) -> di
|
||||
"display": f"{name} 主力 {ths}",
|
||||
"input_label": f"{name} {ths}",
|
||||
"volume": volume,
|
||||
"open_interest": open_interest,
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +251,7 @@ def resolve_main_contract(product: dict) -> Optional[dict]:
|
||||
today = date.today()
|
||||
y, m = today.year, today.month
|
||||
best = None
|
||||
best_score = 0.0
|
||||
|
||||
for i in range(14):
|
||||
cy, cm = y, m + i
|
||||
@@ -244,10 +260,17 @@ def resolve_main_contract(product: dict) -> Optional[dict]:
|
||||
cy += 1
|
||||
sina = build_sina_code(product, cy, cm)
|
||||
raw = fetch_raw_for_volume(sina)
|
||||
if raw and raw["volume"] > 0:
|
||||
item = _make_symbol_item(product, cy, cm, raw["volume"])
|
||||
if best is None or raw["volume"] > best["volume"]:
|
||||
best = item
|
||||
if not raw:
|
||||
continue
|
||||
score = _main_contract_score(raw)
|
||||
if score <= 0:
|
||||
continue
|
||||
item = _make_symbol_item(
|
||||
product, cy, cm, raw["volume"], raw.get("open_interest", 0),
|
||||
)
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best = item
|
||||
|
||||
if best is None:
|
||||
sina_main = build_sina_main_code(product)
|
||||
@@ -288,20 +311,21 @@ def _enrich_item(item: dict) -> dict:
|
||||
def refresh_main_index():
|
||||
"""后台预热全部品种主力合约,搜索时只读本地缓存。"""
|
||||
global _main_index, _main_index_ts
|
||||
new_idx: dict[str, dict] = {}
|
||||
with ThreadPoolExecutor(max_workers=10) as pool:
|
||||
futures = {pool.submit(resolve_main_contract, p): p for p in PRODUCTS}
|
||||
for fut in as_completed(futures):
|
||||
product = futures[fut]
|
||||
try:
|
||||
main = fut.result()
|
||||
if main:
|
||||
new_idx[product["sina"]] = _enrich_item(main)
|
||||
except Exception:
|
||||
pass
|
||||
with _main_index_lock:
|
||||
_main_index = new_idx
|
||||
_main_index_ts = time.time()
|
||||
with _index_refresh_lock:
|
||||
new_idx: dict[str, dict] = {}
|
||||
with ThreadPoolExecutor(max_workers=10) as pool:
|
||||
futures = {pool.submit(resolve_main_contract, p): p for p in PRODUCTS}
|
||||
for fut in as_completed(futures):
|
||||
product = futures[fut]
|
||||
try:
|
||||
main = fut.result()
|
||||
if main:
|
||||
new_idx[product["sina"]] = _enrich_item(main)
|
||||
except Exception:
|
||||
pass
|
||||
with _main_index_lock:
|
||||
_main_index = new_idx
|
||||
_main_index_ts = time.time()
|
||||
|
||||
|
||||
def _warm_loop():
|
||||
@@ -399,15 +423,21 @@ def list_main_contracts_grouped() -> list[dict]:
|
||||
"""按交易所分类返回全部品种主力合约(行情页下拉用)。"""
|
||||
with _main_index_lock:
|
||||
index = dict(_main_index)
|
||||
index_ready = bool(index)
|
||||
|
||||
if len(index) < len(PRODUCTS) // 2:
|
||||
refresh_main_index()
|
||||
with _main_index_lock:
|
||||
index = dict(_main_index)
|
||||
|
||||
buckets: dict[str, list] = defaultdict(list)
|
||||
for p in PRODUCTS:
|
||||
main = index.get(p["sina"])
|
||||
if not main and not index_ready:
|
||||
main = _stub_main_contract(p)
|
||||
if not main:
|
||||
resolved = resolve_main_contract(p)
|
||||
if resolved:
|
||||
main = _enrich_item(resolved)
|
||||
if main:
|
||||
buckets[p["exchange"]].append(_enrich_item(main))
|
||||
buckets[p["exchange"]].append(main)
|
||||
|
||||
groups: list[dict] = []
|
||||
for cat in EXCHANGE_ORDER:
|
||||
|
||||
Reference in New Issue
Block a user