import os import sqlite3 from datetime import datetime, timedelta import httpx DB_PATH = os.path.join(os.path.dirname(os.path.dirname(__file__)), "arb.db") BINANCE_FAPI = "https://fapi.binance.com/fapi/v1" SYMBOLS = ["BTCUSDT", "ETHUSDT"] DISCORD_TOKEN = os.getenv("DISCORD_BOT_TOKEN") if not DISCORD_TOKEN: raise RuntimeError("DISCORD_BOT_TOKEN 未设置,请从 GCP Secret Manager 注入") DISCORD_CHANNEL = os.getenv("DISCORD_SIGNAL_CHANNEL", "1472986545635197033") BINANCE_HEADERS = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"} def ensure_tables(conn): conn.execute( """ CREATE TABLE IF NOT EXISTS signal_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, symbol TEXT NOT NULL, rate REAL NOT NULL, annualized REAL NOT NULL, sent_at TEXT NOT NULL, message TEXT NOT NULL ) """ ) conn.commit() def annualized_from_7d(symbol: str) -> float: end_time = int(datetime.utcnow().timestamp() * 1000) start_time = int((datetime.utcnow() - timedelta(days=7)).timestamp() * 1000) with httpx.Client(timeout=20, headers=BINANCE_HEADERS) as client: r = client.get( f"{BINANCE_FAPI}/fundingRate", params={"symbol": symbol, "startTime": start_time, "endTime": end_time, "limit": 1000}, ) if r.status_code != 200: print(f"Binance error {r.status_code} for {symbol}, using fallback") # 直接用最新单条费率 r2 = client.get(f"{BINANCE_FAPI}/fundingRate", params={"symbol": symbol, "limit": 1}) if r2.status_code != 200: return 0.0 rates = [float(x["fundingRate"]) for x in r2.json()] else: rates = [float(x["fundingRate"]) for x in r.json()] if not rates: return 0.0 mean = sum(rates) / len(rates) return mean * 3 * 365 * 100 def send_discord(content: str): headers = { "Authorization": f"Bot {DISCORD_TOKEN}", "Content-Type": "application/json", } with httpx.Client(timeout=15) as client: resp = client.post( f"https://discord.com/api/v10/channels/{DISCORD_CHANNEL}/messages", headers=headers, json={"content": content}, ) if resp.status_code >= 300: raise RuntimeError(f"Discord send failed: {resp.status_code} {resp.text}") print(f"Discord sent OK: {resp.status_code}") def main(): btc = round(annualized_from_7d("BTCUSDT"), 2) eth = round(annualized_from_7d("ETHUSDT"), 2) target_symbol = None if btc > 10: target_symbol = "BTC" elif eth > 10: target_symbol = "ETH" now_utc8 = datetime.utcnow() now_str = now_utc8.strftime("%Y-%m-%d %H:%M UTC") if not target_symbol: print(f"No signal. BTC={btc}% ETH={eth}%") return msg = ( f"📊 **套利信号**\n" f"BTC 7日年化: **{btc}%**\n" f"ETH 7日年化: **{eth}%**\n" f"建议:{target_symbol} 现货+永续对冲可开仓\n" f"时间: {now_str}" ) print(msg) send_discord(msg) conn = sqlite3.connect(DB_PATH) ensure_tables(conn) conn.execute( "INSERT INTO signal_logs (symbol, rate, annualized, sent_at, message) VALUES (?, ?, ?, ?, ?)", (target_symbol, 0.0, btc if target_symbol == "BTC" else eth, datetime.utcnow().isoformat(), msg), ) conn.commit() conn.close() if __name__ == "__main__": main()