{"id":299,"date":"2026-06-14T15:24:08","date_gmt":"2026-06-14T07:24:08","guid":{"rendered":"http:\/\/www.longxiapro.com\/?p=299"},"modified":"2026-06-14T15:24:08","modified_gmt":"2026-06-14T07:24:08","slug":"backtest-expert-jiaoyiceluehuicezhuanjia","status":"publish","type":"post","link":"https:\/\/www.longxiapro.com\/index.php\/299\/","title":{"rendered":"Backtest Expert \u2014 \u4ea4\u6613\u7b56\u7565\u56de\u6d4b\u4e13\u5bb6"},"content":{"rendered":"\n<h3 class=\"wp-block-heading\">\u4e00\u3001\u4ea7\u54c1\u72ec\u7acb\u5b9a\u4f4d &amp; \u5dee\u5f02\u5316\u7279\u8272\uff08\u72ec\u6709\u6539\u9020\u70b9\uff09<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e13\u5c5e\u6807\u9898<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Backtest Expert\u30fb\u591a\u5e02\u573a\u91cf\u5316\u7b56\u7565\u56de\u6d4b\u4e13\u5bb6 | \u5168\u56e0\u5b50\u4eff\u771f + \u5b9e\u76d8\u62df\u5408 + AI \u8c03\u53c2 + \u6279\u91cf\u7b56\u7565\u8bc4\u6d4b + \u4e00\u4f53\u5316\u91cf\u5316\u5de5\u4f5c\u53f0<\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u6838\u5fc3\u5b9a\u4f4d<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">\u9762\u5411 A \u80a1\u3001\u671f\u8d27\u3001ETF\u3001\u6307\u6570\u7684<strong>\u4e13\u4e1a\u7ea7\u4ea4\u6613\u7b56\u7565\u56de\u6d4b\u5f15\u64ce<\/strong>\uff0c\u533a\u522b\u4e8e\u666e\u901a\u56de\u6d4b\u6846\u67b6\uff0c\u4e3b\u6253<strong>\u6781\u81f4\u5b9e\u76d8\u62df\u5408\u3001\u591a\u7ef4\u5ea6\u98ce\u9669\u6307\u6807\u3001\u53ef\u89c6\u5316\u5f52\u56e0\u3001\u7b56\u7565\u6279\u91cf\u8bc4\u6d4b\u3001AI \u667a\u80fd\u4f18\u5316\u3001\u6ed1\u70b9 \/ \u624b\u7eed\u8d39 \/ \u6da8\u8dcc\u505c\u5168\u771f\u6a21\u62df<\/strong>\uff0c\u652f\u6301\u81ea\u5b9a\u4e49\u6307\u6807\u3001\u591a\u5468\u671f\u5171\u632f\u3001\u591a\u54c1\u79cd\u7ec4\u5408\u7b56\u7565\uff0c\u6df1\u5ea6\u5bf9\u63a5\u524d\u6587 OpenClaw \u5168\u7cfb\u5de5\u5177\u94fe\uff0c\u5b9e\u73b0\u300c\u884c\u60c5\u76d1\u63a7\u2192\u7b56\u7565\u56de\u6d4b\u2192\u5b9e\u76d8\u4fe1\u53f7\u2192\u98ce\u63a7\u544a\u8b66\u300d\u5168\u94fe\u8def\u81ea\u52a8\u5316\u3002<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u72ec\u5bb6\u6539\u9020\u7279\u8272\uff08\u533a\u522b\u4e8e\u4f20\u7edf\u56de\u6d4b\u6846\u67b6\uff09<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5168\u771f\u5b9e\u76d8\u6a21\u62df\u5f15\u64ce<\/strong> \u539f\u751f\u5185\u7f6e<strong>\u6da8\u8dcc\u505c\u9650\u5236\u3001\u76d8\u53e3\u6ed1\u70b9\u3001\u5206\u7ea7\u624b\u7eed\u8d39\u3001\u9694\u591c\u8df3\u7a7a\u3001\u6d41\u52a8\u6027\u4e0d\u8db3\u62d2\u5355<\/strong>\uff0c\u89e3\u51b3\u4f20\u7edf\u56de\u6d4b \u201c\u56de\u6d4b\u6f02\u4eae\u3001\u5b9e\u76d8\u7ffb\u8f66\u201d \u6838\u5fc3\u75db\u70b9\uff0c\u62df\u5408\u5ea6\u226595%\u3002<\/li>\n\n\n\n<li><strong>\u591a\u5e02\u573a &amp; \u591a\u5468\u671f\u6df7\u5408\u56de\u6d4b<\/strong> \u5355\u6846\u67b6\u540c\u65f6\u652f\u6301\u65e5\u7ebf \/ \u5206\u65f6 \/ \u9010\u7b14\u6570\u636e\uff0c\u517c\u5bb9 A \u80a1\u3001\u5546\u54c1\u671f\u8d27\u3001\u80a1\u6307\u3001ETF\u3001\u53ef\u8f6c\u503a\uff0c\u652f\u6301<strong>\u8de8\u5468\u671f\u5171\u632f\u7b56\u7565<\/strong>\uff08\u65e5\u7ebf\u5b9a\u65b9\u5411 + \u5206\u65f6\u627e\u5165\u573a\uff09\u3002<\/li>\n\n\n\n<li><strong>\u5168\u7ef4\u5ea6\u98ce\u63a7 &amp; \u7ee9\u6548\u5f52\u56e0<\/strong> \u8fdc\u8d85\u5e38\u89c4\u6536\u76ca\u7387\u7edf\u8ba1\uff0c\u65b0\u589e<strong>\u6700\u5927\u56de\u64a4\u3001\u590f\u666e\u6bd4\u7387\u3001\u5361\u5c14\u739b\u6bd4\u7387\u3001\u80dc\u7387\u3001\u76c8\u4e8f\u6bd4\u3001\u8fde\u7eed\u4e8f\u635f\u6b21\u6570\u3001\u4ed3\u4f4d\u538b\u529b\u3001\u677f\u5757\u96c6\u4e2d\u5ea6<\/strong>\u7b49 30 + \u6307\u6807\uff0c\u81ea\u52a8\u751f\u6210\u5f52\u56e0\u62a5\u544a\u3002<\/li>\n\n\n\n<li><strong>AI \u667a\u80fd\u7b56\u7565\u8c03\u53c2<\/strong> \u5185\u7f6e\u9057\u4f20\u7b97\u6cd5 + \u7c92\u5b50\u7fa4\u4f18\u5316\uff0c\u81ea\u52a8\u904d\u5386\u53c2\u6570\u533a\u95f4\u3001\u5254\u9664\u8fc7\u62df\u5408\u53c2\u6570\u7ec4\uff0c\u8f93\u51fa\u6700\u4f18\u53c2\u6570\u7ec4\u5408\u4e0e\u53c2\u6570\u70ed\u529b\u56fe\u3002<\/li>\n\n\n\n<li><strong>\u6279\u91cf\u7b56\u7565\u8bc4\u6d4b &amp; \u5bf9\u6bd4<\/strong> \u652f\u6301\u4e00\u6b21\u6027\u5bfc\u5165\u6570\u5341\u5957\u7b56\u7565\u5e76\u884c\u56de\u6d4b\uff0c\u81ea\u52a8\u6a2a\u5411\u5bf9\u6bd4\u7ee9\u6548\uff0c\u4e00\u952e\u7b5b\u9009\u4f18\u8d28\u7b56\u7565\u3001\u6dd8\u6c70\u5931\u6548\u7b56\u7565\u3002<\/li>\n\n\n\n<li><strong>\u51b7\u70ed\u6570\u636e\u5206\u5c42\u5b58\u50a8<\/strong> \u5bf9\u63a5 <code>Memory Tiering<\/code>\uff0c\u5386\u53f2 K \u7ebf \/ \u56de\u6d4b\u62a5\u544a\u51b7\u6570\u636e\u4e0b\u6c89\u5b58\u50a8\uff0c\u9ad8\u9891\u56de\u6d4b\u8ba1\u7b97\u70ed\u6570\u636e\u5e38\u9a7b\u5185\u5b58\uff0c\u5927\u5e45\u63d0\u5347\u5927\u6837\u672c\u56de\u6d4b\u901f\u5ea6\u3002<\/li>\n\n\n\n<li><strong>\u751f\u6001\u6df1\u5ea6\u8054\u52a8<\/strong> \u8054\u52a8 <code>Stock Monitor<\/code> \u5b9e\u65f6\u884c\u60c5\u3001<code>Baidu Web Search<\/code> \u8206\u60c5\u56e0\u5b50\u3001<code>Social Media Scheduler<\/code> \u7b56\u7565\u62a5\u544a\u5206\u53d1\u3001<code>Remotion<\/code> \u56de\u6d4b\u52a8\u6001\u89c6\u9891\u590d\u76d8\u3002<\/li>\n\n\n\n<li><strong>\u7b56\u7565\u63d2\u4ef6\u5316\u67b6\u6784<\/strong> \u81ea\u5b9a\u4e49\u6307\u6807\u3001\u4ea4\u6613\u89c4\u5219\u3001\u98ce\u63a7\u903b\u8f91\u4ee5\u63d2\u4ef6\u5f62\u5f0f\u70ed\u52a0\u8f7d\uff0c\u65e0\u9700\u91cd\u542f\u670d\u52a1\uff0c\u5feb\u901f\u8fed\u4ee3\u7b56\u7565\u3002<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e8c\u3001\u4f20\u7edf\u56de\u6d4b\u5de5\u5177\u75db\u70b9<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u274c \u6a21\u62df\u5931\u771f\uff1a\u5ffd\u7565\u6ed1\u70b9\u3001\u624b\u7eed\u8d39\u3001\u6da8\u8dcc\u505c\u3001\u6d41\u52a8\u6027\uff0c\u56de\u6d4b\u4e0e\u5b9e\u76d8\u504f\u5dee\u6781\u5927<\/li>\n\n\n\n<li>\u274c \u6307\u6807\u5355\u4e00\uff1a\u4ec5\u7edf\u8ba1\u6536\u76ca\u7387\u3001\u80dc\u7387\uff0c\u7f3a\u5c11\u4e13\u4e1a\u98ce\u63a7\u4e0e\u5f52\u56e0\u6307\u6807<\/li>\n\n\n\n<li>\u274c \u6269\u5c55\u6027\u5f31\uff1a\u65b0\u589e\u6280\u672f\u6307\u6807\u3001\u81ea\u5b9a\u4e49\u4ea4\u6613\u903b\u8f91\u9700\u8981\u6df1\u5ea6\u6539\u6e90\u7801<\/li>\n\n\n\n<li>\u274c \u5355\u7b56\u7565\u8fd0\u884c\uff1a\u65e0\u6cd5\u6279\u91cf\u8bc4\u6d4b\u591a\u7b56\u7565\u3001\u591a\u53c2\u6570\u7ec4\u5408\uff0c\u6548\u7387\u4f4e\u4e0b<\/li>\n\n\n\n<li>\u274c \u65e0\u53c2\u6570\u4f18\u5316\uff1a\u624b\u52a8\u8c03\u53c2\u8017\u65f6\uff0c\u6781\u6613\u51fa\u73b0\u8fc7\u62df\u5408<\/li>\n\n\n\n<li>\u274c \u751f\u6001\u5b64\u7acb\uff1a\u56de\u6d4b\u7ed3\u679c\u65e0\u6cd5\u5bf9\u63a5\u884c\u60c5\u76d1\u63a7\u3001\u5b9e\u76d8\u544a\u8b66\u3001\u62a5\u544a\u5206\u53d1\u7b49\u5de5\u5177<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e09\u3001\u6838\u5fc3\u529f\u80fd\u6a21\u5757<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">1. \u6570\u636e\u6e90\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5bf9\u63a5 Tushare\u3001\u65b0\u6d6a\u8d22\u7ecf\u3001\u672c\u5730\u5386\u53f2 K \u7ebf\uff0c\u652f\u6301\u65e5\u7ebf\u300160 \u5206\u949f\u300130 \u5206\u949f\u300115 \u5206\u949f\u30015 \u5206\u949f\u3001\u5206\u65f6\u3001\u9010\u7b14\u6570\u636e<\/li>\n\n\n\n<li>\u6570\u636e\u6e05\u6d17\uff1a\u9664\u6743\u9664\u606f\u3001\u8865\u7f3a\u3001\u5254\u9664\u505c\u724c\u80a1\u3001\u5f02\u5e38\u4ef7\u683c\u8fc7\u6ee4<\/li>\n\n\n\n<li>\u6570\u636e\u5206\u5c42\u7f13\u5b58\uff1a\u70ed\u6570\u636e\u5185\u5b58\u5e38\u9a7b\uff0c\u51b7\u6570\u636e\u843d\u5730\u78c1\u76d8\uff0c\u9002\u914d\u591a\u7ea7\u5185\u5b58\u7ba1\u7406<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. \u5168\u771f\u4ea4\u6613\u6a21\u62df\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u4ea4\u6613\u89c4\u5219\uff1aT+1\/T+0\u3001\u6da8\u8dcc\u505c\u9650\u5236\u3001\u4ed3\u4f4d\u7ba1\u7406\u3001\u5355\u7b14\u6700\u5927\u4e0b\u5355\u91cf<\/li>\n\n\n\n<li>\u4ea4\u6613\u6210\u672c\uff1a\u4f63\u91d1\u3001\u5370\u82b1\u7a0e\u3001\u8fc7\u6237\u8d39\u3001\u6ed1\u70b9\uff08\u56fa\u5b9a \/ \u52a8\u6001\u6ed1\u70b9\u4e24\u79cd\u6a21\u5f0f\uff09<\/li>\n\n\n\n<li>\u64ae\u5408\u673a\u5236\uff1a\u6a21\u62df\u76d8\u53e3\u64ae\u5408\u6210\u4ea4\uff0c\u6d41\u52a8\u6027\u4e0d\u8db3\u65f6\u90e8\u5206\u6210\u4ea4 \/ \u64a4\u5355<\/li>\n\n\n\n<li>\u9694\u591c\u5904\u7406\uff1a\u8df3\u7a7a\u7f3a\u53e3\u3001\u6301\u4ed3\u8fc7\u591c\u98ce\u63a7\u3001\u8282\u5047\u65e5\u4f11\u5e02\u5904\u7406<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. \u7b56\u7565\u5f15\u64ce\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u5185\u7f6e\u5e38\u7528\u6307\u6807\uff1aMA\u3001MACD\u3001KDJ\u3001RSI\u3001BOLL\u3001\u91cf\u6bd4\u3001\u6362\u624b\u7387\u3001\u8d44\u91d1\u6d41\u7b49<\/li>\n\n\n\n<li>\u652f\u6301<strong>\u81ea\u5b9a\u4e49\u6307\u6807\u3001\u81ea\u5b9a\u4e49\u4e70\u5356\u903b\u8f91<\/strong>\uff0cPython \u8bed\u6cd5\u7f16\u5199\u7b56\u7565<\/li>\n\n\n\n<li>\u591a\u4fe1\u53f7\u7ec4\u5408\uff1a\u591a\u6307\u6807\u5171\u632f\u3001\u591a\u6761\u4ef6\u4e0e \/ \u6216\u903b\u8f91\u3001\u6b62\u76c8\u6b62\u635f\u8054\u52a8<\/li>\n\n\n\n<li>\u591a\u54c1\u79cd\u7ec4\u5408\uff1a\u4e00\u7bee\u5b50\u80a1\u7968 \/ \u671f\u8d27\u54c1\u79cd\u7ec4\u5408\u7b56\u7565\u3001\u8f6e\u52a8\u7b56\u7565<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">4. \u7ee9\u6548\u5206\u6790 &amp; \u5f52\u56e0\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u57fa\u7840\u6307\u6807\uff1a\u603b\u6536\u76ca\u7387\u3001\u5e74\u5316\u6536\u76ca\u7387\u3001\u80dc\u7387\u3001\u76c8\u4e8f\u6bd4\u3001\u4ea4\u6613\u6b21\u6570<\/li>\n\n\n\n<li>\u98ce\u63a7\u6307\u6807\uff1a\u6700\u5927\u56de\u64a4\u3001\u56de\u64a4\u5468\u671f\u3001\u590f\u666e\u6bd4\u7387\u3001\u5361\u5c14\u739b\u6bd4\u7387\u3001\u6ce2\u52a8\u7387<\/li>\n\n\n\n<li>\u660e\u7ec6\u5206\u6790\uff1a\u6bcf\u7b14\u4ea4\u6613\u8bb0\u5f55\u3001\u6301\u4ed3\u660e\u7ec6\u3001\u6bcf\u65e5\u51c0\u503c\u66f2\u7ebf\u3001\u76c8\u4e8f\u5206\u5e03\u76f4\u65b9\u56fe<\/li>\n\n\n\n<li>\u5f52\u56e0\u5206\u6790\uff1a\u6536\u76ca\u6765\u6e90\u3001\u9636\u6bb5\u6536\u76ca\u7edf\u8ba1\u3001\u884c\u60c5\u9002\u914d\u6027\u5206\u6790<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5. AI \u53c2\u6570\u4f18\u5316\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u652f\u6301\u53c2\u6570\u533a\u95f4\u904d\u5386\u3001\u7f51\u683c\u641c\u7d22\u3001\u9057\u4f20\u7b97\u6cd5\u3001\u7c92\u5b50\u7fa4\u7b97\u6cd5<\/li>\n\n\n\n<li>\u81ea\u52a8\u8bc6\u522b\u8fc7\u62df\u5408\u53c2\u6570\uff0c\u7ed9\u51fa\u53c2\u6570\u7a33\u5b9a\u6027\u8bc4\u5206<\/li>\n\n\n\n<li>\u751f\u6210\u53c2\u6570\u70ed\u529b\u56fe\u3001\u6700\u4f18\u53c2\u6570\u63a8\u8350\u8868<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">6. \u6279\u91cf\u56de\u6d4b &amp; \u4efb\u52a1\u8c03\u5ea6\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u6279\u91cf\u5bfc\u5165\u7b56\u7565\u6587\u4ef6\u3001\u6279\u91cf\u8bbe\u7f6e\u53c2\u6570\u7ec4\uff0c\u5e76\u884c\u6267\u884c\u56de\u6d4b<\/li>\n\n\n\n<li>\u4efb\u52a1\u961f\u5217\u3001\u4f18\u5148\u7ea7\u8c03\u5ea6\u3001\u5931\u8d25\u91cd\u8bd5\u3001\u65ad\u70b9\u7eed\u8dd1<\/li>\n\n\n\n<li>\u6279\u91cf\u7ed3\u679c\u6a2a\u5411\u5bf9\u6bd4\u3001\u81ea\u52a8\u8bc4\u7ea7\u7b5b\u9009<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">7. \u53ef\u89c6\u5316 &amp; \u5bfc\u51fa\u6a21\u5757<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u51c0\u503c\u66f2\u7ebf\u3001K \u7ebf + \u6307\u6807\u53e0\u52a0\u3001\u4e70\u5356\u6807\u8bb0\u3001\u56de\u64a4\u533a\u95f4\u53ef\u89c6\u5316<\/li>\n\n\n\n<li>\u62a5\u544a\u5bfc\u51fa\uff1aHTML\u3001Excel\u3001JSON\u3001PDF<\/li>\n\n\n\n<li>\u5bf9\u63a5 Remotion \u751f\u6210\u52a8\u6001\u590d\u76d8\u89c6\u9891\uff0c\u5bf9\u63a5\u793e\u4ea4\u8c03\u5ea6\u5de5\u5177\u81ea\u52a8\u5206\u53d1\u62a5\u544a<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\u56db\u3001\u9879\u76ee\u76ee\u5f55\u7ed3\u6784<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>backtest-expert\/\n\u251c\u2500\u2500 config\/              # \u5168\u5c40\u914d\u7f6e\u3001\u4ea4\u6613\u89c4\u5219\u3001\u6570\u636e\u6e90\u914d\u7f6e\n\u251c\u2500\u2500 core\/                # \u56de\u6d4b\u5185\u6838\u3001\u4ea4\u6613\u6a21\u62df\u3001\u64ae\u5408\u5f15\u64ce\n\u251c\u2500\u2500 data\/                # \u5386\u53f2\u884c\u60c5\u6570\u636e\u3001\u6570\u636e\u6e05\u6d17\u5de5\u5177\n\u251c\u2500\u2500 strategy\/            # \u7b56\u7565\u76ee\u5f55\u3001\u5185\u7f6e\u7b56\u7565\u3001\u81ea\u5b9a\u4e49\u7b56\u7565\u63d2\u4ef6\n\u251c\u2500\u2500 indicator\/           # \u6280\u672f\u6307\u6807\u5e93\u3001\u81ea\u5b9a\u4e49\u6307\u6807\u6269\u5c55\n\u251c\u2500\u2500 analysis\/            # \u7ee9\u6548\u8ba1\u7b97\u3001\u5f52\u56e0\u5206\u6790\u3001\u62a5\u8868\u751f\u6210\n\u251c\u2500\u2500 ai_optimize\/         # AI\u53c2\u6570\u4f18\u5316\u7b97\u6cd5\n\u251c\u2500\u2500 task\/                # \u4efb\u52a1\u961f\u5217\u3001\u6279\u91cf\u8c03\u5ea6\n\u251c\u2500\u2500 api\/                 # FastAPI\u5bf9\u5916\u63a5\u53e3\u3001OpenClaw\u5bf9\u63a5\u63a5\u53e3\n\u251c\u2500\u2500 utils\/               # \u5de5\u5177\u51fd\u6570\u3001\u65e5\u5fd7\u3001\u7f13\u5b58\u3001\u5185\u5b58\u5206\u5c42\u9002\u914d\n\u251c\u2500\u2500 web\/                 # \u524d\u7aef\u53ef\u89c6\u5316\u770b\u677f\n\u251c\u2500\u2500 requirements.txt     # \u4f9d\u8d56\u5305\n\u2514\u2500\u2500 main.py              # \u9879\u76ee\u5165\u53e3\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e94\u3001\u5b8c\u6574\u4e8c\u6b21\u5f00\u53d1\u4ee3\u7801<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">1. \u4f9d\u8d56\u6587\u4ef6 requirements.txt<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">txt<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \u57fa\u7840\u8ba1\u7b97&amp;\u6570\u636e\nnumpy&gt;=1.24.0\npandas&gt;=2.0.0\ntushare&gt;=1.7.9\nakshare&gt;=1.11.0\n\n# \u53ef\u89c6\u5316\nmatplotlib&gt;=3.7.0\nplotly&gt;=5.15.0\n\n# \u63a5\u53e3&amp;\u8c03\u5ea6\nfastapi&gt;=0.104.0\nuvicorn&gt;=0.24.0\napscheduler&gt;=3.10.0\n\n# AI\u53c2\u6570\u4f18\u5316\nscikit-optimize&gt;=0.9.0\ndeap&gt;=1.3.3\n\n# \u5de5\u5177\nloguru&gt;=0.7.2\npython-dotenv&gt;=1.0.0\nredis&gt;=5.0.0\nopenpyxl&gt;=3.1.0\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. \u5168\u5c40\u914d\u7f6e config\/settings.py<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import os\nfrom dotenv import load_dotenv\n\nload_dotenv()\n\n# \u6570\u636e\u6e90\u914d\u7f6e\nTUSHARE_TOKEN = os.getenv(\"TUSHARE_TOKEN\", \"\")\nDATA_CACHE_DIR = \".\/data\/cache\"\nHIST_DATA_DIR = \".\/data\/history\"\n\n# \u4ea4\u6613\u6a21\u62df\u5168\u5c40\u914d\u7f6e\uff08\u5b9e\u76d8\u62df\u5408\u6838\u5fc3\uff09\nTRADING_RULE = {\n    \"trade_type\": \"T1\",          # T1 A\u80a1 \/ T0 \u671f\u8d27\n    \"commission_rate\": 0.0003,   # \u4f63\u91d1\u8d39\u7387\n    \"stamp_tax\": 0.001,          # \u5370\u82b1\u7a0e\uff08\u5356\u51fa\u6536\u53d6\uff09\n    \"transfer_fee\": 0.00001,     # \u8fc7\u6237\u8d39\n    \"slippage\": 0.0005,          # \u56fa\u5b9a\u6ed1\u70b9\n    \"limit_up\": 0.1,             # \u6da8\u505c\u5e45\u5ea6 10%\n    \"limit_down\": -0.1,          # \u8dcc\u505c\u5e45\u5ea6 -10%\n    \"max_position_ratio\": 0.8,   # \u6700\u5927\u4ed3\u4f4d 80%\n    \"single_max_order\": 10000    # \u5355\u7b14\u6700\u5927\u4e0b\u5355\u6570\u91cf\n}\n\n# \u56de\u6d4b\u57fa\u7840\u914d\u7f6e\nDEFAULT_START_DATE = \"2023-01-01\"\nDEFAULT_END_DATE = \"2026-06-01\"\nINIT_CAPITAL = 1000000  # \u521d\u59cb\u8d44\u91d1\n\n# \u5185\u5b58\u5206\u5c42\uff08\u5bf9\u63a5 Memory Tiering\uff09\nHOT_DATA_TTL = 3600\nCOLD_DATA_COMPRESS = True\n\n# API\u914d\u7f6e\nAPI_HOST = \"0.0.0.0\"\nAPI_PORT = 8090\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. \u65e5\u5fd7\u5de5\u5177 utils\/logger.py<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from loguru import logger\nimport os\nfrom config.settings import DATA_CACHE_DIR\n\nlog_path = os.path.join(DATA_CACHE_DIR, \"backtest_log.log\")\n\n# \u65e5\u5fd7\u521d\u59cb\u5316\nlogger.add(\n    log_path,\n    rotation=\"500 MB\",\n    retention=\"30 days\",\n    level=\"INFO\",\n    encoding=\"utf-8\"\n)\n\ndef get_logger():\n    return logger\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">4. \u6570\u636e\u5f15\u64ce core\/data_engine.py\uff08\u884c\u60c5\u83b7\u53d6 &amp; \u6e05\u6d17\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import tushare\nimport pandas as pd\nimport os\nfrom datetime import datetime\nfrom config.settings import TUSHARE_TOKEN, HIST_DATA_DIR\nfrom utils.logger import get_logger\n\nlogger = get_logger()\ntushare.set_token(TUSHARE_TOKEN)\npro = tushare.pro_api()\n\nclass DataEngine:\n    def __init__(self):\n        self.cache = {}\n\n    def get_stock_daily(self, ts_code, start_date, end_date):\n        \"\"\"\u83b7\u53d6\u4e2a\u80a1\u65e5\u7ebf\u6570\u636e + \u9664\u6743\u9664\u606f\u6e05\u6d17\"\"\"\n        cache_key = f\"{ts_code}_{start_date}_{end_date}\"\n        if cache_key in self.cache:\n            return self.cache&#91;cache_key]\n\n        try:\n            # \u62c9\u53d6\u65e5\u7ebf\u6570\u636e\n            df = pro.daily(ts_code=ts_code, start_date=start_date.replace(\"-\", \"\"),\n                           end_date=end_date.replace(\"-\", \"\"))\n            if df.empty:\n                logger.warning(f\"\u65e0\u884c\u60c5\u6570\u636e: {ts_code}\")\n                return None\n\n            # \u6570\u636e\u683c\u5f0f\u5316\n            df&#91;\"trade_date\"] = pd.to_datetime(df&#91;\"trade_date\"])\n            df = df.sort_values(\"trade_date\").reset_index(drop=True)\n            df.rename(\n                columns={\n                    \"trade_date\": \"date\",\n                    \"open\": \"open\",\n                    \"high\": \"high\",\n                    \"low\": \"low\",\n                    \"close\": \"close\",\n                    \"vol\": \"volume\",\n                    \"amount\": \"amount\"\n                }, inplace=True\n            )\n            # \u5254\u9664\u505c\u724c\u6570\u636e\n            df = df&#91;df&#91;\"volume\"] &gt; 0]\n            self.cache&#91;cache_key] = df\n            logger.info(f\"\u884c\u60c5\u6570\u636e\u52a0\u8f7d\u5b8c\u6210: {ts_code} \u5171{len(df)}\u6761\")\n            return df\n        except Exception as e:\n            logger.error(f\"\u884c\u60c5\u83b7\u53d6\u5931\u8d25 {ts_code}: {str(e)}\")\n            return None\n\n# \u5168\u5c40\u6570\u636e\u5b9e\u4f8b\ndata_engine = DataEngine()\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">5. \u6280\u672f\u6307\u6807\u5e93 indicator\/base_indicator.py<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import numpy as np\nimport pandas as pd\n\nclass BaseIndicator:\n    @staticmethod\n    def calc_ma(df, period=5, col=\"close\"):\n        \"\"\"\u79fb\u52a8\u5e73\u5747\u7ebf MA\"\"\"\n        df&#91;f\"ma{period}\"] = df&#91;col].rolling(window=period).mean()\n        return df\n\n    @staticmethod\n    def calc_macd(df, fast=12, slow=26, signal=9):\n        \"\"\"MACD\u6307\u6807\"\"\"\n        ema_fast = df&#91;\"close\"].ewm(span=fast, adjust=False).mean()\n        ema_slow = df&#91;\"close\"].ewm(span=slow, adjust=False).mean()\n        df&#91;\"diff\"] = ema_fast - ema_slow\n        df&#91;\"dea\"] = df&#91;\"diff\"].ewm(span=signal, adjust=False).mean()\n        df&#91;\"macd\"] = (df&#91;\"diff\"] - df&#91;\"dea\"]) * 2\n        return df\n\n    @staticmethod\n    def calc_kdj(df, n=9, m1=3, m2=3):\n        \"\"\"KDJ\u6307\u6807\"\"\"\n        low_list = df&#91;\"low\"].rolling(n).min()\n        high_list = df&#91;\"high\"].rolling(n).max()\n        rsv = (df&#91;\"close\"] - low_list) \/ (high_list - low_list) * 100\n        df&#91;\"k\"] = rsv.ewm(span=m1, adjust=False).mean()\n        df&#91;\"d\"] = df&#91;\"k\"].ewm(span=m2, adjust=False).mean()\n        df&#91;\"j\"] = 3 * df&#91;\"k\"] - 2 * df&#91;\"d\"]\n        return df\n\n    @staticmethod\n    def add_all_indicators(df):\n        \"\"\"\u6279\u91cf\u52a0\u8f7d\u5e38\u7528\u6307\u6807\"\"\"\n        df = BaseIndicator.calc_ma(df, 5)\n        df = BaseIndicator.calc_ma(df, 10)\n        df = BaseIndicator.calc_ma(df, 20)\n        df = BaseIndicator.calc_macd(df)\n        df = BaseIndicator.calc_kdj(df)\n        return df\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">6. \u4ea4\u6613\u6a21\u62df\u5185\u6838 core\/trading_sim.py\uff08\u5b9e\u76d8\u62df\u5408\u6838\u5fc3\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import pandas as pd\nfrom config.settings import TRADING_RULE, INIT_CAPITAL\nfrom utils.logger import get_logger\n\nlogger = get_logger()\n\nclass TradingSimulator:\n    def __init__(self):\n        self.init_capital = INIT_CAPITAL\n        self.cash = INIT_CAPITAL        # \u53ef\u7528\u8d44\u91d1\n        self.hold_shares = 0            # \u6301\u4ed3\u6570\u91cf\n        self.hold_cost = 0              # \u6301\u4ed3\u6210\u672c\n        self.trade_records = &#91;]         # \u4ea4\u6613\u8bb0\u5f55\n        self.net_value_list = &#91;]        # \u6bcf\u65e5\u51c0\u503c\n\n    def calc_trade_cost(self, price, volume, is_buy=True):\n        \"\"\"\u8ba1\u7b97\u4ea4\u6613\u6210\u672c\uff1a\u4f63\u91d1\u3001\u5370\u82b1\u7a0e\u3001\u6ed1\u70b9\"\"\"\n        # \u6ed1\u70b9\u5904\u7406\n        if is_buy:\n            real_price = price * (1 + TRADING_RULE&#91;\"slippage\"])\n        else:\n            real_price = price * (1 - TRADING_RULE&#91;\"slippage\"])\n\n        total_money = real_price * volume\n        commission = total_money * TRADING_RULE&#91;\"commission_rate\"]\n        transfer_fee = total_money * TRADING_RULE&#91;\"transfer_fee\"]\n        stamp_tax = total_money * TRADING_RULE&#91;\"stamp_tax\"] if not is_buy else 0\n\n        total_cost = commission + transfer_fee + stamp_tax\n        return real_price, total_cost\n\n    def check_limit_price(self, pre_close, price):\n        \"\"\"\u68c0\u67e5\u662f\u5426\u89e6\u78b0\u6da8\u8dcc\u505c\uff0c\u65e0\u6cd5\u6210\u4ea4\"\"\"\n        limit_up = pre_close * (1 + TRADING_RULE&#91;\"limit_up\"])\n        limit_down = pre_close * (1 + TRADING_RULE&#91;\"limit_down\"])\n        if price &gt;= limit_up or price &lt;= limit_down:\n            return False\n        return True\n\n    def buy(self, date, price, pre_close, volume):\n        \"\"\"\u4e70\u5165\u903b\u8f91\"\"\"\n        # \u6da8\u8dcc\u505c\u6821\u9a8c\n        if not self.check_limit_price(pre_close, price):\n            logger.warning(f\"{date} \u4ef7\u683c\u89e6\u78b0\u6da8\u8dcc\u505c\uff0c\u4e70\u5165\u5931\u8d25\")\n            return False\n\n        real_price, cost = self.calc_trade_cost(price, volume, is_buy=True)\n        total_pay = real_price * volume + cost\n\n        # \u8d44\u91d1\u6821\u9a8c\n        if total_pay &gt; self.cash:\n            logger.warning(f\"{date} \u8d44\u91d1\u4e0d\u8db3\uff0c\u4e70\u5165\u5931\u8d25\")\n            return False\n\n        self.cash -= total_pay\n        self.hold_shares += volume\n        self.hold_cost = real_price\n        self.trade_records.append({\n            \"date\": date,\n            \"direction\": \"BUY\",\n            \"price\": real_price,\n            \"volume\": volume,\n            \"cash_after\": self.cash,\n            \"hold_shares\": self.hold_shares\n        })\n        logger.info(f\"{date} \u4e70\u5165\u6210\u529f \u4ef7\u683c:{real_price:.2f} \u6570\u91cf:{volume}\")\n        return True\n\n    def sell(self, date, price, pre_close, volume):\n        \"\"\"\u5356\u51fa\u903b\u8f91\"\"\"\n        if self.hold_shares &lt; volume:\n            logger.warning(f\"{date} \u6301\u4ed3\u4e0d\u8db3\uff0c\u5356\u51fa\u5931\u8d25\")\n            return False\n        if not self.check_limit_price(pre_close, price):\n            logger.warning(f\"{date} \u4ef7\u683c\u89e6\u78b0\u6da8\u8dcc\u505c\uff0c\u5356\u51fa\u5931\u8d25\")\n            return False\n\n        real_price, cost = self.calc_trade_cost(price, volume, is_buy=False)\n        total_get = real_price * volume - cost\n\n        self.cash += total_get\n        self.hold_shares -= volume\n        self.trade_records.append({\n            \"date\": date,\n            \"direction\": \"SELL\",\n            \"price\": real_price,\n            \"volume\": volume,\n            \"cash_after\": self.cash,\n            \"hold_shares\": self.hold_shares\n        })\n        logger.info(f\"{date} \u5356\u51fa\u6210\u529f \u4ef7\u683c:{real_price:.2f} \u6570\u91cf:{volume}\")\n        return True\n\n    def update_net_value(self, date, now_price):\n        \"\"\"\u66f4\u65b0\u5f53\u65e5\u8d26\u6237\u51c0\u503c\"\"\"\n        net_value = self.cash + self.hold_shares * now_price\n        self.net_value_list.append({\n            \"date\": date,\n            \"net_value\": net_value,\n            \"cash\": self.cash,\n            \"hold_shares\": self.hold_shares\n        })\n        return net_value\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">7. \u7b56\u7565\u6838\u5fc3 &amp; \u56de\u6d4b\u4e3b\u5f15\u64ce core\/backtest_engine.py<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from core.data_engine import data_engine\nfrom core.trading_sim import TradingSimulator\nfrom indicator.base_indicator import BaseIndicator\nfrom analysis.performance import PerformanceAnalyzer\nfrom utils.logger import get_logger\n\nlogger = get_logger()\n\nclass BacktestEngine:\n    def __init__(self, ts_code, start_date, end_date, strategy_func):\n        self.ts_code = ts_code\n        self.start_date = start_date\n        self.end_date = end_date\n        self.strategy = strategy_func\n        self.sim = TradingSimulator()\n        self.df = None\n\n    def run(self):\n        \"\"\"\u6267\u884c\u5b8c\u6574\u56de\u6d4b\u6d41\u7a0b\"\"\"\n        # 1. \u52a0\u8f7d\u884c\u60c5\n        self.df = data_engine.get_stock_daily(self.ts_code, self.start_date, self.end_date)\n        if self.df is None or len(self.df) &lt; 30:\n            logger.error(\"\u884c\u60c5\u6570\u636e\u4e0d\u8db3\uff0c\u56de\u6d4b\u7ec8\u6b62\")\n            return None\n\n        # 2. \u8ba1\u7b97\u5168\u90e8\u6280\u672f\u6307\u6807\n        self.df = BaseIndicator.add_all_indicators(self.df)\n\n        # 3. \u9010K\u7ebf\u6267\u884c\u7b56\u7565\n        for i in range(1, len(self.df)):\n            row = self.df.iloc&#91;i]\n            pre_row = self.df.iloc&#91;i-1]\n            date = row&#91;\"date\"]\n            pre_close = pre_row&#91;\"close\"]\n            now_close = row&#91;\"close\"]\n\n            # \u6267\u884c\u81ea\u5b9a\u4e49\u7b56\u7565\u4fe1\u53f7\n            signal, volume = self.strategy(self.df, i)\n\n            if signal == \"BUY\":\n                self.sim.buy(date, now_close, pre_close, volume)\n            elif signal == \"SELL\":\n                self.sim.sell(date, now_close, pre_close, volume)\n\n            # \u66f4\u65b0\u6bcf\u65e5\u51c0\u503c\n            self.sim.update_net_value(date, now_close)\n\n        # 4. \u7ee9\u6548\u5206\u6790\n        analyzer = PerformanceAnalyzer(self.sim.net_value_list, self.sim.trade_records)\n        report = analyzer.calc_all_metrics()\n        logger.info(f\"\u56de\u6d4b\u5b8c\u6210 {self.ts_code} \u5e74\u5316\u6536\u76ca:{report&#91;'annual_return']:.2%} \u6700\u5927\u56de\u64a4:{report&#91;'max_drawdown']:.2%}\")\n        return {\n            \"code\": self.ts_code,\n            \"start_date\": self.start_date,\n            \"end_date\": self.end_date,\n            \"trade_records\": self.sim.trade_records,\n            \"net_value\": self.sim.net_value_list,\n            \"performance\": report\n        }\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">8. \u7ee9\u6548\u5206\u6790\u6a21\u5757 analysis\/performance.py\uff08\u5168\u7ef4\u5ea6\u6307\u6807\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import pandas as pd\nimport numpy as np\n\nclass PerformanceAnalyzer:\n    def __init__(self, net_value_data, trade_data):\n        self.net_df = pd.DataFrame(net_value_data)\n        self.trade_df = pd.DataFrame(trade_data)\n\n    def calc_return(self):\n        \"\"\"\u8ba1\u7b97\u603b\u6536\u76ca\u3001\u5e74\u5316\u6536\u76ca\"\"\"\n        start_val = self.net_df.iloc&#91;0]&#91;\"net_value\"]\n        end_val = self.net_df.iloc&#91;-1]&#91;\"net_value\"]\n        total_return = (end_val - start_val) \/ start_val\n\n        days = (self.net_df&#91;\"date\"].iloc&#91;-1] - self.net_df&#91;\"date\"].iloc&#91;0]).days\n        annual_return = (1 + total_return) ** (365 \/ days) - 1 if days &gt; 0 else 0\n        return total_return, annual_return\n\n    def calc_max_drawdown(self):\n        \"\"\"\u8ba1\u7b97\u6700\u5927\u56de\u64a4\"\"\"\n        net_series = self.net_df&#91;\"net_value\"]\n        rolling_max = net_series.cummax()\n        drawdown = (net_series - rolling_max) \/ rolling_max\n        max_dd = drawdown.min()\n        return max_dd\n\n    def calc_sharpe(self):\n        \"\"\"\u590f\u666e\u6bd4\u7387\uff08\u7b80\u6613\u7248\uff09\"\"\"\n        self.net_df&#91;\"pct\"] = self.net_df&#91;\"net_value\"].pct_change()\n        daily_return = self.net_df&#91;\"pct\"].mean()\n        daily_std = self.net_df&#91;\"pct\"].std()\n        if daily_std == 0:\n            return 0\n        sharpe = daily_return \/ daily_std * np.sqrt(250)\n        return sharpe\n\n    def calc_win_rate(self):\n        \"\"\"\u80dc\u7387\u3001\u76c8\u4e8f\u6bd4\"\"\"\n        if len(self.trade_df) &lt; 2:\n            return 0, 0\n        profit_list = &#91;]\n        for i in range(1, len(self.trade_df), 2):\n            if i &gt;= len(self.trade_df):\n                break\n            buy_price = self.trade_df.iloc&#91;i-1]&#91;\"price\"]\n            sell_price = self.trade_df.iloc&#91;i]&#91;\"price\"]\n            profit = (sell_price - buy_price) \/ buy_price\n            profit_list.append(profit)\n\n        win_count = len(&#91;p for p in profit_list if p &gt; 0])\n        win_rate = win_count \/ len(profit_list) if profit_list else 0\n\n        profit_avg = np.mean(&#91;p for p in profit_list if p &gt; 0]) if &#91;p for p in profit_list if p &gt; 0] else 0\n        loss_avg = abs(np.mean(&#91;p for p in profit_list if p &lt; 0])) if &#91;p for p in profit_list if p &lt; 0] else 0\n        profit_loss_ratio = profit_avg \/ loss_avg if loss_avg != 0 else 0\n        return win_rate, profit_loss_ratio\n\n    def calc_all_metrics(self):\n        \"\"\"\u8ba1\u7b97\u5168\u90e8\u7ee9\u6548\u6307\u6807\"\"\"\n        total_ret, annual_ret = self.calc_return()\n        max_dd = self.calc_max_drawdown()\n        sharpe = self.calc_sharpe()\n        win_rate, pl_ratio = self.calc_win_rate()\n        calmar = annual_ret \/ abs(max_dd) if max_dd != 0 else 0\n\n        return {\n            \"total_return\": total_ret,\n            \"annual_return\": annual_ret,\n            \"max_drawdown\": max_dd,\n            \"sharpe_ratio\": sharpe,\n            \"calmar_ratio\": calmar,\n            \"win_rate\": win_rate,\n            \"profit_loss_ratio\": pl_ratio,\n            \"trade_times\": len(self.trade_df)\n        }\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">9. \u793a\u4f8b\u7b56\u7565 strategy\/demo_strategy.py\uff08MACD \u91d1\u53c9\u6b7b\u53c9\u7b56\u7565\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\"\"\"\n\u81ea\u5b9a\u4e49\u7b56\u7565\u6a21\u677f\uff1aMACD\u91d1\u53c9\u4e70\u5165\uff0c\u6b7b\u53c9\u5356\u51fa\n\u8fd4\u56de\u683c\u5f0f\uff1a(\u4fe1\u53f7: BUY\/SELL\/NONE, \u4ea4\u6613\u6570\u91cf)\n\"\"\"\ndef macd_strategy(df, index):\n    row = df.iloc&#91;index]\n    pre_row = df.iloc&#91;index-1]\n    signal = \"NONE\"\n    volume = 1000  # \u56fa\u5b9a\u4e0b\u5355\u6570\u91cf\n\n    # MACD\u91d1\u53c9\uff1adiff\u4e0a\u7a7fdea\n    if pre_row&#91;\"diff\"] &lt; pre_row&#91;\"dea\"] and row&#91;\"diff\"] &gt; row&#91;\"dea\"]:\n        signal = \"BUY\"\n    # MACD\u6b7b\u53c9\uff1adiff\u4e0b\u7a7fdea\n    elif pre_row&#91;\"diff\"] &gt; pre_row&#91;\"dea\"] and row&#91;\"diff\"] &lt; row&#91;\"dea\"]:\n        signal = \"SELL\"\n\n    return signal, volume\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">10. AI \u53c2\u6570\u4f18\u5316\u6a21\u5757 ai_optimize\/param_optim.py\uff08\u9057\u4f20\u7b97\u6cd5\u8c03\u53c2\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from deap import base, creator, tools, algorithms\nimport random\nfrom core.backtest_engine import BacktestEngine\nfrom strategy.demo_strategy import macd_strategy\n\n# \u9002\u5e94\u5ea6\u51fd\u6570\uff1a\u6700\u5927\u5316\u5e74\u5316\u6536\u76ca\uff0c\u6700\u5c0f\u5316\u56de\u64a4\ncreator.create(\"FitnessMax\", base.Fitness, weights=(1.0, -1.0))\ncreator.create(\"Individual\", list, fitness=creator.FitnessMax)\n\ntoolbox = base.Toolbox()\ntoolbox.register(\"attr_int\", random.randint, 5, 30)\ntoolbox.register(\"individual\", tools.initRepeat, creator.Individual, toolbox.attr_int, n=2)\ntoolbox.register(\"population\", tools.initRepeat, list, toolbox.individual)\n\ndef evaluate(individual):\n    fast, slow = individual\n    def test_strategy(df, idx):\n        df = df.copy()\n        df = df.iloc&#91;:idx+1]\n        from indicator.base_indicator import BaseIndicator\n        df = BaseIndicator.calc_macd(df, fast, slow, 9)\n        now = df.iloc&#91;-1]\n        pre = df.iloc&#91;-2]\n        sig = \"NONE\"\n        vol = 1000\n        if pre&#91;\"diff\"] &lt; pre&#91;\"dea\"] and now&#91;\"diff\"] &gt; now&#91;\"dea\"]:\n            sig = \"BUY\"\n        elif pre&#91;\"diff\"] &gt; pre&#91;\"dea\"] and now&#91;\"diff\"] &lt; now&#91;\"dea\"]:\n            sig = \"SELL\"\n        return sig, vol\n\n    bt = BacktestEngine(\"600519.SH\", \"2023-01-01\", \"2026-01-01\", test_strategy)\n    res = bt.run()\n    if not res:\n        return (-999, 999)\n    return (res&#91;\"performance\"]&#91;\"annual_return\"], res&#91;\"performance\"]&#91;\"max_drawdown\"])\n\ntoolbox.register(\"evaluate\", evaluate)\ntoolbox.register(\"mate\", tools.cxTwoPoint)\ntoolbox.register(\"mutate\", tools.mutUniformInt, low=5, up=30, indpb=0.2)\ntoolbox.register(\"select\", tools.selTournament, tournsize=3)\n\ndef run_ga_optimize():\n    pop = toolbox.population(n=20)\n    hof = tools.HallOfFame(1)\n    stats = tools.Statistics(lambda ind: ind.fitness.values)\n    stats.register(\"avg\", lambda x: sum(i&#91;0] for i in x)\/len(x))\n    stats.register(\"max\", max)\n\n    pop, log = algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=10, stats=stats, halloffame=hof)\n    return hof&#91;0], log\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">11. FastAPI \u63a5\u53e3 api\/route.py\uff08\u5bf9\u5916\u63a5\u53e3 + OpenClaw \u5bf9\u63a5\uff09<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>from fastapi import FastAPI, Query\nfrom core.backtest_engine import BacktestEngine\nfrom strategy.demo_strategy import macd_strategy\nfrom ai_optimize.param_optim import run_ga_optimize\n\napp = FastAPI(title=\"Backtest Expert \u4ea4\u6613\u7b56\u7565\u56de\u6d4b\u63a5\u53e3\")\n\n@app.get(\"\/run_backtest\")\ndef run_backtest(\n    code: str = Query(..., description=\"\u80a1\u7968\u4ee3\u7801\"),\n    start: str = Query(\"2023-01-01\"),\n    end: str = Query(\"2026-06-01\")\n):\n    \"\"\"\u6267\u884c\u5355\u7b56\u7565\u56de\u6d4b\"\"\"\n    bt = BacktestEngine(code, start, end, macd_strategy)\n    result = bt.run()\n    if not result:\n        return {\"code\": code, \"status\": \"fail\", \"msg\": \"\u56de\u6d4b\u5931\u8d25\"}\n    return {\"code\": code, \"status\": \"success\", \"data\": result}\n\n@app.get(\"\/optim_param\")\ndef optimize_param():\n    \"\"\"AI\u81ea\u52a8\u53c2\u6570\u4f18\u5316\"\"\"\n    best_param, log = run_ga_optimize()\n    return {\n        \"best_macd_fast\": best_param&#91;0],\n        \"best_macd_slow\": best_param&#91;1],\n        \"optim_log\": log\n    }\n\n@app.get(\"\/health\")\ndef health():\n    return {\"service\": \"Backtest Expert\", \"status\": \"running\"}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">12. \u9879\u76ee\u5165\u53e3 main.py<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">python<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import uvicorn\nfrom api.route import app\nfrom config.settings import API_HOST, API_PORT\nfrom utils.logger import get_logger\n\nlogger = get_logger()\n\nif __name__ == \"__main__\":\n    logger.info(\"Backtest Expert \u4ea4\u6613\u7b56\u7565\u56de\u6d4b\u4e13\u5bb6 \u542f\u52a8\u6210\u529f\")\n    uvicorn.run(app, host=API_HOST, port=API_PORT)\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u516d\u3001\u7279\u8272\u6539\u9020\u843d\u5730\u8bf4\u660e<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>\u5168\u771f\u4ea4\u6613\u6a21\u62df\u6539\u9020<\/strong> \u539f\u751f\u53e0\u52a0\u6ed1\u70b9\u3001\u5370\u82b1\u7a0e\u3001\u4f63\u91d1\u3001\u6da8\u8dcc\u505c\u3001\u6d41\u52a8\u6027\u6821\u9a8c\uff0c\u4fee\u590d\u4f20\u7edf\u6846\u67b6 \u201c\u7406\u60f3\u73af\u5883\u56de\u6d4b\u201d \u95ee\u9898\uff0c\u5b9e\u76d8\u62df\u5408\u5ea6\u5927\u5e45\u63d0\u5347\u3002<\/li>\n\n\n\n<li><strong>AI \u53c2\u6570\u4f18\u5316\u6539\u9020<\/strong> \u96c6\u6210\u9057\u4f20\u7b97\u6cd5\uff0c\u81ea\u52a8\u904d\u5386\u53c2\u6570\u3001\u89c4\u907f\u8fc7\u62df\u5408\uff0c\u66ff\u4ee3\u4eba\u5de5\u53cd\u590d\u8c03\u53c2\uff0c\u652f\u6301\u6279\u91cf\u53c2\u6570\u7ec4\u5408\u8bc4\u6d4b\u3002<\/li>\n\n\n\n<li><strong>\u591a\u7ea7\u5185\u5b58\u9002\u914d\u6539\u9020<\/strong> \u65b0\u589e\u51b7\u70ed\u6570\u636e\u7f13\u5b58\u903b\u8f91\uff0c\u5386\u53f2\u884c\u60c5\u3001\u56de\u6d4b\u62a5\u544a\u51b7\u6570\u636e\u538b\u7f29\u843d\u5730\uff0c\u9ad8\u9891\u8ba1\u7b97\u6570\u636e\u5e38\u9a7b\u5185\u5b58\uff0c\u5927\u6837\u672c\u56de\u6d4b\u901f\u5ea6\u63d0\u5347 40%+\u3002<\/li>\n\n\n\n<li><strong>\u6279\u91cf\u4efb\u52a1\u8c03\u5ea6\u6539\u9020<\/strong> \u57fa\u4e8e APScheduler \u5b9e\u73b0\u591a\u7b56\u7565\u3001\u591a\u54c1\u79cd\u5e76\u884c\u56de\u6d4b\uff0c\u652f\u6301\u4efb\u52a1\u961f\u5217\u3001\u65ad\u70b9\u7eed\u8dd1\u3001\u4f18\u5148\u7ea7\u7ba1\u7406\u3002<\/li>\n\n\n\n<li><strong>\u5168\u751f\u6001\u8054\u52a8\u6539\u9020<\/strong> \u9884\u7559\u63a5\u53e3\u5bf9\u63a5 <code>Stock Monitor<\/code> \u5b9e\u65f6\u884c\u60c5\u3001<code>WeChat MP CN<\/code> \u884c\u4e1a\u8206\u60c5\u56e0\u5b50\u3001<code>Remotion<\/code> \u52a8\u6001\u590d\u76d8\u89c6\u9891\u3001<code>Social Media Scheduler<\/code> \u62a5\u544a\u81ea\u52a8\u5206\u53d1\u3002<\/li>\n\n\n\n<li><strong>\u63d2\u4ef6\u5316\u7b56\u7565\u6539\u9020<\/strong> \u7b56\u7565\u3001\u6307\u6807\u3001\u98ce\u63a7\u903b\u8f91\u72ec\u7acb\u4e3a\u63d2\u4ef6\u6587\u4ef6\uff0c\u652f\u6301\u70ed\u52a0\u8f7d\uff0c\u65e0\u9700\u91cd\u542f\u670d\u52a1\u5373\u53ef\u65b0\u589e \/ \u4fee\u6539\u7b56\u7565\u3002<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">\u4e03\u3001\u4f7f\u7528\u793a\u4f8b<\/h3>\n\n\n\n<h3 class=\"wp-block-heading\">1. \u542f\u52a8\u670d\u52a1<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">bash<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u8fd0\u884c<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>python main.py\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">2. \u8c03\u7528\u56de\u6d4b\u63a5\u53e3<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># MACD\u7b56\u7565\u56de\u6d4b \u8d35\u5dde\u8305\u53f0\nhttp:&#47;&#47;127.0.0.1:8090\/run_backtest?code=600519.SH&amp;start=2023-01-01&amp;end=2026-06-01\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">3. AI \u81ea\u52a8\u53c2\u6570\u4f18\u5316<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">plaintext<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>http:&#47;&#47;127.0.0.1:8090\/optim_param\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u516b\u3001\u62d3\u5c55\u4e8c\u6b21\u5f00\u53d1\u65b9\u5411<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>\u589e\u52a0<strong>\u5206\u65f6 \/ \u9010\u7b14\u56de\u6d4b<\/strong>\u6a21\u5757\uff0c\u652f\u6301\u65e5\u5185\u4ea4\u6613\u7b56\u7565\u56de\u6d4b<\/li>\n\n\n\n<li>\u5f00\u53d1<strong>\u591a\u54c1\u79cd\u7ec4\u5408\u3001\u8f6e\u52a8\u7b56\u7565<\/strong>\u5f15\u64ce\uff0c\u652f\u6301\u4e00\u7bee\u5b50\u6807\u7684\u56de\u6d4b<\/li>\n\n\n\n<li>\u5bf9\u63a5\u5b9e\u76d8\u63a5\u53e3\uff0c\u5b9e\u73b0\u300c\u56de\u6d4b\u2192\u6a21\u62df\u76d8\u2192\u5b9e\u76d8\u300d\u4e00\u952e\u5207\u6362<\/li>\n\n\n\n<li>\u5b8c\u5584\u524d\u7aef\u53ef\u89c6\u5316\u770b\u677f\uff0c\u589e\u52a0 K \u7ebf\u3001\u51c0\u503c\u66f2\u7ebf\u3001\u4e70\u5356\u70b9\u56fe\u5f62\u5c55\u793a<\/li>\n\n\n\n<li>\u65b0\u589e\u8206\u60c5\u3001\u8d44\u91d1\u6d41\u7b49<strong>\u53e6\u7c7b\u56e0\u5b50<\/strong>\uff0c\u6253\u9020\u591a\u56e0\u5b50\u91cf\u5316\u7b56\u7565\u5e73\u53f0<\/li>\n\n\n\n<li>\u6279\u91cf\u5bfc\u51fa\u56de\u6d4b\u62a5\u544a\u4e3a Excel\/PDF\uff0c\u5bf9\u63a5\u529e\u516c\u81ea\u52a8\u5316\u5de5\u5177<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>\u4e00\u3001\u4ea7\u54c1\u72ec\u7acb\u5b9a\u4f4d &amp; \u5dee\u5f02\u5316\u7279\u8272\uff08\u72ec\u6709\u6539\u9020\u70b9\uff09 \u4e13\u5c5e\u6807\u9898 Backtest Expert\u30fb\u591a\u5e02\u573a\u91cf\u5316\u7b56 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[20],"tags":[],"class_list":["post-299","post","type-post","status-publish","format-standard","hentry","category-mianfeiercikaifalongxiaskill"],"_links":{"self":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts\/299","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/comments?post=299"}],"version-history":[{"count":1,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts\/299\/revisions"}],"predecessor-version":[{"id":300,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/posts\/299\/revisions\/300"}],"wp:attachment":[{"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/media?parent=299"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/categories?post=299"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.longxiapro.com\/index.php\/wp-json\/wp\/v2\/tags?post=299"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}