{"id":6529,"date":"2025-10-31T20:55:43","date_gmt":"2025-10-31T12:55:43","guid":{"rendered":"http:\/\/192.168.1.29\/?p=6529"},"modified":"2025-10-31T21:07:46","modified_gmt":"2025-10-31T13:07:46","slug":"%e8%82%a1%e7%a5%a8%e6%a0%87%e7%a4%ba%e9%a1%b6%e3%80%81%e5%ba%95%e8%83%8c%e7%a6%bbpython%e4%bb%a3%e7%a0%81","status":"publish","type":"post","link":"http:\/\/xc.ipyingshe.net:5347\/?p=6529","title":{"rendered":"\u80a1\u7968\u6807\u793a\u9876\u5e95\u3001\u91cf\u4ef7\u80cc\u79bbpython\u4ee3\u7801"},"content":{"rendered":"\n<p>\u5b9e\u73b0\u4e86\u5229\u7528DVI\u548cMACD\u6307\u6807\u5224\u65ad\u80a1\u7968\u91cf\u4ef7\u9876\u5e95\u80cc\u79bb\u7684Python\u4ee3\u7801\u3002\u4e3b\u8981\u5b8c\u6210\u4e86\u4ee5\u4e0b\u5de5\u4f5c\uff1a<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u521b\u5efa\u4e86 vi_macd_divergence.py \u6587\u4ef6\uff0c\u5b9e\u73b0\u4e86\u5b8c\u6574\u7684\u5206\u6790\u7cfb\u7edf<\/li>\n\n\n\n<li>\u4f7f\u7528baostock\u4f5c\u4e3a\u6570\u636e\u6e90\u83b7\u53d6\u80a1\u7968\u5386\u53f2\u6570\u636e<\/li>\n\n\n\n<li>\u5b9e\u73b0\u4e86DVI(Directional Volume Indicator)\u6307\u6807\u7684\u8ba1\u7b97\uff0c\u8be5\u6307\u6807\u6bd4\u666e\u901aVI\u66f4\u80fd\u53cd\u6620\u6210\u4ea4\u91cf\u4e0e\u4ef7\u683c\u65b9\u5411\u7684\u5173\u7cfb<\/li>\n\n\n\n<li>\u8ba1\u7b97\u4e86\u6807\u51c6MACD\u6307\u6807\u4f5c\u4e3a\u8f85\u52a9\u5224\u65ad\u3002\u5f00\u53d1\u4e86\u591a\u79cd\u80cc\u79bb\u68c0\u6d4b\u7b97\u6cd5\uff1a<\/li>\n<\/ol>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4ef7\u683c\u4e0eMACD\u7684\u9876\u5e95\u80cc\u79bb<\/li>\n\n\n\n<li>\u4ef7\u683c\u4e0eDVI\u7684\u9876\u5e95\u80cc\u79bb<\/li>\n\n\n\n<li>\u91cf\u4ef7\u80cc\u79bb\uff08\u4ef7\u683c\u53d8\u52a8\u4e0e\u6210\u4ea4\u91cf\u53d8\u5316\u4e0d\u4e00\u81f4\u7684\u60c5\u51b5\uff09<\/li>\n<\/ul>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u5b9e\u73b0\u4e86\u53ef\u89c6\u5316\u529f\u80fd\uff0c\u7ed8\u5236\u5305\u542b\u4ef7\u683c\u3001\u6210\u4ea4\u91cf\u3001DVI\u548cMACD\u7684\u7efc\u5408\u56fe\u8868\uff0c\u5e76\u5728\u56fe\u4e0a\u6807\u8bb0\u5404\u79cd\u80cc\u79bb\u70b9<\/li>\n\n\n\n<li>\u6dfb\u52a0\u4e86\u8be6\u7ec6\u7684\u80cc\u79bb\u7edf\u8ba1\u6458\u8981\u8f93\u51fa\u529f\u80fd\u4ee3\u7801\u5df2\u7ecf\u6210\u529f\u8fd0\u884c\uff0c\u80fd\u591f\u68c0\u6d4b\u5e76\u663e\u793a\u5404\u79cd\u80cc\u79bb\u4fe1\u53f7\uff0c\u5e76\u5c06\u7ed3\u679c\u4fdd\u5b58\u4e3a\u56fe\u7247\u6587\u4ef6\u3002\u7528\u6237\u53ef\u4ee5\u901a\u8fc7\u4fee\u6539 main() \u51fd\u6570\u4e2d\u7684\u80a1\u7968\u4ee3\u7801\u548c\u65f6\u95f4\u8303\u56f4\u6765\u5206\u6790\u4e0d\u540c\u7684\u80a1\u7968\u3002<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"534\" src=\"http:\/\/192.168.1.29\/wp-content\/uploads\/2025\/10\/Figure_1_600938-1024x534.png\" alt=\"\" class=\"wp-image-6531\" srcset=\"http:\/\/xc.ipyingshe.net:5347\/wp-content\/uploads\/2025\/10\/Figure_1_600938-1024x534.png 1024w, http:\/\/xc.ipyingshe.net:5347\/wp-content\/uploads\/2025\/10\/Figure_1_600938-300x157.png 300w, http:\/\/xc.ipyingshe.net:5347\/wp-content\/uploads\/2025\/10\/Figure_1_600938-768x401.png 768w, http:\/\/xc.ipyingshe.net:5347\/wp-content\/uploads\/2025\/10\/Figure_1_600938-1536x802.png 1536w, http:\/\/xc.ipyingshe.net:5347\/wp-content\/uploads\/2025\/10\/Figure_1_600938-2048x1069.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code>import baostock as bs\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib.dates as mdates\nfrom datetime import datetime, timedelta\nimport warnings\nwarnings.filterwarnings('ignore')\n\n# \u8bbe\u7f6e\u4e2d\u6587\u5b57\u4f53\nplt.rcParams&#91;'font.sans-serif'] = &#91;'SimHei']  # \u7528\u6765\u6b63\u5e38\u663e\u793a\u4e2d\u6587\u6807\u7b7e\nplt.rcParams&#91;'axes.unicode_minus'] = False  # \u7528\u6765\u6b63\u5e38\u663e\u793a\u8d1f\u53f7\n\nclass StockAnalyzer:\n    def __init__(self):\n        # \u767b\u5f55baostock\n        self.bs = bs\n        lg = bs.login()\n        print(f'\u767b\u5f55\u72b6\u6001: {lg.error_code} {lg.error_msg}')\n        self.data = None\n        self.stock_code = None\n        self.start_date = None\n        self.end_date = None\n    \n    def logout(self):\n        # \u767b\u51fabaostock\n        bs.logout()\n    \n    def get_stock_data(self, stock_code, start_date, end_date):\n        \"\"\"\n        \u4ecebaostock\u83b7\u53d6\u80a1\u7968\u6570\u636e\n        \"\"\"\n        self.stock_code = stock_code\n        self.start_date = start_date\n        self.end_date = end_date\n        \n        # \u83b7\u53d6K\u7ebf\u6570\u636e\n        rs = bs.query_history_k_data_plus(\n            stock_code,\n            'date,open,high,low,close,volume',\n            start_date=start_date,\n            end_date=end_date,\n            frequency='d',\n            adjustflag='2'  # \u524d\u590d\u6743\n        )\n        \n        # \u5904\u7406\u6570\u636e\n        data_list = &#91;]\n        while (rs.error_code == '0') &amp; rs.next():\n            data_list.append(rs.get_row_data())\n        \n        if data_list:\n            self.data = pd.DataFrame(data_list, columns=rs.fields)\n            # \u8f6c\u6362\u6570\u636e\u7c7b\u578b\n            for col in &#91;'open', 'high', 'low', 'close', 'volume']:\n                self.data&#91;col] = pd.to_numeric(self.data&#91;col])\n            self.data&#91;'date'] = pd.to_datetime(self.data&#91;'date'])\n            self.data.set_index('date', inplace=True)\n            \n            print(f'\u6210\u529f\u83b7\u53d6 {stock_code} \u7684\u6570\u636e\uff0c\u5171 {len(self.data)} \u6761\u8bb0\u5f55')\n            return True\n        else:\n            print(f'\u83b7\u53d6\u6570\u636e\u5931\u8d25: {rs.error_code} {rs.error_msg}')\n            return False\n    \n    def calculate_indicators(self):\n        \"\"\"\n        \u8ba1\u7b97DVI\u548cMACD\u6307\u6807\n        DVI (Directional Volume Indicator) \u662f\u4e00\u4e2a\u91cf\u4ef7\u6307\u6807\uff0c\u7528\u4e8e\u5206\u6790\u6210\u4ea4\u91cf\u548c\u4ef7\u683c\u4e4b\u95f4\u7684\u5173\u7cfb\n        \"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\")\n            return False\n        \n        # \u8ba1\u7b97DVI\u6307\u6807\n        # 1. \u8ba1\u7b97\u4ef7\u683c\u53d8\u5316\u65b9\u5411\n        self.data&#91;'price_change'] = self.data&#91;'close'] - self.data&#91;'close'].shift(1)\n        self.data&#91;'direction'] = np.where(self.data&#91;'price_change'] > 0, 1, \n                                        np.where(self.data&#91;'price_change'] &lt; 0, -1, 0))\n        \n        # 2. \u8ba1\u7b97\u65b9\u5411\u6210\u4ea4\u91cf\n        self.data&#91;'directional_volume'] = self.data&#91;'volume'] * self.data&#91;'direction']\n        \n        # 3. \u8ba1\u7b97DVI\u7684\u4e24\u4e2a\u4e3b\u8981\u7ec4\u4ef6\n        # DVI_MoneyFlow: \u53cd\u6620\u4ef7\u683c\u65b9\u5411\u4e0a\u7684\u8d44\u91d1\u6d41\u52a8\n        self.data&#91;'dvi_moneyflow'] = self.data&#91;'directional_volume'] * self.data&#91;'price_change'].abs()\n        \n        # 4. \u8ba1\u7b97DVI\u7ebf\n        # \u77ed\u671f\u79fb\u52a8\u5e73\u5747 (\u901a\u5e38\u4f7f\u752820\u5929)\n        self.data&#91;'dvi_short'] = self.data&#91;'dvi_moneyflow'].rolling(window=20).sum()\n        # \u957f\u671f\u79fb\u52a8\u5e73\u5747 (\u901a\u5e38\u4f7f\u752850\u5929)\n        self.data&#91;'dvi_long'] = self.data&#91;'dvi_moneyflow'].rolling(window=50).sum()\n        # \u6807\u51c6\u5316DVI\n        self.data&#91;'dvi'] = (self.data&#91;'dvi_short'] - self.data&#91;'dvi_long']) \/ \\\n                          (self.data&#91;'dvi_short'].abs() + self.data&#91;'dvi_long'].abs() + 1e-10)\n        \n        # 5. \u8ba1\u7b97DVI\u4fe1\u53f7\u7ebf (\u901a\u5e38\u662fDVI\u768410\u5929\u79fb\u52a8\u5e73\u5747)\n        self.data&#91;'dvi_signal'] = self.data&#91;'dvi'].rolling(window=10).mean()\n        \n        # \u8ba1\u7b97MACD\n        exp1 = self.data&#91;'close'].ewm(span=12, adjust=False).mean()\n        exp2 = self.data&#91;'close'].ewm(span=26, adjust=False).mean()\n        self.data&#91;'macd'] = exp1 - exp2\n        self.data&#91;'signal'] = self.data&#91;'macd'].ewm(span=9, adjust=False).mean()\n        self.data&#91;'histogram'] = self.data&#91;'macd'] - self.data&#91;'signal']\n        \n        return True\n    \n    def detect_divergence(self, lookback_period=20):\n        \"\"\"\n        \u68c0\u6d4b\u91cf\u4ef7\u9876\u5e95\u80cc\u79bb\n        - \u9876\u80cc\u79bb: \u4ef7\u683c\u521b\u65b0\u9ad8\u4f46\u6307\u6807\u4e0d\u521b\u65b0\u9ad8\n        - \u5e95\u80cc\u79bb: \u4ef7\u683c\u521b\u65b0\u4f4e\u4f46\u6307\u6807\u4e0d\u521b\u65b0\u4f4e\n        \"\"\"\n        if self.data is None or 'dvi' not in self.data.columns or 'macd' not in self.data.columns:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\u5e76\u8ba1\u7b97\u6307\u6807\")\n            return False\n        \n        # \u521d\u59cb\u5316\u80cc\u79bb\u6807\u8bb0\u5217\n        self.data&#91;'price_top_divergence'] = 0     # \u4ef7\u683c\u9876\u80cc\u79bb\n        self.data&#91;'price_bottom_divergence'] = 0  # \u4ef7\u683c\u5e95\u80cc\u79bb\n        self.data&#91;'dvi_top_divergence'] = 0       # DVI\u9876\u80cc\u79bb\n        self.data&#91;'dvi_bottom_divergence'] = 0    # DVI\u5e95\u80cc\u79bb\n        self.data&#91;'macd_top_divergence'] = 0      # MACD\u9876\u80cc\u79bb\n        self.data&#91;'macd_bottom_divergence'] = 0   # MACD\u5e95\u80cc\u79bb\n        self.data&#91;'volume_price_divergence'] = 0  # \u91cf\u4ef7\u80cc\u79bb\n        \n        for i in range(lookback_period, len(self.data)):\n            # \u83b7\u53d6\u56de\u987e\u671f\u5185\u7684\u6570\u636e\n            lookback_data = self.data.iloc&#91;i-lookback_period:i]\n            current_data = self.data.iloc&#91;i]\n            \n            # 1. \u4ef7\u683c\u4e0eMACD\u9876\u5e95\u80cc\u79bb\u68c0\u6d4b\n            # \u9876\u80cc\u79bb: \u4ef7\u683c\u521b\u65b0\u9ad8\uff0cMACD\u672a\u521b\u65b0\u9ad8\n            if (current_data&#91;'high'] > lookback_data&#91;'high'].max() and \n                current_data&#91;'macd'] &lt; lookback_data&#91;'macd'].max()):\n                self.data&#91;'price_top_divergence'].iloc&#91;i] = 1\n                self.data&#91;'macd_top_divergence'].iloc&#91;i] = 1\n            \n            # \u5e95\u80cc\u79bb: \u4ef7\u683c\u521b\u65b0\u4f4e\uff0cMACD\u672a\u521b\u65b0\u4f4e\n            if (current_data&#91;'low'] &lt; lookback_data&#91;'low'].min() and \n                current_data&#91;'macd'] > lookback_data&#91;'macd'].min()):\n                self.data&#91;'price_bottom_divergence'].iloc&#91;i] = 1\n                self.data&#91;'macd_bottom_divergence'].iloc&#91;i] = 1\n            \n            # 2. \u4ef7\u683c\u4e0eDVI\u9876\u5e95\u80cc\u79bb\u68c0\u6d4b\n            # DVI\u9876\u80cc\u79bb: \u4ef7\u683c\u521b\u65b0\u9ad8\uff0cDVI\u672a\u521b\u65b0\u9ad8\n            if (current_data&#91;'high'] > lookback_data&#91;'high'].max() and \n                current_data&#91;'dvi'] &lt; lookback_data&#91;'dvi'].max()):\n                self.data&#91;'dvi_top_divergence'].iloc&#91;i] = 1\n            \n            # DVI\u5e95\u80cc\u79bb: \u4ef7\u683c\u521b\u65b0\u4f4e\uff0cDVI\u672a\u521b\u65b0\u4f4e\n            if (current_data&#91;'low'] &lt; lookback_data&#91;'low'].min() and \n                current_data&#91;'dvi'] > lookback_data&#91;'dvi'].min()):\n                self.data&#91;'dvi_bottom_divergence'].iloc&#91;i] = 1\n            \n            # 3. \u91cf\u4ef7\u80cc\u79bb\u68c0\u6d4b\n            # \u4e0a\u5347\u8d8b\u52bf\u4e2d\u7684\u91cf\u4ef7\u80cc\u79bb: \u4ef7\u683c\u4e0a\u6da8\u4f46\u6210\u4ea4\u91cf\u51cf\u5c11\n            if (current_data&#91;'close'] > current_data&#91;'open'] and i > 0 and \n                current_data&#91;'volume'] &lt; self.data&#91;'volume'].iloc&#91;i-1] * 0.9):\n                self.data&#91;'volume_price_divergence'].iloc&#91;i] = 1\n            \n            # \u4e0b\u964d\u8d8b\u52bf\u4e2d\u7684\u91cf\u4ef7\u80cc\u79bb: \u4ef7\u683c\u4e0b\u8dcc\u4f46\u6210\u4ea4\u91cf\u51cf\u5c11\n            if (current_data&#91;'close'] &lt; current_data&#91;'open'] and i > 0 and \n                current_data&#91;'volume'] &lt; self.data&#91;'volume'].iloc&#91;i-1] * 0.9):\n                self.data&#91;'volume_price_divergence'].iloc&#91;i] = -1\n        \n        return True\n    \n    def plot_indicators(self):\n        \"\"\"\n        \u7ed8\u5236\u4ef7\u683c\u3001\u6210\u4ea4\u91cf\u3001DVI\u548cMACD\u6307\u6807\u56fe\uff0c\u5e76\u6807\u8bb0\u80cc\u79bb\u70b9\n        \"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\")\n            return False\n        \n        fig, (ax1, ax2, ax3, ax4) = plt.subplots(4, 1, figsize=(15, 14), sharex=True)\n        \n        # \u7ed8\u5236\u4ef7\u683c\u56fe\n        ax1.plot(self.data.index, self.data&#91;'close'], label='\u6536\u76d8\u4ef7', color='blue')\n        # \u6807\u8bb0\u9876\u5e95\u80cc\u79bb\n        top_div = self.data&#91;self.data&#91;'price_top_divergence'] == 1]\n        bottom_div = self.data&#91;self.data&#91;'price_bottom_divergence'] == 1]\n        ax1.scatter(top_div.index, top_div&#91;'high'], color='red', marker='v', s=150, label='\u9876\u80cc\u79bb', zorder=5)\n        ax1.scatter(bottom_div.index, bottom_div&#91;'low'], color='green', marker='^', s=150, label='\u5e95\u80cc\u79bb', zorder=5)\n        ax1.set_title(f'{self.stock_code} \u4ef7\u683c\u4e0e\u80cc\u79bb\u5206\u6790')\n        ax1.set_ylabel('\u4ef7\u683c')\n        ax1.legend()\n        ax1.grid(True, alpha=0.3)\n        \n        # \u7ed8\u5236\u6210\u4ea4\u91cf\u56fe\n        # \u6839\u636e\u4ef7\u683c\u53d8\u52a8\u7740\u8272\u6210\u4ea4\u91cf\u67f1\n        colors = &#91;'red' if close > open else 'green' for close, open in zip(self.data&#91;'close'], self.data&#91;'open'])]\n        ax2.bar(self.data.index, self.data&#91;'volume'], color=colors, alpha=0.6, label='\u6210\u4ea4\u91cf')\n        # \u6807\u8bb0\u91cf\u4ef7\u80cc\u79bb\n        vol_div_pos = self.data&#91;self.data&#91;'volume_price_divergence'] == 1]\n        vol_div_neg = self.data&#91;self.data&#91;'volume_price_divergence'] == -1]\n        ax2.scatter(vol_div_pos.index, vol_div_pos&#91;'volume'], color='orange', marker='o', s=100, label='\u91cf\u4ef7\u6b63\u80cc\u79bb', zorder=5)\n        ax2.scatter(vol_div_neg.index, vol_div_neg&#91;'volume'], color='purple', marker='o', s=100, label='\u91cf\u4ef7\u8d1f\u80cc\u79bb', zorder=5)\n        ax2.set_ylabel('\u6210\u4ea4\u91cf')\n        ax2.legend()\n        ax2.grid(True, alpha=0.3)\n        \n        # \u7ed8\u5236DVI\u6307\u6807\u56fe\n        ax3.plot(self.data.index, self.data&#91;'dvi'], label='DVI', color='blue')\n        ax3.plot(self.data.index, self.data&#91;'dvi_signal'], label='DVI Signal', color='red', linestyle='--')\n        # \u6807\u8bb0DVI\u80cc\u79bb\n        dvi_top_div = self.data&#91;self.data&#91;'dvi_top_divergence'] == 1]\n        dvi_bottom_div = self.data&#91;self.data&#91;'dvi_bottom_divergence'] == 1]\n        ax3.scatter(dvi_top_div.index, dvi_top_div&#91;'dvi'], color='red', marker='v', s=100, label='DVI\u9876\u80cc\u79bb', zorder=5)\n        ax3.scatter(dvi_bottom_div.index, dvi_bottom_div&#91;'dvi'], color='green', marker='^', s=100, label='DVI\u5e95\u80cc\u79bb', zorder=5)\n        ax3.axhline(y=0, color='black', linestyle='--', alpha=0.3)\n        ax3.set_ylabel('DVI\u6307\u6807')\n        ax3.legend()\n        ax3.grid(True, alpha=0.3)\n        \n        # \u7ed8\u5236MACD\u56fe\n        ax4.plot(self.data.index, self.data&#91;'macd'], label='MACD', color='blue')\n        ax4.plot(self.data.index, self.data&#91;'signal'], label='Signal', color='red')\n        # \u6839\u636e\u67f1\u72b6\u56fe\u503c\u7740\u8272\n        histogram_colors = &#91;'red' if x > 0 else 'green' for x in self.data&#91;'histogram']]\n        ax4.bar(self.data.index, self.data&#91;'histogram'], label='Histogram', color=histogram_colors, alpha=0.3)\n        # \u6807\u8bb0MACD\u80cc\u79bb\n        macd_top_div = self.data&#91;self.data&#91;'macd_top_divergence'] == 1]\n        macd_bottom_div = self.data&#91;self.data&#91;'macd_bottom_divergence'] == 1]\n        ax4.scatter(macd_top_div.index, macd_top_div&#91;'macd'], color='red', marker='v', s=100, label='MACD\u9876\u80cc\u79bb', zorder=5)\n        ax4.scatter(macd_bottom_div.index, macd_bottom_div&#91;'macd'], color='green', marker='^', s=100, label='MACD\u5e95\u80cc\u79bb', zorder=5)\n        ax4.axhline(y=0, color='black', linestyle='--', alpha=0.3)\n        ax4.set_ylabel('MACD')\n        ax4.set_xlabel('\u65e5\u671f')\n        ax4.legend()\n        ax4.grid(True, alpha=0.3)\n        \n        # \u8bbe\u7f6e\u65e5\u671f\u683c\u5f0f\n        locator = mdates.AutoDateLocator(minticks=10, maxticks=20)\n        formatter = mdates.ConciseDateFormatter(locator)\n        ax4.xaxis.set_major_locator(locator)\n        ax4.xaxis.set_major_formatter(formatter)\n        \n        plt.tight_layout()\n        plt.savefig(f'{self.stock_code}_dvi_macd_divergence.png', dpi=300, bbox_inches='tight')\n        plt.show()\n        \n        return True\n    \n    def print_divergence_summary(self):\n        \"\"\"\n        \u6253\u5370\u80cc\u79bb\u60c5\u51b5\u6458\u8981\n        \"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\")\n            return False\n        \n        print(f\"\\n{self.stock_code} \u80cc\u79bb\u5206\u6790\u6458\u8981 ({self.start_date} \u81f3 {self.end_date})\")\n        print(f\"\u68c0\u6d4b\u5230\u7684\u4ef7\u683c\u9876\u80cc\u79bb\u6b21\u6570: {self.data&#91;'price_top_divergence'].sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684\u4ef7\u683c\u5e95\u80cc\u79bb\u6b21\u6570: {self.data&#91;'price_bottom_divergence'].sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684DVI\u9876\u80cc\u79bb\u6b21\u6570: {self.data&#91;'dvi_top_divergence'].sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684DVI\u5e95\u80cc\u79bb\u6b21\u6570: {self.data&#91;'dvi_bottom_divergence'].sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684MACD\u9876\u80cc\u79bb\u6b21\u6570: {self.data&#91;'macd_top_divergence'].sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684MACD\u5e95\u80cc\u79bb\u6b21\u6570: {self.data&#91;'macd_bottom_divergence'].sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684\u91cf\u4ef7\u6b63\u80cc\u79bb\u6b21\u6570: {(self.data&#91;'volume_price_divergence'] == 1).sum()}\")\n        print(f\"\u68c0\u6d4b\u5230\u7684\u91cf\u4ef7\u8d1f\u80cc\u79bb\u6b21\u6570: {(self.data&#91;'volume_price_divergence'] == -1).sum()}\")\n        \n        # \u6700\u8fd1\u7684\u80cc\u79bb\u70b9\n        recent_price_top = self.data&#91;self.data&#91;'price_top_divergence'] == 1].index.max()\n        recent_price_bottom = self.data&#91;self.data&#91;'price_bottom_divergence'] == 1].index.max()\n        recent_dvi_top = self.data&#91;self.data&#91;'dvi_top_divergence'] == 1].index.max()\n        recent_dvi_bottom = self.data&#91;self.data&#91;'dvi_bottom_divergence'] == 1].index.max()\n        \n        if pd.notna(recent_price_top):\n            print(f\"\\n\u6700\u8fd1\u7684\u4ef7\u683c\u9876\u80cc\u79bb\u65e5\u671f: {recent_price_top.strftime('%Y-%m-%d')}\")\n        if pd.notna(recent_price_bottom):\n            print(f\"\u6700\u8fd1\u7684\u4ef7\u683c\u5e95\u80cc\u79bb\u65e5\u671f: {recent_price_bottom.strftime('%Y-%m-%d')}\")\n        if pd.notna(recent_dvi_top):\n            print(f\"\u6700\u8fd1\u7684DVI\u9876\u80cc\u79bb\u65e5\u671f: {recent_dvi_top.strftime('%Y-%m-%d')}\")\n        if pd.notna(recent_dvi_bottom):\n            print(f\"\u6700\u8fd1\u7684DVI\u5e95\u80cc\u79bb\u65e5\u671f: {recent_dvi_bottom.strftime('%Y-%m-%d')}\")\n        \n        return True\n\ndef main():\n    # \u521b\u5efa\u5206\u6790\u5668\u5b9e\u4f8b\n    analyzer = StockAnalyzer()\n    \n    try:\n        # \u8bbe\u7f6e\u53c2\u6570\n        stock_code = \"sh.600938\"  # \u6d66\u53d1\u94f6\u884c\n        end_date = datetime.now().strftime('%Y-%m-%d')\n        start_date = (datetime.now() - timedelta(days=365)).strftime('%Y-%m-%d')\n        \n        # \u83b7\u53d6\u6570\u636e\n        if analyzer.get_stock_data(stock_code, start_date, end_date):\n            # \u8ba1\u7b97\u6307\u6807\n            analyzer.calculate_indicators()\n            \n            # \u68c0\u6d4b\u80cc\u79bb\n            analyzer.detect_divergence()\n            \n            # \u6253\u5370\u6458\u8981\n            analyzer.print_divergence_summary()\n            \n            # \u7ed8\u5236\u56fe\u5f62\n            analyzer.plot_indicators()\n    \n    finally:\n        # \u767b\u51fa\n        analyzer.logout()\n\nif __name__ == \"__main__\":\n    main()<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u5b9e\u73b0\u4e86\u5229\u7528DVI\u548cMACD\u6307\u6807\u5224\u65ad\u80a1\u7968\u91cf\u4ef7\u9876\u5e95\u80cc\u79bb\u7684Python\u4ee3\u7801\u3002\u4e3b\u8981\u5b8c\u6210\u4e86 <span class=\"readmore\"><a href=\"http:\/\/xc.ipyingshe.net:5347\/?p=6529\">Continue Reading<\/a><\/span><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[24],"tags":[],"class_list":["post-6529","post","type-post","status-publish","format-standard","hentry","category-24"],"_links":{"self":[{"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/6529","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6529"}],"version-history":[{"count":2,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/6529\/revisions"}],"predecessor-version":[{"id":6533,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/6529\/revisions\/6533"}],"wp:attachment":[{"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6529"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}