{"id":5630,"date":"2025-05-21T23:30:20","date_gmt":"2025-05-21T15:30:20","guid":{"rendered":"http:\/\/192.168.1.29\/?p=5630"},"modified":"2025-05-21T23:30:20","modified_gmt":"2025-05-21T15:30:20","slug":"python%e4%bb%a3%e7%a0%81%e7%bb%98%e5%88%b6%e6%9f%90%e5%8f%aa%e8%82%a1%e7%a5%a8%e7%9a%84k%e7%ba%bf%e5%9b%be%e3%80%81%e5%9d%87%e7%ba%bf%e3%80%81%e9%87%8f%e8%83%bd%e5%9b%be","status":"publish","type":"post","link":"http:\/\/g1n29wqq.ipyingshe.net:5347\/?p=5630","title":{"rendered":"python\u4ee3\u7801\u7ed8\u5236\u67d0\u53ea\u80a1\u7968\u7684K\u7ebf\u56fe\u3001\u5747\u7ebf\u3001\u91cf\u80fd\u56fe"},"content":{"rendered":"\n<pre class=\"wp-block-code\"><code>import akshare as ak\nimport matplotlib.pyplot as plt\nimport pandas as pd\nimport mplfinance as mpf\nimport matplotlib.dates as mdates\nimport numpy as np\nimport os\nfrom datetime import datetime, timedelta\n\n# \u8bbe\u7f6e\u4e2d\u6587\u5b57\u4f53\nplt.rcParams&#91;\"font.family\"] = &#91;\"SimHei\",  \"Microsoft YaHei\"]\nplt.rcParams&#91;\"axes.unicode_minus\"] = False  # \u89e3\u51b3\u8d1f\u53f7\u663e\u793a\u95ee\u9898\n\ndef get_stock_data(stock_code, start_date=None, end_date=None, adjust=\"qfq\"):\n    \"\"\"\n    \u4f7f\u7528 AkShare \u83b7\u53d6\u80a1\u7968\u6570\u636e\n    \n    \u53c2\u6570:\n    stock_code (str): \u80a1\u7968\u4ee3\u7801\uff0c\u5982 'sh000001' \u6216 '000001.SZ'\n    start_date (str): \u5f00\u59cb\u65e5\u671f\uff0c\u683c\u5f0f 'YYYYMMDD'\uff0c\u9ed8\u8ba4\u4e3a 3 \u4e2a\u6708\u524d\n    end_date (str): \u7ed3\u675f\u65e5\u671f\uff0c\u683c\u5f0f 'YYYYMMDD'\uff0c\u9ed8\u8ba4\u4e3a\u4eca\u5929\n    adjust (str): \u590d\u6743\u7c7b\u578b\uff0c'qfq' \u4e3a\u524d\u590d\u6743\uff0c'hfq' \u4e3a\u540e\u590d\u6743\uff0cNone \u4e3a\u4e0d\u590d\u6743\n    \n    \u8fd4\u56de:\n    DataFrame: \u5305\u542b\u80a1\u7968\u6570\u636e\u7684 DataFrame\n    \"\"\"\n    # \u5904\u7406\u9ed8\u8ba4\u65e5\u671f\n    if end_date is None:\n        end_date = datetime.now().strftime('%Y%m%d')\n    \n    if start_date is None:\n        start_date = (datetime.now() - timedelta(days=90)).strftime('%Y%m%d')\n    \n    # \u683c\u5f0f\u5316\u80a1\u7968\u4ee3\u7801\u4ee5\u9002\u5e94 AkShare\n    if not stock_code.startswith(('sh', 'sz')):\n        market = 'sh' if stock_code.startswith(('6', '9')) else 'sz'\n        stock_code = f'{market}{stock_code}'\n    \n    try:\n        # \u4f7f\u7528 AkShare \u83b7\u53d6\u80a1\u7968\u65e5\u7ebf\u6570\u636e\n        stock_data = ak.stock_zh_a_hist_tx(symbol=stock_code, \n                                      start_date=start_date, end_date=end_date, \n                                      adjust=adjust)\n        \n        # \u91cd\u547d\u540d\u5217\u4ee5\u7b26\u5408 mplfinance \u8981\u6c42\n        # \u68c0\u67e5\u5e76\u6620\u5c04\u6210\u4ea4\u91cf\u5217\u540d\n        volume_columns = &#91;'\u6210\u4ea4\u91cf', '\u6210\u4ea4\u989d', 'volume','amount']\n        volume_col = next((col for col in volume_columns if col in stock_data.columns), None)\n        \n        if volume_col is None:\n            print(\"\u8b66\u544a: \u6570\u636e\u4e2d\u672a\u627e\u5230\u6210\u4ea4\u91cf\u5217\uff0c\u56fe\u8868\u53ef\u80fd\u4e0d\u5b8c\u6574\")\n            stock_data&#91;'volume'] = 0  # \u6dfb\u52a0\u9ed8\u8ba4\u6210\u4ea4\u91cf\u5217\n        else:\n            stock_data = stock_data.rename(columns={volume_col: 'volume'})\n        \n        # \u91cd\u547d\u540d\u5176\u4ed6\u5fc5\u8981\u5217\n        stock_data = stock_data.rename(columns={\n            '\u65e5\u671f': 'date', '\u5f00\u76d8': 'open', '\u6536\u76d8': 'close', \n            '\u6700\u9ad8': 'high', '\u6700\u4f4e': 'low'\n        })\n        \n        # \u8f6c\u6362\u65e5\u671f\u683c\u5f0f\n        stock_data&#91;'date'] = pd.to_datetime(stock_data&#91;'date'])\n        \n        # \u786e\u4fdd\u6570\u636e\u6309\u65e5\u671f\u6392\u5e8f\n        stock_data = stock_data.sort_values('date')\n        \n        # \u68c0\u67e5\u662f\u5426\u6709\u5fc5\u8981\u7684\u5217\n        required_columns = &#91;'date', 'open', 'high', 'low', 'close', 'volume']\n        missing_columns = &#91;col for col in required_columns if col not in stock_data.columns]\n        \n        if missing_columns:\n            print(f\"\u9519\u8bef: \u6570\u636e\u7f3a\u5c11\u5fc5\u8981\u7684\u5217: {', '.join(missing_columns)}\")\n            return None\n        \n        return stock_data\n    \n    except Exception as e:\n        print(f\"\u83b7\u53d6\u80a1\u7968\u6570\u636e\u65f6\u51fa\u9519: {e}\")\n        return None\n\ndef plot_stock_daily_movement(stock_data, stock_name=\"\u80a1\u7968\", save_path=None, ma_periods=None):\n    \"\"\"\n    \u7ed8\u5236\u80a1\u7968\u65e5\u53d8\u52a8\u60c5\u51b5\uff0c\u5305\u62ec K \u7ebf\u56fe\u548c\u6210\u4ea4\u91cf\u53ca\u5747\u7ebf\n    \n    \u53c2\u6570:\n    stock_data (DataFrame): \u5305\u542b\u80a1\u7968\u6570\u636e\u7684 DataFrame\uff0c\u5fc5\u987b\u5305\u542b 'date', 'open', 'high', 'low', 'close', 'volume' \u5217\n    stock_name (str): \u80a1\u7968\u540d\u79f0\uff0c\u7528\u4e8e\u56fe\u8868\u6807\u9898\n    save_path (str): \u56fe\u8868\u4fdd\u5b58\u8def\u5f84\uff0c\u9ed8\u8ba4\u4e3a None \u4e0d\u4fdd\u5b58\n    ma_periods (list): \u5747\u7ebf\u5468\u671f\u5217\u8868\uff0c\u9ed8\u8ba4\u4e3a &#91;5, 10, 20]\n    \"\"\"\n    if stock_data is None or stock_data.empty:\n        print(\"\u6ca1\u6709\u6570\u636e\u53ef\u7ed8\u5236\u56fe\u8868\")\n        return\n    \n    # \u8bbe\u7f6e\u9ed8\u8ba4\u5747\u7ebf\u5468\u671f\n    if ma_periods is None:\n        ma_periods = &#91;5, 10, 20]\n    \n    # \u590d\u5236\u6570\u636e\u5e76\u51c6\u5907 mplfinance \u6240\u9700\u683c\u5f0f\n    plot_data = stock_data.copy()\n    plot_data = plot_data.set_index('date')\n    \n    # \u6dfb\u52a0\u5747\u7ebf\n    for period in ma_periods:\n        column_name = f'MA{period}'\n        plot_data&#91;column_name] = plot_data&#91;'close'].rolling(window=period).mean()\n    \n    # \u8bbe\u7f6e mplfinance \u6837\u5f0f\n    mc = mpf.make_marketcolors(up='r', down='g', inherit=True)\n    \n    # \u81ea\u5b9a\u4e49\u5747\u7ebf\u989c\u8272\n    ma_colors = &#91;'blue', 'purple', 'orange', 'green', 'red', 'brown', 'gray']\n    ma_colors = ma_colors&#91;:len(ma_periods)]  # \u786e\u4fdd\u989c\u8272\u6570\u91cf\u4e0e\u5747\u7ebf\u5468\u671f\u6570\u91cf\u5339\u914d\n    \n    s = mpf.make_mpf_style(\n        marketcolors=mc, \n        gridstyle='--', \n        y_on_right=False,\n        rc={\n            'font.family': &#91;'SimHei', 'WenQuanYi Micro Hei', 'Heiti TC', 'Microsoft YaHei'],\n            'lines.linewidth': 1.5  # \u589e\u52a0\u5747\u7ebf\u7ebf\u6761\u5bbd\u5ea6\n        }\n    )\n    \n    # \u7ed8\u5236 K \u7ebf\u56fe\u548c\u6210\u4ea4\u91cf\n    fig, axes = mpf.plot(\n        plot_data,\n        type='candle',\n        style=s,\n        title=f'{stock_name} \u65e5\u53d8\u52a8\u60c5\u51b5',\n        ylabel='\u4ef7\u683c (\u5143)',\n        volume=True,\n        ylabel_lower='\u6210\u4ea4\u91cf (\u624b)',\n        mav=tuple(ma_periods),  # \u4f20\u9012\u5747\u7ebf\u5468\u671f\n        returnfig=True,\n        figsize=(14, 10),\n        update_width_config=dict(\n            candle_linewidth=1.2,  # K\u7ebf\u5b9e\u4f53\u8fb9\u6846\u5bbd\u5ea6\n            candle_width=0.6,      # K\u7ebf\u5bbd\u5ea6\n            volume_width=0.8,      # \u6210\u4ea4\u91cf\u67f1\u5bbd\u5ea6\n        )\n    )\n    \n    # \u8c03\u6574\u6807\u9898\u4f4d\u7f6e\n    fig.suptitle(f'{stock_name} \u65e5\u53d8\u52a8\u60c5\u51b5', fontsize=16, y=0.98)\n    \n    # \u6dfb\u52a0\u5747\u7ebf\u56fe\u4f8b\n    ax = axes&#91;0]  # K\u7ebf\u56fe\u7684\u8f74\n    for i, period in enumerate(ma_periods):\n        ax.plot(&#91;], &#91;], color=ma_colors&#91;i], label=f'MA{period}', linewidth=1.5)\n    \n    ax.legend(loc='upper left')\n    \n    # \u5982\u679c\u6307\u5b9a\u4e86\u4fdd\u5b58\u8def\u5f84\uff0c\u5219\u4fdd\u5b58\u56fe\u8868\n    if save_path:\n        save_dir = os.path.dirname(save_path)\n        if not os.path.exists(save_dir):\n            os.makedirs(save_dir)\n        plt.savefig(save_path, dpi=300, bbox_inches='tight')\n        print(f\"\u56fe\u8868\u5df2\u4fdd\u5b58\u81f3: {save_path}\")\n    \n    plt.show()\n\n# \u4e3b\u51fd\u6570\nif __name__ == \"__main__\":\n    # \u7528\u6237\u8f93\u5165\u80a1\u7968\u4ee3\u7801\u548c\u540d\u79f0\n    stock_code = input(\"\u8bf7\u8f93\u5165\u80a1\u7968\u4ee3\u7801\uff08\u4f8b\u5982 000001 \u6216 sh000001\uff09: \").strip()\n    stock_name = input(\"\u8bf7\u8f93\u5165\u80a1\u7968\u540d\u79f0\uff08\u4f8b\u5982 \u5e73\u5b89\u94f6\u884c\uff09: \").strip()\n    \n    # \u83b7\u53d6\u80a1\u7968\u6570\u636e\n    print(\"\u6b63\u5728\u83b7\u53d6\u80a1\u7968\u6570\u636e...\")\n    stock_data = get_stock_data(stock_code)\n    \n    if stock_data is not None and not stock_data.empty:\n        print(f\"\u6210\u529f\u83b7\u53d6 {len(stock_data)} \u5929\u7684\u80a1\u7968\u6570\u636e\")\n        \n        # \u663e\u793a\u6570\u636e\u5217\u540d\uff0c\u7528\u4e8e\u8c03\u8bd5\n        print(f\"\u6570\u636e\u5217\u540d: {', '.join(stock_data.columns)}\")\n        \n        # \u53ef\u9009\uff1a\u81ea\u5b9a\u4e49\u5747\u7ebf\u5468\u671f\n        ma_input = input(\"\u8bf7\u8f93\u5165\u5747\u7ebf\u5468\u671f\uff08\u7528\u9017\u53f7\u5206\u9694\uff0c\u9ed8\u8ba4 5,10,20\uff09: \").strip()\n        if ma_input:\n            try:\n                ma_periods = &#91;int(p.strip()) for p in ma_input.split(',')]\n            except ValueError:\n                print(\"\u65e0\u6548\u7684\u5747\u7ebf\u5468\u671f\uff0c\u4f7f\u7528\u9ed8\u8ba4\u503c\")\n                ma_periods = &#91;5, 10, 20]\n        else:\n            ma_periods = &#91;5, 10, 20]\n        \n        # \u7ed8\u5236\u80a1\u7968\u65e5\u53d8\u52a8\u60c5\u51b5\n        plot_stock_daily_movement(stock_data, stock_name, ma_periods=ma_periods)\n    else:\n        print(\"\u672a\u80fd\u83b7\u53d6\u80a1\u7968\u6570\u636e\uff0c\u8bf7\u68c0\u67e5\u80a1\u7968\u4ee3\u7801\u662f\u5426\u6b63\u786e\")    <\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"777\" src=\"http:\/\/192.168.1.29\/wp-content\/uploads\/2025\/05\/image-1024x777.png\" alt=\"\" class=\"wp-image-5631\" srcset=\"http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1024x777.png 1024w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-300x228.png 300w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-768x583.png 768w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1536x1166.png 1536w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-2048x1554.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">000937\u80a1\u7968\u6700\u8fd190\u5929K\u7ebf\u56fe<\/figcaption><\/figure>\n","protected":false},"excerpt":{"rendered":"","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-5630","post","type-post","status-publish","format-standard","hentry","category-2","category-24"],"_links":{"self":[{"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/5630","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5630"}],"version-history":[{"count":1,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/5630\/revisions"}],"predecessor-version":[{"id":5632,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/5630\/revisions\/5632"}],"wp:attachment":[{"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5630"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5630"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5630"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}