據(jù)可視化與推薦系統(tǒng)實(shí)戰(zhàn):從K線到智能選股)
1. 為什么要做可視化推薦二合一畢設(shè)選題的切入點(diǎn)與整體技術(shù)選型先說(shuō)個(gè)很現(xiàn)實(shí)的問(wèn)題市面上用Python做股票數(shù)據(jù)分析的項(xiàng)目一抓一大把但大多數(shù)都停留在拉數(shù)據(jù)、畫(huà)K線、算個(gè)均線的層面功能上很像一個(gè)加強(qiáng)版Excel。而單純做推薦系統(tǒng)又往往脫離真實(shí)場(chǎng)景用戶根本不知道推薦的依據(jù)是什么。這個(gè)課題比較聰明的點(diǎn)在于把兩塊需求擰在一起先讓用戶看得懂行情再根據(jù)看得懂的數(shù)據(jù)給出有依據(jù)的推薦。數(shù)據(jù)可視化解決的是信息過(guò)載的問(wèn)題——幾千只股票、幾十個(gè)指標(biāo)光靠人眼盯盤(pán)根本篩不過(guò)來(lái)推薦系統(tǒng)解決的是選擇困難的問(wèn)題——盯盤(pán)盯出來(lái)了候選池但選哪只、為什么選需要一套可解釋的邏輯來(lái)輔助決策。先說(shuō)技術(shù)選型。我默認(rèn)這個(gè)項(xiàng)目面向的是課程設(shè)計(jì)或者本科畢設(shè)技術(shù)棧選擇必須兼顧三點(diǎn)難度適中、有東西可寫(xiě)、答辯時(shí)講得清楚。推薦使用Python 3.8 Pandas Matplotlib Pyecharts Scikit-learn SQLite。不要一上來(lái)就上什么深度學(xué)習(xí)和LSTM那是給自己挖坑。大多數(shù)情況下K線形態(tài)識(shí)別加簡(jiǎn)單的協(xié)同過(guò)濾加一個(gè)技術(shù)面評(píng)分卡已經(jīng)足夠撐起一個(gè)完整閉環(huán)而且每一步都能在白板上畫(huà)出來(lái)。模塊劃分也要符合設(shè)計(jì)與實(shí)現(xiàn)這個(gè)題目氣質(zhì)。我當(dāng)時(shí)把系統(tǒng)切成四層數(shù)據(jù)層、計(jì)算層、可視化層、推薦層。數(shù)據(jù)層負(fù)責(zé)從公開(kāi)數(shù)據(jù)源拉取行情并做清洗入庫(kù)計(jì)算層負(fù)責(zé)技術(shù)指標(biāo)計(jì)算和特征工程可視化層做交互式圖表和看板推薦層基于用戶的瀏覽/收藏行為做策略推薦。四層各干各的聯(lián)調(diào)時(shí)才不會(huì)互相牽制。架構(gòu)設(shè)計(jì)圖我不知道你打算怎么畫(huà)但建議畫(huà)一張四層結(jié)構(gòu)加數(shù)據(jù)流向的圖。注意答辯老師最愛(ài)問(wèn)的就是數(shù)據(jù)流是怎么走的——從原始數(shù)據(jù)變成最終推薦結(jié)果中間每一步都要能說(shuō)清楚。2. 數(shù)據(jù)獲取與預(yù)處理90%的坑都埋在這層股票數(shù)據(jù)可視化項(xiàng)目第一個(gè)繞不開(kāi)的問(wèn)題是數(shù)據(jù)從哪來(lái)。這是個(gè)比寫(xiě)代碼更磨人的環(huán)節(jié)。常見(jiàn)的公開(kāi)數(shù)據(jù)源包括Tushare、AkShare、Yahoo Finance美股、東方財(cái)富的公開(kāi)接口等。我比較推薦用AkShare原因是免費(fèi)、無(wú)需token、接口豐富A股、港股、美股都能覆蓋而且返回的就是Pandas DataFrame代碼上非常順手。不過(guò)AkShare有幾個(gè)使用細(xì)節(jié)你需要提前知道一天內(nèi)頻繁調(diào)用接口容易觸發(fā)對(duì)方的風(fēng)控建議爬取做節(jié)流每次請(qǐng)求后sleep 0.3到0.5秒數(shù)據(jù)偶爾會(huì)返回空值或復(fù)權(quán)因子不一致必須做數(shù)據(jù)清洗。數(shù)據(jù)清洗不是簡(jiǎn)單地dropna要分情況處理日行情數(shù)據(jù)缺失的常用方案是前值填充ffill但前值填充對(duì)收盤(pán)價(jià)只是權(quán)宜之計(jì)如果連續(xù)缺失超過(guò)三天建議直接剔除該時(shí)間段數(shù)據(jù)避免生成錯(cuò)誤的均線和指標(biāo)。入庫(kù)方案我用的是SQLite因?yàn)檎麄€(gè)項(xiàng)目體量不大不需要上MySQL。建表結(jié)構(gòu)建議至少兩張表stock_price存行情快照字段包括code、date、open、high、low、close、volume、amountstock_info存股票基礎(chǔ)信息包括code、name、industry、list_date。兩張表通過(guò)code關(guān)聯(lián)。所有數(shù)據(jù)寫(xiě)入之前先做去重以code, date作為聯(lián)合主鍵后寫(xiě)入的覆蓋先寫(xiě)入的防止反復(fù)爬取產(chǎn)生臟數(shù)據(jù)。這里有一個(gè)實(shí)操中的關(guān)鍵經(jīng)驗(yàn)建議先拉5年以上的日線數(shù)據(jù)存到本地再啟動(dòng)可視化應(yīng)用。我見(jiàn)過(guò)太多人做項(xiàng)目時(shí)圖表畫(huà)到一半就空白排查半天發(fā)現(xiàn)是網(wǎng)絡(luò)斷了或接口限了流。數(shù)據(jù)落地到本地后整個(gè)開(kāi)發(fā)調(diào)試不需要聯(lián)網(wǎng)穩(wěn)得多。如果你要復(fù)現(xiàn)這個(gè)項(xiàng)目先跑一個(gè)月數(shù)據(jù)的demo再拉全量別一上來(lái)就貪多。提示不要在這個(gè)環(huán)節(jié)過(guò)分糾纏實(shí)時(shí)數(shù)據(jù)。實(shí)時(shí)行情需要WebSocket申請(qǐng)授權(quán)、處理推送緩沖區(qū)、應(yīng)對(duì)斷線重連復(fù)雜度會(huì)直接爆炸。實(shí)時(shí)推送針對(duì)畢設(shè)是加分可選項(xiàng)不是必選項(xiàng)。先把日線級(jí)別的歷史數(shù)據(jù)做成穩(wěn)定閉環(huán)再做實(shí)時(shí)擴(kuò)展層層遞進(jìn)風(fēng)險(xiǎn)小得多。3. 可視化模塊的核心實(shí)現(xiàn)K線圖的坑、指標(biāo)疊加的邏輯和交互設(shè)計(jì)可視化模塊是看起來(lái)最值錢(qián)的部分。如果你用Matplotlib畫(huà)普通折線圖雖然也能展示但大概率會(huì)被打回重做——太像上課練習(xí)了。建議用Pyecharts 1.x 版本做交互式圖表。它生成的是HTML格式瀏覽器直接打開(kāi)支持鼠標(biāo)懸停、縮放、平移演示效果比靜態(tài)Matplotlib高出不止一個(gè)檔次。K線圖是可視化模塊的核心。Pyecharts里K線用Kline組件實(shí)現(xiàn)數(shù)據(jù)格式是[open, close, low, high]注意順序和你在其他平臺(tái)看到的OHLC不同。我當(dāng)時(shí)在這塊踩了個(gè)大坑文檔里寫(xiě)的是[open, close, lowest, highest]沒(méi)仔細(xì)看文檔直接按常規(guī)OHLC傳數(shù)據(jù)結(jié)果K線的實(shí)體方向全畫(huà)反了陽(yáng)線陰線完全顛倒。視覺(jué)上看起來(lái)好像也能用但嚴(yán)重誤導(dǎo)判斷。K線之上疊加均線是另一個(gè)常見(jiàn)需求。MA5、MA10、MA20三條均線是標(biāo)配。Pyecharts中可以用Line組件和Kline做overlap層疊但疊的時(shí)候有個(gè)重要參數(shù)不能漏overlap之后的坐標(biāo)軸類型要統(tǒng)一且均線的數(shù)據(jù)長(zhǎng)度要和K線完全對(duì)齊否則尾部錯(cuò)位。更穩(wěn)妥的做法是在K線圖里加dataZoom縮放組件讓用戶滑動(dòng)查看特定時(shí)間窗口而不是一屏塞所有數(shù)據(jù)。除了K線推薦系統(tǒng)決策的依據(jù)還需要幾個(gè)副圖指標(biāo)。成交量是必須的MACD或RSI可以選一個(gè)其中RSI相對(duì)容易解釋——它的計(jì)算不涉及復(fù)雜的EMA遞歸對(duì)答辯更友好。我當(dāng)時(shí)實(shí)現(xiàn)的是成交量柱狀圖 RSI副圖作為附在圖下方的第二和第三個(gè)區(qū)域。這里分享一個(gè)布局層面的心得用Pyecharts的Grid組件實(shí)現(xiàn)多圖拼接時(shí)每個(gè)子圖占位比例要調(diào)好K線主體占60%以上RSI占20%成交量占20%不然視覺(jué)上主次不分。還有一點(diǎn)容易被低估數(shù)據(jù)是按證券代碼分文件存還是統(tǒng)一存一張表后用字段過(guò)濾這直接決定加載性能。如果用一張表存所有數(shù)據(jù)date字段建索引查詢單只股票時(shí)走索引返回很快如果按代碼分文件保存讀取時(shí)邏輯簡(jiǎn)單但文件數(shù)量會(huì)很多。我實(shí)測(cè)下來(lái)SQLite單表加索引的方式綜合性能更好因?yàn)橐粋€(gè)連接搞定所有查詢不用頻繁打開(kāi)關(guān)閉文件。提示股票可視化項(xiàng)目的交互不止是圖表的hover。你要讓用戶能輸入股票代碼切換標(biāo)的、選擇時(shí)間范圍、切換指標(biāo)組合推薦結(jié)果要能聯(lián)動(dòng)到對(duì)應(yīng)K線視圖。這種輸入→展示→互動(dòng)→推薦的閉環(huán)才是答辯時(shí)老師眼中系統(tǒng)完整度的關(guān)鍵評(píng)判點(diǎn)。4. 推薦系統(tǒng)怎么做才有說(shuō)服力三路策略與特征相關(guān)性分析推薦系統(tǒng)是這個(gè)項(xiàng)目的理論高地和得分點(diǎn)。但股票推薦和電商推薦邏輯上有所不同電商推薦可以基于買(mǎi)過(guò)A的人還會(huì)買(mǎi)B這種協(xié)同過(guò)濾而股票推薦需要結(jié)合相似K線形態(tài)和技術(shù)指標(biāo)共振來(lái)做更解釋得通的推薦。我實(shí)際采用的是三路推薦策略最終結(jié)果按加權(quán)融合輸出。第一路是相似度推薦基于協(xié)同過(guò)濾思想不過(guò)不是算用戶相似度而是算股票在特征空間的距離。特征怎么選用相對(duì)變化量而不用絕對(duì)價(jià)格這一點(diǎn)至關(guān)重要——直接把收盤(pán)價(jià)丟進(jìn)歐氏距離計(jì)算是沒(méi)有意義的因?yàn)楦邇r(jià)股和低價(jià)股天然差很多量綱完全不同。我選擇了近20日收益率、近5日波動(dòng)率、RSI值、成交量變化率這幾個(gè)特征做Z-score標(biāo)準(zhǔn)化隨機(jī)森林Random Forest中常用的特征重要性方法可以用來(lái)給每個(gè)特征賦初始權(quán)重再用K近鄰KNN找出距離最近的Top-N只股票。第二路是技術(shù)面共振推薦規(guī)則引擎的設(shè)計(jì)思路。設(shè)置幾條可解釋的規(guī)則例如MA5上穿MA10且RSI在50~70之間且成交量放量1.5倍以上條件全部滿足就標(biāo)記為共振信號(hào)候選。這一路的價(jià)值在于可解釋性強(qiáng)每條推薦理由都能對(duì)應(yīng)到具體指標(biāo)特征。第三路是用戶偏好協(xié)同過(guò)濾?;谟脩舻臍v史瀏覽/收藏/買(mǎi)入行為記錄構(gòu)建行為矩陣。用戶A瀏覽了某只股票系統(tǒng)在推薦時(shí)統(tǒng)計(jì)該股票被類似用戶偏好集中的行為進(jìn)一步生成推薦候選池。這里的類似用戶通過(guò)行為記錄的余弦相似度計(jì)算。這三路需要做數(shù)據(jù)融合。我當(dāng)時(shí)用的策略是加權(quán)排序相似度推薦權(quán)重在0.5形態(tài)共振在0.3協(xié)同過(guò)濾在0.2。各路的輸出還是候選集合并集取得分最高的Top-3作為最終結(jié)果。為什么是Top-3列表太長(zhǎng)會(huì)分散用戶注意力而且推薦系統(tǒng)的評(píng)測(cè)指標(biāo)中Top-3的精確率遠(yuǎn)比Top-10更直觀、更容易在答辯時(shí)解釋。這里要回答一個(gè)常見(jiàn)問(wèn)題為什么不做嚴(yán)格的收益率預(yù)測(cè)模型因?yàn)槟鞘橇硪粋€(gè)研究問(wèn)題涉及時(shí)序平穩(wěn)性、滑點(diǎn)、手續(xù)費(fèi)、過(guò)擬合等一系列復(fù)雜工程在課程設(shè)計(jì)的時(shí)間框架內(nèi)很難做得嚴(yán)謹(jǐn)。而推薦系統(tǒng)的定位是輔助決策工具它的評(píng)估指標(biāo)是推薦列表中候選股票池與歷史強(qiáng)勢(shì)股的命中率我建議采用命中率N做評(píng)估推薦Top-5中包含未來(lái)20日漲幅前10%的股票算一次命中最終統(tǒng)計(jì)整體命中率。這個(gè)指標(biāo)實(shí)現(xiàn)簡(jiǎn)單、表達(dá)清晰、答辯時(shí)也拿得出手。5. 系統(tǒng)整體聯(lián)調(diào)時(shí)的三個(gè)大坑與最終版本的處理方式設(shè)計(jì)文檔寫(xiě)得再漂亮聯(lián)調(diào)才能發(fā)現(xiàn)真問(wèn)題。這個(gè)項(xiàng)目我印象最深的是三個(gè)坑寫(xiě)出來(lái)供你參考。第一個(gè)坑是中文編碼。Pyecharts在HTML中默認(rèn)輸出UTF-8一般沒(méi)問(wèn)題。但如果股票名稱中含有繁體字或者特殊字符如ST和退標(biāo)志在文件名、命令行輸出、日志打印時(shí)極易出現(xiàn)UnicodeEncodeError。我的處理方式是在項(xiàng)目啟動(dòng)入口處設(shè)置sys.stdout.reconfigure(encodingutf-8)并在所有文件寫(xiě)入操作中顯式指定編碼方式。這個(gè)坑雖小但足以讓演示現(xiàn)場(chǎng)直接崩潰。第二個(gè)坑是推薦結(jié)果的時(shí)間穿越。這是最容易被忽視的邏輯漏洞用今天的數(shù)據(jù)算出的特征去匹配包含未來(lái)數(shù)據(jù)的樣本得出的相似股票純屬作弊。處理方式是數(shù)據(jù)切分必須嚴(yán)格按時(shí)間切——訓(xùn)練推薦模型的KNN只使用截止到某個(gè)交易日的歷史數(shù)據(jù)對(duì)某一天的推薦只允許使用這一天之前的數(shù)據(jù)。代碼上只需要對(duì)特征矩陣做時(shí)間過(guò)濾即可但很多人就是沒(méi)做。第三個(gè)坑是SQLite并發(fā)讀寫(xiě)。如果用Flask做Web界面可能同時(shí)多個(gè)請(qǐng)求訪問(wèn)數(shù)據(jù)庫(kù)。SQLite默認(rèn)同一時(shí)刻只允許一個(gè)進(jìn)程寫(xiě)庫(kù)并發(fā)高時(shí)會(huì)出現(xiàn)database is locked。我建議連接時(shí)設(shè)置timeout10并把高頻讀操作做成緩存。另一個(gè)更省事的方案是所有查詢接口加一個(gè)統(tǒng)一緩存裝飾器短期數(shù)據(jù)用字典存內(nèi)存過(guò)期時(shí)間設(shè)置為5分鐘。這樣不僅能解決鎖問(wèn)題響應(yīng)速度還快了很多。最終版本的實(shí)現(xiàn)效果我用一句話概括用戶在系統(tǒng)頁(yè)輸入股票代碼和時(shí)間范圍系統(tǒng)展示K線和指標(biāo)副圖用戶點(diǎn)擊相似股票按鈕生成推薦候選并按得分排序每條推薦右側(cè)顯示推薦理由標(biāo)簽用戶可以將推薦結(jié)果標(biāo)注為感興趣/不感興趣行為數(shù)據(jù)回流數(shù)據(jù)庫(kù)供后續(xù)協(xié)同過(guò)濾更新。6. 文檔與PPT的內(nèi)容編排答辯視角下的側(cè)重優(yōu)化項(xiàng)目代碼寫(xiě)完了只是完成50%。課程設(shè)計(jì)的另一半是文檔和PPT而這兩樣?xùn)|西決定了答辯老師第一印象。文檔我建議分成五章緒論、需求分析、系統(tǒng)設(shè)計(jì)架構(gòu)數(shù)據(jù)庫(kù)設(shè)計(jì)、系統(tǒng)實(shí)現(xiàn)界面截圖核心代碼片段、系統(tǒng)測(cè)試功能測(cè)試用例表推薦命中率結(jié)果分析。如果學(xué)校要求設(shè)計(jì)與實(shí)現(xiàn)這個(gè)標(biāo)題章節(jié)命名直接對(duì)應(yīng)標(biāo)題邏輯最穩(wěn)。需要注意不要事無(wú)巨細(xì)把所有源碼都貼進(jìn)去核心類和數(shù)據(jù)流圖才是老師最想看的。關(guān)于PPT我強(qiáng)烈建議控制在15到20頁(yè)以內(nèi)。結(jié)構(gòu)設(shè)計(jì)注意突出數(shù)據(jù)流向這條主線數(shù)據(jù)從哪來(lái)處理過(guò)后長(zhǎng)什么樣特征如何構(gòu)建推薦如何計(jì)算用戶如何看到結(jié)果。每頁(yè)只放一個(gè)核心圖寧可少講不要堆砌。答辯演示環(huán)節(jié)先演示一個(gè)完整流程輸入股票代碼→展示K線→點(diǎn)擊推薦→展示Top-3候選及推薦理由。這個(gè)流程走完再講解核心代碼片段和評(píng)估結(jié)果整個(gè)邏輯鏈清晰反而顯得研究得很透。一個(gè)加分的小設(shè)計(jì)在系統(tǒng)測(cè)試章節(jié)加入一個(gè)簡(jiǎn)單的對(duì)比實(shí)驗(yàn)表格對(duì)比隨機(jī)選股與推薦系統(tǒng)的命中率。我當(dāng)時(shí)實(shí)測(cè)的數(shù)據(jù)推薦Top-5在測(cè)試集上的命中率大約為27%到35%左右隨機(jī)選股基線只有4%。這個(gè)對(duì)比數(shù)據(jù)放在PPT里非常有力。別虛報(bào)數(shù)據(jù)真實(shí)測(cè)出來(lái)的就好老師更在意你是否理解評(píng)測(cè)方法。7. 源碼交付前最后該檢查的核心點(diǎn)項(xiàng)目的源碼要保證讓另一個(gè)同學(xué)拿到也能跑通。這一點(diǎn)在課程設(shè)計(jì)提交時(shí)特別重要因?yàn)橛行├蠋煴旧聿豢创a但會(huì)抽測(cè)運(yùn)行。提交前檢查清單可以做得很簡(jiǎn)潔首次運(yùn)行是否會(huì)自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)并拉取示例數(shù)據(jù)requirements.txt是否包含完整的第三方庫(kù)且版本號(hào)已鎖README是否寫(xiě)清楚運(yùn)行步驟建議三步走安裝依賴、運(yùn)行初始化腳本、啟動(dòng)主程序是否附帶一個(gè)演示用的數(shù)據(jù)集文件防止評(píng)審現(xiàn)場(chǎng)網(wǎng)絡(luò)連不上外部的數(shù)據(jù)源。其中演示用數(shù)據(jù)集文件可以重點(diǎn)加上。我當(dāng)時(shí)放了一份近兩年的日線數(shù)據(jù)CSV壓縮包README里寫(xiě)明如網(wǎng)絡(luò)不可用可使用本地?cái)?shù)據(jù)文件初始化數(shù)據(jù)庫(kù)。這個(gè)細(xì)節(jié)在答辯時(shí)遇到斷網(wǎng)環(huán)境簡(jiǎn)直是救命稻草。還有一個(gè)細(xì)節(jié)是項(xiàng)目的目錄結(jié)構(gòu)要清晰data/、core/、visual/、recommender/、web/、docs/各歸其位入口文件放在根目錄下命名為main.py或app.py。不要把所有代碼堆在根目錄下這是代碼潔癖的問(wèn)題。根據(jù)我這幾年看過(guò)各種課程設(shè)計(jì)的經(jīng)驗(yàn)?zāi)苷嬲玫礁叻值捻?xiàng)目不一定用了最前沿的技術(shù)但一定在數(shù)據(jù)鏈路完整、文檔邏輯清晰、代碼可直接運(yùn)行這三個(gè)基本盤(pán)上做得扎實(shí)??梢暬K負(fù)責(zé)直觀展示你的工作量推薦模塊負(fù)責(zé)體現(xiàn)你的思考深度文檔和PPT負(fù)責(zé)讓這一切被看見(jiàn)。把這三件事做透這個(gè)課題拿高分并不難。