{"id":14,"date":"2026-02-25T10:52:51","date_gmt":"2026-02-25T02:52:51","guid":{"rendered":"http:\/\/word.tickermd.com\/?p=14"},"modified":"2026-02-25T10:52:51","modified_gmt":"2026-02-25T02:52:51","slug":"%e6%9c%9f%e8%b4%a7api%e6%95%b0%e6%8d%ae%e4%b8%8e%e8%84%89%e5%8a%a8%e6%95%b0%e6%8d%ae%e8%a1%8c%e6%83%85%e5%af%b9%e6%8e%a5%e6%8c%87%e5%8d%97","status":"publish","type":"post","link":"https:\/\/word.tickermd.com\/index.php\/2026\/02\/25\/%e6%9c%9f%e8%b4%a7api%e6%95%b0%e6%8d%ae%e4%b8%8e%e8%84%89%e5%8a%a8%e6%95%b0%e6%8d%ae%e8%a1%8c%e6%83%85%e5%af%b9%e6%8e%a5%e6%8c%87%e5%8d%97\/","title":{"rendered":"\u671f\u8d27API\u6570\u636e\u4e0e\u8109\u52a8\u6570\u636e\u884c\u60c5\u5bf9\u63a5\u6307\u5357"},"content":{"rendered":"\n<p>\u200b<\/p>\n\n\n\n<p>\u5728\u91cf\u5316\u4ea4\u6613\u548c\u91d1\u878d\u5e94\u7528\u5f00\u53d1\u9886\u57df\uff0c\u7a33\u5b9a\u3001\u5b9e\u65f6\u7684\u884c\u60c5\u6570\u636e\u662f\u7cfb\u7edf\u8fd0\u884c\u7684\u57fa\u77f3\u3002\u8109\u52a8\u6570\u636e\u4f5c\u4e3a\u4e13\u4e1a\u7684\u884c\u60c5\u670d\u52a1\u63d0\u4f9b\u5546\uff0c\u4e3a\u5f00\u53d1\u8005\u63d0\u4f9b\u4e86\u5b8c\u6574\u7684WebSocket\u548cHTTP\u63a5\u53e3\u89e3\u51b3\u65b9\u6848\u3002\u672c\u6587\u5c06\u8be6\u7ec6\u4ecb\u7ecd\u8109\u52a8\u6570\u636eAPI\u7684\u7279\u6027\uff0c\u5e76\u901a\u8fc7\u5b9e\u6218\u4ee3\u7801\u6f14\u793a\u5982\u4f55\u5feb\u901f\u5bf9\u63a5\u5168\u7403\u671f\u8d27\u5e02\u573a\u6570\u636e\u3002<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e00\u3001\u8109\u52a8\u6570\u636e\u5e73\u53f0\u6982\u8ff0<\/h2>\n\n\n\n<p>\u8109\u52a8\u6570\u636e\u4e13\u6ce8\u4e8e\u63d0\u4f9b\u5168\u7403\u91d1\u878d\u5e02\u573a\u7684\u5b9e\u65f6\u884c\u60c5\u6570\u636e\u670d\u52a1\uff0c\u6db5\u76d6\u5916\u6c47\u3001\u56fd\u9645\u671f\u8d27\u3001\u56fd\u5185\u671f\u8d27\u3001\u6570\u5b57\u8d27\u5e01\u3001\u56fd\u9645\u80a1\u6307\u671f\u8d27\u7b49\u591a\u4e2a\u54c1\u79cd\u3002\u5176API\u63a5\u53e3\u5177\u6709\u4ee5\u4e0b\u6838\u5fc3\u7279\u6027\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u6570\u636e\u65f6\u6548\u6027<\/strong>\uff1aWebSocket\u5b9e\u65f6\u63a8\u9001\uff0c\u884c\u60c5\u66f4\u65b0\u5373\u63a8\u9001<\/li>\n\n\n\n<li><strong>\u63a5\u5165\u65b9\u5f0f<\/strong>\uff1a\u540c\u65f6\u652f\u6301WebSocket\u5b9e\u65f6\u63a8\u9001\u548cHTTP REST\u63a5\u53e3<\/li>\n\n\n\n<li><strong>\u6570\u636e\u683c\u5f0f<\/strong>\uff1a\u7edf\u4e00\u7684JSON\u683c\u5f0f\uff0c\u4fbf\u4e8e\u89e3\u6790\u548c\u5904\u7406<\/li>\n\n\n\n<li><strong>\u6388\u6743\u673a\u5236<\/strong>\uff1a\u670d\u52a1\u5668IP\u767d\u540d\u5355\u6388\u6743\uff0c\u786e\u4fdd\u6570\u636e\u5b89\u5168<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1.1 \u63a5\u5165\u524d\u51c6\u5907<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5b98\u7f51\uff1a<a href=\"http:\/\/39.107.99.235:1008\/market\">http:\/\/39.107.99.235:1008\/market<\/a><\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e8c\u3001API\u63a5\u53e3\u4f53\u7cfb<\/h2>\n\n\n\n<p>\u8109\u52a8\u6570\u636e\u63d0\u4f9b\u4e94\u5927\u7c7b\u63a5\u53e3\uff0c\u6ee1\u8db3\u4e0d\u540c\u7684\u6570\u636e\u9700\u6c42\uff1a<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>\u63a5\u53e3\u7c7b\u578b<\/th><th>\u529f\u80fd\u63cf\u8ff0<\/th><th>\u9002\u7528\u573a\u666f<\/th><\/tr><\/thead><tbody><tr><td>WebSocket\u5b9e\u65f6\u63a8\u9001<\/td><td>\u5b9e\u65f6\u5206\u7b14\u660e\u7ec6\u6570\u636e\u63a8\u9001<\/td><td>\u9ad8\u9891\u4ea4\u6613\u3001\u5b9e\u65f6\u76d1\u63a7<\/td><\/tr><tr><td>\u83b7\u53d6\u5b9e\u65f6\u6570\u636e\u63a5\u53e3<\/td><td>HTTP\u65b9\u5f0f\u83b7\u53d6\u5b9e\u65f6\u884c\u60c5<\/td><td>\u4e2d\u4f4e\u9891\u67e5\u8be2\u3001\u5e94\u7528\u96c6\u6210<\/td><\/tr><tr><td>\u83b7\u53d6K\u7ebf\u56fe\u63a5\u53e3<\/td><td>\u5386\u53f2K\u7ebf\u6570\u636e\u67e5\u8be2<\/td><td>\u6280\u672f\u5206\u6790\u3001\u56de\u6d4b<\/td><\/tr><tr><td>\u67e5\u8be2\u4ea7\u54c1\u5206\u7c7b\u63a5\u53e3<\/td><td>\u83b7\u53d6\u4ea7\u54c1\u5206\u7c7bID<\/td><td>\u6570\u636e\u6d4f\u89c8\u3001\u5206\u7c7b\u67e5\u8be2<\/td><\/tr><tr><td>\u67e5\u8be2\u4ea7\u54c1\u8ba2\u9605\u4ee3\u7801<\/td><td>\u83b7\u53d6\u4ea7\u54c1\u4ee3\u7801\u5217\u8868<\/td><td>\u8ba2\u9605\u51c6\u5907\u3001\u4ee3\u7801\u6620\u5c04<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e09\u3001WebSocket\u5b9e\u65f6\u6570\u636e\u63a8\u9001\u5b9e\u6218<\/h2>\n\n\n\n<p>WebSocket\u63a5\u53e3\u9002\u5408\u9700\u8981\u5b9e\u65f6\u76d1\u63a7\u884c\u60c5\u7684\u9ad8\u9891\u4ea4\u6613\u573a\u666f<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3.1 WebSocket\u8fde\u63a5\u4e0e\u5fc3\u8df3\u7ef4\u6301<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import asyncio\nimport websockets\nimport json\nimport time\n\nclass PulseDataWebSocket:\n    def __init__(self, uri=\"ws:\/\/39.107.99.235\/ws\"):\n        self.uri = uri\n        self.websocket = None\n        self.subscribed_symbols = &#91;]\n        self.running = False\n    \n    async def connect(self):\n        \"\"\"\u5efa\u7acbWebSocket\u8fde\u63a5\"\"\"\n        try:\n            self.websocket = await websockets.connect(self.uri)\n            self.running = True\n            print(f\"WebSocket\u8fde\u63a5\u6210\u529f: {self.uri}\")\n            \n            # \u542f\u52a8\u5fc3\u8df3\u4efb\u52a1\n            asyncio.create_task(self.heartbeat())\n            \n            # \u5982\u679c\u6709\u4e4b\u524d\u8ba2\u9605\u7684\u4ee3\u7801\uff0c\u91cd\u65b0\u8ba2\u9605\n            if self.subscribed_symbols:\n                await self.subscribe(self.subscribed_symbols)\n                \n            # \u5f00\u59cb\u63a5\u6536\u6d88\u606f\n            await self.receive_messages()\n            \n        except Exception as e:\n            print(f\"\u8fde\u63a5\u5931\u8d25: {e}\")\n            await self.reconnect()\n    \n    async def heartbeat(self):\n        \"\"\"\u6bcf10\u79d2\u53d1\u9001\u5fc3\u8df3\"\"\"\n        while self.running:\n            if self.websocket:\n                try:\n                    ping_msg = {\"ping\": int(time.time())}\n                    await self.websocket.send(json.dumps(ping_msg))\n                    print(f\"\u53d1\u9001\u5fc3\u8df3: {ping_msg}\")\n                    await asyncio.sleep(10)\n                except Exception as e:\n                    print(f\"\u5fc3\u8df3\u53d1\u9001\u5931\u8d25: {e}\")\n                    break\n    \n    async def subscribe(self, symbols):\n        \"\"\"\u8ba2\u9605\u4ea7\u54c1\u4ee3\u7801\"\"\"\n        self.subscribed_symbols = symbols\n        if self.websocket:\n            subscribe_msg = {\"Key\": \",\".join(symbols)}\n            await self.websocket.send(json.dumps(subscribe_msg))\n            print(f\"\u8ba2\u9605\u4ea7\u54c1: {subscribe_msg}\")\n    \n    async def receive_messages(self):\n        \"\"\"\u63a5\u6536\u670d\u52a1\u5668\u63a8\u9001\u7684\u6d88\u606f\"\"\"\n        while self.running:\n            try:\n                message = await self.websocket.recv()\n                data = json.loads(message)\n                \n                # \u5904\u7406pong\u54cd\u5e94\n                if \"pong\" in data:\n                    print(f\"\u6536\u5230\u5fc3\u8df3\u54cd\u5e94: {data}\")\n                else:\n                    # \u5904\u7406\u884c\u60c5\u6570\u636e\n                    self.process_market_data(data)\n                    \n            except websockets.exceptions.ConnectionClosed:\n                print(\"\u8fde\u63a5\u5173\u95ed\uff0c\u5c1d\u8bd5\u91cd\u8fde...\")\n                await self.reconnect()\n                break\n            except Exception as e:\n                print(f\"\u63a5\u6536\u6d88\u606f\u9519\u8bef: {e}\")\n    \n    def process_market_data(self, data):\n        \"\"\"\u5904\u7406\u884c\u60c5\u6570\u636e\"\"\"\n        if \"body\" in data:\n            body = data&#91;\"body\"]\n            print(f\"\"\"\n            \u4ea7\u54c1\u4ee3\u7801: {body.get('StockCode')}\n            \u6700\u65b0\u4ef7: {body.get('Price')}\n            \u5f00\u76d8\u4ef7: {body.get('Open')}\n            \u6700\u9ad8\u4ef7: {body.get('High')}\n            \u6700\u4f4e\u4ef7: {body.get('Low')}\n            \u65f6\u95f4: {body.get('Time')}\n            \"\"\")\n            \n            # \u5904\u7406\u6df1\u5ea6\u6570\u636e\n            if \"Depth\" in body:\n                depth = body&#91;\"Depth\"]\n                if \"Buy\" in depth and depth&#91;\"Buy\"]:\n                    print(f\"\u4e70\u4e00\u4ef7: {depth&#91;'Buy']&#91;0].get('BP1')}, \u4e70\u4e00\u91cf: {depth&#91;'Buy']&#91;0].get('BV1')}\")\n                if \"Sell\" in depth and depth&#91;\"Sell\"]:\n                    print(f\"\u5356\u4e00\u4ef7: {depth&#91;'Sell']&#91;0].get('SP1')}, \u5356\u4e00\u91cf: {depth&#91;'Sell']&#91;0].get('SV1')}\")\n    \n    async def reconnect(self):\n        \"\"\"\u65ad\u7ebf\u91cd\u8fde\u673a\u5236\"\"\"\n        self.running = False\n        if self.websocket:\n            await self.websocket.close()\n        \n        # \u6307\u6570\u9000\u907f\u91cd\u8fde\n        retry_count = 0\n        while True:\n            retry_count += 1\n            wait_time = min(30, 2 ** retry_count)\n            print(f\"{wait_time}\u79d2\u540e\u5c1d\u8bd5\u7b2c{retry_count}\u6b21\u91cd\u8fde...\")\n            await asyncio.sleep(wait_time)\n            \n            try:\n                await self.connect()\n                print(\"\u91cd\u8fde\u6210\u529f\")\n                break\n            except Exception as e:\n                print(f\"\u91cd\u8fde\u5931\u8d25: {e}\")\n    \n    async def close(self):\n        \"\"\"\u5173\u95ed\u8fde\u63a5\"\"\"\n        self.running = False\n        if self.websocket:\n            await self.websocket.close()\n\n# \u4f7f\u7528\u793a\u4f8b\nasync def main():\n    client = PulseDataWebSocket()\n    \n    # \u8fde\u63a5\u5e76\u8ba2\u9605\u4ea7\u54c1\n    await client.connect()\n    await client.subscribe(&#91;\"btcusdt\", \"ethusdt\"])\n    \n    # \u8fd0\u884c60\u79d2\u540e\u5173\u95ed\n    await asyncio.sleep(60)\n    await client.close()\n\nif __name__ == \"__main__\":\n    asyncio.run(main())<\/code><\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"15\" src=\"blob:http:\/\/word.tickermd.com\/bf80fc53-ad94-47b3-98f7-2181ec593fb2\" width=\"15\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u56db\u3001HTTP\u5b9e\u65f6\u6570\u636e\u63a5\u53e3\u5b9e\u6218<\/h2>\n\n\n\n<p>\u5bf9\u4e8e\u4e0d\u9700\u8981\u5b9e\u65f6\u63a8\u9001\u7684\u5e94\u7528\u573a\u666f\uff0c\u53ef\u4ee5\u4f7f\u7528HTTP\u63a5\u53e3\u83b7\u53d6\u5b9e\u65f6\u884c\u60c5\u3002\u9700\u8981\u6ce8\u610f\u8bf7\u6c42\u9891\u7387\u9650\u5236\uff1a\u6bcf\u4e2a\u4ea7\u54c1\u6bcf\u79d2\u6700\u5927\u652f\u6301\u8bf7\u6c423\u6b21\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4.1 \u83b7\u53d6\u5b9e\u65f6\u884c\u60c5<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import requests\nimport gzip\nimport json\nfrom io import BytesIO\nimport time\nfrom functools import lru_cache\n\nclass PulseDataHTTP:\n    def __init__(self, base_url=\"http:\/\/39.107.99.235:1008\"):\n        self.base_url = base_url\n        self.session = requests.Session()\n        # \u542f\u7528gzip\u538b\u7f29\n        self.session.headers.update({\n            'Accept-Encoding': 'gzip',\n            'User-Agent': 'Mozilla\/5.0 (compatible; PulseDataClient\/1.0)'\n        })\n    \n    def _handle_response(self, response):\n        \"\"\"\u5904\u7406gzip\u538b\u7f29\u7684\u54cd\u5e94\"\"\"\n        if response.headers.get('Content-Encoding') == 'gzip':\n            buf = BytesIO(response.content)\n            with gzip.GzipFile(fileobj=buf) as f:\n                return json.loads(f.read().decode('utf-8'))\n        return response.json()\n    \n    def get_quote(self, code):\n        \"\"\"\n        \u83b7\u53d6\u5b9e\u65f6\u884c\u60c5\n        :param code: \u4ea7\u54c1\u4ee3\u7801\uff0c\u5982 btcusdt\n        :return: \u884c\u60c5\u6570\u636e\n        \"\"\"\n        url = f\"{self.base_url}\/getQuote.php\"\n        params = {'code': code}\n        \n        try:\n            response = self.session.get(url, params=params, timeout=10)\n            response.raise_for_status()\n            \n            data = self._handle_response(response)\n            \n            if data.get('code') == 200:\n                return data.get('data', {})\n            else:\n                print(f\"API\u9519\u8bef: {data.get('msg')}\")\n                return None\n                \n        except requests.exceptions.RequestException as e:\n            print(f\"\u8bf7\u6c42\u5931\u8d25: {e}\")\n            return None\n    \n    @lru_cache(maxsize=128)\n    def get_cached_quote(self, code, cache_seconds=1):\n        \"\"\"\n        \u5e26\u7f13\u5b58\u7684\u884c\u60c5\u67e5\u8be2\uff0c\u51cf\u5c11API\u8c03\u7528\n        :param code: \u4ea7\u54c1\u4ee3\u7801\n        :param cache_seconds: \u7f13\u5b58\u65f6\u95f4\uff08\u79d2\uff09\n        \"\"\"\n        # \u901a\u8fc7lru_cache\u5b9e\u73b0\u7b80\u5355\u7f13\u5b58\uff0c\u5b9e\u9645\u5e94\u7528\u5efa\u8bae\u4f7f\u7528Redis\u7b49\n        return self.get_quote(code)\n    \n    def parse_quote_data(self, data):\n        \"\"\"\u89e3\u6790\u884c\u60c5\u6570\u636e\"\"\"\n        if not data or 'body' not in data:\n            return None\n        \n        body = data&#91;'body']\n        \n        quote_info = {\n            'code': body.get('StockCode'),\n            'price': body.get('Price'),\n            'open': body.get('Open'),\n            'high': body.get('High'),\n            'low': body.get('Low'),\n            'last_close': body.get('LastClose'),\n            'time': body.get('Time'),\n            'timestamp': body.get('LastTime'),\n            'volume': body.get('TotalVol'),\n            'diff': data.get('Diff'),\n            'diff_rate': data.get('DiffRate')\n        }\n        \n        # \u89e3\u6790\u76d8\u53e3\u6570\u636e\n        if 'Depth' in body:\n            depth = body&#91;'Depth']\n            quote_info&#91;'bid'] = depth.get('Buy', &#91;])\n            quote_info&#91;'ask'] = depth.get('Sell', &#91;])\n        \n        # \u89e3\u6790\u5b9e\u65f6\u6210\u4ea4\n        if 'BS' in body:\n            quote_info&#91;'trades'] = body.get('BS', &#91;])\n        \n        return quote_info\n\n# \u4f7f\u7528\u793a\u4f8b\ndef demo_http_api():\n    client = PulseDataHTTP()\n    \n    # \u67e5\u8be2BTCUSDT\u884c\u60c5\n    data = client.get_quote('btcusdt')\n    if data:\n        quote = client.parse_quote_data(data)\n        if quote:\n            print(f\"\"\"\n            === {quote&#91;'code']} \u5b9e\u65f6\u884c\u60c5 ===\n            \u6700\u65b0\u4ef7: {quote&#91;'price']}\n            \u5f00\u76d8\u4ef7: {quote&#91;'open']}\n            \u6700\u9ad8\u4ef7: {quote&#91;'high']}\n            \u6700\u4f4e\u4ef7: {quote&#91;'low']}\n            \u6210\u4ea4\u91cf: {quote&#91;'volume']}\n            \u6da8\u8dcc\u989d: {quote&#91;'diff']}\n            \u6da8\u8dcc\u5e45: {quote&#91;'diff_rate']}%\n            \u66f4\u65b0\u65f6\u95f4: {quote&#91;'time']}\n            \"\"\")\n            \n            # \u6253\u5370\u76d8\u53e3\n            if quote.get('bid'):\n                print(\"\\n\u4e70\u76d8:\")\n                for i, bid in enumerate(quote&#91;'bid']&#91;:3]):\n                    print(f\"\u4e70{i+1}: {bid.get(f'BP{i+1}')} @ {bid.get(f'BV{i+1}')}\")\n            \n            if quote.get('ask'):\n                print(\"\\n\u5356\u76d8:\")\n                for i, ask in enumerate(quote&#91;'ask']&#91;:3]):\n                    print(f\"\u5356{i+1}: {ask.get(f'SP{i+1}')} @ {ask.get(f'SV{i+1}')}\")\n\nif __name__ == \"__main__\":\n    demo_http_api()<\/code><\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"15\" src=\"blob:http:\/\/word.tickermd.com\/df6983b6-f24f-4f48-a6ff-3c84a060d304\" width=\"15\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e94\u3001K\u7ebf\u6570\u636e\u83b7\u53d6\u4e0e\u5904\u7406<\/h2>\n\n\n\n<p>K\u7ebf\u6570\u636e\u662f\u6280\u672f\u5206\u6790\u7684\u57fa\u7840\uff0c\u8109\u52a8\u6570\u636e\u652f\u6301\u591a\u79cd\u65f6\u95f4\u5468\u671f\u7684K\u7ebf\u67e5\u8be2\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">5.1 K\u7ebf\u6570\u636e\u63a5\u53e3\u5c01\u88c5<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>import pandas as pd\nimport matplotlib.pyplot as plt\nfrom datetime import datetime\n\nclass PulseDataKLine(PulseDataHTTP):\n    \"\"\"K\u7ebf\u6570\u636e\u67e5\u8be2\u7c7b\uff0c\u7ee7\u627f\u57fa\u7840HTTP\u5ba2\u6237\u7aef\"\"\"\n    \n    # \u65f6\u95f4\u5468\u671f\u6620\u5c04\n    TIME_FRAMES = {\n        '1m': '1\u5206\u949f',\n        '5m': '5\u5206\u949f',\n        '15m': '15\u5206\u949f',\n        '30m': '30\u5206\u949f',\n        '1h': '1\u5c0f\u65f6',\n        '1d': '\u65e5\u7ebf',\n        '1M': '\u6708\u7ebf'\n    }\n    \n    # \u6700\u5927\u6761\u6570\u9650\u5236\n    MAX_ROWS = {\n        '1m': 600,\n        '5m': 300,\n        '15m': 300,\n        '30m': 300,\n        '1h': 300,\n        '1d': 300,\n        '1M': 100\n    }\n    \n    def get_kline(self, code, timeframe='1m', rows=100):\n        \"\"\"\n        \u83b7\u53d6K\u7ebf\u6570\u636e\n        :param code: \u4ea7\u54c1\u4ee3\u7801\n        :param timeframe: \u65f6\u95f4\u5468\u671f\uff0c\u652f\u6301 1m,5m,15m,30m,1h,1d,1M\n        :param rows: \u83b7\u53d6\u6761\u6570\uff0c\u4e0d\u80fd\u8d85\u8fc7\u5bf9\u5e94\u5468\u671f\u7684\u6700\u5927\u9650\u5236\n        :return: K\u7ebf\u6570\u636e\u5217\u8868\n        \"\"\"\n        # \u53c2\u6570\u9a8c\u8bc1\n        if timeframe not in self.TIME_FRAMES:\n            raise ValueError(f\"\u4e0d\u652f\u6301\u7684\u65f6\u95f4\u5468\u671f: {timeframe}\uff0c\u652f\u6301: {list(self.TIME_FRAMES.keys())}\")\n        \n        max_rows = self.MAX_ROWS.get(timeframe, 100)\n        if rows &gt; max_rows:\n            print(f\"\u8b66\u544a: {timeframe}\u5468\u671f\u6700\u5927\u652f\u6301{max_rows}\u6761\u6570\u636e\uff0c\u5c06\u4f7f\u7528{max_rows}\")\n            rows = max_rows\n        \n        url = f\"{self.base_url}\/redis.php\"\n        params = {\n            'code': code,\n            'time': timeframe,\n            'rows': rows\n        }\n        \n        try:\n            response = self.session.get(url, params=params, timeout=10)\n            response.raise_for_status()\n            \n            data = self._handle_response(response)\n            \n            if isinstance(data, list):\n                return self._parse_kline_data(data)\n            else:\n                print(f\"API\u8fd4\u56de\u5f02\u5e38: {data}\")\n                return None\n                \n        except Exception as e:\n            print(f\"K\u7ebf\u67e5\u8be2\u5931\u8d25: {e}\")\n            return None\n    \n    def _parse_kline_data(self, raw_data):\n        \"\"\"\u89e3\u6790K\u7ebf\u539f\u59cb\u6570\u636e\"\"\"\n        klines = &#91;]\n        for item in raw_data:\n            if len(item) &gt;= 7:\n                kline = {\n                    'timestamp': item&#91;0],  # \u6beb\u79d2\u65f6\u95f4\u6233\n                    'open': float(item&#91;1]),\n                    'high': float(item&#91;2]),\n                    'low': float(item&#91;3]),\n                    'close': float(item&#91;4]),\n                    'time_str': item&#91;5],    # \u683c\u5f0f\u5316\u65f6\u95f4\u5b57\u7b26\u4e32\n                    'volume': float(item&#91;6]) if item&#91;6] else 0  # \u6210\u4ea4\u91cf\n                }\n                klines.append(kline)\n        return klines\n    \n    def kline_to_dataframe(self, klines):\n        \"\"\"\u5c06K\u7ebf\u6570\u636e\u8f6c\u6362\u4e3aPandas DataFrame\"\"\"\n        if not klines:\n            return None\n        \n        df = pd.DataFrame(klines)\n        df&#91;'datetime'] = pd.to_datetime(df&#91;'timestamp'], unit='ms')\n        df.set_index('datetime', inplace=True)\n        \n        # \u8ba1\u7b97\u5e38\u7528\u6280\u672f\u6307\u6807\n        df&#91;'ma5'] = df&#91;'close'].rolling(window=5).mean()\n        df&#91;'ma10'] = df&#91;'close'].rolling(window=10).mean()\n        df&#91;'ma20'] = df&#91;'close'].rolling(window=20).mean()\n        \n        return df\n    \n    def plot_kline(self, df, title=None):\n        \"\"\"\u7ed8\u5236K\u7ebf\u56fe\uff08\u7b80\u5316\u7248\uff0c\u4ec5\u663e\u793a\u6536\u76d8\u4ef7\uff09\"\"\"\n        if df is None or df.empty:\n            return\n        \n        plt.figure(figsize=(12, 6))\n        \n        # \u7ed8\u5236\u6536\u76d8\u4ef7\n        plt.subplot(2, 1, 1)\n        plt.plot(df.index, df&#91;'close'], label='Close', color='black')\n        plt.plot(df.index, df&#91;'ma5'], label='MA5', color='blue', alpha=0.7)\n        plt.plot(df.index, df&#91;'ma10'], label='MA10', color='orange', alpha=0.7)\n        plt.plot(df.index, df&#91;'ma20'], label='MA20', color='red', alpha=0.7)\n        plt.title(title or 'K\u7ebf\u56fe')\n        plt.legend()\n        plt.grid(True, alpha=0.3)\n        \n        # \u7ed8\u5236\u6210\u4ea4\u91cf\n        plt.subplot(2, 1, 2)\n        plt.bar(df.index, df&#91;'volume'], color='gray', alpha=0.5)\n        plt.title('\u6210\u4ea4\u91cf')\n        plt.grid(True, alpha=0.3)\n        \n        plt.tight_layout()\n        plt.show()\n\n# \u4f7f\u7528\u793a\u4f8b\ndef demo_kline():\n    client = PulseDataKLine()\n    \n    # \u83b7\u53d6BTCUSDT 15\u5206\u949fK\u7ebf\uff0c\u6700\u8fd150\u6761\n    klines = client.get_kline('btcusdt', timeframe='15m', rows=50)\n    \n    if klines:\n        # \u8f6c\u6362\u4e3aDataFrame\n        df = client.kline_to_dataframe(klines)\n        print(f\"\u83b7\u53d6\u5230 {len(df)} \u6761K\u7ebf\u6570\u636e\")\n        print(df&#91;&#91;'open', 'high', 'low', 'close', 'volume']].tail())\n        \n        # \u7ed8\u5236K\u7ebf\u56fe\n        client.plot_kline(df, title='BTCUSDT 15\u5206\u949fK\u7ebf')\n        \n        # \u7b80\u5355\u5206\u6790\n        latest = df.iloc&#91;-1]\n        print(f\"\"\"\n        \u6700\u65b0K\u7ebf:\n        \u65f6\u95f4: {latest.name}\n        \u5f00\u76d8: {latest&#91;'open']}\n        \u6700\u9ad8: {latest&#91;'high']}\n        \u6700\u4f4e: {latest&#91;'low']}\n        \u6536\u76d8: {latest&#91;'close']}\n        \u6210\u4ea4\u91cf: {latest&#91;'volume']}\n        \u6da8\u8dcc\u5e45: {(latest&#91;'close'] - df.iloc&#91;-2]&#91;'close']) \/ df.iloc&#91;-2]&#91;'close'] * 100:.2f}%\n        \"\"\")\n\nif __name__ == \"__main__\":\n    demo_kline()<\/code><\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"15\" src=\"blob:http:\/\/word.tickermd.com\/9996d90f-6cdd-49eb-bb06-0dcbb5b186df\" width=\"15\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u516d\u3001\u4ea7\u54c1\u5206\u7c7b\u4e0e\u4ee3\u7801\u67e5\u8be2<\/h2>\n\n\n\n<p>\u5728\u5b9e\u9645\u5e94\u7528\u4e2d\uff0c\u9700\u8981\u5148\u67e5\u8be2\u4ea7\u54c1\u5206\u7c7b\u548c\u8ba2\u9605\u4ee3\u7801\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6.1 \u4ea7\u54c1\u5206\u7c7b\u67e5\u8be2<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>class PulseDataSymbol(PulseDataHTTP):\n    \"\"\"\u4ea7\u54c1\u4ee3\u7801\u67e5\u8be2\u7c7b\"\"\"\n    \n    def get_categories(self):\n        \"\"\"\u83b7\u53d6\u4ea7\u54c1\u5206\u7c7b\"\"\"\n        url = f\"{self.base_url}\/getCategory.php\"\n        \n        try:\n            response = self.session.get(url, timeout=10)\n            response.raise_for_status()\n            \n            data = self._handle_response(response)\n            \n            if data.get('code') == 200:\n                return data.get('data', {}).get('list', &#91;])\n            else:\n                print(f\"\u83b7\u53d6\u5206\u7c7b\u5931\u8d25: {data.get('msg')}\")\n                return &#91;]\n                \n        except Exception as e:\n            print(f\"\u5206\u7c7b\u67e5\u8be2\u5f02\u5e38: {e}\")\n            return &#91;]\n    \n    def get_symbols(self, category_id, page=1, page_size=10):\n        \"\"\"\n        \u83b7\u53d6\u4ea7\u54c1\u8ba2\u9605\u4ee3\u7801\n        :param category_id: \u5206\u7c7bID\uff08\u4eceget_categories\u83b7\u53d6\uff09\n        :param page: \u9875\u7801\n        :param page_size: \u6bcf\u9875\u6761\u6570\n        :return: \u4ea7\u54c1\u5217\u8868\n        \"\"\"\n        url = f\"{self.base_url}\/getSymbolList.php\"\n        params = {\n            'category': category_id,\n            'page': page,\n            'pageSize': page_size\n        }\n        \n        try:\n            response = self.session.get(url, params=params, timeout=10)\n            response.raise_for_status()\n            \n            data = self._handle_response(response)\n            \n            if data.get('code') == 200:\n                return data.get('data', {})\n            else:\n                print(f\"\u83b7\u53d6\u4ea7\u54c1\u5217\u8868\u5931\u8d25: {data.get('msg')}\")\n                return {}\n                \n        except Exception as e:\n            print(f\"\u4ea7\u54c1\u67e5\u8be2\u5f02\u5e38: {e}\")\n            return {}\n    \n    def search_symbols_by_name(self, keyword):\n        \"\"\"\u6839\u636e\u540d\u79f0\u641c\u7d22\u4ea7\u54c1\uff08\u904d\u5386\u6240\u6709\u5206\u7c7b\uff09\"\"\"\n        results = &#91;]\n        \n        # \u83b7\u53d6\u6240\u6709\u5206\u7c7b\n        categories = self.get_categories()\n        \n        for category in categories:\n            cat_id = category&#91;'id']\n            cat_name = category&#91;'name']\n            \n            # \u83b7\u53d6\u7b2c\u4e00\u9875\u6570\u636e\n            data = self.get_symbols(cat_id, page=1, page_size=50)\n            \n            if data and 'list' in data:\n                for symbol in data&#91;'list']:\n                    if keyword.lower() in symbol&#91;'name'].lower() or keyword.lower() in symbol&#91;'code'].lower():\n                        symbol&#91;'category'] = cat_name\n                        results.append(symbol)\n        \n        return results\n\n# \u4f7f\u7528\u793a\u4f8b\ndef demo_symbol_query():\n    client = PulseDataSymbol()\n    \n    # 1. \u83b7\u53d6\u6240\u6709\u5206\u7c7b\n    print(\"=== \u4ea7\u54c1\u5206\u7c7b ===\")\n    categories = client.get_categories()\n    for cat in categories:\n        print(f\"ID: {cat&#91;'id']}, \u540d\u79f0: {cat&#91;'name']}\")\n    \n    # 2. \u67e5\u8be2\u6570\u5b57\u8d27\u5e01\u5206\u7c7b\u4e0b\u7684\u4ea7\u54c1\n    print(\"\\n=== \u6570\u5b57\u8d27\u5e01\u4ea7\u54c1\uff08\u7b2c1\u9875\uff09===\")\n    data = client.get_symbols('4', page=1, page_size=5)\n    if data:\n        print(f\"\u603b\u6761\u6570: {data.get('total')}\")\n        for symbol in data.get('list', &#91;]):\n            print(f\"\u4ee3\u7801: {symbol&#91;'code']}, \u540d\u79f0: {symbol&#91;'name']}\")\n    \n    # 3. \u641c\u7d22\u6bd4\u7279\u5e01\u76f8\u5173\u4ea7\u54c1\n    print(\"\\n=== \u641c\u7d22 'BTC' \u76f8\u5173\u4ea7\u54c1 ===\")\n    results = client.search_symbols_by_name('BTC')\n    for symbol in results:\n        print(f\"&#91;{symbol&#91;'category']}] {symbol&#91;'code']} - {symbol&#91;'name']}\")\n\nif __name__ == \"__main__\":\n    demo_symbol_query()<\/code><\/pre>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" height=\"15\" src=\"blob:http:\/\/word.tickermd.com\/bcccd9a8-63e7-4ec5-8f5b-9e57165bca24\" width=\"15\"><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\u4e03\u3001\u603b\u7ed3<\/h2>\n\n\n\n<p>\u8109\u52a8\u6570\u636eAPI\u4e3a\u5f00\u53d1\u8005\u63d0\u4f9b\u4e86\u5b8c\u6574\u7684\u671f\u8d27\u3001\u6570\u5b57\u8d27\u5e01\u7b49\u91d1\u878d\u4ea7\u54c1\u884c\u60c5\u6570\u636e\u63a5\u5165\u65b9\u6848\u3002\u901a\u8fc7\u672c\u6587\u7684\u5b9e\u6218\u6307\u5357\uff0c\u60a8\u53ef\u4ee5\u5feb\u901f\u638c\u63e1\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>WebSocket\u5b9e\u65f6\u6570\u636e\u63a8\u9001<\/strong>\uff1a\u9002\u7528\u4e8e\u9ad8\u9891\u4ea4\u6613\u548c\u5b9e\u65f6\u76d1\u63a7\u573a\u666f\uff0c\u9700\u5b9e\u73b0\u5fc3\u8df3\u7ef4\u6301\u548c\u65ad\u7ebf\u91cd\u8fde\u673a\u5236<\/li>\n\n\n\n<li><strong>HTTP\u63a5\u53e3\u8c03\u7528<\/strong>\uff1a\u9002\u7528\u4e8e\u4e2d\u4f4e\u9891\u67e5\u8be2\uff0c\u9700\u6ce8\u610f\u8bf7\u6c42\u9891\u7387\u9650\u5236\uff0c\u5efa\u8bae\u914d\u5408\u7f13\u5b58\u4f7f\u7528<\/li>\n\n\n\n<li><strong>K\u7ebf\u6570\u636e\u83b7\u53d6<\/strong>\uff1a\u652f\u6301\u591a\u79cd\u65f6\u95f4\u5468\u671f\uff0c\u53ef\u7528\u4e8e\u6280\u672f\u5206\u6790\u548c\u7b56\u7565\u56de\u6d4b<\/li>\n\n\n\n<li><strong>\u4ea7\u54c1\u5206\u7c7b\u4e0e\u4ee3\u7801\u67e5\u8be2<\/strong>\uff1a\u4fbf\u4e8e\u52a8\u6001\u83b7\u53d6\u53ef\u8ba2\u9605\u7684\u4ea7\u54c1\u5217\u8868<\/li>\n\n\n\n<li><strong>\u7cfb\u7edf\u96c6\u6210\u6700\u4f73\u5b9e\u8df5<\/strong>\uff1a\u5305\u62ec\u9650\u6d41\u3001\u7f13\u5b58\u3001\u9519\u8bef\u5904\u7406\u7b49\u5173\u952e\u673a\u5236<\/li>\n<\/ol>\n\n\n\n<p>\u8109\u52a8\u6570\u636e\u7684\u4e3b\u8981\u4f18\u52bf\u5305\u62ec\uff1a<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>\u591a\u5e02\u573a\u8986\u76d6<\/strong>\uff1a\u5916\u6c47\u3001\u671f\u8d27\u3001\u6570\u5b57\u8d27\u5e01\u7b49\u5168\u7403\u54c1\u79cd<\/li>\n\n\n\n<li><strong>\u53cc\u901a\u9053\u63a5\u5165<\/strong>\uff1aWebSocket\u5b9e\u65f6\u63a8\u9001 + HTTP\u67e5\u8be2<\/li>\n\n\n\n<li><strong>\u6570\u636e\u4e30\u5bcc<\/strong>\uff1a\u4e0d\u4ec5\u5305\u542b\u57fa\u7840\u884c\u60c5\uff0c\u8fd8\u6709\u6df1\u5ea6\u76d8\u53e3\u3001\u5b9e\u65f6\u6210\u4ea4\u7b49<\/li>\n\n\n\n<li><strong>\u5f00\u53d1\u8005\u53cb\u597d<\/strong>\uff1a\u7edf\u4e00\u7684JSON\u683c\u5f0f\uff0c\u6e05\u6670\u7684\u5b57\u6bb5\u8bf4\u660e<\/li>\n<\/ul>\n\n\n\n<p>\u65e0\u8bba\u662f\u6784\u5efa\u91cf\u5316\u4ea4\u6613\u7cfb\u7edf\u3001\u5f00\u53d1\u884c\u60c5\u5206\u6790\u5de5\u5177\uff0c\u8fd8\u662f\u521b\u5efa\u98ce\u9669\u7ba1\u7406\u5e73\u53f0\uff0c\u8109\u52a8\u6570\u636eAPI\u90fd\u80fd\u63d0\u4f9b\u7a33\u5b9a\u53ef\u9760\u7684\u6570\u636e\u652f\u6301\u3002\u5efa\u8bae\u5f00\u53d1\u8005\u6839\u636e\u5b9e\u9645\u9700\u6c42\u9009\u62e9\u5408\u9002\u7684\u63a5\u5165\u65b9\u5f0f\uff0c\u5e76\u5408\u7406\u8bbe\u8ba1\u9650\u6d41\u3001\u7f13\u5b58\u548c\u9519\u8bef\u5904\u7406\u673a\u5236\uff0c\u786e\u4fdd\u7cfb\u7edf\u7684\u7a33\u5b9a\u6027\u548c\u6027\u80fd\u3002<\/p>\n\n\n\n<p><strong>\u6ce8<\/strong>\uff1a\u671f\u8d27\u548c\u6570\u5b57\u8d27\u5e01\u4ea4\u6613\u5177\u6709\u9ad8\u98ce\u9669\uff0c\u6295\u8d44\u8005\u5e94\u5145\u5206\u4e86\u89e3\u76f8\u5173\u98ce\u9669\u5e76\u8c28\u614e\u51b3\u7b56\u3002\u672c\u6587\u793a\u4f8b\u4ee3\u7801\u4ec5\u4f9b\u53c2\u8003\uff0c\u5b9e\u9645\u4f7f\u7528\u65f6\u8bf7\u6839\u636e\u5177\u4f53\u9700\u6c42\u8fdb\u884c\u8c03\u6574\u4f18\u5316\u3002 \u200b<\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u200b \u5728\u91cf\u5316\u4ea4\u6613\u548c\u91d1\u878d\u5e94\u7528\u5f00\u53d1\u9886\u57df\uff0c\u7a33\u5b9a\u3001 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"footnotes":""},"categories":[1],"tags":[],"class_list":["post-14","post","type-post","status-publish","format-standard","hentry","category-ticket"],"_links":{"self":[{"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/posts\/14","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/comments?post=14"}],"version-history":[{"count":2,"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/posts\/14\/revisions"}],"predecessor-version":[{"id":17,"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/posts\/14\/revisions\/17"}],"wp:attachment":[{"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/media?parent=14"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/categories?post=14"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/word.tickermd.com\/index.php\/wp-json\/wp\/v2\/tags?post=14"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}