戰(zhàn):機(jī)器學(xué)習(xí)策略demo從數(shù)據(jù)到回測全流程)
簡介這是一份面向具備一定Python基礎(chǔ)、對炒股與量化投資尚不熟悉的初學(xué)者打造的入門級demo源碼圍繞機(jī)器學(xué)習(xí)在A股量化策略中的應(yīng)用展開幫助讀者快速理解數(shù)據(jù)收集、特征工程、模型構(gòu)建與回測評估的完整流程。資源包共14個文件以5個py腳本、6張png圖表、2個txt說明及1個gitignore為主Python文件分別承擔(dān)回測、特征處理、模型訓(xùn)練與數(shù)據(jù)讀取等模塊職責(zé)圖片則直觀展示決策樹預(yù)測股價漲幅、最大回撤、K線走勢等關(guān)鍵結(jié)果壓縮包整體約727KB結(jié)構(gòu)清晰便于按模塊學(xué)習(xí)。目前已有156人學(xué)習(xí)下載。通過這份源碼讀者可對照運(yùn)行從數(shù)據(jù)處理到策略回測的全流程觀察不同機(jī)器學(xué)習(xí)算法在價格預(yù)測中的表現(xiàn)并借助回測結(jié)果理解止損、倉位控制等風(fēng)險管理思路為后續(xù)獨(dú)立編寫量化策略打下實(shí)踐基礎(chǔ)。1. 從一份能跑通的 A 股量化 demo 說起它到底解決了什么很多人第一次接觸量化投資卡住的地方不是不會寫 Python而是不知道一個完整策略從數(shù)據(jù)到回測到底長什么樣。網(wǎng)上搜「機(jī)器學(xué)習(xí) 量化投資策略」出來的要么是純理論推導(dǎo)要么是殘缺的代碼片段跑不起來也驗(yàn)證不了。這份ai_qua2323weweer.zip就是沖著這個痛點(diǎn)來的——它把數(shù)據(jù)獲取、特征工程、模型訓(xùn)練、回測評估串成了一條能跑通的鏈路用 A 股市場做樣本適合有 Python 基礎(chǔ)但對炒股和量化一無所知的人拿來拆解。壓縮包里除了main.py、backtest.py、feature.py、model.py、data.py這幾個核心腳本還附了柱狀圖、最大回撤、K 線圖、決策樹預(yù)測股價漲幅等運(yùn)行截圖以及tushare_token.png提示你需要自己配數(shù)據(jù)源憑證。說白了它不是一個能直接拿去實(shí)盤的策略而是一個讓你看清「量化策略長什么樣」的腳手架。2. 拆開壓縮包五個 Python 文件各自扛什么活2.1 模塊分工與調(diào)用關(guān)系拿到源碼先別急著pip install花五分鐘把文件職責(zé)理清楚后面調(diào)參和排錯會省很多事。這個 demo 的結(jié)構(gòu)是典型的流水線式設(shè)計main.py是總調(diào)度其余四個模塊各管一段。文件職責(zé)關(guān)鍵輸入關(guān)鍵輸出data.py拉取 A 股歷史行情、清洗缺失值tushare token、股票代碼列表規(guī)整的 DataFramefeature.py從原始行情構(gòu)造技術(shù)指標(biāo)特征行情 DataFrame特征矩陣 Xmodel.py構(gòu)建并訓(xùn)練機(jī)器學(xué)習(xí)模型特征矩陣、標(biāo)簽 y訓(xùn)練好的模型對象backtest.py用歷史數(shù)據(jù)模擬買賣、算收益模型預(yù)測信號、價格序列收益曲線、最大回撤main.py串聯(lián)全流程以上全部回測報告與圖表stock_list.txt里放的是股票池requirements.txt鎖定了依賴版本.gitignore說明作者是按工程習(xí)慣組織的。img目錄下的截圖不是裝飾是你跑完后用來對照結(jié)果是否正常的參照物——比如最大回撤.png能幫你判斷回測邏輯有沒有寫反。2.2 環(huán)境準(zhǔn)備與依賴安裝先確認(rèn) Python 版本這類 demo 一般跑在 3.8 到 3.10 之間比較穩(wěn)。建虛擬環(huán)境是基本操作別嫌麻煩血淚經(jīng)驗(yàn)是直接在系統(tǒng)環(huán)境里裝依賴后面版本沖突能讓你重裝系統(tǒng)。# 創(chuàng)建并激活虛擬環(huán)境 python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate # 安裝依賴requirements.txt 里通常包含這些 pip install -r requirements.txtrequirements.txt里大概率會有pandas、numpy、scikit-learn、tushare、matplotlib這幾個。如果安裝tushare時報錯常見原因是網(wǎng)絡(luò)問題或版本過舊可以指定版本重試。裝完后用pip list核對一下重點(diǎn)看scikit-learn的版本因?yàn)椴煌姹纠顳ecisionTreeClassifier的默認(rèn)參數(shù)有差異會影響你復(fù)現(xiàn)截圖里的決策樹結(jié)果。2.3 數(shù)據(jù)源配置token 是繞不過去的門檻tushare_token.png這張截圖就是在提醒你數(shù)據(jù)獲取需要憑證。tushare 的免費(fèi)額度對 demo 級別的回測夠用但需要注冊后拿到 token。# data.py 中通常會有類似這樣的初始化代碼 import tushare as ts # 把這里的 token 替換成你自己的 ts.set_token(你的tushare_token) pro ts.pro_api() # 拉取單只股票日線數(shù)據(jù) df pro.daily(ts_code000001.SZ, start_date20200101, end_date20231231)邏輯說明set_token只需調(diào)用一次之后pro_api()返回的接口對象可以復(fù)用。daily接口返回的是未復(fù)權(quán)數(shù)據(jù)做量化回測時要注意復(fù)權(quán)問題否則除權(quán)除息日會出現(xiàn)價格跳空導(dǎo)致特征失真。參數(shù)上ts_code的格式是「代碼.交易所」000001.SZ是深交所600000.SH是上交所。如果你拉數(shù)據(jù)返回空 DataFrame先檢查 token 是否過期再確認(rèn)股票代碼格式和日期范圍。3. 特征工程與模型訓(xùn)練把行情變成模型能吃的數(shù)字3.1 從價格序列構(gòu)造有效特征原始行情只有開高低收和成交量直接丟給模型效果很差。feature.py的核心任務(wù)就是把這些序列轉(zhuǎn)成有預(yù)測意義的特征。常見做法是構(gòu)造動量類、波動率類、成交量類指標(biāo)。# feature.py 中特征構(gòu)造的典型寫法 import pandas as pd import numpy as np def build_features(df): df df.sort_values(trade_date).reset_index(dropTrue) # 動量特征過去 5 日、10 日收益率 df[ret_5] df[close].pct_change(5) df[ret_10] df[close].pct_change(10) # 波動率特征過去 10 日收益率標(biāo)準(zhǔn)差 df[vol_10] df[close].pct_change().rolling(10).std() # 成交量特征當(dāng)日成交量與 5 日均量之比 df[vol_ratio] df[vol] / df[vol].rolling(5).mean() # 標(biāo)簽未來 5 日漲跌方向漲為 1 跌為 0 df[label] (df[close].shift(-5) df[close]).astype(int) df df.dropna() return df邏輯說明pct_change(5)算的是 5 日累計收益率反映中期動量。rolling(10).std()衡量波動率波動率放大往往伴隨趨勢轉(zhuǎn)折。vol_ratio捕捉放量行為放量突破在 A 股里是比較有效的信號。標(biāo)簽用shift(-5)構(gòu)造未來 5 日方向注意這會造成最后 5 行標(biāo)簽為 NaN必須dropna掉否則訓(xùn)練時會報錯。參數(shù)上窗口期不是固定的5 日和 10 日只是 demo 默認(rèn)值你可以改成 3 日或 20 日觀察模型表現(xiàn)變化。3.2 決策樹模型的訓(xùn)練與調(diào)參model.py里用的是決策樹截圖決策樹預(yù)測股價漲幅.png就是它的輸出。選決策樹而不是神經(jīng)網(wǎng)絡(luò)是因?yàn)樗山忉屝詮?qiáng)能直接看到哪些特征重要對入門者友好。# model.py 中模型訓(xùn)練的核心代碼 from sklearn.tree import DecisionTreeClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score def train_model(df): feature_cols [ret_5, ret_10, vol_10, vol_ratio] X df[feature_cols] y df[label] # 按時間順序切分不能隨機(jī)打亂 split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] clf DecisionTreeClassifier( max_depth4, # 控制樹深防止過擬合 min_samples_leaf50, # 葉子節(jié)點(diǎn)最少樣本數(shù) random_state42 ) clf.fit(X_train, y_train) pred clf.predict(X_test) print(測試集準(zhǔn)確率:, accuracy_score(y_test, pred)) return clf邏輯說明時間序列數(shù)據(jù)絕對不能隨機(jī)切分train_test_split默認(rèn)的 shuffle 會把未來信息泄露到訓(xùn)練集導(dǎo)致回測虛高這是新手最容易翻車的地方。max_depth4是刻意壓低的決策樹不加限制可以無限生長訓(xùn)練集準(zhǔn)確率能到 99% 但測試集一塌糊涂。min_samples_leaf50保證每個葉子有足夠樣本支撐避免模型記住噪聲。如果你把max_depth調(diào)到 10 以上大概率會看到訓(xùn)練準(zhǔn)確率飆升但回測收益反而下降這就是過擬合的典型表現(xiàn)。3.3 回測邏輯別讓未來函數(shù)毀掉結(jié)果backtest.py是整個 demo 最需要盯緊的部分?;販y的核心是模擬「如果當(dāng)時按信號操作收益會怎樣」但很容易寫出用到未來數(shù)據(jù)的代碼。# backtest.py 中簡化的回測循環(huán) def run_backtest(df, model): df df.copy() df[signal] model.predict(df[[ret_5, ret_10, vol_10, vol_ratio]]) # 信號滯后一期避免用當(dāng)日收盤信號做當(dāng)日交易 df[position] df[signal].shift(1) # 當(dāng)日收益率 df[daily_ret] df[close].pct_change() # 策略收益 持倉信號 × 次日收益 df[strategy_ret] df[position] * df[daily_ret] # 累計凈值 df[cum_ret] (1 df[strategy_ret]).cumprod() # 最大回撤 df[peak] df[cum_ret].cummax() df[drawdown] (df[cum_ret] - df[peak]) / df[peak] max_dd df[drawdown].min() print(累計收益:, df[cum_ret].iloc[-1] - 1) print(最大回撤:, max_dd) return df邏輯說明shift(1)是關(guān)鍵一步它保證你今天看到的信號明天才執(zhí)行模擬真實(shí)交易中「收盤后算信號、次日開盤操作」的節(jié)奏。如果不加這個滯后等于用當(dāng)天收盤價預(yù)測當(dāng)天漲跌再當(dāng)天買賣回測收益會好得不真實(shí)。cumprod()算累計凈值cummax()算歷史高點(diǎn)兩者相除得到回撤序列。最大回撤是負(fù)數(shù)越接近 0 越好。跑完后對照最大回撤.png如果你的回撤超過 -50%要么是模型有問題要么是回測邏輯寫錯了。4. 避坑與排查跑不通、結(jié)果離譜時先看這幾條4.1 現(xiàn)象tushare 返回空數(shù)據(jù)或報權(quán)限錯誤原因通常有三個token 沒設(shè)置、token 過期、或者調(diào)用的接口超出免費(fèi)額度。解決方法是先打印ts.get_token()確認(rèn) token 已寫入再去 tushare 官網(wǎng)看接口權(quán)限說明。免費(fèi)賬戶對daily接口有頻率限制短時間內(nèi)大量拉取會被臨時封禁建議在循環(huán)里加time.sleep(0.5)。4.2 現(xiàn)象回測收益高得離譜年化幾百個百分點(diǎn)這是典型的未來函數(shù)問題。檢查backtest.py里信號有沒有做shift檢查特征構(gòu)造時有沒有用到當(dāng)日收盤之后才知道的數(shù)據(jù)。另一個隱蔽的坑是標(biāo)簽構(gòu)造用了shift(-5)但特征里不小心混入了未來信息。解決方法是把回測信號整體滯后一期并且用樣本外數(shù)據(jù)驗(yàn)證。4.3 現(xiàn)象決策樹準(zhǔn)確率 99% 但實(shí)盤邏輯不成立原因幾乎一定是過擬合。決策樹不加max_depth和min_samples_leaf限制可以無限分裂直到每個葉子只有一個樣本訓(xùn)練集完美擬合但毫無泛化能力。解決方法是在model.py里把樹深壓到 3 到 5 層葉子最小樣本數(shù)設(shè)到 30 以上然后觀察測試集準(zhǔn)確率是否在 50% 到 60% 之間——這個區(qū)間對股票方向預(yù)測才是正常的。4.4 現(xiàn)象matplotlib 畫圖中文顯示成方塊原因是默認(rèn)字體不支持中文。在main.py或畫圖腳本開頭加兩行配置即可。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # Windows 用黑體 plt.rcParams[axes.unicode_minus] False # 正常顯示負(fù)號Mac 系統(tǒng)把SimHei換成Arial Unicode MS。改完如果還不行清一下 matplotlib 的字體緩存再重跑。4.5 現(xiàn)象requirements.txt 安裝時報版本沖突常見于pandas和numpy版本不匹配。解決方法是先裝numpy再裝pandas或者直接用pip install -r requirements.txt --no-deps跳過依賴解析手動補(bǔ)齊缺失的包。如果scikit-learn版本和 demo 作者不一致導(dǎo)致 API 報錯查一下你裝的版本里DecisionTreeClassifier的參數(shù)名有沒有變多數(shù)情況向下兼容。5. 進(jìn)階玩法把 demo 改成你自己的策略實(shí)驗(yàn)臺跑通默認(rèn)流程只是起點(diǎn)這個 demo 真正的價值在于它給你留了改的空間。我一般會從三個方向動手換模型、換特征、換股票池。換模型最直接。model.py里把DecisionTreeClassifier換成RandomForestClassifier或GradientBoostingClassifier接口幾乎不用改但要注意隨機(jī)森林的n_estimators別設(shè)太大100 到 200 棵足夠再多訓(xùn)練時間線性增長而收益提升有限。換成LogisticRegression的話記得先對特征做標(biāo)準(zhǔn)化否則量綱差異會讓系數(shù)失去意義。換特征需要回到feature.py。demo 里只用了動量和波動率你可以加均線交叉、RSI、MACD 柱狀圖斜率這些經(jīng)典指標(biāo)。但每加一個特征都要問自己這個特征在交易時點(diǎn)是否可得如果用了未來數(shù)據(jù)回測再好看也是自欺欺人。我習(xí)慣每加三個特征就跑一次回測對比最大回撤和收益曲線避免一次性堆太多導(dǎo)致無法歸因。換股票池是最容易被低估的一步。stock_list.txt里如果只有幾只股票結(jié)論沒有統(tǒng)計意義。常見做法是拉滬深 300 成分股對每只股票單獨(dú)訓(xùn)練模型再匯總信號或者把所有股票數(shù)據(jù)拼在一起訓(xùn)練一個通用模型。前者更穩(wěn)健但計算量大后者訓(xùn)練快但容易忽略個股差異。我一般先用 20 只股票做快速驗(yàn)證確認(rèn)流程沒問題再擴(kuò)到全市場。驗(yàn)證方法上除了看累計收益和最大回撤建議加一個基準(zhǔn)對比——比如買入持有滬深 300 ETF 的收益曲線。如果你的策略跑不贏基準(zhǔn)那模型再復(fù)雜也沒有意義。這個對比圖我每次都會畫它比任何單一指標(biāo)都更能說明問題。從那以后我每次拿到新的量化 demo都強(qiáng)制先跑一遍默認(rèn)參數(shù)、記下基準(zhǔn)結(jié)果再動任何一行代碼。這樣后面改出問題時至少知道是改壞了還是本來就這樣。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取