"""通达信数据源(mootdx TCP直连通达信服务器) 通过 TCP 协议直连通达信行情服务器,不走 HTTP,不会被限流。 主要用途: - K线数据:主数据源(稳定可靠) - 板块数据:东方财富 push2 的降级方案 """ import asyncio from typing import Optional, List def _get_market(code: str) -> str: return "sh" if code.startswith("6") else "sz" def _create_client(): from mootdx.quotes import Quotes return Quotes.factory(market="std", multithread=True, heartbeat=True) def _sync_fetch_kline(code: str, days: int) -> Optional[list]: client = _create_client() klines = client.bars(symbol=code, frequency=9, offset=min(days, 800)) if klines is None or len(klines) == 0: return None result = [] prev_close = 0.0 for bar in reversed(klines): close = float(bar.close) change_pct = 0 if prev_close > 0: change_pct = (close - prev_close) / prev_close * 100 result.append( { "date": ( bar.datetime.strftime("%Y-%m-%d") if hasattr(bar.datetime, "strftime") else str(bar.datetime)[:10] ), "open": float(bar.open), "close": close, "high": float(bar.high), "low": float(bar.low), "volume": int(bar.vol) if hasattr(bar, "vol") else 0, "turnover": ( float(bar.amount) if hasattr(bar, "amount") and bar.amount else 0 ), "changePercent": round(change_pct, 2), } ) prev_close = close result.sort(key=lambda x: x["date"]) return result async def fetch_kline_history(code: str, days: int = 90) -> Optional[List[dict]]: """获取日K线(TCP直连通达信,主数据源)""" try: loop = asyncio.get_event_loop() return await loop.run_in_executor(None, _sync_fetch_kline, code, days) except ImportError: return None except Exception as e: print(f"[mootdx] fetch_kline error: {e}") return None # ---- 板块数据(东方财富降级方案)---- def _sync_fetch_sectors(sector_type: str) -> Optional[list]: from mootdx.consts import MARKET_SH, MARKET_SZ client = _create_client() # block() 返回 DataFrame,列:code, name 等 # 按板块类型过滤 block_df = client.block() if block_df is None or block_df.empty: return None items = [] for _, row in block_df.iterrows(): name = str(row.get("name", "") or row.get("blockname", "")) code = str(row.get("code", "") or row.get("blockcode", "")) if not code or not name: continue items.append( { "code": code, "name": name, "level": None, "changePercent": None, "changeAmount": None, "mainNetInflow": 0, "mainNetInflowPercent": None, "superLargeInflow": None, "superLargeInflowPercent": None, "largeInflow": None, "largeInflowPercent": None, "mediumInflow": None, "mediumInflowPercent": None, "smallInflow": None, "smallInflowPercent": None, "turnover": 0, } ) return items if items else None async def fetch_sector_list(sector_type: str) -> Optional[List[dict]]: """获取板块列表(东方财富的降级方案,仅含代码和名称)""" try: loop = asyncio.get_event_loop() return await loop.run_in_executor(None, _sync_fetch_sectors, sector_type) except ImportError: return None except Exception as e: print(f"[mootdx] fetch_sectors error: {e}") return None