Files
qihuo/modules/market/kline_seed.py
T

138 lines
4.5 KiB
Python

# Copyright (c) 2025-2026 马建军. All rights reserved.
# 专有软件 — 未经授权禁止复制、传播、转售。
# 详见 LICENSE.zh-CN.txt
"""20 万以下四品种 K 线预下载(玉米、豆粕、甲醇、螺纹钢)。"""
from __future__ import annotations
import logging
import threading
import time
from typing import Optional
from modules.core.symbols import PRODUCTS, resolve_main_contract
from modules.market.kline_chart import fetch_sina_klines, ths_to_sina_chart_symbol
from modules.market.kline_store import (
connect_kline_db,
ensure_kline_tables,
load_meta,
save_bars,
)
from modules.trading.product_recommend import (
SMALL_ACCOUNT_PRODUCT_THS,
normalize_product_ths,
)
logger = logging.getLogger(__name__)
# 小账户默认预下载周期(行情页 + 关键位常用)
SMALL_ACCOUNT_KLINE_PERIODS = ("5m", "15m", "1h", "d")
MIN_SEED_BARS = 30
_SEED_LOCK = threading.Lock()
_SEED_STARTED = False
def _small_account_products() -> list[dict]:
allowed = {x.upper() for x in SMALL_ACCOUNT_PRODUCT_THS}
out: list[dict] = []
for p in PRODUCTS:
root = normalize_product_ths(p.get("ths") or "")
if root.upper() in allowed:
out.append(p)
return out
def _resolve_main_symbol(product: dict) -> Optional[str]:
try:
from modules.core.symbols import _main_index, _main_index_lock
with _main_index_lock:
cached = (_main_index or {}).get(product.get("sina") or "")
if cached and cached.get("ths_code"):
return str(cached["ths_code"])
except Exception:
pass
try:
main = resolve_main_contract(product)
if main and main.get("ths_code"):
return str(main["ths_code"])
except Exception as exc:
logger.debug("resolve main for seed %s: %s", product.get("ths"), exc)
try:
from modules.core.symbols import _stub_main_contract
stub = _stub_main_contract(product)
if stub and stub.get("ths_code"):
return str(stub["ths_code"])
except Exception:
pass
return None
def seed_small_account_klines(
*,
db_path: Optional[str] = None,
force: bool = False,
) -> dict[str, int]:
"""下载四品种主力合约 K 线到独立库;已存在且充足时跳过。"""
conn = connect_kline_db(db_path)
try:
ensure_kline_tables(conn)
saved: dict[str, int] = {}
for product in _small_account_products():
sym = _resolve_main_symbol(product)
if not sym:
continue
chart_sym = ths_to_sina_chart_symbol(sym)
if not chart_sym:
continue
for period in SMALL_ACCOUNT_KLINE_PERIODS:
key = f"{sym}:{period}"
meta = load_meta(conn, chart_sym, period)
if (
not force
and meta
and int(meta.get("bar_count") or 0) >= MIN_SEED_BARS
):
continue
try:
bars = fetch_sina_klines(sym, period) or []
except Exception as exc:
logger.warning("seed kline fetch failed %s %s: %s", sym, period, exc)
continue
if len(bars) < MIN_SEED_BARS:
logger.debug(
"seed kline too few bars %s %s: %d",
sym, period, len(bars),
)
continue
n = save_bars(conn, chart_sym, period, bars)
saved[key] = n
logger.info("seeded kline %s %s (%d bars)", sym, period, n)
return saved
finally:
conn.close()
def start_small_account_kline_seed(*, db_path: Optional[str] = None, delay_sec: float = 8.0) -> None:
"""后台预下载(仅执行一次)。"""
global _SEED_STARTED
with _SEED_LOCK:
if _SEED_STARTED:
return
_SEED_STARTED = True
def _run() -> None:
if delay_sec > 0:
time.sleep(delay_sec)
try:
saved = seed_small_account_klines(db_path=db_path)
if saved:
logger.info("small-account kline seed done: %s", ", ".join(saved.keys()))
else:
logger.debug("small-account kline seed: nothing new to download")
except Exception as exc:
logger.warning("small-account kline seed failed: %s", exc)
threading.Thread(target=_run, daemon=True, name="kline-seed").start()