Files
crpto_predictor/scripts/hourly_fetch_btc_1h.py
T

113 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""整点后5秒抓取 BTC 1h K线(至少512根,且包含最新时间)"""
import os
import sys
import time
from datetime import datetime, timedelta
import pandas as pd
import pytz
# 直接运行脚本时,确保可以从项目根目录导入 data 包
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from data.fetcher import BinanceDataFetcher
def get_next_run_time(delay_seconds: int = 5) -> datetime:
"""计算下一次执行时间:下一个整点 + delay_seconds。"""
now_utc = datetime.now(pytz.UTC)
next_hour = (now_utc + timedelta(hours=1)).replace(minute=0, second=0, microsecond=0)
return next_hour + timedelta(seconds=delay_seconds)
def fetch_min_klines_with_latest(
fetcher: BinanceDataFetcher,
min_klines: int = 512,
latest_check_retries: int = 8,
latest_check_interval: int = 2,
) -> pd.DataFrame:
"""
拉取 BTC 1h K线,确保:
1) 数量不少于 min_klines
2) 包含当前小时的最新时间(UTC 整点)
"""
now_utc = datetime.now(pytz.UTC)
expected_latest_ts = now_utc.replace(minute=0, second=0, microsecond=0).replace(tzinfo=None)
lookback_days = 35
for attempt in range(latest_check_retries):
start_date = (datetime.now(pytz.UTC) - timedelta(days=lookback_days)).strftime("%Y-%m-%d")
# 使用 WebSocket 拉取,避免仅命中缓存导致最新K线不更新。
df = fetcher.fetch_ohlcv(
symbol="BTC",
timeframe="1h",
start_date=start_date,
end_date=None,
use_cache=False,
max_retries=3,
)
if len(df) < min_klines:
lookback_days += 7
print(
f"[WARN] 当前仅获取 {len(df)} 根K线,小于 {min_klines},扩大回看窗口到 {lookback_days} 天后重试。"
)
continue
latest_ts = df["timestamp"].max().to_pydatetime().replace(tzinfo=None)
if latest_ts >= expected_latest_ts:
result = df.tail(min_klines).copy().reset_index(drop=True)
print(
f"[OK] 获取成功: {len(result)} 根 | 最新时间: {latest_ts} UTC | 期望最新时间: {expected_latest_ts} UTC"
)
return result
if attempt < latest_check_retries - 1:
print(
f"[WAIT] 最新K线未到位,当前最新: {latest_ts} UTC,期望: {expected_latest_ts} UTC,"
f"{latest_check_interval}秒后重试({attempt + 1}/{latest_check_retries})。"
)
time.sleep(latest_check_interval)
raise RuntimeError("多次重试后仍未拿到包含最新时间的BTC 1h K线数据")
def run_once(min_klines: int = 512) -> None:
"""执行一次抓取任务。"""
fetcher = BinanceDataFetcher()
data = fetch_min_klines_with_latest(fetcher=fetcher, min_klines=min_klines)
print("-" * 80)
print(f"抓取完成,返回K线数量: {len(data)}")
print(f"时间范围: {data['timestamp'].min()} -> {data['timestamp'].max()} (UTC)")
print(data.tail(3)[["timestamp", "open", "high", "low", "close", "volume"]])
print("-" * 80)
def run_forever(min_klines: int = 512, delay_seconds: int = 5) -> None:
"""常驻运行:每个整点延迟 delay_seconds 后执行抓取。"""
while True:
next_run = get_next_run_time(delay_seconds=delay_seconds)
sleep_seconds = max(0.0, (next_run - datetime.now(pytz.UTC)).total_seconds())
print(f"[SCHEDULE] 当前UTC: {datetime.now(pytz.UTC)},下次执行: {next_run},等待 {sleep_seconds:.1f} 秒")
time.sleep(sleep_seconds)
try:
run_once(min_klines=min_klines)
except Exception as exc:
print(f"[ERROR] 本轮抓取失败: {exc}")
def main() -> None:
"""脚本入口。"""
# 默认常驻运行,如需只执行一次,可把这里改成 run_once(min_klines=512)
run_forever(min_klines=512, delay_seconds=5)
if __name__ == "__main__":
main()