戰(zhàn):Python量化回測(cè)從入門(mén)到多股策略?xún)?yōu)化)
簡(jiǎn)介面向量化交易初學(xué)者的Backtrader中文入門(mén)教程PDF由CSDN博主整理內(nèi)容整合了官方文檔的核心章節(jié)與源碼解讀適合熟悉Python但剛接觸回測(cè)框架的開(kāi)發(fā)者系統(tǒng)學(xué)習(xí)。教程從Backtrader的安裝配置講起依次覆蓋Cerebro引擎、數(shù)據(jù)源、策略編寫(xiě)、指標(biāo)庫(kù)、訂單與傭金、分析器及實(shí)盤(pán)交易等核心概念并以示例代碼演示快速回測(cè)流程。資源共1個(gè)文件、PDF格式壓縮包大小僅1MB便攜易讀目前已有1344人學(xué)習(xí)或下載。除基礎(chǔ)入門(mén)外還包含策略與Cerebro源碼參考、TA-Lib集成、tushare與聚寬數(shù)據(jù)源接入、性能優(yōu)化和內(nèi)存管理進(jìn)階技巧可幫助讀者從調(diào)用工具走向理解底層原理快速搭建自己的量化回測(cè)系統(tǒng)。1. 寫(xiě)在開(kāi)始Backtrader到底是什么能幫你干什么第一次接觸量化交易的人十有八九會(huì)被兩個(gè)門(mén)檻卡住。第一個(gè)門(mén)檻是策略思路——你到底想怎么賺錢(qián)趨勢(shì)跟蹤、均值回歸還是套利第二個(gè)門(mén)檻是技術(shù)實(shí)現(xiàn)——你腦子里想的那套規(guī)則怎么才能在歷史數(shù)據(jù)上快速驗(yàn)證一遍。實(shí)話(huà)說(shuō)策略思路這東西急不來(lái)但技術(shù)實(shí)現(xiàn)完全有捷徑可走而這個(gè)捷徑通常就叫Backtrader。Backtrader是一個(gè)開(kāi)源的Python量化回測(cè)框架純本地運(yùn)行數(shù)據(jù)自己下策略自己寫(xiě)結(jié)果自己看。它做的事情說(shuō)白了就一件把你對(duì)“什么時(shí)候買(mǎi)入、什么時(shí)候賣(mài)出”的一套規(guī)則扔進(jìn)歷史行情里跑一遍告訴你這套規(guī)則過(guò)去幾年能賺多少錢(qián)、最大回撤是多少、勝率有多高。這種驗(yàn)證方式在行業(yè)內(nèi)叫回測(cè)而B(niǎo)acktrader幾乎是個(gè)人量化愛(ài)好者上手回測(cè)的默認(rèn)選擇。這個(gè)框架最大的特點(diǎn)一是不需要你從零去寫(xiě)撮合、滑點(diǎn)、手續(xù)費(fèi)這些交易底層邏輯框架全幫你處理好了二是策略代碼用起來(lái)很順手定義好買(mǎi)賣(mài)信號(hào)就行三是生態(tài)成熟網(wǎng)上關(guān)于它的教程、案例、踩坑記錄非常多遇到問(wèn)題基本都能搜到答案。我當(dāng)年從研究pandas手動(dòng)回測(cè)轉(zhuǎn)到Backtrader最大的感受就是回測(cè)這件事從“造輪子”變成了“用輪子”省下來(lái)的時(shí)間全都可以花在打磨策略本身上。這篇教程適合三類(lèi)人看剛接觸量化、想找一個(gè)靠譜回測(cè)工具的新手已經(jīng)在用其他框架但被各種配置搞到頭疼的開(kāi)發(fā)者以及有交易想法但不知道怎么快速驗(yàn)證的投資者。我會(huì)從安裝開(kāi)始講到核心概念再給你一個(gè)能跑通的完整策略最后說(shuō)說(shuō)多股回測(cè)怎么玩。內(nèi)容不深但足夠讓你今天就把第一個(gè)回測(cè)跑起來(lái)。2. 環(huán)境準(zhǔn)備與最小化框架搭建2.1 安裝比你想象中還要簡(jiǎn)單Backtrader的安裝過(guò)程幾乎沒(méi)有什么可說(shuō)的pip一行命令搞定pip install backtrader如果你的環(huán)境里有多個(gè)Python版本記得用pip3或者先激活對(duì)應(yīng)的虛擬環(huán)境。裝完之后可以去Python交互環(huán)境里驗(yàn)證一下import backtrader print(backtrader.__version__)能正常打印版本號(hào)就說(shuō)明安裝成功了。這里提醒一點(diǎn)Backtrader在Python 3.7以上的版本表現(xiàn)都很穩(wěn)定如果你還在用Python 2或者很老的3.x版本建議升級(jí)一下否則可能會(huì)有兼容性報(bào)錯(cuò)。之前有個(gè)朋友卡在導(dǎo)入階段后來(lái)發(fā)現(xiàn)是Python 3.5的歷史遺留環(huán)境換了Python 3.9之后一切正常。2.2 最小回測(cè)代碼跑通再說(shuō)理解很多教程喜歡先畫(huà)架構(gòu)圖再講概念最后才給代碼。我的習(xí)慣反著來(lái)——先給你一段能跑通的代碼讓程序跑起來(lái)你親眼看到收益曲線(xiàn)了再回頭理解每個(gè)組件是什么。這樣學(xué)起來(lái)沒(méi)那么抽象。以最經(jīng)典的雙均線(xiàn)策略為例當(dāng)短期均線(xiàn)從下往上穿過(guò)長(zhǎng)期均線(xiàn)時(shí)買(mǎi)入當(dāng)短期均線(xiàn)從上往下穿過(guò)長(zhǎng)期均線(xiàn)時(shí)賣(mài)出。用Backtrader實(shí)現(xiàn)大概是下面這個(gè)結(jié)構(gòu)import backtrader as bt # 1. 定義策略 class SmaCross(bt.Strategy): params ((pfast, 10), (pslow, 30)) def __init__(self): sma_fast bt.indicators.SMA(periodself.p.pfast) sma_slow bt.indicators.SMA(periodself.p.pslow) self.crossover bt.indicators.CrossOver(sma_fast, sma_slow) def next(self): if not self.position: if self.crossover 0: self.buy() elif self.crossover 0: self.close() # 2. 獲取數(shù)據(jù) data bt.feeds.YahooFinanceData(datanameAAPL, fromdatedatetime(2020, 1, 1), todatedatetime(2023, 1, 1)) # 3. 組裝引擎并運(yùn)行 cerebro bt.Cerebro() cerebro.adddata(data) cerebro.addstrategy(SmaCross) cerebro.broker.setcash(100000.0) cerebro.run() cerebro.plot()這段代碼雖然短但已經(jīng)覆蓋了Backtrader的核心五要素策略、數(shù)據(jù)、引擎Cerebro、經(jīng)紀(jì)人Broker、圖表。跑起來(lái)之后你會(huì)看到一張帶買(mǎi)賣(mài)點(diǎn)的K線(xiàn)圖圖上標(biāo)注了每一筆交易的位置那時(shí)候你對(duì)“回測(cè)”這個(gè)概念就有了最直觀(guān)的理解。3. 核心概念拆解用生活類(lèi)比一次講清楚3.1 Cerebro整個(gè)回測(cè)的中央調(diào)度中心Cerebro翻譯過(guò)來(lái)是“大腦”在Backtrader里它就是整個(gè)框架的中樞系統(tǒng)。所有組件都要加到Cerebro里才能運(yùn)行包括數(shù)據(jù)、策略、分析器、觀(guān)察器甚至傭金模型和滑點(diǎn)設(shè)置。你可以把它理解成一個(gè)導(dǎo)演——數(shù)據(jù)是演員策略是劇本經(jīng)紀(jì)人負(fù)責(zé)管錢(qián)Cerebro負(fù)責(zé)導(dǎo)演完整場(chǎng)戲。實(shí)際編碼中你至少要掌握Cerebro的幾個(gè)核心方法adddata()添加數(shù)據(jù)addstrategy()添加策略注意這里是傳策略類(lèi)而不是實(shí)例broker.setcash()設(shè)置初始資金run()開(kāi)始回測(cè)plot()畫(huà)圖。還有后面會(huì)講到的addanalyzer()和addobserver()一個(gè)用來(lái)統(tǒng)計(jì)績(jī)效指標(biāo)一個(gè)用來(lái)在圖上畫(huà)輔助信息。3.2 Broker那個(gè)幫你處理交易細(xì)節(jié)的經(jīng)紀(jì)人很多初學(xué)回測(cè)的人會(huì)忽略Broker的重要性但恰恰是它幫你處理了成交價(jià)、手續(xù)費(fèi)、滑點(diǎn)這些細(xì)節(jié)。Backtrader里的Broker默認(rèn)支持實(shí)時(shí)成交和成交量檢查這意味著當(dāng)你發(fā)出買(mǎi)入指令時(shí)Broker會(huì)檢查當(dāng)前K線(xiàn)是否有足夠的成交量來(lái)成交而不是無(wú)腦按收盤(pán)價(jià)給你成交。對(duì)于新手我建議一開(kāi)始就設(shè)置好傭金哪怕設(shè)個(gè)萬(wàn)三、千一都行這樣回測(cè)結(jié)果里已經(jīng)包含交易成本了。設(shè)置方法很簡(jiǎn)單cerebro.broker.setcommission(commission0.001)這里0.001代表千分之一的手續(xù)費(fèi)比例。別小看這一步我見(jiàn)過(guò)太多人跑回測(cè)沒(méi)設(shè)手續(xù)費(fèi)結(jié)果每次都覺(jué)得自己找到了印鈔機(jī)實(shí)盤(pán)一跑就露餡。傭金對(duì)策略收益的侵蝕遠(yuǎn)比你想象的大尤其是高頻交易場(chǎng)景傭金可能就是決定盈虧的關(guān)鍵因素。3.3 Strategy與Indicator策略與指標(biāo)的分層邏輯Strategy是Backtrader里最核心的類(lèi)你要寫(xiě)的就是繼承它然后重寫(xiě)幾個(gè)關(guān)鍵方法。最常用的是__init__和next兩個(gè)方法。__init__里面負(fù)責(zé)創(chuàng)建指標(biāo)、定義線(xiàn)路linenext里面寫(xiě)在每根K線(xiàn)上要執(zhí)行的邏輯。這里要特別注意Backtrader的一個(gè)設(shè)計(jì)哲學(xué)__init__里創(chuàng)建的指標(biāo)Backtrader會(huì)做自動(dòng)預(yù)計(jì)算能夠在每根K線(xiàn)到來(lái)之前就準(zhǔn)備好所有指標(biāo)值。所以你在__init__里寫(xiě)指標(biāo)計(jì)算在next里用self.crossover[0]來(lái)取值性能會(huì)好很多。Indicator是Backtrader內(nèi)置的豐富指標(biāo)庫(kù)SMA、EMA、MACD、RSI、布林帶、ATR全都有直接一行代碼就能創(chuàng)建。你還可以自己寫(xiě)指標(biāo)繼承bt.Indicator就行。這個(gè)我們先不展開(kāi)后面實(shí)操部分會(huì)用到。3.4 Data Feed數(shù)據(jù)從哪來(lái)怎么接入最省事Backtrader支持的數(shù)據(jù)源很多內(nèi)置的就有Yahoo財(cái)經(jīng)、CSV文件、Pandas DataFrame、SQLite數(shù)據(jù)庫(kù)等。我最推薦大家從CSV或者Pandas接入原因很簡(jiǎn)單——可控性強(qiáng)。無(wú)論是從Tushare、AkShare這些免費(fèi)數(shù)據(jù)源拿數(shù)據(jù)還是自己用爬蟲(chóng)下載最終你都能整理成DataFrame然后喂給Backtrader不用被某個(gè)固定數(shù)據(jù)源綁定。使用Pandas數(shù)據(jù)源的示例import pandas as pd from datetime import datetime df pd.read_csv(AAPL.csv, parse_datesTrue, index_coldate) data bt.feeds.PandasData(datanamedf)這里需要注意的是Backtrader默認(rèn)會(huì)識(shí)別open、high、low、close、volume、openinterest這幾列。如果你的DataFrame列名不是這套標(biāo)準(zhǔn)命名就得用PandasData的參數(shù)做映射data bt.feeds.PandasData( datanamedf, datetimedate, open開(kāi)盤(pán)價(jià), high最高價(jià), low最低價(jià), close收盤(pán)價(jià), volume成交量 )把數(shù)據(jù)準(zhǔn)備好之后別忘了檢查時(shí)間范圍和數(shù)據(jù)完整性。很多回測(cè)結(jié)果失真不是因?yàn)椴呗詫?xiě)錯(cuò)了而是數(shù)據(jù)里有缺口或者臟數(shù)據(jù)這個(gè)問(wèn)題在后面排查部分我會(huì)詳細(xì)說(shuō)。4. 實(shí)操?gòu)膶?xiě)一個(gè)自己的策略到看懂回測(cè)報(bào)告4.1 策略代碼的完整骨架我把自己常用的策略模板貼出來(lái)以后你寫(xiě)任何策略都可以在這個(gè)骨架上修改。import backtrader as bt import datetime class MyStrategy(bt.Strategy): def __init__(self): self.sma_fast bt.indicators.SMA(period10) self.sma_slow bt.indicators.SMA(period30) self.cross bt.indicators.CrossOver(self.sma_fast, self.sma_slow) def next(self): # 當(dāng)前持倉(cāng)判定 if self.position.size 0: if self.cross[0] 0: self.buy(size100) else: if self.cross[0] 0: self.close()在這個(gè)模板里self.position.size表示當(dāng)前持倉(cāng)數(shù)量為0說(shuō)明空倉(cāng)。self.buy(size100)指定了買(mǎi)入100股self.close()表示清倉(cāng)。你看買(mǎi)賣(mài)就是這么簡(jiǎn)單。但光有買(mǎi)賣(mài)還不夠你總想知道這策略跑完到底賺了多少、回撤多大所以需要在Cerebro里加分析器cerebro bt.Cerebro() cerebro.addstrategy(MyStrategy) cerebro.addanalyzer(bt.analyzers.SharpeRatio, _namesharpe) cerebro.addanalyzer(bt.analyzers.DrawDown, _namedrawdown) cerebro.addanalyzer(bt.analyzers.Returns, _namereturns) results cerebro.run() strategy results[0] print(夏普比率:, strategy.analyzers.sharpe.get_analysis()) print(回撤:, strategy.analyzers.drawdown.get_analysis()) print(收益:, strategy.analyzers.returns.get_analysis())這里我加了三類(lèi)分析器夏普比率反映風(fēng)險(xiǎn)調(diào)整后的收益回撤反映策略最壞情況下的虧損幅度收益反映最終的賺錢(qián)能力。這三個(gè)指標(biāo)基本夠你評(píng)估一個(gè)策略的初步質(zhì)量了。4.2 參數(shù)優(yōu)化別只試一組參數(shù)策略寫(xiě)出來(lái)之后參數(shù)的選擇直接決定了回測(cè)結(jié)果。均線(xiàn)用5日和20日還是10日和60日這個(gè)不能拍腦袋最常用的方法是做參數(shù)網(wǎng)格搜索。Backtrader對(duì)此有非常好的支持直接用addstrategy傳參數(shù)列表就行cerebro.optstrategy(MyStrategy, sma_fastrange(5, 21, 5), sma_slowrange(20, 81, 10))注意optstrategy和addstrategy的區(qū)別addstrategy跑一組參數(shù)optstrategy會(huì)遍歷所有參數(shù)組合并分別回測(cè)。參數(shù)優(yōu)化完成后可以對(duì)比不同參數(shù)組合的收益、回撤和夏普比率選出相對(duì)穩(wěn)健的一組。這里要強(qiáng)調(diào)一個(gè)新手最容易犯的錯(cuò)誤參數(shù)過(guò)度優(yōu)化。如果你把參數(shù)調(diào)到過(guò)去數(shù)據(jù)表現(xiàn)最好的位置不代表未來(lái)也能賺錢(qián)因?yàn)闅v史高點(diǎn)和低點(diǎn)已經(jīng)被這組參數(shù)“記憶”住了。業(yè)內(nèi)管這叫過(guò)擬合。我的建議是參數(shù)優(yōu)化結(jié)果一定要分成樣本內(nèi)和樣本外做驗(yàn)證或者做滾動(dòng)回測(cè)保證策略在不同時(shí)間段都穩(wěn)定而不是只在一段特定行情里表現(xiàn)好。4.3 回測(cè)報(bào)告怎么看什么樣的策略算好跑完回測(cè)別急著激動(dòng)先把幾個(gè)關(guān)鍵指標(biāo)讀懂指標(biāo)含義經(jīng)驗(yàn)參考總收益率回測(cè)期間累計(jì)收益越高越好但要結(jié)合回撤看年化收益率折算成年度的收益水平超過(guò)年化20%就要警惕過(guò)擬合最大回撤從最高點(diǎn)到最低點(diǎn)的最大跌幅20%以?xún)?nèi)相對(duì)可控夏普比率單位風(fēng)險(xiǎn)獲得的超額收益大于1算及格大于2算優(yōu)秀交易次數(shù)整個(gè)回測(cè)周期內(nèi)買(mǎi)賣(mài)總數(shù)太少樣本不足太多手續(xù)費(fèi)侵蝕勝率盈利交易占總交易的比例趨勢(shì)策略勝率普遍低于50%屬正常有些新手看到總收益率300%就興奮得睡不著其實(shí)完全沒(méi)必要。正確的打開(kāi)方式應(yīng)該是首先看最大回撤能不能接受再看交易次數(shù)會(huì)不會(huì)太少最后把收益率和基準(zhǔn)指數(shù)做對(duì)比。如果一只策略過(guò)去三年收益只有20%但你拿的是大牛市那這個(gè)策略實(shí)際是跑輸市場(chǎng)的不值得實(shí)盤(pán)。5. 進(jìn)階實(shí)戰(zhàn)backtrader多股回測(cè)該怎么做5.1 為什么要做多股回測(cè)單只股票的回測(cè)有個(gè)天然缺陷你的策略可能只在這只股票上表現(xiàn)好換成另一只就完全失效。多股回測(cè)的核心價(jià)值在于驗(yàn)證策略的普適性——把同樣的策略放在多只股票上跑一遍如果大多數(shù)都能盈利說(shuō)明策略捕捉到的是市場(chǎng)規(guī)律而不是某只股票的運(yùn)氣。另外多股回測(cè)也是構(gòu)建投資組合的基礎(chǔ)。單只股票的波動(dòng)率通常很高通過(guò)多品種分散配置可以在不降低預(yù)期收益的情況下顯著降低組合的波動(dòng)。我最早做多股回測(cè)的初衷就是把資金分散在十只不同的股票上每只股票根據(jù)同樣的策略信號(hào)獨(dú)立交易整體組合的收益曲線(xiàn)比單只股票平滑太多了。5.2 多股回測(cè)的兩種常見(jiàn)實(shí)現(xiàn)在Backtrader里多股回測(cè)的實(shí)現(xiàn)方式大致分兩種。第一種是把多只股票的數(shù)據(jù)全部塞進(jìn)同一個(gè)Cerebro里每只股票作為一個(gè)數(shù)據(jù)源。策略里可以用self.datas[i]來(lái)引用不同的數(shù)據(jù)源。這種方式適合所有股票用同一套規(guī)則策略在每只股票上獨(dú)立判斷信號(hào)并獨(dú)立建倉(cāng)。第二種是為每只股票單獨(dú)創(chuàng)建一個(gè)Cerebro實(shí)例跑完再匯總結(jié)果。這種方案代碼寫(xiě)起來(lái)更直觀(guān)邏輯獨(dú)立但缺點(diǎn)是如果股票數(shù)量比較多循環(huán)跑起來(lái)相對(duì)慢一些。先看第一種方案的邏輯示例cerebro bt.Cerebro() for ticker in [AAPL, MSFT, GOOGL, AMZN, TSLA]: df load_data_from_somewhere(ticker) data bt.feeds.PandasData(datanamedf) cerebro.adddata(data, nameticker) cerebro.addstrategy(MultiStockStrategy)在這個(gè)策略里如果想要分別處理每只股票的信號(hào)可以在next()里遍歷self.datasclass MultiStockStrategy(bt.Strategy): def __init__(self): self.crosses {} for data in self.datas: sma_fast bt.indicators.SMA(data, period10) sma_slow bt.indicators.SMA(data, period30) self.crosses[data._name] bt.indicators.CrossOver(sma_fast, sma_slow) def next(self): for data in self.datas: name data._name pos self.getposition(data) if not pos.size: if self.crosses[name][0] 0: self.buy(datadata) else: if self.crosses[name][0] 0: self.close(datadata)注意bt.indicators.SMA(data, period10)這里把data作為第一個(gè)參數(shù)傳進(jìn)去了表示指標(biāo)基于哪條數(shù)據(jù)線(xiàn)計(jì)算。self.getposition(data)返回該數(shù)據(jù)源上的當(dāng)前倉(cāng)位。self.buy(datadata)明確指定買(mǎi)入哪個(gè)數(shù)據(jù)源這樣每只股票就能獨(dú)立運(yùn)行同一套策略。5.3 組合維度分析關(guān)注整體凈值和曲線(xiàn)多股回測(cè)和單股回測(cè)在后處理上最大的區(qū)別是你需要關(guān)注整個(gè)組合的表現(xiàn)而不是單個(gè)標(biāo)的的表現(xiàn)。Backtrader默認(rèn)會(huì)把所有倉(cāng)位合并計(jì)算賬戶(hù)凈值所以results[0].analyzers.returns.get_analysis()拿到的就是整個(gè)組合的收益情況。如果把每只股票分開(kāi)統(tǒng)計(jì)可以給每個(gè)數(shù)據(jù)源單獨(dú)添加觀(guān)察器或者在策略里自定義執(zhí)行邏輯用列表記錄每筆交易的盈虧來(lái)源。一個(gè)簡(jiǎn)單做法是在策略里維護(hù)一個(gè)字典按數(shù)據(jù)源名字記錄每筆交易的毛利和凈利最終回測(cè)輸出時(shí)再做匯總。class MultiStockStrategy(bt.Strategy): def __init__(self): self.trades {} def notify_trade(self, trade): if trade.isclosed: name trade.data._name if name not in self.trades: self.trades[name] {count: 0, profit: 0.0} self.trades[name][count] 1 self.trades[name][profit] trade.pnlcomm def stop(self): # 回測(cè)結(jié)束時(shí)輸出每只股票的交易統(tǒng)計(jì) for name, stat in self.trades.items(): print(f{name}: 交易次數(shù) {stat[count]}, 凈利 {stat[profit]:.2f})notify_trade是Backtrader里的一個(gè)回調(diào)方法當(dāng)一筆交易結(jié)束時(shí)自動(dòng)觸發(fā)trade.pnlcomm表示扣除手續(xù)費(fèi)后的凈盈虧。stop方法在回測(cè)結(jié)束時(shí)自動(dòng)調(diào)用正好適合做統(tǒng)計(jì)匯總輸出。5.4 多股回測(cè)必須注意的三個(gè)坑第一數(shù)據(jù)對(duì)齊。不同股票的交易日期不完全一致有的股票中途停牌有的股票上市時(shí)間晚。Backtrader默認(rèn)會(huì)以所有數(shù)據(jù)源的共同時(shí)間范圍作為回測(cè)區(qū)間如果其中有一只股票數(shù)據(jù)從2022年才開(kāi)始那2020到2021年回測(cè)就根本不會(huì)跑。你得先檢查每只股票的數(shù)據(jù)覆蓋范圍要么統(tǒng)一長(zhǎng)度要么明確自己接受較短的共同區(qū)間。第二行情的未來(lái)函數(shù)。如果你一不小心把數(shù)據(jù)算錯(cuò)——比如用了未來(lái)某天的價(jià)格計(jì)算指標(biāo)——回測(cè)收益會(huì)漂亮得離譜但這個(gè)收益完全是假的。Backtrader的指標(biāo)機(jī)制本身就規(guī)避了大部分未來(lái)函數(shù)問(wèn)題但你在自定義邏輯時(shí)要小心比如在next里使用了self.data.close[0]是當(dāng)前這根K線(xiàn)的收盤(pán)價(jià)用self.data.close[-1]才是上一根K線(xiàn)的價(jià)格千萬(wàn)別混淆。第三過(guò)多的股票數(shù)量會(huì)拖慢回測(cè)速度。數(shù)據(jù)源越多每根K線(xiàn)上需要執(zhí)行的邏輯就越多回測(cè)時(shí)間會(huì)線(xiàn)性增加。如果股票數(shù)量上百建議考慮用向量化回測(cè)的方式或者減少回測(cè)時(shí)間范圍先做初步驗(yàn)證確認(rèn)策略有效后再擴(kuò)展到全量數(shù)據(jù)。6. 常見(jiàn)問(wèn)題速查與避坑心得6.1 我回測(cè)的收益曲線(xiàn)為什么看起來(lái)太完美如果某一天你的賬戶(hù)凈值曲線(xiàn)近乎直線(xiàn)向上沒(méi)有明顯回撤先別高興大概率是代碼里有未來(lái)函數(shù)。常見(jiàn)原因是回測(cè)代碼里用了整段數(shù)據(jù)的統(tǒng)計(jì)量比如用了全量?jī)r(jià)格的最高點(diǎn)作為止盈目標(biāo)或者用了未來(lái)數(shù)據(jù)計(jì)算指標(biāo)??梢园奄I(mǎi)賣(mài)點(diǎn)逐一核對(duì)確保策略只在當(dāng)前時(shí)刻能看到的信息上做決策。6.2 回測(cè)結(jié)果為什么跟實(shí)盤(pán)差異巨大回測(cè)和實(shí)盤(pán)的差異來(lái)源主要有三個(gè)手續(xù)費(fèi)沒(méi)配全、滑點(diǎn)沒(méi)考慮、成交價(jià)假設(shè)和實(shí)際不同。證券交易除了傭金還有印花稅、過(guò)戶(hù)費(fèi)如果策略交易頻率高這些費(fèi)用會(huì)大大影響最終收益。另外實(shí)盤(pán)中大單買(mǎi)入時(shí)會(huì)推動(dòng)價(jià)格上漲你能成交的價(jià)格往往比當(dāng)前價(jià)格略差這就是滑點(diǎn)。Backtrader支持設(shè)置滑點(diǎn)建議在更接近實(shí)際情況的假設(shè)下再評(píng)估策略。6.3 數(shù)據(jù)源報(bào)錯(cuò)、日期不對(duì)、數(shù)據(jù)缺失怎么辦遇到數(shù)據(jù)問(wèn)題先打印數(shù)據(jù)范圍做檢查data bt.feeds.PandasData(datanamedf) print(data.fromdate, data.todate) print(len(data))如果數(shù)據(jù)量和預(yù)期不一致多半是DataFrame里有空行或者索引不是按時(shí)間順序排列的。處理辦法是先做好數(shù)據(jù)清理去重、排序、填充缺失值這些步驟最好在接入Backtrader之前做干凈。數(shù)據(jù)是回測(cè)的地基地基不牢再好的策略都是空中樓閣。6.4 關(guān)于參數(shù)優(yōu)化的最后一點(diǎn)心得我自己踩過(guò)最大的坑就是沉迷參數(shù)優(yōu)化恨不得把均線(xiàn)周期、止損比例、倉(cāng)位大小全部調(diào)到收益最大再上實(shí)盤(pán)結(jié)果實(shí)盤(pán)表現(xiàn)遠(yuǎn)不如回測(cè)。后來(lái)我養(yǎng)成了一個(gè)習(xí)慣每?jī)?yōu)化一組參數(shù)就用另一段時(shí)間的數(shù)據(jù)做驗(yàn)證并且接受收益回落到七成這個(gè)現(xiàn)實(shí)。畢竟回測(cè)是在歷史地圖上找寶藏實(shí)盤(pán)是走進(jìn)迷霧里尋路二者本來(lái)就不是同一件事。Backtrader這套框架你越用越會(huì)發(fā)現(xiàn)它值得深挖的地方實(shí)在太多了。除了SMA和CrossOver這些基礎(chǔ)指標(biāo)你還能在策略里加自定義止損、動(dòng)態(tài)倉(cāng)位、多空雙向交易除了回測(cè)你還能用Cerebro的runlive()接入實(shí)盤(pán)數(shù)據(jù)源做紙面交易測(cè)試。對(duì)個(gè)人玩家來(lái)說(shuō)Backtrader完全可以覆蓋從點(diǎn)子到驗(yàn)證再到模擬交易的完整鏈路。我個(gè)人的建議是不要一上來(lái)就追求復(fù)雜的策略和高級(jí)的功能先把雙均線(xiàn)這類(lèi)最簡(jiǎn)單的策略跑透搞清楚每個(gè)指標(biāo)怎么讀、每筆交易怎么記錄再慢慢往上疊加規(guī)則。回測(cè)工具只是工具箱里的一把扳手真正的盈利能力來(lái)自你對(duì)市場(chǎng)、對(duì)規(guī)則、對(duì)風(fēng)險(xiǎn)的持續(xù)理解。本文還有配套的精品資源點(diǎn)擊獲取