{"id":6546,"date":"2025-11-09T20:45:52","date_gmt":"2025-11-09T12:45:52","guid":{"rendered":"http:\/\/192.168.1.29\/?p=6546"},"modified":"2025-11-09T20:45:52","modified_gmt":"2025-11-09T12:45:52","slug":"%e9%87%87%e7%94%a8akshare%e6%95%b0%e6%8d%ae%e6%ba%90python%e7%9a%84ta-lib%e5%ba%93%e8%ae%a1%e7%ae%97%e8%82%a1%e7%a5%a8%e5%a4%9a%e4%b8%aa%e6%8c%87%e6%a0%87","status":"publish","type":"post","link":"http:\/\/xc.ipyingshe.net:5347\/?p=6546","title":{"rendered":"\u91c7\u7528akshare\u6570\u636e\u6e90python\u7684TA-lib\u5e93\u8ba1\u7b97\u80a1\u7968\u591a\u4e2a\u6307\u6807"},"content":{"rendered":"\n<p>readme.md   <\/p>\n\n\n\n<div class=\"wp-block-file\"><a id=\"wp-block-file--media-01b3b95c-ff31-4489-9608-8c9f7a00ca97\" href=\"http:\/\/192.168.1.29\/wp-content\/uploads\/2025\/11\/README.txt\">README<\/a><a href=\"http:\/\/192.168.1.29\/wp-content\/uploads\/2025\/11\/README.txt\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-01b3b95c-ff31-4489-9608-8c9f7a00ca97\">\u4e0b\u8f7d<\/a><\/div>\n\n\n\n<p>requirementst.txt<\/p>\n\n\n\n<div class=\"wp-block-file\"><a id=\"wp-block-file--media-72057434-f887-4f48-8e01-17638d5c1511\" href=\"http:\/\/192.168.1.29\/wp-content\/uploads\/2025\/11\/requirements.txt\">requirements<\/a><a href=\"http:\/\/192.168.1.29\/wp-content\/uploads\/2025\/11\/requirements.txt\" class=\"wp-block-file__button wp-element-button\" download aria-describedby=\"wp-block-file--media-72057434-f887-4f48-8e01-17638d5c1511\">\u4e0b\u8f7d<\/a><\/div>\n\n\n\n<p><strong>\u6e90\u4ee3\u7801\uff1a<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport matplotlib.dates as mdates\nimport talib as ta\nimport akshare as ak\nimport baostock as bs\nimport datetime\nfrom matplotlib.gridspec import GridSpec\n  \n  # \u8bbe\u7f6e\u4e2d\u6587\u663e\u793a\nplt.rcParams&#91;\"font.family\"] = &#91;\"SimHei\", \"Microsoft YaHei\", \"SimSun\", \"KaiTi\", \"FangSong\"]\nplt.rcParams&#91;\"axes.unicode_minus\"] = False  # \u6b63\u786e\u663e\u793a\u8d1f\u53f7\n\nclass StockTechnicalAnalyzer:\n    def __init__(self, data_source='baostock'):\n        \"\"\"\n        \u521d\u59cb\u5316\u80a1\u7968\u6280\u672f\u5206\u6790\u5668\n        data_source: \u6570\u636e\u6e90\uff0c\u53ef\u9009 'baostock' \u6216 'akshare'\n        \"\"\"\n        self.data_source = data_source\n        self.data = None\n        self.stock_code = None\n        self.start_date = None\n        self.end_date = None\n    \n    def get_stock_data(self, stock_code, start_date, end_date):\n        \"\"\"\u4ece\u6307\u5b9a\u6570\u636e\u6e90\u83b7\u53d6\u80a1\u7968\u6570\u636e\"\"\"\n        self.stock_code = stock_code\n        self.start_date = start_date\n        self.end_date = end_date\n        \n        print(f\"\u6b63\u5728\u4ece{self.data_source}\u83b7\u53d6{stock_code}\u4ece{start_date}\u5230{end_date}\u7684\u6570\u636e...\")\n        \n        if self.data_source == 'baostock':\n            return self._get_baostock_data(stock_code, start_date, end_date)\n        elif self.data_source == 'akshare':\n            return self._get_akshare_data(stock_code, start_date, end_date)\n        else:\n            raise ValueError(\"\u6570\u636e\u6e90\u5fc5\u987b\u662f 'baostock' \u6216 'akshare'\")\n    \n    def _get_baostock_data(self, stock_code, start_date, end_date):\n        \"\"\"\u4ecebaostock\u83b7\u53d6\u80a1\u7968\u6570\u636e\"\"\"\n        # \u767b\u5f55baostock\n        lg = bs.login()\n        if lg.error_code != '0':\n            print(f\"\u767b\u5f55\u5931\u8d25\uff1a{lg.error_msg}\")\n            return None\n        \n        # \u83b7\u53d6\u80a1\u7968\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        # \u767b\u51fabaostock\n        bs.logout()\n        \n        # \u8f6c\u6362\u4e3aDataFrame\u5e76\u5904\u7406\n        if not data_list:\n            print(\"\u6ca1\u6709\u83b7\u53d6\u5230\u6570\u636e\")\n            return None\n            \n        df = pd.DataFrame(data_list, columns=rs.fields)\n        # \u8f6c\u6362\u6570\u636e\u7c7b\u578b\n        df&#91;'date'] = pd.to_datetime(df&#91;'date'])\n        df&#91;'open'] = df&#91;'open'].astype(float)\n        df&#91;'high'] = df&#91;'high'].astype(float)\n        df&#91;'low'] = df&#91;'low'].astype(float)\n        df&#91;'close'] = df&#91;'close'].astype(float)\n        df&#91;'volume'] = df&#91;'volume'].astype(float)\n        \n        df.set_index('date', inplace=True)\n        self.data = df\n        print(f\"\u6210\u529f\u83b7\u53d6{len(df)}\u6761\u6570\u636e\")\n        return df\n    \n    def _get_akshare_data(self, stock_code, start_date, end_date):\n        \"\"\"\u4eceakshare\u83b7\u53d6\u80a1\u7968\u6570\u636e\"\"\"\n        try:\n            # \u5bf9\u4e8eA\u80a1\uff0cakshare\u7684\u4ee3\u7801\u683c\u5f0f\u4e3a 'sh600000' \u6216 'sz000000'\n            # \u8f6c\u6362baostock\u683c\u5f0f\u5230akshare\u683c\u5f0f\n            if stock_code.startswith('sh.'):\n                ak_code = 'sh' + stock_code&#91;3:]\n            elif stock_code.startswith('sz.'):\n                ak_code = 'sz' + stock_code&#91;3:]\n            else:\n                ak_code = stock_code\n                \n            # \u83b7\u53d6\u80a1\u7968\u6570\u636e\n            df = ak.stock_zh_a_daily(symbol=ak_code, start_date=start_date.replace('-', ''), \n                                   end_date=end_date.replace('-', ''), adjust=\"qfq\")\n            \n            # \u91cd\u547d\u540d\u5217\u4ee5\u7edf\u4e00\u683c\u5f0f\n            if not df.empty:\n                df&#91;'date'] = pd.to_datetime(df&#91;'date'])\n                df.set_index('date', inplace=True)\n                self.data = df\n                print(f\"\u6210\u529f\u83b7\u53d6{len(df)}\u6761\u6570\u636e\")\n                return df\n            else:\n                print(\"\u6ca1\u6709\u83b7\u53d6\u5230\u6570\u636e\")\n                return None\n        except Exception as e:\n            print(f\"\u83b7\u53d6\u6570\u636e\u65f6\u51fa\u9519: {str(e)}\")\n            return None\n    \n    def calculate_indicators(self, rsi_period=14, macd_fast=12, macd_slow=26, macd_signal=9,\n                           bb_period=20, bb_std=2, atr_period=14, cci_period=14, \n                           roc_period=12, wr_period=14, bias_period=6):\n        \"\"\"\u4f7f\u7528TA-Lib\u8ba1\u7b97\u5404\u79cd\u6280\u672f\u6307\u6807\"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\")\n            return None\n        \n        df = self.data.copy()\n        \n        # \u8ba1\u7b97RSI\n        df&#91;'rsi'] = ta.RSI(df&#91;'close'], timeperiod=rsi_period)\n        \n        # \u8ba1\u7b97MACD\n        df&#91;'macd'], df&#91;'macd_signal'], df&#91;'macd_hist'] = ta.MACD(\n            df&#91;'close'], fastperiod=macd_fast, slowperiod=macd_slow, signalperiod=macd_signal\n        )\n        \n        # \u8ba1\u7b97\u5e03\u6797\u5e26\n        df&#91;'bb_upper'], df&#91;'bb_middle'], df&#91;'bb_lower'] = ta.BBANDS(\n            df&#91;'close'], timeperiod=bb_period, nbdevup=bb_std, nbdevdn=bb_std\n        )\n        \n        # \u8ba1\u7b97ATR\n        df&#91;'atr'] = ta.ATR(df&#91;'high'], df&#91;'low'], df&#91;'close'], timeperiod=atr_period)\n        df&#91;'atr_pct'] = df&#91;'atr'] \/ df&#91;'close'] * 100\n        \n        # \u8ba1\u7b97MA\n        df&#91;'ma5'] = ta.MA(df&#91;'close'], timeperiod=5)\n        df&#91;'ma10'] = ta.MA(df&#91;'close'], timeperiod=10)\n        df&#91;'ma20'] = ta.MA(df&#91;'close'], timeperiod=20)\n        df&#91;'ma60'] = ta.MA(df&#91;'close'], timeperiod=60)\n        \n        # \u8ba1\u7b97\u6210\u4ea4\u91cfMA\n        df&#91;'volume_ma5'] = ta.MA(df&#91;'volume'], timeperiod=5)\n        df&#91;'volume_ma10'] = ta.MA(df&#91;'volume'], timeperiod=10)\n        \n        # \u8ba1\u7b97KDJ\n        df&#91;'k'], df&#91;'d'] = ta.STOCH(df&#91;'high'], df&#91;'low'], df&#91;'close'])\n        df&#91;'j'] = 3 * df&#91;'k'] - 2 * df&#91;'d']\n        \n        # \u8ba1\u7b97OBV\n        df&#91;'obv'] = ta.OBV(df&#91;'close'], df&#91;'volume'])\n        \n        # \u8ba1\u7b97CCI (\u987a\u52bf\u6307\u6807)\n        df&#91;'cci'] = ta.CCI(df&#91;'high'], df&#91;'low'], df&#91;'close'], timeperiod=cci_period)\n        \n        # \u8ba1\u7b97ROC (\u53d8\u5316\u7387)\n        df&#91;'roc'] = ta.ROC(df&#91;'close'], timeperiod=roc_period)\n        \n        # \u8ba1\u7b97W&amp;R (\u5a01\u5ec9\u6307\u6807)\n        df&#91;'wr'] = -100 * ((df&#91;'high'].rolling(window=wr_period).max() - df&#91;'close']) \/ \n                          (df&#91;'high'].rolling(window=wr_period).max() - df&#91;'low'].rolling(window=wr_period).min()))\n        \n        # \u8ba1\u7b97BIAS (\u4e56\u79bb\u7387)\n        df&#91;'bias'] = (df&#91;'close'] - df&#91;'close'].rolling(window=bias_period).mean()) \/ \\\n                     df&#91;'close'].rolling(window=bias_period).mean() * 100\n        \n        # \u8ba1\u7b97ENE\u6307\u6807 (\u8f68\u9053\u7ebf)\n        df&#91;'ene_middle'] = df&#91;'close'].rolling(window=10).mean()\n        df&#91;'ene_upper'] = df&#91;'ene_middle'] * 1.1\n        df&#91;'ene_lower'] = df&#91;'ene_middle'] * 0.9\n        \n        # \u8ba1\u7b97VI (\u6ce2\u52a8\u7387\u6307\u6807) - \u57fa\u4e8e\u771f\u5b9e\u6ce2\u5e45\u7684\u79fb\u52a8\u5e73\u5747\n        df&#91;'vi_positive'] = ta.SMA(df&#91;'high'] - df&#91;'open'], timeperiod=14)\n        df&#91;'vi_negative'] = ta.SMA(df&#91;'open'] - df&#91;'low'], timeperiod=14)\n        \n        # \u8ba1\u7b97DMI\u6307\u6807\n        df&#91;'adx'] = ta.ADX(df&#91;'high'], df&#91;'low'], df&#91;'close'], timeperiod=14)\n        df&#91;'plus_di'] = ta.PLUS_DI(df&#91;'high'], df&#91;'low'], df&#91;'close'], timeperiod=14)\n        df&#91;'minus_di'] = ta.MINUS_DI(df&#91;'high'], df&#91;'low'], df&#91;'close'], timeperiod=14)\n        \n        # \u8ba1\u7b97PSY (\u5fc3\u7406\u7ebf)\n        df&#91;'price_change'] = df&#91;'close'].diff(1)\n        df&#91;'positive_days'] = df&#91;'price_change'].apply(lambda x: 1 if x > 0 else 0)\n        df&#91;'psy'] = df&#91;'positive_days'].rolling(window=12).sum() \/ 12 * 100\n        \n        # \u6e05\u7406\u4e34\u65f6\u5217\n        if 'price_change' in df.columns:\n            df.drop('price_change', axis=1, inplace=True)\n        if 'positive_days' in df.columns:\n            df.drop('positive_days', axis=1, inplace=True)\n        \n        self.data = df\n        return df\n    \n    def generate_signals(self):\n        \"\"\"\u57fa\u4e8e\u6280\u672f\u6307\u6807\u751f\u6210\u4ea4\u6613\u4fe1\u53f7\"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\u5e76\u8ba1\u7b97\u6307\u6807\")\n            return None\n        \n        df = self.data.copy()\n        df&#91;'signal'] = 0  # 0\u8868\u793a\u65e0\u4fe1\u53f7\uff0c1\u8868\u793a\u4e70\u5165\uff0c-1\u8868\u793a\u5356\u51fa\n        \n        # RSI\u8d85\u4e70\u8d85\u5356\u4fe1\u53f7\n        df.loc&#91;df&#91;'rsi'] &lt; 30, 'signal'] += 1  # RSI&lt;30 \u8003\u8651\u4e70\u5165\n        df.loc&#91;df&#91;'rsi'] > 70, 'signal'] += -1  # RSI>70 \u8003\u8651\u5356\u51fa\n        \n        # MACD\u91d1\u53c9\u6b7b\u53c9\u4fe1\u53f7\n        macd_cross_up = (df&#91;'macd'] > df&#91;'macd_signal']) &amp; (df&#91;'macd'].shift(1) &lt;= df&#91;'macd_signal'].shift(1))\n        macd_cross_down = (df&#91;'macd'] &lt; df&#91;'macd_signal']) &amp; (df&#91;'macd'].shift(1) >= df&#91;'macd_signal'].shift(1))\n        df.loc&#91;macd_cross_up, 'signal'] += 1  # MACD\u91d1\u53c9 \u4e70\u5165\u4fe1\u53f7\n        df.loc&#91;macd_cross_down, 'signal'] += -1  # MACD\u6b7b\u53c9 \u5356\u51fa\u4fe1\u53f7\n        \n        # \u5e03\u6797\u5e26\u7a81\u7834\u4fe1\u53f7\n        bb_upper_break = (df&#91;'close'] > df&#91;'bb_upper']) &amp; (df&#91;'close'].shift(1) &lt;= df&#91;'bb_upper'].shift(1))\n        bb_lower_break = (df&#91;'close'] &lt; df&#91;'bb_lower']) &amp; (df&#91;'close'].shift(1) >= df&#91;'bb_lower'].shift(1))\n        df.loc&#91;bb_upper_break, 'signal'] += 1  # \u7a81\u7834\u4e0a\u8f68 \u4e70\u5165\u4fe1\u53f7\n        df.loc&#91;bb_lower_break, 'signal'] += -1  # \u8dcc\u7834\u4e0b\u8f68 \u5356\u51fa\u4fe1\u53f7\n        \n        # \u7b80\u5316\u4fe1\u53f7\uff1a>0\u4e3a\u4e70\u5165\uff0c&lt;0\u4e3a\u5356\u51fa\n        df&#91;'final_signal'] = 0\n        df.loc&#91;df&#91;'signal'] > 0, 'final_signal'] = 1\n        df.loc&#91;df&#91;'signal'] &lt; 0, 'final_signal'] = -1\n        \n        self.data = df\n        return df\n    \n    def plot_moving_averages(self):\n        \"\"\"\u5355\u72ec\u7ed8\u5236\u79fb\u52a8\u5e73\u5747\u7ebf\u56fe\u8868\"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\u5e76\u8ba1\u7b97\u6307\u6807\")\n            return None\n        \n        df = self.data.dropna()\n        \n        # \u521b\u5efa\u7b2c\u4e8c\u4e2a\u56fe\u5f62\n        fig = plt.figure(2, figsize=(16, 8))\n        fig.clf()  # \u6e05\u9664\u56fe\u5f62\u5185\u5bb9\u4f46\u4fdd\u7559\u56fe\u5f62\u5b9e\u4f8b\n        \n        # \u7ed8\u5236\u4ef7\u683c\u548c\u79fb\u52a8\u5e73\u5747\u7ebf\n        plt.plot(df.index, df&#91;'close'], label='\u6536\u76d8\u4ef7', linewidth=2)\n        plt.plot(df.index, df&#91;'ma5'], label='MA5', alpha=0.7)\n        plt.plot(df.index, df&#91;'ma10'], label='MA10', alpha=0.7)\n        plt.plot(df.index, df&#91;'ma20'], label='MA20', alpha=0.7)\n        plt.plot(df.index, df&#91;'ma60'], label='MA60', alpha=0.7)\n        \n        # \u5e03\u6797\u5e26\n        plt.plot(df.index, df&#91;'bb_upper'], 'r--', label='\u4e0a\u8f68', alpha=0.6)\n        plt.plot(df.index, df&#91;'bb_middle'], 'g--', label='\u4e2d\u8f68', alpha=0.6)\n        plt.plot(df.index, df&#91;'bb_lower'], 'r--', label='\u4e0b\u8f68', alpha=0.6)\n        plt.fill_between(df.index, df&#91;'bb_upper'], df&#91;'bb_lower'], alpha=0.1, color='gray')\n        \n        # ENE\u8f68\u9053\u7ebf\n        plt.plot(df.index, df&#91;'ene_upper'], 'c-.', label='ENE\u4e0a\u8f68', alpha=0.8)\n        plt.plot(df.index, df&#91;'ene_middle'], 'm-.', label='ENE\u4e2d\u8f68', alpha=0.8)\n        plt.plot(df.index, df&#91;'ene_lower'], 'c-.', label='ENE\u4e0b\u8f68', alpha=0.8)\n        \n        # \u4ea4\u6613\u4fe1\u53f7\n        if 'final_signal' in df.columns:\n            buy_signals = df&#91;df&#91;'final_signal'] == 1]\n            sell_signals = df&#91;df&#91;'final_signal'] == -1]\n            plt.scatter(buy_signals.index, buy_signals&#91;'close'], marker='^', color='g', \n                       label='\u4e70\u5165\u4fe1\u53f7', s=100, zorder=3)\n            plt.scatter(sell_signals.index, sell_signals&#91;'close'], marker='v', color='r', \n                       label='\u5356\u51fa\u4fe1\u53f7', s=100, zorder=3)\n        \n        plt.title(f'{self.stock_code} \u4ef7\u683c\u4e0e\u79fb\u52a8\u5e73\u5747\u7ebf ({self.start_date} \u81f3 {self.end_date})')\n        plt.ylabel('\u4ef7\u683c')\n        plt.grid(True, alpha=0.3)\n        plt.legend()\n        \n        # \u8bbe\u7f6ex\u8f74\u65e5\u671f\u683c\u5f0f\n        plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=3))\n        plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))\n        plt.xticks(rotation=45)\n        \n        plt.tight_layout()\n        plt.show(block=True)  # \u963b\u585e\u663e\u793a\uff0c\u7b49\u5f85\u7528\u6237\u5173\u95ed\n        \n        return fig\n    \n    def plot_all_indicators(self):\n        \"\"\"\u7ed8\u5236\u6240\u6709\u6280\u672f\u6307\u6807\u56fe\u8868\uff08\u4e0d\u542b\u5747\u7ebf\u56fe\uff09\"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\u5e76\u8ba1\u7b97\u6307\u6807\")\n            return None\n        \n        df = self.data.dropna()  # \u53bb\u9664NaN\u503c\n        \n        # \u521b\u5efa\u7b2c\u4e09\u4e2a\u56fe\u5f62\n        fig = plt.figure(3, figsize=(16, 30))\n        fig.clf()  # \u6e05\u9664\u56fe\u5f62\u5185\u5bb9\u4f46\u4fdd\u7559\u56fe\u5f62\u5b9e\u4f8b\n        gs = GridSpec(9, 1, height_ratios=&#91;1, 2, 2, 2, 2, 2, 2, 2, 2])\n        \n        # 1. \u4ef7\u683c\n        ax1 = plt.subplot(gs&#91;0])\n        ax1.plot(df.index, df&#91;'close'], label='\u6536\u76d8\u4ef7', linewidth=2, color='blue')\n        ax1.set_title(f'{self.stock_code} \u4ef7\u683c')\n        ax1.set_ylabel('\u4ef7\u683c')\n        ax1.grid(True, alpha=0.3)\n        \n        # 2. \u6210\u4ea4\u91cf\n        ax2 = plt.subplot(gs&#91;1], sharex=ax1)\n        ax2.bar(df.index, df&#91;'volume'], label='\u6210\u4ea4\u91cf', alpha=0.6, color='gray')\n        ax2.plot(df.index, df&#91;'volume_ma5'], label='\u6210\u4ea4\u91cfMA5', color='blue')\n        ax2.plot(df.index, df&#91;'volume_ma10'], label='\u6210\u4ea4\u91cfMA10', color='red')\n        ax2.set_ylabel('\u6210\u4ea4\u91cf')\n        ax2.legend()\n        ax2.grid(True, alpha=0.3)\n        \n        # 3. RSI\u6307\u6807\n        ax3 = plt.subplot(gs&#91;2], sharex=ax1)\n        ax3.plot(df.index, df&#91;'rsi'], label=f'RSI', color='purple', linewidth=2)\n        ax3.axhline(70, color='r', linestyle='--', alpha=0.7, label='\u8d85\u4e70\u7ebf(70)')\n        ax3.axhline(30, color='g', linestyle='--', alpha=0.7, label='\u8d85\u5356\u7ebf(30)')\n        ax3.axhline(50, color='b', linestyle='--', alpha=0.5)\n        ax3.set_ylabel('RSI\u503c')\n        ax3.set_ylim(0, 100)\n        ax3.legend()\n        ax3.grid(True, alpha=0.3)\n        \n        # 4. MACD\u6307\u6807\n        ax4 = plt.subplot(gs&#91;3], sharex=ax1)\n        ax4.plot(df.index, df&#91;'macd'], label='MACD', color='blue')\n        ax4.plot(df.index, df&#91;'macd_signal'], label='\u4fe1\u53f7\u7ebf', color='red')\n        ax4.bar(df.index, df&#91;'macd_hist'], label='\u67f1\u72b6\u56fe', alpha=0.5, color='gray')\n        ax4.axhline(0, color='black', linestyle='-', alpha=0.3)\n        ax4.set_ylabel('MACD\u503c')\n        ax4.legend()\n        ax4.grid(True, alpha=0.3)\n        \n        # 5. KDJ\u6307\u6807\n        ax5 = plt.subplot(gs&#91;4], sharex=ax1)\n        ax5.plot(df.index, df&#91;'k'], label='K\u7ebf', color='blue')\n        ax5.plot(df.index, df&#91;'d'], label='D\u7ebf', color='red')\n        ax5.plot(df.index, df&#91;'j'], label='J\u7ebf', color='green')\n        ax5.axhline(80, color='r', linestyle='--', alpha=0.7, label='\u8d85\u4e70\u7ebf(80)')\n        ax5.axhline(20, color='g', linestyle='--', alpha=0.7, label='\u8d85\u5356\u7ebf(20)')\n        ax5.set_ylabel('KDJ\u503c')\n        ax5.set_ylim(0, 100)\n        ax5.legend()\n        ax5.grid(True, alpha=0.3)\n        \n        # 6. CCI\u6307\u6807\n        ax6 = plt.subplot(gs&#91;5], sharex=ax1)\n        ax6.plot(df.index, df&#91;'cci'], label='CCI', color='orange', linewidth=2)\n        ax6.axhline(100, color='r', linestyle='--', alpha=0.7, label='\u8d85\u4e70\u7ebf(100)')\n        ax6.axhline(-100, color='g', linestyle='--', alpha=0.7, label='\u8d85\u5356\u7ebf(-100)')\n        ax6.axhline(0, color='b', linestyle='--', alpha=0.5)\n        ax6.set_ylabel('CCI\u503c')\n        ax6.legend()\n        ax6.grid(True, alpha=0.3)\n        \n        # 7. W&amp;R\u548cBIAS\u6307\u6807\n        ax7 = plt.subplot(gs&#91;6], sharex=ax1)\n        \n        # W&amp;R\n        color1 = 'tab:red'\n        ax7.set_ylabel('W&amp;R(%)', color=color1)\n        ax7.plot(df.index, df&#91;'wr'], color=color1, label='W&amp;R')\n        ax7.axhline(-20, color='r', linestyle='--', alpha=0.7, label='\u8d85\u4e70\u7ebf(-20)')\n        ax7.axhline(-80, color='g', linestyle='--', alpha=0.7, label='\u8d85\u5356\u7ebf(-80)')\n        ax7.tick_params(axis='y', labelcolor=color1)\n        ax7.grid(True, alpha=0.3)\n        \n        # BIAS\n        ax8 = ax7.twinx()\n        color2 = 'tab:blue'\n        ax8.set_ylabel('BIAS(%)', color=color2)\n        ax8.plot(df.index, df&#91;'bias'], color=color2, label='BIAS')\n        ax8.tick_params(axis='y', labelcolor=color2)\n        \n        # \u5408\u5e76\u56fe\u4f8b\n        lines1, labels1 = ax7.get_legend_handles_labels()\n        lines2, labels2 = ax8.get_legend_handles_labels()\n        ax7.legend(lines1 + lines2, labels1 + labels2, loc='upper left')\n        \n        # 8. VI\u6307\u6807\n        ax9 = plt.subplot(gs&#91;7], sharex=ax1)\n        ax9.plot(df.index, df&#91;'vi_positive'], 'g-', label='VI+', alpha=0.7)\n        ax9.plot(df.index, df&#91;'vi_negative'], 'r-', label='VI-', alpha=0.7)\n        ax9.set_ylabel('VI\u503c')\n        ax9.legend()\n        ax9.grid(True, alpha=0.3)\n        \n        # 9. OBV\u6307\u6807\n        ax10 = plt.subplot(gs&#91;8], sharex=ax1)\n        ax10.set_ylabel('OBV')\n        ax10.plot(df.index, df&#91;'obv'], color='tab:blue', label='OBV')\n        ax10.tick_params(axis='y')\n        ax10.legend()\n        ax10.grid(True, alpha=0.3)\n        \n        # \u8bbe\u7f6ex\u8f74\u65e5\u671f\u683c\u5f0f\n        ax10.xaxis.set_major_locator(mdates.MonthLocator(interval=3))\n        ax10.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))\n        plt.xticks(rotation=45)\n        \n        plt.suptitle(f'{self.stock_code} \u6280\u672f\u6307\u6807\u5206\u6790 ({self.start_date} \u81f3 {self.end_date})', fontsize=16)\n        plt.tight_layout()\n        plt.subplots_adjust(top=0.97)\n        plt.show(block=True)  # \u963b\u585e\u663e\u793a\uff0c\u7b49\u5f85\u7528\u6237\u5173\u95ed\n        \n        return fig\n    \n    def plot_simplified(self):\n        \"\"\"\u7ed8\u5236\u7b80\u5316\u7248\u56fe\u8868\uff08\u4ef7\u683c\u3001RSI\u3001MACD\uff09\"\"\"\n        if self.data is None:\n            print(\"\u8bf7\u5148\u83b7\u53d6\u80a1\u7968\u6570\u636e\u5e76\u8ba1\u7b97\u6307\u6807\")\n            return None\n        \n        df = self.data.dropna()\n        \n        # \u76f4\u63a5\u521b\u5efa\u65b0\u56fe\u5f62\uff0c\u786e\u4fdd\u662f\u7b2c\u4e00\u4e2a\u663e\u793a\u7684\u56fe\u8868\n        fig = plt.figure(1, figsize=(16, 15))\n        fig.clf()  # \u6e05\u9664\u56fe\u5f62\u5185\u5bb9\u4f46\u4fdd\u7559\u56fe\u5f62\u5b9e\u4f8b\n        ax1 = plt.subplot(3, 1, 1)\n        ax2 = plt.subplot(3, 1, 2, sharex=ax1)\n        ax3 = plt.subplot(3, 1, 3, sharex=ax1)\n        \n        # 1. \u4ef7\u683c\u548c\u5e03\u6797\u5e26\n        ax1.plot(df.index, df&#91;'close'], label='\u6536\u76d8\u4ef7', linewidth=2)\n        ax1.plot(df.index, df&#91;'bb_upper'], 'r--', label='\u4e0a\u8f68')\n        ax1.plot(df.index, df&#91;'bb_middle'], 'g--', label='\u4e2d\u8f68')\n        ax1.plot(df.index, df&#91;'bb_lower'], 'r--', label='\u4e0b\u8f68')\n        ax1.fill_between(df.index, df&#91;'bb_upper'], df&#91;'bb_lower'], alpha=0.1, color='gray')\n        \n        if 'final_signal' in df.columns:\n            buy_signals = df&#91;df&#91;'final_signal'] == 1]\n            sell_signals = df&#91;df&#91;'final_signal'] == -1]\n            ax1.scatter(buy_signals.index, buy_signals&#91;'close'], marker='^', color='g', \n                       label='\u4e70\u5165\u4fe1\u53f7', s=100, zorder=3)\n            ax1.scatter(sell_signals.index, sell_signals&#91;'close'], marker='v', color='r', \n                       label='\u5356\u51fa\u4fe1\u53f7', s=100, zorder=3)\n        \n        ax1.set_title(f'{self.stock_code} \u4ef7\u683c\u4e0e\u5e03\u6797\u5e26')\n        ax1.set_ylabel('\u4ef7\u683c')\n        ax1.legend()\n        ax1.grid(True, alpha=0.3)\n        \n        # 2. RSI\n        ax2.plot(df.index, df&#91;'rsi'], label='RSI', color='purple', linewidth=2)\n        ax2.axhline(70, color='r', linestyle='--', alpha=0.7)\n        ax2.axhline(30, color='g', linestyle='--', alpha=0.7)\n        ax2.axhline(50, color='b', linestyle='--', alpha=0.5)\n        ax2.set_ylabel('RSI\u503c')\n        ax2.set_ylim(0, 100)\n        ax2.legend()\n        ax2.grid(True, alpha=0.3)\n        \n        # 3. MACD\n        ax3.plot(df.index, df&#91;'macd'], label='MACD', color='blue')\n        ax3.plot(df.index, df&#91;'macd_signal'], label='\u4fe1\u53f7\u7ebf', color='red')\n        ax3.bar(df.index, df&#91;'macd_hist'], label='\u67f1\u72b6\u56fe', alpha=0.5)\n        ax3.axhline(0, color='black', linestyle='-', alpha=0.3)\n        ax3.set_ylabel('MACD\u503c')\n        ax3.legend()\n        ax3.grid(True, alpha=0.3)\n        \n        # \u8bbe\u7f6ex\u8f74\u65e5\u671f\u683c\u5f0f\n        ax3.xaxis.set_major_locator(mdates.MonthLocator(interval=3))\n        ax3.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))\n        plt.xticks(rotation=45)\n        \n        plt.tight_layout()\n        plt.show(block=True)  # \u963b\u585e\u663e\u793a\uff0c\u7b49\u5f85\u7528\u6237\u5173\u95ed\n        \n        return fig\n\ndef main():\n    # \u521b\u5efa\u5206\u6790\u5668\u5b9e\u4f8b\n    analyzer = StockTechnicalAnalyzer(data_source='baostock')  # \u53ef\u9009 'baostock' \u6216 'akshare'\n    \n    # \u8bbe\u7f6e\u80a1\u7968\u4ee3\u7801\u548c\u65e5\u671f\u8303\u56f4\n    #stock_code = 'sh.600938'  # \u4e2d\u56fd\u6d77\u6cb9\n    stock_code = 'sz.000651'  # \u683c\u529b\u7535\u5668\n    end_date = datetime.datetime.now().strftime(\"%Y-%m-%d\")\n    start_date = (datetime.datetime.now() - datetime.timedelta(days=365*2)).strftime(\"%Y-%m-%d\")  # \u8fc7\u53bb2\u5e74\n    \n    # \u83b7\u53d6\u6570\u636e\n    data = analyzer.get_stock_data(stock_code, start_date, end_date)\n    if data is None or len(data) &lt; 60:\n        print(\"\u6570\u636e\u4e0d\u8db3\uff0c\u65e0\u6cd5\u8fdb\u884c\u5206\u6790\")\n        return\n    \n    # \u8ba1\u7b97\u6280\u672f\u6307\u6807\n    analyzer.calculate_indicators()\n    \n    # \u751f\u6210\u4ea4\u6613\u4fe1\u53f7\n    analyzer.generate_signals()\n    \n    # \u7ed8\u5236\u7b80\u5316\u56fe\u8868\n    print(\"\u6b63\u5728\u7ed8\u5236\u7b80\u5316\u56fe\u8868...\")\n    analyzer.plot_simplified()\n    \n    # \u5355\u72ec\u7ed8\u5236\u79fb\u52a8\u5e73\u5747\u7ebf\u56fe\u8868\n    print(\"\u6b63\u5728\u7ed8\u5236\u79fb\u52a8\u5e73\u5747\u7ebf\u56fe\u8868...\")\n    analyzer.plot_moving_averages()\n    \n    # \u7ed8\u5236\u5b8c\u6574\u56fe\u8868\uff08\u5305\u542b\u6240\u6709\u6307\u6807\uff0c\u4e0d\u542b\u8be6\u7ec6\u5747\u7ebf\uff09\n    print(\"\u6b63\u5728\u7ed8\u5236\u5b8c\u6574\u6307\u6807\u56fe\u8868...\")\n    analyzer.plot_all_indicators()\n    \n    # \u663e\u793a\u6700\u65b0\u7684\u6307\u6807\u503c\n    latest_data = analyzer.data.iloc&#91;-1]\n    print(f\"\\n\u6700\u65b0\u6307\u6807\u503c\uff08{latest_data.name.date()}\uff09:\")\n    print(f\"RSI(14): {latest_data&#91;'rsi']:.2f}\")\n    print(f\"MACD: {latest_data&#91;'macd']:.4f}, \u4fe1\u53f7\u7ebf: {latest_data&#91;'macd_signal']:.4f}, \u67f1\u72b6\u56fe: {latest_data&#91;'macd_hist']:.4f}\")\n    print(f\"KDJ: K={latest_data&#91;'k']:.2f}, D={latest_data&#91;'d']:.2f}, J={latest_data&#91;'j']:.2f}\")\n    print(f\"ATR\u767e\u5206\u6bd4: {latest_data&#91;'atr_pct']:.2f}%\")\n    print(f\"CCI: {latest_data&#91;'cci']:.2f}\")\n    print(f\"W&amp;R: {latest_data&#91;'wr']:.2f}%\")\n    print(f\"BIAS: {latest_data&#91;'bias']:.2f}%\")\n    print(f\"ROC: {latest_data&#91;'roc']:.2f}%\")\n    print(f\"PSY: {latest_data&#91;'psy']:.2f}%\")\n    print(f\"ENE: \u4e0a\u8f68={latest_data&#91;'ene_upper']:.2f}, \u4e2d\u8f68={latest_data&#91;'ene_middle']:.2f}, \u4e0b\u8f68={latest_data&#91;'ene_lower']:.2f}\")\n    print(f\"VI: VI+={latest_data&#91;'vi_positive']:.2f}, VI-={latest_data&#91;'vi_negative']:.2f}\")\n\nif __name__ == \"__main__\":\n    main()<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>readme.md requirementst.txt \u6e90\u4ee3\u7801\uff1a<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2,24],"tags":[],"class_list":["post-6546","post","type-post","status-publish","format-standard","hentry","category-2","category-24"],"_links":{"self":[{"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/6546","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=6546"}],"version-history":[{"count":1,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/6546\/revisions"}],"predecessor-version":[{"id":6549,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/6546\/revisions\/6549"}],"wp:attachment":[{"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6546"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6546"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/xc.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6546"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}