BTC 行情预警系统实战教程

本教程将围绕 两个完整可用的脚本 展开:

  • HTTP 脚本:通过REST API 定期查询 BTC 最新成交价,适合入门和低频监控

  • WebSocket 脚本:通过WebSocket 订阅实时行情,构建专业级技术指标预警(金叉 + 放量)

读完本文,你将能够:

  • 理解 Infoway HTTP 与 WebSocket 的使用场景差异

  • 独立运行并修改这两个脚本

  • 知道如何从“拿数据”升级到“做交易信号预警”

一、准备工作

在开始之前,请确保你已经具备以下环境:

  1. Python 3.8+

  2. 已安装依赖库:

pip install requests websocket-client schedule loguru
  1. Infoway API官网申请并获取 API Key

本文所有示例都以 BTCUSDT 为例,你可以很容易替换成其他交易对。

二、HTTP 版行情预警脚本(基础版)

import requests
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import time

# ================= Infoway API =================
API_URL = "https://data.infoway.io/crypto/batch_trade/BTCUSDT"
API_KEY = "yourInfowayApiKey"

# ================= 邮件设置 =================
SMTP_SERVER = "smtp.your-email-provider.com"
SMTP_PORT = 587
EMAIL_ADDRESS = "your_email@example.com"
EMAIL_PASSWORD = "your_password_here"
TO_EMAIL = "recipient@example.com"

# ================= 状态 =================
last_alert_price = None
THRESHOLD = 73000

# ================= 获取最新成交价 =================
def get_btc_price():
    headers = {
        "User-Agent": "Mozilla/5.0",
        "Accept": "application/json",
        "apiKey": API_KEY
    }

    try:
        response = requests.get(API_URL, headers=headers, timeout=10)
    except Exception as e:
        print("请求异常:", e)
        return None

    if response.status_code != 200:
        print("HTTP错误:", response.status_code, response.text)
        return None

    try:
        data = response.json()
    except Exception as e:
        print("JSON解析失败:", e)
        return None

    try:
        # ✅ 根据你给的返回示例
        return float(data["p"])
    except (KeyError, ValueError) as e:
        print("成交价解析失败:", e, data)
        return None

# ================= 发送邮件 =================
def send_email_notification(price):
    subject = "比特币价格提醒"
    body = f"比特币最新成交价:${price},已超过阈值 {THRESHOLD}"

    msg = MIMEMultipart()
    msg["From"] = EMAIL_ADDRESS
    msg["To"] = TO_EMAIL
    msg["Subject"] = subject
    msg.attach(MIMEText(body, "plain"))

    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT) as server:
            server.starttls()
            server.login(EMAIL_ADDRESS, EMAIL_PASSWORD)
            server.sendmail(EMAIL_ADDRESS, TO_EMAIL, msg.as_string())
        print("📧 邮件已发送")
    except Exception as e:
        print("邮件发送失败:", e)

# ================= 主逻辑 =================
def main():
    global last_alert_price

    price = get_btc_price()
    if price is None:
        return

    print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] 最新成交价: ${price}")

    # 超过阈值且避免重复报警
    if price > THRESHOLD and price != last_alert_price:
        last_alert_price = price
        send_email_notification(price)

if __name__ == "__main__":
    main()

2.1 适合什么场景?

HTTP 轮询方式适合:

  • 新手快速上手

  • 低频价格提醒(例如价格突破某个整数位)

  • 不需要毫秒级实时性

它的特点是:简单、稳定、易调试

2.2 使用的 Infoway 接口

GET https://data.infoway.io/crypto/batch_trade/BTCUSDT

该接口返回的是 最新一笔成交信息,示例:

{
  "s": "BTCUSDT",
  "t": 1752947397177,
  "p": "117783.22",
  "v": "0.00048"
}

我们真正关心的字段只有一个:

  • p → 最新成交价

2.3 HTTP 脚本核心逻辑拆解

第一步:请求最新成交价
price = float(data["p"])

这一步的目标只有一个:拿到当前市场成交价

第二步:设定预警条件
THRESHOLD = 73000
if price > THRESHOLD:
    send_email_notification(price)

这就是一个最简单、也是最常见的行情预警模型:

当价格突破某个关键位置时提醒我

第三步:避免重复报警(非常重要)
last_alert_price

如果不做控制,价格在阈值之上会反复触发报警。

解决思路:

  • 只在 第一次突破时 报警

  • 或者增加冷却时间(cooldown)

2.4 HTTP 方案的优缺点总结

优点

  • 逻辑简单

  • 稳定可靠

  • 非常适合新手

缺点

  • 有延迟(取决于轮询间隔)

  • 不适合技术指标计算

当你开始关心:

“是不是刚刚金叉了?”

HTTP 就不够用了,这时就该进入 WebSocket 世界。

三、WebSocket 版行情预警脚本(进阶版)

import json
import time
import schedule
import threading
import websocket
from loguru import logger
from collections import deque

