ChanPivotMonitor: 实时中枢特征跟踪 + Telegram推送

- ChanPivotClassifier: 提取 calc_duration/contraction/shift 为 @staticmethod,新增 compute_features()
- ChanPivotMonitor: 实时追踪当前中枢,bi_count 增长时重新计算 shift/contraction/duration
- bsp_monitor/fetcher: 改用 data_provider HTTP API 替代直连 CCXT
- bsp_monitor/notify: 新增 send_telegram_message() 通用推送
- bsp_monitor/main: 集成 ChanPivotMonitor,有新笔或 BSP 时推送到 Telegram

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
jackyu66git
2026-05-26 11:35:09 +08:00
co-authored by Claude Opus 4.7
parent 5ad761fad4
commit b1cbdca707
5 changed files with 300 additions and 40 deletions
+20 -26
View File
@@ -1,45 +1,39 @@
"""
fetcher.py - CCXT REST 拉取 Binance 永续合约 1m K 线,从固定起点累积
fetcher.py - 从 data_provider HTTP API 拉取 K 线数据
"""
import ccxt
import requests
import pandas as pd
import logging
from datetime import datetime, timezone
logger = logging.getLogger(__name__)
SYMBOL = "BTC/USDT:USDT"
TIMEFRAME = "1m"
# 每次拉取最近 LIMIT 根 K 线(Binance 上限 1500,足够缠论管线用 ~25h 数据)
_FETCH_LIMIT = 1000
_exchange = None
def _get_exchange():
global _exchange
if _exchange is None:
_exchange = ccxt.binance({
"enableRateLimit": True,
"options": {"defaultType": "future"},
})
_exchange.load_markets()
logger.info("ccxt binance 已初始化")
return _exchange
PROVIDER_URL = "http://103.179.242.166"
FETCH_LIMIT = 1000
def fetch_ohlcv() -> pd.DataFrame:
"""拉取最近 _FETCH_LIMIT 根 1m K 线。
"""从 data_provider API 拉取最近 FETCH_LIMIT 根 1m K 线。"""
url = f"{PROVIDER_URL}/api/candles"
params = {
"symbol": SYMBOL,
"tf": TIMEFRAME,
"limit": FETCH_LIMIT,
}
resp = requests.get(url, params=params, timeout=30)
resp.raise_for_status()
data = resp.json()
管线每次重跑最新的 K 线窗口。
用 limit 而非 since 避免 API 500 根限制截断新数据
"""
exchange = _get_exchange()
raw = exchange.fetch_ohlcv(SYMBOL, TIMEFRAME, limit=_FETCH_LIMIT)
if not data:
logger.warning("API 返回空数据")
return pd.DataFrame()
df = pd.DataFrame(raw, columns=["timestamp", "open", "high", "low", "close", "volume"])
df = pd.DataFrame(data)
df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms", utc=True)
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