{"id":5642,"date":"2025-05-25T07:05:23","date_gmt":"2025-05-24T23:05:23","guid":{"rendered":"http:\/\/cnliutz.pgrm.cc\/?p=5642"},"modified":"2025-05-25T07:05:23","modified_gmt":"2025-05-24T23:05:23","slug":"python%e8%82%a1%e7%a5%a8%e5%9d%87%e7%ba%bf%e7%b3%bb%e7%bb%9f%e5%ba%94%e7%94%a8","status":"publish","type":"post","link":"http:\/\/g1n29wqq.ipyingshe.net:5347\/?p=5642","title":{"rendered":"python\u80a1\u7968\u5747\u7ebf\u7cfb\u7edf\u5e94\u7528"},"content":{"rendered":"\n<p><strong>\u901a\u9053\u7ebf\u3001\u5747\u7ebf\u4ea4\u53c9\u3001\u845b\u5170\u78a7Granville\u4e70\u5356\u516b\u6cd5\u6807\u6ce8<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#\u80a1\u7968\u5747\u7ebf\u7cfb\u7edf\u5e94\u7528\nimport akshare as ak\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport numpy as np\n\n# \u83b7\u53d6\u80a1\u7968\u6570\u636e\ndef get_stock_data(stock_code, start_date, end_date):\n    stock_df = ak.stock_zh_a_hist(symbol=stock_code, period=\"daily\", start_date=start_date, end_date=end_date, adjust=\"qfq\")\n    stock_df&#91;'\u65e5\u671f'] = pd.to_datetime(stock_df&#91;'\u65e5\u671f'])\n    stock_df.set_index('\u65e5\u671f', inplace=True)\n    return stock_df\n\n# \u8ba1\u7b97\u5747\u7ebf\ndef calculate_ma(data, ma_periods=&#91;5, 10, 20, 30, 60, 120]):\n    for period in ma_periods:\n        data&#91;f'MA{period}'] = data&#91;'\u6536\u76d8'].rolling(window=period).mean()\n    return data\n\n# \u8bc6\u522b\u5747\u7ebf\u591a\u5934\u6392\u5217\ndef identify_bullish_arrangement(data, ma_periods=&#91;5, 10, 20, 30, 60, 120]):\n    ma_columns = &#91;f'MA{period}' for period in ma_periods]\n    data&#91;'bullish_arrangement'] = (data&#91;ma_columns].diff(axis=1).iloc&#91;:, 1:] > 0).all(axis=1)\n    return data\n\n# \u8bc6\u522b\u5747\u7ebf\u4ea4\u53c9\u4fe1\u53f7\ndef identify_ma_cross(data, short_ma=5, long_ma=20):\n    data&#91;'ma_cross'] = np.where(data&#91;f'MA{short_ma}'] > data&#91;f'MA{long_ma}'], 1, -1)\n    data&#91;'ma_cross_signal'] = data&#91;'ma_cross'].diff()\n    return data\n\n# \u8ba1\u7b97\u845b\u5170\u78a7\u516b\u5927\u6cd5\u5219\u4e70\u5356\u70b9\ndef granville_rules(data):\n    close = data&#91;'\u6536\u76d8']\n    ma = data&#91;'MA20']\n    data&#91;'buy_signal'] = 0\n    data&#91;'sell_signal'] = 0\n\n    # \u6cd5\u5219 1: \u5747\u7ebf\u4ece\u4e0b\u964d\u9010\u6e10\u8d70\u5e73\u4e14\u7565\u5411\u4e0a\u65b9\u62ac\u5934\uff0c\u800c\u80a1\u4ef7\u4ece\u5747\u7ebf\u4e0b\u65b9\u5411\u4e0a\u65b9\u7a81\u7834\uff0c\u4e3a\u4e70\u8fdb\u4fe1\u53f7\n    data.loc&#91;(ma.shift(1) &lt; ma) &amp; (close > ma) &amp; (close.shift(1) &lt;= ma.shift(1)), 'buy_signal'] = 1\n    # \u6cd5\u5219 2: \u80a1\u4ef7\u4f4d\u4e8e\u5747\u7ebf\u4e4b\u4e0a\u8fd0\u884c\uff0c\u56de\u6863\u65f6\u672a\u8dcc\u7834\u5747\u7ebf\u540e\u53c8\u518d\u5ea6\u4e0a\u5347\u65f6\u4e3a\u4e70\u8fdb\u65f6\u673a\n    data.loc&#91;(close > ma) &amp; (close.shift(1) &lt; ma) &amp; (close > close.shift(1)), 'buy_signal'] = 1\n    # \u6cd5\u5219 3: \u80a1\u4ef7\u4f4d\u4e8e\u5747\u7ebf\u4e4b\u4e0a\u8fd0\u884c\uff0c\u56de\u6863\u65f6\u8dcc\u7834\u5747\u7ebf\uff0c\u4f46\u77ed\u671f\u5747\u7ebf\u7ee7\u7eed\u5448\u4e0a\u5347\u8d8b\u52bf\uff0c\u6b64\u65f6\u4e3a\u4e70\u8fdb\u65f6\u673a\n    data.loc&#91;(close &lt; ma) &amp; (close.shift(1) > ma) &amp; (ma.shift(1) &lt; ma), 'buy_signal'] = 1\n    # \u6cd5\u5219 4: \u80a1\u4ef7\u4f4d\u4e8e\u5747\u7ebf\u4ee5\u4e0b\u8fd0\u884c\uff0c\u7a81\u7136\u66b4\u8dcc\uff0c\u8ddd\u79bb\u5747\u7ebf\u592a\u8fdc\uff0c\u6781\u6709\u53ef\u80fd\u5411\u5747\u7ebf\u9760\u8fd1(\u7269\u6781\u5fc5\u53cd\uff0c\u4e0b\u8dcc\u53cd\u5f39)\uff0c\u6b64\u65f6\u4e3a\u4e70\u8fdb\u65f6\u673a\n    data.loc&#91;(close &lt; ma) &amp; ((ma - close) \/ ma > 0.1), 'buy_signal'] = 1\n\n    # \u6cd5\u5219 5: \u80a1\u4ef7\u4f4d\u4e8e\u5747\u7ebf\u4e4b\u4e0a\u8fd0\u884c\uff0c\u8fde\u7eed\u6570\u65e5\u5927\u6da8\uff0c\u79bb\u5747\u7ebf\u6108\u6765\u6108\u8fdc\uff0c\u8bf4\u660e\u8fd1\u671f\u5185\u8d2d\u4e70\u80a1\u7968\u8005\u83b7\u5229\u4e30\u539a\uff0c\u968f\u65f6\u90fd\u4f1a\u4ea7\u751f\u83b7\u5229\u56de\u5410\u7684\u5356\u538b\uff0c\u5e94\u6682\u65f6\u5356\u51fa\u6301\u80a1\n    data.loc&#91;(close > ma) &amp; ((close - ma) \/ ma > 0.1), 'sell_signal'] = 1\n    # \u6cd5\u5219 6: \u5747\u7ebf\u4ece\u4e0a\u5347\u9010\u6e10\u8d70\u5e73\uff0c\u800c\u80a1\u4ef7\u4ece\u5747\u7ebf\u4e0a\u65b9\u5411\u4e0b\u8dcc\u7834\u5747\u7ebf\u65f6\u8bf4\u660e\u5356\u538b\u6e10\u91cd\uff0c\u5e94\u5356\u51fa\u6240\u6301\u80a1\u7968\n    data.loc&#91;(ma.shift(1) > ma) &amp; (close &lt; ma) &amp; (close.shift(1) >= ma.shift(1)), 'sell_signal'] = 1\n    # \u6cd5\u5219 7: \u80a1\u4ef7\u4f4d\u4e8e\u5747\u7ebf\u4e0b\u65b9\u8fd0\u884c\uff0c\u53cd\u5f39\u65f6\u672a\u7a81\u7834\u5747\u7ebf\uff0c\u4e14\u5747\u7ebf\u8dcc\u52bf\u51cf\u7f13\uff0c\u8d8b\u4e8e\u6c34\u5e73\u540e\u53c8\u51fa\u73b0\u4e0b\u8dcc\u8d8b\u52bf\uff0c\u6b64\u65f6\u4e3a\u5356\u51fa\u65f6\u673a\n    data.loc&#91;(close &lt; ma) &amp; (close.shift(1) > ma) &amp; (ma.shift(1) > ma), 'sell_signal'] = 1\n    # \u6cd5\u5219 8: \u80a1\u4ef7\u53cd\u5f39\u540e\u5728\u5747\u7ebf\u4e0a\u65b9\u5f98\u5f8a\uff0c\u800c\u5747\u7ebf\u5374\u7ee7\u7eed\u4e0b\u8dcc\uff0c\u5b9c\u5356\u51fa\u6240\u6301\u80a1\u7968\n    data.loc&#91;(close > ma) &amp; (ma.shift(1) > ma), 'sell_signal'] = 1\n\n    return data\n\n# \u8ba1\u7b97\u5747\u7ebf\u901a\u9053\u7b56\u7565\ndef calculate_ma_channel(data, ma_period=20):\n    ma = data&#91;f'MA{ma_period}']\n    std = data&#91;'\u6536\u76d8'].rolling(window=ma_period).std()\n    data&#91;'upper_channel'] = ma + 2 * std\n    data&#91;'lower_channel'] = ma - 2 * std\n    return data\n\n# \u7ed8\u5236\u56fe\u5f62\ndef plot_data(data, ma_periods, stock_code):  # \u6dfb\u52a0 stock_code \u53c2\u6570\n    plt.figure(figsize=(16, 10))\n    plt.plot(data&#91;'\u6536\u76d8'], label='Close Price')  \n    for period in ma_periods:\n        plt.plot(data&#91;f'MA{period}'], label=f'MA{period}')\n    plt.plot(data&#91;'upper_channel'], label='Upper Channel', linestyle='--', color='r')\n    plt.plot(data&#91;'lower_channel'], label='Lower Channel', linestyle='--', color='g')\n\n    # \u7ed8\u5236\u5747\u7ebf\u4ea4\u53c9\u4fe1\u53f7\n    buy_cross = data&#91;data&#91;'ma_cross_signal'] == 2].index\n    sell_cross = data&#91;data&#91;'ma_cross_signal'] == -2].index\n    plt.scatter(buy_cross, data.loc&#91;buy_cross, '\u6536\u76d8'], marker='^', color='g', label='MA Cross Buy Signal')\n    plt.scatter(sell_cross, data.loc&#91;sell_cross, '\u6536\u76d8'], marker='v', color='r', label='MA Cross Sell Signal')\n\n    # \u7ed8\u5236\u845b\u5170\u78a7\u516b\u5927\u6cd5\u5219\u4e70\u5356\u70b9\n    buy_points = data&#91;data&#91;'buy_signal'] == 1].index\n    sell_points = data&#91;data&#91;'sell_signal'] == 1].index\n    plt.scatter(buy_points, data.loc&#91;buy_points, '\u6536\u76d8'], marker='o', color='b', label='Granville Buy Signal')\n    plt.scatter(sell_points, data.loc&#91;sell_points, '\u6536\u76d8'], marker='x', color='m', label='Granville Sell Signal')\n\n    plt.title(f'Stock Price with MA and Channel - Stock Code: {stock_code}')  # \u4fee\u6539\u6807\u9898\n    plt.xlabel('Date')\n    plt.ylabel('Price')\n    plt.legend()\n    plt.grid(True)\n    plt.show()\n\nif __name__ == \"__main__\":\n    stock_code = \"600938\"  # \u8d35\u5dde\u8305\u53f0\n    start_date = \"20241201\"\n    end_date = \"20250523\"\n    ma_periods=&#91;5, 10, 20, 30]\n    # \u83b7\u53d6\u6570\u636e\n    stock_data = get_stock_data(stock_code, start_date, end_date)\n\n    # \u8ba1\u7b97\u5747\u7ebf\n    stock_data = calculate_ma(stock_data)\n\n    # \u8bc6\u522b\u5747\u7ebf\u591a\u5934\u6392\u5217\n    stock_data = identify_bullish_arrangement(stock_data)\n\n    # \u8bc6\u522b\u5747\u7ebf\u4ea4\u53c9\u4fe1\u53f7\n    stock_data = identify_ma_cross(stock_data)\n\n    # \u8ba1\u7b97\u845b\u5170\u78a7\u516b\u5927\u6cd5\u5219\u4e70\u5356\u70b9\n    stock_data = granville_rules(stock_data)\n\n    # \u8ba1\u7b97\u5747\u7ebf\u901a\u9053\u7b56\u7565\n    stock_data = calculate_ma_channel(stock_data)\n\n    # \u7ed8\u5236\u56fe\u5f62\n    plot_data(stock_data, ma_periods, stock_code)  # \u4f20\u9012 stock_code \u53c2\u6570<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"550\" src=\"http:\/\/cnliutz.pgrm.cc\/wp-content\/uploads\/2025\/05\/image-1-1024x550.png\" alt=\"\" class=\"wp-image-5643\" srcset=\"http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1-1024x550.png 1024w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1-300x161.png 300w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1-768x413.png 768w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1-1536x826.png 1536w, http:\/\/g1n29wqq.ipyingshe.net:5347\/wp-content\/uploads\/2025\/05\/image-1-2048x1101.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>\u901a\u9053\u7ebf\u3001\u5747\u7ebf\u4ea4\u53c9\u3001\u845b\u5170\u78a7Granville\u4e70\u5356\u516b\u6cd5\u6807\u6ce8<\/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-5642","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\/5642","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=5642"}],"version-history":[{"count":1,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/5642\/revisions"}],"predecessor-version":[{"id":5644,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=\/wp\/v2\/posts\/5642\/revisions\/5644"}],"wp:attachment":[{"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5642"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5642"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/g1n29wqq.ipyingshe.net:5347\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5642"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}