class WebsocketExample:
    def __init__(self):
        self.session = None
        self.ws_url = "wss://data.infoway.io/ws?business=crypto&apikey=yourApikey"
        self.is_ws_connected = False

        # ====== K线缓存 ======
        self.closes = deque(maxlen=30)    # close价
        self.volumes = deque(maxlen=30)   # 成交量

        self.prev_ma5 = None
        self.prev_ma20 = None

    # ================= 连接相关 =================
    def connect_all(self):
        self.connect(self.ws_url)
        self.start_reconnection(self.ws_url)

    def start_reconnection(self, url):
        def check():
            if not self.is_connected():
                self.connect(url)

        schedule.every(10).seconds.do(check)
        threading.Thread(target=self._run_schedule, daemon=True).start()

    def _run_schedule(self):
        while True:
            schedule.run_pending()
            time.sleep(1)

    def is_connected(self):
        return self.session and self.is_ws_connected

    def connect(self, url):
        self.session = websocket.WebSocketApp(
            url,
            on_open=self.on_open,
            on_message=self.on_message,
            on_error=self.on_error,
            on_close=self.on_close
        )
        threading.Thread(target=self.session.run_forever, daemon=True).start()

    # ================= WebSocket 回调 =================
    def on_open(self, ws):
        logger.info("WebSocket connected")
        self.is_ws_connected = True

        # 最新成交
        self.send_message({
            "code": 10000,
            "trace": "trade",
            "data": {"codes": "BTCUSDT"}
        })

        time.sleep(3)

        # 盘口
        self.send_message({
            "code": 10003,
            "trace": "depth",
            "data": {"codes": "BTCUSDT"}
        })

        time.sleep(3)

        # 1分钟K线
        self.send_message({
            "code": 10006,
            "trace": "kline",
            "data": {
                "arr": [{"type": 1, "codes": "BTCUSDT"}]
            }
        })

        schedule.every(30).seconds.do(self.ping)

    def on_message(self, ws, message):
        try:
            data = json.loads(message)
        except:
            return

        # ====== 只处理K线数据 ======
        if data.get("code") == 10006:
            self.handle_kline(data)

    def on_close(self, ws, *args):
        self.is_ws_connected = False
        logger.warning("WebSocket closed")

    def on_error(self, ws, error):
        self.is_ws_connected = False
        logger.error(f"WebSocket error: {error}")

    # ================= K线处理 & 预警 =================
    def handle_kline(self, msg):
        """
        假设 data 中包含:
        close: 收盘价
        volume: 成交量
        """
        k = msg.get("data", {})
        close_price = float(k.get("c", 0))
        volume = float(k.get("v", 0))

        if close_price == 0:
            return

        self.closes.append(close_price)
        self.volumes.append(volume)

        if len(self.closes) < 20:
            return

        ma5 = sum(list(self.closes)[-5:]) / 5
        ma20 = sum(list(self.closes)[-20:]) / 20
        avg_volume = sum(self.volumes) / len(self.volumes)

        # ====== 金叉判断 ======
        golden_cross = (
            self.prev_ma5 is not None and
            self.prev_ma20 is not None and
            self.prev_ma5 <= self.prev_ma20 and
            ma5 > ma20
        )

        # ====== 成交量激增 ======
        volume_spike = volume > avg_volume * 2

        if golden_cross and volume_spike:
            self.alert(close_price, volume, ma5, ma20)

        self.prev_ma5 = ma5
        self.prev_ma20 = ma20

    # ================= 预警动作 =================
    def alert(self, price, volume, ma5, ma20):
        logger.warning(
            f"🚨 BTC 金叉 + 放量!\n"
            f"价格: {price}\n"
            f"MA5: {ma5:.2f}, MA20: {ma20:.2f}\n"
            f"成交量: {volume}"
        )

        # 👉 这里可以接:
        # send_email()
        # send_telegram()
        # send_webhook()

    # ================= 工具 =================
    def send_message(self, obj):
        if self.is_connected():
            self.session.send(json.dumps(obj))

    def ping(self):
        self.send_message({"code": 10010, "trace": "ping"})


# ================= 启动 =================
if __name__ == "__main__":
    ws = WebsocketExample()
    ws.connect_all()

    while True:
        schedule.run_pending()
        time.sleep(1)

3.1 为什么要用 WebSocket?

WebSocket 是 交易系统的标配

  • 实时推送

  • 无需频繁请求

  • 可以持续接收 K 线、成交、盘口

一句话总结:

只要你开始做策略,就一定要用 WebSocket

3.2 本脚本订阅了哪些数据?

在 WebSocket 连接建立后,我们订阅了三类数据:

  1. 最新成交价(trade)

  2. 盘口数据(depth)

  3. 1 分钟 K 线(kline) ← 本文预警逻辑的核心

3.3 金叉预警逻辑详解

什么是金叉?

金叉是最经典的趋势信号之一:

  • 短期均线向上突破长期均线

在脚本中我们定义为:

  • MA5(5 根 K 线均价)

  • MA20(20 根 K 线均价)

上一根K线:MA5 <= MA20
当前K线:MA5 >  MA20

这样可以有效避免“假突破”。

3.4 为什么还要加「成交量激增」?

没有成交量配合的金叉,可信度非常低。

因此我们增加第二个过滤条件:

当前成交量 > 最近 20 根 K 线平均成交量 × 2

这意味着:

市场不是“慢慢飘过去的”,而是有真实资金推动

3.5 最终触发条件(重点)

金叉成立 AND 成交量激增

只有当 趋势 + 资金 同时出现时,才发送预警。

这已经是一个实战级信号了。

3.6 WebSocket 脚本结构讲解

1️⃣ K 线缓存
self.closes = deque(maxlen=30)
self.volumes = deque(maxlen=30)

用于计算均线和平均成交量。

2️⃣ 均线计算
ma5 = sum(closes[-5:]) / 5
ma20 = sum(closes[-20:]) / 20
3️⃣ 预警触发
if golden_cross and volume_spike:
    self.alert(...)

在这里,你可以自由扩展:

  • 发邮件

  • 发 Telegram

  • 调用 Webhook

四、HTTP vs WebSocket:该怎么选?

场景推荐方式
价格突破提醒HTTP
技术指标预警WebSocket
实时交易策略WebSocket
新手入门HTTP

建议路径

HTTP 入门 → WebSocket 进阶 → 策略组合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值