:從數(shù)據(jù)清洗到特征工程與建模)
簡介資源是《4天學會Python機器學習與量化交易》系列筆記的第二部分對應視頻教程p16至p20面向正在入門量化投資與Python機器學習的學習者。筆記聚焦多因子策略中的市值因子選股結合RiceQuant在線量化平臺以滬深300成分股為例介紹如何通過get_fundamentals等API獲取市值、財務等基本面數(shù)據(jù)并利用query函數(shù)按市值排序篩選股票、構建并動態(tài)調(diào)整個股組合。內(nèi)容還系統(tǒng)梳理了多因子策略的完整流程——從數(shù)據(jù)獲取、因子計算、因子篩選到組合構建與交易執(zhí)行同時說明因子數(shù)據(jù)中截面數(shù)據(jù)與面板數(shù)據(jù)的區(qū)別。去極值處理是重點模塊分別講解了中位數(shù)去極值、3倍中位數(shù)去極值和3sigma法的原理及代碼實現(xiàn)并配有p19、p20的具體案例幫助讀者消除極端值對因子模型的干擾。資源以單個PDF文件呈現(xiàn)大小僅562KB方便本地閱讀與檢索目前已有1363人學習瀏覽適合希望快速上手量化選股與常見因子預處理方法的初學者作為筆記型參考。 這是“4天學會python機器學習與量化交易”系列筆記的第二篇接著上次的進度今天覆蓋p16到p20這5節(jié)課程。上一篇筆記寫完了p1~p15那會兒還處在python基礎語法、numpy和pandas的入門階段代碼寫得再多也還是“學會了工具但不知道拿來干嘛”的狀態(tài)。到p16~p20這塊課程節(jié)奏明顯變了開始真正進入量化交易的數(shù)據(jù)獲取、數(shù)據(jù)處理、特征工程和機器學習建模環(huán)節(jié)算是第一次把python、機器學習和量化交易串在了一條線上。如果你正在跟著這套課學或者只是想看看“機器學習做量化到底怎么落地”這篇筆記應該能幫你省不少事。我不光會把每節(jié)課的知識點重新梳理一遍還會把跑代碼時遇到的坑、視頻里沒解釋清楚的細節(jié)、以及我自己的一些擴展思考一并寫出來方便你復現(xiàn)和避雷。1. p16~p20課程地圖學習量化的第一段完整閉環(huán)1.1 五節(jié)課分別講了什么前15節(jié)還在鋪python基礎語法和數(shù)據(jù)分析工具的底子到了p16~p20課程開始動真格了。我把這幾節(jié)的內(nèi)容做了一張對照表方便你一目了然地看到整個節(jié)奏安排視頻主題核心知識點p16機器學習與量化交易概述機器學習在量化中的應用場景、策略類型、整體處理流程p17行情數(shù)據(jù)獲取akshare/tushare等數(shù)據(jù)接口的基本用法、數(shù)據(jù)結構p18數(shù)據(jù)清洗與預處理列名統(tǒng)一、缺失值處理、數(shù)據(jù)類型轉換、時間索引設置p19特征工程與標簽構造常用技術指標計算、訓練樣本構造、預測目標定義p20第一個機器學習模型線性回歸入門、sklearn建模、訓練集測試集劃分與評估這五節(jié)課整體看下來其實就是在走一條“從數(shù)據(jù)到模型”的產(chǎn)業(yè)鏈先用接口把行情數(shù)據(jù)拿到手然后清洗成能用的標準格式接著從價格序列中構造出特征和預測目標最后丟給sklearn跑一個最簡單的模型。整套流程走完你就算是對“機器學習做量化”這件事有了一個全景認知——盡管此時還看不清它的全貌但至少知道了零件長什么樣、大概怎么裝配。1.2 為什么課程在這個位置切入機器學習我翻了一下p1~p15的內(nèi)容基本是變量、循環(huán)、函數(shù)、numpy和pandas的基礎操作。如果課程從這會兒就直接上隨機森林、神經(jīng)網(wǎng)絡新手大概率當場勸退因為數(shù)據(jù)不會整理、維度對不上、概念聽不懂報錯都看不懂在報什么。而p16~p20作為過渡段把“數(shù)據(jù)處理”當成了先頭部隊——這其實是很符合實戰(zhàn)節(jié)奏的安排。實際做量化的朋友應該深有體會真正花時間的從來不是模型調(diào)參而是數(shù)據(jù)獲取、清洗、拼接、對齊這些臟活累活。數(shù)據(jù)質(zhì)量不過關后面模型再強也是“垃圾進、垃圾出”。課程把數(shù)據(jù)處理放在建模前面而且是分了兩節(jié)課篇幅來講這個比例我是認可的。另外線性回歸作為第一個模型也選得很穩(wěn)它足夠簡單幾行代碼就能跑完能讓剛接觸機器學習的人先建立起“特征-模型-預測-評估”的完整心智模型而不是一上來就被復雜算法的數(shù)學推導勸退。2. 數(shù)據(jù)獲取別一上來就調(diào)接口2.1 akshare和tushare到底選哪個視頻里老師用的是tushare但我實操下來發(fā)現(xiàn)tushare的新版接口需要先到官網(wǎng)注冊申請token部分接口還有積分門檻——不同積分等級能調(diào)用的數(shù)據(jù)范圍不一樣。對剛入門的朋友來說這是一個不小的隱形門檻。相比之下akshare不用注冊、不用token直接pip install akshare裝好就能用接口返回的就是pandas的DataFrame剛好可以順勢復習上一篇筆記里學的pandas操作。我把兩者做了個簡單對比對比項aksharetushare是否需要token不需要需要注冊申請上手難度較低中等數(shù)據(jù)來源聚合公開網(wǎng)頁數(shù)據(jù)自建數(shù)據(jù)倉庫適合場景個人學習、快速驗證想對數(shù)據(jù)穩(wěn)定性要求更高的場景當然這只是我基于個人學習體驗做的對比不是要分個高下。數(shù)據(jù)源沒有絕對的優(yōu)劣只有合不合適。如果你想快速驗證一個想法、跑通一套流程akshare足夠如果后續(xù)要做更正式的量化項目再去研究tushare的積分體系也不遲。2.2 一個能跑的取數(shù)demo以獲取貴州茅臺的歷史日線數(shù)據(jù)為例用akshare只需要幾行代碼import akshare as ak # 獲取貴州茅臺歷史日線數(shù)據(jù)不復權 df ak.stock_zh_a_hist( symbol600519, perioddaily, start_date20200101, end_date20241231, adjust ) print(df.head()) print(df.tail())第一次跑通這個接口時我還是有點興奮的——畢竟這是第一次真正“親手”把真實行情數(shù)據(jù)拉到本地。但興奮勁很快就過去了因為緊接著就會發(fā)現(xiàn)一個讓人頭疼的問題返回的DataFrame列名是中文的而且包含了不少我們暫時用不上的字段比如振幅、漲跌幅、換手率等。2.3 清洗從“能跑”到“能用”如果只是打印出來看看那確實不需要額外處理。但要喂給機器學習模型就得先把數(shù)據(jù)整理成統(tǒng)一格式。這一步?jīng)]什么高深技術主要就是四件事列名改英文、日期轉成datetime類型、把日期設為索引、按時間排序。我當時的清洗代碼如下import pandas as pd # 統(tǒng)一列名只保留后續(xù)用得上的字段 df.columns [date, open, close, high, low, volume, amount, amplitude, pct_change, change, turnover] # 日期轉datetime并設為索引按時間正序排列 df[date] pd.to_datetime(df[date]) df df.set_index(date).sort_index() # 只保留建模需要的列 df df[[open, close, high, low, volume]] # 檢查缺失值 print(df.isnull().sum())這里有兩個細節(jié)值得多說一句。第一sort_index()一定要做因為有些數(shù)據(jù)源返回的順序可能不是嚴格按時間排的而后續(xù)計算移動平均線依賴數(shù)據(jù)的先后順序一旦順序亂了指標就全是錯的。第二改列名時不要只圖省事直接照抄建議提前想好你后面建模到底需要哪些字段把多余的列盡早砍掉數(shù)據(jù)干凈了后續(xù)的排查成本會低很多。3. 特征工程機器學習里的“吃雞裝備”3.1 為什么需要特征工程如果只把原始價格喂給模型相當于讓一個沒帶裝備的新人直接跳傘到?jīng)Q賽圈——他肉眼看到的只有紅紅綠綠的K線很難從中歸納出規(guī)律。特征工程就是給這個新人配上倍鏡、防彈衣和醫(yī)療包讓模型從同樣的數(shù)據(jù)里看到更有區(qū)分度的信息。具體到量化場景移動平均線、RSI、布林帶這些經(jīng)典技術指標本質(zhì)上就是對原始價格序列做了一次加工和壓縮把“最近一段時間的趨勢強弱”這類信息顯式地暴露給模型。這一步做得好不好直接決定模型能力的上限。很多初學者喜歡把時間花在調(diào)模型超參數(shù)上但實戰(zhàn)經(jīng)驗是特征工程對效果的提升往往比調(diào)參來得更明顯。3.2 用pandas計算常見技術指標這里我用pandas把幾個出場率最高的指標都算了一遍# 移動平均線 df[ma5] df[close].rolling(window5).mean() df[ma10] df[close].rolling(window10).mean() df[ma20] df[close].rolling(window20).mean() # RSI指標 delta df[close].diff() gain delta.clip(lower0) loss -delta.clip(upper0) avg_gain gain.rolling(window14).mean() avg_loss loss.rolling(window14).mean() rs avg_gain / avg_loss df[rsi] 100 - (100 / (1 rs)) # 布林帶 df[std20] df[close].rolling(window20).std() df[boll_upper] df[ma20] 2 * df[std20] df[boll_lower] df[ma20] - 2 * df[std20] # 把剛開始計算產(chǎn)生的NaN丟棄 df df.dropna()先解釋一下rolling(window5).mean()到底做了什么它相當于開了一個長度為5的滑窗從數(shù)據(jù)第一行開始往后滑動每滑到一行就取包括自己在內(nèi)的前5行求平均這個值和這一行綁定在一起。所以ma5這一列的第10行表示第6到第10天的收盤價均值。窗口越短指標對價格反應越靈敏但也越容易產(chǎn)生噪音窗口越長趨勢越平滑但滯后越嚴重。這個“靈敏與滯后”的權衡幾乎貫穿所有技術指標的設計邏輯。RSI的公式看起來有點唬人其實就是計算一段時間內(nèi)“漲的力量”在總波動中的占比取值在0到100之間。通常認為RSI高于70進入超買區(qū)、低于30進入超賣區(qū)但學習階段先不用糾結閾值怎么選而是要把“它是在衡量什么”想明白——衡量的是最近漲跌力量的對比。3.3 標簽構造你到底要預測什么特征有了接下來最重要的問題是模型的y是什么機器學習本質(zhì)上是找x到y(tǒng)的映射關系在量化場景里最常見的一個做法是把“明天的收盤價比今天高不高”定義為一個分類問題明天漲記作1跌記作0。# 用明天的收盤價是否高于今天作為預測目標 df[target] (df[close].shift(-1) df[close]).astype(int) df df.dropna()這一行代碼可以說是整個p16~p20里最容易被忽視、但最值得停下來想明白的地方。shift(-1)的作用是把整列數(shù)據(jù)向上平移一行——讓第t行的target值等于第t1天的收盤價與第t天收盤價的比較結果。為什么要這么干因為我們今天能拿到的所有特征ma5、rsi、布林帶等都是基于截至今天的歷史數(shù)據(jù)計算出來的我們想要預測的是明天會發(fā)生什么所以標簽必須來自未來一天的信息。如果不做shift直接用當天的close去構造target模型就是在拿今天的特征預測今天已經(jīng)發(fā)生的事——這就不叫預測了叫“事后諸葛亮”。這也是量化里經(jīng)常提到的“未來函數(shù)”問題后面踩坑部分我會專門展開說。4. 第一個模型線性回歸其實暴露了很多問題4.1 為什么選線性回歸而不是別的p20一口氣把線性回歸的建模、訓練、評估流程走完了。這里選線性回歸而不是邏輯回歸或者決策樹我覺得有三個原因簡單、可解釋、跑得快。線性回歸的預測結果本質(zhì)上是對所有特征做了一次加權求和公式就是y w1x1 w2x2 ... b配合sklearn大概五行代碼就能訓練完。對于剛接觸機器學習的同學重要的不是模型多高級而是先把“fit-predict-evaluate”這條流水線走通。當然線性回歸本身是回歸模型輸出的是連續(xù)值而我們構造的target是0和1的分類標簽。課程這里直接用“預測值大于0.5就判定為漲”的方式把回歸結果硬掰成分類結果。嚴格來說更標準的做法是用邏輯回歸LogisticRegression它對分類問題的建模方式更合理。但作為啟蒙演示線性回歸夠用了它能把“訓練一套模型”的流程感建立起來邏輯回歸的數(shù)學細節(jié)留到后面再深入反而是更平滑的學習曲線。4.2 時間序列數(shù)據(jù)不能隨機切分很多第一次跑機器學習的人會習慣性地直接調(diào)用train_test_split()把數(shù)據(jù)隨機切成訓練集和測試集。但在量化場景里這個做法有嚴重問題時間序列數(shù)據(jù)有先后依賴關系隨機切分等于讓模型“看了未來再做過去題”測試集里混入了未來信息評估結果會虛高得非常離譜。正確做法是按時間順序用前80%的數(shù)據(jù)訓練、后20%的數(shù)據(jù)測試from sklearn.linear_model import LinearRegression from sklearn.metrics import classification_report # 特征列 feature_cols [ma5, ma10, ma20, rsi, boll_upper, boll_lower] X df[feature_cols].values y df[target].values # 按時間切分前80%訓練后20%測試 split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:] # 訓練線性回歸模型 model LinearRegression() model.fit(X_train, y_train) # 預測并將連續(xù)值轉換為0/1 y_pred (model.predict(X_test) 0.5).astype(int) # 輸出評估報告 print(classification_report(y_test, y_pred))這里還有個容易踩的雷特征x和標簽y必須嚴格對齊。如果特征計算完dropna之后又單獨對y做了一次dropna行數(shù)就對不上了后面的fit會直接報錯。我當時就犯過這個錯來回對了好幾遍索引才發(fā)現(xiàn)是行數(shù)不一致。建議習慣性看一眼X.shape和y.shape是不是一致。4.3 評估結果怎么看以及為什么賺錢沒那么容易classification_report會輸出precision、recall、f1-score這些指標。第一次看到這份報告的時候我的準確率大概在53%左右比瞎猜的50%強了一點點但遠談不上“找到了財富密碼”。這里多說一句很多新手容易盯住準確率不放但漲跌分類有一個天然的不平衡問題如果市場整體上漲天數(shù)略多模型什么都不做、全部預測“漲”也能拿到一個還不錯的準確率。所以真正要看的其實是precision、recall和f1-score的組合尤其對“漲”這個類別單獨看。更扎心的是即便模型準確率做到了52%在真實交易里還要扣掉手續(xù)費、滑點這些交易成本52%的勝率扣完成本很可能根本不賺錢。機器學習在量化里的作用不是“點石成金”而是幫你從大量數(shù)據(jù)里找出統(tǒng)計意義上的規(guī)律再把規(guī)律包裝成策略去執(zhí)行。離“穩(wěn)定盈利”中間還隔著一個完整的交易系統(tǒng)——倉控、止損、資金管理、回撤控制這些一個都不能少。p16~p20只是把機器學習模型的輪子轉了起來離“上路”還遠。5. 踩坑記錄這些坑比課程本身更有學習價值5.1 未來函數(shù)看似90%準確率的假象我在自己動手復現(xiàn)時曾經(jīng)寫出了這樣一段錯誤的標簽構造代碼# 錯誤示范 df[target] (df[close] df[close].shift(1)).astype(int)看起來好像沒什么問題就是把“今天比昨天漲沒漲”作為標簽。問題出在特征里ma5、ma10、ma20這些指標都包含了當天的收盤價信息而target又是由當天收盤價和昨天收盤價的比較得到的。模型訓練時特征和標簽共享了同一個“今天的收盤價”相當于考試時答案就壓在卷子底下——測試集準確率沖到了90%以上我還以為自己發(fā)現(xiàn)了什么不得了的規(guī)律后來對照課程才發(fā)現(xiàn)標簽構造邏輯搞反了方向。應該用close.shift(-1)制造“明天”的信息而不是用close.shift(1)去提取“昨天”的信息。這類未來函數(shù)問題是量化新手最容易踩的坑錯誤代碼跑出來的評估結果越漂亮越要警惕數(shù)據(jù)里是不是藏著未來。5.2 除權除息會把股價“打出一個坑”取數(shù)時還有一個很容易被忽視的細節(jié)默認參數(shù)獲取的往往是“不復權”數(shù)據(jù)。一旦股票在某一天除權除息價格會突然向下跳空一大截但跳空之前的歷史價格沒有做調(diào)整。這時候計算均線、RSI等指標就會在除權日附近出現(xiàn)假信號——模型會以為股價暴跌了但其實只是分紅除權導致的價格修正。解決辦法很簡單把取數(shù)接口的adjust參數(shù)改成qfq前復權讓歷史價格在除權時做統(tǒng)一調(diào)整。前復權的邏輯是保持當前價格不變把歷史價格按比例調(diào)整這樣算出來的技術指標才是連續(xù)的。5.3 sklearn版本差異帶來的API變更課程視頻里用的是當時某個版本的sklearn代碼在我現(xiàn)在的新版本環(huán)境里偶爾會碰到一些參數(shù)失效或者模塊遷移的情況。比如某些老版本里的函數(shù)在新版本中換了位置或者改了默認值直接復制老代碼會莫名其妙報錯。我的處理辦法是在項目一開始就創(chuàng)建一個虛擬環(huán)境把依賴版本固定下來pip install scikit-learn1.0.2這樣做的好處是以后不管課程代碼怎么變、系統(tǒng)環(huán)境怎么升級項目本身能穩(wěn)定復現(xiàn)。如果你懶得管版本也沒問題——但每跑一步遇到報錯先看一眼報錯信息里的sklearn相關字樣通常就能定位是版本問題還是代碼問題。5.4 重新理解“4天學會”這件事把p16~p20跑通之后我對“4天學會”這個標題的理解更清醒了。4天時間能完成的事是搭好環(huán)境、了解接口、跑通一套標準的建模流程讓你看到一個從數(shù)據(jù)到模型的完整骨架。但距離真正“學會”還差著大量的實操換一只股票能不能跑通換一組特征效果會怎樣模型參數(shù)調(diào)一調(diào)又會怎樣這些問題靠4天是不可能得到答案的。課程的真正價值是把一條最常用的技術路徑喂到你嘴邊——數(shù)據(jù)獲取-清洗-特征-建模-評估后面需要你自己反復咀嚼、消化成自己的能力。寫到這里p16~p20的內(nèi)容算是基本消化完了。一個很主觀的感受這套課程真正的價值不在那幾行代碼而在把“機器學習做量化”這條路上最常見的幾個坑提前暴露給你。我踩過的未來函數(shù)、復權、版本兼容你大概率也會踩一遍早點知道至少能少掉幾根頭發(fā)。接下來我會繼續(xù)往下學下一篇筆記打算整理p21~p25的回測部分到那會兒就能看到策略完整跑起來是什么效果了。有興趣的可以繼續(xù)關注也建議你把上一篇筆記里的環(huán)境搭建和基礎語法補一補再看本文的代碼會順暢很多。本文還有配套的精品資源點擊獲取