[{"content":"如果你尝试过用传统方式学 WiFi 攻击,流程通常是这样:订一块支持 monitor 模式和包注入的 USB 无线网卡,跟 Linux 驱动斗一晚上,搭一个自己拥有的测试热点,然后才开始练你真正想学的攻击。WiFi-Forge 把这一整套准备工作直接跳过。\nWiFi-Forge 是 Black Hills InfoSec 开源的项目,提供一个安全合法的环境给你练习无线攻击。不需要硬件,不会误伤别人的网络,也不会把驱动搞坏。开一个虚拟实验室,你就可以开始动手。\n为什么需要它 —— WiFi 学习的三个坑 传统 WiFi 实战教学有三件事最劝退人:\n硬件不可控。 不是每张 USB 网卡都干净支持 monitor + injection。靠谱的几款(Alfa AWUS036、Panda PAU09 等)要 30~60 美金,而且一次只能用一张。 法律灰区。 在大多数国家,接触任何不属于你的网络 —— 哪怕只是被动监听 —— 都是违法行为。\u0026ldquo;我只是嗅探一下\u0026rdquo; 不是抗辩理由。 环境难复位。 真实硬件不能一键回退。搞砸了的配置没办法 git checkout 回去。 WiFi-Forge 把这三个问题全部合并到笔记本上的一个沙盒里。\n底层是什么 WiFi-Forge 建在 mininet-wifi 之上 —— 一个 802.11 网络模拟器,在 Linux 网络命名空间里创建虚拟接入点、客户端和\u0026quot;无线电波\u0026quot;。每个 AP 和客户端都是真实的 Linux 进程,你可以用 iwconfig、airodump-ng、tcpdump,甚至 Reaver、Hashcat 直接打模拟流量,所有标准工具的行为跟在真实电波上完全一样。\nWiFi-Forge 在这个基础上加了:预设好的拓扑、开箱即用的攻击场景、以及一套引导式结构,让你不用每次想练点东西都先去设计一个网络。\n你能练什么 自带的实验室覆盖了常见的 WiFi 攻击类别:\nWPA/WPA2 握手包抓取 —— deauth 一个客户端,抓 4 路握手,用 hashcat 或 aircrack-ng 离线爆破 WPS 攻击 —— Reaver PIN 暴力破解、Pixie-Dust 攻击 Evil-twin / Karma —— 起一个伪装目标 SSID 的钓鱼 AP,看客户端自动连过来 Deauth 洪泛 —— 把客户端从合法 AP 上踢下来 Beacon 洪泛 —— 喷上千个假 AP,把扫描工具搞崩 MAC 随机化分析 —— 看看现代设备怎么藏自己身份(以及它们在哪些地方会泄露) PMKID 攻击 —— 不需要客户端连着也能抓握手 每个实验室会启动对应的拓扑,把你扔进 shell,给你一个小型 CTF 风格的目标。\n怎么开始 git clone https://github.com/blackhillsinfosec/WifiForge cd WifiForge sudo ./install.sh sudo python3 wififorge.py 需要 Linux 环境(Ubuntu/Debian 最稳)、Python 3、root 权限(mininet-wifi 要用内核功能)。安装脚本会自动处理依赖 —— mininet-wifi、aircrack-ng、hashcat、reaver 等。\n适合谁用 OSCP / OSWP 备考者 —— 可以练跟考试实验室相似的场景,不用买硬件 CTF 出题人 —— 快速搭起无线挑战,不用配真实电台 安全讲师 —— 每个学生都有独立沙盒,几秒就能复位 好奇的开发者 —— 终于能用调试器一步步看清 4 路握手到底长什么样 ⚠️ WiFi-Forge 学不到什么: 物理层 —— RF、天线选择、真实信号衰减。这部分早晚还是要真卡。但 90% 的实战攻击都发生在协议层 —— 这一层的模拟和真实流量完全无差。\n关于合法性和职业操守 这种项目必须把话讲在前面:这些技术只能对你自己拥有的网络、或者拿到书面授权许可的网络使用。 WiFi-Forge 之所以存在,就是因为有了模拟环境就没必要去\u0026quot;试一下\u0026quot;咖啡馆隔壁的 WiFi。安全地学习,这才是它的全部意义。\n仓库: github.com/blackhillsinfosec/WifiForge 底层: mininet-wifi 维护者: Black Hills InfoSec ","permalink":"https://dibi8.com/zh/posts/wifi-forge-%E4%B8%80%E4%B8%AA%E5%AE%89%E5%85%A8%E5%90%88%E6%B3%95%E7%9A%84-wifi-%E9%BB%91%E5%AE%A2%E5%AD%A6%E4%B9%A0%E6%B2%99%E7%9B%92/","summary":"\u003cp\u003e如果你尝试过用传统方式学 WiFi 攻击,流程通常是这样:订一块支持 monitor 模式和包注入的 USB 无线网卡,跟 Linux 驱动斗一晚上,搭一个自己拥有的测试热点,\u003cem\u003e然后\u003c/em\u003e才开始练你真正想学的攻击。\u003cstrong\u003eWiFi-Forge\u003c/strong\u003e 把这一整套准备工作直接跳过。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"WiFi-Forge 横幅\" loading=\"lazy\" src=\"https://github.com/her3ticAVI/MiniNet-framework/raw/main/images/WifiForgeVersion2.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/blackhillsinfosec/WifiForge\"\u003eWiFi-Forge\u003c/a\u003e 是 \u003ca href=\"https://www.blackhillsinfosec.com/\"\u003eBlack Hills InfoSec\u003c/a\u003e 开源的项目,提供一个\u003cstrong\u003e安全合法\u003c/strong\u003e的环境给你练习无线攻击。不需要硬件,不会误伤别人的网络,也不会把驱动搞坏。开一个虚拟实验室,你就可以开始动手。\u003c/p\u003e\n\u003ch2 id=\"为什么需要它--wifi-学习的三个坑\"\u003e为什么需要它 —— WiFi 学习的三个坑\u003c/h2\u003e\n\u003cp\u003e传统 WiFi 实战教学有三件事最劝退人:\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e硬件不可控。\u003c/strong\u003e 不是每张 USB 网卡都干净支持 monitor + injection。靠谱的几款(Alfa AWUS036、Panda PAU09 等)要 30~60 美金,而且一次只能用一张。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e法律灰区。\u003c/strong\u003e 在大多数国家,接触任何不属于你的网络 —— 哪怕只是被动监听 —— 都是违法行为。\u0026ldquo;我只是嗅探一下\u0026rdquo; 不是抗辩理由。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e环境难复位。\u003c/strong\u003e 真实硬件不能一键回退。搞砸了的配置没办法 \u003ccode\u003egit checkout\u003c/code\u003e 回去。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003eWiFi-Forge 把这三个问题全部合并到笔记本上的一个沙盒里。\u003c/p\u003e\n\u003ch2 id=\"底层是什么\"\u003e底层是什么\u003c/h2\u003e\n\u003cp\u003eWiFi-Forge 建在 \u003ca href=\"https://github.com/intrig-unicamp/mininet-wifi\"\u003emininet-wifi\u003c/a\u003e 之上 —— 一个 802.11 网络模拟器,在 Linux 网络命名空间里创建虚拟接入点、客户端和\u0026quot;无线电波\u0026quot;。每个 AP 和客户端都是真实的 Linux 进程,你可以用 \u003ccode\u003eiwconfig\u003c/code\u003e、\u003ccode\u003eairodump-ng\u003c/code\u003e、\u003ccode\u003etcpdump\u003c/code\u003e,甚至 Reaver、Hashcat 直接打模拟流量,所有标准工具的行为跟在真实电波上完全一样。\u003c/p\u003e\n\u003cp\u003eWiFi-Forge 在这个基础上加了:预设好的拓扑、开箱即用的攻击场景、以及一套引导式结构,让你不用每次想练点东西都先去设计一个网络。\u003c/p\u003e\n\u003ch2 id=\"你能练什么\"\u003e你能练什么\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"WiFi-Forge 运行截图\" loading=\"lazy\" src=\"https://github.com/her3ticAVI/MiniNet-framework/raw/main/images/wififorge-running.png\"\u003e\u003c/p\u003e","title":"WiFi-Forge — 一个安全合法的 WiFi 黑客学习沙盒"},{"content":"本文把 Code Vault 当前发布的 7 个代码片段做一次完整梳理 —— 这是一个个人代码仓,内容覆盖加密交易雷达、自主交易系统、安全工具,全部纯 Python,零或接近零的 API 成本。每个片段下方都附了完整源码,可以直接读、Fork、本地跑。右侧目录可以跳转到具体工具。\n⚠️ 风险提示 —— 这些工具直接对接行情和链上数据,部分会通过 Telegram 实时推送告警,其中\u0026quot;AI 自主交易\u0026quot;会在币安合约上开虚拟仓。请仔细阅读每个工具下方的说明。使用风险自担,作者不对交易结果做任何担保。\n交易雷达 V神卖币雷达 发布日期: 2026.05.02　标签: Python · WebSocket · Ethereum · Telegram · Event-Driven\nGitHub: vitalik-sell-radar\nWebSocket事件驱动 · V神钱包卖出检测 · 秒级TG推送\n通过WebSocket事件订阅实时监控V神钱包(vitalik.eth)的ERC-20代币卖出行为 — 零轮询，亚秒级延迟。自动分类接收方：DEX Router（Uniswap/1inch/SushiSwap）、CEX热钱包（币安/Coinbase/Kraken）、LP池。DexScreener实时查价。多RPC故障切换+自动重连。纯Python零成本，使用免费公共RPC节点。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; Vitalik Sell Radar — Event-Driven Edition WebSocket subscription to ERC-20 Transfer events, real-time detection of Vitalik\u0026#39;s sell activity, instant push to Telegram. Architecture: 1. WebSocket subscribes to Transfer(from=vitalik) events → sub-second detection 2. Classifies sell behavior (transfers to DEX Router / CEX / LP Pool) 3. Queries token info + price via DexScreener 4. Pushes alert to Telegram 5. Auto-reconnect + multi-RPC failover \u0026#34;\u0026#34;\u0026#34; import asyncio import json import logging import os import signal import sys import time from datetime import datetime, timezone from pathlib import Path import aiohttp import websockets # Load .env file def load_env(): env_path = Path(__file__).parent / \u0026#34;.env\u0026#34; if env_path.exists(): for line in env_path.read_text().splitlines(): line = line.strip() if line and not line.startswith(\u0026#34;#\u0026#34;) and \u0026#34;=\u0026#34; in line: k, v = line.split(\u0026#34;=\u0026#34;, 1) os.environ.setdefault(k.strip(), v.strip()) load_env() # ============================================================ # Configuration # ============================================================ # Vitalik\u0026#39;s main wallet VITALIK_ADDRESS = \u0026#34;0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\u0026#34; VITALIK_PADDED = \u0026#34;0x\u0026#34; + VITALIK_ADDRESS[2:].lower().zfill(64) # ERC-20 Transfer event topic TRANSFER_TOPIC = \u0026#34;0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\u0026#34; # WebSocket RPC endpoints (free, support eth_subscribe) WS_ENDPOINTS = [ \u0026#34;wss://ethereum-rpc.publicnode.com\u0026#34;, \u0026#34;wss://eth.drpc.org\u0026#34;, \u0026#34;wss://ethereum.publicnode.com\u0026#34;, ] # HTTP RPC for querying token info HTTP_RPC = os.environ.get(\u0026#34;HTTP_RPC\u0026#34;, \u0026#34;https://eth.drpc.org\u0026#34;) # Telegram TG_BOT_TOKEN = os.environ.get(\u0026#34;TG_BOT_TOKEN\u0026#34;, \u0026#34;\u0026#34;) TG_CHAT_ID = os.environ.get(\u0026#34;TG_CHAT_ID\u0026#34;, \u0026#34;\u0026#34;) # Known DEX Router addresses (sell destinations) KNOWN_DEX_ROUTERS = { # Uniswap \u0026#34;0x7a250d5630b4cf539739df2c5dacb4c659f2488d\u0026#34;: \u0026#34;Uniswap V2 Router\u0026#34;, \u0026#34;0xe592427a0aece92de3edee1f18e0157c05861564\u0026#34;: \u0026#34;Uniswap V3 Router\u0026#34;, \u0026#34;0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45\u0026#34;: \u0026#34;Uniswap V3 Router2\u0026#34;, \u0026#34;0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad\u0026#34;: \u0026#34;Uniswap Universal Router\u0026#34;, \u0026#34;0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b\u0026#34;: \u0026#34;Uniswap Universal Router (old)\u0026#34;, # 1inch \u0026#34;0x1111111254eeb25477b68fb85ed929f73a960582\u0026#34;: \u0026#34;1inch V5\u0026#34;, \u0026#34;0x111111125421ca6dc452d289314280a0f8842a65\u0026#34;: \u0026#34;1inch V6\u0026#34;, # SushiSwap \u0026#34;0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f\u0026#34;: \u0026#34;SushiSwap Router\u0026#34;, # CoW Protocol \u0026#34;0x9008d19f58aabd9ed0d60971565aa8510560ab41\u0026#34;: \u0026#34;CoW Settlement\u0026#34;, # 0x \u0026#34;0xdef1c0ded9bec7f1a1670819833240f027b25eff\u0026#34;: \u0026#34;0x Exchange Proxy\u0026#34;, # Curve \u0026#34;0x99a58482bd75cbab83b27ec03ca68ff489b5788f\u0026#34;: \u0026#34;Curve Router\u0026#34;, } # Known CEX hot wallets (partial list) KNOWN_CEX = { \u0026#34;0x28c6c06298d514db089934071355e5743bf21d60\u0026#34;: \u0026#34;Binance Hot Wallet\u0026#34;, \u0026#34;0x21a31ee1afc51d94c2efccaa2092ad1028285549\u0026#34;: \u0026#34;Binance Hot Wallet 2\u0026#34;, \u0026#34;0xdfd5293d8e347dfe59e90efd55b2956a1343963d\u0026#34;: \u0026#34;Binance Hot Wallet 3\u0026#34;, \u0026#34;0x56eddb7aa87536c09ccc2793473599fd21a8b17f\u0026#34;: \u0026#34;Binance Hot Wallet 4\u0026#34;, \u0026#34;0x71660c4005ba85c37ccec55d0c4493e66fe775d3\u0026#34;: \u0026#34;Coinbase\u0026#34;, \u0026#34;0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43\u0026#34;: \u0026#34;Coinbase 10\u0026#34;, \u0026#34;0x503828976d22510aad0201ac7ec88293211d23da\u0026#34;: \u0026#34;Coinbase 2\u0026#34;, \u0026#34;0x2faf487a4414fe77e2327f0bf4ae2a264a776ad2\u0026#34;: \u0026#34;FTX (defunct)\u0026#34;, \u0026#34;0x267be1c1d684f78cb4f6a176c4911b741e4ffdc0\u0026#34;: \u0026#34;Kraken\u0026#34;, \u0026#34;0xae2d4617c862309a3d75a0ffb358c7a5009c673f\u0026#34;: \u0026#34;Kraken 10\u0026#34;, } # Minimum notification amount (USD), 0 = notify all MIN_NOTIFY_USD = int(os.environ.get(\u0026#34;MIN_NOTIFY_USD\u0026#34;, \u0026#34;0\u0026#34;)) # Reconnect settings RECONNECT_DELAY = 5 MAX_RECONNECT_DELAY = 60 # ============================================================ # Logging # ============================================================ logging.basicConfig( level=logging.INFO, format=\u0026#34;%(asctime)s [%(levelname)s] %(message)s\u0026#34;, datefmt=\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;, ) log = logging.getLogger(\u0026#34;vitalik-radar\u0026#34;) # ============================================================ # Global state # ============================================================ # Dedup set for processed tx hashes (last 1000) seen_txs: set = set() seen_txs_list: list = [] # HTTP session http_session: aiohttp.ClientSession | None = None # Token info cache: {address: {symbol, name, decimals}} token_cache: dict = {} # Running flag running = True # ============================================================ # Utility functions # ============================================================ def shorten_addr(addr: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;Shorten address for display\u0026#34;\u0026#34;\u0026#34; return f\u0026#34;{addr[:6]}...{addr[-4:]}\u0026#34; def decode_transfer_value(data_hex: str, decimals: int) -\u0026gt; float: \u0026#34;\u0026#34;\u0026#34;Decode Transfer event value\u0026#34;\u0026#34;\u0026#34; try: raw = int(data_hex, 16) return raw / (10 ** decimals) except: return 0.0 def topic_to_address(topic: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;Extract 20-byte address from 32-byte topic\u0026#34;\u0026#34;\u0026#34; return \u0026#34;0x\u0026#34; + topic[-40:] def classify_recipient(to_addr: str) -\u0026gt; tuple[str, str]: \u0026#34;\u0026#34;\u0026#34; Classify recipient address. Returns (type, name) Type: \u0026#34;dex\u0026#34; / \u0026#34;cex\u0026#34; / \u0026#34;pool\u0026#34; / \u0026#34;unknown\u0026#34; \u0026#34;\u0026#34;\u0026#34; to_lower = to_addr.lower() if to_lower in KNOWN_DEX_ROUTERS: return \u0026#34;dex\u0026#34;, KNOWN_DEX_ROUTERS[to_lower] if to_lower in KNOWN_CEX: return \u0026#34;cex\u0026#34;, KNOWN_CEX[to_lower] return \u0026#34;unknown\u0026#34;, \u0026#34;\u0026#34; async def get_http_session() -\u0026gt; aiohttp.ClientSession: global http_session if http_session is None or http_session.closed: http_session = aiohttp.ClientSession() return http_session async def rpc_call(method: str, params: list) -\u0026gt; dict | None: \u0026#34;\u0026#34;\u0026#34;HTTP JSON-RPC call\u0026#34;\u0026#34;\u0026#34; session = await get_http_session() try: async with session.post( HTTP_RPC, json={\u0026#34;jsonrpc\u0026#34;: \u0026#34;2.0\u0026#34;, \u0026#34;id\u0026#34;: 1, \u0026#34;method\u0026#34;: method, \u0026#34;params\u0026#34;: params}, timeout=aiohttp.ClientTimeout(total=10), ) as resp: data = await resp.json() return data.get(\u0026#34;result\u0026#34;) except Exception as e: log.error(f\u0026#34;RPC call {method} failed: {e}\u0026#34;) return None async def get_token_info(token_addr: str) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34;Query ERC-20 token info (symbol, name, decimals)\u0026#34;\u0026#34;\u0026#34; addr_lower = token_addr.lower() if addr_lower in token_cache: return token_cache[addr_lower] info = {\u0026#34;symbol\u0026#34;: \u0026#34;???\u0026#34;, \u0026#34;name\u0026#34;: \u0026#34;Unknown\u0026#34;, \u0026#34;decimals\u0026#34;: 18, \u0026#34;address\u0026#34;: token_addr} # symbol() result = await rpc_call(\u0026#34;eth_call\u0026#34;, [ {\u0026#34;to\u0026#34;: token_addr, \u0026#34;data\u0026#34;: \u0026#34;0x95d89b41\u0026#34;}, \u0026#34;latest\u0026#34; ]) if result and len(result) \u0026gt; 2: try: hex_str = result[2:] if len(hex_str) \u0026gt;= 128: offset = int(hex_str[:64], 16) * 2 length = int(hex_str[offset:offset+64], 16) symbol_hex = hex_str[offset+64:offset+64+length*2] info[\u0026#34;symbol\u0026#34;] = bytes.fromhex(symbol_hex).decode(\u0026#34;utf-8\u0026#34;, errors=\u0026#34;replace\u0026#34;).strip(\u0026#39;\\x00\u0026#39;) elif len(hex_str) == 64: info[\u0026#34;symbol\u0026#34;] = bytes.fromhex(hex_str).decode(\u0026#34;utf-8\u0026#34;, errors=\u0026#34;replace\u0026#34;).strip(\u0026#39;\\x00\u0026#39;) except: pass # decimals() result = await rpc_call(\u0026#34;eth_call\u0026#34;, [ {\u0026#34;to\u0026#34;: token_addr, \u0026#34;data\u0026#34;: \u0026#34;0x313ce567\u0026#34;}, \u0026#34;latest\u0026#34; ]) if result and len(result) \u0026gt; 2: try: info[\u0026#34;decimals\u0026#34;] = int(result, 16) except: pass # name() result = await rpc_call(\u0026#34;eth_call\u0026#34;, [ {\u0026#34;to\u0026#34;: token_addr, \u0026#34;data\u0026#34;: \u0026#34;0x06fdde03\u0026#34;}, \u0026#34;latest\u0026#34; ]) if result and len(result) \u0026gt; 2: try: hex_str = result[2:] if len(hex_str) \u0026gt;= 128: offset = int(hex_str[:64], 16) * 2 length = int(hex_str[offset:offset+64], 16) name_hex = hex_str[offset+64:offset+64+length*2] info[\u0026#34;name\u0026#34;] = bytes.fromhex(name_hex).decode(\u0026#34;utf-8\u0026#34;, errors=\u0026#34;replace\u0026#34;).strip(\u0026#39;\\x00\u0026#39;) elif len(hex_str) == 64: info[\u0026#34;name\u0026#34;] = bytes.fromhex(hex_str).decode(\u0026#34;utf-8\u0026#34;, errors=\u0026#34;replace\u0026#34;).strip(\u0026#39;\\x00\u0026#39;) except: pass token_cache[addr_lower] = info return info async def check_if_pool(addr: str) -\u0026gt; bool: \u0026#34;\u0026#34;\u0026#34;Check if address is a Uniswap V2/V3 pool (has token0 method)\u0026#34;\u0026#34;\u0026#34; result = await rpc_call(\u0026#34;eth_call\u0026#34;, [ {\u0026#34;to\u0026#34;: addr, \u0026#34;data\u0026#34;: \u0026#34;0x0dfe1681\u0026#34;}, \u0026#34;latest\u0026#34; # token0() ]) if result and len(result) == 66: # 0x + 64 hex chars return True return False async def get_eth_price() -\u0026gt; float: \u0026#34;\u0026#34;\u0026#34;Get ETH price from CoinGecko\u0026#34;\u0026#34;\u0026#34; session = await get_http_session() try: async with session.get( \u0026#34;https://api.coingecko.com/api/v3/simple/price?ids=ethereum\u0026amp;vs_currencies=usd\u0026#34;, timeout=aiohttp.ClientTimeout(total=5), ) as resp: data = await resp.json() return data.get(\u0026#34;ethereum\u0026#34;, {}).get(\u0026#34;usd\u0026#34;, 0) except: return 2300 # fallback async def get_token_price_usd(token_addr: str) -\u0026gt; float | None: \u0026#34;\u0026#34;\u0026#34;Get token USD price from DexScreener (free, no API key needed)\u0026#34;\u0026#34;\u0026#34; session = await get_http_session() try: async with session.get( f\u0026#34;https://api.dexscreener.com/latest/dex/tokens/{token_addr}\u0026#34;, timeout=aiohttp.ClientTimeout(total=5), ) as resp: data = await resp.json() pairs = data.get(\u0026#34;pairs\u0026#34;, []) if pairs: # Pick pair with highest liquidity pairs.sort(key=lambda p: float(p.get(\u0026#34;liquidity\u0026#34;, {}).get(\u0026#34;usd\u0026#34;, 0) or 0), reverse=True) price = float(pairs[0].get(\u0026#34;priceUsd\u0026#34;, 0) or 0) return price if price \u0026gt; 0 else None except: pass return None # ============================================================ # Telegram notifications # ============================================================ async def send_telegram(text: str): \u0026#34;\u0026#34;\u0026#34;Send Telegram message\u0026#34;\u0026#34;\u0026#34; if not TG_BOT_TOKEN: log.warning(\u0026#34;[TG] No bot token configured, skipping notification\u0026#34;) return session = await get_http_session() url = f\u0026#34;https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage\u0026#34; payload = { \u0026#34;chat_id\u0026#34;: TG_CHAT_ID, \u0026#34;text\u0026#34;: text, \u0026#34;parse_mode\u0026#34;: \u0026#34;HTML\u0026#34;, \u0026#34;disable_web_page_preview\u0026#34;: True, } try: async with session.post(url, json=payload, timeout=aiohttp.ClientTimeout(total=10)) as resp: result = await resp.json() if not result.get(\u0026#34;ok\u0026#34;): log.error(f\u0026#34;[TG] Send failed: {result.get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;)}\u0026#34;) # Retry with plain text if HTML parse fails if \u0026#34;parse\u0026#34; in result.get(\u0026#34;description\u0026#34;, \u0026#34;\u0026#34;).lower(): payload[\u0026#34;parse_mode\u0026#34;] = None async with session.post(url, json=payload) as resp2: pass else: log.info(\u0026#34;[TG] Message sent\u0026#34;) except Exception as e: log.error(f\u0026#34;[TG] Error: {e}\u0026#34;) # ============================================================ # Event handling # ============================================================ async def handle_transfer_event(log_entry: dict): \u0026#34;\u0026#34;\u0026#34;Process a single Transfer event\u0026#34;\u0026#34;\u0026#34; tx_hash = log_entry.get(\u0026#34;transactionHash\u0026#34;, \u0026#34;\u0026#34;) token_addr = log_entry.get(\u0026#34;address\u0026#34;, \u0026#34;\u0026#34;) topics = log_entry.get(\u0026#34;topics\u0026#34;, []) data = log_entry.get(\u0026#34;data\u0026#34;, \u0026#34;0x0\u0026#34;) # Dedup dedup_key = f\u0026#34;{tx_hash}:{token_addr}\u0026#34; if dedup_key in seen_txs: return seen_txs.add(dedup_key) seen_txs_list.append(dedup_key) # Cleanup (keep last 1000) while len(seen_txs_list) \u0026gt; 1000: old = seen_txs_list.pop(0) seen_txs.discard(old) # Parse topics: [Transfer, from, to] if len(topics) \u0026lt; 3: return from_addr = topic_to_address(topics[1]) to_addr = topic_to_address(topics[2]) # Confirm it\u0026#39;s from Vitalik if from_addr.lower() != VITALIK_ADDRESS.lower(): return # Get token info token_info = await get_token_info(token_addr) symbol = token_info[\u0026#34;symbol\u0026#34;] decimals = token_info[\u0026#34;decimals\u0026#34;] amount = decode_transfer_value(data, decimals) if amount == 0: return # Classify recipient recipient_type, recipient_name = classify_recipient(to_addr) # If unknown, check if it\u0026#39;s an LP pool is_pool = False if recipient_type == \u0026#34;unknown\u0026#34;: is_pool = await check_if_pool(to_addr) if is_pool: recipient_type = \u0026#34;pool\u0026#34; recipient_name = \u0026#34;LP Pool\u0026#34; # Determine if this is a \u0026#34;sell\u0026#34; action is_sell = recipient_type in (\u0026#34;dex\u0026#34;, \u0026#34;cex\u0026#34;, \u0026#34;pool\u0026#34;) # If not a sell, just a regular transfer — log silently if not is_sell: log.info(f\u0026#34;[Transfer] {symbol} {amount:,.2f} → {shorten_addr(to_addr)} (regular transfer, not notifying)\u0026#34;) return # Get price token_price = await get_token_price_usd(token_addr) usd_value = amount * token_price if token_price else None # Below minimum notification amount — skip if usd_value is not None and usd_value \u0026lt; MIN_NOTIFY_USD: log.info(f\u0026#34;[Sell] {symbol} ${usd_value:.0f} \u0026lt; ${MIN_NOTIFY_USD} minimum, skipping\u0026#34;) return # Build notification message timestamp = datetime.now(timezone.utc).strftime(\u0026#34;%H:%M:%S UTC\u0026#34;) sell_type_label = { \u0026#34;dex\u0026#34;: \u0026#34;🔄 DEX Sell\u0026#34;, \u0026#34;cex\u0026#34;: \u0026#34;🏦 CEX Transfer\u0026#34;, \u0026#34;pool\u0026#34;: \u0026#34;🌊 Pool Sell\u0026#34;, } msg_lines = [ f\u0026#34;\u0026lt;b\u0026gt;🚨 Vitalik Sell Signal\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;\u0026#34;, f\u0026#34;Token: \u0026lt;b\u0026gt;{symbol}\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;Amount: {amount:,.4f}\u0026#34;, ] if usd_value is not None: msg_lines.append(f\u0026#34;Value: \u0026lt;b\u0026gt;${usd_value:,.0f}\u0026lt;/b\u0026gt;\u0026#34;) if token_price is not None: msg_lines.append(f\u0026#34;Price: ${token_price:,.8f}\u0026#34;) msg_lines.extend([ f\u0026#34;\u0026#34;, f\u0026#34;Type: {sell_type_label.get(recipient_type, \u0026#39;Unknown\u0026#39;)}\u0026#34;, f\u0026#34;Destination: {recipient_name or shorten_addr(to_addr)}\u0026#34;, f\u0026#34;Time: {timestamp}\u0026#34;, f\u0026#34;\u0026#34;, f\u0026#34;TX: https://etherscan.io/tx/{tx_hash}\u0026#34;, f\u0026#34;Token: https://dexscreener.com/ethereum/{token_addr}\u0026#34;, ]) msg = \u0026#34;\\n\u0026#34;.join(msg_lines) log.info(f\u0026#34;[SELL DETECTED] {symbol} {amount:,.4f} → {recipient_name or to_addr} | ${usd_value or \u0026#39;?\u0026#39;}\u0026#34;) await send_telegram(msg) # ============================================================ # WebSocket listener main loop # ============================================================ async def subscribe_and_listen(ws_url: str): \u0026#34;\u0026#34;\u0026#34;Connect to WebSocket and subscribe to Vitalik Transfer events\u0026#34;\u0026#34;\u0026#34; log.info(f\u0026#34;Connecting to {ws_url}...\u0026#34;) async with websockets.connect( ws_url, ping_interval=20, ping_timeout=30, close_timeout=10, max_size=2**20, # 1MB ) as ws: # Subscribe to Transfer FROM Vitalik sub_from = { \u0026#34;jsonrpc\u0026#34;: \u0026#34;2.0\u0026#34;, \u0026#34;id\u0026#34;: 1, \u0026#34;method\u0026#34;: \u0026#34;eth_subscribe\u0026#34;, \u0026#34;params\u0026#34;: [\u0026#34;logs\u0026#34;, { \u0026#34;topics\u0026#34;: [TRANSFER_TOPIC, VITALIK_PADDED] }] } await ws.send(json.dumps(sub_from)) resp = await asyncio.wait_for(ws.recv(), timeout=10) data = json.loads(resp) if \u0026#34;error\u0026#34; in data: raise Exception(f\u0026#34;Subscribe failed: {data[\u0026#39;error\u0026#39;]}\u0026#34;) sub_id_from = data.get(\u0026#34;result\u0026#34;, \u0026#34;\u0026#34;) log.info(f\u0026#34;✅ Subscribed to Transfer FROM Vitalik (id={sub_id_from[:10]}...)\u0026#34;) # Also subscribe to Transfer TO Vitalik (monitor buys/receives) sub_to = { \u0026#34;jsonrpc\u0026#34;: \u0026#34;2.0\u0026#34;, \u0026#34;id\u0026#34;: 2, \u0026#34;method\u0026#34;: \u0026#34;eth_subscribe\u0026#34;, \u0026#34;params\u0026#34;: [\u0026#34;logs\u0026#34;, { \u0026#34;topics\u0026#34;: [TRANSFER_TOPIC, None, VITALIK_PADDED] }] } await ws.send(json.dumps(sub_to)) resp2 = await asyncio.wait_for(ws.recv(), timeout=10) data2 = json.loads(resp2) sub_id_to = data2.get(\u0026#34;result\u0026#34;, \u0026#34;\u0026#34;) if sub_id_to: log.info(f\u0026#34;✅ Subscribed to Transfer TO Vitalik (id={sub_id_to[:10]}...)\u0026#34;) log.info(\u0026#34;🔍 Monitoring Vitalik wallet... waiting for events\u0026#34;) # Listen for events async for message in ws: if not running: break try: evt = json.loads(message) if \u0026#34;params\u0026#34; not in evt: continue sub_id = evt[\u0026#34;params\u0026#34;].get(\u0026#34;subscription\u0026#34;, \u0026#34;\u0026#34;) log_entry = evt[\u0026#34;params\u0026#34;].get(\u0026#34;result\u0026#34;, {}) if sub_id == sub_id_from: # Vitalik sent tokens → check if it\u0026#39;s a sell await handle_transfer_event(log_entry) elif sub_id == sub_id_to: # Vitalik received tokens — log silently token_addr = log_entry.get(\u0026#34;address\u0026#34;, \u0026#34;\u0026#34;) token_info = await get_token_info(token_addr) data_hex = log_entry.get(\u0026#34;data\u0026#34;, \u0026#34;0x0\u0026#34;) amount = decode_transfer_value(data_hex, token_info[\u0026#34;decimals\u0026#34;]) log.debug(f\u0026#34;[Receive] {token_info[\u0026#39;symbol\u0026#39;]} +{amount:,.4f}\u0026#34;) except json.JSONDecodeError: continue except Exception as e: log.error(f\u0026#34;Event handling error: {e}\u0026#34;, exc_info=True) async def main(): \u0026#34;\u0026#34;\u0026#34;Main loop with auto-reconnect\u0026#34;\u0026#34;\u0026#34; global running # Validate required config if not TG_BOT_TOKEN: log.warning(\u0026#34;TG_BOT_TOKEN not set — Telegram notifications disabled\u0026#34;) if not TG_CHAT_ID: log.warning(\u0026#34;TG_CHAT_ID not set — Telegram notifications disabled\u0026#34;) # Signal handling def shutdown(sig, frame): global running log.info(f\u0026#34;Received signal {sig}, shutting down...\u0026#34;) running = False signal.signal(signal.SIGINT, shutdown) signal.signal(signal.SIGTERM, shutdown) # Startup notice log.info(\u0026#34;=\u0026#34; * 50) log.info(\u0026#34;Vitalik Sell Radar — Started\u0026#34;) log.info(f\u0026#34;Monitoring: {VITALIK_ADDRESS}\u0026#34;) log.info(f\u0026#34;Min notify: ${MIN_NOTIFY_USD}\u0026#34;) log.info(f\u0026#34;Telegram: {\u0026#39;✅ Configured\u0026#39; if TG_BOT_TOKEN else \u0026#39;❌ Not configured\u0026#39;}\u0026#34;) log.info(\u0026#34;=\u0026#34; * 50) if TG_BOT_TOKEN and TG_CHAT_ID: await send_telegram( \u0026#34;🟢 Vitalik Sell Radar — Started\\n\\n\u0026#34; f\u0026#34;Monitoring: {shorten_addr(VITALIK_ADDRESS)}\\n\u0026#34; f\u0026#34;Min notify: ${MIN_NOTIFY_USD}\\n\u0026#34; \u0026#34;Mode: WebSocket event-driven (sub-second latency)\u0026#34; ) endpoint_idx = 0 reconnect_delay = RECONNECT_DELAY while running: ws_url = WS_ENDPOINTS[endpoint_idx % len(WS_ENDPOINTS)] try: await subscribe_and_listen(ws_url) reconnect_delay = RECONNECT_DELAY # reset on success except websockets.exceptions.ConnectionClosed as e: log.warning(f\u0026#34;WebSocket closed: {e}\u0026#34;) except asyncio.TimeoutError: log.warning(\u0026#34;WebSocket timeout\u0026#34;) except Exception as e: log.error(f\u0026#34;WebSocket error: {e}\u0026#34;) if not running: break # Switch endpoint and retry endpoint_idx += 1 log.info(f\u0026#34;Reconnecting in {reconnect_delay}s... (next: {WS_ENDPOINTS[endpoint_idx % len(WS_ENDPOINTS)]})\u0026#34;) await asyncio.sleep(reconnect_delay) reconnect_delay = min(reconnect_delay * 1.5, MAX_RECONNECT_DELAY) # Cleanup if http_session and not http_session.closed: await http_session.close() log.info(\u0026#34;Vitalik Sell Radar — Stopped\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: asyncio.run(main()) 链上雷达 发布日期: 2026.04.28　标签: Python · GMGN · DEXScreener · Telegram\n动量驱动 · 链上新币发现 · 覆盖 ETH/SOL/BSC/Base\n动量是唯一推送引擎，叙事只做分类标签。每30秒扫描4条链（ETH/SOL/BSC/Base），连涨3轮+涨幅5%才推送。叙事分类（马斯克/川普、币安/CZ、名人热点）只标星级（★★★/★★/★），不独立触发推送。安全检查：SOL用RugCheck、EVM用GoPlus。纯Python零AI成本。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; 叙事雷达 → 链上雷达 v1 纯Python，零AI成本（关键词匹配 + 叙事去重） 三条推送通道： 1. 全新叙事 — 从未见过的概念/故事，全链推 2. 马斯克/川普相关 — 重点ETH+SOL，BSC也推 3. 币安/CZ相关 — 只推BSC 数据源：GMGN新币 + DEXScreener搜索 叙事历史：SQLite去重 \u0026#34;\u0026#34;\u0026#34; import requests import json import time import os import re import sqlite3 import hashlib from datetime import datetime, timedelta from pathlib import Path from difflib import SequenceMatcher # === 配置 === DATA_DIR = os.path.expanduser(\u0026#34;~/crypto-trading\u0026#34;) DB_FILE = os.path.join(DATA_DIR, \u0026#34;narrative_history.db\u0026#34;) LOG_FILE = os.path.join(DATA_DIR, \u0026#34;narrative_radar.log\u0026#34;) SEEN_FILE = os.path.join(DATA_DIR, \u0026#34;narrative_seen.json\u0026#34;) FLAP_SEEN_FILE = os.path.join(DATA_DIR, \u0026#34;flap_seen.json\u0026#34;) # 扫描间隔 SCAN_INTERVAL = 30 # 30秒（GMGN数据约1-5分钟刷新一次，10秒太频繁且数据不变） # 动量追踪器 — 内存中记录每个币的价格/市值快照 # {address: [{\u0026#39;ts\u0026#39;: timestamp, \u0026#39;mc\u0026#39;: market_cap, \u0026#39;vol\u0026#39;: volume, \u0026#39;price\u0026#39;: price}, ...]} MOMENTUM_TRACKER = {} MOMENTUM_PUSHED = {} # {address: {\u0026#39;count\u0026#39;: N, \u0026#39;last_ts\u0026#39;: ts, \u0026#39;last_mc\u0026#39;: mc}} 推送计数 MOMENTUM_CONSECUTIVE_UP = 3 # 连续涨3轮（数据实际变化时才算一轮） # 从.env读取TG配置 def load_env(): env = {} env_file = os.path.expanduser(\u0026#34;~/.env\u0026#34;) if os.path.exists(env_file): with open(env_file) as f: for line in f: line = line.strip() if \u0026#39;=\u0026#39; in line and not line.startswith(\u0026#39;#\u0026#39;): k, v = line.split(\u0026#39;=\u0026#39;, 1) env[k] = v return env ENV = load_env() TG_TOKEN = ENV.get(\u0026#39;TELEGRAM_BOT_TOKEN\u0026#39;, \u0026#39;\u0026#39;) TG_CHAT_ID = int(os.environ.get(\u0026#39;TG_CHAT_ID\u0026#39;, \u0026#39;0\u0026#39;)) GMGN_HEADERS = { \u0026#39;User-Agent\u0026#39;: \u0026#39;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36\u0026#39;, \u0026#39;Accept\u0026#39;: \u0026#39;application/json\u0026#39;, \u0026#39;Referer\u0026#39;: \u0026#39;https://gmgn.ai/\u0026#39;, } # ============================================================ # 马斯克/川普关键词库（大小写不敏感） # ============================================================ MUSK_TRUMP_KEYWORDS = { # 马斯克核心 \u0026#39;musk\u0026#39;, \u0026#39;elon\u0026#39;, \u0026#39;elonmusk\u0026#39;, # SpaceX/Tesla/X \u0026#39;spacex\u0026#39;, \u0026#39;starship\u0026#39;, \u0026#39;tesla\u0026#39;, \u0026#39;cybertruck\u0026#39;, \u0026#39;roadster\u0026#39;, \u0026#39;neuralink\u0026#39;, \u0026#39;boring\u0026#39;, \u0026#39;hyperloop\u0026#39;, \u0026#39;xai\u0026#39;, \u0026#39;grok\u0026#39;, # 马斯克相关人物/宠物/梗 \u0026#39;floki\u0026#39;, \u0026#39;shiba\u0026#39;, # 只在新币上下文中用 \u0026#39;doge father\u0026#39;, \u0026#39;dogefather\u0026#39;, \u0026#39;technoking\u0026#39;, \u0026#39;mars colony\u0026#39;, \u0026#39;mars\u0026#39;, # 川普核心 \u0026#39;trump\u0026#39;, \u0026#39;donald\u0026#39;, \u0026#39;maga\u0026#39;, \u0026#39;potus\u0026#39;, \u0026#39;trump47\u0026#39;, \u0026#39;melania\u0026#39;, \u0026#39;barron\u0026#39;, \u0026#39;ivanka\u0026#39;, # 川普相关 \u0026#39;dark maga\u0026#39;, \u0026#39;darkmaga\u0026#39;, \u0026#39;ultra maga\u0026#39;, \u0026#39;save america\u0026#39;, \u0026#39;truth social\u0026#39;, \u0026#39;covfefe\u0026#39;, # 马斯克+川普联动 \u0026#39;doge department\u0026#39;, \u0026#39;d.o.g.e\u0026#39;, \u0026#39;government efficiency\u0026#39;, } # 马斯克/川普正则（捕捉变体） MUSK_TRUMP_PATTERNS = [ r\u0026#39;\\belon\\b\u0026#39;, r\u0026#39;\\bmusk\\b\u0026#39;, r\u0026#39;\\btrump\\b\u0026#39;, r\u0026#39;\\bmaga\\b\u0026#39;, r\u0026#39;\\bspacex\\b\u0026#39;, r\u0026#39;\\bstarship\\b\u0026#39;, r\u0026#39;\\btesla\\b\u0026#39;, r\u0026#39;\\bgrok\\b\u0026#39;, r\u0026#39;\\bmelania\\b\u0026#39;, r\u0026#39;\\bbarron\\b\u0026#39;, r\u0026#39;\\bdoge\\s*department\\b\u0026#39;, r\u0026#39;\\bd\\.?o\\.?g\\.?e\\b\u0026#39;, # D.O.G.E变体 r\u0026#39;\\bx\\s*ai\\b\u0026#39;, r\u0026#39;\\bneuralink\\b\u0026#39;, ] # ============================================================ # 币安/CZ关键词库 # ============================================================ BINANCE_CZ_KEYWORDS = { # CZ核心 \u0026#39;cz\u0026#39;, \u0026#39;changpeng\u0026#39;, \u0026#39;zhao\u0026#39;, \u0026#39;czb\u0026#39;, \u0026#39;czbinance\u0026#39;, # 何一（BSC现在的核心推手！） \u0026#39;heyi\u0026#39;, \u0026#39;yi he\u0026#39;, \u0026#39;he yi\u0026#39;, \u0026#39;何一\u0026#39;, \u0026#39;yihe\u0026#39;, \u0026#39;sister yi\u0026#39;, \u0026#39;yi jie\u0026#39;, \u0026#39;一姐\u0026#39;, \u0026#39;何一姐\u0026#39;, # 币安品牌 \u0026#39;binance\u0026#39;, \u0026#39;bnb\u0026#39;, \u0026#39;pancake\u0026#39;, \u0026#39;pancakeswap\u0026#39;, # CZ相关动态词（书、活动、推特高频词） \u0026#39;giggle academy\u0026#39;, \u0026#39;binance life\u0026#39;, \u0026#39;bnb chain\u0026#39;, \u0026#39;principles\u0026#39;, \u0026#39;cz book\u0026#39;, # YZi Labs (原Binance Labs) \u0026#39;yzi\u0026#39;, \u0026#39;yzi labs\u0026#39;, # 中文关键词（BSC上常见） \u0026#39;赵长鹏\u0026#39;, \u0026#39;币安\u0026#39;, \u0026#39;长鹏\u0026#39;, \u0026#39;cz的\u0026#39;, \u0026#39;何一的\u0026#39;, # Four.meme平台相关 \u0026#39;fourmeme\u0026#39;, \u0026#39;four meme\u0026#39;, \u0026#39;4meme\u0026#39;, # CZ/何一推特互动高频词 \u0026#39;czs dog\u0026#39;, \u0026#39;cz dog\u0026#39;, \u0026#39;bnb dog\u0026#39;, \u0026#39;build on bnb\u0026#39;, \u0026#39;bnb ecosystem\u0026#39;, } BINANCE_CZ_PATTERNS = [ r\u0026#39;\\bcz\\b\u0026#39;, r\u0026#39;\\bbinance\\b\u0026#39;, r\u0026#39;\\bbnb\\b\u0026#39;, r\u0026#39;\\bheyi\\b\u0026#39;, r\u0026#39;\\byi\\s*he\\b\u0026#39;, r\u0026#39;\\bhe\\s*yi\\b\u0026#39;, r\u0026#39;\\b何一\\b\u0026#39;, r\u0026#39;\\b一姐\\b\u0026#39;, r\u0026#39;\\bpancake\\b\u0026#39;, r\u0026#39;\\bgiggle\\b\u0026#39;, r\u0026#39;\\byzi\\b\u0026#39;, r\u0026#39;\\bfourmeme\\b\u0026#39;, r\u0026#39;\\b4meme\\b\u0026#39;, ] # ============================================================ # 推特热点/名人关键词库（★★级别） # ============================================================ CELEBRITY_VIRAL_KEYWORDS = { # 科技名人 \u0026#39;vitalik\u0026#39;, \u0026#39;buterin\u0026#39;, \u0026#39;sam altman\u0026#39;, \u0026#39;satoshi\u0026#39;, \u0026#39;michael saylor\u0026#39;, \u0026#39;saylor\u0026#39;, \u0026#39;cathie wood\u0026#39;, \u0026#39;jack dorsey\u0026#39;, \u0026#39;zuckerberg\u0026#39;, \u0026#39;bezos\u0026#39;, \u0026#39;jensen huang\u0026#39;, \u0026#39;nvidia\u0026#39;, \u0026#39;tim cook\u0026#39;, # 币圈名人 \u0026#39;justin sun\u0026#39;, \u0026#39;sun yuchen\u0026#39;, \u0026#39;孙宇晨\u0026#39;, \u0026#39;tron\u0026#39;, \u0026#39;arthur hayes\u0026#39;, \u0026#39;su zhu\u0026#39;, \u0026#39;3ac\u0026#39;, \u0026#39;brian armstrong\u0026#39;, \u0026#39;coinbase\u0026#39;, \u0026#39;larry fink\u0026#39;, \u0026#39;blackrock\u0026#39;, \u0026#39;gary gensler\u0026#39;, \u0026#39;sec\u0026#39;, \u0026#39;michael novogratz\u0026#39;, \u0026#39;galaxy\u0026#39;, # 政治/社会名人 \u0026#39;biden\u0026#39;, \u0026#39;obama\u0026#39;, \u0026#39;putin\u0026#39;, \u0026#39;xi jinping\u0026#39;, \u0026#39;kanye\u0026#39;, \u0026#39;drake\u0026#39;, \u0026#39;snoop dogg\u0026#39;, \u0026#39;paris hilton\u0026#39;, \u0026#39;mark cuban\u0026#39;, \u0026#39;mr beast\u0026#39;, \u0026#39;mrbeast\u0026#39;, # 病毒式传播热词（龙虾级别的梗） \u0026#39;lobster\u0026#39;, \u0026#39;龙虾\u0026#39;, \u0026#39;lobsta\u0026#39;, \u0026#39;hawk tuah\u0026#39;, \u0026#39;griddy\u0026#39;, \u0026#39;skibidi\u0026#39;, \u0026#39;rizz\u0026#39;, \u0026#39;sigma\u0026#39;, \u0026#39;gyatt\u0026#39;, # 重大事件关键词 \u0026#39;etf\u0026#39;, \u0026#39;halving\u0026#39;, \u0026#39;减半\u0026#39;, \u0026#39;world war\u0026#39;, \u0026#39;wwiii\u0026#39;, \u0026#39;fed\u0026#39;, \u0026#39;rate cut\u0026#39;, \u0026#39;降息\u0026#39;, \u0026#39;tiktok ban\u0026#39;, \u0026#39;tiktok\u0026#39;, } CELEBRITY_VIRAL_PATTERNS = [ r\u0026#39;\\bvitalik\\b\u0026#39;, r\u0026#39;\\bsaylor\\b\u0026#39;, r\u0026#39;\\bblackrock\\b\u0026#39;, r\u0026#39;\\bcoinbase\\b\u0026#39;, r\u0026#39;\\bjustin\\s*sun\\b\u0026#39;, r\u0026#39;\\blobster\\b\u0026#39;, r\u0026#39;\\betf\\b\u0026#39;, r\u0026#39;\\bhalving\\b\u0026#39;, r\u0026#39;\\bmrbeast\\b\u0026#39;, r\u0026#39;\\bsnoop\\b\u0026#39;, r\u0026#39;\\bkanye\\b\u0026#39;, r\u0026#39;\\bdrake\\b\u0026#39;, ] # ============================================================ # 通用垃圾词（过滤明显的骗局/低质量币） # ============================================================ SPAM_PATTERNS = [ r\u0026#39;airdrop\u0026#39;, r\u0026#39;presale\u0026#39;, r\u0026#39;pre\\s*sale\u0026#39;, r\u0026#39;1000x\u0026#39;, r\u0026#39;100x guaranteed\u0026#39;, r\u0026#39;safe\\s*moon\u0026#39;, r\u0026#39;baby\\s*\\w+\u0026#39;, # babydoge等仿盘 r\u0026#39;pornhub\u0026#39;, r\u0026#39;porn\u0026#39;, r\u0026#39;xxx\u0026#39;, r\u0026#39;nsfw\u0026#39;, r\u0026#39;nigga\u0026#39;, r\u0026#39;nigger\u0026#39;, r\u0026#39;faggot\u0026#39;, r\u0026#39;scam\u0026#39;, r\u0026#39;rugpull\u0026#39;, r\u0026#39;rug\\s*pull\u0026#39;, r\u0026#39;official\\s*token\u0026#39;, r\u0026#39;official\\s*coin\u0026#39;, ] # 常见无叙事意义的单词（过滤单词名币） COMMON_NOISE_WORDS = { \u0026#39;nice\u0026#39;, \u0026#39;good\u0026#39;, \u0026#39;bad\u0026#39;, \u0026#39;cool\u0026#39;, \u0026#39;hot\u0026#39;, \u0026#39;big\u0026#39;, \u0026#39;small\u0026#39;, \u0026#39;life\u0026#39;, \u0026#39;love\u0026#39;, \u0026#39;hate\u0026#39;, \u0026#39;happy\u0026#39;, \u0026#39;sad\u0026#39;, \u0026#39;fun\u0026#39;, \u0026#39;lol\u0026#39;, \u0026#39;cat\u0026#39;, \u0026#39;dog\u0026#39;, \u0026#39;moon\u0026#39;, \u0026#39;sun\u0026#39;, \u0026#39;star\u0026#39;, \u0026#39;king\u0026#39;, \u0026#39;queen\u0026#39;, \u0026#39;gold\u0026#39;, \u0026#39;rich\u0026#39;, \u0026#39;cash\u0026#39;, \u0026#39;money\u0026#39;, \u0026#39;pay\u0026#39;, \u0026#39;buy\u0026#39;, \u0026#39;sell\u0026#39;, \u0026#39;pump\u0026#39;, \u0026#39;dump\u0026#39;, \u0026#39;bull\u0026#39;, \u0026#39;bear\u0026#39;, \u0026#39;green\u0026#39;, \u0026#39;red\u0026#39;, \u0026#39;hello\u0026#39;, \u0026#39;world\u0026#39;, \u0026#39;yes\u0026#39;, \u0026#39;no\u0026#39;, \u0026#39;wow\u0026#39;, \u0026#39;omg\u0026#39;, \u0026#39;lmao\u0026#39;, \u0026#39;simp\u0026#39;, \u0026#39;chad\u0026#39;, \u0026#39;based\u0026#39;, \u0026#39;cope\u0026#39;, \u0026#39;seethe\u0026#39;, \u0026#39;test\u0026#39;, \u0026#39;new\u0026#39;, \u0026#39;old\u0026#39;, \u0026#39;real\u0026#39;, \u0026#39;fake\u0026#39;, # 垃圾币名常见词 \u0026#39;shit\u0026#39;, \u0026#39;shitcoin\u0026#39;, \u0026#39;fuck\u0026#39;, \u0026#39;fart\u0026#39;, \u0026#39;poop\u0026#39;, \u0026#39;pee\u0026#39;, \u0026#39;cum\u0026#39;, \u0026#39;dick\u0026#39;, \u0026#39;ass\u0026#39;, \u0026#39;boob\u0026#39;, \u0026#39;tit\u0026#39;, \u0026#39;nigga\u0026#39;, \u0026#39;retard\u0026#39;, \u0026#39;slop\u0026#39;, # 超通用币名 \u0026#39;the\u0026#39;, \u0026#39;and\u0026#39;, \u0026#39;for\u0026#39;, \u0026#39;from\u0026#39;, \u0026#39;with\u0026#39;, \u0026#39;this\u0026#39;, \u0026#39;that\u0026#39;, \u0026#39;coin\u0026#39;, \u0026#39;token\u0026#39;, \u0026#39;meme\u0026#39;, \u0026#39;pepe\u0026#39;, \u0026#39;wojak\u0026#39;, \u0026#39;peg\u0026#39;, \u0026#39;usd\u0026#39;, \u0026#39;usdt\u0026#39;, \u0026#39;usdc\u0026#39;, \u0026#39;dai\u0026#39;, } # ============================================================ # 工具函数 # ============================================================ def log(msg): ts = datetime.now().strftime(\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;) line = f\u0026#34;[{ts}] {msg}\u0026#34; print(line) os.makedirs(DATA_DIR, exist_ok=True) with open(LOG_FILE, \u0026#39;a\u0026#39;) as f: f.write(line + \u0026#39;\\n\u0026#39;) def load_flap_seen(): if os.path.exists(FLAP_SEEN_FILE): try: with open(FLAP_SEEN_FILE) as f: return json.load(f) except: pass return {} def save_flap_seen(data): # 只保留7天内的 cutoff = int(time.time()) - 86400 * 7 data = {k: v for k, v in data.items() if v \u0026gt; cutoff} with open(FLAP_SEEN_FILE, \u0026#39;w\u0026#39;) as f: json.dump(data, f) def tg_send(text, parse_mode=\u0026#39;Markdown\u0026#39;): if not TG_TOKEN: log(f\u0026#34;[TG] No token, skip: {text[:80]}\u0026#34;) return False try: resp = requests.post( f\u0026#39;https://api.telegram.org/bot{TG_TOKEN}/sendMessage\u0026#39;, json={\u0026#39;chat_id\u0026#39;: TG_CHAT_ID, \u0026#39;text\u0026#39;: text, \u0026#39;parse_mode\u0026#39;: parse_mode}, timeout=10 ) result = resp.json() if not result.get(\u0026#39;ok\u0026#39;): # Markdown失败时降级到纯文本 if \u0026#39;can\\\u0026#39;t parse\u0026#39; in str(result.get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;)).lower(): resp = requests.post( f\u0026#39;https://api.telegram.org/bot{TG_TOKEN}/sendMessage\u0026#39;, json={\u0026#39;chat_id\u0026#39;: TG_CHAT_ID, \u0026#39;text\u0026#39;: text}, timeout=10 ) else: log(f\u0026#34;[TG] Error: {result.get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;)}\u0026#34;) return False return True except Exception as e: log(f\u0026#34;[TG] Send error: {e}\u0026#34;) return False # ============================================================ # 叙事历史数据库 # ============================================================ def init_db(): \u0026#34;\u0026#34;\u0026#34;初始化SQLite叙事历史库\u0026#34;\u0026#34;\u0026#34; conn = sqlite3.connect(DB_FILE) c = conn.cursor() # 所有见过的叙事主题 c.execute(\u0026#39;\u0026#39;\u0026#39;CREATE TABLE IF NOT EXISTS narratives ( id INTEGER PRIMARY KEY AUTOINCREMENT, theme TEXT NOT NULL, -- 归一化的叙事主题（小写） first_token_name TEXT, -- 第一次出现时的代币名 first_token_address TEXT, -- 第一次出现时的地址 first_chain TEXT, -- 第一次出现的链 first_seen_at INTEGER, -- 第一次看到的时间戳 token_count INTEGER DEFAULT 1, -- 出现过多少次 last_seen_at INTEGER -- 最近一次看到 )\u0026#39;\u0026#39;\u0026#39;) # 所有扫描过的代币 c.execute(\u0026#39;\u0026#39;\u0026#39;CREATE TABLE IF NOT EXISTS tokens_seen ( address TEXT PRIMARY KEY, chain TEXT, name TEXT, symbol TEXT, narrative_theme TEXT, category TEXT, -- \u0026#39;musk_trump\u0026#39; / \u0026#39;binance_cz\u0026#39; / \u0026#39;novel\u0026#39; / \u0026#39;common\u0026#39; first_seen_at INTEGER, market_cap REAL, pushed INTEGER DEFAULT 0, -- 是否已推送 seen_count INTEGER DEFAULT 1 -- 出现次数 )\u0026#39;\u0026#39;\u0026#39;) # 索引 c.execute(\u0026#39;CREATE INDEX IF NOT EXISTS idx_theme ON narratives(theme)\u0026#39;) c.execute(\u0026#39;CREATE INDEX IF NOT EXISTS idx_addr ON tokens_seen(address)\u0026#39;) conn.commit() return conn def normalize_theme(name, symbol): \u0026#34;\u0026#34;\u0026#34; 从代币名称+符号提取归一化的叙事主题 例如：\u0026#39;Elon Mars Colony\u0026#39; → \u0026#39;elon mars colony\u0026#39; \u0026#39;TRUMP2028\u0026#39; → \u0026#39;trump\u0026#39; \u0026#39;PancakeBunny\u0026#39; → \u0026#39;pancake bunny\u0026#39; \u0026#34;\u0026#34;\u0026#34; # 合并name和symbol text = f\u0026#34;{name} {symbol}\u0026#34;.lower().strip() # 去除常见后缀/前缀 noise = [\u0026#39;token\u0026#39;, \u0026#39;coin\u0026#39;, \u0026#39;inu\u0026#39;, \u0026#39;swap\u0026#39;, \u0026#39;finance\u0026#39;, \u0026#39;protocol\u0026#39;, \u0026#39;dao\u0026#39;, \u0026#39;defi\u0026#39;, \u0026#39;nft\u0026#39;, \u0026#39;meta\u0026#39;, \u0026#39;verse\u0026#39;, \u0026#39;fi\u0026#39;, \u0026#39;ai\u0026#39;, \u0026#39;pepe\u0026#39;, \u0026#39;wojak\u0026#39;, \u0026#39;chad\u0026#39;, \u0026#39;based\u0026#39;] # 分割camelCase text = re.sub(r\u0026#39;([a-z])([A-Z])\u0026#39;, r\u0026#39;\\1 \\2\u0026#39;, text) # 去除数字（如2028、1000x） text = re.sub(r\u0026#39;\\d+x?\u0026#39;, \u0026#39;\u0026#39;, text) # 只保留字母和空格 text = re.sub(r\u0026#39;[^a-z\\s]\u0026#39;, \u0026#39; \u0026#39;, text) # 去噪 words = [w for w in text.split() if w and len(w) \u0026gt; 1 and w not in noise] if not words: return name.lower().strip() return \u0026#39; \u0026#39;.join(sorted(set(words))) def is_similar_theme(theme1, theme2, threshold=0.7): \u0026#34;\u0026#34;\u0026#34;模糊匹配两个叙事主题\u0026#34;\u0026#34;\u0026#34; if theme1 == theme2: return True # 子串匹配 if theme1 in theme2 or theme2 in theme1: return True # 词重叠 words1 = set(theme1.split()) words2 = set(theme2.split()) if words1 and words2: overlap = len(words1 \u0026amp; words2) / min(len(words1), len(words2)) if overlap \u0026gt;= 0.6: return True # 序列匹配 return SequenceMatcher(None, theme1, theme2).ratio() \u0026gt;= threshold def check_narrative_novelty(conn, theme, name, symbol, address, chain): \u0026#34;\u0026#34;\u0026#34; 检查叙事状态 返回： (\u0026#39;novel\u0026#39;, None) — 第一次见到 (\u0026#39;heating\u0026#39;, narrative_row) — 短时间内持续出现新币！热点信号！ (\u0026#39;existing\u0026#39;, existing_theme_row) — 已有叙事，不热 核心逻辑：同一主题在30分钟内出现2+个不同的币 = 热点 \u0026#34;\u0026#34;\u0026#34; c = conn.cursor() now = int(time.time()) HEAT_WINDOW = 1800 # 30分钟窗口 HEAT_THRESHOLD = 2 # 窗口内出现2个以上同主题币就是热点 # 精确匹配 c.execute(\u0026#39;SELECT id, theme, first_token_name, first_token_address, first_chain, first_seen_at, token_count, last_seen_at FROM narratives WHERE theme = ?\u0026#39;, (theme,)) exact = c.fetchone() if exact: row_id, _, _, _, _, first_seen, count, last_seen = exact # 更新计数 new_count = count + 1 c.execute(\u0026#39;UPDATE narratives SET token_count = ?, last_seen_at = ? WHERE theme = ?\u0026#39;, (new_count, now, theme)) conn.commit() # 热点判断：在HEAT_WINDOW内出现了多个币 if now - first_seen \u0026lt; HEAT_WINDOW and new_count \u0026gt;= HEAT_THRESHOLD: return (\u0026#39;heating\u0026#39;, exact) # 或者：最近一次和这次间隔很短（说明持续在冒） if now - last_seen \u0026lt; HEAT_WINDOW and new_count \u0026gt;= HEAT_THRESHOLD: return (\u0026#39;heating\u0026#39;, exact) return (\u0026#39;existing\u0026#39;, exact) # 模糊匹配 — 取最近1000个主题比对 c.execute(\u0026#39;SELECT id, theme, first_token_name, first_token_address, first_chain, first_seen_at, token_count, last_seen_at FROM narratives ORDER BY last_seen_at DESC LIMIT 1000\u0026#39;) for row in c.fetchall(): if is_similar_theme(theme, row[1]): row_id, _, _, _, _, first_seen, count, last_seen = row new_count = count + 1 c.execute(\u0026#39;UPDATE narratives SET token_count = ?, last_seen_at = ? WHERE id = ?\u0026#39;, (new_count, now, row[0])) conn.commit() # 热点判断 if now - last_seen \u0026lt; HEAT_WINDOW and new_count \u0026gt;= HEAT_THRESHOLD: return (\u0026#39;heating\u0026#39;, row) return (\u0026#39;existing\u0026#39;, row) # 第一次见到 — 记录 c.execute(\u0026#39;\u0026#39;\u0026#39;INSERT INTO narratives (theme, first_token_name, first_token_address, first_chain, first_seen_at, last_seen_at) VALUES (?, ?, ?, ?, ?, ?)\u0026#39;\u0026#39;\u0026#39;, (theme, name, address, chain, now, now)) conn.commit() return (\u0026#39;novel\u0026#39;, None) def get_token_seen_count(conn, address): \u0026#34;\u0026#34;\u0026#34;获取代币出现次数\u0026#34;\u0026#34;\u0026#34; c = conn.cursor() c.execute(\u0026#39;SELECT seen_count FROM tokens_seen WHERE address = ?\u0026#39;, (address,)) row = c.fetchone() return row[0] if row else 0 def is_token_seen(conn, address): \u0026#34;\u0026#34;\u0026#34;检查代币是否已经扫描过\u0026#34;\u0026#34;\u0026#34; c = conn.cursor() c.execute(\u0026#39;SELECT address FROM tokens_seen WHERE address = ?\u0026#39;, (address,)) return c.fetchone() is not None def record_token(conn, address, chain, name, symbol, theme, category, mc, pushed=False): \u0026#34;\u0026#34;\u0026#34;记录已扫描的代币 — 重复出现时计数+1\u0026#34;\u0026#34;\u0026#34; c = conn.cursor() # 检查是否已存在 c.execute(\u0026#39;SELECT seen_count FROM tokens_seen WHERE address = ?\u0026#39;, (address,)) existing = c.fetchone() if existing: # 已存在：计数+1，更新市值 new_count = existing[0] + 1 c.execute(\u0026#39;\u0026#39;\u0026#39;UPDATE tokens_seen SET seen_count = ?, market_cap = ?, category = ? WHERE address = ?\u0026#39;\u0026#39;\u0026#39;, (new_count, mc, category, address)) else: # 新记录 c.execute(\u0026#39;\u0026#39;\u0026#39;INSERT INTO tokens_seen (address, chain, name, symbol, narrative_theme, category, first_seen_at, market_cap, pushed, seen_count) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1)\u0026#39;\u0026#39;\u0026#39;, (address, chain, name, symbol, theme, category, int(time.time()), mc, 1 if pushed else 0)) conn.commit() # ============================================================ # 叙事分类引擎 # ============================================================ def classify_narrative(name, symbol, chain): \u0026#34;\u0026#34;\u0026#34; 分类代币叙事 返回：(\u0026#39;musk_trump\u0026#39;, matched_keywords) / (\u0026#39;binance_cz\u0026#39;, matched_keywords) / (\u0026#39;novel\u0026#39;, None) / (\u0026#39;common\u0026#39;, None) \u0026#34;\u0026#34;\u0026#34; text = f\u0026#34;{name} {symbol}\u0026#34;.lower() # 1. 检查是否是垃圾币 for pat in SPAM_PATTERNS: if re.search(pat, text, re.IGNORECASE): return (\u0026#39;spam\u0026#39;, None) # 2. 马斯克/川普检测 matched_mt = [] for kw in MUSK_TRUMP_KEYWORDS: if kw.lower() in text: matched_mt.append(kw) if not matched_mt: for pat in MUSK_TRUMP_PATTERNS: m = re.search(pat, text, re.IGNORECASE) if m: matched_mt.append(m.group()) if matched_mt: # 马斯克/川普：重点ETH+SOL，BSC也可以 chain_lower = chain.lower() if chain_lower in (\u0026#39;eth\u0026#39;, \u0026#39;ethereum\u0026#39;, \u0026#39;sol\u0026#39;, \u0026#39;solana\u0026#39;, \u0026#39;bsc\u0026#39;, \u0026#39;base\u0026#39;): return (\u0026#39;musk_trump\u0026#39;, matched_mt) # 3. 币安/CZ检测 — 只在BSC上推 matched_bc = [] for kw in BINANCE_CZ_KEYWORDS: if kw.lower() in text: matched_bc.append(kw) if not matched_bc: for pat in BINANCE_CZ_PATTERNS: m = re.search(pat, text, re.IGNORECASE) if m: matched_bc.append(m.group()) if matched_bc: chain_lower = chain.lower() if chain_lower in (\u0026#39;bsc\u0026#39;,): return (\u0026#39;binance_cz\u0026#39;, matched_bc) else: return (\u0026#39;binance_cz_wrong_chain\u0026#39;, matched_bc) # 4. 名人/推特热点检测（★★级别） matched_cv = [] for kw in CELEBRITY_VIRAL_KEYWORDS: if kw.lower() in text: matched_cv.append(kw) if not matched_cv: for pat in CELEBRITY_VIRAL_PATTERNS: m = re.search(pat, text, re.IGNORECASE) if m: matched_cv.append(m.group()) if matched_cv: return (\u0026#39;celebrity_viral\u0026#39;, matched_cv) # 5. 都不匹配 → 需要进一步检查是否全新叙事 return (\u0026#39;check_novelty\u0026#39;, None) # ============================================================ # 安全检查（复用现有逻辑） # ============================================================ def check_token_safety(chain, address): \u0026#34;\u0026#34;\u0026#34;快速安全检查 — 只拦硬伤（蜜罐/可增发），卖税不作为否决条件\u0026#34;\u0026#34;\u0026#34; if chain in (\u0026#39;sol\u0026#39;, \u0026#39;solana\u0026#39;): try: r = requests.get(f\u0026#39;https://api.rugcheck.xyz/v1/tokens/{address}/report\u0026#39;, timeout=10) if r.status_code == 200: data = r.json() score = data.get(\u0026#39;score\u0026#39;, 999) mint = data.get(\u0026#39;mintAuthority\u0026#39;) freeze = data.get(\u0026#39;freezeAuthority\u0026#39;) return { \u0026#39;safe\u0026#39;: not mint and not freeze, \u0026#39;score\u0026#39;: score, \u0026#39;mint\u0026#39;: mint is not None, \u0026#39;freeze\u0026#39;: freeze is not None } except: pass else: chain_map = {\u0026#39;ethereum\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;eth\u0026#39;: \u0026#39;1\u0026#39;, \u0026#39;bsc\u0026#39;: \u0026#39;56\u0026#39;, \u0026#39;base\u0026#39;: \u0026#39;8453\u0026#39;} cid = chain_map.get(chain, \u0026#39;1\u0026#39;) try: r = requests.get(f\u0026#39;https://api.gopluslabs.io/api/v1/token_security/{cid}?contract_addresses={address}\u0026#39;, timeout=10) if r.status_code == 200: result = r.json().get(\u0026#39;result\u0026#39;, {}) data = result.get(address.lower(), {}) if data: honeypot = data.get(\u0026#39;is_honeypot\u0026#39;, \u0026#39;0\u0026#39;) == \u0026#39;1\u0026#39; mintable = data.get(\u0026#39;is_mintable\u0026#39;, \u0026#39;0\u0026#39;) == \u0026#39;1\u0026#39; sell_tax = float(data.get(\u0026#39;sell_tax\u0026#39;, \u0026#39;0\u0026#39;) or \u0026#39;0\u0026#39;) buy_tax = float(data.get(\u0026#39;buy_tax\u0026#39;, \u0026#39;0\u0026#39;) or \u0026#39;0\u0026#39;) return { \u0026#39;safe\u0026#39;: not honeypot and not mintable, # 卖税不作为否决 \u0026#39;honeypot\u0026#39;: honeypot, \u0026#39;mintable\u0026#39;: mintable, \u0026#39;sell_tax\u0026#39;: sell_tax, \u0026#39;buy_tax\u0026#39;: buy_tax } except: pass return {\u0026#39;safe\u0026#39;: False, \u0026#39;reason\u0026#39;: \u0026#39;无法检查\u0026#39;} # 无法检查时不推，宁可错过不踩坑 # ============================================================ # GMGN数据获取 # ============================================================ def gmgn_get(url): try: resp = requests.get(url, headers=GMGN_HEADERS, timeout=15) if resp.status_code == 200: return resp.json().get(\u0026#39;data\u0026#39;, {}) except: pass return {} def fetch_token_description(chain, address): \u0026#34;\u0026#34;\u0026#34;获取代币描述/故事 — 叙事雷达核心信息\u0026#34;\u0026#34;\u0026#34; desc = \u0026#39;\u0026#39; # SOL链：Pump.fun有最完整的description if chain in (\u0026#39;sol\u0026#39;, \u0026#39;solana\u0026#39;): try: r = requests.get(f\u0026#39;https://frontend-api-v3.pump.fun/coins/{address}\u0026#39;, timeout=8) if r.status_code == 200: data = r.json() desc = data.get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) or \u0026#39;\u0026#39; twitter = data.get(\u0026#39;twitter\u0026#39;, \u0026#39;\u0026#39;) or \u0026#39;\u0026#39; telegram = data.get(\u0026#39;telegram\u0026#39;, \u0026#39;\u0026#39;) or \u0026#39;\u0026#39; website = data.get(\u0026#39;website\u0026#39;, \u0026#39;\u0026#39;) or \u0026#39;\u0026#39; return { \u0026#39;description\u0026#39;: desc.strip(), \u0026#39;twitter\u0026#39;: twitter, \u0026#39;telegram\u0026#39;: telegram, \u0026#39;website\u0026#39;: website, } except: pass # 所有链：DEXScreener info字段（网站+社交链接） try: chain_dex = {\u0026#39;sol\u0026#39;: \u0026#39;solana\u0026#39;, \u0026#39;eth\u0026#39;: \u0026#39;ethereum\u0026#39;, \u0026#39;bsc\u0026#39;: \u0026#39;bsc\u0026#39;, \u0026#39;base\u0026#39;: \u0026#39;base\u0026#39;, \u0026#39;solana\u0026#39;: \u0026#39;solana\u0026#39;, \u0026#39;ethereum\u0026#39;: \u0026#39;ethereum\u0026#39;}.get(chain, chain) r = requests.get(f\u0026#39;https://api.dexscreener.com/latest/dex/tokens/{address}\u0026#39;, timeout=8) if r.status_code == 200: pairs = r.json().get(\u0026#39;pairs\u0026#39;, []) if pairs: info = pairs[0].get(\u0026#39;info\u0026#39;, {}) websites = info.get(\u0026#39;websites\u0026#39;, []) socials = info.get(\u0026#39;socials\u0026#39;, []) twitter = \u0026#39;\u0026#39; telegram = \u0026#39;\u0026#39; website = \u0026#39;\u0026#39; for s in socials: if s.get(\u0026#39;type\u0026#39;) == \u0026#39;twitter\u0026#39;: twitter = s.get(\u0026#39;url\u0026#39;, \u0026#39;\u0026#39;) elif s.get(\u0026#39;type\u0026#39;) == \u0026#39;telegram\u0026#39;: telegram = s.get(\u0026#39;url\u0026#39;, \u0026#39;\u0026#39;) for w in websites: if w.get(\u0026#39;label\u0026#39;, \u0026#39;\u0026#39;).lower() == \u0026#39;website\u0026#39;: website = w.get(\u0026#39;url\u0026#39;, \u0026#39;\u0026#39;) if not desc: # DEXScreener没有description但有社交信息 return { \u0026#39;description\u0026#39;: desc, \u0026#39;twitter\u0026#39;: twitter, \u0026#39;telegram\u0026#39;: telegram, \u0026#39;website\u0026#39;: website, } except: pass return {\u0026#39;description\u0026#39;: desc, \u0026#39;twitter\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;telegram\u0026#39;: \u0026#39;\u0026#39;, \u0026#39;website\u0026#39;: \u0026#39;\u0026#39;} def fetch_new_tokens(): \u0026#34;\u0026#34;\u0026#34;从GMGN获取各链新币 + 多维度覆盖\u0026#34;\u0026#34;\u0026#34; all_tokens = [] seen_addrs = set() for chain in [\u0026#39;eth\u0026#39;, \u0026#39;bsc\u0026#39;, \u0026#39;base\u0026#39;]: # 多维度拉数据，避免漏掉 urls = [ # 按创建时间 — 最新的币 f\u0026#39;https://gmgn.ai/defi/quotation/v1/rank/{chain}/swaps/1h?orderby=open_timestamp\u0026amp;direction=desc\u0026amp;limit=100\u0026#39;, # 按交易量 — 最活跃的币 f\u0026#39;https://gmgn.ai/defi/quotation/v1/rank/{chain}/swaps/1h?orderby=swaps\u0026amp;direction=desc\u0026amp;limit=50\u0026#39;, ] for url in urls: data = gmgn_get(url) tokens = data.get(\u0026#39;rank\u0026#39;, []) for t in tokens: addr = t.get(\u0026#39;address\u0026#39;, \u0026#39;\u0026#39;) if not addr or addr in seen_addrs: continue mc = t.get(\u0026#39;market_cap\u0026#39;, 0) or t.get(\u0026#39;fdv\u0026#39;, 0) or 0 liq = t.get(\u0026#39;liquidity\u0026#39;, 0) or 0 # 基本过滤：太小的不看 if mc \u0026lt; 1000 or liq \u0026lt; 500 or mc \u0026gt; 10000000: continue age_ts = t.get(\u0026#39;open_timestamp\u0026#39;, 0) age_h = (time.time() - age_ts) / 3600 if age_ts \u0026gt; 0 else 999 # 不限年龄 — 动量追踪核心逻辑：涨就推，不管新旧 seen_addrs.add(addr) all_tokens.append({ \u0026#39;address\u0026#39;: addr, \u0026#39;chain\u0026#39;: chain, \u0026#39;name\u0026#39;: t.get(\u0026#39;name\u0026#39;, \u0026#39;?\u0026#39;), \u0026#39;symbol\u0026#39;: t.get(\u0026#39;symbol\u0026#39;, \u0026#39;?\u0026#39;), \u0026#39;mc\u0026#39;: mc, \u0026#39;liq\u0026#39;: liq, \u0026#39;volume\u0026#39;: t.get(\u0026#39;volume\u0026#39;, 0) or 0, \u0026#39;holders\u0026#39;: t.get(\u0026#39;holder_count\u0026#39;, 0) or 0, \u0026#39;sm\u0026#39;: t.get(\u0026#39;smart_degen_count\u0026#39;, 0) or 0, \u0026#39;chg_1h\u0026#39;: t.get(\u0026#39;price_change_percent1h\u0026#39;, 0) or 0, \u0026#39;chg_24h\u0026#39;: t.get(\u0026#39;price_change_percent\u0026#39;, 0) or 0, \u0026#39;age_h\u0026#39;: age_h, \u0026#39;price\u0026#39;: t.get(\u0026#39;price\u0026#39;, 0), \u0026#39;buys_1h\u0026#39;: t.get(\u0026#39;buys\u0026#39;, 0) or 0, \u0026#39;sells_1h\u0026#39;: t.get(\u0026#39;sells\u0026#39;, 0) or 0, }) time.sleep(0.3) return all_tokens def fetch_flap_tokens(): \u0026#34;\u0026#34;\u0026#34; FLAP平台扫描 — BSC社区驱动型发射台 找形态：跌下来但有底部支撑（有庄在低位推） 特征：24h跌了，但1h企稳/反弹，买入\u0026gt;卖出，holders在涨 \u0026#34;\u0026#34;\u0026#34; data = gmgn_get( \u0026#39;https://gmgn.ai/defi/quotation/v1/rank/bsc/swaps/24h?launchpad=flap\u0026amp;orderby=volume\u0026amp;direction=desc\u0026amp;limit=30\u0026#39; ) tokens = data.get(\u0026#39;rank\u0026#39;, []) candidates = [] for t in tokens: addr = t.get(\u0026#39;address\u0026#39;, \u0026#39;\u0026#39;) if not addr: continue mc = t.get(\u0026#39;market_cap\u0026#39;, 0) or 0 liq = t.get(\u0026#39;liquidity\u0026#39;, 0) or 0 vol = t.get(\u0026#39;volume\u0026#39;, 0) or 0 holders = t.get(\u0026#39;holder_count\u0026#39;, 0) or 0 buys = t.get(\u0026#39;buys\u0026#39;, 0) or 0 sells = t.get(\u0026#39;sells\u0026#39;, 0) or 0 chg_1h = t.get(\u0026#39;price_change_percent1h\u0026#39;, 0) or 0 chg_24h = t.get(\u0026#39;price_change_percent\u0026#39;, 0) or 0 age_ts = t.get(\u0026#39;open_timestamp\u0026#39;, 0) age_h = (time.time() - age_ts) / 3600 if age_ts \u0026gt; 0 else 0 # 基本门槛 if mc \u0026lt; 1000 or liq \u0026lt; 500: continue if holders \u0026lt; 5: continue # 底部支撑形态判断： # 条件1: 24h跌了（或者涨幅有限），说明不是刚拉的 # 条件2: 1h跌幅小于24h跌幅，说明在企稳 # 条件3: 买入 \u0026gt; 卖出，有人在接 buy_ratio = buys / max(sells, 1) is_support = False reason = \u0026#39;\u0026#39; # 形态A: 24h跌了，1h在企稳/反弹 if chg_24h \u0026lt; -10 and chg_1h \u0026gt; chg_24h * 0.3: is_support = True reason = f\u0026#39;24h跌{chg_24h:.0f}%但1h企稳{chg_1h:+.0f}%\u0026#39; # 形态B: 24h微跌或横盘，1h微涨，买卖比健康 if -10 \u0026lt;= chg_24h \u0026lt;= 30 and chg_1h \u0026gt; -5 and buy_ratio \u0026gt; 1.1: is_support = True reason = f\u0026#39;底部横盘 买卖比{buy_ratio:.2f}\u0026#39; # 形态C: 大跌后强反弹 if chg_24h \u0026lt; -30 and chg_1h \u0026gt; 10: is_support = True reason = f\u0026#39;大跌{chg_24h:.0f}%后反弹{chg_1h:+.0f}%\u0026#39; if is_support and buy_ratio \u0026gt;= 1.0: candidates.append({ \u0026#39;address\u0026#39;: addr, \u0026#39;chain\u0026#39;: \u0026#39;bsc\u0026#39;, \u0026#39;name\u0026#39;: t.get(\u0026#39;name\u0026#39;, \u0026#39;?\u0026#39;), \u0026#39;symbol\u0026#39;: t.get(\u0026#39;symbol\u0026#39;, \u0026#39;?\u0026#39;), \u0026#39;mc\u0026#39;: mc, \u0026#39;liq\u0026#39;: liq, \u0026#39;volume\u0026#39;: vol, \u0026#39;holders\u0026#39;: holders, \u0026#39;sm\u0026#39;: 0, \u0026#39;chg_1h\u0026#39;: chg_1h, \u0026#39;chg_24h\u0026#39;: chg_24h, \u0026#39;age_h\u0026#39;: age_h, \u0026#39;price\u0026#39;: t.get(\u0026#39;price\u0026#39;, 0), \u0026#39;buys\u0026#39;: buys, \u0026#39;sells\u0026#39;: sells, \u0026#39;buy_ratio\u0026#39;: buy_ratio, \u0026#39;support_reason\u0026#39;: reason, \u0026#39;launchpad\u0026#39;: \u0026#39;flap\u0026#39;, }) # 按市值排序 candidates.sort(key=lambda x: x[\u0026#39;mc\u0026#39;], reverse=True) return candidates def format_flap_alert(token, desc_info=None): \u0026#34;\u0026#34;\u0026#34;FLAP低吸信号推送\u0026#34;\u0026#34;\u0026#34; msg = f\u0026#34;链上雷达 — FLAP低吸信号\\n\u0026#34; msg += f\u0026#34;链: BSC | 平台: FLAP\\n\\n\u0026#34; msg += f\u0026#34;{token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]})\\n\u0026#34; msg += f\u0026#34;`{token[\u0026#39;address\u0026#39;]}`\\n\\n\u0026#34; # 故事描述 desc = (desc_info or {}).get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) if desc: if len(desc) \u0026gt; 200: desc = desc[:200] + \u0026#39;...\u0026#39; msg += f\u0026#34;故事: {desc}\\n\\n\u0026#34; msg += f\u0026#34;形态: {token[\u0026#39;support_reason\u0026#39;]}\\n\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;市值 ${token[\u0026#39;mc\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;流动性 ${token[\u0026#39;liq\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;24h量 ${token[\u0026#39;volume\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;持有人 {token[\u0026#39;holders\u0026#39;]:\u0026gt;12,d}\\n\u0026#34; msg += f\u0026#34;买/卖 {token[\u0026#39;buys\u0026#39;]:\u0026gt;6,d}/{token[\u0026#39;sells\u0026#39;]:\u0026gt;6,d}\\n\u0026#34; msg += f\u0026#34;买卖比 {token[\u0026#39;buy_ratio\u0026#39;]:\u0026gt;12.2f}\\n\u0026#34; msg += f\u0026#34;1h涨幅 {token[\u0026#39;chg_1h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; msg += f\u0026#34;24h涨幅 {token[\u0026#39;chg_24h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += \u0026#34;\\nFLAP社区币 — 低吸进场信号\u0026#34; # 社交链接 links = [] if (desc_info or {}).get(\u0026#39;twitter\u0026#39;): links.append(f\u0026#34;\\nTwitter: {desc_info[\u0026#39;twitter\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;telegram\u0026#39;): links.append(f\u0026#34;TG: {desc_info[\u0026#39;telegram\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;website\u0026#39;): links.append(f\u0026#34;Web: {desc_info[\u0026#39;website\u0026#39;]}\u0026#34;) if links: msg += \u0026#39;\\n\u0026#39;.join(links) return msg # ============================================================ # 推送格式 # ============================================================ def format_musk_trump_alert(token, matched_kw, desc_info=None): \u0026#34;\u0026#34;\u0026#34;马斯克/川普叙事推送\u0026#34;\u0026#34;\u0026#34; chain_map = {\u0026#39;sol\u0026#39;: \u0026#39;SOL\u0026#39;, \u0026#39;eth\u0026#39;: \u0026#39;ETH\u0026#39;, \u0026#39;bsc\u0026#39;: \u0026#39;BSC\u0026#39;, \u0026#39;base\u0026#39;: \u0026#39;BASE\u0026#39;} ch = chain_map.get(token[\u0026#39;chain\u0026#39;], token[\u0026#39;chain\u0026#39;].upper()) msg = f\u0026#34;链上雷达 — 马斯克/川普概念\\n\u0026#34; msg += f\u0026#34;链: {ch}\\n\\n\u0026#34; msg += f\u0026#34;{token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]})\\n\u0026#34; msg += f\u0026#34;`{token[\u0026#39;address\u0026#39;]}`\\n\\n\u0026#34; # 叙事故事（核心！） desc = (desc_info or {}).get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) if desc: # 截取前200字符，避免太长 if len(desc) \u0026gt; 200: desc = desc[:200] + \u0026#39;...\u0026#39; msg += f\u0026#34;故事: {desc}\\n\\n\u0026#34; msg += f\u0026#34;命中关键词: {\u0026#39;, \u0026#39;.join(matched_kw[:5])}\\n\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;市值 ${token[\u0026#39;mc\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;流动性 ${token[\u0026#39;liq\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;1h涨幅 {token[\u0026#39;chg_1h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; if token.get(\u0026#39;sm\u0026#39;, 0) \u0026gt; 0: msg += f\u0026#34;聪明钱 {token[\u0026#39;sm\u0026#39;]:\u0026gt;12d}\\n\u0026#34; msg += f\u0026#34;币龄 {token[\u0026#39;age_h\u0026#39;]:\u0026gt;10.1f}h\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; # 社交链接 links = [] if (desc_info or {}).get(\u0026#39;twitter\u0026#39;): links.append(f\u0026#34;Twitter: {desc_info[\u0026#39;twitter\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;telegram\u0026#39;): links.append(f\u0026#34;TG: {desc_info[\u0026#39;telegram\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;website\u0026#39;): links.append(f\u0026#34;Web: {desc_info[\u0026#39;website\u0026#39;]}\u0026#34;) if links: msg += \u0026#39;\\n\u0026#39; + \u0026#39;\\n\u0026#39;.join(links) return msg def format_binance_cz_alert(token, matched_kw, desc_info=None): \u0026#34;\u0026#34;\u0026#34;币安/CZ叙事推送\u0026#34;\u0026#34;\u0026#34; msg = f\u0026#34;链上雷达 — 币安/CZ概念\\n\u0026#34; msg += f\u0026#34;链: BSC\\n\\n\u0026#34; msg += f\u0026#34;{token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]})\\n\u0026#34; msg += f\u0026#34;`{token[\u0026#39;address\u0026#39;]}`\\n\\n\u0026#34; # 叙事故事 desc = (desc_info or {}).get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) if desc: if len(desc) \u0026gt; 200: desc = desc[:200] + \u0026#39;...\u0026#39; msg += f\u0026#34;故事: {desc}\\n\\n\u0026#34; msg += f\u0026#34;命中关键词: {\u0026#39;, \u0026#39;.join(matched_kw[:5])}\\n\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;市值 ${token[\u0026#39;mc\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;流动性 ${token[\u0026#39;liq\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;1h涨幅 {token[\u0026#39;chg_1h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; msg += f\u0026#34;币龄 {token[\u0026#39;age_h\u0026#39;]:\u0026gt;10.1f}h\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; # 社交链接 links = [] if (desc_info or {}).get(\u0026#39;twitter\u0026#39;): links.append(f\u0026#34;Twitter: {desc_info[\u0026#39;twitter\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;telegram\u0026#39;): links.append(f\u0026#34;TG: {desc_info[\u0026#39;telegram\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;website\u0026#39;): links.append(f\u0026#34;Web: {desc_info[\u0026#39;website\u0026#39;]}\u0026#34;) if links: msg += \u0026#39;\\n\u0026#39; + \u0026#39;\\n\u0026#39;.join(links) return msg def format_novel_narrative_alert(token, theme, desc_info=None): \u0026#34;\u0026#34;\u0026#34;全新叙事推送 — 保留备用\u0026#34;\u0026#34;\u0026#34; return format_heating_narrative_alert(token, theme, 1, desc_info) def format_heating_narrative_alert(token, theme, count, desc_info=None): \u0026#34;\u0026#34;\u0026#34;叙事热点推送 — 同主题持续冒新币\u0026#34;\u0026#34;\u0026#34; chain_map = {\u0026#39;sol\u0026#39;: \u0026#39;SOL\u0026#39;, \u0026#39;eth\u0026#39;: \u0026#39;ETH\u0026#39;, \u0026#39;bsc\u0026#39;: \u0026#39;BSC\u0026#39;, \u0026#39;base\u0026#39;: \u0026#39;BASE\u0026#39;} ch = chain_map.get(token[\u0026#39;chain\u0026#39;], token[\u0026#39;chain\u0026#39;].upper()) msg = f\u0026#34;链上雷达 — 叙事热点\\n\u0026#34; msg += f\u0026#34;链: {ch}\\n\\n\u0026#34; msg += f\u0026#34;{token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]})\\n\u0026#34; msg += f\u0026#34;`{token[\u0026#39;address\u0026#39;]}`\\n\\n\u0026#34; # 叙事故事 desc = (desc_info or {}).get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) if desc: if len(desc) \u0026gt; 300: desc = desc[:300] + \u0026#39;...\u0026#39; msg += f\u0026#34;故事: {desc}\\n\\n\u0026#34; else: msg += f\u0026#34;叙事主题: {theme}\\n\\n\u0026#34; msg += f\u0026#34;同类概念已出现{count}个币 — 持续有人做\\n\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;市值 ${token[\u0026#39;mc\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;流动性 ${token[\u0026#39;liq\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;1h涨幅 {token[\u0026#39;chg_1h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; if token.get(\u0026#39;sm\u0026#39;, 0) \u0026gt; 0: msg += f\u0026#34;聪明钱 {token[\u0026#39;sm\u0026#39;]:\u0026gt;12d}\\n\u0026#34; msg += f\u0026#34;持有人 {token[\u0026#39;holders\u0026#39;]:\u0026gt;12d}\\n\u0026#34; msg += f\u0026#34;币龄 {token[\u0026#39;age_h\u0026#39;]:\u0026gt;10.1f}h\\n\u0026#34; msg += f\u0026#34;```\u0026#34; # 社交链接 links = [] if (desc_info or {}).get(\u0026#39;twitter\u0026#39;): links.append(f\u0026#34;\\nTwitter: {desc_info[\u0026#39;twitter\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;telegram\u0026#39;): links.append(f\u0026#34;TG: {desc_info[\u0026#39;telegram\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;website\u0026#39;): links.append(f\u0026#34;Web: {desc_info[\u0026#39;website\u0026#39;]}\u0026#34;) if links: msg += \u0026#39;\\n\u0026#39;.join(links) return msg # ============================================================ # 动量追踪器 — 持续上涨+放量检测 # ============================================================ def track_momentum(tokens): \u0026#34;\u0026#34;\u0026#34; 每轮扫描更新币的快照。 连续多轮市值上涨+成交量增加 = 动量信号，直接推。 \u0026#34;\u0026#34;\u0026#34; global MOMENTUM_TRACKER, MOMENTUM_PUSHED now = time.time() alerts = [] # 当前轮所有地址 current_addrs = set() for token in tokens: addr = token[\u0026#39;address\u0026#39;] mc = token[\u0026#39;mc\u0026#39;] vol = token.get(\u0026#39;volume\u0026#39;, 0) or 0 price = token.get(\u0026#39;price\u0026#39;, 0) or 0 buys = token.get(\u0026#39;buys_1h\u0026#39;, 0) or token.get(\u0026#39;buys\u0026#39;, 0) or 0 current_addrs.add(addr) # 基本门槛 if mc \u0026lt; 1000 or token.get(\u0026#39;liq\u0026#39;, 0) \u0026lt; 500 or mc \u0026gt; 10000000: continue # 记录快照 — 只有数据真正变化时才记录（GMGN有缓存） if addr not in MOMENTUM_TRACKER: MOMENTUM_TRACKER[addr] = [] snapshots = MOMENTUM_TRACKER[addr] # 跳过重复数据（跟上一次完全一样就不记录） if snapshots and snapshots[-1][\u0026#39;mc\u0026#39;] == mc and snapshots[-1][\u0026#39;vol\u0026#39;] == vol: continue # 数据没变，跳过 snapshots.append({ \u0026#39;ts\u0026#39;: now, \u0026#39;mc\u0026#39;: mc, \u0026#39;vol\u0026#39;: vol, \u0026#39;price\u0026#39;: price, \u0026#39;buys\u0026#39;: buys, }) # 只保留最近20个快照（约200秒） if len(snapshots) \u0026gt; 20: snapshots[:] = snapshots[-20:] # 至少需要3个快照才能判断 if len(snapshots) \u0026lt; MOMENTUM_CONSECUTIVE_UP: continue # 检测最近N轮是否持续涨 recent = snapshots[-MOMENTUM_CONSECUTIVE_UP:] consecutive_up = True total_gain = 0 for i in range(1, len(recent)): prev_mc = recent[i-1][\u0026#39;mc\u0026#39;] curr_mc = recent[i][\u0026#39;mc\u0026#39;] if prev_mc \u0026lt;= 0: consecutive_up = False break gain = (curr_mc - prev_mc) / prev_mc if gain \u0026lt;= 0: # 任何一轮没涨就不算 consecutive_up = False break total_gain += gain if not consecutive_up: continue # 连续涨了！检查放量（成交量在增） vol_increasing = True for i in range(1, len(recent)): if recent[i][\u0026#39;buys\u0026#39;] \u0026lt; recent[i-1][\u0026#39;buys\u0026#39;] * 0.8: # 允许小幅波动 vol_increasing = False break # 计算总涨幅 first_mc = recent[0][\u0026#39;mc\u0026#39;] last_mc = recent[-1][\u0026#39;mc\u0026#39;] pct_gain = ((last_mc - first_mc) / first_mc * 100) if first_mc \u0026gt; 0 else 0 # 推送条件：连续涨 + 涨幅\u0026gt;5% if pct_gain \u0026lt; 5: continue # 信号计数：同一个币每次触发信号，计数+1 push_info = MOMENTUM_PUSHED.get(addr, {\u0026#39;count\u0026#39;: 0, \u0026#39;last_ts\u0026#39;: 0, \u0026#39;last_mc\u0026#39;: 0}) # 必须比上次推送时市值还高才推（真的还在涨） if push_info[\u0026#39;count\u0026#39;] \u0026gt; 0 and last_mc \u0026lt;= push_info[\u0026#39;last_mc\u0026#39;]: continue push_info[\u0026#39;count\u0026#39;] += 1 push_info[\u0026#39;last_ts\u0026#39;] = now push_info[\u0026#39;last_mc\u0026#39;] = last_mc signal_count = push_info[\u0026#39;count\u0026#39;] # 安全检查 safety = check_token_safety(token[\u0026#39;chain\u0026#39;], addr) if not safety.get(\u0026#39;safe\u0026#39;): continue # 叙事分类 → 星级评分 category, matched_kw = classify_narrative(token[\u0026#39;name\u0026#39;], token[\u0026#39;symbol\u0026#39;], token[\u0026#39;chain\u0026#39;]) is_flap = token.get(\u0026#39;launchpad\u0026#39;) == \u0026#39;flap\u0026#39; if category == \u0026#39;musk_trump\u0026#39;: stars = 3 narrative_tag = f\u0026#34;马斯克/川普概念 ({\u0026#39;, \u0026#39;.join(matched_kw[:3])})\u0026#34; elif category == \u0026#39;binance_cz\u0026#39;: stars = 3 narrative_tag = f\u0026#34;币安/CZ概念 ({\u0026#39;, \u0026#39;.join(matched_kw[:3])})\u0026#34; elif category == \u0026#39;celebrity_viral\u0026#39;: stars = 2 narrative_tag = f\u0026#34;名人/热点 ({\u0026#39;, \u0026#39;.join(matched_kw[:3])})\u0026#34; elif is_flap: stars = 2 narrative_tag = \u0026#34;FLAP社区币\u0026#34; else: # 检查是否全新叙事 theme = normalize_theme(token[\u0026#39;name\u0026#39;], token[\u0026#39;symbol\u0026#39;]) theme_words = [w for w in theme.split() if w not in COMMON_NOISE_WORDS and len(w) \u0026gt; 2] if len(theme_words) \u0026gt;= 2: stars = 2 narrative_tag = f\u0026#34;叙事: {theme}\u0026#34; else: stars = 1 narrative_tag = \u0026#34;无明确叙事\u0026#34; # 生成推送 desc_info = fetch_token_description(token[\u0026#39;chain\u0026#39;], addr) # FLAP币额外标注社区/CTO信息 if is_flap: has_twitter = bool(desc_info.get(\u0026#39;twitter\u0026#39;)) has_tg = bool(desc_info.get(\u0026#39;telegram\u0026#39;)) has_web = bool(desc_info.get(\u0026#39;website\u0026#39;)) community_tags = [] if has_twitter: community_tags.append(\u0026#34;有推特\u0026#34;) if has_tg: community_tags.append(\u0026#34;有TG群\u0026#34;) if has_web: community_tags.append(\u0026#34;有官网\u0026#34;) if community_tags: narrative_tag += f\u0026#34; | {\u0026#39; \u0026#39;.join(community_tags)}\u0026#34; stars = min(3, stars + 1) # 有社区加一星 else: narrative_tag += \u0026#34; | 无社区链接\u0026#34; msg = format_momentum_alert(token, pct_gain, len(recent), vol_increasing, stars, narrative_tag, desc_info, signal_count) alerts.append({\u0026#39;msg\u0026#39;: msg, \u0026#39;token\u0026#39;: token}) MOMENTUM_PUSHED[addr] = push_info log(f\u0026#34;[动量信号{signal_count}] {token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]}) on {token[\u0026#39;chain\u0026#39;]} — 连涨{len(recent)}轮 +{pct_gain:.1f}%\u0026#34;) # 清理不再出现的币 stale = [a for a in MOMENTUM_TRACKER if a not in current_addrs] for a in stale: if now - MOMENTUM_TRACKER[a][-1][\u0026#39;ts\u0026#39;] \u0026gt; 600: # 10分钟没出现就清理 del MOMENTUM_TRACKER[a] # 清理推送记录 — 1小时没出现的清掉 MOMENTUM_PUSHED = {k: v for k, v in MOMENTUM_PUSHED.items() if now - v.get(\u0026#39;last_ts\u0026#39;, 0) \u0026lt; 3600} return alerts def format_momentum_alert(token, pct_gain, rounds, vol_up, stars, narrative_tag, desc_info=None, seen_count=0): \u0026#34;\u0026#34;\u0026#34;持续上涨动量推送 — 带叙事星级\u0026#34;\u0026#34;\u0026#34; chain_map = {\u0026#39;sol\u0026#39;: \u0026#39;SOL\u0026#39;, \u0026#39;eth\u0026#39;: \u0026#39;ETH\u0026#39;, \u0026#39;bsc\u0026#39;: \u0026#39;BSC\u0026#39;, \u0026#39;base\u0026#39;: \u0026#39;BASE\u0026#39;} ch = chain_map.get(token[\u0026#39;chain\u0026#39;], token[\u0026#39;chain\u0026#39;].upper()) vol_tag = \u0026#34;放量\u0026#34; if vol_up else \u0026#34;\u0026#34; star_str = \u0026#34;★\u0026#34; * stars + \u0026#34;☆\u0026#34; * (3 - stars) # 信号编号从标题移到下面 msg = f\u0026#34;链上雷达\\n\u0026#34; msg += f\u0026#34;链: {ch}\\n\\n\u0026#34; msg += f\u0026#34;{token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]})\\n\u0026#34; msg += f\u0026#34;`{token[\u0026#39;address\u0026#39;]}`\\n\\n\u0026#34; # 故事描述 desc = (desc_info or {}).get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) if desc: if len(desc) \u0026gt; 200: desc = desc[:200] + \u0026#39;...\u0026#39; msg += f\u0026#34;故事: {desc}\\n\\n\u0026#34; msg += f\u0026#34;叙事: {narrative_tag}\\n\u0026#34; msg += f\u0026#34;连涨{rounds}轮 +{pct_gain:.1f}% {vol_tag}\\n\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;市值 ${token[\u0026#39;mc\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;流动性 ${token[\u0026#39;liq\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;1h涨幅 {token[\u0026#39;chg_1h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; if token.get(\u0026#39;sm\u0026#39;, 0) \u0026gt; 0: msg += f\u0026#34;聪明钱 {token[\u0026#39;sm\u0026#39;]:\u0026gt;12d}\\n\u0026#34; msg += f\u0026#34;币龄 {token[\u0026#39;age_h\u0026#39;]:\u0026gt;10.1f}h\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;评星: {star_str} 出现次数: {seen_count}\u0026#34; # 社交链接 links = [] if (desc_info or {}).get(\u0026#39;twitter\u0026#39;): links.append(f\u0026#34;\\nTwitter: {desc_info[\u0026#39;twitter\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;telegram\u0026#39;): links.append(f\u0026#34;TG: {desc_info[\u0026#39;telegram\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;website\u0026#39;): links.append(f\u0026#34;Web: {desc_info[\u0026#39;website\u0026#39;]}\u0026#34;) if links: msg += \u0026#39;\\n\u0026#39;.join(links) return msg def format_celebrity_alert(token, matched_kw, desc_info=None): \u0026#34;\u0026#34;\u0026#34;名人/推特热点推送 ★★\u0026#34;\u0026#34;\u0026#34; chain_map = {\u0026#39;sol\u0026#39;: \u0026#39;SOL\u0026#39;, \u0026#39;eth\u0026#39;: \u0026#39;ETH\u0026#39;, \u0026#39;bsc\u0026#39;: \u0026#39;BSC\u0026#39;, \u0026#39;base\u0026#39;: \u0026#39;BASE\u0026#39;} ch = chain_map.get(token[\u0026#39;chain\u0026#39;], token[\u0026#39;chain\u0026#39;].upper()) msg = f\u0026#34;链上雷达 — 名人/热点 ★★\\n\u0026#34; msg += f\u0026#34;链: {ch}\\n\\n\u0026#34; msg += f\u0026#34;{token[\u0026#39;name\u0026#39;]} ({token[\u0026#39;symbol\u0026#39;]})\\n\u0026#34; msg += f\u0026#34;`{token[\u0026#39;address\u0026#39;]}`\\n\\n\u0026#34; desc = (desc_info or {}).get(\u0026#39;description\u0026#39;, \u0026#39;\u0026#39;) if desc: if len(desc) \u0026gt; 200: desc = desc[:200] + \u0026#39;...\u0026#39; msg += f\u0026#34;故事: {desc}\\n\\n\u0026#34; msg += f\u0026#34;命中关键词: {\u0026#39;, \u0026#39;.join(matched_kw[:5])}\\n\\n\u0026#34; msg += f\u0026#34;```\\n\u0026#34; msg += f\u0026#34;市值 ${token[\u0026#39;mc\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;流动性 ${token[\u0026#39;liq\u0026#39;]:\u0026gt;12,.0f}\\n\u0026#34; msg += f\u0026#34;1h涨幅 {token[\u0026#39;chg_1h\u0026#39;]:\u0026gt;+11.1f}%\\n\u0026#34; if token.get(\u0026#39;sm\u0026#39;, 0) \u0026gt; 0: msg += f\u0026#34;聪明钱 {token[\u0026#39;sm\u0026#39;]:\u0026gt;12d}\\n\u0026#34; msg += f\u0026#34;币龄 {token[\u0026#39;age_h\u0026#39;]:\u0026gt;10.1f}h\\n\u0026#34; msg += f\u0026#34;```\u0026#34; links = [] if (desc_info or {}).get(\u0026#39;twitter\u0026#39;): links.append(f\u0026#34;\\nTwitter: {desc_info[\u0026#39;twitter\u0026#39;]}\u0026#34;) if (desc_info or {}).get(\u0026#39;telegram\u0026#39;): links.append(f\u0026#34;TG: {desc_info[\u0026#39;telegram\u0026#39;]}\u0026#34;) if links: msg += \u0026#39;\\n\u0026#39;.join(links) return msg # ============================================================ # 核心扫描逻辑 # ============================================================ def scan_narratives(): \u0026#34;\u0026#34;\u0026#34;主扫描函数\u0026#34;\u0026#34;\u0026#34; conn = init_db() tokens = fetch_new_tokens() log(f\u0026#34;扫描 {len(tokens)} 个新币...\u0026#34;) # === 动量追踪 — 每轮更新所有币的快照，检测持续上涨 === # 拉FLAP币一起喂进动量追踪器 flap_tokens = [] try: flap_tokens = fetch_flap_tokens() except: pass all_momentum_tokens = tokens + flap_tokens momentum_alerts = track_momentum(all_momentum_tokens) for token in tokens: addr = token[\u0026#39;address\u0026#39;] chain = token[\u0026#39;chain\u0026#39;] name = token[\u0026#39;name\u0026#39;] symbol = token[\u0026#39;symbol\u0026#39;] # 已扫描过的 — 更新seen_count和narratives的token_count，但不重复推 if is_token_seen(conn, addr): # 更新seen_count c = conn.cursor() c.execute(\u0026#39;UPDATE tokens_seen SET seen_count = seen_count + 1, market_cap = ? WHERE address = ?\u0026#39;, (token[\u0026#39;mc\u0026#39;], addr)) # 更新narratives表的token_count（按主题） theme_tmp = normalize_theme(name, symbol) if theme_tmp: c.execute(\u0026#39;UPDATE narratives SET token_count = token_count + 1, last_seen_at = ? WHERE theme = ?\u0026#39;, (int(time.time()), theme_tmp)) conn.commit() continue # 分类叙事 category, matched_kw = classify_narrative(name, symbol, chain) if category == \u0026#39;spam\u0026#39;: record_token(conn, addr, chain, name, symbol, \u0026#39;\u0026#39;, \u0026#39;spam\u0026#39;, token[\u0026#39;mc\u0026#39;]) continue # 基本质量门槛（防止推太多垃圾） min_mc = 1000 min_liq = 500 if token[\u0026#39;mc\u0026#39;] \u0026lt; min_mc or token[\u0026#39;liq\u0026#39;] \u0026lt; min_liq: record_token(conn, addr, chain, name, symbol, \u0026#39;\u0026#39;, \u0026#39;too_small\u0026#39;, token[\u0026#39;mc\u0026#39;]) continue theme = normalize_theme(name, symbol) # 所有分类只记录，不直接推送 — 推送统一走动量引擎 record_token(conn, addr, chain, name, symbol, theme, category, token[\u0026#39;mc\u0026#39;]) check_narrative_novelty(conn, theme, name, symbol, addr, chain) conn.close() # === 推送动量信号 === pushed = 0 for ma in momentum_alerts[:8]: # 单轮最多推8个 if tg_send(ma[\u0026#39;msg\u0026#39;]): pushed += 1 time.sleep(1) # 避免TG限流 return pushed, len(momentum_alerts) # ============================================================ # 主循环 # ============================================================ def main(): log(\u0026#34;=\u0026#34; * 50) log(\u0026#34;链上雷达 v1 启动\u0026#34;) log(f\u0026#34;扫描间隔: {SCAN_INTERVAL}s\u0026#34;) log(f\u0026#34;推送逻辑: 动量优先 — 连涨才推，叙事只做分类标签\u0026#34;) log(\u0026#34;=\u0026#34; * 50) # 初始化DB init_db() # 启动通知 tg_send( \u0026#34;链上雷达 v1 已启动\\n\\n\u0026#34; \u0026#34;核心逻辑: 动量优先\\n\u0026#34; \u0026#34;连涨3轮+涨幅\u0026gt;5%才推送\\n\u0026#34; \u0026#34;叙事只做分类标签:\\n\u0026#34; \u0026#34;★★★ 马斯克/川普 | 币安/CZ | FLAP有社区\\n\u0026#34; \u0026#34;★★ 名人热点 | FLAP无社区 | 有叙事\\n\u0026#34; \u0026#34;★ 无明确叙事\\n\\n\u0026#34; f\u0026#34;扫描频率: 每{SCAN_INTERVAL}秒\u0026#34; ) scan_count = 0 total_pushed = 0 while True: try: scan_count += 1 pushed, found = scan_narratives() total_pushed += pushed if pushed \u0026gt; 0: log(f\u0026#34;第{scan_count}轮: 发现{found}个, 推送{pushed}个 (累计推送{total_pushed})\u0026#34;) else: if scan_count % 20 == 0: # 每20轮报一次无信号 log(f\u0026#34;第{scan_count}轮: 无新信号 (累计推送{total_pushed})\u0026#34;) except Exception as e: log(f\u0026#34;扫描异常: {e}\u0026#34;) time.sleep(SCAN_INTERVAL) if __name__ == \u0026#39;__main__\u0026#39;: main() OI + 费率转负扫描器 发布日期: 2026.04.25　标签: Python · Binance Futures · Telegram\n费率正转负 + OI 放大 · 实时扫描\n快照对比：费率刚从正变负且OI在涨时秒推TG。每5分钟运行，零AI成本。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; OI持续放大 + 费率由正转负 扫描器 - 每分钟运行一次 - 检测: OI持续放大(4段递增, 总涨幅\u0026gt;8%) + 费率由正转负 - 去重: 同一币种24小时内只推一次 - 纯API零成本 \u0026#34;\u0026#34;\u0026#34; import requests import json import os import time import sys from datetime import datetime, timedelta from pathlib import Path # ============ 配置 ============ SCRIPT_DIR = Path(__file__).parent ENV_FILE = SCRIPT_DIR / \u0026#34;.env.oi\u0026#34; ALERT_HISTORY_FILE = SCRIPT_DIR / \u0026#34;oi_funding_alerts.json\u0026#34; FR_SNAPSHOT_FILE = SCRIPT_DIR / \u0026#34;fr_snapshot.json\u0026#34; # 上一次费率快照 # 信号参数 MIN_OI_CHANGE_PCT = 8 # OI总涨幅最低8% MIN_VOLUME_USDT = 0 # 无门槛，全扫 MIN_FR_PERIODS_POSITIVE = 2 # 转负前至少2期为正 DEDUP_HOURS = 24 # 去重窗口24小时 # ============ 加载TG配置 ============ def load_env(): env = {} if ENV_FILE.exists(): for line in ENV_FILE.read_text().strip().split(\u0026#39;\\n\u0026#39;): if \u0026#39;=\u0026#39; in line and not line.startswith(\u0026#39;#\u0026#39;): k, v = line.split(\u0026#39;=\u0026#39;, 1) env[k.strip()] = v.strip() return env env = load_env() TG_BOT_TOKEN = env.get(\u0026#39;TG_BOT_TOKEN\u0026#39;, \u0026#39;\u0026#39;) TG_CHAT_ID = env.get(\u0026#39;TG_CHAT_ID\u0026#39;, \u0026#39;\u0026#39;) # ============ TG推送 ============ def send_tg(text): if not TG_BOT_TOKEN or not TG_CHAT_ID: print(\u0026#34;[TG] 未配置, 仅打印:\u0026#34;) print(text) return url = f\u0026#34;https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage\u0026#34; # 分段发送(TG限制4096字) chunks = [text[i:i+4000] for i in range(0, len(text), 4000)] for chunk in chunks: try: resp = requests.post(url, json={ \u0026#39;chat_id\u0026#39;: TG_CHAT_ID, \u0026#39;text\u0026#39;: chunk, \u0026#39;parse_mode\u0026#39;: \u0026#39;Markdown\u0026#39; }, timeout=10) if resp.status_code != 200: # fallback无格式 requests.post(url, json={ \u0026#39;chat_id\u0026#39;: TG_CHAT_ID, \u0026#39;text\u0026#39;: chunk }, timeout=10) except Exception as e: print(f\u0026#34;[TG] 发送失败: {e}\u0026#34;) # ============ 去重 ============ def load_alert_history(): if ALERT_HISTORY_FILE.exists(): try: return json.loads(ALERT_HISTORY_FILE.read_text()) except: return {} return {} def save_alert_history(history): ALERT_HISTORY_FILE.write_text(json.dumps(history)) def is_duplicate(symbol, history): if symbol not in history: return False last_alert = datetime.fromisoformat(history[symbol]) return (datetime.now() - last_alert).total_seconds() \u0026lt; DEDUP_HOURS * 3600 def mark_alerted(symbol, history): history[symbol] = datetime.now().isoformat() # 清理过期记录 cutoff = datetime.now() - timedelta(hours=DEDUP_HOURS * 2) history = {k: v for k, v in history.items() if datetime.fromisoformat(v) \u0026gt; cutoff} return history # ============ 费率快照 ============ def load_fr_snapshot(): if FR_SNAPSHOT_FILE.exists(): try: return json.loads(FR_SNAPSHOT_FILE.read_text()) except: pass return {} def save_fr_snapshot(snapshot): FR_SNAPSHOT_FILE.write_text(json.dumps(snapshot)) # ============ 核心扫描 ============ def scan(): ts_start = time.time() # 1. 获取所有永续合约 try: info = requests.get(\u0026#39;https://fapi.binance.com/fapi/v1/exchangeInfo\u0026#39;, timeout=10).json() symbols = [s[\u0026#39;symbol\u0026#39;] for s in info[\u0026#39;symbols\u0026#39;] if s[\u0026#39;contractType\u0026#39;] == \u0026#39;PERPETUAL\u0026#39; and s[\u0026#39;quoteAsset\u0026#39;] == \u0026#39;USDT\u0026#39; and s[\u0026#39;status\u0026#39;] == \u0026#39;TRADING\u0026#39;] except Exception as e: print(f\u0026#34;[ERROR] exchangeInfo: {e}\u0026#34;) return [] # 2. 批量获取24h行情(过滤低量币) try: tickers = requests.get(\u0026#39;https://fapi.binance.com/fapi/v1/ticker/24hr\u0026#39;, timeout=10).json() ticker_map = {t[\u0026#39;symbol\u0026#39;]: t for t in tickers} except Exception as e: print(f\u0026#34;[ERROR] ticker: {e}\u0026#34;) return [] active = [s for s in symbols if float(ticker_map.get(s, {}).get(\u0026#39;quoteVolume\u0026#39;, 0)) \u0026gt; MIN_VOLUME_USDT] # 3. 批量获取当前费率 (一次拿全部) try: fr_all = requests.get(\u0026#39;https://fapi.binance.com/fapi/v1/premiumIndex\u0026#39;, timeout=10).json() fr_current = {item[\u0026#39;symbol\u0026#39;]: float(item[\u0026#39;lastFundingRate\u0026#39;]) for item in fr_all} except: fr_current = {} # 4. 加载上次快照，对比找\u0026#34;刚转负\u0026#34;的 prev_snapshot = load_fr_snapshot() # 保存本次快照(供下次对比) save_fr_snapshot(fr_current) if not prev_snapshot: print(f\u0026#34;[{datetime.now().strftime(\u0026#39;%H:%M:%S\u0026#39;)}] 首次运行，保存快照，下次开始对比\u0026#34;) return [] # 找出: 上次\u0026gt;=0, 这次\u0026lt;0 的币 just_turned_negative = [] for sym in active: prev_fr = prev_snapshot.get(sym) curr_fr = fr_current.get(sym) if prev_fr is None or curr_fr is None: continue if prev_fr \u0026gt;= 0 and curr_fr \u0026lt; 0: just_turned_negative.append(sym) if not just_turned_negative: elapsed = time.time() - ts_start print(f\u0026#34;[{datetime.now().strftime(\u0026#39;%H:%M:%S\u0026#39;)}] 扫描完成: {len(active)}币/{elapsed:.1f}s, 无新转负\u0026#34;) return [] print(f\u0026#34;[{datetime.now().strftime(\u0026#39;%H:%M:%S\u0026#39;)}] 发现 {len(just_turned_negative)} 个刚转负: {just_turned_negative}\u0026#34;) # 5. 只对刚转负的币查OI signals = [] for sym in just_turned_negative: try: # OI历史 oi_hist = requests.get(\u0026#39;https://fapi.binance.com/futures/data/openInterestHist\u0026#39;, params={\u0026#39;symbol\u0026#39;: sym, \u0026#39;period\u0026#39;: \u0026#39;1h\u0026#39;, \u0026#39;limit\u0026#39;: 48}, timeout=10).json() oi_chg = 0 segs = [] oi_rising = False if oi_hist and len(oi_hist) \u0026gt;= 12: oi_values = [float(x[\u0026#39;sumOpenInterestValue\u0026#39;]) for x in oi_hist] seg_len = len(oi_values) // 4 if seg_len \u0026gt;= 3: segs = [ sum(oi_values[:seg_len]) / seg_len, sum(oi_values[seg_len:seg_len*2]) / seg_len, sum(oi_values[seg_len*2:seg_len*3]) / seg_len, sum(oi_values[seg_len*3:]) / max(1, len(oi_values[seg_len*3:])) ] oi_chg = (segs[3] - segs[0]) / segs[0] * 100 if segs[0] \u0026gt; 0 else 0 oi_rising = oi_chg \u0026gt; 0 t = ticker_map.get(sym, {}) signals.append({ \u0026#39;symbol\u0026#39;: sym, \u0026#39;price\u0026#39;: float(t.get(\u0026#39;lastPrice\u0026#39;, 0)), \u0026#39;price_chg_24h\u0026#39;: float(t.get(\u0026#39;priceChangePercent\u0026#39;, 0)), \u0026#39;volume\u0026#39;: float(t.get(\u0026#39;quoteVolume\u0026#39;, 0)), \u0026#39;oi_change\u0026#39;: oi_chg, \u0026#39;oi_segments\u0026#39;: segs, \u0026#39;oi_rising\u0026#39;: oi_rising, \u0026#39;current_fr\u0026#39;: fr_current.get(sym, 0), \u0026#39;prev_fr\u0026#39;: prev_snapshot.get(sym, 0), }) except: continue elapsed = time.time() - ts_start print(f\u0026#34;[{datetime.now().strftime(\u0026#39;%H:%M:%S\u0026#39;)}] 扫描完成: {len(active)}币/{elapsed:.1f}s, 信号: {len(signals)}\u0026#34;) return signals # ============ 附加信息 ============ def get_square_discussion(coin): \u0026#34;\u0026#34;\u0026#34;查询币安广场该币的帖子数和浏览量\u0026#34;\u0026#34;\u0026#34; try: r = requests.get( \u0026#34;https://www.binance.com/bapi/composite/v4/friendly/pgc/content/queryByHashtag\u0026#34;, params={\u0026#34;hashtag\u0026#34;: f\u0026#34;#{coin.lower()}\u0026#34;, \u0026#34;pageIndex\u0026#34;: 1, \u0026#34;pageSize\u0026#34;: 1, \u0026#34;orderBy\u0026#34;: \u0026#34;HOT\u0026#34;}, headers={\u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0\u0026#34;, \u0026#34;Referer\u0026#34;: \u0026#34;https://www.binance.com/en/square\u0026#34;}, timeout=8 ) if r.status_code == 200: ht = r.json().get(\u0026#34;data\u0026#34;, {}).get(\u0026#34;hashtag\u0026#34;, {}) return ht.get(\u0026#34;contentCount\u0026#34;, 0), ht.get(\u0026#34;viewCount\u0026#34;, 0) except: pass return 0, 0 def get_market_caps(): \u0026#34;\u0026#34;\u0026#34;获取币安流通市值\u0026#34;\u0026#34;\u0026#34; mcap = {} try: r = requests.get( \u0026#34;https://www.binance.com/bapi/composite/v1/public/marketing/symbol/list\u0026#34;, timeout=10 ) if r.status_code == 200: for item in r.json().get(\u0026#34;data\u0026#34;, []): name = item.get(\u0026#34;name\u0026#34;, \u0026#34;\u0026#34;) mc = item.get(\u0026#34;marketCap\u0026#34;, 0) if name and mc: mcap[name] = float(mc) except: pass return mcap def get_spot_symbols(): \u0026#34;\u0026#34;\u0026#34;获取有现货的币种\u0026#34;\u0026#34;\u0026#34; try: info = requests.get(\u0026#34;https://api.binance.com/api/v3/exchangeInfo\u0026#34;, timeout=10).json() return {s[\u0026#34;baseAsset\u0026#34;] for s in info[\u0026#34;symbols\u0026#34;] if s[\u0026#34;quoteAsset\u0026#34;] == \u0026#34;USDT\u0026#34; and s[\u0026#34;status\u0026#34;] == \u0026#34;TRADING\u0026#34;} except: return set() def fmt_mcap(v): if v \u0026gt;= 1e9: return f\u0026#34;${v/1e9:.2f}B\u0026#34; if v \u0026gt;= 1e6: return f\u0026#34;${v/1e6:.1f}M\u0026#34; if v \u0026gt;= 1e3: return f\u0026#34;${v/1e3:.0f}K\u0026#34; return f\u0026#34;${v:.0f}\u0026#34; def fmt_views(v): if v \u0026gt;= 1e6: return f\u0026#34;{v/1e6:.1f}M\u0026#34; if v \u0026gt;= 1e3: return f\u0026#34;{v/1e3:.0f}K\u0026#34; return str(v) # ============ 格式化推送 ============ def format_alert(signals): if not signals: return None # OI在涨的排前面，同组内按费率绝对值排序 signals.sort(key=lambda x: (-int(x.get(\u0026#39;oi_rising\u0026#39;, False)), x[\u0026#39;current_fr\u0026#39;])) # 批量获取附加信息 mcap_map = get_market_caps() spot_set = get_spot_symbols() now = datetime.now().strftime(\u0026#39;%m-%d %H:%M\u0026#39;) lines = [f\u0026#34;*[ 费率刚转负+OI涨 ]* {now}\\n\u0026#34;] for s in signals: coin = s[\u0026#39;symbol\u0026#39;].replace(\u0026#39;USDT\u0026#39;, \u0026#39;\u0026#39;) # 费率: 上期→本期 fr_change = f\u0026#34;{s[\u0026#39;prev_fr\u0026#39;]:+.4%} -\u0026gt; {s[\u0026#39;current_fr\u0026#39;]:+.4%}\u0026#34; # 附加信息 mcap = mcap_map.get(coin, 0) has_spot = coin in spot_set sq_posts, sq_views = get_square_discussion(coin) lines.append(f\u0026#34;```\u0026#34;) lines.append(f\u0026#34;{coin}\u0026#34;) lines.append(f\u0026#34; 价格: {s[\u0026#39;price\u0026#39;]:.4f} 24h: {s[\u0026#39;price_chg_24h\u0026#39;]:+.1f}%\u0026#34;) lines.append(f\u0026#34; 费率: {fr_change}\u0026#34;) if s[\u0026#39;oi_segments\u0026#39;]: oi_segs = \u0026#39; \u0026gt; \u0026#39;.join([f\u0026#34;{v/1e6:.1f}M\u0026#34; for v in s[\u0026#39;oi_segments\u0026#39;]]) lines.append(f\u0026#34; OI: +{s[\u0026#39;oi_change\u0026#39;]:.1f}% ({oi_segs})\u0026#34;) lines.append(f\u0026#34; 成交额: ${s[\u0026#39;volume\u0026#39;]/1e6:.1f}M\u0026#34;) lines.append(f\u0026#34; 市值: {fmt_mcap(mcap) if mcap \u0026gt; 0 else \u0026#39;未知\u0026#39;} 现货: {\u0026#39;有\u0026#39; if has_spot else \u0026#39;仅合约\u0026#39;}\u0026#34;) if sq_posts \u0026gt; 0: lines.append(f\u0026#34; 广场: {sq_posts}帖 / {fmt_views(sq_views)}浏览\u0026#34;) else: lines.append(f\u0026#34; 广场: 无讨论\u0026#34;) lines.append(f\u0026#34;```\u0026#34;) return \u0026#39;\\n\u0026#39;.join(lines) # ============ 主逻辑 ============ def main(): signals = scan() if signals: # 只推: 费率当前为负 + OI在涨 (最强组合) strong = [s for s in signals if s[\u0026#39;current_fr\u0026#39;] \u0026lt; 0 and s.get(\u0026#39;oi_rising\u0026#39;)] if strong: msg = format_alert(strong) if msg: send_tg(msg) print(f\u0026#34; 推送 {len(strong)} 个信号 (总{len(signals)}个转负, {len(strong)}个OI也涨)\u0026#34;) else: print(f\u0026#34; {len(signals)} 个转负但无OI在涨的, 跳过\u0026#34;) else: print(f\u0026#34; 无信号\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: main() 热度做多雷达 发布日期: 2026.04.25　标签: Python · Binance · CoinGlass · Telegram\n热度排行 + OI 异动 + 智能推送\n每小时扫描：热度币追踪、OI异动检测、TG推送。纯Python零AI成本。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; 热度做多雷达 v2 — 热度+费率+OI 三维扫描 核心逻辑（拉哪模式）： 1. 热度先行 → CG热搜+放量=资金涌入信号 2. 负费率=空头燃料，庄家拉盘爆空单 3. OI暴涨=大资金建仓=即将拉盘 单策略：发现热度→小仓做多→严格止损→拿住赢家 数据源：币安合约API + CoinGecko Trending（零成本） \u0026#34;\u0026#34;\u0026#34; import json import os import sys import time import requests from datetime import datetime, timezone, timedelta from pathlib import Path from square_heat import get_square_heat # === 加载 .env === env_file = Path(__file__).parent / \u0026#34;.env.oi\u0026#34; if env_file.exists(): with open(env_file) as f: for line in f: line = line.strip() if line and not line.startswith(\u0026#34;#\u0026#34;) and \u0026#34;=\u0026#34; in line: k, v = line.split(\u0026#34;=\u0026#34;, 1) os.environ.setdefault(k.strip(), v.strip()) # === 配置 === TG_BOT_TOKEN = os.getenv(\u0026#34;TG_BOT_TOKEN\u0026#34;, \u0026#34;\u0026#34;) TG_CHAT_ID = os.getenv(\u0026#34;TG_CHAT_ID\u0026#34;, \u0026#34;YOUR_CHAT_ID\u0026#34;) FAPI = \u0026#34;https://fapi.binance.com\u0026#34; # 热度历史记录（用于检测首次上榜） HEAT_HISTORY_FILE = Path(__file__).parent / \u0026#34;heat_history.json\u0026#34; # 热度参数 VOL_SURGE_MULT = 2.5 # 成交量放大2.5倍以上=放量 MIN_VOL_USD = 20_000_000 # 日均成交\u0026gt;$20M才检测放量 # OI异动参数 MIN_OI_DELTA_PCT = 3.0 # OI变化至少3% MIN_OI_USD = 2_000_000 # 最低OI门槛 $2M def api_get(endpoint, params=None): \u0026#34;\u0026#34;\u0026#34;币安API请求\u0026#34;\u0026#34;\u0026#34; url = f\u0026#34;{FAPI}{endpoint}\u0026#34; for attempt in range(3): try: resp = requests.get(url, params=params, timeout=10) if resp.status_code == 200: return resp.json() elif resp.status_code == 429: time.sleep(2) else: return None except: time.sleep(1) return None def format_usd(v): if v \u0026gt;= 1e9: return f\u0026#34;${v/1e9:.1f}B\u0026#34; if v \u0026gt;= 1e6: return f\u0026#34;${v/1e6:.1f}M\u0026#34; if v \u0026gt;= 1e3: return f\u0026#34;${v/1e3:.0f}K\u0026#34; return f\u0026#34;${v:.0f}\u0026#34; def mcap_str(v): if v \u0026gt;= 1e9: return f\u0026#34;${v/1e9:.1f}B\u0026#34; if v \u0026gt;= 1e6: return f\u0026#34;${v/1e6:.0f}M\u0026#34; if v \u0026gt;= 1e3: return f\u0026#34;${v/1e3:.0f}K\u0026#34; return f\u0026#34;${v:.0f}\u0026#34; def send_telegram(text): \u0026#34;\u0026#34;\u0026#34;发送TG消息\u0026#34;\u0026#34;\u0026#34; if not TG_BOT_TOKEN: print(\u0026#34;\\n[TG] No token, stdout:\\n\u0026#34;) print(text) return url = f\u0026#34;https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage\u0026#34; # 分段发送（TG限制4096字） chunks = [] current = \u0026#34;\u0026#34; for line in text.split(\u0026#34;\\n\u0026#34;): if len(current) + len(line) + 1 \u0026gt; 3800: chunks.append(current) current = line else: current += \u0026#34;\\n\u0026#34; + line if current else line if current: chunks.append(current) for chunk in chunks: try: resp = requests.post(url, json={ \u0026#34;chat_id\u0026#34;: TG_CHAT_ID, \u0026#34;text\u0026#34;: chunk, \u0026#34;parse_mode\u0026#34;: \u0026#34;Markdown\u0026#34; }, timeout=10) if resp.status_code == 200: print(f\u0026#34;[TG] Sent ✓ ({len(chunk)} chars)\u0026#34;) else: # Markdown失败就用纯文本 resp2 = requests.post(url, json={ \u0026#34;chat_id\u0026#34;: TG_CHAT_ID, \u0026#34;text\u0026#34;: chunk.replace(\u0026#34;*\u0026#34;, \u0026#34;\u0026#34;).replace(\u0026#34;_\u0026#34;, \u0026#34;\u0026#34;), }, timeout=10) print(f\u0026#34;[TG] Sent plain ({\u0026#39;✓\u0026#39; if resp2.status_code == 200 else \u0026#39;✗\u0026#39;})\u0026#34;) except Exception as e: print(f\u0026#34;[TG] Error: {e}\u0026#34;) time.sleep(0.5) def main(): print(f\u0026#34;🔥 热度做多雷达 v2 — {datetime.now().strftime(\u0026#39;%Y-%m-%d %H:%M:%S\u0026#39;)}\\n\u0026#34;) # 1. 全市场行情+费率 tickers_raw = api_get(\u0026#34;/fapi/v1/ticker/24hr\u0026#34;) premiums_raw = api_get(\u0026#34;/fapi/v1/premiumIndex\u0026#34;) if not tickers_raw or not premiums_raw: print(\u0026#34;❌ API失败\u0026#34;) return ticker_map = {} for t in tickers_raw: if t[\u0026#34;symbol\u0026#34;].endswith(\u0026#34;USDT\u0026#34;): ticker_map[t[\u0026#34;symbol\u0026#34;]] = { \u0026#34;px_chg\u0026#34;: float(t[\u0026#34;priceChangePercent\u0026#34;]), \u0026#34;vol\u0026#34;: float(t[\u0026#34;quoteVolume\u0026#34;]), \u0026#34;price\u0026#34;: float(t[\u0026#34;lastPrice\u0026#34;]), } funding_map = {} for p in premiums_raw: if p[\u0026#34;symbol\u0026#34;].endswith(\u0026#34;USDT\u0026#34;): funding_map[p[\u0026#34;symbol\u0026#34;]] = float(p[\u0026#34;lastFundingRate\u0026#34;]) # 2. 真实流通市值（币安现货API） mcap_map = {} try: r = requests.get( \u0026#34;https://www.binance.com/bapi/composite/v1/public/marketing/symbol/list\u0026#34;, timeout=10 ) if r.status_code == 200: for item in r.json().get(\u0026#34;data\u0026#34;, []): name = item.get(\u0026#34;name\u0026#34;, \u0026#34;\u0026#34;) mc = item.get(\u0026#34;marketCap\u0026#34;, 0) if name and mc: mcap_map[name] = float(mc) print(f\u0026#34;✅ 真实市值: {len(mcap_map)}个币\u0026#34;) except Exception as e: print(f\u0026#34;⚠️ 市值API失败: {e}\u0026#34;) # 3. 热度检测：币安广场热搜 + CoinGecko Trending + 成交量暴增 heat_map = {} cg_trending = set() square_trending = set() # 3a. 币安广场热搜（6H）— 最重要！币安用户=交易用户 sq_coins = get_square_heat() if sq_coins: for i, c in enumerate(sq_coins): coin = c[\u0026#34;coin\u0026#34;] square_trending.add(coin) # 排名越靠前分越高，急速上升额外加分 rank_score = max(50 - i * 4, 10) if c.get(\u0026#34;rapidRiser\u0026#34;): rank_score += 15 heat_map[coin] = heat_map.get(coin, 0) + rank_score print(f\u0026#34;🏦 广场热搜: {len(square_trending)}个币 {[c[\u0026#39;coin\u0026#39;] for c in sq_coins[:5]]}\u0026#34;) # 3b. CoinGecko Trending try: r = requests.get(\u0026#34;https://api.coingecko.com/api/v3/search/trending\u0026#34;, timeout=10) if r.status_code == 200: for item in r.json().get(\u0026#34;coins\u0026#34;, []): sym = item[\u0026#34;item\u0026#34;][\u0026#34;symbol\u0026#34;].upper() rank = item[\u0026#34;item\u0026#34;].get(\u0026#34;score\u0026#34;, 99) cg_trending.add(sym) heat_map[sym] = heat_map.get(sym, 0) + max(50 - rank * 3, 10) print(f\u0026#34;🌐 CG Trending: {len(cg_trending)}个币\u0026#34;) except Exception as e: print(f\u0026#34;⚠️ CG Trending失败: {e}\u0026#34;) # 成交量暴增检测 vol_surge_coins = set() for sym, tk in ticker_map.items(): coin = sym.replace(\u0026#34;USDT\u0026#34;, \u0026#34;\u0026#34;) vol_24h = tk[\u0026#34;vol\u0026#34;] if vol_24h \u0026gt; MIN_VOL_USD: kl = api_get(\u0026#34;/fapi/v1/klines\u0026#34;, {\u0026#34;symbol\u0026#34;: sym, \u0026#34;interval\u0026#34;: \u0026#34;1d\u0026#34;, \u0026#34;limit\u0026#34;: 8}) if kl and len(kl) \u0026gt;= 5: avg_prev = sum(float(k[7]) for k in kl[:-1]) / (len(kl) - 1) if avg_prev \u0026gt; 0: ratio = vol_24h / avg_prev if ratio \u0026gt;= VOL_SURGE_MULT: vol_surge_coins.add(coin) heat_map[coin] = heat_map.get(coin, 0) + min(ratio * 10, 50) time.sleep(0.05) print(f\u0026#34;📈 放量(≥{VOL_SURGE_MULT}x): {len(vol_surge_coins)}个币\u0026#34;) # 双重/三重热度 dual_heat = cg_trending \u0026amp; vol_surge_coins square_vol = square_trending \u0026amp; vol_surge_coins triple_heat = cg_trending \u0026amp; vol_surge_coins \u0026amp; square_trending all_multi_heat = dual_heat | square_vol if all_multi_heat: for coin in all_multi_heat: heat_map[coin] = heat_map.get(coin, 0) + 20 if triple_heat: for coin in triple_heat: heat_map[coin] = heat_map.get(coin, 0) + 30 # 三重热度超级加分 print(f\u0026#34;🔥🔥🔥 三重热度: {triple_heat}\u0026#34;) else: print(f\u0026#34;🔥🔥 双重热度: {all_multi_heat}\u0026#34;) # 4. OI扫描（Top100成交量 + 热度币） scan_syms = set() # 热度币必扫 for coin in heat_map: sym = coin + \u0026#34;USDT\u0026#34; if sym in ticker_map: scan_syms.add(sym) # Top100成交量 top_by_vol = sorted(ticker_map.items(), key=lambda x: x[1][\u0026#34;vol\u0026#34;], reverse=True)[:100] for sym, _ in top_by_vol: scan_syms.add(sym) oi_map = {} for i, sym in enumerate(scan_syms): oi_hist = api_get(\u0026#34;/futures/data/openInterestHist\u0026#34;, { \u0026#34;symbol\u0026#34;: sym, \u0026#34;period\u0026#34;: \u0026#34;1h\u0026#34;, \u0026#34;limit\u0026#34;: 6 }) if oi_hist and len(oi_hist) \u0026gt;= 2: curr = float(oi_hist[-1][\u0026#34;sumOpenInterestValue\u0026#34;]) prev_1h = float(oi_hist[-2][\u0026#34;sumOpenInterestValue\u0026#34;]) prev_6h = float(oi_hist[0][\u0026#34;sumOpenInterestValue\u0026#34;]) d1h = ((curr - prev_1h) / prev_1h * 100) if prev_1h \u0026gt; 0 else 0 d6h = ((curr - prev_6h) / prev_6h * 100) if prev_6h \u0026gt; 0 else 0 oi_map[sym] = {\u0026#34;oi_usd\u0026#34;: curr, \u0026#34;d1h\u0026#34;: d1h, \u0026#34;d6h\u0026#34;: d6h} if (i + 1) % 10 == 0: time.sleep(0.5) print(f\u0026#34;📊 OI扫描: {len(oi_map)}个币\u0026#34;) # 5. 整合所有数据 all_syms = set(list(ticker_map.keys())) coin_data = {} for sym in all_syms: tk = ticker_map.get(sym, {}) if not tk: continue oi = oi_map.get(sym, {}) fr = funding_map.get(sym, 0) coin = sym.replace(\u0026#34;USDT\u0026#34;, \u0026#34;\u0026#34;) d6h = oi.get(\u0026#34;d6h\u0026#34;, 0) fr_pct = fr * 100 oi_usd = oi.get(\u0026#34;oi_usd\u0026#34;, 0) # 真实市值优先，fallback粗估 if coin in mcap_map: est_mcap = mcap_map[coin] else: est_mcap = max(tk[\u0026#34;vol\u0026#34;] * 0.3, oi_usd * 2) if oi_usd \u0026gt; 0 else tk[\u0026#34;vol\u0026#34;] * 0.3 heat = heat_map.get(coin, 0) coin_data[sym] = { \u0026#34;coin\u0026#34;: coin, \u0026#34;sym\u0026#34;: sym, \u0026#34;px_chg\u0026#34;: tk[\u0026#34;px_chg\u0026#34;], \u0026#34;vol\u0026#34;: tk[\u0026#34;vol\u0026#34;], \u0026#34;fr_pct\u0026#34;: fr_pct, \u0026#34;d6h\u0026#34;: d6h, \u0026#34;oi_usd\u0026#34;: oi_usd, \u0026#34;est_mcap\u0026#34;: est_mcap, \u0026#34;heat\u0026#34;: heat, \u0026#34;in_cg\u0026#34;: coin in cg_trending, \u0026#34;in_sq\u0026#34;: coin in square_trending, \u0026#34;vol_surge\u0026#34;: coin in vol_surge_coins, } # ═══════════════════════════════════════ # 热度榜 # ═══════════════════════════════════════ hot_coins = sorted( [d for d in coin_data.values() if d[\u0026#34;heat\u0026#34;] \u0026gt; 0], key=lambda x: x[\u0026#34;heat\u0026#34;], reverse=True ) # 检测首次上榜 heat_history = {} if HEAT_HISTORY_FILE.exists(): try: heat_history = json.loads(HEAT_HISTORY_FILE.read_text()) except: pass now_ts = datetime.now(timezone(timedelta(hours=8))).strftime(\u0026#34;%Y-%m-%d %H:%M\u0026#34;) new_entries = [] # 首次上榜的币 for s in hot_coins: coin = s[\u0026#34;coin\u0026#34;] if coin not in heat_history: # 首次上榜！ heat_history[coin] = {\u0026#34;first_seen\u0026#34;: now_ts, \u0026#34;price\u0026#34;: s.get(\u0026#34;px_chg\u0026#34;, 0)} sources = [] if s[\u0026#34;in_sq\u0026#34;]: sources.append(\u0026#34;广场\u0026#34;) if s[\u0026#34;in_cg\u0026#34;]: sources.append(\u0026#34;CG\u0026#34;) if s[\u0026#34;vol_surge\u0026#34;]: sources.append(\u0026#34;放量\u0026#34;) new_entries.append({\u0026#34;coin\u0026#34;: coin, \u0026#34;sources\u0026#34;: sources, \u0026#34;data\u0026#34;: s}) # 清理超过7天的历史（避免文件无限增长） cutoff = (datetime.now(timezone(timedelta(hours=8))) - timedelta(days=7)).strftime(\u0026#34;%Y-%m-%d\u0026#34;) heat_history = {k: v for k, v in heat_history.items() if v.get(\u0026#34;first_seen\u0026#34;, \u0026#34;9999\u0026#34;) \u0026gt;= cutoff} # 保存历史 HEAT_HISTORY_FILE.write_text(json.dumps(heat_history, indent=2, ensure_ascii=False)) # ═══════════════════════════════════════ # 追多：负费率+在涨 # ═══════════════════════════════════════ chase = [] for sym, d in coin_data.items(): if d[\u0026#34;px_chg\u0026#34;] \u0026gt; 3 and d[\u0026#34;fr_pct\u0026#34;] \u0026lt; -0.005 and d[\u0026#34;vol\u0026#34;] \u0026gt; 1_000_000: fr_hist = api_get(\u0026#34;/fapi/v1/fundingRate\u0026#34;, {\u0026#34;symbol\u0026#34;: sym, \u0026#34;limit\u0026#34;: 5}) fr_rates = [float(f[\u0026#34;fundingRate\u0026#34;]) * 100 for f in fr_hist] if fr_hist else [d[\u0026#34;fr_pct\u0026#34;]] fr_prev = fr_rates[-2] if len(fr_rates) \u0026gt;= 2 else d[\u0026#34;fr_pct\u0026#34;] fr_delta = d[\u0026#34;fr_pct\u0026#34;] - fr_prev trend = \u0026#34;加速恶化\u0026#34; if fr_delta \u0026lt; -0.05 else \u0026#34;转负\u0026#34; if fr_delta \u0026lt; -0.01 else \u0026#34;持平\u0026#34; if abs(fr_delta) \u0026lt; 0.01 else \u0026#34;回升\u0026#34; chase.append({**d, \u0026#34;fr_delta\u0026#34;: fr_delta, \u0026#34;trend\u0026#34;: trend, \u0026#34;rates\u0026#34;: \u0026#34; → \u0026#34;.join([f\u0026#34;{x:.3f}\u0026#34; for x in fr_rates[-3:]])}) time.sleep(0.2) chase.sort(key=lambda x: x[\u0026#34;fr_pct\u0026#34;]) # ═══════════════════════════════════════ # 生成推送 # ═══════════════════════════════════════ now = datetime.now(timezone(timedelta(hours=8))) lines = [ f\u0026#34;**热度做多雷达**\u0026#34;, f\u0026#34;{now.strftime(\u0026#39;%Y-%m-%d %H:%M\u0026#39;)} CST\u0026#34;, ] # 热度榜（表格） # 首次上榜放最前面 if new_entries: lines.append(f\u0026#34;\\n**[ 首次上榜 ]** 新出现的热度币，重点关注\u0026#34;) tbl = [\u0026#34;```\u0026#34;] tbl.append(f\u0026#34;{\u0026#39;币种\u0026#39;:\u0026lt;10} {\u0026#39;市值\u0026#39;:\u0026gt;8} {\u0026#39;涨幅\u0026#39;:\u0026gt;7} {\u0026#39;来源\u0026#39;}\u0026#34;) tbl.append(f\u0026#34;{\u0026#39;-\u0026#39;*10} {\u0026#39;-\u0026#39;*8} {\u0026#39;-\u0026#39;*7} {\u0026#39;-\u0026#39;*20}\u0026#34;) for e in new_entries: s = e[\u0026#34;data\u0026#34;] src_str = \u0026#34;/\u0026#34;.join(e[\u0026#34;sources\u0026#34;]) tbl.append(f\u0026#34;{s[\u0026#39;coin\u0026#39;]:\u0026lt;10} {mcap_str(s[\u0026#39;est_mcap\u0026#39;]):\u0026gt;8} {s[\u0026#39;px_chg\u0026#39;]:\u0026gt;+6.0f}% {src_str}\u0026#34;) tbl.append(\u0026#34;```\u0026#34;) lines.append(\u0026#34;\\n\u0026#34;.join(tbl)) if hot_coins: lines.append(f\u0026#34;\\n**[ 热度榜 ]**\u0026#34;) tbl = [\u0026#34;```\u0026#34;] tbl.append(f\u0026#34;{\u0026#39;币种\u0026#39;:\u0026lt;10} {\u0026#39;市值\u0026#39;:\u0026gt;8} {\u0026#39;涨幅\u0026#39;:\u0026gt;7} {\u0026#39;来源\u0026#39;}\u0026#34;) tbl.append(f\u0026#34;{\u0026#39;-\u0026#39;*10} {\u0026#39;-\u0026#39;*8} {\u0026#39;-\u0026#39;*7} {\u0026#39;-\u0026#39;*20}\u0026#34;) for s in hot_coins[:10]: sources = [] if s[\u0026#34;in_sq\u0026#34;]: sources.append(\u0026#34;广场\u0026#34;) if s[\u0026#34;in_cg\u0026#34;]: sources.append(\u0026#34;CG\u0026#34;) if s[\u0026#34;vol_surge\u0026#34;]: sources.append(\u0026#34;放量\u0026#34;) extra = [] if abs(s[\u0026#34;d6h\u0026#34;]) \u0026gt;= 3: extra.append(f\u0026#34;OI{s[\u0026#39;d6h\u0026#39;]:+.0f}%\u0026#34;) if s[\u0026#34;fr_pct\u0026#34;] \u0026lt; -0.03: extra.append(f\u0026#34;费率{s[\u0026#39;fr_pct\u0026#39;]:.2f}%\u0026#34;) src_str = \u0026#34;/\u0026#34;.join(sources) if extra: src_str += \u0026#34; \u0026#34; + \u0026#34; \u0026#34;.join(extra) coin_name = s[\u0026#39;coin\u0026#39;] tbl.append(f\u0026#34;{coin_name:\u0026lt;10} {mcap_str(s[\u0026#39;est_mcap\u0026#39;]):\u0026gt;8} {s[\u0026#39;px_chg\u0026#39;]:\u0026gt;+6.0f}% {src_str}\u0026#34;) tbl.append(\u0026#34;```\u0026#34;) lines.append(\u0026#34;\\n\u0026#34;.join(tbl)) else: lines.append(\u0026#34;\\n**[ 热度榜 ]** 暂无热点\u0026#34;) # 追多（表格） lines.append(f\u0026#34;\\n**[ 追多 ]** 涨了+费率负=空头燃料\u0026#34;) if chase: tbl = [\u0026#34;```\u0026#34;] tbl.append(f\u0026#34;{\u0026#39;币种\u0026#39;:\u0026lt;10} {\u0026#39;费率\u0026#39;:\u0026gt;10} {\u0026#39;趋势\u0026#39;:\u0026gt;8} {\u0026#39;涨幅\u0026#39;:\u0026gt;7} {\u0026#39;市值\u0026#39;:\u0026gt;8}\u0026#34;) tbl.append(f\u0026#34;{\u0026#39;-\u0026#39;*10} {\u0026#39;-\u0026#39;*10} {\u0026#39;-\u0026#39;*8} {\u0026#39;-\u0026#39;*7} {\u0026#39;-\u0026#39;*8}\u0026#34;) for s in chase[:8]: tbl.append( f\u0026#34;{s[\u0026#39;coin\u0026#39;]:\u0026lt;10} {s[\u0026#39;fr_pct\u0026#39;]:\u0026gt;+9.3f}% {s[\u0026#39;trend\u0026#39;]:\u0026gt;8} {s[\u0026#39;px_chg\u0026#39;]:\u0026gt;+6.0f}% {mcap_str(s[\u0026#39;est_mcap\u0026#39;]):\u0026gt;7}\u0026#34; ) tbl.append(\u0026#34;```\u0026#34;) lines.append(\u0026#34;\\n\u0026#34;.join(tbl)) else: lines.append(\u0026#34; 暂无符合条件的标的\u0026#34;) # OI异动（表格） oi_alerts = [] for sym, oi in oi_map.items(): if abs(oi[\u0026#34;d6h\u0026#34;]) \u0026gt;= 8: d = coin_data.get(sym) if d and d[\u0026#34;heat\u0026#34;] == 0: oi_alerts.append(d) oi_alerts.sort(key=lambda x: abs(x[\u0026#34;d6h\u0026#34;]), reverse=True) if oi_alerts: lines.append(f\u0026#34;\\n**[ OI异动 ]** 6小时持仓变化\u0026gt;=8%\u0026#34;) tbl = [\u0026#34;```\u0026#34;] tbl.append(f\u0026#34;{\u0026#39;币种\u0026#39;:\u0026lt;10} {\u0026#39;方向\u0026#39;:\u0026gt;4} {\u0026#39;OI变化\u0026#39;:\u0026gt;8} {\u0026#39;涨幅\u0026#39;:\u0026gt;7} {\u0026#39;市值\u0026#39;:\u0026gt;8}\u0026#34;) tbl.append(f\u0026#34;{\u0026#39;-\u0026#39;*10} {\u0026#39;-\u0026#39;*4} {\u0026#39;-\u0026#39;*8} {\u0026#39;-\u0026#39;*7} {\u0026#39;-\u0026#39;*8}\u0026#34;) for s in oi_alerts[:6]: direction = \u0026#34;增仓\u0026#34; if s[\u0026#34;d6h\u0026#34;] \u0026gt; 0 else \u0026#34;减仓\u0026#34; tbl.append( f\u0026#34;{s[\u0026#39;coin\u0026#39;]:\u0026lt;10} {direction:\u0026gt;4} {s[\u0026#39;d6h\u0026#39;]:\u0026gt;+7.1f}% {s[\u0026#39;px_chg\u0026#39;]:\u0026gt;+6.0f}% {mcap_str(s[\u0026#39;est_mcap\u0026#39;]):\u0026gt;7}\u0026#34; ) tbl.append(\u0026#34;```\u0026#34;) lines.append(\u0026#34;\\n\u0026#34;.join(tbl)) # 值得关注 highlights = [] hot_oi = [d for d in coin_data.values() if d[\u0026#34;heat\u0026#34;] \u0026gt; 0 and d[\u0026#34;d6h\u0026#34;] \u0026gt; 5] for s in sorted(hot_oi, key=lambda x: x[\u0026#34;d6h\u0026#34;], reverse=True)[:3]: highlights.append(f\u0026#34;{s[\u0026#39;coin\u0026#39;]} — 热度高+OI涨{s[\u0026#39;d6h\u0026#39;]:+.0f}%，资金涌入\u0026#34;) hot_fuel = [d for d in coin_data.values() if d[\u0026#34;heat\u0026#34;] \u0026gt; 0 and d[\u0026#34;fr_pct\u0026#34;] \u0026lt; -0.03] for s in sorted(hot_fuel, key=lambda x: x[\u0026#34;fr_pct\u0026#34;])[:2]: if s[\u0026#34;coin\u0026#34;] not in \u0026#34; \u0026#34;.join(highlights): highlights.append(f\u0026#34;{s[\u0026#39;coin\u0026#39;]} — 热度高+费率{s[\u0026#39;fr_pct\u0026#39;]:.2f}%，空头燃料足\u0026#34;) chase_fire = [s for s in chase[:5] if \u0026#34;加速\u0026#34; in s.get(\u0026#34;trend\u0026#34;, \u0026#34;\u0026#34;)] for s in chase_fire[:2]: if s[\u0026#34;coin\u0026#34;] not in \u0026#34; \u0026#34;.join(highlights): highlights.append(f\u0026#34;{s[\u0026#39;coin\u0026#39;]} — 费率{s[\u0026#39;fr_pct\u0026#39;]:.3f}%持续恶化，逼空在即\u0026#34;) if highlights: lines.append(f\u0026#34;\\n**[ 值得关注 ]**\u0026#34;) for h in highlights[:5]: lines.append(f\u0026#34; {h}\u0026#34;) lines.append(f\u0026#34;\\n广场=币安站内搜索 / CG=CoinGecko全球热度\u0026#34;) lines.append(f\u0026#34;费率负=做空多，庄家拉盘爆空头\u0026#34;) report = \u0026#34;\\n\u0026#34;.join(lines) send_telegram(report) print(\u0026#34;\\n✅ 完成\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: main() 币安 Alpha 公告监控 发布日期: 2026.04.23　标签: Python · Binance · Claude AI · Telegram\nWebSocket 实时监听 · AI 分析 · TG 推送\n自动检测币安Alpha新上币公告，Claude AI分析代币质量，TG实时推送。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; Binance Alpha Monitor v2 — 稳定版 REST轮询 + 智能过滤 + 评级 + TG推送 零API Key，零AI成本（评级用规则引擎） 运行: python3 alpha_monitor.py \u0026#34;\u0026#34;\u0026#34; import asyncio import hashlib import json import logging import os import re import sqlite3 import time from dataclasses import dataclass, field from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Optional import httpx # ============================================================ # 配置 # ============================================================ BASE_DIR = Path(__file__).parent DB_PATH = str(BASE_DIR / \u0026#34;data\u0026#34; / \u0026#34;alpha.db\u0026#34;) # TG TG_BOT_TOKEN = os.environ.get(\u0026#34;TG_BOT_TOKEN\u0026#34;, \u0026#34;\u0026#34;) TG_CHAT_ID = os.environ.get(\u0026#34;TG_CHAT_ID\u0026#34;, \u0026#34;\u0026#34;) # LLM (可选，用于抽取叙事，不配置则降级为规则) # 优先从hermes auth.json读credential pool（与scanner一致） def _load_anthropic_key(): \u0026#34;\u0026#34;\u0026#34;从auth.json或环境变量获取API key\u0026#34;\u0026#34;\u0026#34; # 1. hermes auth.json credential pool try: auth_file = os.path.expanduser(\u0026#34;~/.hermes/auth.json\u0026#34;) if os.path.exists(auth_file): with open(auth_file) as f: auth = json.load(f) pool = auth.get(\u0026#34;credential_pool\u0026#34;, {}).get(\u0026#34;anthropic\u0026#34;, []) if pool: key = pool[0].get(\u0026#34;access_token\u0026#34;, \u0026#34;\u0026#34;) if key and key != \u0026#34;***\u0026#34;: return key except Exception: pass # 2. 环境变量 fallback return os.environ.get(\u0026#34;ANTHROPIC_API_KEY\u0026#34;, \u0026#34;\u0026#34;) ANTHROPIC_API_KEY = _load_anthropic_key() ANTHROPIC_BASE_URL = os.environ.get(\u0026#34;ANTHROPIC_BASE_URL\u0026#34;, \u0026#34;https://api.anthropic.com\u0026#34;) ANTHROPIC_MODEL = os.environ.get(\u0026#34;ANTHROPIC_MODEL\u0026#34;, \u0026#34;claude-sonnet-4-6\u0026#34;) # 轮询间隔 ANNOUNCEMENT_POLL_INTERVAL = 30 # 公告轮询30秒 AGGREGATION_POLL_INTERVAL = 15 # 聚合工作者15秒 MONITOR_POLL_INTERVAL = 120 # 上线后监控2分钟 # HTTP HEADERS = { \u0026#34;User-Agent\u0026#34;: \u0026#34;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\u0026#34;, \u0026#34;Accept\u0026#34;: \u0026#34;application/json\u0026#34;, \u0026#34;Accept-Language\u0026#34;: \u0026#34;en-US,en;q=0.9\u0026#34;, } BINANCE_ANNOUNCEMENT_API = \u0026#34;https://www.binance.com/bapi/composite/v1/public/cms/article/list/query\u0026#34; # ============================================================ # 日志 # ============================================================ logging.basicConfig( level=logging.INFO, format=\u0026#34;%(asctime)s | %(levelname)s | %(name)s | %(message)s\u0026#34;, datefmt=\u0026#34;%Y-%m-%d %H:%M:%S\u0026#34;, ) logger = logging.getLogger(\u0026#34;alpha\u0026#34;) # ============================================================ # 过滤规则 # ============================================================ # 触发关键词 — 命中任意一个就触发 TRIGGER_KEYWORDS = [ \u0026#34;alpha\u0026#34;, \u0026#34;空投\u0026#34;, \u0026#34;airdrop\u0026#34;, \u0026#34;tge\u0026#34;, \u0026#34;token generation\u0026#34;, \u0026#34;将上线\u0026#34;, \u0026#34;will list\u0026#34;, \u0026#34;will launch\u0026#34;, \u0026#34;独家\u0026#34;, \u0026#34;exclusive\u0026#34;, \u0026#34;binance wallet\u0026#34;, \u0026#34;hodler\u0026#34;, ] # 排除关键词 — 命中任意一个直接排除 EXCLUDE_KEYWORDS = [ \u0026#34;delisting\u0026#34;, \u0026#34;delist\u0026#34;, \u0026#34;下架\u0026#34;, \u0026#34;deprecate\u0026#34;, \u0026#34;退市\u0026#34;, \u0026#34;maintenance\u0026#34;, \u0026#34;维护\u0026#34;, \u0026#34;launchpool\u0026#34;, \u0026#34;megadrop\u0026#34;, \u0026#34;buyback\u0026#34;, \u0026#34;回购\u0026#34;, \u0026#34;已完成\u0026#34;, \u0026#34;完成结算\u0026#34;, \u0026#34;perpetual contract\u0026#34;, # 永续合约 \u0026#34;futures will launch\u0026#34;, # 期货上新 \u0026#34;usdⓢ-margined\u0026#34;, # U本位合约 \u0026#34;coin-margined\u0026#34;, # 币本位合约 \u0026#34;margin will add\u0026#34;, # 杠杆上新 \u0026#34;trading bots services\u0026#34;, # 交易机器人 \u0026#34;trading pairs\u0026#34;, # 交易对调整(非新币) ] # Alpha Box 盲盒 ALPHA_BOX_KEYWORDS = [\u0026#34;alpha box\u0026#34;, \u0026#34;盲盒\u0026#34;, \u0026#34;mystery box\u0026#34;] # ============================================================ # VC 白名单 # ============================================================ TIER1_VCS = [ \u0026#34;binance labs\u0026#34;, \u0026#34;yzi labs\u0026#34;, \u0026#34;coinbase ventures\u0026#34;, \u0026#34;a16z\u0026#34;, \u0026#34;andreessen horowitz\u0026#34;, \u0026#34;paradigm\u0026#34;, \u0026#34;polychain\u0026#34;, \u0026#34;polychain capital\u0026#34;, \u0026#34;sequoia\u0026#34;, \u0026#34;sequoia china\u0026#34;, \u0026#34;sequoia capital\u0026#34;, \u0026#34;multicoin\u0026#34;, \u0026#34;multicoin capital\u0026#34;, \u0026#34;pantera\u0026#34;, \u0026#34;pantera capital\u0026#34;, \u0026#34;dragonfly\u0026#34;, \u0026#34;dragonfly capital\u0026#34;, \u0026#34;founders fund\u0026#34;, ] TIER2_VCS = [ \u0026#34;abcde\u0026#34;, \u0026#34;iosg\u0026#34;, \u0026#34;hashkey\u0026#34;, \u0026#34;okx ventures\u0026#34;, \u0026#34;sevenx\u0026#34;, \u0026#34;folius\u0026#34;, \u0026#34;foresight\u0026#34;, \u0026#34;hashed\u0026#34;, \u0026#34;bitkraft\u0026#34;, \u0026#34;framework\u0026#34;, \u0026#34;framework ventures\u0026#34;, \u0026#34;delphi\u0026#34;, \u0026#34;delphi digital\u0026#34;, \u0026#34;electric capital\u0026#34;, \u0026#34;variant\u0026#34;, \u0026#34;1kx\u0026#34;, \u0026#34;placeholder\u0026#34;, \u0026#34;animoca\u0026#34;, \u0026#34;animoca brands\u0026#34;, \u0026#34;jump\u0026#34;, \u0026#34;jump crypto\u0026#34;, \u0026#34;hack vc\u0026#34;, \u0026#34;bain capital\u0026#34;, ] # 叙事热度 HOT_NARRATIVES = [\u0026#34;defi_perp\u0026#34;, \u0026#34;ai_agent\u0026#34;, \u0026#34;ai_defi\u0026#34;, \u0026#34;defai\u0026#34;, \u0026#34;zk_proof\u0026#34;] WEAK_NARRATIVES = [\u0026#34;gamefi\u0026#34;, \u0026#34;meme\u0026#34;, \u0026#34;social\u0026#34;] # 币安亲儿子信号 BINANCE_DARLING_KEYWORDS = [\u0026#34;yzi labs\u0026#34;, \u0026#34;binance labs\u0026#34;] # ============================================================ # 评级引擎 # ============================================================ TIER_ICONS = {\u0026#34;S\u0026#34;: \u0026#34;🟢🟢🟢\u0026#34;, \u0026#34;A\u0026#34;: \u0026#34;🟡🟡\u0026#34;, \u0026#34;B\u0026#34;: \u0026#34;🟠\u0026#34;, \u0026#34;C\u0026#34;: \u0026#34;⚪\u0026#34;} TIER_LABELS = {\u0026#34;S\u0026#34;: \u0026#34;S 级(必研究)\u0026#34;, \u0026#34;A\u0026#34;: \u0026#34;A 级(值得看)\u0026#34;, \u0026#34;B\u0026#34;: \u0026#34;B 级(正常)\u0026#34;, \u0026#34;C\u0026#34;: \u0026#34;C 级(了解)\u0026#34;} def count_vc_tier(vcs: list, vc_list: list) -\u0026gt; int: count = 0 vcs_lower = [v.lower() for v in vcs] for t in vc_list: if any(t in v for v in vcs_lower): count += 1 return count def rate_project(circ_mcap: float, fdv: float, vcs: list, narrative: str, is_darling: bool) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34;S/A/B/C 评级\u0026#34;\u0026#34;\u0026#34; t1 = count_vc_tier(vcs, TIER1_VCS) t2 = count_vc_tier(vcs, TIER2_VCS) hot = narrative in HOT_NARRATIVES weak = narrative in WEAK_NARRATIVES circ_mcap = circ_mcap or 0 fdv = fdv or 0 warnings = [] if weak: warnings.append(f\u0026#34;⚠️ {narrative} 历史破发率较高\u0026#34;) # S级 5条路径 if is_darling: return {\u0026#34;tier\u0026#34;: \u0026#34;S\u0026#34;, \u0026#34;reason\u0026#34;: \u0026#34;币安亲儿子(YZi/Binance Labs/CZ)\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} if hot and t1 \u0026gt;= 1 and fdv \u0026lt; 500_000_000: return {\u0026#34;tier\u0026#34;: \u0026#34;S\u0026#34;, \u0026#34;reason\u0026#34;: f\u0026#34;热叙事({narrative})+ Tier1 VC\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} if t1 \u0026gt;= 2 and circ_mcap \u0026lt; 50_000_000 and fdv \u0026lt; 300_000_000: return {\u0026#34;tier\u0026#34;: \u0026#34;S\u0026#34;, \u0026#34;reason\u0026#34;: \u0026#34;≥2家 Tier1 中盘\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} if t1 \u0026gt;= 1 and circ_mcap \u0026lt; 10_000_000 and fdv \u0026lt; 100_000_000: return {\u0026#34;tier\u0026#34;: \u0026#34;S\u0026#34;, \u0026#34;reason\u0026#34;: \u0026#34;Tier1 微盘\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} if hot and circ_mcap \u0026lt; 10_000_000 and fdv \u0026lt; 50_000_000: return {\u0026#34;tier\u0026#34;: \u0026#34;S\u0026#34;, \u0026#34;reason\u0026#34;: f\u0026#34;热叙事({narrative})微盘\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} # A级 if t1 \u0026gt;= 1 and circ_mcap \u0026lt; 20_000_000 and fdv \u0026lt; 200_000_000: return {\u0026#34;tier\u0026#34;: \u0026#34;A\u0026#34;, \u0026#34;reason\u0026#34;: \u0026#34;Tier1 小盘\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} # B级 if circ_mcap \u0026lt; 50_000_000 and fdv \u0026lt; 500_000_000: return {\u0026#34;tier\u0026#34;: \u0026#34;B\u0026#34;, \u0026#34;reason\u0026#34;: \u0026#34;中盘\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} return {\u0026#34;tier\u0026#34;: \u0026#34;C\u0026#34;, \u0026#34;reason\u0026#34;: \u0026#34;大盘/弱信号\u0026#34;, \u0026#34;warnings\u0026#34;: warnings} # ============================================================ # 数据库 # ============================================================ def init_db(): Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True) conn = sqlite3.connect(DB_PATH) c = conn.cursor() c.execute(\u0026#34;\u0026#34;\u0026#34; CREATE TABLE IF NOT EXISTS projects ( id TEXT PRIMARY KEY, symbol TEXT NOT NULL, name TEXT, launch_time TEXT, source TEXT, raw_text TEXT, tier TEXT DEFAULT \u0026#39;PENDING\u0026#39;, tier_reason TEXT, narrative TEXT, narrative_desc TEXT, vcs_json TEXT DEFAULT \u0026#39;[]\u0026#39;, is_darling INTEGER DEFAULT 0, open_price REAL, total_supply REAL, circulating_supply REAL, fdv REAL, circulating_mcap REAL, excluded INTEGER DEFAULT 0, exclude_reason TEXT, discovered_at TEXT, updated_at TEXT )\u0026#34;\u0026#34;\u0026#34;) c.execute(\u0026#34;\u0026#34;\u0026#34; CREATE TABLE IF NOT EXISTS pushes ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL, push_type TEXT, sent_at TEXT, content TEXT )\u0026#34;\u0026#34;\u0026#34;) c.execute(\u0026#34;\u0026#34;\u0026#34; CREATE TABLE IF NOT EXISTS snapshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, project_id TEXT NOT NULL, timestamp TEXT NOT NULL, price REAL, circulating_mcap REAL, fdv REAL )\u0026#34;\u0026#34;\u0026#34;) conn.commit() conn.close() def project_id(symbol: str, date_str: str) -\u0026gt; str: return hashlib.md5(f\u0026#34;{symbol.upper()}_{date_str}\u0026#34;.encode()).hexdigest()[:16] def project_exists(pid: str) -\u0026gt; bool: conn = sqlite3.connect(DB_PATH) exists = conn.execute(\u0026#34;SELECT 1 FROM projects WHERE id=?\u0026#34;, (pid,)).fetchone() is not None conn.close() return exists def save_project(project: dict): conn = sqlite3.connect(DB_PATH) now = datetime.utcnow().isoformat() conn.execute(\u0026#34;\u0026#34;\u0026#34; INSERT OR IGNORE INTO projects (id, symbol, name, launch_time, source, raw_text, tier, tier_reason, narrative, narrative_desc, vcs_json, is_darling, open_price, total_supply, circulating_supply, fdv, circulating_mcap, excluded, exclude_reason, discovered_at, updated_at) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) \u0026#34;\u0026#34;\u0026#34;, ( project[\u0026#34;id\u0026#34;], project[\u0026#34;symbol\u0026#34;], project.get(\u0026#34;name\u0026#34;), project.get(\u0026#34;launch_time\u0026#34;), project.get(\u0026#34;source\u0026#34;), project.get(\u0026#34;raw_text\u0026#34;), project.get(\u0026#34;tier\u0026#34;, \u0026#34;PENDING\u0026#34;), project.get(\u0026#34;tier_reason\u0026#34;), project.get(\u0026#34;narrative\u0026#34;), project.get(\u0026#34;narrative_desc\u0026#34;), json.dumps(project.get(\u0026#34;vcs\u0026#34;, [])), int(project.get(\u0026#34;is_darling\u0026#34;, False)), project.get(\u0026#34;open_price\u0026#34;), project.get(\u0026#34;total_supply\u0026#34;), project.get(\u0026#34;circulating_supply\u0026#34;), project.get(\u0026#34;fdv\u0026#34;), project.get(\u0026#34;circulating_mcap\u0026#34;), int(project.get(\u0026#34;excluded\u0026#34;, 0)), project.get(\u0026#34;exclude_reason\u0026#34;), now, now, )) conn.commit() conn.close() def update_project(pid: str, fields: dict): if not fields: return conn = sqlite3.connect(DB_PATH) fields[\u0026#34;updated_at\u0026#34;] = datetime.utcnow().isoformat() set_parts = [f\u0026#34;{k}=?\u0026#34; for k in fields] values = list(fields.values()) + [pid] conn.execute(f\u0026#34;UPDATE projects SET {\u0026#39;,\u0026#39;.join(set_parts)} WHERE id=?\u0026#34;, values) conn.commit() conn.close() def get_project(pid: str) -\u0026gt; Optional[dict]: conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row row = conn.execute(\u0026#34;SELECT * FROM projects WHERE id=?\u0026#34;, (pid,)).fetchone() conn.close() return dict(row) if row else None def list_pending() -\u0026gt; list: conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row rows = conn.execute( \u0026#34;SELECT * FROM projects WHERE excluded=0 AND tier=\u0026#39;PENDING\u0026#39; ORDER BY discovered_at\u0026#34; ).fetchall() conn.close() return [dict(r) for r in rows] def list_active() -\u0026gt; list: \u0026#34;\u0026#34;\u0026#34;上线后需要监控的项目（非PENDING非EXCLUDED，有launch_time）\u0026#34;\u0026#34;\u0026#34; conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row rows = conn.execute(\u0026#34;\u0026#34;\u0026#34; SELECT * FROM projects WHERE excluded=0 AND launch_time IS NOT NULL AND launch_time != \u0026#39;\u0026#39; AND tier NOT IN (\u0026#39;PENDING\u0026#39;, \u0026#39;EXCLUDED\u0026#39;, \u0026#39;ERROR\u0026#39;) \u0026#34;\u0026#34;\u0026#34;).fetchall() conn.close() return [dict(r) for r in rows] def has_pushed(pid: str, push_type: str) -\u0026gt; bool: conn = sqlite3.connect(DB_PATH) exists = conn.execute( \u0026#34;SELECT 1 FROM pushes WHERE project_id=? AND push_type=?\u0026#34;, (pid, push_type) ).fetchone() is not None conn.close() return exists def log_push(pid: str, push_type: str, content: str): conn = sqlite3.connect(DB_PATH) conn.execute( \u0026#34;INSERT INTO pushes (project_id, push_type, sent_at, content) VALUES (?,?,?,?)\u0026#34;, (pid, push_type, datetime.utcnow().isoformat(), content) ) conn.commit() conn.close() def save_snapshot(pid: str, price: float, mcap: float, fdv: float): conn = sqlite3.connect(DB_PATH) conn.execute( \u0026#34;INSERT INTO snapshots (project_id, timestamp, price, circulating_mcap, fdv) VALUES (?,?,?,?,?)\u0026#34;, (pid, datetime.utcnow().isoformat(), price, mcap, fdv) ) conn.commit() conn.close() # ============================================================ # TG 推送 # ============================================================ async def send_tg(text: str, silent: bool = False) -\u0026gt; bool: if not TG_BOT_TOKEN or not TG_CHAT_ID: logger.error(\u0026#34;TG_BOT_TOKEN 或 TG_CHAT_ID 未配置\u0026#34;) return False url = f\u0026#34;https://api.telegram.org/bot{TG_BOT_TOKEN}/sendMessage\u0026#34; payload = { \u0026#34;chat_id\u0026#34;: TG_CHAT_ID, \u0026#34;text\u0026#34;: text, \u0026#34;parse_mode\u0026#34;: \u0026#34;HTML\u0026#34;, \u0026#34;disable_notification\u0026#34;: silent, } try: async with httpx.AsyncClient(timeout=15, headers=HEADERS) as client: resp = await client.post(url, json=payload) if resp.status_code != 200: logger.error(f\u0026#34;TG发送失败 {resp.status_code}: {resp.text[:200]}\u0026#34;) return False return True except Exception as e: logger.error(f\u0026#34;TG发送异常: {e}\u0026#34;) return False # ============================================================ # 公告标题解析 # ============================================================ def is_trigger(title: str) -\u0026gt; tuple[bool, Optional[str]]: t = title.lower() for kw in EXCLUDE_KEYWORDS: if kw.lower() in t: return False, f\u0026#34;排除: {kw}\u0026#34; for kw in ALPHA_BOX_KEYWORDS: if kw.lower() in t: return False, \u0026#34;Alpha Box 盲盒\u0026#34; for kw in TRIGGER_KEYWORDS: if kw.lower() in t: return True, None return False, None def extract_symbol(title: str) -\u0026gt; Optional[str]: # 英文括号: \u0026#34;Chip (CHIP)\u0026#34; m = re.search(r\u0026#34;\\(([A-Z0-9]{2,10})\\)\u0026#34;, title) if m: return m.group(1) # 中文括号 m = re.search(r\u0026#34;（([A-Z0-9]{2,10})）\u0026#34;, title) if m: return m.group(1) return None def extract_name(title: str) -\u0026gt; Optional[str]: patterns = [ r\u0026#34;(?:上线|List|list|Launch|launch|featured)\\s+([A-Za-z0-9 ]+?)\\s*[\\(（]\u0026#34;, ] for p in patterns: m = re.search(p, title, re.IGNORECASE) if m: return m.group(1).strip() return None # ============================================================ # 币安公告抓取 # ============================================================ async def fetch_announcements(limit: int = 20) -\u0026gt; list: \u0026#34;\u0026#34;\u0026#34;抓取币安最新公告\u0026#34;\u0026#34;\u0026#34; all_articles = [] # 48: New Cryptocurrency Listing, 161: Latest Activities, 93: Latest News for catalog_id in [48, 161, 93]: params = {\u0026#34;type\u0026#34;: 1, \u0026#34;catalogId\u0026#34;: catalog_id, \u0026#34;pageNo\u0026#34;: 1, \u0026#34;pageSize\u0026#34;: limit} try: async with httpx.AsyncClient(timeout=15, headers=HEADERS) as client: resp = await client.get(BINANCE_ANNOUNCEMENT_API, params=params) resp.raise_for_status() data = resp.json() for catalog in data.get(\u0026#34;data\u0026#34;, {}).get(\u0026#34;catalogs\u0026#34;, []): for a in catalog.get(\u0026#34;articles\u0026#34;, []): a[\u0026#34;_catalog_id\u0026#34;] = catalog_id all_articles.append(a) except Exception as e: logger.warning(f\u0026#34;抓取分类 {catalog_id} 失败: {e}\u0026#34;) # 去重 seen = set() unique = [] for a in all_articles: code = a.get(\u0026#34;code\u0026#34;) if code and code not in seen: seen.add(code) unique.append(a) return unique # ============================================================ # CoinGecko 数据 # ============================================================ async def fetch_coingecko(symbol: str, project_name: str = \u0026#34;\u0026#34;) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34;查CoinGecko代币经济数据。双重匹配：symbol精确匹配 + 项目名模糊匹配\u0026#34;\u0026#34;\u0026#34; result = {\u0026#34;found\u0026#34;: False, \u0026#34;price\u0026#34;: None, \u0026#34;fdv\u0026#34;: None, \u0026#34;mcap\u0026#34;: None, \u0026#34;total_supply\u0026#34;: None, \u0026#34;circ_supply\u0026#34;: None, \u0026#34;chain\u0026#34;: None, \u0026#34;contract\u0026#34;: None} try: async with httpx.AsyncClient(timeout=15, headers=HEADERS) as client: # 搜索策略：先搜symbol，如果项目名不同再搜项目名 queries = [symbol] if project_name and project_name.upper() != symbol.upper(): queries.append(project_name) coin_id = None best_rank = 999999 name_exact_match = None # 项目名精确匹配优先 for query in queries: resp = await client.get(\u0026#34;https://api.coingecko.com/api/v3/search\u0026#34;, params={\u0026#34;query\u0026#34;: query}) if resp.status_code != 200: continue coins = resp.json().get(\u0026#34;coins\u0026#34;, []) for c in coins: c_sym = c.get(\u0026#34;symbol\u0026#34;, \u0026#34;\u0026#34;).upper() c_name = c.get(\u0026#34;name\u0026#34;, \u0026#34;\u0026#34;).lower() c_rank = c.get(\u0026#34;market_cap_rank\u0026#34;) or 999999 # 项目名精确匹配（最高优先级，如MegaETH -\u0026gt; megaeth） if project_name and c_name == project_name.lower(): name_exact_match = c[\u0026#34;id\u0026#34;] # 精确symbol匹配 — 取market_cap_rank最高（最小）的 if c_sym == symbol.upper(): if c_rank \u0026lt; best_rank: coin_id = c[\u0026#34;id\u0026#34;] best_rank = c_rank # 项目名模糊匹配（如MegaETH搜mega） if project_name and project_name.lower() in c_name: if c_rank \u0026lt; best_rank: coin_id = c[\u0026#34;id\u0026#34;] best_rank = c_rank # 项目名精确匹配 \u0026gt; 其他匹配 if name_exact_match: coin_id = name_exact_match if not coin_id: logger.info(f\u0026#34;CoinGecko未找到 {symbol}/{project_name}\u0026#34;) return result logger.info(f\u0026#34;CoinGecko匹配: {symbol} -\u0026gt; {coin_id} (rank={best_rank})\u0026#34;) resp2 = await client.get( f\u0026#34;https://api.coingecko.com/api/v3/coins/{coin_id}\u0026#34;, params={\u0026#34;localization\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;tickers\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;market_data\u0026#34;: \u0026#34;true\u0026#34;, \u0026#34;community_data\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;developer_data\u0026#34;: \u0026#34;false\u0026#34;} ) if resp2.status_code == 429: await asyncio.sleep(5) resp2 = await client.get( f\u0026#34;https://api.coingecko.com/api/v3/coins/{coin_id}\u0026#34;, params={\u0026#34;localization\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;tickers\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;market_data\u0026#34;: \u0026#34;true\u0026#34;, \u0026#34;community_data\u0026#34;: \u0026#34;false\u0026#34;, \u0026#34;developer_data\u0026#34;: \u0026#34;false\u0026#34;} ) if resp2.status_code != 200: return result d = resp2.json() md = d.get(\u0026#34;market_data\u0026#34;, {}) result.update({ \u0026#34;found\u0026#34;: True, \u0026#34;price\u0026#34;: (md.get(\u0026#34;current_price\u0026#34;) or {}).get(\u0026#34;usd\u0026#34;), \u0026#34;fdv\u0026#34;: (md.get(\u0026#34;fully_diluted_valuation\u0026#34;) or {}).get(\u0026#34;usd\u0026#34;), \u0026#34;mcap\u0026#34;: (md.get(\u0026#34;market_cap\u0026#34;) or {}).get(\u0026#34;usd\u0026#34;), \u0026#34;total_supply\u0026#34;: md.get(\u0026#34;total_supply\u0026#34;), \u0026#34;circ_supply\u0026#34;: md.get(\u0026#34;circulating_supply\u0026#34;), }) # 提取categories（含VC信息如\u0026#34;YZi Labs Portfolio\u0026#34;）和description result[\u0026#34;categories\u0026#34;] = d.get(\u0026#34;categories\u0026#34;, []) result[\u0026#34;description\u0026#34;] = (d.get(\u0026#34;description\u0026#34;) or {}).get(\u0026#34;en\u0026#34;, \u0026#34;\u0026#34;)[:500] platforms = d.get(\u0026#34;platforms\u0026#34;, {}) for chain, addr in platforms.items(): if addr: result[\u0026#34;chain\u0026#34;] = chain result[\u0026#34;contract\u0026#34;] = addr break # CoinGecko没价格时（新币常见），从币安合约/现货补充 if not result[\u0026#34;price\u0026#34;]: binance_price = await _fetch_binance_price(symbol, client) if binance_price: result[\u0026#34;price\u0026#34;] = binance_price ts = result.get(\u0026#34;total_supply\u0026#34;) or 0 cs = result.get(\u0026#34;circ_supply\u0026#34;) or 0 if ts \u0026gt; 0: result[\u0026#34;fdv\u0026#34;] = binance_price * ts if cs \u0026gt; 0: result[\u0026#34;mcap\u0026#34;] = binance_price * cs logger.info(f\u0026#34;币安补充价格 {symbol}: ${binance_price}, FDV=${result.get(\u0026#39;fdv\u0026#39;,0):,.0f}\u0026#34;) except Exception as e: logger.warning(f\u0026#34;CoinGecko查询失败 {symbol}: {e}\u0026#34;) return result async def _fetch_binance_price(symbol: str, client: httpx.AsyncClient) -\u0026gt; float: \u0026#34;\u0026#34;\u0026#34;从币安现货或合约获取价格\u0026#34;\u0026#34;\u0026#34; pair = f\u0026#34;{symbol.upper()}USDT\u0026#34; # 1. 现货 try: resp = await client.get(f\u0026#34;https://api.binance.com/api/v3/ticker/price\u0026#34;, params={\u0026#34;symbol\u0026#34;: pair}) if resp.status_code == 200: return float(resp.json()[\u0026#34;price\u0026#34;]) except Exception: pass # 2. 合约 try: resp = await client.get(f\u0026#34;https://fapi.binance.com/fapi/v1/ticker/price\u0026#34;, params={\u0026#34;symbol\u0026#34;: pair}) if resp.status_code == 200: return float(resp.json()[\u0026#34;price\u0026#34;]) except Exception: pass return 0.0 # ============================================================ # LLM 叙事抽取（可选，降级为规则） # ============================================================ async def llm_extract(raw_text: str, symbol: str, name: str = \u0026#34;\u0026#34;, cg_data: dict = None) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34;用LLM从公告+CoinGecko数据抽取叙事/VC/是否亲儿子\u0026#34;\u0026#34;\u0026#34; fallback = { \u0026#34;narrative\u0026#34;: \u0026#34;unknown\u0026#34;, \u0026#34;narrative_desc\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;vcs\u0026#34;: [], \u0026#34;is_darling\u0026#34;: False, \u0026#34;exclude_reason\u0026#34;: None, } # 从CoinGecko categories自动提取信息 cg_data = cg_data or {} categories = cg_data.get(\u0026#34;categories\u0026#34;, []) description = cg_data.get(\u0026#34;description\u0026#34;, \u0026#34;\u0026#34;) # 自动检测亲儿子（从categories） darling_cats = [c for c in categories if any(kw in c.lower() for kw in [\u0026#34;yzi labs\u0026#34;, \u0026#34;binance labs\u0026#34;])] if darling_cats: fallback[\u0026#34;is_darling\u0026#34;] = True if not ANTHROPIC_API_KEY: # 降级：从标题+categories关键词猜 t = raw_text.lower() for kw in BINANCE_DARLING_KEYWORDS: if kw in t: fallback[\u0026#34;is_darling\u0026#34;] = True # 从categories猜叙事 cat_str = \u0026#34; \u0026#34;.join(categories).lower() if \u0026#34;defi\u0026#34; in cat_str: fallback[\u0026#34;narrative\u0026#34;] = \u0026#34;defi\u0026#34; elif \u0026#34;ai\u0026#34; in cat_str: fallback[\u0026#34;narrative\u0026#34;] = \u0026#34;ai_agent\u0026#34; elif \u0026#34;gaming\u0026#34; in cat_str or \u0026#34;gamefi\u0026#34; in cat_str: fallback[\u0026#34;narrative\u0026#34;] = \u0026#34;gamefi\u0026#34; elif \u0026#34;meme\u0026#34; in cat_str: fallback[\u0026#34;narrative\u0026#34;] = \u0026#34;meme\u0026#34; elif \u0026#34;rwa\u0026#34; in cat_str or \u0026#34;real world\u0026#34; in cat_str: fallback[\u0026#34;narrative\u0026#34;] = \u0026#34;rwa\u0026#34; return fallback # 构建丰富的上下文 extra_context = \u0026#34;\u0026#34; if categories: extra_context += f\u0026#34;\\nCoinGecko分类: {\u0026#39;, \u0026#39;.join(categories)}\u0026#34; if description: extra_context += f\u0026#34;\\n项目描述: {description[:300]}\u0026#34; if cg_data.get(\u0026#34;found\u0026#34;): extra_context += f\u0026#34;\\n市场数据: FDV=${cg_data.get(\u0026#39;fdv\u0026#39;,0):,.0f}, MCap=${cg_data.get(\u0026#39;mcap\u0026#39;,0):,.0f}, 价格=${cg_data.get(\u0026#39;price\u0026#39;,0)}\u0026#34; if cg_data.get(\u0026#34;chain\u0026#34;): extra_context += f\u0026#34;, 链={cg_data[\u0026#39;chain\u0026#39;]}\u0026#34; system = \u0026#34;你是加密货币研究员，从币安公告和项目数据中提取关键信息。只返回JSON，无其他文字。\u0026#34; user = f\u0026#34;\u0026#34;\u0026#34;分析这个币安上新项目： 代币: {symbol}, 项目名: {name or \u0026#34;未知\u0026#34;} 公告原文: {raw_text} {extra_context} 返回JSON: {{ \u0026#34;narrative\u0026#34;: \u0026#34;defi_perp|ai_agent|ai_defi|defai|zk_proof|infra|defi|rwa|gamefi|meme|social|stablecoin|unknown\u0026#34;, \u0026#34;narrative_desc\u0026#34;: \u0026#34;一句话中文描述这个项目做什么、有什么特点\u0026#34;, \u0026#34;vcs\u0026#34;: [\u0026#34;从CoinGecko分类和公告中提取的投资机构列表\u0026#34;], \u0026#34;is_darling\u0026#34;: true/false, \u0026#34;exclude_reason\u0026#34;: null|\u0026#34;already_tge\u0026#34;|\u0026#34;meme_only\u0026#34; }} 判断规则: - narrative: 选最主要的一个类别 - vcs: CoinGecko分类里如果有 \u0026#34;XXX Portfolio\u0026#34; 就提取XXX作为机构 - is_darling: 如果有YZi Labs/Binance Labs投资 或 CZ/何一站台 则true - exclude_reason: 只有当项目在其他主要CEX(如Coinbase/OKX/Bybit)上线超过3个月才算\u0026#34;already_tge\u0026#34;。如果只是在DEX或刚在币安上线，不算already_tge。CoinGecko有价格数据不代表already_tge。纯meme无叙事则\u0026#34;meme_only\u0026#34; \u0026#34;\u0026#34;\u0026#34; try: async with httpx.AsyncClient(timeout=30, headers=HEADERS) as client: resp = await client.post( f\u0026#34;{ANTHROPIC_BASE_URL.rstrip(\u0026#39;/\u0026#39;)}/v1/messages\u0026#34;, headers={ \u0026#34;x-api-key\u0026#34;: ANTHROPIC_API_KEY, \u0026#34;anthropic-version\u0026#34;: \u0026#34;2023-06-01\u0026#34;, \u0026#34;content-type\u0026#34;: \u0026#34;application/json\u0026#34;, }, json={ \u0026#34;model\u0026#34;: ANTHROPIC_MODEL, \u0026#34;max_tokens\u0026#34;: 800, \u0026#34;temperature\u0026#34;: 0, \u0026#34;system\u0026#34;: system, \u0026#34;messages\u0026#34;: [{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: user}], } ) if resp.status_code != 200: logger.warning(f\u0026#34;LLM调用失败 {resp.status_code}\u0026#34;) return fallback data = resp.json() text = \u0026#34;\u0026#34; for block in data.get(\u0026#34;content\u0026#34;, []): if block.get(\u0026#34;type\u0026#34;) == \u0026#34;text\u0026#34;: text = block.get(\u0026#34;text\u0026#34;, \u0026#34;\u0026#34;) break text = text.strip() if text.startswith(\u0026#34;```\u0026#34;): lines = text.split(\u0026#34;\\n\u0026#34;) text = \u0026#34;\\n\u0026#34;.join(lines[1:-1]) return json.loads(text) except Exception as e: logger.warning(f\u0026#34;LLM抽取异常: {e}\u0026#34;) return fallback # ============================================================ # 消息格式化 # ============================================================ def _fmt_mcap(v): if not v: return \u0026#34;N/A\u0026#34; if v \u0026gt;= 1e9: return f\u0026#34;${v/1e9:.1f}B\u0026#34; if v \u0026gt;= 1e6: return f\u0026#34;${v/1e6:.1f}M\u0026#34; if v \u0026gt;= 1e3: return f\u0026#34;${v/1e3:.0f}K\u0026#34; return f\u0026#34;${v:.0f}\u0026#34; def _fmt_price(v): if not v: return \u0026#34;N/A\u0026#34; if v \u0026gt;= 1: return f\u0026#34;${v:.2f}\u0026#34; if v \u0026gt;= 0.01: return f\u0026#34;${v:.4f}\u0026#34; return f\u0026#34;${v:.6f}\u0026#34; def fmt_discovery(p: dict) -\u0026gt; str: tier = p.get(\u0026#34;tier\u0026#34;, \u0026#34;C\u0026#34;) icon = TIER_ICONS.get(tier, \u0026#34;⚪\u0026#34;) label = TIER_LABELS.get(tier, \u0026#34;\u0026#34;) symbol = p[\u0026#34;symbol\u0026#34;] name = p.get(\u0026#34;name\u0026#34;) or \u0026#34;\u0026#34; vcs = json.loads(p.get(\u0026#34;vcs_json\u0026#34;, \u0026#34;[]\u0026#34;)) if isinstance(p.get(\u0026#34;vcs_json\u0026#34;), str) else p.get(\u0026#34;vcs\u0026#34;, []) lines = [ f\u0026#34;{icon} \u0026lt;b\u0026gt;Alpha 首发 · ${symbol}\u0026lt;/b\u0026gt; {icon}\u0026#34;, f\u0026#34;📋 {label}\u0026#34;, \u0026#34;\u0026#34;, f\u0026#34;\u0026lt;b\u0026gt;{name}\u0026lt;/b\u0026gt;\u0026#34; if name else \u0026#34;\u0026#34;, ] if p.get(\u0026#34;narrative_desc\u0026#34;): lines.append(f\u0026#34;💡 {p[\u0026#39;narrative_desc\u0026#39;]}\u0026#34;) if p.get(\u0026#34;narrative\u0026#34;) and p[\u0026#34;narrative\u0026#34;] != \u0026#34;unknown\u0026#34;: lines.append(f\u0026#34;🏷 叙事: {p[\u0026#39;narrative\u0026#39;]}\u0026#34;) lines.append(\u0026#34;\u0026#34;) if p.get(\u0026#34;fdv\u0026#34;): lines.append(f\u0026#34;📊 FDV: {_fmt_mcap(p[\u0026#39;fdv\u0026#39;])}\u0026#34;) if p.get(\u0026#34;circulating_mcap\u0026#34;): lines.append(f\u0026#34;📊 流通市值: {_fmt_mcap(p[\u0026#39;circulating_mcap\u0026#39;])}\u0026#34;) if p.get(\u0026#34;open_price\u0026#34;): lines.append(f\u0026#34;💰 预估开盘价: {_fmt_price(p[\u0026#39;open_price\u0026#39;])}\u0026#34;) if p.get(\u0026#34;total_supply\u0026#34;) and p.get(\u0026#34;circulating_supply\u0026#34;): pct = p[\u0026#34;circulating_supply\u0026#34;] / p[\u0026#34;total_supply\u0026#34;] * 100 lines.append(f\u0026#34;📦 初始流通: {pct:.1f}%\u0026#34;) if vcs: lines.append(\u0026#34;\u0026#34;) lines.append(\u0026#34;🏛 \u0026lt;b\u0026gt;机构\u0026lt;/b\u0026gt;\u0026#34;) for v in vcs[:5]: is_t1 = any(t in v.lower() for t in TIER1_VCS) lines.append(f\u0026#34; {\u0026#39;⭐\u0026#39; if is_t1 else \u0026#39;·\u0026#39;} {v}\u0026#34;) if p.get(\u0026#34;is_darling\u0026#34;): lines.append(\u0026#34;\u0026#34;) lines.append(\u0026#34;🔥 \u0026lt;b\u0026gt;币安亲儿子\u0026lt;/b\u0026gt;\u0026#34;) if p.get(\u0026#34;tier_reason\u0026#34;): lines.append(\u0026#34;\u0026#34;) lines.append(f\u0026#34;🎯 {p[\u0026#39;tier_reason\u0026#39;]}\u0026#34;) lines.append(\u0026#34;\u0026#34;) lines.append(f\u0026#34;\u0026lt;i\u0026gt;📌 来源: {p.get(\u0026#39;source\u0026#39;, \u0026#39;binance\u0026#39;)}\u0026lt;/i\u0026gt;\u0026#34;) if p.get(\u0026#34;raw_text\u0026#34;): lines.append(f\u0026#34;\u0026lt;i\u0026gt;{p[\u0026#39;raw_text\u0026#39;][:120]}\u0026lt;/i\u0026gt;\u0026#34;) return \u0026#34;\\n\u0026#34;.join(l for l in lines if l is not None) def fmt_countdown(p: dict, minutes: int) -\u0026gt; str: icon = TIER_ICONS.get(p.get(\u0026#34;tier\u0026#34;, \u0026#34;C\u0026#34;), \u0026#34;⚪\u0026#34;) t = f\u0026#34;{minutes//60}h{minutes%60}m\u0026#34; if minutes \u0026gt;= 60 else f\u0026#34;{minutes}m\u0026#34; lines = [ f\u0026#34;{icon} \u0026lt;b\u0026gt;倒计时提醒\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;\u0026lt;b\u0026gt;${p[\u0026#39;symbol\u0026#39;]}\u0026lt;/b\u0026gt; · {p.get(\u0026#39;name\u0026#39;, \u0026#39;\u0026#39;)}\u0026#34;, f\u0026#34;⏰ 距上线还有 \u0026lt;b\u0026gt;{t}\u0026lt;/b\u0026gt;\u0026#34;, ] if p.get(\u0026#34;fdv\u0026#34;): lines.append(f\u0026#34;FDV: {_fmt_mcap(p[\u0026#39;fdv\u0026#39;])}\u0026#34;) if minutes \u0026lt;= 30: lines.append(\u0026#34;🔔 \u0026lt;b\u0026gt;准备下单\u0026lt;/b\u0026gt;\u0026#34;) return \u0026#34;\\n\u0026#34;.join(lines) def fmt_launch(p: dict, price: float, mcap: float, fdv: float) -\u0026gt; str: lines = [ f\u0026#34;🚀 \u0026lt;b\u0026gt;${p[\u0026#39;symbol\u0026#39;]} 已上线\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;开盘价: \u0026lt;b\u0026gt;{_fmt_price(price)}\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;流通市值: \u0026lt;b\u0026gt;{_fmt_mcap(mcap)}\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;FDV: \u0026lt;b\u0026gt;{_fmt_mcap(fdv)}\u0026lt;/b\u0026gt;\u0026#34;, ] return \u0026#34;\\n\u0026#34;.join(lines) def fmt_periodic(p: dict, idx: int, price: float, mcap: float, change_pct: float) -\u0026gt; str: arrow = \u0026#34;📈\u0026#34; if change_pct \u0026gt; 0 else \u0026#34;📉\u0026#34; minutes = 30 * idx lines = [ f\u0026#34;⏱ \u0026lt;b\u0026gt;${p[\u0026#39;symbol\u0026#39;]} · +{minutes}min\u0026lt;/b\u0026gt;\u0026#34;, f\u0026#34;流通市值: {_fmt_mcap(mcap)} ({arrow} {change_pct:+.1f}%)\u0026#34;, f\u0026#34;当前价: {_fmt_price(price)}\u0026#34;, ] if change_pct \u0026gt;= 100: lines.append(\u0026#34;💡 \u0026lt;b\u0026gt;已翻倍，考虑分批止盈\u0026lt;/b\u0026gt;\u0026#34;) elif change_pct \u0026lt;= -30: lines.append(\u0026#34;⚠️ 跌幅较大，评估是否止损\u0026#34;) return \u0026#34;\\n\u0026#34;.join(lines) def fmt_anomaly(p: dict, atype: str, price: float, change_pct: float) -\u0026gt; str: emoji = {\u0026#34;double\u0026#34;: \u0026#34;🚀\u0026#34;, \u0026#34;halve\u0026#34;: \u0026#34;🔻\u0026#34;}.get(atype, \u0026#34;⚡\u0026#34;) desc = {\u0026#34;double\u0026#34;: \u0026#34;市值翻倍\u0026#34;, \u0026#34;halve\u0026#34;: \u0026#34;市值腰斩\u0026#34;}.get(atype, \u0026#34;异动\u0026#34;) return f\u0026#34;{emoji} \u0026lt;b\u0026gt;${p[\u0026#39;symbol\u0026#39;]} {desc}\u0026lt;/b\u0026gt;\\n变化: {change_pct:+.1f}%\\n当前价: {_fmt_price(price)}\u0026#34; # ============================================================ # 核心逻辑: 公告监听 # ============================================================ async def announcement_listener(): \u0026#34;\u0026#34;\u0026#34;轮询币安公告，发现新Alpha项目\u0026#34;\u0026#34;\u0026#34; logger.info(f\u0026#34;📡 公告监听启动 · 轮询 {ANNOUNCEMENT_POLL_INTERVAL}s\u0026#34;) while True: try: articles = await fetch_announcements() new_count = 0 for art in articles: title = art.get(\u0026#34;title\u0026#34;, \u0026#34;\u0026#34;) triggered, reason = is_trigger(title) if not triggered: continue symbol = extract_symbol(title) if not symbol: continue # 用发布日期去重 release_ts = art.get(\u0026#34;releaseDate\u0026#34;) release_iso = datetime.fromtimestamp(release_ts / 1000).isoformat() if release_ts else \u0026#34;\u0026#34; launch_date = release_iso[:10] if release_iso else datetime.utcnow().date().isoformat() pid = project_id(symbol, launch_date) if project_exists(pid): continue project = { \u0026#34;id\u0026#34;: pid, \u0026#34;symbol\u0026#34;: symbol, \u0026#34;name\u0026#34;: extract_name(title), \u0026#34;launch_time\u0026#34;: release_iso, \u0026#34;source\u0026#34;: \u0026#34;binance_announcement\u0026#34;, \u0026#34;raw_text\u0026#34;: title, \u0026#34;tier\u0026#34;: \u0026#34;PENDING\u0026#34;, \u0026#34;vcs\u0026#34;: [], \u0026#34;is_darling\u0026#34;: False, \u0026#34;excluded\u0026#34;: 0, } save_project(project) new_count += 1 logger.info(f\u0026#34;🆕 发现 ${symbol}: {title[:80]}\u0026#34;) if new_count: logger.info(f\u0026#34;本轮发现 {new_count} 个新项目\u0026#34;) except Exception as e: logger.error(f\u0026#34;公告监听异常: {e}\u0026#34;, exc_info=True) await asyncio.sleep(ANNOUNCEMENT_POLL_INTERVAL) # ============================================================ # 核心逻辑: 聚合 + 推送 # ============================================================ async def aggregation_worker(): \u0026#34;\u0026#34;\u0026#34;对PENDING项目做数据聚合、评级、推送\u0026#34;\u0026#34;\u0026#34; logger.info(f\u0026#34;🧠 聚合工作者启动 · 轮询 {AGGREGATION_POLL_INTERVAL}s\u0026#34;) while True: try: pending = list_pending() for p in pending: symbol = p[\u0026#34;symbol\u0026#34;] try: logger.info(f\u0026#34;📦 聚合 ${symbol}\u0026#34;) # 1. CoinGecko cg = await fetch_coingecko(symbol, project_name=p.get(\u0026#39;name\u0026#39;, \u0026#39;\u0026#39;)) await asyncio.sleep(1) # 避免限流 # 2. LLM抽取叙事（传入CoinGecko数据增强分析） llm = await llm_extract(p.get(\u0026#34;raw_text\u0026#34;, \u0026#34;\u0026#34;), symbol, p.get(\u0026#34;name\u0026#34;), cg_data=cg) await asyncio.sleep(1) # 3. 判断是否排除 if llm.get(\u0026#34;exclude_reason\u0026#34;) in (\u0026#34;already_tge\u0026#34;, \u0026#34;meme_only\u0026#34;): update_project(p[\u0026#34;id\u0026#34;], { \u0026#34;excluded\u0026#34;: 1, \u0026#34;exclude_reason\u0026#34;: llm[\u0026#34;exclude_reason\u0026#34;], \u0026#34;tier\u0026#34;: \u0026#34;EXCLUDED\u0026#34;, }) logger.info(f\u0026#34;⏭ ${symbol} 排除: {llm[\u0026#39;exclude_reason\u0026#39;]}\u0026#34;) continue # 4. 数据校验 — CoinGecko数据可能匹配错或项目太新没数据 cg_fdv = cg.get(\u0026#34;fdv\u0026#34;, 0) or 0 cg_mcap = cg.get(\u0026#34;mcap\u0026#34;, 0) or 0 data_suspect = False data_warnings = [] # 上币安的项目FDV不可能低于$100K（除非CoinGecko匹配错了） if cg.get(\u0026#34;found\u0026#34;) and cg_fdv \u0026gt; 0 and cg_fdv \u0026lt; 100_000: data_suspect = True data_warnings.append(f\u0026#34;FDV=${cg_fdv:,.0f}异常低，可能CoinGecko匹配错误\u0026#34;) logger.warning(f\u0026#34;⚠️ {symbol} FDV=${cg_fdv:,.0f} 异常低，数据可能不准\u0026#34;) # 流通100%的大项目也要警惕（新代币通常不会100%流通） if (cg.get(\u0026#34;circ_supply\u0026#34;) and cg.get(\u0026#34;total_supply\u0026#34;) and cg[\u0026#34;circ_supply\u0026#34;] == cg[\u0026#34;total_supply\u0026#34;] and cg_fdv \u0026lt; 1_000_000): data_suspect = True data_warnings.append(\u0026#34;流通=总量且FDV极低，可能是同名垃圾币\u0026#34;) if data_suspect: # 标记数据不可靠，评级时不使用CoinGecko的FDV/MCap cg_fdv = 0 cg_mcap = 0 logger.warning(f\u0026#34;⚠️ {symbol} CoinGecko数据不可靠，评级使用LLM判断为主\u0026#34;) # 5. 评级 is_darling = llm.get(\u0026#34;is_darling\u0026#34;, False) vcs = llm.get(\u0026#34;vcs\u0026#34;, []) narrative = llm.get(\u0026#34;narrative\u0026#34;, \u0026#34;unknown\u0026#34;) rating = rate_project( cg_mcap, cg_fdv, vcs, narrative, is_darling ) # 6. 更新DB（data_suspect时不存CoinGecko的错误数据） update_project(p[\u0026#34;id\u0026#34;], { \u0026#34;tier\u0026#34;: rating[\u0026#34;tier\u0026#34;], \u0026#34;tier_reason\u0026#34;: rating[\u0026#34;reason\u0026#34;], \u0026#34;narrative\u0026#34;: narrative, \u0026#34;narrative_desc\u0026#34;: llm.get(\u0026#34;narrative_desc\u0026#34;, \u0026#34;\u0026#34;), \u0026#34;vcs_json\u0026#34;: json.dumps(vcs), \u0026#34;is_darling\u0026#34;: int(is_darling), \u0026#34;open_price\u0026#34;: cg.get(\u0026#34;price\u0026#34;) if not data_suspect else None, \u0026#34;total_supply\u0026#34;: cg.get(\u0026#34;total_supply\u0026#34;) if not data_suspect else None, \u0026#34;circulating_supply\u0026#34;: cg.get(\u0026#34;circ_supply\u0026#34;) if not data_suspect else None, \u0026#34;fdv\u0026#34;: cg_fdv if cg_fdv else None, \u0026#34;circulating_mcap\u0026#34;: cg_mcap if cg_mcap else None, }) # 6. 推送discovery full = get_project(p[\u0026#34;id\u0026#34;]) if full and not has_pushed(p[\u0026#34;id\u0026#34;], \u0026#34;discovery\u0026#34;): text = fmt_discovery(full) silent = rating[\u0026#34;tier\u0026#34;] in (\u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;) ok = await send_tg(text, silent=silent) if ok: log_push(p[\u0026#34;id\u0026#34;], \u0026#34;discovery\u0026#34;, text) logger.info(f\u0026#34;✅ 推送 ${symbol} [{rating[\u0026#39;tier\u0026#39;]}]\u0026#34;) except Exception as e: logger.error(f\u0026#34;聚合 {symbol} 失败: {e}\u0026#34;, exc_info=True) update_project(p[\u0026#34;id\u0026#34;], {\u0026#34;tier\u0026#34;: \u0026#34;ERROR\u0026#34;, \u0026#34;tier_reason\u0026#34;: str(e)[:100]}) except Exception as e: logger.error(f\u0026#34;聚合循环异常: {e}\u0026#34;, exc_info=True) await asyncio.sleep(AGGREGATION_POLL_INTERVAL) # ============================================================ # 核心逻辑: 上线后监控 # ============================================================ async def post_launch_monitor(): \u0026#34;\u0026#34;\u0026#34;倒计时提醒 + 上线瞬间 + 30min×4跟踪 + 异动\u0026#34;\u0026#34;\u0026#34; logger.info(f\u0026#34;📊 上线监控启动 · 轮询 {MONITOR_POLL_INTERVAL}s\u0026#34;) while True: try: projects = list_active() for p in projects: try: await _monitor_project(p) except Exception as e: logger.error(f\u0026#34;监控 {p[\u0026#39;symbol\u0026#39;]} 异常: {e}\u0026#34;) except Exception as e: logger.error(f\u0026#34;监控循环异常: {e}\u0026#34;, exc_info=True) await asyncio.sleep(MONITOR_POLL_INTERVAL) async def _monitor_project(p: dict): pid = p[\u0026#34;id\u0026#34;] symbol = p[\u0026#34;symbol\u0026#34;] launch_str = p.get(\u0026#34;launch_time\u0026#34;, \u0026#34;\u0026#34;) if not launch_str: return try: launch = datetime.fromisoformat(launch_str.replace(\u0026#34;Z\u0026#34;, \u0026#34;\u0026#34;).split(\u0026#34;+\u0026#34;)[0]) except: return now = datetime.utcnow() delta_sec = (launch - now).total_seconds() # T-3h if 3*3600 - 300 \u0026lt;= delta_sec \u0026lt;= 3*3600 + 300: if not has_pushed(pid, \u0026#34;t_minus_3h\u0026#34;): text = fmt_countdown(p, int(delta_sec / 60)) ok = await send_tg(text, silent=p.get(\u0026#34;tier\u0026#34;) in (\u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;)) if ok: log_push(pid, \u0026#34;t_minus_3h\u0026#34;, text) # T-30m elif 30*60 - 150 \u0026lt;= delta_sec \u0026lt;= 30*60 + 150: if not has_pushed(pid, \u0026#34;t_minus_30m\u0026#34;): text = fmt_countdown(p, int(delta_sec / 60)) ok = await send_tg(text, silent=False) if ok: log_push(pid, \u0026#34;t_minus_30m\u0026#34;, text) # 上线瞬间 elif -300 \u0026lt;= delta_sec \u0026lt;= 0: if not has_pushed(pid, \u0026#34;at_launch\u0026#34;): cg = await fetch_coingecko(symbol, project_name=p.get(\u0026#39;name\u0026#39;, \u0026#39;\u0026#39;)) if cg.get(\u0026#34;price\u0026#34;): text = fmt_launch(p, cg[\u0026#34;price\u0026#34;], cg.get(\u0026#34;mcap\u0026#34;, 0), cg.get(\u0026#34;fdv\u0026#34;, 0)) ok = await send_tg(text, silent=False) if ok: log_push(pid, \u0026#34;at_launch\u0026#34;, text) save_snapshot(pid, cg[\u0026#34;price\u0026#34;], cg.get(\u0026#34;mcap\u0026#34;, 0), cg.get(\u0026#34;fdv\u0026#34;, 0)) # 上线后30min × 4 elif 0 \u0026lt; -delta_sec \u0026lt;= 2.5 * 3600: minutes_after = int(-delta_sec / 60) for idx, target in enumerate([30, 60, 90, 120], 1): if abs(minutes_after - target) \u0026lt;= 5: ptype = f\u0026#34;post_30m_{idx}\u0026#34; if not has_pushed(pid, ptype): cg = await fetch_coingecko(symbol, project_name=p.get(\u0026#39;name\u0026#39;, \u0026#39;\u0026#39;)) if cg.get(\u0026#34;price\u0026#34;): open_price = p.get(\u0026#34;open_price\u0026#34;) or cg[\u0026#34;price\u0026#34;] change = ((cg[\u0026#34;price\u0026#34;] - open_price) / open_price * 100) if open_price else 0 text = fmt_periodic(p, idx, cg[\u0026#34;price\u0026#34;], cg.get(\u0026#34;mcap\u0026#34;, 0), change) ok = await send_tg(text, silent=p.get(\u0026#34;tier\u0026#34;) in (\u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;) and idx \u0026gt; 1) if ok: log_push(pid, ptype, text) save_snapshot(pid, cg[\u0026#34;price\u0026#34;], cg.get(\u0026#34;mcap\u0026#34;, 0), cg.get(\u0026#34;fdv\u0026#34;, 0)) # 异动 if change \u0026gt;= 100 and not has_pushed(pid, \u0026#34;anomaly_double\u0026#34;): t = fmt_anomaly(p, \u0026#34;double\u0026#34;, cg[\u0026#34;price\u0026#34;], change) if await send_tg(t): log_push(pid, \u0026#34;anomaly_double\u0026#34;, t) elif change \u0026lt;= -50 and not has_pushed(pid, \u0026#34;anomaly_halve\u0026#34;): t = fmt_anomaly(p, \u0026#34;halve\u0026#34;, cg[\u0026#34;price\u0026#34;], change) if await send_tg(t): log_push(pid, \u0026#34;anomaly_halve\u0026#34;, t) break # ============================================================ # 启动 # ============================================================ async def main(): init_db() logger.info(f\u0026#34;📂 数据库: {DB_PATH}\u0026#34;) # 测试TG ok = await send_tg(\u0026#34;🎉 \u0026lt;b\u0026gt;Alpha Monitor v2 启动\u0026lt;/b\u0026gt;\\n\\n📡 币安公告监听中...\\n🔔 有新Alpha会立即推送\u0026#34;) if ok: logger.info(\u0026#34;✅ TG推送正常\u0026#34;) else: logger.warning(\u0026#34;⚠️ TG推送失败，检查配置\u0026#34;) tasks = [ asyncio.create_task(announcement_listener(), name=\u0026#34;announcements\u0026#34;), asyncio.create_task(aggregation_worker(), name=\u0026#34;aggregator\u0026#34;), asyncio.create_task(post_launch_monitor(), name=\u0026#34;monitor\u0026#34;), ] logger.info(\u0026#34;=\u0026#34; * 50) logger.info(\u0026#34;🚀 Alpha Monitor v2 启动完成\u0026#34;) logger.info(f\u0026#34; 📡 公告轮询: {ANNOUNCEMENT_POLL_INTERVAL}s\u0026#34;) logger.info(f\u0026#34; 🧠 LLM: {\u0026#39;Sonnet\u0026#39; if ANTHROPIC_API_KEY else \u0026#39;降级(规则)\u0026#39;}\u0026#34;) logger.info(f\u0026#34; 🔔 TG: {\u0026#39;✅\u0026#39; if TG_BOT_TOKEN else \u0026#39;❌\u0026#39;}\u0026#34;) logger.info(\u0026#34;=\u0026#34; * 50) try: await asyncio.gather(*tasks) except KeyboardInterrupt: for t in tasks: t.cancel() if __name__ == \u0026#34;__main__\u0026#34;: try: asyncio.run(main()) except KeyboardInterrupt: pass AI自主交易 合约+Alpha代币 自主交易 v1 发布日期: 2026.04.29　标签: Python · Binance Futures · Autonomous · Telegram\nAI独立完成 扫描→分析→虚拟开仓→盯盘→平仓→复盘 完整交易循环\n⚠️ 风险提示：本代码仅运行过虚拟盘，没有任何实盘交易经验，不能作为接入实盘的交易依据。本代码未经过真实资金的实际交易验证，使用本代码时请务必谨慎，风险自担。\nAI每30秒全自动扫描币安全市场合约，发现异常信号后独立分析并虚拟开仓。4种信号检测策略（费率极端深负→做多逼空、费率极端正→做空、暴涨后回落做空、暴跌企稳反弹）。开仓前必须通过多维度综合环境检查（BTC环境+Fear\u0026amp;Greed情绪+OI关注度+成交量活跃度），得分\u0026gt;=3/7才开仓。每30秒自动检查持仓止损止盈。\n当前成绩（诚实公布）：已平仓4笔，胜率75%，收益+13.94U（初始100U）。但利润集中在一笔（IR做空+45%），去掉这笔基本持平。持仓风险分散不够，容易同方向同逻辑重仓。综合判断仍是规则打分，不是真正独立思考。\n做不到：不能像人类交易员理解叙事转折，不能判断信号背后深层原因，容易重复犯同类错误，无法处理突发事件。\n训练路径：扫描→开仓→平仓→复盘→发现问题→改进策略→再循环。目标：从规则执行器进化为独立思考的交易员。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; 市场扫描器 - 每分钟运行 纯Python零AI成本，发现异常信号自动开仓 \u0026#34;\u0026#34;\u0026#34; import json import os import sys import time import requests from datetime import datetime, timezone, timedelta SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_FILE = os.path.join(SCRIPT_DIR, \u0026#34;trades.json\u0026#34;) SCANNER_STATE = os.path.join(SCRIPT_DIR, \u0026#34;scanner_state.json\u0026#34;) SCANNER_LOG = os.path.join(SCRIPT_DIR, \u0026#34;scanner.log\u0026#34;) INITIAL_BALANCE = 100.0 TZ_UTC8 = timezone(timedelta(hours=8)) # === 配置 === MAX_OPEN_POSITIONS = 3 # 最多同时持仓 POSITION_PCT = 30 # 每笔仓位占比% LEVERAGE = 3 # 杠杆 COOLDOWN_HOURS = 4 # 同一币种冷却时间 MIN_VOLUME_M = 10 # 最小24h成交额(百万U) # === TG推送 === def load_tg_config(): \u0026#34;\u0026#34;\u0026#34;Load TG config from environment variables or .env file\u0026#34;\u0026#34;\u0026#34; env = {} # Try .env in script directory, then current directory for env_path in [ os.path.join(SCRIPT_DIR, \u0026#34;.env\u0026#34;), os.path.join(os.getcwd(), \u0026#34;.env\u0026#34;), ]: if os.path.exists(env_path): with open(env_path) as f: for line in f: line = line.strip() if \u0026#39;=\u0026#39; in line and not line.startswith(\u0026#39;#\u0026#39;): k, v = line.split(\u0026#39;=\u0026#39;, 1) env[k] = v.strip().strip(\u0026#39;\u0026#34;\u0026#39;).strip(\u0026#34;\u0026#39;\u0026#34;) break # OS environment variables override file for key in [\u0026#39;TG_BOT_TOKEN\u0026#39;, \u0026#39;TELEGRAM_BOT_TOKEN\u0026#39;, \u0026#39;TG_CHAT_ID\u0026#39;]: val = os.environ.get(key) if val: env[key] = val return env def send_tg(text): try: env = load_tg_config() token = env.get(\u0026#39;TG_BOT_TOKEN\u0026#39;, env.get(\u0026#39;TELEGRAM_BOT_TOKEN\u0026#39;, \u0026#39;\u0026#39;)) if not token: return chat_id = env.get(\u0026#39;TG_CHAT_ID\u0026#39;, \u0026#39;\u0026#39;) if not chat_id: return url = f\u0026#34;https://api.telegram.org/bot{token}/sendMessage\u0026#34; requests.post(url, json={ \u0026#34;chat_id\u0026#34;: chat_id, \u0026#34;text\u0026#34;: text, \u0026#34;parse_mode\u0026#34;: \u0026#34;Markdown\u0026#34; }, timeout=10) except: pass # === 数据加载 === def load_trades(): if os.path.exists(DATA_FILE): with open(DATA_FILE, \u0026#34;r\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as f: return json.load(f) return {\u0026#34;initial_balance\u0026#34;: INITIAL_BALANCE, \u0026#34;trades\u0026#34;: []} def save_trades(data): with open(DATA_FILE, \u0026#34;w\u0026#34;, encoding=\u0026#34;utf-8\u0026#34;) as f: json.dump(data, f, ensure_ascii=False, indent=2) def load_state(): if os.path.exists(SCANNER_STATE): with open(SCANNER_STATE, \u0026#34;r\u0026#34;) as f: return json.load(f) return {\u0026#34;last_opens\u0026#34;: {}, \u0026#34;signals_seen\u0026#34;: {}} def save_state(state): with open(SCANNER_STATE, \u0026#34;w\u0026#34;) as f: json.dump(state, f, ensure_ascii=False, indent=2) def get_balance(data): balance = data.get(\u0026#34;initial_balance\u0026#34;, INITIAL_BALANCE) for t in data[\u0026#34;trades\u0026#34;]: if t[\u0026#34;status\u0026#34;] == \u0026#34;closed\u0026#34; and t[\u0026#34;pnl_usd\u0026#34;] is not None: balance += t[\u0026#34;pnl_usd\u0026#34;] return balance def next_id(data): if not data[\u0026#34;trades\u0026#34;]: return \u0026#34;001\u0026#34; max_id = max(int(t[\u0026#34;id\u0026#34;]) for t in data[\u0026#34;trades\u0026#34;]) return f\u0026#34;{max_id + 1:03d}\u0026#34; def now_str(): return datetime.now(TZ_UTC8).strftime(\u0026#34;%Y-%m-%dT%H:%M:%S\u0026#34;) def log(msg): ts = datetime.now(TZ_UTC8).strftime(\u0026#34;%m-%d %H:%M:%S\u0026#34;) line = f\u0026#34;[{ts}] {msg}\u0026#34; print(line) with open(SCANNER_LOG, \u0026#34;a\u0026#34;) as f: f.write(line + \u0026#34;\\n\u0026#34;) # === 币安API === def get_all_tickers(): url = \u0026#34;https://fapi.binance.com/fapi/v1/ticker/24hr\u0026#34; resp = requests.get(url, timeout=10) return resp.json() def get_funding_rates(): \u0026#34;\u0026#34;\u0026#34;获取所有币种最新费率\u0026#34;\u0026#34;\u0026#34; url = \u0026#34;https://fapi.binance.com/fapi/v1/premiumIndex\u0026#34; resp = requests.get(url, timeout=10) return {item[\u0026#39;symbol\u0026#39;]: float(item[\u0026#39;lastFundingRate\u0026#39;]) * 100 for item in resp.json()} def get_funding_history(symbol, limit=8): url = f\u0026#34;https://fapi.binance.com/fapi/v1/fundingRate?symbol={symbol}\u0026amp;limit={limit}\u0026#34; resp = requests.get(url, timeout=10) return [float(item[\u0026#39;fundingRate\u0026#39;]) * 100 for item in resp.json()] def get_open_interest(symbol): url = f\u0026#34;https://fapi.binance.com/fapi/v1/openInterest?symbol={symbol}\u0026#34; resp = requests.get(url, timeout=10) data = resp.json() return float(data[\u0026#39;openInterest\u0026#39;]) def get_klines(symbol, interval=\u0026#34;4h\u0026#34;, limit=6): url = f\u0026#34;https://fapi.binance.com/fapi/v1/klines?symbol={symbol}\u0026amp;interval={interval}\u0026amp;limit={limit}\u0026#34; resp = requests.get(url, timeout=10) return resp.json() # === 信号检测 === def detect_extreme_negative_funding(symbol, funding_rate, funding_rates_map): \u0026#34;\u0026#34;\u0026#34; 策略1: 费率极端深负 → 做多(逼空) 条件: 当前费率\u0026lt;-0.08% 且 连续多期为负 \u0026#34;\u0026#34;\u0026#34; if funding_rate \u0026gt;= -0.08: return None try: history = get_funding_history(symbol, 8) neg_count = sum(1 for r in history if r \u0026lt; -0.03) if neg_count \u0026lt; 4: return None avg_rate = sum(history) / len(history) # 费率越极端，信号越强 strength = \u0026#34;S\u0026#34; if avg_rate \u0026lt; -0.15 else \u0026#34;A\u0026#34; if avg_rate \u0026lt; -0.10 else \u0026#34;B\u0026#34; return { \u0026#34;type\u0026#34;: \u0026#34;extreme_neg_funding\u0026#34;, \u0026#34;direction\u0026#34;: \u0026#34;long\u0026#34;, \u0026#34;strength\u0026#34;: strength, \u0026#34;reason\u0026#34;: f\u0026#34;费率极端深负 avg:{avg_rate:.4f}% 连续{neg_count}/8期为负 逼空概率高\u0026#34;, \u0026#34;sl_pct\u0026#34;: 0.08, # 止损8% \u0026#34;tp_pct\u0026#34;: 0.12, # 止盈12% } except: return None def detect_extreme_positive_funding(symbol, funding_rate, funding_rates_map): \u0026#34;\u0026#34;\u0026#34; 策略2: 费率极端正 → 做空(多头拥挤) 条件: 当前费率\u0026gt;0.10% 且 连续多期高正 \u0026#34;\u0026#34;\u0026#34; if funding_rate \u0026lt;= 0.10: return None try: history = get_funding_history(symbol, 8) pos_count = sum(1 for r in history if r \u0026gt; 0.05) if pos_count \u0026lt; 4: return None avg_rate = sum(history) / len(history) strength = \u0026#34;S\u0026#34; if avg_rate \u0026gt; 0.20 else \u0026#34;A\u0026#34; if avg_rate \u0026gt; 0.12 else \u0026#34;B\u0026#34; return { \u0026#34;type\u0026#34;: \u0026#34;extreme_pos_funding\u0026#34;, \u0026#34;direction\u0026#34;: \u0026#34;short\u0026#34;, \u0026#34;strength\u0026#34;: strength, \u0026#34;reason\u0026#34;: f\u0026#34;费率极端正 avg:{avg_rate:.4f}% 连续{pos_count}/8期高正 多头过度拥挤\u0026#34;, \u0026#34;sl_pct\u0026#34;: 0.10, \u0026#34;tp_pct\u0026#34;: 0.15, } except: return None def detect_crash_bounce(ticker): \u0026#34;\u0026#34;\u0026#34; 策略3: 暴跌后反弹(超跌反弹) 条件: 24h跌\u0026gt;25% 但最近4h企稳/反弹 \u0026#34;\u0026#34;\u0026#34; change_pct = float(ticker[\u0026#39;priceChangePercent\u0026#39;]) if change_pct \u0026gt;= -25: return None symbol = ticker[\u0026#39;symbol\u0026#39;] try: klines = get_klines(symbol, \u0026#34;1h\u0026#34;, 6) # 最近2根K线 recent_closes = [float(k[4]) for k in klines[-3:]] # 企稳: 最近K线收盘 \u0026gt;= 前一根 if len(recent_closes) \u0026gt;= 2 and recent_closes[-1] \u0026gt;= recent_closes[-2]: return { \u0026#34;type\u0026#34;: \u0026#34;crash_bounce\u0026#34;, \u0026#34;direction\u0026#34;: \u0026#34;long\u0026#34;, \u0026#34;strength\u0026#34;: \u0026#34;B\u0026#34;, # 风险较高给B级 \u0026#34;reason\u0026#34;: f\u0026#34;24h暴跌{change_pct:.1f}%后企稳 超跌反弹\u0026#34;, \u0026#34;sl_pct\u0026#34;: 0.10, \u0026#34;tp_pct\u0026#34;: 0.15, } except: pass return None def detect_pump_short(ticker): \u0026#34;\u0026#34;\u0026#34; 策略4: 暴涨后做空(ATH回落) 条件: 24h涨\u0026gt;40% — 根据生命周期模型，暴涨后回调概率\u0026gt;85% 需要确认已经开始回落(不在最高点做空) \u0026#34;\u0026#34;\u0026#34; change_pct = float(ticker[\u0026#39;priceChangePercent\u0026#39;]) if change_pct \u0026lt;= 40: return None symbol = ticker[\u0026#39;symbol\u0026#39;] try: klines = get_klines(symbol, \u0026#34;1h\u0026#34;, 6) highs = [float(k[2]) for k in klines] closes = [float(k[4]) for k in klines] current = closes[-1] peak = max(highs) # 从最高点回落超过10%才做空 pullback = (peak - current) / peak * 100 if pullback \u0026lt; 10: return None strength = \u0026#34;A\u0026#34; if change_pct \u0026gt; 80 else \u0026#34;B\u0026#34; return { \u0026#34;type\u0026#34;: \u0026#34;pump_short\u0026#34;, \u0026#34;direction\u0026#34;: \u0026#34;short\u0026#34;, \u0026#34;strength\u0026#34;: strength, \u0026#34;reason\u0026#34;: f\u0026#34;24h暴涨{change_pct:.1f}%后回落{pullback:.1f}% 历史回调概率\u0026gt;85%\u0026#34;, \u0026#34;sl_pct\u0026#34;: 0.15, # 暴涨币波动大，止损宽一些 \u0026#34;tp_pct\u0026#34;: 0.20, } except: pass return None # === 综合环境检查 === def check_environment(symbol, signal): \u0026#34;\u0026#34;\u0026#34; 开仓前综合检查：不是单一信号触发就开，要多维度对齐 返回 (pass/fail, analysis_dict, adjusted_strength) \u0026#34;\u0026#34;\u0026#34; analysis = { \u0026#34;btc_env\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;sentiment\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;oi_check\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;volume_check\u0026#34;: \u0026#34;\u0026#34;, \u0026#34;verdict\u0026#34;: \u0026#34;\u0026#34; } score = 0 # 综合得分，\u0026gt;=3才开仓 try: # 1. BTC环境 — 做多需要BTC不在暴跌，做空需要BTC不在暴涨 btc_url = \u0026#34;https://fapi.binance.com/fapi/v1/ticker/24hr?symbol=BTCUSDT\u0026#34; btc = requests.get(btc_url, timeout=5).json() btc_chg = float(btc[\u0026#39;priceChangePercent\u0026#39;]) if signal[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34;: if btc_chg \u0026gt; -2: score += 1 analysis[\u0026#34;btc_env\u0026#34;] = f\u0026#34;BTC {btc_chg:+.1f}% 环境正常 +1\u0026#34; elif btc_chg \u0026lt; -5: score -= 1 analysis[\u0026#34;btc_env\u0026#34;] = f\u0026#34;BTC {btc_chg:+.1f}% 暴跌中做多危险 -1\u0026#34; else: analysis[\u0026#34;btc_env\u0026#34;] = f\u0026#34;BTC {btc_chg:+.1f}% 偏弱 0\u0026#34; else: # short if btc_chg \u0026lt; 2: score += 1 analysis[\u0026#34;btc_env\u0026#34;] = f\u0026#34;BTC {btc_chg:+.1f}% 环境正常 +1\u0026#34; elif btc_chg \u0026gt; 5: score -= 1 analysis[\u0026#34;btc_env\u0026#34;] = f\u0026#34;BTC {btc_chg:+.1f}% 暴涨中做空危险 -1\u0026#34; else: analysis[\u0026#34;btc_env\u0026#34;] = f\u0026#34;BTC {btc_chg:+.1f}% 偏强 0\u0026#34; # 2. 市场情绪(Fear \u0026amp; Greed) try: fng = requests.get(\u0026#34;https://api.alternative.me/fng/\u0026#34;, timeout=5).json() fng_val = int(fng[\u0026#39;data\u0026#39;][0][\u0026#39;value\u0026#39;]) if signal[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34;: if fng_val \u0026lt;= 25: score += 1 analysis[\u0026#34;sentiment\u0026#34;] = f\u0026#34;FGI={fng_val}极度恐惧 逆向做多 +1\u0026#34; elif fng_val \u0026gt;= 75: score -= 1 analysis[\u0026#34;sentiment\u0026#34;] = f\u0026#34;FGI={fng_val}极度贪婪 做多风险 -1\u0026#34; else: analysis[\u0026#34;sentiment\u0026#34;] = f\u0026#34;FGI={fng_val}中性 0\u0026#34; else: if fng_val \u0026gt;= 75: score += 1 analysis[\u0026#34;sentiment\u0026#34;] = f\u0026#34;FGI={fng_val}极度贪婪 逆向做空 +1\u0026#34; elif fng_val \u0026lt;= 25: score -= 1 analysis[\u0026#34;sentiment\u0026#34;] = f\u0026#34;FGI={fng_val}极度恐惧 做空风险 -1\u0026#34; else: analysis[\u0026#34;sentiment\u0026#34;] = f\u0026#34;FGI={fng_val}中性 0\u0026#34; except: analysis[\u0026#34;sentiment\u0026#34;] = \u0026#34;FGI获取失败 0\u0026#34; # 3. OI变化 — 看该币OI是否支持方向 try: oi = get_open_interest(symbol) ticker = requests.get(f\u0026#34;https://fapi.binance.com/fapi/v1/ticker/24hr?symbol={symbol}\u0026#34;, timeout=5).json() price = float(ticker[\u0026#39;lastPrice\u0026#39;]) oi_usd = oi * price if oi_usd \u0026gt; 5_000_000: # OI \u0026gt; 5M说明有关注度 score += 1 analysis[\u0026#34;oi_check\u0026#34;] = f\u0026#34;OI={oi_usd/1e6:.1f}M 有关注度 +1\u0026#34; else: analysis[\u0026#34;oi_check\u0026#34;] = f\u0026#34;OI={oi_usd/1e6:.1f}M 关注度低 0\u0026#34; except: analysis[\u0026#34;oi_check\u0026#34;] = \u0026#34;OI获取失败 0\u0026#34; # 4. 成交量 — 量能是否活跃 try: vol = float(ticker.get(\u0026#39;quoteVolume\u0026#39;, 0)) if vol \u0026gt; 50_000_000: score += 1 analysis[\u0026#34;volume_check\u0026#34;] = f\u0026#34;24h量={vol/1e6:.0f}M 活跃 +1\u0026#34; elif vol \u0026gt; 20_000_000: analysis[\u0026#34;volume_check\u0026#34;] = f\u0026#34;24h量={vol/1e6:.0f}M 一般 0\u0026#34; else: score -= 1 analysis[\u0026#34;volume_check\u0026#34;] = f\u0026#34;24h量={vol/1e6:.0f}M 冷清 -1\u0026#34; except: analysis[\u0026#34;volume_check\u0026#34;] = \u0026#34;量能获取失败 0\u0026#34; # 5. 信号本身的强度加分 if signal[\u0026#34;strength\u0026#34;] == \u0026#34;S\u0026#34;: score += 2 elif signal[\u0026#34;strength\u0026#34;] == \u0026#34;A\u0026#34;: score += 1 # 综合判定: \u0026gt;=3通过 analysis[\u0026#34;verdict\u0026#34;] = f\u0026#34;综合得分:{score}/7\u0026#34; if score \u0026gt;= 3: return True, analysis, signal[\u0026#34;strength\u0026#34;] else: return False, analysis, signal[\u0026#34;strength\u0026#34;] except Exception as e: analysis[\u0026#34;verdict\u0026#34;] = f\u0026#34;检查异常:{e} 保守不开\u0026#34; return False, analysis, signal[\u0026#34;strength\u0026#34;] # === 开仓执行 === def execute_open(data, state, symbol, price, signal): \u0026#34;\u0026#34;\u0026#34;执行虚拟开仓 — 先过综合环境检查\u0026#34;\u0026#34;\u0026#34; # 综合环境检查 passed, env_analysis, strength = check_environment(symbol, signal) env_summary = \u0026#34; | \u0026#34;.join(v for v in env_analysis.values() if v) if not passed: log(f\u0026#34;综合检查未通过 {symbol}: {env_summary}\u0026#34;) return log(f\u0026#34;综合检查通过 {symbol}: {env_summary}\u0026#34;) balance = get_balance(data) position_usd = balance * POSITION_PCT / 100 if signal[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34;: sl = round(price * (1 - signal[\u0026#34;sl_pct\u0026#34;]), 6) tp = round(price * (1 + signal[\u0026#34;tp_pct\u0026#34;]), 6) else: sl = round(price * (1 + signal[\u0026#34;sl_pct\u0026#34;]), 6) tp = round(price * (1 - signal[\u0026#34;tp_pct\u0026#34;]), 6) trade = { \u0026#34;id\u0026#34;: next_id(data), \u0026#34;symbol\u0026#34;: symbol, \u0026#34;direction\u0026#34;: signal[\u0026#34;direction\u0026#34;], \u0026#34;leverage\u0026#34;: LEVERAGE, \u0026#34;position_pct\u0026#34;: POSITION_PCT, \u0026#34;position_usd\u0026#34;: round(position_usd, 4), \u0026#34;notional_usd\u0026#34;: round(position_usd * LEVERAGE, 4), \u0026#34;entry_price\u0026#34;: price, \u0026#34;stop_loss\u0026#34;: sl, \u0026#34;take_profit\u0026#34;: tp, \u0026#34;entry_time\u0026#34;: now_str(), \u0026#34;exit_price\u0026#34;: None, \u0026#34;exit_time\u0026#34;: None, \u0026#34;exit_reason\u0026#34;: None, \u0026#34;pnl_pct\u0026#34;: None, \u0026#34;pnl_usd\u0026#34;: None, \u0026#34;status\u0026#34;: \u0026#34;open\u0026#34;, \u0026#34;pre_analysis\u0026#34;: { \u0026#34;btc_env\u0026#34;: env_analysis.get(\u0026#34;btc_env\u0026#34;, \u0026#34;\u0026#34;), \u0026#34;sentiment\u0026#34;: env_analysis.get(\u0026#34;sentiment\u0026#34;, \u0026#34;\u0026#34;), \u0026#34;oi\u0026#34;: env_analysis.get(\u0026#34;oi_check\u0026#34;, \u0026#34;\u0026#34;), \u0026#34;volume\u0026#34;: env_analysis.get(\u0026#34;volume_check\u0026#34;, \u0026#34;\u0026#34;), \u0026#34;key_reason\u0026#34;: f\u0026#34;[{signal[\u0026#39;strength\u0026#39;]}级] {signal[\u0026#39;reason\u0026#39;]}\u0026#34;, \u0026#34;risk\u0026#34;: f\u0026#34;综合得分:{env_analysis.get(\u0026#39;verdict\u0026#39;,\u0026#39;\u0026#39;)} 策略:{signal[\u0026#39;type\u0026#39;]}\u0026#34; }, \u0026#34;post_review\u0026#34;: None } data[\u0026#34;trades\u0026#34;].append(trade) save_trades(data) # 记录冷却 state[\u0026#34;last_opens\u0026#34;][symbol] = now_str() save_state(state) direction_cn = \u0026#34;做多\u0026#34; if signal[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34; else \u0026#34;做空\u0026#34; msg = f\u0026#34;\u0026#34;\u0026#34;``` [扫描开仓] #{trade[\u0026#39;id\u0026#39;]} 币种: {symbol} 方向: {direction_cn} {LEVERAGE}x 入场: {price} 止损: {sl} 止盈: {tp} 仓位: {position_usd:.2f}U 信号: [{signal[\u0026#39;strength\u0026#39;]}] {signal[\u0026#39;reason\u0026#39;]} 时间: {trade[\u0026#39;entry_time\u0026#39;]} ```\u0026#34;\u0026#34;\u0026#34; log(f\u0026#34;开仓 #{trade[\u0026#39;id\u0026#39;]} {symbol} {direction_cn} @ {price} | {signal[\u0026#39;reason\u0026#39;]}\u0026#34;) send_tg(msg) print(msg) # === 换仓逻辑 === def swap_weakest(data, state, open_positions, new_signal, tickers): \u0026#34;\u0026#34;\u0026#34;满仓时遇到S级信号，平掉浮亏最大的持仓，开新仓\u0026#34;\u0026#34;\u0026#34; ticker_map = {t[\u0026#39;symbol\u0026#39;]: float(t[\u0026#39;lastPrice\u0026#39;]) for t in tickers} # 计算每个持仓的浮盈% worst_trade = None worst_pnl = float(\u0026#39;inf\u0026#39;) for t in open_positions: price = ticker_map.get(t[\u0026#34;symbol\u0026#34;]) if price is None: continue if t[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34;: pnl_pct = (price - t[\u0026#34;entry_price\u0026#34;]) / t[\u0026#34;entry_price\u0026#34;] * 100 else: pnl_pct = (t[\u0026#34;entry_price\u0026#34;] - price) / t[\u0026#34;entry_price\u0026#34;] * 100 if pnl_pct \u0026lt; worst_pnl: worst_pnl = pnl_pct worst_trade = t worst_price = price if worst_trade is None: return # 只换掉亏损的仓位，盈利的不动 if worst_pnl \u0026gt; 0: log(f\u0026#34;满仓但所有持仓盈利，不换仓 | 新信号: {new_signal[\u0026#39;symbol\u0026#39;]}\u0026#34;) return # 平掉最弱的 if worst_trade[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34;: pnl_pct_lev = (worst_price - worst_trade[\u0026#34;entry_price\u0026#34;]) / worst_trade[\u0026#34;entry_price\u0026#34;] * 100 * worst_trade[\u0026#34;leverage\u0026#34;] else: pnl_pct_lev = (worst_trade[\u0026#34;entry_price\u0026#34;] - worst_price) / worst_trade[\u0026#34;entry_price\u0026#34;] * 100 * worst_trade[\u0026#34;leverage\u0026#34;] pos_usd = worst_trade.get(\u0026#34;position_usd\u0026#34;, worst_trade.get(\u0026#34;position_pct\u0026#34;, 30)) pnl_usd = round(pnl_pct_lev / 100 * pos_usd, 4) worst_trade[\u0026#34;exit_price\u0026#34;] = worst_price worst_trade[\u0026#34;exit_time\u0026#34;] = now_str() worst_trade[\u0026#34;exit_reason\u0026#34;] = f\u0026#34;换仓→{new_signal[\u0026#39;symbol\u0026#39;]}\u0026#34; worst_trade[\u0026#34;pnl_pct\u0026#34;] = round(pnl_pct_lev, 2) worst_trade[\u0026#34;pnl_usd\u0026#34;] = pnl_usd worst_trade[\u0026#34;status\u0026#34;] = \u0026#34;closed\u0026#34; save_trades(data) direction_cn = \u0026#34;多\u0026#34; if worst_trade[\u0026#34;direction\u0026#34;] == \u0026#34;long\u0026#34; else \u0026#34;空\u0026#34; msg = f\u0026#34;\u0026#34;\u0026#34;``` [换仓平仓] #{worst_trade[\u0026#39;id\u0026#39;]} 平掉: {worst_trade[\u0026#39;symbol\u0026#39;]} {direction_cn} 入场: {worst_trade[\u0026#39;entry_price\u0026#39;]} 出场: {worst_price} 盈亏: {pnl_pct_lev:+.2f}% ({pnl_usd:+.2f}U) 原因: S级信号{new_signal[\u0026#39;symbol\u0026#39;]}更强 ```\u0026#34;\u0026#34;\u0026#34; log(f\u0026#34;换仓平 #{worst_trade[\u0026#39;id\u0026#39;]} {worst_trade[\u0026#39;symbol\u0026#39;]} {pnl_usd:+.2f}U → 开 {new_signal[\u0026#39;symbol\u0026#39;]}\u0026#34;) send_tg(msg) # 开新仓 execute_open(data, state, new_signal[\u0026#34;symbol\u0026#34;], new_signal[\u0026#34;price\u0026#34;], new_signal) # === 主扫描逻辑 === def scan(): data = load_trades() state = load_state() now = datetime.now(TZ_UTC8) # 检查持仓数 open_positions = [t for t in data[\u0026#34;trades\u0026#34;] if t[\u0026#34;status\u0026#34;] == \u0026#34;open\u0026#34;] open_symbols = set(t[\u0026#34;symbol\u0026#34;] for t in open_positions) if len(open_positions) \u0026gt;= MAX_OPEN_POSITIONS: return # 获取市场数据 try: tickers = get_all_tickers() funding_rates = get_funding_rates() except Exception as e: log(f\u0026#34;API错误: {e}\u0026#34;) return # 过滤USDT合约 + 最小成交量 exclude = {\u0026#34;BTCUSDT\u0026#34;, \u0026#34;ETHUSDT\u0026#34;, \u0026#34;USDCUSDT\u0026#34;, \u0026#34;FDUSDUSDT\u0026#34;, \u0026#34;BTCDOMUSDT\u0026#34;, \u0026#34;BTCSTUSDT\u0026#34;} candidates = [t for t in tickers if t[\u0026#39;symbol\u0026#39;].endswith(\u0026#39;USDT\u0026#39;) and t[\u0026#39;symbol\u0026#39;] not in exclude and float(t[\u0026#39;quoteVolume\u0026#39;]) \u0026gt; MIN_VOLUME_M * 1e6] signals = [] for ticker in candidates: symbol = ticker[\u0026#39;symbol\u0026#39;] # 跳过已持仓的币 if symbol in open_symbols: continue # 冷却检查 last_open = state.get(\u0026#34;last_opens\u0026#34;, {}).get(symbol) if last_open: try: last_dt = datetime.fromisoformat(last_open) if last_dt.tzinfo is None: last_dt = last_dt.replace(tzinfo=TZ_UTC8) if (now - last_dt).total_seconds() \u0026lt; COOLDOWN_HOURS * 3600: continue except: pass fr = funding_rates.get(symbol, 0) # 运行所有策略检测 for detect_fn in [ lambda s, f, m: detect_extreme_negative_funding(s, f, m), lambda s, f, m: detect_extreme_positive_funding(s, f, m), lambda s, f, m: detect_crash_bounce(ticker), lambda s, f, m: detect_pump_short(ticker), ]: try: signal = detect_fn(symbol, fr, funding_rates) if signal: signal[\u0026#34;symbol\u0026#34;] = symbol signal[\u0026#34;price\u0026#34;] = float(ticker[\u0026#39;lastPrice\u0026#39;]) signal[\u0026#34;volume_m\u0026#34;] = float(ticker[\u0026#39;quoteVolume\u0026#39;]) / 1e6 signals.append(signal) except: continue if not signals: return # 按信号强度排序 S\u0026gt;A\u0026gt;B strength_order = {\u0026#34;S\u0026#34;: 0, \u0026#34;A\u0026#34;: 1, \u0026#34;B\u0026#34;: 2} signals.sort(key=lambda x: strength_order.get(x[\u0026#34;strength\u0026#34;], 3)) # 只取最强的信号开仓(一次最多开1笔) best = signals[0] # B级信号跳过，只开S和A级 if best[\u0026#34;strength\u0026#34;] == \u0026#34;B\u0026#34;: log(f\u0026#34;B级信号跳过: {best[\u0026#39;symbol\u0026#39;]} {best[\u0026#39;reason\u0026#39;]}\u0026#34;) return slots = MAX_OPEN_POSITIONS - len(open_positions) if slots \u0026gt; 0: execute_open(data, state, best[\u0026#34;symbol\u0026#34;], best[\u0026#34;price\u0026#34;], best) elif best[\u0026#34;strength\u0026#34;] == \u0026#34;S\u0026#34;: # 满仓但遇到S级信号 → 换掉最弱的持仓 swap_weakest(data, state, open_positions, best, tickers) if __name__ == \u0026#34;__main__\u0026#34;: scan() 实用工具 VoiceKey — 声纹密钥 发布日期: 2026.04.27　标签: Python · Security · Telegram · Speaker Verification\n用声纹替代密码，保护你的 AI 助手\n为什么做这个？越来越多人用TG控制AI Agent（服务器、交易机器人、智能家居）。一旦TG账号被盗，攻击者可以用你的AI做任何事。密码可以被偷看、复制、社工，但声纹不行——每个人的声音特征是唯一的。VoiceKey要求每次新会话发一条语音验证身份，通过才放行。零AI成本，纯本地CPU运行。\n完整源码 #!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34; VoiceKey — 声纹密钥 (Voiceprint Authentication) Speaker verification for Telegram bot security. Uses resemblyzer (GE2E model) for speaker embedding extraction and cosine similarity for verification. Zero AI cost — runs entirely on local CPU. Usage: # Register voiceprint from audio files python voicekey.py register --audio voice1.ogg voice2.ogg --owner \u0026#34;YourName\u0026#34; # Verify a voice against stored voiceprint python voicekey.py verify --audio test.ogg # As a Python module from voicekey import VoiceKey vk = VoiceKey() vk.register([\u0026#34;voice1.ogg\u0026#34;, \u0026#34;voice2.ogg\u0026#34;], owner=\u0026#34;YourName\u0026#34;) is_owner, score = vk.verify(\u0026#34;test.ogg\u0026#34;) \u0026#34;\u0026#34;\u0026#34; import os import json import tempfile import argparse import numpy as np from pathlib import Path from datetime import datetime # Lazy imports to speed up module load when not needed _encoder = None def _get_encoder(): \u0026#34;\u0026#34;\u0026#34;Lazy-load the voice encoder model (first call takes ~1s).\u0026#34;\u0026#34;\u0026#34; global _encoder if _encoder is None: from resemblyzer import VoiceEncoder _encoder = VoiceEncoder() return _encoder def _audio_to_wav(audio_path: str) -\u0026gt; str: \u0026#34;\u0026#34;\u0026#34;Convert any audio format to 16kHz mono WAV for processing.\u0026#34;\u0026#34;\u0026#34; from pydub import AudioSegment ext = Path(audio_path).suffix.lower() if ext in (\u0026#39;.ogg\u0026#39;, \u0026#39;.oga\u0026#39;): audio = AudioSegment.from_ogg(audio_path) elif ext == \u0026#39;.mp3\u0026#39;: audio = AudioSegment.from_mp3(audio_path) elif ext == \u0026#39;.wav\u0026#39;: return audio_path # already wav elif ext in (\u0026#39;.m4a\u0026#39;, \u0026#39;.aac\u0026#39;): audio = AudioSegment.from_file(audio_path, format=ext.lstrip(\u0026#39;.\u0026#39;)) else: audio = AudioSegment.from_file(audio_path) # Convert to 16kHz mono audio = audio.set_frame_rate(16000).set_channels(1) tmp = tempfile.NamedTemporaryFile(suffix=\u0026#39;.wav\u0026#39;, delete=False) audio.export(tmp.name, format=\u0026#39;wav\u0026#39;) return tmp.name def _extract_embedding(audio_path: str) -\u0026gt; np.ndarray: \u0026#34;\u0026#34;\u0026#34;Extract voice embedding from an audio file.\u0026#34;\u0026#34;\u0026#34; from resemblyzer import preprocess_wav encoder = _get_encoder() wav_path = _audio_to_wav(audio_path) wav = preprocess_wav(wav_path) # Cleanup temp file if wav_path != audio_path: os.unlink(wav_path) if len(wav) \u0026lt; 1600: # Less than 0.1s raise ValueError(f\u0026#34;Audio too short: {len(wav)/16000:.1f}s (need \u0026gt;0.1s)\u0026#34;) return encoder.embed_utterance(wav) class VoiceKey: \u0026#34;\u0026#34;\u0026#34; Speaker verification using voice embeddings. Attributes: data_dir: Directory to store voiceprint data threshold: Cosine similarity threshold for verification (default: 0.75) voiceprint: The registered owner\u0026#39;s voice embedding (256-dim vector) \u0026#34;\u0026#34;\u0026#34; def __init__(self, data_dir: str = None, threshold: float = 0.75): if data_dir is None: data_dir = os.path.expanduser(\u0026#34;~/.hermes/voiceprint\u0026#34;) self.data_dir = Path(data_dir) self.data_dir.mkdir(parents=True, exist_ok=True) self.threshold = threshold self.voiceprint = None self.metadata = {} self._load() def _load(self): \u0026#34;\u0026#34;\u0026#34;Load existing voiceprint if available.\u0026#34;\u0026#34;\u0026#34; vp_path = self.data_dir / \u0026#34;voiceprint.npy\u0026#34; meta_path = self.data_dir / \u0026#34;voiceprint_meta.json\u0026#34; if vp_path.exists(): self.voiceprint = np.load(str(vp_path)) if meta_path.exists(): with open(meta_path) as f: self.metadata = json.load(f) self.threshold = self.metadata.get(\u0026#34;threshold\u0026#34;, self.threshold) @property def is_registered(self) -\u0026gt; bool: \u0026#34;\u0026#34;\u0026#34;Check if a voiceprint is registered.\u0026#34;\u0026#34;\u0026#34; return self.voiceprint is not None def register(self, audio_paths: list, owner: str = \u0026#34;owner\u0026#34;) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34; Register a voiceprint from multiple audio samples. Args: audio_paths: List of audio file paths (ogg, mp3, wav, etc.) owner: Name of the voiceprint owner Returns: dict with registration results \u0026#34;\u0026#34;\u0026#34; embeddings = [] results = [] for path in audio_paths: try: embed = _extract_embedding(path) embeddings.append(embed) results.append({\u0026#34;file\u0026#34;: os.path.basename(path), \u0026#34;status\u0026#34;: \u0026#34;ok\u0026#34;}) except Exception as e: results.append({\u0026#34;file\u0026#34;: os.path.basename(path), \u0026#34;status\u0026#34;: \u0026#34;error\u0026#34;, \u0026#34;error\u0026#34;: str(e)}) if not embeddings: raise ValueError(\u0026#34;No valid audio samples provided\u0026#34;) # Average embeddings and normalize voiceprint = np.mean(embeddings, axis=0) voiceprint = voiceprint / np.linalg.norm(voiceprint) # Save np.save(str(self.data_dir / \u0026#34;voiceprint.npy\u0026#34;), voiceprint) self.metadata = { \u0026#34;owner\u0026#34;: owner, \u0026#34;samples_used\u0026#34;: len(embeddings), \u0026#34;embedding_dim\u0026#34;: int(voiceprint.shape[0]), \u0026#34;threshold\u0026#34;: self.threshold, \u0026#34;created\u0026#34;: datetime.now().isoformat(), } with open(self.data_dir / \u0026#34;voiceprint_meta.json\u0026#34;, \u0026#34;w\u0026#34;) as f: json.dump(self.metadata, f, indent=2) self.voiceprint = voiceprint # Self-test similarities = [] for embed in embeddings: sim = float(np.dot(voiceprint, embed / np.linalg.norm(embed))) similarities.append(sim) return { \u0026#34;owner\u0026#34;: owner, \u0026#34;samples\u0026#34;: len(embeddings), \u0026#34;self_test_scores\u0026#34;: similarities, \u0026#34;min_score\u0026#34;: min(similarities), \u0026#34;details\u0026#34;: results, } def verify(self, audio_path: str) -\u0026gt; tuple: \u0026#34;\u0026#34;\u0026#34; Verify if an audio sample matches the registered voiceprint. Args: audio_path: Path to audio file to verify Returns: tuple: (is_verified: bool, similarity_score: float) \u0026#34;\u0026#34;\u0026#34; if not self.is_registered: raise RuntimeError(\u0026#34;No voiceprint registered. Call register() first.\u0026#34;) embed = _extract_embedding(audio_path) embed = embed / np.linalg.norm(embed) similarity = float(np.dot(self.voiceprint, embed)) is_verified = similarity \u0026gt;= self.threshold return is_verified, similarity def get_info(self) -\u0026gt; dict: \u0026#34;\u0026#34;\u0026#34;Get voiceprint registration info.\u0026#34;\u0026#34;\u0026#34; if not self.is_registered: return {\u0026#34;registered\u0026#34;: False} return { \u0026#34;registered\u0026#34;: True, **self.metadata, } def main(): parser = argparse.ArgumentParser( description=\u0026#34;VoiceKey — Speaker verification for security\u0026#34; ) sub = parser.add_subparsers(dest=\u0026#34;command\u0026#34;) # Register reg = sub.add_parser(\u0026#34;register\u0026#34;, help=\u0026#34;Register voiceprint from audio files\u0026#34;) reg.add_argument(\u0026#34;--audio\u0026#34;, nargs=\u0026#34;+\u0026#34;, required=True, help=\u0026#34;Audio files (ogg/mp3/wav)\u0026#34;) reg.add_argument(\u0026#34;--owner\u0026#34;, default=\u0026#34;owner\u0026#34;, help=\u0026#34;Owner name\u0026#34;) reg.add_argument(\u0026#34;--data-dir\u0026#34;, default=None, help=\u0026#34;Data directory\u0026#34;) reg.add_argument(\u0026#34;--threshold\u0026#34;, type=float, default=0.75, help=\u0026#34;Verification threshold\u0026#34;) # Verify ver = sub.add_parser(\u0026#34;verify\u0026#34;, help=\u0026#34;Verify audio against voiceprint\u0026#34;) ver.add_argument(\u0026#34;--audio\u0026#34;, required=True, help=\u0026#34;Audio file to verify\u0026#34;) ver.add_argument(\u0026#34;--data-dir\u0026#34;, default=None, help=\u0026#34;Data directory\u0026#34;) # Info sub.add_parser(\u0026#34;info\u0026#34;, help=\u0026#34;Show voiceprint info\u0026#34;) args = parser.parse_args() if args.command == \u0026#34;register\u0026#34;: vk = VoiceKey(data_dir=args.data_dir, threshold=args.threshold) result = vk.register(args.audio, owner=args.owner) print(f\u0026#34;Registered voiceprint for: {result[\u0026#39;owner\u0026#39;]}\u0026#34;) print(f\u0026#34;Samples used: {result[\u0026#39;samples\u0026#39;]}\u0026#34;) print(f\u0026#34;Self-test scores: {[f\u0026#39;{s:.4f}\u0026#39; for s in result[\u0026#39;self_test_scores\u0026#39;]]}\u0026#34;) print(f\u0026#34;Min score: {result[\u0026#39;min_score\u0026#39;]:.4f} (threshold: {args.threshold})\u0026#34;) elif args.command == \u0026#34;verify\u0026#34;: vk = VoiceKey(data_dir=getattr(args, \u0026#39;data_dir\u0026#39;, None)) is_ok, score = vk.verify(args.audio) status = \u0026#34;PASS\u0026#34; if is_ok else \u0026#34;FAIL\u0026#34; print(f\u0026#34;[{status}] Similarity: {score:.4f} (threshold: {vk.threshold})\u0026#34;) elif args.command == \u0026#34;info\u0026#34;: vk = VoiceKey() info = vk.get_info() for k, v in info.items(): print(f\u0026#34; {k}: {v}\u0026#34;) else: parser.print_help() if __name__ == \u0026#34;__main__\u0026#34;: main() 总结 以上 7 个片段全部来源于 connectfarm1.com,抓取时间 2026-05-04。它们共享三个理念:(1) 零或接近零的 AI 成本 —— 大多用规则引擎和免费公共 API,不依赖 LLM;(2) 只用 Python,便宜 VPS 上配 crontab 或一个 while True 就能跑;(3) Telegram 作为统一输出口 —— 不做仪表盘,不做前端,直接把信息推到你真正会看的地方。把多个雷达组合起来交叉验证信号,把\u0026quot;自主交易\u0026quot;当成研究沙盒,不要当成印钞机。\n","permalink":"https://dibi8.com/zh/posts/code-vault-7-%E4%B8%AA%E5%BC%80%E6%BA%90%E7%9A%84%E5%8A%A0%E5%AF%86%E9%9B%B7%E8%BE%BE%E4%B8%8E%E4%BA%A4%E6%98%93%E5%B7%A5%E5%85%B7%E5%90%88%E9%9B%86/","summary":"\u003cp\u003e本文把 \u003ca href=\"https://connectfarm1.com/\"\u003eCode Vault\u003c/a\u003e 当前发布的 7 个代码片段做一次完整梳理 —— 这是一个个人代码仓,内容覆盖加密交易雷达、自主交易系统、安全工具,全部纯 Python,零或接近零的 API 成本。每个片段下方都附了\u003cstrong\u003e完整源码\u003c/strong\u003e,可以直接读、Fork、本地跑。右侧目录可以跳转到具体工具。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e⚠️ \u003cstrong\u003e风险提示\u003c/strong\u003e —— 这些工具直接对接行情和链上数据,部分会通过 Telegram 实时推送告警,其中\u0026quot;AI 自主交易\u0026quot;会在币安合约上开\u003cstrong\u003e虚拟仓\u003c/strong\u003e。请仔细阅读每个工具下方的说明。\u003cstrong\u003e使用风险自担,作者不对交易结果做任何担保。\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"交易雷达\"\u003e交易雷达\u003c/h2\u003e\n\u003ch3 id=\"v神卖币雷达\"\u003eV神卖币雷达\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e发布日期:\u003c/strong\u003e 2026.05.02　\u003cstrong\u003e标签:\u003c/strong\u003e \u003ccode\u003ePython\u003c/code\u003e · \u003ccode\u003eWebSocket\u003c/code\u003e · \u003ccode\u003eEthereum\u003c/code\u003e · \u003ccode\u003eTelegram\u003c/code\u003e · \u003ccode\u003eEvent-Driven\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eGitHub:\u003c/strong\u003e \u003ca href=\"https://github.com/connectfarm1/vitalik-sell-radar\"\u003evitalik-sell-radar\u003c/a\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003eWebSocket事件驱动 · V神钱包卖出检测 · 秒级TG推送\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e通过WebSocket事件订阅实时监控V神钱包(vitalik.eth)的ERC-20代币卖出行为 — 零轮询，亚秒级延迟。自动分类接收方：DEX Router（Uniswap/1inch/SushiSwap）、CEX热钱包（币安/Coinbase/Kraken）、LP池。DexScreener实时查价。多RPC故障切换+自动重连。纯Python零成本，使用免费公共RPC节点。\u003c/p\u003e\n\u003ch4 id=\"完整源码\"\u003e完整源码\u003c/h4\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"ch\"\u003e#!/usr/bin/env python3\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003eVitalik Sell Radar — Event-Driven Edition\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003eWebSocket subscription to ERC-20 Transfer events, real-time detection of\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003eVitalik\u0026#39;s sell activity, instant push to Telegram.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003eArchitecture:\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e1. WebSocket subscribes to Transfer(from=vitalik) events → sub-second detection\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e2. Classifies sell behavior (transfers to DEX Router / CEX / LP Pool)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e3. Queries token info + price via DexScreener\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e4. Pushes alert to Telegram\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e5. Auto-reconnect + multi-RPC failover\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003easyncio\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ejson\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003elogging\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eos\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003esignal\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003esys\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003etime\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003edatetime\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003edatetime\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etimezone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003epathlib\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003ePath\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eaiohttp\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003ewebsockets\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Load .env file\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eload_env\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eenv_path\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ePath\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"vm\"\u003e__file__\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eparent\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;.env\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eenv_path\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexists\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eenv_path\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eread_text\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esplitlines\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eline\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estartswith\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;#\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eline\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esplit\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esetdefault\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ek\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003ev\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eload_env\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Configuration\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Vitalik\u0026#39;s main wallet\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eVITALIK_ADDRESS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eVITALIK_PADDED\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003eVITALIK_ADDRESS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ezfill\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ERC-20 Transfer event topic\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eTRANSFER_TOPIC\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# WebSocket RPC endpoints (free, support eth_subscribe)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eWS_ENDPOINTS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;wss://ethereum-rpc.publicnode.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;wss://eth.drpc.org\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;wss://ethereum.publicnode.com\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# HTTP RPC for querying token info\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eHTTP_RPC\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;HTTP_RPC\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;https://eth.drpc.org\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Telegram\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eTG_BOT_TOKEN\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;TG_BOT_TOKEN\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eTG_CHAT_ID\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;TG_CHAT_ID\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Known DEX Router addresses (sell destinations)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eKNOWN_DEX_ROUTERS\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Uniswap\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x7a250d5630b4cf539739df2c5dacb4c659f2488d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Uniswap V2 Router\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xe592427a0aece92de3edee1f18e0157c05861564\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Uniswap V3 Router\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x68b3465833fb72a70ecdf485e0e4c7bd8665fc45\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Uniswap V3 Router2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x3fc91a3afd70395cd496c647d5a6cc9d4b2b7fad\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Uniswap Universal Router\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xef1c6e67703c7bd7107eed8303fbe6ec2554bf6b\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Uniswap Universal Router (old)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# 1inch\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x1111111254eeb25477b68fb85ed929f73a960582\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;1inch V5\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x111111125421ca6dc452d289314280a0f8842a65\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;1inch V6\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# SushiSwap\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;SushiSwap Router\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# CoW Protocol\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x9008d19f58aabd9ed0d60971565aa8510560ab41\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;CoW Settlement\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# 0x\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xdef1c0ded9bec7f1a1670819833240f027b25eff\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x Exchange Proxy\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Curve\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x99a58482bd75cbab83b27ec03ca68ff489b5788f\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Curve Router\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Known CEX hot wallets (partial list)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eKNOWN_CEX\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x28c6c06298d514db089934071355e5743bf21d60\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Binance Hot Wallet\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x21a31ee1afc51d94c2efccaa2092ad1028285549\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Binance Hot Wallet 2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xdfd5293d8e347dfe59e90efd55b2956a1343963d\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Binance Hot Wallet 3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x56eddb7aa87536c09ccc2793473599fd21a8b17f\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Binance Hot Wallet 4\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x71660c4005ba85c37ccec55d0c4493e66fe775d3\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Coinbase\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Coinbase 10\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x503828976d22510aad0201ac7ec88293211d23da\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Coinbase 2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x2faf487a4414fe77e2327f0bf4ae2a264a776ad2\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;FTX (defunct)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0x267be1c1d684f78cb4f6a176c4911b741e4ffdc0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Kraken\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;0xae2d4617c862309a3d75a0ffb358c7a5009c673f\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Kraken 10\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Minimum notification amount (USD), 0 = notify all\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eMIN_NOTIFY_USD\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eenviron\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;MIN_NOTIFY_USD\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Reconnect settings\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eRECONNECT_DELAY\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e5\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eMAX_RECONNECT_DELAY\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e60\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Logging\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebasicConfig\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elevel\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eINFO\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"nb\"\u003eformat\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e%(asctime)s\u003c/span\u003e\u003cspan class=\"s2\"\u003e [\u003c/span\u003e\u003cspan class=\"si\"\u003e%(levelname)s\u003c/span\u003e\u003cspan class=\"s2\"\u003e] \u003c/span\u003e\u003cspan class=\"si\"\u003e%(message)s\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edatefmt\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;%Y-%m-\u003c/span\u003e\u003cspan class=\"si\"\u003e%d\u003c/span\u003e\u003cspan class=\"s2\"\u003e %H:%M:%S\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003elog\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elogging\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003egetLogger\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;vitalik-radar\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Global state\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Dedup set for processed tx hashes (last 1000)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eseen_txs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eset\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eset\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eseen_txs_list\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003elist\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# HTTP session\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientSession\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Token info cache: {address: {symbol, name, decimals}}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003etoken_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003edict\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Running flag\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003erunning\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Utility functions\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshorten_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Shorten address for display\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"mi\"\u003e6\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e...\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e4\u003c/span\u003e\u003cspan class=\"p\"\u003e:]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003edecode_transfer_value\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata_hex\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edecimals\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003efloat\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Decode Transfer event value\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eraw\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata_hex\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eraw\u003c/span\u003e \u003cspan class=\"o\"\u003e/\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e \u003cspan class=\"o\"\u003e**\u003c/span\u003e \u003cspan class=\"n\"\u003edecimals\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mf\"\u003e0.0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003etopic_to_address\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etopic\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Extract 20-byte address from 32-byte topic\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e+\u003c/span\u003e \u003cspan class=\"n\"\u003etopic\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"o\"\u003e-\u003c/span\u003e\u003cspan class=\"mi\"\u003e40\u003c/span\u003e\u003cspan class=\"p\"\u003e:]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eclassify_recipient\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003etuple\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e]:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    Classify recipient address.\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    Returns (type, name)\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    Type: \u0026#34;dex\u0026#34; / \u0026#34;cex\u0026#34; / \u0026#34;pool\u0026#34; / \u0026#34;unknown\u0026#34;\n\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"s2\"\u003e    \u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eto_lower\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eto_lower\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eKNOWN_DEX_ROUTERS\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;dex\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eKNOWN_DEX_ROUTERS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eto_lower\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eto_lower\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eKNOWN_CEX\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;cex\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eKNOWN_CEX\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eto_lower\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;unknown\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_http_session\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientSession\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eglobal\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e \u003cspan class=\"ow\"\u003eis\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclosed\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientSession\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003erpc_call\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003elist\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003edict\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;HTTP JSON-RPC call\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esession\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_http_session\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esession\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eHTTP_RPC\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;jsonrpc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;2.0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;method\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;params\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eparams\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etotal\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;result\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"ne\"\u003eException\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;RPC call \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003emethod\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e failed: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_token_info\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003edict\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Query ERC-20 token info (symbol, name, decimals)\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eaddr_lower\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eaddr_lower\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr_lower\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003einfo\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;symbol\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;???\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;Unknown\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;decimals\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e18\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;address\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# symbol()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003erpc_call\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;eth_call\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;to\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;data\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x95d89b41\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;latest\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"mi\"\u003e128\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eoffset\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003elength\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003esymbol_hex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"n\"\u003elength\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;symbol\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ebytes\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efromhex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esymbol_hex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerrors\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;replace\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\x00\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelif\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;symbol\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ebytes\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efromhex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerrors\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;replace\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\x00\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# decimals()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003erpc_call\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;eth_call\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;to\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;data\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x313ce567\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;latest\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;decimals\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# name()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003erpc_call\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;eth_call\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;to\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;data\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x06fdde03\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;latest\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e:]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;=\u003c/span\u003e \u003cspan class=\"mi\"\u003e128\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eoffset\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003elength\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e],\u003c/span\u003e \u003cspan class=\"mi\"\u003e16\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003ename_hex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\u003cspan class=\"n\"\u003eoffset\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"o\"\u003e+\u003c/span\u003e\u003cspan class=\"n\"\u003elength\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ebytes\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efromhex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ename_hex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerrors\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;replace\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\x00\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelif\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e64\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;name\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003ebytes\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003efromhex\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ehex_str\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edecode\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;utf-8\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eerrors\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;replace\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrip\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\x00\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etoken_cache\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr_lower\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003einfo\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003echeck_if_pool\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003ebool\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Check if address is a Uniswap V2/V3 pool (has token0 method)\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003erpc_call\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;eth_call\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;to\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eaddr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;data\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x0dfe1681\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e},\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;latest\u0026#34;\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# token0()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e66\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 0x + 64 hex chars\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003eFalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_eth_price\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003efloat\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Get ETH price from CoinGecko\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esession\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_http_session\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esession\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;https://api.coingecko.com/api/v3/simple/price?ids=ethereum\u0026amp;vs_currencies=usd\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etotal\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ethereum\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;usd\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"mi\"\u003e2300\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# fallback\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eget_token_price_usd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u0026gt;\u003c/span\u003e \u003cspan class=\"nb\"\u003efloat\u003c/span\u003e \u003cspan class=\"o\"\u003e|\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Get token USD price from DexScreener (free, no API key needed)\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esession\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_http_session\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esession\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;https://api.dexscreener.com/latest/dex/tokens/\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etotal\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e5\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003epairs\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;pairs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003epairs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"c1\"\u003e# Pick pair with highest liquidity\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003epairs\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esort\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ekey\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"k\"\u003elambda\u003c/span\u003e \u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003efloat\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ep\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;liquidity\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;usd\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e \u003cspan class=\"n\"\u003ereverse\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eprice\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003efloat\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003epairs\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;priceUsd\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003eprice\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eprice\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Telegram notifications\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003esend_telegram\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Send Telegram message\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_BOT_TOKEN\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewarning\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[TG] No bot token configured, skipping notification\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esession\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_http_session\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eurl\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;https://api.telegram.org/bot\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eTG_BOT_TOKEN\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e/sendMessage\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003epayload\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;chat_id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_CHAT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;text\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003etext\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;parse_mode\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;HTML\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;disable_web_page_preview\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esession\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003eaiohttp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eClientTimeout\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etotal\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eresult\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;ok\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[TG] Send failed: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;description\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"c1\"\u003e# Retry with plain text if HTML parse fails\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;parse\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eresult\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;description\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;parse_mode\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003esession\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epost\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eurl\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"n\"\u003epayload\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003eresp2\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                        \u003cspan class=\"k\"\u003epass\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eelse\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[TG] Message sent\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"ne\"\u003eException\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[TG] Error: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Event handling\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003ehandle_transfer_event\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003edict\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Process a single Transfer event\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etx_hash\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;transactionHash\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;address\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etopics\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;topics\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e[])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;data\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Dedup\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ededup_key\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etx_hash\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e:\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ededup_key\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eseen_txs\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eseen_txs\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eadd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ededup_key\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eseen_txs_list\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eappend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ededup_key\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Cleanup (keep last 1000)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eseen_txs_list\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026gt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e1000\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eold\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eseen_txs_list\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epop\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eseen_txs\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ediscard\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eold\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Parse topics: [Transfer, from, to]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etopics\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"mi\"\u003e3\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003efrom_addr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etopic_to_address\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etopics\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etopic_to_address\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etopics\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Confirm it\u0026#39;s from Vitalik\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003efrom_addr\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"o\"\u003e!=\u003c/span\u003e \u003cspan class=\"n\"\u003eVITALIK_ADDRESS\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elower\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Get token info\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etoken_info\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_token_info\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esymbol\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_info\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;symbol\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003edecimals\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_info\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;decimals\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eamount\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edecode_transfer_value\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003edecimals\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Classify recipient\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003erecipient_type\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003erecipient_name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eclassify_recipient\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# If unknown, check if it\u0026#39;s an LP pool\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eis_pool\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eFalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003erecipient_type\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;unknown\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eis_pool\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003echeck_if_pool\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eis_pool\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003erecipient_type\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;pool\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003erecipient_name\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;LP Pool\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Determine if this is a \u0026#34;sell\u0026#34; action\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eis_sell\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003erecipient_type\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;dex\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;cex\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;pool\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# If not a sell, just a regular transfer — log silently\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eis_sell\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[Transfer] \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esymbol\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e,.2f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e → \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eshorten_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e (regular transfer, not notifying)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Get price\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etoken_price\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_token_price_usd\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eamount\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_price\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_price\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Below minimum notification amount — skip\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e \u003cspan class=\"ow\"\u003eis\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e \u003cspan class=\"o\"\u003e\u0026lt;\u003c/span\u003e \u003cspan class=\"n\"\u003eMIN_NOTIFY_USD\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[Sell] \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esymbol\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e $\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e.0f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e \u0026lt; $\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eMIN_NOTIFY_USD\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e minimum, skipping\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Build notification message\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003etimestamp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edatetime\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003enow\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etimezone\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eutc\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estrftime\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;%H:%M:%S UTC\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esell_type_label\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;dex\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;🔄 DEX Sell\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;cex\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;🏦 CEX Transfer\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"s2\"\u003e\u0026#34;pool\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;🌊 Pool Sell\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emsg_lines\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026lt;b\u0026gt;🚨 Vitalik Sell Signal\u0026lt;/b\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Token: \u0026lt;b\u0026gt;\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esymbol\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026lt;/b\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Amount: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e,.4f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e \u003cspan class=\"ow\"\u003eis\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003emsg_lines\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eappend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Value: \u0026lt;b\u0026gt;$\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e,.0f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026lt;/b\u0026gt;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_price\u003c/span\u003e \u003cspan class=\"ow\"\u003eis\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003emsg_lines\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eappend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Price: $\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_price\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e,.8f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emsg_lines\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eextend\u003c/span\u003e\u003cspan class=\"p\"\u003e([\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Type: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esell_type_label\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003erecipient_type\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;Unknown\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Destination: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003erecipient_name\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003eshorten_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Time: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etimestamp\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;TX: https://etherscan.io/tx/\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etx_hash\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Token: https://dexscreener.com/ethereum/\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003emsg\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ejoin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emsg_lines\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[SELL DETECTED] \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esymbol\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e,.4f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e → \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003erecipient_name\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"n\"\u003eto_addr\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e | $\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eusd_value\u003c/span\u003e \u003cspan class=\"ow\"\u003eor\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;?\u0026#39;\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003esend_telegram\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emsg\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# WebSocket listener main loop\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# ============================================================\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003esubscribe_and_listen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ews_url\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Connect to WebSocket and subscribe to Vitalik Transfer events\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Connecting to \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ews_url\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e...\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003ewith\u003c/span\u003e \u003cspan class=\"n\"\u003ewebsockets\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnect\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ews_url\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eping_interval\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eping_timeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e30\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eclose_timeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003emax_size\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"o\"\u003e**\u003c/span\u003e\u003cspan class=\"mi\"\u003e20\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# 1MB\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"p\"\u003e)\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ews\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# Subscribe to Transfer FROM Vitalik\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esub_from\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;jsonrpc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;2.0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;method\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;eth_subscribe\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;params\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;logs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;topics\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eTRANSFER_TOPIC\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eVITALIK_PADDED\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ews\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edumps\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esub_from\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eresp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003easyncio\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewait_for\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ews\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erecv\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edata\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eloads\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresp\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;error\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eraise\u003c/span\u003e \u003cspan class=\"ne\"\u003eException\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Subscribe failed: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;error\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esub_id_from\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edata\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;result\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;✅ Subscribed to Transfer FROM Vitalik (id=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esub_id_from\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e...)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# Also subscribe to Transfer TO Vitalik (monitor buys/receives)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esub_to\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;jsonrpc\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;2.0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"mi\"\u003e2\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;method\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;eth_subscribe\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;params\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;logs\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"s2\"\u003e\u0026#34;topics\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eTRANSFER_TOPIC\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eVITALIK_PADDED\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"p\"\u003e}]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ews\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esend\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edumps\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esub_to\u003c/span\u003e\u003cspan class=\"p\"\u003e))\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eresp2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003easyncio\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewait_for\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ews\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erecv\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e \u003cspan class=\"n\"\u003etimeout\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003edata2\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eloads\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eresp2\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003esub_id_to\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edata2\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;result\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003esub_id_to\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;✅ Subscribed to Transfer TO Vitalik (id=\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esub_id_to\u003c/span\u003e\u003cspan class=\"p\"\u003e[:\u003c/span\u003e\u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e...)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;🔍 Monitoring Vitalik wallet... waiting for events\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# Listen for events\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003efor\u003c/span\u003e \u003cspan class=\"n\"\u003emessage\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003ews\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003erunning\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003eevt\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eloads\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emessage\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;params\u0026#34;\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"ow\"\u003ein\u003c/span\u003e \u003cspan class=\"n\"\u003eevt\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"k\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003esub_id\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eevt\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;params\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;subscription\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eevt\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;params\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;result\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"p\"\u003e{})\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003esub_id\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003esub_id_from\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"c1\"\u003e# Vitalik sent tokens → check if it\u0026#39;s a sell\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ehandle_transfer_event\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003eelif\u003c/span\u003e \u003cspan class=\"n\"\u003esub_id\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"n\"\u003esub_id_to\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"c1\"\u003e# Vitalik received tokens — log silently\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;address\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003etoken_info\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003eget_token_info\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003edata_hex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003elog_entry\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eget\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;data\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;0x0\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003eamount\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003edecode_transfer_value\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edata_hex\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003etoken_info\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;decimals\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e])\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003edebug\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;[Receive] \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003etoken_info\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;symbol\u0026#39;\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e +\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eamount\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e,.4f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"n\"\u003ejson\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eJSONDecodeError\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"k\"\u003econtinue\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"ne\"\u003eException\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e                \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Event handling error: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_info\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"kc\"\u003eTrue\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003easync\u003c/span\u003e \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"s2\"\u003e\u0026#34;\u0026#34;\u0026#34;Main loop with auto-reconnect\u0026#34;\u0026#34;\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eglobal\u003c/span\u003e \u003cspan class=\"n\"\u003erunning\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Validate required config\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_BOT_TOKEN\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewarning\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;TG_BOT_TOKEN not set — Telegram notifications disabled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_CHAT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewarning\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;TG_CHAT_ID not set — Telegram notifications disabled\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Signal handling\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eshutdown\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esig\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eframe\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eglobal\u003c/span\u003e \u003cspan class=\"n\"\u003erunning\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Received signal \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003esig\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e, shutting down...\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003erunning\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eFalse\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSIGINT\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eshutdown\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003esignal\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSIGTERM\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eshutdown\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Startup notice\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e50\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Vitalik Sell Radar — Started\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Monitoring: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eVITALIK_ADDRESS\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Min notify: $\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eMIN_NOTIFY_USD\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Telegram: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"s1\"\u003e\u0026#39;✅ Configured\u0026#39;\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_BOT_TOKEN\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"s1\"\u003e\u0026#39;❌ Not configured\u0026#39;\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;=\u0026#34;\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mi\"\u003e50\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_BOT_TOKEN\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eTG_CHAT_ID\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003esend_telegram\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;🟢 Vitalik Sell Radar — Started\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Monitoring: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eshorten_addr\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eVITALIK_ADDRESS\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Min notify: $\u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eMIN_NOTIFY_USD\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"se\"\u003e\\n\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"s2\"\u003e\u0026#34;Mode: WebSocket event-driven (sub-second latency)\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003eendpoint_idx\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"mi\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003ereconnect_delay\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eRECONNECT_DELAY\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ewhile\u003c/span\u003e \u003cspan class=\"n\"\u003erunning\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ews_url\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eWS_ENDPOINTS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eendpoint_idx\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eWS_ENDPOINTS\u003c/span\u003e\u003cspan class=\"p\"\u003e)]\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003etry\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003esubscribe_and_listen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ews_url\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003ereconnect_delay\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eRECONNECT_DELAY\u003c/span\u003e  \u003cspan class=\"c1\"\u003e# reset on success\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"n\"\u003ewebsockets\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexceptions\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eConnectionClosed\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewarning\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;WebSocket closed: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"n\"\u003easyncio\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eTimeoutError\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ewarning\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;WebSocket timeout\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eexcept\u003c/span\u003e \u003cspan class=\"ne\"\u003eException\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eerror\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;WebSocket error: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ee\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003erunning\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"k\"\u003ebreak\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"c1\"\u003e# Switch endpoint and retry\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003eendpoint_idx\u003c/span\u003e \u003cspan class=\"o\"\u003e+=\u003c/span\u003e \u003cspan class=\"mi\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Reconnecting in \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003ereconnect_delay\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003es... (next: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003eWS_ENDPOINTS\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"n\"\u003eendpoint_idx\u003c/span\u003e \u003cspan class=\"o\"\u003e%\u003c/span\u003e \u003cspan class=\"nb\"\u003elen\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eWS_ENDPOINTS\u003c/span\u003e\u003cspan class=\"p\"\u003e)]\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e)\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003easyncio\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003esleep\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ereconnect_delay\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ereconnect_delay\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"nb\"\u003emin\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ereconnect_delay\u003c/span\u003e \u003cspan class=\"o\"\u003e*\u003c/span\u003e \u003cspan class=\"mf\"\u003e1.5\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eMAX_RECONNECT_DELAY\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"c1\"\u003e# Cleanup\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"ow\"\u003enot\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclosed\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eawait\u003c/span\u003e \u003cspan class=\"n\"\u003ehttp_session\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003elog\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003einfo\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Vitalik Sell Radar — Stopped\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"vm\"\u003e__name__\u003c/span\u003e \u003cspan class=\"o\"\u003e==\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;__main__\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"n\"\u003easyncio\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003erun\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emain\u003c/span\u003e\u003cspan class=\"p\"\u003e())\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003chr\u003e\n\u003ch3 id=\"链上雷达\"\u003e链上雷达\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e发布日期:\u003c/strong\u003e 2026.04.28　\u003cstrong\u003e标签:\u003c/strong\u003e \u003ccode\u003ePython\u003c/code\u003e · \u003ccode\u003eGMGN\u003c/code\u003e · \u003ccode\u003eDEXScreener\u003c/code\u003e · \u003ccode\u003eTelegram\u003c/code\u003e\u003c/p\u003e","title":"Code Vault — 7 个开源的加密雷达与交易工具合集"},{"content":"发现 TikCoin：一起提升我们收益的革命性方式！ 我刚刚发现了一种令人惊叹的方法，可以一起增加我们的收益！如果您加入TikCoin，我们都将受益。活跃伙伴越多，我们的奖励就越高！💰✨\nTikCoin是一个尖端的加密货币平台，将挖矿、社区奖励和伙伴激励结合起来，为所有参与者创造共赢生态系统。\n立即注册并与我一起开始挖矿：GRP-NBUC3PAH\n为什么 TikCoin 挖矿与众不同 社区驱动的奖励 与您独自工作的传统挖矿不同，TikCoin的奖励随着社区参与而增加。当您使用我的邀请码加入时，我们俩都从不断增长的网络中受益。\n被动收入生成 通过TikCoin的创新挖矿系统开始赚取加密货币奖励。加入我们网络的人越多，每个人获得的奖励就越高。\n轻松入门 无需昂贵的硬件。只需使用邀请链接注册即可立即开始挖矿。\n推荐奖金 通过邀请朋友和家人加入TikCoin赚取额外奖励。我们社区增长得越大，我们所有人赚得就越多。\nTikCoin 挖矿如何运作 第1步：使用邀请码注册 使用我的独家邀请码 GRP-NBUC3PAH 加入TikCoin网络。\n第2步：开始挖矿 通过平台的挖矿系统开始赚取TikCoin代币。\n第3步：邀请伙伴 分享您的邀请码来增长我们的社区并增加奖励。\n第4步：一起赚钱 随着每个新活跃成员的加入，看着我们的集体奖励增长。\n奖励分配 活跃参与的基础挖矿奖励 基于网络增长的社区奖金 带来新成员的推荐奖励 特殊活动奖金和赠品 加入 TikCoin 的好处 财务优势 被动赚取加密货币 潜在显著回报 多种收入来源 社区优势 加入不断增长的矿工网络 访问独家社区活动 来自经验丰富成员的支持 安全功能 安全钱包集成 受保护的交易处理 定期平台审计 TikCoin 挖矿计划 入门计划 低入门门槛 基础挖矿奖励 社区访问 专业计划 增强挖矿能力 更高奖励率 优先支持 企业计划 最大挖矿功率 最佳奖励乘数 VIP社区访问 TikCoin 用户成功案例 纽约的Alex：\u0026ldquo;自从用朋友的代码加入以来，我的收益翻了三倍！社区方面让它更有价值。\u0026rdquo;\n伦敦的Sarah：\u0026ldquo;TikCoin改变了我的加密挖矿思维方式。这不仅仅是赚钱，而是一起建设一些东西。\u0026rdquo;\n东京的Mike：\u0026ldquo;推荐奖金令人难以置信。我带来的每个新成员都会增加每个人的奖励。\u0026rdquo;\n开始使用 TikCoin 准备加入革命了吗？以下是方法：\n点击注册链接：立即加入TikCoin 输入邀请码：GRP-NBUC3PAH 完成注册：设置您的账户 开始挖矿：立即开始赚钱 邀请他人：分享您的成功 为什么选择我的邀请码？ 当您使用 GRP-NBUC3PAH 时，您将加入活跃矿工的成熟网络。这意味着：\n更高的初始奖励 更快的社区增长奖金 新功能的优先访问 来自经验丰富社区成员的支持 TikCoin vs 传统挖矿 功能 传统挖矿 TikCoin 挖矿 所需硬件 昂贵的GPU/ASIC 无 社区优势 无 高 奖励扩展 固定 与网络一起增加 入门简易性 复杂 简单 被动收入 有限 高 安全性和透明度 TikCoin优先考虑用户安全，包括：\n高级加密 透明奖励计算 安全钱包连接 定期安全更新 社区挖矿的未来 TikCoin代表加密货币挖矿的未来 - 个人成功促进集体繁荣。随着我们的社区增长，每个人的奖励也会增长。\n不要错过成为革命性事物一部分的机会！\n立即注册，使用代码 GRP-NBUC3PAH\n常见问题 TikCoin是什么？ TikCoin是一个社区驱动的加密货币平台，为挖矿和网络参与奖励用户。\n奖励如何运作？ 奖励基于社区参与和网络增长而增加。\nTikCoin安全吗？ 是的，具有银行级安全和定期审计。\n我可以提取我的收益吗？ 是的，收益可以随时提取到受支持的钱包。\n推荐如何运作？ 分享您的邀请码，当他人加入时赚取奖金奖励。\n立即加入TikCoin社区\n加入TikCoin - 我们一起赚更多！ 社区挖矿的力量就在这里。今天加入TikCoin，让我们一起增加收益！\n开始使用TikCoin挖矿\n免责声明：加密货币挖矿涉及风险。始终在参与前进行彻底研究。\n","permalink":"https://dibi8.com/zh/posts/%E5%8A%A0%E5%85%A5-tikcoin-%E6%8C%96%E7%9F%BF-%E4%B8%80%E8%B5%B7%E5%A2%9E%E5%8A%A0%E6%88%91%E4%BB%AC%E7%9A%84%E6%94%B6%E7%9B%8A/","summary":"\u003ch2 id=\"发现-tikcoin一起提升我们收益的革命性方式\"\u003e发现 TikCoin：一起提升我们收益的革命性方式！\u003c/h2\u003e\n\u003cp\u003e我刚刚发现了一种令人惊叹的方法，可以一起增加我们的收益！如果您加入TikCoin，我们都将受益。活跃伙伴越多，我们的奖励就越高！💰✨\u003c/p\u003e\n\u003cp\u003eTikCoin是一个尖端的加密货币平台，将挖矿、社区奖励和伙伴激励结合起来，为所有参与者创造共赢生态系统。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://tikcoin.info/?invitationCode=GRP-NBUC3PAH\"\u003e立即注册并与我一起开始挖矿：GRP-NBUC3PAH\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"为什么-tikcoin-挖矿与众不同\"\u003e为什么 TikCoin 挖矿与众不同\u003c/h2\u003e\n\u003ch3 id=\"社区驱动的奖励\"\u003e\u003cstrong\u003e社区驱动的奖励\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e与您独自工作的传统挖矿不同，TikCoin的奖励随着社区参与而增加。当您使用我的邀请码加入时，我们俩都从不断增长的网络中受益。\u003c/p\u003e\n\u003ch3 id=\"被动收入生成\"\u003e\u003cstrong\u003e被动收入生成\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e通过TikCoin的创新挖矿系统开始赚取加密货币奖励。加入我们网络的人越多，每个人获得的奖励就越高。\u003c/p\u003e\n\u003ch3 id=\"轻松入门\"\u003e\u003cstrong\u003e轻松入门\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e无需昂贵的硬件。只需使用邀请链接注册即可立即开始挖矿。\u003c/p\u003e\n\u003ch3 id=\"推荐奖金\"\u003e\u003cstrong\u003e推荐奖金\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e通过邀请朋友和家人加入TikCoin赚取额外奖励。我们社区增长得越大，我们所有人赚得就越多。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"TikCoin 挖矿奖励\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHYxOc_aAAA3hKG?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003ch2 id=\"tikcoin-挖矿如何运作\"\u003eTikCoin 挖矿如何运作\u003c/h2\u003e\n\u003ch3 id=\"第1步使用邀请码注册\"\u003e\u003cstrong\u003e第1步：使用邀请码注册\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e使用我的独家邀请码 \u003cstrong\u003eGRP-NBUC3PAH\u003c/strong\u003e 加入TikCoin网络。\u003c/p\u003e\n\u003ch3 id=\"第2步开始挖矿\"\u003e\u003cstrong\u003e第2步：开始挖矿\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e通过平台的挖矿系统开始赚取TikCoin代币。\u003c/p\u003e\n\u003ch3 id=\"第3步邀请伙伴\"\u003e\u003cstrong\u003e第3步：邀请伙伴\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e分享您的邀请码来增长我们的社区并增加奖励。\u003c/p\u003e\n\u003ch3 id=\"第4步一起赚钱\"\u003e\u003cstrong\u003e第4步：一起赚钱\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e随着每个新活跃成员的加入，看着我们的集体奖励增长。\u003c/p\u003e\n\u003ch3 id=\"奖励分配\"\u003e\u003cstrong\u003e奖励分配\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e活跃参与的基础挖矿奖励\u003c/li\u003e\n\u003cli\u003e基于网络增长的社区奖金\u003c/li\u003e\n\u003cli\u003e带来新成员的推荐奖励\u003c/li\u003e\n\u003cli\u003e特殊活动奖金和赠品\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"TikCoin 社区增长\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHYxOc4aYAAYP8v?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003ch2 id=\"加入-tikcoin-的好处\"\u003e加入 TikCoin 的好处\u003c/h2\u003e\n\u003ch3 id=\"财务优势\"\u003e\u003cstrong\u003e财务优势\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e被动赚取加密货币\u003c/li\u003e\n\u003cli\u003e潜在显著回报\u003c/li\u003e\n\u003cli\u003e多种收入来源\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"社区优势\"\u003e\u003cstrong\u003e社区优势\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e加入不断增长的矿工网络\u003c/li\u003e\n\u003cli\u003e访问独家社区活动\u003c/li\u003e\n\u003cli\u003e来自经验丰富成员的支持\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"安全功能\"\u003e\u003cstrong\u003e安全功能\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e安全钱包集成\u003c/li\u003e\n\u003cli\u003e受保护的交易处理\u003c/li\u003e\n\u003cli\u003e定期平台审计\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"tikcoin-挖矿计划\"\u003eTikCoin 挖矿计划\u003c/h2\u003e\n\u003ch3 id=\"入门计划\"\u003e\u003cstrong\u003e入门计划\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e低入门门槛\u003c/li\u003e\n\u003cli\u003e基础挖矿奖励\u003c/li\u003e\n\u003cli\u003e社区访问\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"专业计划\"\u003e\u003cstrong\u003e专业计划\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e增强挖矿能力\u003c/li\u003e\n\u003cli\u003e更高奖励率\u003c/li\u003e\n\u003cli\u003e优先支持\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"企业计划\"\u003e\u003cstrong\u003e企业计划\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e最大挖矿功率\u003c/li\u003e\n\u003cli\u003e最佳奖励乘数\u003c/li\u003e\n\u003cli\u003eVIP社区访问\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"tikcoin-用户成功案例\"\u003eTikCoin 用户成功案例\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e纽约的Alex\u003c/strong\u003e：\u0026ldquo;自从用朋友的代码加入以来，我的收益翻了三倍！社区方面让它更有价值。\u0026rdquo;\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e伦敦的Sarah\u003c/strong\u003e：\u0026ldquo;TikCoin改变了我的加密挖矿思维方式。这不仅仅是赚钱，而是一起建设一些东西。\u0026rdquo;\u003c/p\u003e","title":"加入 TikCoin 挖矿 - 一起增加我们的收益！"},{"content":"介绍 TikChain - 用区块链重塑社交媒体 在社交媒体和加密货币动态发展的环境中，TikChain 作为一个突破性平台出现，它弥合了内容创作者、观众和区块链技术之间的差距。无论您是 TikTok 爱好者、内容创作者还是加密投资者，TikChain 都为现代数字经济提供创新解决方案。\n今天加入 TikChain\n什么是 TikChain？ TikChain 是一个专为社交媒体集成设计的尖端区块链平台。它利用先进区块链技术创建一个去中心化生态系统，让用户能够通过代币奖励和独家功能来货币化他们的社交媒体影响力。\n主要功能： 社交媒体集成：与主要社交平台无缝连接 代币奖励：为内容创作和参与赚取代币 去中心化治理：社区驱动的平台开发 安全交易：基于区块链的支付系统 跨平台兼容性：跨多个社交媒体网络工作 为什么选择 TikChain？ 1. 赋权内容创作者 将您的社交媒体影响力转化为有形价值。TikChain 允许创作者通过代币奖励和独家功能货币化他们的内容。\n2. 社区驱动经济 在一个去中心化经济中参与，其中您的参与直接有助于平台增长和治理。\n3. 安全透明 基于强大的区块链技术构建，确保透明度、安全性和所有交易的不可变性。\n4. 多平台支持 无缝集成 TikTok、Instagram、Twitter 和其他社交媒体平台。\n5. 面向未来的技术 领先区块链创新和社交媒体趋势的曲线。\nTikChain 如何运作 逐步指南： 注册：使用推荐链接创建账户 连接平台：链接您的社交媒体账户 开始赚取：通过参与开始赚取代币 参与：加入社区活动和治理 提取收益：随时将收益转移到您的钱包 代币经济学： 原生代币：TikChain 的平台功能代币 奖励系统：为点赞、分享和内容创作赚取代币 质押选项：锁定代币以获得额外收益 推荐程序：通过邀请他人赚取奖金奖励 加入 TikChain 的好处 内容创作者 货币化社交媒体影响力 建立忠诚社区 访问独家创作者工具 参与平台治理 用户 为参与赚取奖励 访问优质内容 参与社区活动 安全数字资产管理 投资者 早期访问创新平台 潜在代币升值 治理参与 多元加密投资组合 TikChain 生态系统 TikChain 创建一个全面生态系统，包括：\n内容市场：买卖数字内容 NFT 集成：创建和交易社交媒体 NFT DeFi 功能：借贷、借出和收益 farming 游戏元素：游戏化社交媒体体验 开始使用 TikChain 准备探索社交媒体区块链的未来吗？按照以下步骤操作：\n访问平台：注册 TikChain 完成注册：设置您的个人资料 连接账户：链接您的社交媒体个人资料 开始探索：发现功能并赚取奖励 参与社区：加入讨论和活动 安全和合规 TikChain 优先考虑用户安全，包括：\n端到端加密 安全钱包集成 定期安全审计 遵守全球法规 社交媒体的未来 随着社交媒体的不断演变，像 TikChain 这样的平台代表下一代数字互动。通过将社交媒体与区块链技术的最佳结合，TikChain 为创作者、用户和投资者创造新机会。\n今天成为 TikChain 的一部分\n社区功能 加入不断增长的 TikChain 社区：\n开发者论坛和讨论 定期 AMA 与团队 社区活动和赠品 教育资源和教程 常见问题 TikChain 有何独特之处？ TikChain 将社交媒体参与与区块链奖励独特结合，创建一个无缝的内容货币化生态系统。\n如何在 TikChain 上赚取代币？ 通过内容创作、社交媒体参与、社区参与和推荐程序赚取代币。\nTikChain 安全吗？ 是的，具有区块链安全、加密和定期审计。\n支持哪些社交媒体平台？ TikChain 支持与 TikTok、Instagram、Twitter、YouTube 等集成。\n如何提取我的收益？ 可以通过受支持的加密钱包进行提取或兑换其他资产。\n开始您的 TikChain 之旅\n加入 TikChain 革命 不要错过这个将社交媒体与区块链结合的革命性平台。无论您是创作者、用户还是投资者，TikChain 都能为您提供。\n立即注册，使用推荐链接\n免责声明：加密货币和区块链投资涉及风险。始终在参与前进行彻底研究。\n","permalink":"https://dibi8.com/zh/posts/%E5%8F%91%E7%8E%B0-tikchain-%E6%82%A8%E7%9A%84%E7%A4%BE%E4%BA%A4%E5%AA%92%E4%BD%93%E5%8C%BA%E5%9D%97%E9%93%BE%E7%BD%91%E5%85%B3/","summary":"\u003ch2 id=\"介绍-tikchain---用区块链重塑社交媒体\"\u003e介绍 TikChain - 用区块链重塑社交媒体\u003c/h2\u003e\n\u003cp\u003e在社交媒体和加密货币动态发展的环境中，TikChain 作为一个突破性平台出现，它弥合了内容创作者、观众和区块链技术之间的差距。无论您是 TikTok 爱好者、内容创作者还是加密投资者，TikChain 都为现代数字经济提供创新解决方案。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://tikchain.network/user/luckybbjason\"\u003e今天加入 TikChain\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"什么是-tikchain\"\u003e什么是 TikChain？\u003c/h2\u003e\n\u003cp\u003eTikChain 是一个专为社交媒体集成设计的尖端区块链平台。它利用先进区块链技术创建一个去中心化生态系统，让用户能够通过代币奖励和独家功能来货币化他们的社交媒体影响力。\u003c/p\u003e\n\u003ch3 id=\"主要功能\"\u003e主要功能：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e社交媒体集成\u003c/strong\u003e：与主要社交平台无缝连接\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e代币奖励\u003c/strong\u003e：为内容创作和参与赚取代币\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e去中心化治理\u003c/strong\u003e：社区驱动的平台开发\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e安全交易\u003c/strong\u003e：基于区块链的支付系统\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e跨平台兼容性\u003c/strong\u003e：跨多个社交媒体网络工作\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"TikChain 平台概览\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHEEgA6WkAAH7Fl?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003ch2 id=\"为什么选择-tikchain\"\u003e为什么选择 TikChain？\u003c/h2\u003e\n\u003ch3 id=\"1-赋权内容创作者\"\u003e1. \u003cstrong\u003e赋权内容创作者\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e将您的社交媒体影响力转化为有形价值。TikChain 允许创作者通过代币奖励和独家功能货币化他们的内容。\u003c/p\u003e\n\u003ch3 id=\"2-社区驱动经济\"\u003e2. \u003cstrong\u003e社区驱动经济\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e在一个去中心化经济中参与，其中您的参与直接有助于平台增长和治理。\u003c/p\u003e\n\u003ch3 id=\"3-安全透明\"\u003e3. \u003cstrong\u003e安全透明\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e基于强大的区块链技术构建，确保透明度、安全性和所有交易的不可变性。\u003c/p\u003e\n\u003ch3 id=\"4-多平台支持\"\u003e4. \u003cstrong\u003e多平台支持\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e无缝集成 TikTok、Instagram、Twitter 和其他社交媒体平台。\u003c/p\u003e\n\u003ch3 id=\"5-面向未来的技术\"\u003e5. \u003cstrong\u003e面向未来的技术\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e领先区块链创新和社交媒体趋势的曲线。\u003c/p\u003e\n\u003ch2 id=\"tikchain-如何运作\"\u003eTikChain 如何运作\u003c/h2\u003e\n\u003ch3 id=\"逐步指南\"\u003e逐步指南：\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e注册\u003c/strong\u003e：使用推荐链接创建账户\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e连接平台\u003c/strong\u003e：链接您的社交媒体账户\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e开始赚取\u003c/strong\u003e：通过参与开始赚取代币\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e参与\u003c/strong\u003e：加入社区活动和治理\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e提取收益\u003c/strong\u003e：随时将收益转移到您的钱包\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"代币经济学\"\u003e代币经济学：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e原生代币\u003c/strong\u003e：TikChain 的平台功能代币\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e奖励系统\u003c/strong\u003e：为点赞、分享和内容创作赚取代币\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e质押选项\u003c/strong\u003e：锁定代币以获得额外收益\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e推荐程序\u003c/strong\u003e：通过邀请他人赚取奖金奖励\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"代币奖励系统\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HGWsb5FbIAAQavy?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003ch2 id=\"加入-tikchain-的好处\"\u003e加入 TikChain 的好处\u003c/h2\u003e\n\u003ch3 id=\"内容创作者\"\u003e\u003cstrong\u003e内容创作者\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e货币化社交媒体影响力\u003c/li\u003e\n\u003cli\u003e建立忠诚社区\u003c/li\u003e\n\u003cli\u003e访问独家创作者工具\u003c/li\u003e\n\u003cli\u003e参与平台治理\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"用户\"\u003e\u003cstrong\u003e用户\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e为参与赚取奖励\u003c/li\u003e\n\u003cli\u003e访问优质内容\u003c/li\u003e\n\u003cli\u003e参与社区活动\u003c/li\u003e\n\u003cli\u003e安全数字资产管理\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"投资者\"\u003e\u003cstrong\u003e投资者\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e早期访问创新平台\u003c/li\u003e\n\u003cli\u003e潜在代币升值\u003c/li\u003e\n\u003cli\u003e治理参与\u003c/li\u003e\n\u003cli\u003e多元加密投资组合\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"tikchain-生态系统\"\u003eTikChain 生态系统\u003c/h2\u003e\n\u003cp\u003eTikChain 创建一个全面生态系统，包括：\u003c/p\u003e","title":"发现 TikChain - 您的社交媒体区块链网关"},{"content":"介绍 Billions 钱包 - 重塑加密货币管理 在快速发展的加密货币世界中，拥有可靠且功能丰富的钱包至关重要。Billions 钱包作为一个全面的解决方案出现，旨在满足您所有的数字资产管理需求。无论您是经验丰富的交易者还是刚开始加密货币之旅，Billions 钱包都提供无与伦比的安全性、功能性和用户体验。\n立即开始使用 Billions 钱包\n为什么选择 Billions 钱包？ 1. 无与伦比的安全功能 Billions 钱包优先考虑您的资产安全，通过先进的加密、多重签名支持和生物识别认证。您的加密货币受到银行级安全协议的保护。\n2. 多资产支持 管理各种加密货币，包括比特币、以太坊、稳定币和其他山寨币。Billions 钱包支持跨多个区块链的 1000+ 种数字资产。\n3. 用户友好界面 以简洁为设计理念，直观界面使初学者和专家都能轻松浏览和管理投资组合。\n4. 高级交易工具 内置交换功能，允许您直接在钱包中交易加密货币，具有竞争性利率和快速执行。\n5. 质押和收益机会 通过质押您的资产或参与收益 farming 程序，在钱包中赚取被动收入。\nBillions 钱包的关键功能 安全存储 硬件钱包集成 冷存储选项 加密私钥 恢复短语备份 DeFi 集成 去中心化交易所访问 流动性池参与 收益 farming 机会 NFT 市场 分析和洞察 实时投资组合跟踪 价格警报和通知 绩效分析 税务报告工具 开始使用 Billions 钱包 步骤1：下载并安装 访问官方网站，为您的首选平台下载 Billions 钱包 - iOS、Android、桌面或网络。\n步骤2：创建账户 使用推荐链接注册以获得独家奖金和增强功能。\n创建您的 Billions 钱包账户\n步骤3：保护钱包 设置生物识别认证并在安全位置备份恢复短语。\n步骤4：添加资产 存入加密货币或通过集成交易所直接购买。\n步骤5：开始管理和交易 探索所有功能，开始优化您的加密货币投资组合。\nBillions 钱包 vs 传统钱包 功能 Billions 钱包 传统钱包 多资产支持 1000+ 资产 有限 DeFi 集成 完全支持 无 内置交易所 是 否 质押奖励 高收益 低或无 安全性 军用级 基本 用户体验 直观 复杂 社区和支持 加入蓬勃发展的 Billions 钱包社区：\n活跃的 Discord 和 Telegram 群组 全面的知识库 24/7 客户支持 定期更新和新功能 Billions 用户成功故事 Alex Chen，新加坡：\u0026ldquo;Billions 钱包改变了我的加密体验。质押奖励令人难以置信！\u0026rdquo;\nMaria Rodriguez，西班牙：\u0026ldquo;终于有一个钱包支持我最喜欢的山寨币。强烈推荐！\u0026rdquo;\nDavid Kim，韩国：\u0026ldquo;安全功能让我放心。这是最好的钱包。\u0026rdquo;\n未来路线图 Billions 钱包不断发展，计划功能包括：\n跨链互操作性 增强 NFT 支持 机构级工具 AI 驱动交易助手 常见问题 Billions 钱包安全吗？ 是的，具有多层安全、加密和定期审计。\n支持哪些加密货币？ 包括 BTC、ETH、USDT 等 1000+ 资产。\n如何在 Billions 上赚取奖励？ 通过质押、收益 farming 和推荐程序。\n有移动应用吗？ 是的，可在 CH Play 和 App Store 下载。\n立即下载 Billions 钱包\n加密货币钱包的未来在这里 Billions 钱包代表下一代加密货币管理工具。以其全面的功能集、顶级安全性和用户中心设计，它有望成为加密货币爱好者的首选钱包。\n不要错过升级加密货币体验的机会。加入已发现 Billions 钱包力量的数百万用户。\n立即注册，使用代码 33E57NWH45\n免责声明：加密货币投资涉及风险。始终进行彻底研究后再投资。\n","permalink":"https://dibi8.com/zh/posts/%E5%8F%91%E7%8E%B0-billions-%E9%92%B1%E5%8C%85-%E6%82%A8%E7%9A%84%E7%BB%88%E6%9E%81%E5%8A%A0%E5%AF%86%E8%B4%A7%E5%B8%81%E4%BC%B4%E4%BE%A3/","summary":"\u003ch2 id=\"介绍-billions-钱包---重塑加密货币管理\"\u003e介绍 Billions 钱包 - 重塑加密货币管理\u003c/h2\u003e\n\u003cp\u003e在快速发展的加密货币世界中，拥有可靠且功能丰富的钱包至关重要。Billions 钱包作为一个全面的解决方案出现，旨在满足您所有的数字资产管理需求。无论您是经验丰富的交易者还是刚开始加密货币之旅，Billions 钱包都提供无与伦比的安全性、功能性和用户体验。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://official.app/rc/33E57NWH45\"\u003e立即开始使用 Billions 钱包\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"为什么选择-billions-钱包\"\u003e为什么选择 Billions 钱包？\u003c/h2\u003e\n\u003ch3 id=\"1-无与伦比的安全功能\"\u003e1. \u003cstrong\u003e无与伦比的安全功能\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eBillions 钱包优先考虑您的资产安全，通过先进的加密、多重签名支持和生物识别认证。您的加密货币受到银行级安全协议的保护。\u003c/p\u003e\n\u003ch3 id=\"2-多资产支持\"\u003e2. \u003cstrong\u003e多资产支持\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e管理各种加密货币，包括比特币、以太坊、稳定币和其他山寨币。Billions 钱包支持跨多个区块链的 1000+ 种数字资产。\u003c/p\u003e\n\u003ch3 id=\"3-用户友好界面\"\u003e3. \u003cstrong\u003e用户友好界面\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e以简洁为设计理念，直观界面使初学者和专家都能轻松浏览和管理投资组合。\u003c/p\u003e\n\u003ch3 id=\"4-高级交易工具\"\u003e4. \u003cstrong\u003e高级交易工具\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e内置交换功能，允许您直接在钱包中交易加密货币，具有竞争性利率和快速执行。\u003c/p\u003e\n\u003ch3 id=\"5-质押和收益机会\"\u003e5. \u003cstrong\u003e质押和收益机会\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e通过质押您的资产或参与收益 farming 程序，在钱包中赚取被动收入。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Billions 钱包功能\" loading=\"lazy\" src=\"https://pbs.twimg.com/card_img/2049154231807168512/LxIZxOKR?format=jpg\u0026name=small\"\u003e\u003c/p\u003e\n\u003ch2 id=\"billions-钱包的关键功能\"\u003eBillions 钱包的关键功能\u003c/h2\u003e\n\u003ch3 id=\"安全存储\"\u003e\u003cstrong\u003e安全存储\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e硬件钱包集成\u003c/li\u003e\n\u003cli\u003e冷存储选项\u003c/li\u003e\n\u003cli\u003e加密私钥\u003c/li\u003e\n\u003cli\u003e恢复短语备份\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"defi-集成\"\u003e\u003cstrong\u003eDeFi 集成\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e去中心化交易所访问\u003c/li\u003e\n\u003cli\u003e流动性池参与\u003c/li\u003e\n\u003cli\u003e收益 farming 机会\u003c/li\u003e\n\u003cli\u003eNFT 市场\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"分析和洞察\"\u003e\u003cstrong\u003e分析和洞察\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e实时投资组合跟踪\u003c/li\u003e\n\u003cli\u003e价格警报和通知\u003c/li\u003e\n\u003cli\u003e绩效分析\u003c/li\u003e\n\u003cli\u003e税务报告工具\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"钱包安全\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHCAye0aQAA29MM?format=png\u0026name=small\"\u003e\u003c/p\u003e\n\u003ch2 id=\"开始使用-billions-钱包\"\u003e开始使用 Billions 钱包\u003c/h2\u003e\n\u003ch3 id=\"步骤1下载并安装\"\u003e步骤1：下载并安装\u003c/h3\u003e\n\u003cp\u003e访问官方网站，为您的首选平台下载 Billions 钱包 - iOS、Android、桌面或网络。\u003c/p\u003e\n\u003ch3 id=\"步骤2创建账户\"\u003e步骤2：创建账户\u003c/h3\u003e\n\u003cp\u003e使用推荐链接注册以获得独家奖金和增强功能。\u003c/p\u003e","title":"发现 Billions 钱包 - 您的终极加密货币伴侣"},{"content":"探索 Access Network 数字资产处理的未来 查看这些来自我们社区的相关图片和视频：\n观看社区视频\n在区块链和加密货币不断发展的环境中，Access Network 作为一个革命性平台出现，它通过创新的挖矿技术让用户参与数字资产处理。无论您是加密货币新手还是经验丰富的矿工，Access Network 都提供了一种无缝方式来为网络做出贡献并赚取丰厚的奖励。\n立即加入 Access Network\n什么是 Access Network 挖矿？ Access Network 是一个尖端的区块链平台，利用先进的处理能力来验证交易、保护网络并分发数字资产。与需要昂贵硬件的传统挖矿不同，Access Network 采用基于云的处理，使挖矿对每个人都易于访问。\n主要功能： 基于云的挖矿：无需昂贵的 GPU 或 ASIC 即时奖励：处理后立即赚取代币 低能耗：环保的挖矿解决方案 全球可访问性：从世界任何地方加入 多资产支持：同时处理各种数字资产 为什么选择 Access Network 进行挖矿？ 1. 被动收入生成 无需持续监控即可开始赚取数字奖励。Access Network 的自动化系统处理一切，而您收集收益。\n2. 低入门门槛 与成本数千的传统挖矿设备不同，Access Network 挖矿需要最少的投资和技术知识。\n3. 可持续挖矿 与传统挖矿方法相比，能耗显著降低的环境意识方法。\n4. 社区驱动增长 加入蓬勃发展的矿工社区，随着更多用户参与而受益于网络效应。\n5. 面向未来的技术 基于适应市场变化和技术进步的下一代区块链技术构建。\nAccess Network 挖矿如何运作 逐步过程： 注册：使用邀请链接创建账户 选择计划：从各种挖矿包中选择 开始处理：立即开始赚取奖励 提取收益：随时将奖励转到您的钱包 挖矿机制： 处理能力分配：系统根据您的计划分配处理任务 奖励分发：实时计算收益 质押选项：锁定代币以获得额外奖励 推荐程序：通过邀请他人赚取奖金奖励 加入 Access Network 的好处 财务优势 竞争性奖励率 每日付款 复利选项 多种提取方法 技术优势 用户友好的界面 24/7 系统可用性 安全的交易处理 高级加密 社区功能 讨论论坛 教育资源 定期更新和新闻 来自经验丰富成员的支持 开始使用 Access Network 挖矿 准备开始您的挖矿之旅了吗？按照以下简单步骤操作：\n访问平台：使用邀请加入 完成注册：提供基本信息 验证账户：使用 2FA 保护您的账户 资金账户：添加初始投资 开始挖矿：开始处理和赚取 Access Network 挖矿计划 入门计划 最低投资：$50 每日奖励：1-2%% 处理能力：100 GH/s 专业计划 最低投资：$500 每日奖励：2-3%% 处理能力：1000 GH/s 企业计划 最低投资：$5000 每日奖励：3-5%% 处理能力：10000 GH/s 安全性和透明度 Access Network 优先考虑安全性，包括：\n军用级加密 定期安全审计 透明奖励计算 安全钱包集成 挖矿的未来 随着传统挖矿变得越来越困难和昂贵，Access Network 代表了数字资产处理的未来。通过利用云计算和创新算法，Access Network 使挖矿大众化，同时保持盈利能力。\n常见问题解答 Access Network 有何不同？ Access Network 将云计算与区块链处理相结合，消除硬件要求同时保持盈利能力。\n奖励多久支付一次？ 奖励每日计算并支付，即时提取可用。\nAccess Network 安全吗？ 是的，具有银行级安全性、加密和定期审计。\n我可以随时提取吗？ 是的，一旦请求，提取立即处理。\n立即开始您的挖矿之旅\n加入 Access Network 革命 不要错过成为数字资产处理革命一部分的机会。通过 Access Network，挖矿从未如此简单和有益。\n立即注册，使用代码 YCM9D7\n免责声明：数字资产处理涉及风险。始终负责任地投资并进行彻底研究。\n","permalink":"https://dibi8.com/zh/posts/%E5%8A%A0%E5%85%A5-access-network-%E6%8C%96%E7%9F%BF-%E5%A4%84%E7%90%86%E6%95%B0%E5%AD%97%E8%B5%84%E4%BA%A7%E5%B9%B6%E8%B5%9A%E5%8F%96%E5%A5%96%E5%8A%B1/","summary":"\u003ch2 id=\"探索-access-network-数字资产处理的未来\"\u003e探索 Access Network 数字资产处理的未来\u003c/h2\u003e\n\u003cp\u003e查看这些来自我们社区的相关图片和视频：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"挖矿图片 1\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHYvLD_bAAEtLCi?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"挖矿图片 2\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHZ2puFWUAEXRBs?format=jpg\u0026name=medium\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"挖矿图片 3\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HGq5w8bW4AA6ShR?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"挖矿图片 4\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HGnDWVOX0AANAWu?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://x.com/i/status/2047276073973346585\"\u003e观看社区视频\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e在区块链和加密货币不断发展的环境中，Access Network 作为一个革命性平台出现，它通过创新的挖矿技术让用户参与数字资产处理。无论您是加密货币新手还是经验丰富的矿工，Access Network 都提供了一种无缝方式来为网络做出贡献并赚取丰厚的奖励。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://accesschain.org/?invite=YCM9D7\"\u003e立即加入 Access Network\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"什么是-access-network-挖矿\"\u003e什么是 Access Network 挖矿？\u003c/h2\u003e\n\u003cp\u003eAccess Network 是一个尖端的区块链平台，利用先进的处理能力来验证交易、保护网络并分发数字资产。与需要昂贵硬件的传统挖矿不同，Access Network 采用基于云的处理，使挖矿对每个人都易于访问。\u003c/p\u003e\n\u003ch3 id=\"主要功能\"\u003e主要功能：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e基于云的挖矿\u003c/strong\u003e：无需昂贵的 GPU 或 ASIC\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e即时奖励\u003c/strong\u003e：处理后立即赚取代币\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e低能耗\u003c/strong\u003e：环保的挖矿解决方案\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e全球可访问性\u003c/strong\u003e：从世界任何地方加入\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e多资产支持\u003c/strong\u003e：同时处理各种数字资产\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"为什么选择-access-network-进行挖矿\"\u003e为什么选择 Access Network 进行挖矿？\u003c/h2\u003e\n\u003ch3 id=\"1-被动收入生成\"\u003e1. \u003cstrong\u003e被动收入生成\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e无需持续监控即可开始赚取数字奖励。Access Network 的自动化系统处理一切，而您收集收益。\u003c/p\u003e\n\u003ch3 id=\"2-低入门门槛\"\u003e2. \u003cstrong\u003e低入门门槛\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e与成本数千的传统挖矿设备不同，Access Network 挖矿需要最少的投资和技术知识。\u003c/p\u003e\n\u003ch3 id=\"3-可持续挖矿\"\u003e3. \u003cstrong\u003e可持续挖矿\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e与传统挖矿方法相比，能耗显著降低的环境意识方法。\u003c/p\u003e\n\u003ch3 id=\"4-社区驱动增长\"\u003e4. \u003cstrong\u003e社区驱动增长\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e加入蓬勃发展的矿工社区，随着更多用户参与而受益于网络效应。\u003c/p\u003e\n\u003ch3 id=\"5-面向未来的技术\"\u003e5. \u003cstrong\u003e面向未来的技术\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e基于适应市场变化和技术进步的下一代区块链技术构建。\u003c/p\u003e\n\u003ch2 id=\"access-network-挖矿如何运作\"\u003eAccess Network 挖矿如何运作\u003c/h2\u003e\n\u003ch3 id=\"逐步过程\"\u003e逐步过程：\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e注册\u003c/strong\u003e：使用邀请链接创建账户\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e选择计划\u003c/strong\u003e：从各种挖矿包中选择\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e开始处理\u003c/strong\u003e：立即开始赚取奖励\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e提取收益\u003c/strong\u003e：随时将奖励转到您的钱包\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"挖矿机制\"\u003e挖矿机制：\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e处理能力分配\u003c/strong\u003e：系统根据您的计划分配处理任务\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e奖励分发\u003c/strong\u003e：实时计算收益\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e质押选项\u003c/strong\u003e：锁定代币以获得额外奖励\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e推荐程序\u003c/strong\u003e：通过邀请他人赚取奖金奖励\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"加入-access-network-的好处\"\u003e加入 Access Network 的好处\u003c/h2\u003e\n\u003ch3 id=\"财务优势\"\u003e\u003cstrong\u003e财务优势\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e竞争性奖励率\u003c/li\u003e\n\u003cli\u003e每日付款\u003c/li\u003e\n\u003cli\u003e复利选项\u003c/li\u003e\n\u003cli\u003e多种提取方法\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"技术优势\"\u003e\u003cstrong\u003e技术优势\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e用户友好的界面\u003c/li\u003e\n\u003cli\u003e24/7 系统可用性\u003c/li\u003e\n\u003cli\u003e安全的交易处理\u003c/li\u003e\n\u003cli\u003e高级加密\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"社区功能\"\u003e\u003cstrong\u003e社区功能\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e讨论论坛\u003c/li\u003e\n\u003cli\u003e教育资源\u003c/li\u003e\n\u003cli\u003e定期更新和新闻\u003c/li\u003e\n\u003cli\u003e来自经验丰富成员的支持\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"开始使用-access-network-挖矿\"\u003e开始使用 Access Network 挖矿\u003c/h2\u003e\n\u003cp\u003e准备开始您的挖矿之旅了吗？按照以下简单步骤操作：\u003c/p\u003e","title":"加入 Access Network 挖矿 - 处理数字资产并赚取奖励"},{"content":"这是一个有趣的Twitter帖子吸引了我的注意：\n在这里查看完整Twitter帖子\n您对这个帖子有什么看法？请在评论中分享您的想法！\n","permalink":"https://dibi8.com/zh/posts/%E6%9C%89%E8%B6%A3%E7%9A%84twitter%E5%B8%96%E5%AD%90-%E6%9D%A5%E7%9C%8B%E7%9C%8B/","summary":"\u003cp\u003e这是一个有趣的Twitter帖子吸引了我的注意：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Twitter图片1\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHYvLD_bAAEtLCi?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Twitter图片2\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HHZ2puFWUAEXRBs?format=jpg\u0026name=medium\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Twitter图片3\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HGq5w8bW4AA6ShR?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"Twitter图片4\" loading=\"lazy\" src=\"https://pbs.twimg.com/media/HGnDWVOX0AANAWu?format=jpg\u0026name=large\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://x.com/i/status/2047276073973346585\"\u003e在这里查看完整Twitter帖子\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e您对这个帖子有什么看法？请在评论中分享您的想法！\u003c/p\u003e","title":"有趣的Twitter帖子 - 来看看"},{"content":"在当今互联互通的全球经济中，企业面临着接受来自世界各地客户付款的挑战。传统支付方式往往将商家限制在特定货币或地区，但NowPayments通过启用所有货币的支付接受来改变这一局面。无论您的客户使用法定货币如美元、欧元或日元，还是加密货币如比特币、以太坊或稳定币，NowPayments都能确保无缝交易。\n什么是NowPayments？ NowPayments是一家领先的加密货币支付网关，它弥合了传统金融和数字资产之间的差距。公司秉持让加密支付对每个人都易于使用的愿景，为商家提供简单、安全、高效的全球支付方式。\n主要功能包括：\n通用货币支持：处理100多种加密货币和主要法定货币的支付 即时结算：资金直接进入您的钱包，无需中介 低交易费用：从每笔交易0.5%起的竞争性费率 高级安全：PCI DSS合规，多重签名钱包和加密 轻松集成：适用于WooCommerce、Shopify等热门电商平台的API和插件 为什么选择NowPayments进行全球支付？ 1. 打破货币壁垒 传统支付处理器往往将商家限制在其本地货币或少数主要货币上。NowPayments消除了这些壁垒，让您无需货币转换麻烦即可从世界任何地方接受支付。\n2. 拥抱加密货币采用 随着数字货币获得主流认可，NowPayments将您的业务置于这一趋势的前沿。除了传统支付外，还接受比特币、以太坊、USDT和其他热门加密货币。\n3. 降低交易成本 费用低至0.5%，与收取2-3%或更多费用的传统支付处理器相比，NowPayments提供显著节省。此外，即时结算意味着更快地访问您的资金。\n4. 提升客户体验 客户欣赏选择首选支付方式的灵活性。无论他们更喜欢信用卡、银行转账还是加密货币，NowPayments都能满足多样化偏好。\n5. 让您的业务面向未来 随着世界向数字金融迈进，集成NowPayments确保您的业务保持竞争力，并为新兴支付趋势做好准备。\n如何开始使用NowPayments 开始使用非常简单：\n注册：在NowPayments创建您的免费账户 选择您的计划：从入门版、商务版或企业版中选择 集成：使用我们的API或WooCommerce、Shopify等插件 开始接受支付：立即开始接收支付 实际应用场景 NowPayments为以下场景提供支付支持：\n电商商店：接受全球订单的在线零售商 自由职业者：从国际客户接收付款的专业人士 游戏公司：游戏内购买和订阅 非营利组织：来自世界各地支持者的捐款 软件公司：SaaS订阅和许可费用 安全性和合规性 NowPayments优先考虑安全性，包括：\n端到端加密 资金冷存储 定期安全审计 遵守国际法规 支付的未来就在这里 随着加密货币采用率的增长，像NowPayments这样的支付网关对于希望在数字时代蓬勃发展的企业至关重要。今天就不要落后 – 拥抱支付的未来。\n准备好接受所有货币的支付了吗？立即创建您的NowPayments账户，开始即时接收全球支付。\n创建账户\n加入已从无缝、无国界支付中受益的数千名商家。\n","permalink":"https://dibi8.com/zh/posts/%E6%89%80%E6%9C%89%E8%B4%A7%E5%B8%81%E5%8F%AF%E4%BB%A5%E6%94%AF%E4%BB%98-nowpayments/","summary":"\u003cp\u003e在当今互联互通的全球经济中，企业面临着接受来自世界各地客户付款的挑战。传统支付方式往往将商家限制在特定货币或地区，但NowPayments通过启用\u003cstrong\u003e所有货币的支付接受\u003c/strong\u003e来改变这一局面。无论您的客户使用法定货币如美元、欧元或日元，还是加密货币如比特币、以太坊或稳定币，NowPayments都能确保无缝交易。\u003c/p\u003e\n\u003ch2 id=\"什么是nowpayments\"\u003e什么是NowPayments？\u003c/h2\u003e\n\u003cp\u003eNowPayments是一家领先的加密货币支付网关，它弥合了传统金融和数字资产之间的差距。公司秉持让加密支付对每个人都易于使用的愿景，为商家提供简单、安全、高效的全球支付方式。\u003c/p\u003e\n\u003cp\u003e主要功能包括：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e通用货币支持\u003c/strong\u003e：处理100多种加密货币和主要法定货币的支付\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e即时结算\u003c/strong\u003e：资金直接进入您的钱包，无需中介\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e低交易费用\u003c/strong\u003e：从每笔交易0.5%起的竞争性费率\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e高级安全\u003c/strong\u003e：PCI DSS合规，多重签名钱包和加密\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e轻松集成\u003c/strong\u003e：适用于WooCommerce、Shopify等热门电商平台的API和插件\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"为什么选择nowpayments进行全球支付\"\u003e为什么选择NowPayments进行全球支付？\u003c/h2\u003e\n\u003ch3 id=\"1-打破货币壁垒\"\u003e1. 打破货币壁垒\u003c/h3\u003e\n\u003cp\u003e传统支付处理器往往将商家限制在其本地货币或少数主要货币上。NowPayments消除了这些壁垒，让您无需货币转换麻烦即可从世界任何地方接受支付。\u003c/p\u003e\n\u003ch3 id=\"2-拥抱加密货币采用\"\u003e2. 拥抱加密货币采用\u003c/h3\u003e\n\u003cp\u003e随着数字货币获得主流认可，NowPayments将您的业务置于这一趋势的前沿。除了传统支付外，还接受比特币、以太坊、USDT和其他热门加密货币。\u003c/p\u003e\n\u003ch3 id=\"3-降低交易成本\"\u003e3. 降低交易成本\u003c/h3\u003e\n\u003cp\u003e费用低至0.5%，与收取2-3%或更多费用的传统支付处理器相比，NowPayments提供显著节省。此外，即时结算意味着更快地访问您的资金。\u003c/p\u003e\n\u003ch3 id=\"4-提升客户体验\"\u003e4. 提升客户体验\u003c/h3\u003e\n\u003cp\u003e客户欣赏选择首选支付方式的灵活性。无论他们更喜欢信用卡、银行转账还是加密货币，NowPayments都能满足多样化偏好。\u003c/p\u003e\n\u003ch3 id=\"5-让您的业务面向未来\"\u003e5. 让您的业务面向未来\u003c/h3\u003e\n\u003cp\u003e随着世界向数字金融迈进，集成NowPayments确保您的业务保持竞争力，并为新兴支付趋势做好准备。\u003c/p\u003e\n\u003ch2 id=\"如何开始使用nowpayments\"\u003e如何开始使用NowPayments\u003c/h2\u003e\n\u003cp\u003e开始使用非常简单：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e注册\u003c/strong\u003e：在NowPayments创建您的免费账户\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e选择您的计划\u003c/strong\u003e：从入门版、商务版或企业版中选择\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e集成\u003c/strong\u003e：使用我们的API或WooCommerce、Shopify等插件\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e开始接受支付\u003c/strong\u003e：立即开始接收支付\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"实际应用场景\"\u003e实际应用场景\u003c/h2\u003e\n\u003cp\u003eNowPayments为以下场景提供支付支持：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e电商商店\u003c/strong\u003e：接受全球订单的在线零售商\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e自由职业者\u003c/strong\u003e：从国际客户接收付款的专业人士\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e游戏公司\u003c/strong\u003e：游戏内购买和订阅\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e非营利组织\u003c/strong\u003e：来自世界各地支持者的捐款\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e软件公司\u003c/strong\u003e：SaaS订阅和许可费用\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"安全性和合规性\"\u003e安全性和合规性\u003c/h2\u003e\n\u003cp\u003eNowPayments优先考虑安全性，包括：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e端到端加密\u003c/li\u003e\n\u003cli\u003e资金冷存储\u003c/li\u003e\n\u003cli\u003e定期安全审计\u003c/li\u003e\n\u003cli\u003e遵守国际法规\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"支付的未来就在这里\"\u003e支付的未来就在这里\u003c/h2\u003e\n\u003cp\u003e随着加密货币采用率的增长，像NowPayments这样的支付网关对于希望在数字时代蓬勃发展的企业至关重要。今天就不要落后 – 拥抱支付的未来。\u003c/p\u003e\n\u003cp\u003e准备好接受所有货币的支付了吗？立即创建您的NowPayments账户，开始即时接收全球支付。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://account.nowpayments.io/create-account?link_id=2402603534\u0026amp;utm_source=affiliate_lk\u0026amp;utm_medium=referral\"\u003e创建账户\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e加入已从无缝、无国界支付中受益的数千名商家。\u003c/p\u003e","title":"所有货币可以支付 - NowPayments"},{"content":"欢迎来到 RR6958 - 在线娱乐的巅峰 2026年，随着在线赌场行业的蓬勃发展，RR6958作为提供世界级游戏体验的首要目的地脱颖而出。凭借超过10年的专业经验，RR6958为全球数百万玩家提供公平、透明、高度娱乐的平台，拥有数千种多样化游戏和全天候顶级客户服务。\n立即注册领取100%%奖金\n为什么选择 RR6958？ 1. 数百种激动人心的赌场游戏 RR6958提供无与伦比的游戏体验，包括：\n与迷人荷官的现场百家乐 适合各种偏好的轮盘赌变体 规则简单、高胜率的二十一点 巨额奖金的老虎机 德州扑克、骰宝、龙虎斗等 2. 慷慨的奖金和促销 首次存款奖金：初始存款最高200%% 每周返现：根据您的等级5-15%% 忠诚度计划：可转换为真钱的积分 特殊活动：节日期间双倍奖金 3. 绝对安全和公平 SSL 256位加密保护所有数据 由国际组织认证的RNG 超快支付，1-5分钟内完成 来自专业团队的全天候支持 4. 用户友好的界面 移动端响应式设计 包括英语在内的多语言支持 实时聊天支持 iOS/Android便捷移动应用 如何注册 RR6958 开始使用非常简单 - 只需要3个步骤：\n访问注册页面：在RR6958注册 输入您的详细信息：姓名、邮箱、电话号码 验证并存款：立即获得奖金 RR6958 的多种支付方式 RR6958支持越南所有热门支付选项：\n越南银行转账（BIDV、Vietcombank、Techcombank） 电子钱包（Momo、ZaloPay、ViettelPay） 电话卡（Vinaphone、Mobifone、Viettel） 加密货币（比特币、以太坊、USDT） 当地代理进行面对面支付 在 RR6958 玩游戏的独家福利 优质游戏体验 越南服务器，实现闪电般的速度 4K图形和沉浸式声音 来自菲律宾的真实荷官 无中断的流畅游戏 持续的促销计划 所有会员的每日奖金 周末三倍奖金 活跃玩家的月度奖励 独家活动和锦标赛 五星级客户服务 热线：1900 XXX XXX（免费通话） 邮箱：support@rr6958.com 全天候实时聊天 说越南语的支持人员 RR6958 - 越南玩家首选 经过10多年的发展，RR6958自豪地为全球数百万玩家服务，并拥有：\n全球1000万+会员 所有类别1000+游戏 每月支付数十亿奖金 99.9%%支付成功率 今天就不要错过加入RR6958社区的机会！\n免费注册并领取200%%奖金\nRR6958 2026热门游戏 VIP百家乐 专业玩家的高级游戏，赔付高达9倍投注。\n欧洲轮盘赌 经典命运之轮，高胜率和吸引人的奖金。\n奖金老虎机 数百台老虎机等待您赢得百万美元奖金。\n在 RR6958 安全赌博提示 始终负责任地玩游戏，永远不要超过预算 检查每日促销 加入会员群组学习策略 使用推荐代码获得额外奖金 使用推荐代码vbx2083注册\n常见问题 RR6958 在越南合法吗？ RR6958 在国际许可证下运营，并遵守越南数据保护法。\n提款需要多长时间？ 提款在1-5分钟内处理。\n有移动应用吗？ 是的，可在CH Play和App Store下载。\n首次存款奖金是什么？ 首次存款200%%奖金，立即适用。\n立即注册获得200%%奖金\n结论 - RR6958 是在线赌场的未来 在2026年的数字时代，RR6958继续以尖端技术和卓越服务领先。现在加入来体验不同！\n加入RR6958 - 越南顶级赌场\n注意：负责任地玩游戏。RR6958鼓励健康的游戏习惯。\n","permalink":"https://dibi8.com/zh/posts/%E4%BB%8B%E7%BB%8D-rr6958-%E7%BB%88%E6%9E%81%E5%9C%A8%E7%BA%BF%E8%B5%8C%E5%9C%BA%E4%BD%93%E9%AA%8C/","summary":"\u003ch2 id=\"欢迎来到-rr6958---在线娱乐的巅峰\"\u003e欢迎来到 RR6958 - 在线娱乐的巅峰\u003c/h2\u003e\n\u003cp\u003e2026年，随着在线赌场行业的蓬勃发展，RR6958作为提供世界级游戏体验的首要目的地脱颖而出。凭借超过10年的专业经验，RR6958为全球数百万玩家提供公平、透明、高度娱乐的平台，拥有数千种多样化游戏和全天候顶级客户服务。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"http://gioithieu.rr6958.com/?referralCode=vbx2083\"\u003e立即注册领取100%%奖金\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"为什么选择-rr6958\"\u003e为什么选择 RR6958？\u003c/h2\u003e\n\u003ch3 id=\"1-数百种激动人心的赌场游戏\"\u003e1. \u003cstrong\u003e数百种激动人心的赌场游戏\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003eRR6958提供无与伦比的游戏体验，包括：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e与迷人荷官的现场百家乐\u003c/li\u003e\n\u003cli\u003e适合各种偏好的轮盘赌变体\u003c/li\u003e\n\u003cli\u003e规则简单、高胜率的二十一点\u003c/li\u003e\n\u003cli\u003e巨额奖金的老虎机\u003c/li\u003e\n\u003cli\u003e德州扑克、骰宝、龙虎斗等\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"2-慷慨的奖金和促销\"\u003e2. \u003cstrong\u003e慷慨的奖金和促销\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e首次存款奖金\u003c/strong\u003e：初始存款最高200%%\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e每周返现\u003c/strong\u003e：根据您的等级5-15%%\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e忠诚度计划\u003c/strong\u003e：可转换为真钱的积分\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e特殊活动\u003c/strong\u003e：节日期间双倍奖金\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"3-绝对安全和公平\"\u003e3. \u003cstrong\u003e绝对安全和公平\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003eSSL 256位加密保护所有数据\u003c/li\u003e\n\u003cli\u003e由国际组织认证的RNG\u003c/li\u003e\n\u003cli\u003e超快支付，1-5分钟内完成\u003c/li\u003e\n\u003cli\u003e来自专业团队的全天候支持\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"4-用户友好的界面\"\u003e4. \u003cstrong\u003e用户友好的界面\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e移动端响应式设计\u003c/li\u003e\n\u003cli\u003e包括英语在内的多语言支持\u003c/li\u003e\n\u003cli\u003e实时聊天支持\u003c/li\u003e\n\u003cli\u003eiOS/Android便捷移动应用\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"如何注册-rr6958\"\u003e如何注册 RR6958\u003c/h2\u003e\n\u003cp\u003e开始使用非常简单 - 只需要3个步骤：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e访问注册页面\u003c/strong\u003e：\u003ca href=\"http://gioithieu.rr6958.com/?referralCode=vbx2083\"\u003e在RR6958注册\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e输入您的详细信息\u003c/strong\u003e：姓名、邮箱、电话号码\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e验证并存款\u003c/strong\u003e：立即获得奖金\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"rr6958-的多种支付方式\"\u003eRR6958 的多种支付方式\u003c/h2\u003e\n\u003cp\u003eRR6958支持越南所有热门支付选项：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e越南银行转账（BIDV、Vietcombank、Techcombank）\u003c/li\u003e\n\u003cli\u003e电子钱包（Momo、ZaloPay、ViettelPay）\u003c/li\u003e\n\u003cli\u003e电话卡（Vinaphone、Mobifone、Viettel）\u003c/li\u003e\n\u003cli\u003e加密货币（比特币、以太坊、USDT）\u003c/li\u003e\n\u003cli\u003e当地代理进行面对面支付\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"在-rr6958-玩游戏的独家福利\"\u003e在 RR6958 玩游戏的独家福利\u003c/h2\u003e\n\u003ch3 id=\"优质游戏体验\"\u003e\u003cstrong\u003e优质游戏体验\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e越南服务器，实现闪电般的速度\u003c/li\u003e\n\u003cli\u003e4K图形和沉浸式声音\u003c/li\u003e\n\u003cli\u003e来自菲律宾的真实荷官\u003c/li\u003e\n\u003cli\u003e无中断的流畅游戏\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"持续的促销计划\"\u003e\u003cstrong\u003e持续的促销计划\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e所有会员的每日奖金\u003c/li\u003e\n\u003cli\u003e周末三倍奖金\u003c/li\u003e\n\u003cli\u003e活跃玩家的月度奖励\u003c/li\u003e\n\u003cli\u003e独家活动和锦标赛\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"五星级客户服务\"\u003e\u003cstrong\u003e五星级客户服务\u003c/strong\u003e\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e热线：1900 XXX XXX（免费通话）\u003c/li\u003e\n\u003cli\u003e邮箱：support@rr6958.com\u003c/li\u003e\n\u003cli\u003e全天候实时聊天\u003c/li\u003e\n\u003cli\u003e说越南语的支持人员\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"rr6958---越南玩家首选\"\u003eRR6958 - 越南玩家首选\u003c/h2\u003e\n\u003cp\u003e经过10多年的发展，RR6958自豪地为全球数百万玩家服务，并拥有：\u003c/p\u003e","title":"介绍 RR6958 - 终极在线赌场体验"},{"content":"Python 爬虫工具大致经历了四个时代。先是 urllib 加正则。然后 是 requests 加 BeautifulSoup。再后来,严肃一点的活就上 Scrapy。最后,半个互联网都变成 JavaScript-only 之后,前面 三件套被一脚踹下悬崖,Playwright 顶上来了。\nScrapling 就是想 在这个栈上再加一层的新库之一 —— 一个工具包同时覆盖简单页面、 重度 JS 页面、以及反爬保护页面这三种场景,让你不用再把三个不 同的库缝在一起。\n我把项目、benchmark 和 API 都翻了一遍。下面是我觉得它真正有 价值的地方、需要小心的地方,以及在什么情况下我会选它而不是更 显而易见的替代品。\n一句话讲清楚它是什么 Scrapling 是一个 Python 3.10+ 的爬虫框架,它把三种不同的请求 后端 —— 带 TLS 指纹模拟的纯 HTTP、带反检测的隐身浏览器、完整 的 Playwright 浏览器 —— 包在同一套 selector API 后面。 BSD-3-Clause 协议。\n仓库的标语是 \u0026ldquo;Effortless Web Scraping for the Modern Web\u0026rdquo; (为现代 Web 而生的轻松爬虫),这种话每个爬虫库都会说。更有 用的描述方式是:它想做 Scrapy 的 Spider 模型 + curl_cffi 的 TLS 指纹模拟 + 一个反检测 Playwright,合并成一个 import。\n三层 fetcher 的设计 这是我觉得这个项目里真正下了功夫的部分。大多数爬虫项目最 后都会变成一坨缝合怪:requests 处理快页面,Selenium 或 Playwright 处理 JS 重的页面,再加上一些自己写的 CDN 绕过逻辑。 Scrapling 把这些拆成三档,而且响应对象长得一样:\nFetcher 后端 什么时候用 Fetcher 纯 HTTP,带 TLS 指纹模拟 静态 HTML;不需要真浏览器;你想要它够快 StealthyFetcher 带反检测补丁的 headless 浏览器 Cloudflare / JS 保护页面,必须用真浏览器 DynamicFetcher Playwright/Chromium 完整自动化 SPA、复杂登录流程、JS 渲染数据 在同一个 Spider 类里你可以给不同请求打不同的档位标记。README 里的例子:\nasync def parse(self, response: Response): for link in response.css(\u0026#39;a::attr(href)\u0026#39;).getall(): if \u0026#34;protected\u0026#34; in link: yield Request(link, sid=\u0026#34;stealth\u0026#34;) else: yield Request(link, sid=\u0026#34;fast\u0026#34;, callback=self.parse) 为什么这事重要:在真实的爬取任务里,只有一小部分页面真正需要 重型后端,但你最后往往会因为重写太麻烦,所有页面都付浏览器开 销的代价。让一个 Spider 能混合不同档位,就把平均成本压下来了。\nBenchmark,带保留意见 README 里贴的是解析 5,000 个嵌套元素的数据:\n库 时间 相对值 Scrapling 2.02 ms 1.0× Parsel / Scrapy 2.04 ms 1.01× Raw lxml 2.54 ms 1.26× BeautifulSoup4 + lxml 1584.31 ms ~784× 两点诚实的解读:\n是的,BeautifulSoup 真的就是这么慢。 那个数字不是打错了。 BS4 是个易用性优先的库;在大量文档的紧密循环里,基于 lxml 的 解析器(Scrapling、Parsel、raw lxml 都是)能快上几个数量级。 这是一个已知结果,不是 Scrapling 独有的发现。\n不,Scrapling 在解析层并没有真的比 Scrapy 快。 看表 —— Scrapy 用的 Parsel 是 2.04 ms,Scrapling 是 2.02 ms。在微基 准的误差范围内。\u0026ldquo;比 BS4 快几百倍\u0026quot;是真的;\u0026ldquo;比 Scrapy 快\u0026quot;不是, 至少在解析这一层不是。\nScrapling 在真实负载里大概率胜出的地方,是网络层 —— Fetcher 的 TLS 指纹模拟让你不用为了绕过基础的 JA3 指纹检测 就去引入 Playwright。这种节省是按\u0026quot;秒/请求\u0026quot;算的,不是按 \u0026ldquo;微秒\u0026quot;算的。\n自适应选择 —— 真正有意思的想法 爬虫的痛点不是写第一条 selector,而是三周以后目标网站重命 名了一个 class,你的 selector 就废了。Scrapling 在这块的承 诺是 \u0026ldquo;smart element relocation after website changes using similarity algorithms\u0026rdquo;(用相似度算法在网站结构变更后智能重定 位元素)—— 也就是说当原 selector 失效时,库会通过和之前抓过 的结构做相似度比较,试图重新找到那个元素。\n我对这个谨慎乐观。这是该有的功能。实际表现上,基于相似度 的重定位在外观性改动(class 重命名、加一层 wrapper div) 上会工作得很好,在语义性改动(数据搬到了另一个页面、整个 区块被重新模板化)上则会失败。把它当成\u0026quot;省你三点钟为了一个 class 名爬起来改代码\u0026quot;的功能,不要当成\u0026quot;你的爬虫从此自我维护\u0026rdquo; 的功能。\n最简单能跑的写法 直接照搬文档,绝对最小是这样:\nfrom scrapling.fetchers import Fetcher, FetcherSession with FetcherSession(impersonate=\u0026#39;chrome\u0026#39;) as session: page = session.get(\u0026#39;https://quotes.toscrape.com/\u0026#39;, stealthy_headers=True) quotes = page.css(\u0026#39;.quote .text::text\u0026#39;).getall() 就这样,三行代码搞定\u0026quot;用 Chrome 的 TLS 指纹去抓页面并解析\u0026rdquo;。 impersonate='chrome' 是 curl_cffi 风格的指纹欺骗 —— 在目标 站点用基础指纹检测时很有用。\n针对 Cloudflare 保护的页面:\nfrom scrapling.fetchers import StealthyFetcher page = StealthyFetcher.fetch(\u0026#39;https://nopecha.com/demo/cloudflare\u0026#39;) data = page.css(\u0026#39;#padded_content a\u0026#39;).getall() 注意 StealthyFetcher 要单独装浏览器:\npip install \u0026#34;scrapling[fetchers]\u0026#34; scrapling install scrapling install 这一步会下打过补丁的 Chromium。在干净的服务 器上是几百 MB 的依赖,在小内存 VPS 上 pip install 之前要心里 有数。\n跟显而易见的替代品比 需求 选什么 一次性脚本、简单 HTML、入门学习 requests + BeautifulSoup 大规模抓取、流水线明确、生态成熟 Scrapy 重度 JS 应用、复杂登录、自己控制浏览器 直接用 Playwright TLS 指纹模拟、不要浏览器开销 curl_cffi Cloudflare/Turnstile 保护的静态页 cloudscraper 或 Scrapling.StealthyFetcher 以上所有都想要,合并成一个库 Scrapling 老实说一句:如果你的项目形态明确(\u0026ldquo;我要在一个已知站点上爬 1000 万个商品页\u0026rdquo;),那就用专门为这个形态设计的工具 —— 大型 受规约的爬取,Scrapy 还是对的;严肃的浏览器自动化,Playwright 还是对的。Scrapling 的价值在那些形态不明确的项目上,在那 里你本来要被迫维护三个不同的爬虫。\n哪些地方要小心 反爬绕过是个移动靶。 README 自己也明说了:企业级系统 (Akamai、DataDome、Kasada、Incapsula)需要第三方方案,文档 里也没承诺能搞定。即便是 Scrapling 确实针对的系统 —— 比如 Cloudflare Turnstile —— 你也要预期\u0026quot;今天能用的下个季度未必能 用\u0026rdquo;。如果你的业务靠这个吃饭,要做好持续维护的预算,而不 是\u0026quot;装上就忘\u0026quot;。\nrobots_txt_obey 是 opt-in,不是默认开。 这是有意为之的设 计选择(有些用户有合理理由绕过 robots,比如他在爬自己的站), 但意思就是你必须自觉打开。在第三方站点上忘了打开,你会先 在法庭上后悔,再在技术上后悔。\n隐蔽 ≠ 授权。 库本身有一份免责声明,值得引用一下:\n\u0026ldquo;本库仅供教育与研究用途。使用本库即表示您同意遵守当地及国 际数据抓取与隐私法律。\u0026rdquo;\n反检测能力对合法场景有用 —— 学术研究、网站存档、无障碍辅 助抓取、监测你有授权的站点、把你自己的数据从不愿提供导出的服 务里取出来。但同样这些能力也正是广告欺诈、内容盗窃、违反 ToS 用的。库本身不在乎你在干哪种;法院和监管机构在乎。把 \u0026ldquo;stealthy\u0026quot;系列功能当成电动工具来对待 —— 有用,但你得知 道什么时候不用它。\n我会在什么情况下真的用它 三种我觉得 Scrapling 合适的具体场景:\n个人数据导出。 某个服务持有你自己的数据但不给真正的 导出 API。你要慢慢地、尊重对方限速地、用真浏览器爬你自己 的账号 —— Scrapling 的 DynamicSession 干这个挺合适。\n小型商业爬取,但需要面对两三种不同保护级别。 你不想为 一个一周的项目去搭一套 Scrapy + Playwright + curl_cffi 流 水线。Scrapling 让你更快出原型。\n研究和存档。 抓公共记录、数据集、政府门户、新闻档案。 这种活的特点就是大批量用快 HTTP,少数几个奇葩页面用真浏览 器。\n我不会首选它的情况:目标是单一、深度了解、要爬好几年的站 点(Scrapy 的纪律性在这种场景下回报更高);或者站点所有者明 确说请你别爬(没有任何库能解决这个问题)。\n结论 Scrapling 是一个真实存在、设计认真的库 —— 不是噱头,不是炒作。 对 BeautifulSoup 的 benchmark 是真的,对 Scrapy 的 benchmark 诚实地讲是在噪声范围内;真正的优势是三档 fetcher 的统一 接口,以及网络层指纹处理让你能避免为不需要浏览器的场景启动浏 览器。\n它没有解决爬虫的真正难题 —— 那些都在法律和伦理层面,没有 任何库能帮你 —— 但它干净利落地解决了一个真实的工程问题。如果 你今天要起一个新爬虫项目,而且还不确定要不要浏览器,这是个合 理的起点。\n完整源码和文档在 github.com/D4Vinci/Scrapling。\n","permalink":"https://dibi8.com/zh/posts/scrapling-review/","summary":"Scrapling 把自己定位成 Scrapy 和 BeautifulSoup 的下一代替代品,主打更快、更隐蔽。读完文档和 benchmark 后,这里是我对它实际能做什么、能用在哪、不能用在哪的一份诚实评测。","title":"Scrapling 实测:更快、更隐蔽的 Python 爬虫框架"},{"content":"大多数教程展示了平凡的 with open(...) 示例，然后就停止了。这里是我在实际代码中实际使用的三个模式，以及每个模式防止的故障模式。\n案例1：资源清理与错误处理 当您有必须清理的资源时，无论发生什么：\nclass DatabaseConnection: def __init__(self, config): self.config = config self.connection = None def __enter__(self): self.connection = create_connection(self.config) return self.connection def __exit__(self, exc_type, exc_val, exc_tb): if self.connection: self.connection.close() 为什么这很重要 即使在异常情况下也会清理资源 防止资源泄漏 使代码更可靠 案例2：临时状态管理 当您需要临时更改某些状态，然后恢复它：\nclass TempDir: def __init__(self, base_path=\u0026#34;/tmp\u0026#34;): self.base_path = base_path self.temp_path = None def __enter__(self): import tempfile self.temp_path = tempfile.mkdtemp(dir=self.base_path) return self.temp_path def __exit__(self, exc_type, exc_val, exc_tb): import shutil if self.temp_path and os.path.exists(self.temp_path): shutil.rmtree(self.temp_path) 为什么这很重要 自动清理临时文件 防止磁盘空间浪费 简化错误处理 案例3：性能监控和计时 当您需要测量代码执行时间：\nimport time class Timer: def __enter__(self): self.start = time.time() return self def __exit__(self, exc_type, exc_val, exc_tb): self.end = time.time() print(f\u0026#34;执行时间: {self.end - self.start:.2f} 秒\u0026#34;) 为什么这很重要 自动计时代码块 即使在异常情况下也会报告时间 便于性能监控 关键要点 上下文管理器不仅仅用于文件 - 它们适用于任何需要设置/清理的资源 异常安全 - __exit__ 方法总是被调用，即使在异常情况下 可组合 - 您可以嵌套多个上下文管理器 可重用 - 创建一次，使用多次 实际应用 这些模式在实际代码中非常有用，特别是当您处理数据库连接、临时文件、网络连接或需要精确计时的操作时。\n记住：上下文管理器是Python中资源管理的瑞士军刀。学习使用它们将使您的代码更健壮和更易维护。\n","permalink":"https://dibi8.com/zh/posts/python%E4%B8%8A%E4%B8%8B%E6%96%87%E7%AE%A1%E7%90%86%E5%99%A8%E6%82%A8%E5%AE%9E%E9%99%85%E9%9C%80%E8%A6%81%E7%9A%84%E4%B8%89%E4%B8%AA%E6%A1%88%E4%BE%8B/","summary":"\u003cp\u003e大多数教程展示了平凡的 \u003ccode\u003ewith open(...)\u003c/code\u003e 示例，然后就停止了。这里是我在实际代码中实际使用的三个模式，以及每个模式防止的故障模式。\u003c/p\u003e\n\u003ch2 id=\"案例1资源清理与错误处理\"\u003e案例1：资源清理与错误处理\u003c/h2\u003e\n\u003cp\u003e当您有必须清理的资源时，无论发生什么：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eDatabaseConnection\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__init__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003econfig\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnection\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__enter__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnection\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ecreate_connection\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econfig\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnection\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__exit__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_type\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_val\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_tb\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnection\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003econnection\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eclose\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"为什么这很重要\"\u003e为什么这很重要\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e即使在异常情况下也会清理资源\u003c/li\u003e\n\u003cli\u003e防止资源泄漏\u003c/li\u003e\n\u003cli\u003e使代码更可靠\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"案例2临时状态管理\"\u003e案例2：临时状态管理\u003c/h2\u003e\n\u003cp\u003e当您需要临时更改某些状态，然后恢复它：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eTempDir\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__init__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ebase_path\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/tmp\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebase_path\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003ebase_path\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etemp_path\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__enter__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003etempfile\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etemp_path\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etempfile\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003emkdtemp\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"nb\"\u003edir\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ebase_path\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etemp_path\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__exit__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_type\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_val\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_tb\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003eshutil\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etemp_path\u003c/span\u003e \u003cspan class=\"ow\"\u003eand\u003c/span\u003e \u003cspan class=\"n\"\u003eos\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003epath\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eexists\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etemp_path\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003eshutil\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ermtree\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etemp_path\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"为什么这很重要-1\"\u003e为什么这很重要\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e自动清理临时文件\u003c/li\u003e\n\u003cli\u003e防止磁盘空间浪费\u003c/li\u003e\n\u003cli\u003e简化错误处理\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"案例3性能监控和计时\"\u003e案例3：性能监控和计时\u003c/h2\u003e\n\u003cp\u003e当您需要测量代码执行时间：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003etime\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eTimer\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__enter__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estart\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etime\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__exit__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_type\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_val\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eexc_tb\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eend\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etime\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etime\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;执行时间: \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eend\u003c/span\u003e \u003cspan class=\"o\"\u003e-\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003estart\u003c/span\u003e\u003cspan class=\"si\"\u003e:\u003c/span\u003e\u003cspan class=\"s2\"\u003e.2f\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e 秒\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003ch3 id=\"为什么这很重要-2\"\u003e为什么这很重要\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e自动计时代码块\u003c/li\u003e\n\u003cli\u003e即使在异常情况下也会报告时间\u003c/li\u003e\n\u003cli\u003e便于性能监控\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"关键要点\"\u003e关键要点\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e上下文管理器不仅仅用于文件\u003c/strong\u003e - 它们适用于任何需要设置/清理的资源\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e异常安全\u003c/strong\u003e - \u003ccode\u003e__exit__\u003c/code\u003e 方法总是被调用，即使在异常情况下\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e可组合\u003c/strong\u003e - 您可以嵌套多个上下文管理器\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e可重用\u003c/strong\u003e - 创建一次，使用多次\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"实际应用\"\u003e实际应用\u003c/h2\u003e\n\u003cp\u003e这些模式在实际代码中非常有用，特别是当您处理数据库连接、临时文件、网络连接或需要精确计时的操作时。\u003c/p\u003e","title":"Python上下文管理器：您实际需要的三个案例"},{"content":"EXPLAIN ANALYZE 输出看起来令人生畏，直到您知道实际重要的三个数字。以下是我阅读它们的顺序，以及指向特定错误的模式。\n实际重要的三个数字 1. 总执行时间 (Execution Time) Total runtime: 1234.567 ms 这是最重要的指标。如果查询慢，这里会告诉您。\n2. 实际行数 vs 估计行数 (Actual vs Estimated Rows) Seq Scan on users (cost=0.00..123.45 rows=1000 width=32) (actual time=1.234..567.890 rows=50000 loops=1) 巨大差异表明规划器做出了错误假设。\n3. 缓冲区命中率 (Buffer Hit Ratio) Buffers: shared hit=1000 read=50 高命中率 = 好缓存使用，低命中率 = 磁盘 I/O 问题。\n阅读顺序 从底部开始 - 总执行时间 向上工作 - 查找成本最高的节点 检查行数估计 - 实际 vs 估计 查看缓冲区统计 - I/O 模式 常见问题模式 顺序扫描当应该索引扫描时 Seq Scan on large_table (cost=1000.00..2000.00 rows=100000 width=32) 解决方案: 添加适当的索引\n嵌套循环当应该哈希连接时 Nested Loop (cost=1000.00..100000.00 rows=1000 width=64) -\u0026gt; Seq Scan on users -\u0026gt; Index Scan on orders 解决方案: 增加 work_mem 或重写查询\n缓冲区未命中太多 Buffers: shared hit=10 read=1000 解决方案: 增加 shared_buffers 或改善查询\n排序溢出到磁盘 Sort Method: external merge Disk: 16384kB 解决方案: 增加 work_mem\n实际应用 这些洞察帮助我识别并修复了：\n缺失索引 低效的连接顺序 内存不足的问题 缓存未命中 记住：EXPLAIN ANALYZE 是您的查询性能调试器。学习阅读它将节省您无数小时的猜测。\n","permalink":"https://dibi8.com/zh/posts/%E9%98%85%E8%AF%BB-postgresql-%E4%B8%AD%E7%9A%84-explain-analyze-%E8%BE%93%E5%87%BA%E8%80%8C%E4%B8%8D%E8%BF%B7%E5%A4%B1/","summary":"\u003cp\u003eEXPLAIN ANALYZE 输出看起来令人生畏，直到您知道实际重要的三个数字。以下是我阅读它们的顺序，以及指向特定错误的模式。\u003c/p\u003e\n\u003ch2 id=\"实际重要的三个数字\"\u003e实际重要的三个数字\u003c/h2\u003e\n\u003ch3 id=\"1-总执行时间-execution-time\"\u003e1. \u003cstrong\u003e总执行时间 (Execution Time)\u003c/strong\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eTotal runtime: 1234.567 ms\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e这是最重要的指标。如果查询慢，这里会告诉您。\u003c/p\u003e\n\u003ch3 id=\"2-实际行数-vs-估计行数-actual-vs-estimated-rows\"\u003e2. \u003cstrong\u003e实际行数 vs 估计行数 (Actual vs Estimated Rows)\u003c/strong\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSeq Scan on users  (cost=0.00..123.45 rows=1000 width=32) (actual time=1.234..567.890 rows=50000 loops=1)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e巨大差异表明规划器做出了错误假设。\u003c/p\u003e\n\u003ch3 id=\"3-缓冲区命中率-buffer-hit-ratio\"\u003e3. \u003cstrong\u003e缓冲区命中率 (Buffer Hit Ratio)\u003c/strong\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eBuffers: shared hit=1000 read=50\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e高命中率 = 好缓存使用，低命中率 = 磁盘 I/O 问题。\u003c/p\u003e\n\u003ch2 id=\"阅读顺序\"\u003e阅读顺序\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e从底部开始\u003c/strong\u003e - 总执行时间\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e向上工作\u003c/strong\u003e - 查找成本最高的节点\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e检查行数估计\u003c/strong\u003e - 实际 vs 估计\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e查看缓冲区统计\u003c/strong\u003e - I/O 模式\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"常见问题模式\"\u003e常见问题模式\u003c/h2\u003e\n\u003ch3 id=\"顺序扫描当应该索引扫描时\"\u003e\u003cstrong\u003e顺序扫描当应该索引扫描时\u003c/strong\u003e\u003c/h3\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eSeq Scan on large_table (cost=1000.00..2000.00 rows=100000 width=32)\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003e\u003cstrong\u003e解决方案\u003c/strong\u003e: 添加适当的索引\u003c/p\u003e","title":"阅读 PostgreSQL 中的 EXPLAIN ANALYZE 输出而不迷失"},{"content":"这是一个个人技术笔记站,内容是后端工程、系统底层,以及那些 我自己经常忘了又不得不去查的现代 Web 开发零碎。\n只有当我手上有点真东西可以写时才会更新 —— 比如某次让我学到 点东西的调试经历、跑出意外结果的 benchmark,或者一个从零搭起 来、想留个底好下次自己看的功能。\n如果你看到任何错误,欢迎指出来。我宁可改对,也不愿留着错的 内容在网上。\n","permalink":"https://dibi8.com/zh/about/","summary":"关于这个站点","title":"关于"}]