bsp_monitor: 支持全部20个币对 + 数据源切换至data_provider + Python 3.9兼容

- fetcher.py: 数据源从CCXT改为data_provider HTTP API,新增get_symbols()自动获取所有币对
- main.py: 重构为多币对架构,每个币对独立SymbolState(pivot_monitor/BSP去重/首轮抑制)
- engine.py: format_bsp_detail()支持动态币对名
- ChanPivotMonitor/Classifier: 修复Python 3.9类型注解兼容(X|None → Optional[X])
- 首轮初始化时不推送中枢和BSP,避免启动时20条消息轰炸

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jackyu66git
2026-05-26 11:55:23 +08:00
co-authored by Claude Opus 4.7
parent b1cbdca707
commit 881c9d5eac
5 changed files with 153 additions and 117 deletions
+24 -6
View File
@@ -2,23 +2,42 @@
fetcher.py - 从 data_provider HTTP API 拉取 K 线数据。
"""
from typing import List, Optional
import requests
import pandas as pd
import logging
logger = logging.getLogger(__name__)
SYMBOL = "BTC/USDT:USDT"
TIMEFRAME = "1m"
PROVIDER_URL = "http://103.179.242.166"
FETCH_LIMIT = 1000
_symbols_cache: Optional[List[str]] = None
def fetch_ohlcv() -> pd.DataFrame:
"""从 data_provider API 拉取最近 FETCH_LIMIT 根 1m K 线。"""
def get_symbols() -> list[str]:
"""从 data_provider /health 获取所有可用币对(缓存)。"""
global _symbols_cache
if _symbols_cache is not None:
return _symbols_cache
try:
resp = requests.get(f"{PROVIDER_URL}/health", timeout=10)
resp.raise_for_status()
_symbols_cache = resp.json().get("symbols", [])
logger.info(f"获取到 {len(_symbols_cache)} 个币对")
except Exception as e:
logger.error(f"获取币对列表失败: {e}")
_symbols_cache = ["BTC/USDT:USDT"]
return _symbols_cache
def fetch_ohlcv(symbol: str) -> pd.DataFrame:
"""从 data_provider API 拉取某个币对最近 FETCH_LIMIT 根 1m K 线。"""
url = f"{PROVIDER_URL}/api/candles"
params = {
"symbol": SYMBOL,
"symbol": symbol,
"tf": TIMEFRAME,
"limit": FETCH_LIMIT,
}
@@ -27,7 +46,7 @@ def fetch_ohlcv() -> pd.DataFrame:
data = resp.json()
if not data:
logger.warning("API 返回空数据")
logger.warning(f"{symbol}: API 返回空数据")
return pd.DataFrame()
df = pd.DataFrame(data)
@@ -35,5 +54,4 @@ def fetch_ohlcv() -> pd.DataFrame:
df["date"] = df["timestamp"]
df = df.drop_duplicates(subset="timestamp").sort_values("timestamp").reset_index(drop=True)
logger.info(f"拉取 {len(df)}{TIMEFRAME} K 线 from {PROVIDER_URL}")
return df