股票回測引擎
歡迎使用MindGo量化交易平臺,本文檔詳細介紹了MindGo平臺的股票回測引擎,內容較多,您可通過Ctrl+F進行關鍵字搜索。
如果您在使用過程中遇到幫助中無法解答的問題,您可以:
編寫股票交易策略step 1.導航欄中,依次點擊"我的策略"—"策略創(chuàng)作"—"策略研究" step 2.新建"股票期貨"策略,點擊進入"策略編輯"頁面,如下圖 step 3.左側編譯環(huán)境內使用python 3.5 實現策略邏輯 交易股票:貴州茅臺
買入:5日均線上穿20日均線
賣出:5日均線下穿20日均線
step 4.右側選擇回測時間區(qū)間,并進行回測
 策略源碼:
#初始化賬戶
def init(context):
g.index='600519.SH'
def handle_bar(context,bar_dict):
close = history(g.index, ['close'], 20, '1d', False, fq = 'pre', is_panel=0)
MA5 = close['close'].values[-5:].mean()
#計算二十日均線價格
MA20 = close['close'].values.mean()
#如果五日均線大于二十日均線
if MA5 > MA20:
#使用所有現金買入證券
order_target_percent(g.index,1)
#記錄本次買入
log.info("全倉買入{0}".format(g.index))
#如果五日均線小于二十日均線
if MA5 < MA20 :
#賣出所有證券
order_target_percent(g.index,0)
#記錄本次賣出
log.info("全倉賣出{0}".format(g.index))
回測環(huán)境回測引擎運行在Python3.5之上, 請您使用Python3.5來實現策略邏輯 我們支持所有的Python標準庫和部分常用第三方庫, 具體請看: Python庫 同時,我們也支持自定義Python庫,只需將您的.py文件存放于“我的研究”根目錄, 即可在回測中直接調用, 具體說明請參見常見問題-如何調用自定義Python庫? 支持代碼調試功能,具體請看:代碼調試
編譯運行
1.您的策略必須在init()和handle_bar()函數框架下實現: 2.完成策略編寫后,選定回測開始日期和結束日期,選擇初始資金、運行頻率(每日或每分鐘)等參數,點擊"編譯運行";
 3.回測引擎根據您選擇的運行頻率調用handle_bar函數,也就是執(zhí)行該函數下的代碼?;販y引擎會實時顯示策略當前時間的數據,如收益、風險指標、持倉等信息;
 4.回測引擎會根據您所使用的下單方式進行下單,并根據后續(xù)實際成交情況進行訂單處理; 5.您可以在任何時候調用log.info函數來打印需要輸出的日志;通過record函數輸出自定義圖形。 
#初始化賬戶
def init(context):
g.index='600519.SH'
def handle_bar(context,bar_dict):
close = history(g.index, ['close'], 20, '1d', False, fq = 'pre', is_panel=0)
MA5 = close['close'].values[-5:].mean()
#計算二十日均線價格
MA20 = close['close'].values.mean()
#設置交易信號
trade_signal=0
#如果五日均線大于二十日均線
if MA5 > MA20:
#使用所有現金買入證券
order_target_percent(g.index,1)
#記錄本次買入
log.info("全倉買入{0}".format(g.index))
#記錄買入信號
trade_signal=1
#如果五日均線小于二十日均線
if MA5 < MA20 :
#賣出所有證券
order_target_percent(g.index,0)
#記錄本次賣出
log.info("全倉賣出{0}".format(g.index))
#記錄賣出信號
trade_signal=-1
log.info(trade_signal)
record(trade_signal=trade_signal)
策略回測如果策略能成功完成編譯運行,則說明策略代碼在指定歷史區(qū)間是可運行的。 一般而言,策略回測是將可運行的策略代碼進行歷史區(qū)間回測,并獲取策略回測詳情報告:交易明細、歷史持倉、收益&風險指標分析、組合歸因等。 步驟1.將可運行的策略代碼,點擊"開始回測"
 步驟2.在策略詳情頁面,查看收益概況、交易明細、歷史持倉及相關分析結果,如下圖:

數據MindGo提供海量優(yōu)質的金融數據,以便您能實現策略邏輯 運行頻率在"開始回測"左側選擇運行頻率參數,參數分"每日"和"分鐘"兩種。 選擇"每日",則系統(tǒng)按"日回測"進行回測,即每個交易日09:30運行一次 選擇"分鐘",則系統(tǒng)按"分鐘回測"進行回測,即每個交易日內每分鐘都會運行一次
運行時間
1.開盤前(9:00)運行: before_trading函數 2.盤中運行: handle_bar函數 >日回測(9:31:00)運行一次 >分鐘回測(9:31:00-11:30,13:01:00-15:00:00),每分鐘運行一次 >tick回測(9:30:03-11:30,13:00:03-15:00:00),每3秒鐘運行一次 3.收盤后(15:30)運行: after_trading函數 滑點
在實戰(zhàn)交易中,往往最終成交價和預期價格有一定偏差,因此我們提供兩種滑點模式來幫助您更好地模擬真實市場的表現: 拆分合并與分紅
當股票發(fā)生拆分,合并或者分紅時,股價會出現跳空缺口,為了消除這種價格變化對回測結果的影響,我們會根據個股除權除息信息對賬戶中的現金或持股數量進行相應的調整修正,并自動更新到您的context信息中。 為了使回測結果更加準確,更加貼近實盤場景,我們做了如下處理: 回測所用價格數據與下單所用價格數據是獨立的。即在回測過程中,您可采用前復權/不復權或后復權價格來計算交易下單信號,回測引擎均采用真實價格(即不復權價格)下單; 回測引擎會在除權除息當日,調用before_trading_start函數之前,自動處理并更新您的賬戶信息。 前復權數據采用動態(tài)復權模式,即在回測過程中,輪循至某個股除權除息日,則按除權后價格對之前的價格數據進行調整。
訂單處理
對于您在某個單位時間下的單,我們會做如下處理:
1.按天回測
A.交易價格: >市價單:開盤價+滑點。 >限價單:委托價。 B.最大成交量: >默認為下單當日總成交量的25%,該比例可通過市場參與度函數set_volume_limit進行調整。 >若下單量低于最大成交量,則按下單量成交;若下單量大于最大成交量,則按最大成交量成交。 C.撮合方式: >市價單:開盤下單,一次性撮合,不成交或未成交部分不再撮合。 >限價單:開盤下單,一次性撮合,不成交或未成交部分不再撮合。
2.分鐘回測
A.交易價格: >市價單:當前分鐘起始價+滑點 >限價單:委托價 B.最大成交量: >默認為下單當前分鐘總成交量的25%,該比例可通過市場參與度函數set_volume_limit進行調整。 >若下單量低于最大成交量,則按下單量成交;若下單量大于最大成交量,則按最大成交量成交。 C.撮合方式: >市價單:分鐘起始點下單,一次性撮合,不成交或未成交部分即刻取消委托。 >限價單:分鐘起始點下單,之后每分鐘均按分鐘價量撮合一次,未成交部分順延至下一分鐘進行撮合,直到完全成交或者當天收盤為止。
3.注意: 1.一天結束后, 所有未完成的訂單會被取消。
2.每次訂單完成(完全成交)或者取消后,我們會根據成交量計算交易費(參見set_commission), 減少您的現金。
風險模型r=α+βf+μ
其中:r為股票收益率向量,β為因子暴露,μ為特質收益,f為因子收益率
當股票特質收益率與公共因子不相關時,組合預期風險可以分解為公共因子解釋的風險及特質風險:
wΣw′=wβΩβ′w′+wΔw′
其中:w為組合成份股權重,Ω為因子收益率的協方差矩陣,Δ為股票的特質風險矩陣,我們假設股票的特質風險之間也互不相關,所以Δ是一個對角矩陣
組合優(yōu)化器組合優(yōu)化器函數查詢:OptimizePort,opt.add_constraint
優(yōu)化方式 組合優(yōu)化器優(yōu)化方式暫定為三種:最大化夏普比(MVO)、最大化信息比率、效用最大化(剔除成本影響)優(yōu)化方式后期持續(xù)增加,例如L-B模型、風險平價模型等常用的資產配置模型。 MVO算法 公式:
 使用最優(yōu)化算法求解,使target最大。R為一系列個股預期收益值[r1,r2,...rn],n為股票數量,為函數輸入參數項,可以選擇使用歷史平均收益作為預期收益,需輸入股票代碼。W為各個股票的權重,為優(yōu)化器所要計算的未知量[w1,w2,...wn],n為股票數量?!茷閭€股協方差矩陣(nXn),使用個股歷史漲跌幅計算,取數范圍為前120個周期,例如日頻數據,則選取前120個交易日的漲跌幅,如為周頻,則使用日頻數據進行轉換。
最大化信息比率 公式:
 使用最優(yōu)化算法求解,使target最大。R,W,∑定義同1.1.1中描述。Wb為個股在所選基準中所占的權重,為[wb1,wb2,...wbn],n為股票數量,當個股沒有在基準中時,wb為0?;鶞士勺杂蛇x擇,作為函數輸入項,默認為中證500。
效用最大化 公式:
 使用最優(yōu)化算法求解,使target最大。R,W,∑定義同1.1中描述。γ 為風險厭惡系數,函數輸入參數項,默認為0.5。λ為交易成本,函數輸入參數項,默認為0。Wi,t-1 為股票初始權重,可自定義輸入,也可選擇初始權重都為0。
約束條件 目前暫定七個約束條件:換手率、跟蹤誤差、組合風險、個股權重、風格因子主動暴露、行業(yè)因子主動暴露、行業(yè)中性。 換手率約束 約束條件: Wi,t 為函數求解值,個股優(yōu)化后的權重;Wi,t-1 為股票初始權重,可自定義輸入,也可選擇初始權重都為0。Uplimit為輸入的換手率限制值,不可小于0,最大值為100(默認單位%)。
跟蹤誤差 約束條件: W和Wb為組合中的股票和基準中的股票做并集后的權重向量。例如組合中有股票A,B,C,基準包含的股票為A,B,D,則W和Wb兩個向量都有四個元素[WA,WB,WC,WD]和[WbA,WbB,WbC,WbD],且WD和WbC 為0。[WA,WB,WC]為組合中股票的權重,為優(yōu)化器所要求解的未知量[w1,w2,...wn],n為股票數量?!茷閭€股協方差矩陣(nXn),n為組合和基準股票的并集,算法同1.1.1中描述?;鶞士勺杂蛇x擇,作為函數輸入項,默認為中證500。Downlimt和uplimit作為輸入的跟蹤誤差上下限,downlimt不可小于0。
組合風險 約束條件: W為各個股票的權重,為優(yōu)化器所要求解的未知量[w1,w2,...wn],n為股票數量?!茷閭€股協方差矩陣(nXn),計算同1.1中描述。Downlimt和uplimit作為輸入的組合風險上下限,downlimt不可小于0。
個股權重 約束條件: 可針對每個股票做單獨的權重約束,downlimt和uplimt為所對應股票的權重上下限,可自由輸入。函數輸入項包含個股代碼及所要約束的上下限值,輸入的股票數量0<=w<=n(優(yōu)化目標中所輸入的股票總數)。每個股票需在優(yōu)化目標中輸入的股票列表中,否則報錯。為內置權重約束,所有股票的權重值加起來等于1。
風格因子主動暴露度 約束條件: Downlimt和uplimit為自定義輸入的某個因子的暴露度約束上下限,輸入方式為因子名稱、下限值、上限值。當前共有十個風格因子,如需針對全部因子做限制,則需要輸入十個相對應的風格因子暴露度上下限值。Xstyle 為個股對該風格因子的暴露度,為[x1 style1,x2style1,...xnstyle1]該數據由風險模型模塊計算得出(目前易源博每日會更新該數據)。W為各個股票的權重,為優(yōu)化器所要求解的未知量[w1,w2,...wn]。
行業(yè)因子主動暴露度 約束條件: Downlimt和uplimit為自定義輸入的某個行業(yè)因子的暴露度約束上下限,輸入方式為行業(yè)因子名稱、下限值、上限值。當前共有29個行業(yè)因子(中信一級),如需針對全部因子做限制,則需要輸入29個相對應的行業(yè)因子暴露度上下限值。Xindustry 為個股對該行業(yè)的暴露度(即該個股是否屬于該行業(yè),屬于為1,不屬于為0)。W為各個股票的權重,為優(yōu)化器所要求解的未知量[w1,w2,...wn]。 Windustry為該行業(yè)在基準中所占的權重,不是矩陣,為數值。
行業(yè)中性 約束條件: Xindustry 為個股對該行業(yè)的暴露度(即該個股是否屬于該行業(yè),屬于為1,不屬于為0)。W為各個股票的權重,為優(yōu)化器所要求解的未知量[w1,w2,...wn]。 Windustry為該行業(yè)在基準中所占的權重,不是矩陣,為數值。
組合歸因組合歸因是對資產組合進行分析,將其回報和風險歸到提前設定的可能的原因上。目前該功能可實現對股票型策略進行收益及風險拆分,主要分為Brinson分析和風格分析。 我們首先會對每一期的持倉表進行單期歸因,然后將多期的結果使用進行累計。下面我們會介紹單期歸因的算法和累計歸因所得到的結果。 策略成功回測后,在回測詳情頁面點擊組合歸因功能,如下圖:
 組合歸因詳解一、Brinson組合歸因Brinson 是目前最為人知的歸因方案。除了需要組合持倉內容之外,還設定一個基準。最初的brinson模型是把收益拆解為行業(yè)選擇收益、個股選擇收益和交叉收益,我們在傳統(tǒng)模型基礎上做了進一步的擴展,拆分出配置收益和交易收益。 
對于各個拆分收益的計算如下:
 上述指標的計算只針對單期的brinson拆解,為了分析策略在一個周期里的收益歸類,我們使用Carino簡化因子調整。
Carino簡化因子的計算方式如下:
 Rt,bt為t期策略和基準的收益率;r,b為區(qū)間內策略和基準收益率。
使用簡化因子對多期收益分解進行加和:
 At,St,It為t期收益的分解項。
把收益按行業(yè)拆分,查看每個策略在每個行業(yè)是否帶來了主動收益。圖中主動收益展示的也是多期的收益率,通過Carino因子進行調整。主動風險計算的是多期主動風險的方差,衡量多期主動收益的穩(wěn)定性。主動權重是統(tǒng)計整個回測區(qū)間內各行業(yè)主動權重的均值。
 二、風格分析| 風格因子代碼 | 因子名稱 | 含義 | 備注 |
|---|
| beta | beta | 上市公司與指數之間的協同性 | 個股的Beta值,指數收益使用HS300 | | bp | 估值 | 記賬價值和市值的比值 | 市凈率=總市值/歸屬于母公司所有者權益合計 | | earnings_yield | 盈利 | 分析師預測與過去財年收益統(tǒng)計 |
| | growth | 成長 | 由銷售額及盈利統(tǒng)計。 |
| | leverage | 杠桿 | 上市公司的使用杠桿的情況。 |
| | liquidity | 流動性 | 由交易量和頻率不同而帶來的收益 |
| | momentum | 動量 | 能量性指標,表示相對強度 | 從過去某一天開始,向過去再推504天,計算其每天的超額收益對數值,再針對這504天進行加權平均 | | non_linear_size | 非線性市值 | 上市公司的規(guī)模處于中等的程度 |
| | size | 市值 | 上市公司的規(guī)模,即該公司是大盤股的程度 | 對數市值 | | volatility | 波動率 | 對大盤偏離的不確定性 |
|
選取策略組合在回測期間的四個界面查看其當期的主動收益、主動風險及主動暴露度。相關的計算公式如下:



收益&風險指標—評定策略的優(yōu)劣策略收益率—Returns
策略年化收益率—Annualized Returns
基準收益率—Benchmark Returns
基準年化收益率—Annualized Benchmark Returns
超額收益率—Excess Return
非系統(tǒng)性風險—Alpha
系統(tǒng)風險—Beta
夏普比率—Sharpe
收益波動率—Volatility
信息比率—Information Ratio
最大回撤—Max Drawdown
索提諾比率—Sortino
跟蹤誤差—Tracking error
下行波動率—Downside Risk
調試功能 在策略編輯器左側,點擊"行號",設置斷點后,點擊"編譯運行"啟動調試程序。

 調試程序簡介 1.啟動調試程序后,代碼會自動運行到第一個斷點處,如上圖,第一個斷點是行7,代碼當前已運行完行7(行8還未運行) 2.調試程序右上角顯示的時間是回測系統(tǒng)的運行時間 3.調試程序上方菜單欄,從左到右共6個按鈕,分別為:恢復執(zhí)行代碼(跳至下一個斷點),執(zhí)行下一步(不運行函數),執(zhí)行下一步(運行函數),跳出此函數,清空console,結束調試繼續(xù)編譯 4.調試程序左側即為console面板,右側為監(jiān)控屬性面板 5.1分鐘內不使用調試程序,則自動關閉調試程序 6.調試時,程序無視注釋內容
菜單欄按鈕介紹 1.恢復執(zhí)行代碼(跳至下一個斷點):直接運行至下一個斷點處 2.執(zhí)行下一步(不運行函數):運行下一行代碼,如果下一行代碼調用函數,則直接運行完該函數,接著準備下一行代碼 3.執(zhí)行下一步(運行函數):運行下一行代碼,如果下一行代碼調用函數,則會進入函數內,執(zhí)行函數內第一行代碼 4.跳出此函數:如果當前行處于函數中,則直接運行完該函數 5.清空console:清空左側console面板的所有內容 6.結束調試繼續(xù)編譯:關閉調試程序,策略繼續(xù)編譯
如何使用console面板? 輸入變量,“Enter”輸出變量值(無法修改參數) 變量之間運算,“Enter”輸出結果 判斷變量是否滿足條件,“Enter”輸出結果 
如何監(jiān)控變量? 調試程序右側監(jiān)控屬性,點擊"+添加",輸入變量名,點擊“完成”,即可進行監(jiān)控  監(jiān)控面板顯示當前監(jiān)控的變量及變量值,繼續(xù)運行代碼  運行代碼的過程中,監(jiān)控面板實時顯示監(jiān)控變量及變量值,便于您進行觀察,不需要print打印函數來逐一輸出。 
對比功能一般而言,量化策略會存在一個或多個參數,在驗證策略邏輯的過程中,寬客們往往會嘗試用不同的參數進行回測,比如在2018年,對平安銀行采用雙均線策略,采用不同周期的兩條均線會使策略收益截然不同哦~ 如何啟動回測對比?
策略回測列表中,選取相應的回測,點擊左上角的對比按鈕,即可啟動~

 如何使用回測對比?
對比功能,包括:概況、累積收益、回撤、源碼。 其中概況頁面,詳細展示了所選回測的回測指標,我們以平安銀行雙均線策略為例,創(chuàng)建3組參數,分別為(5,20),(10,55),(20,60)。
 累積收益頁面,詳細展示了所選回測的收益走勢,可以發(fā)現(20,60)參數組的累積收益較高,參數周期越短,交易次數越多,收益反而越差,可能與18年的暴跌行情相關。
 回撤頁面,詳細展示了所選回測的回撤情況,可以發(fā)現(20,60)參數組的回撤較低,參數周期越短,交易次數越多,回撤反而越大,可能與18年的暴跌行情相關。
 源碼頁面,詳細展示了所選回測的源碼,同時只能選取兩個回測進行對比

python庫MindGo平臺支持所有Python的基礎庫(https://docs./3.5/library/index.html),并支持目前流行的第三方庫,例如NumPy,pandas,Ta-Lib,scikit-learn,TuShare等。主要模塊介紹如下表所示。 為了保障MindGo平臺的安全,以下的python包和功能已被禁用,如有不便敬請見諒: | 包 | 屬性和方法 |
|---|
| os | __builtins__ | | sys | __import__ | | six | get_ipython | | subprocess | exec | | pickle | eval | | socket |
| | ast |
| | IPython |
| | ipykernel |
|
【案例】MACD交易策略#導入talib庫
import talib
#初始化函數
def init(context):
#輸入股票代碼
g.security = '600519.SH'
#設置MACD模型參數
#短周期平滑均線參數
g.Short = 12
#長周期平滑均線參數
g.Long = 26
#DIFF的平滑均線參數
g.M = 9
#每日運行函數
def handle_bar(context, bar_dict):
#調用MACD計算函數,獲取MACD值
macd = get_macd(g.security)
#判斷金叉,且無持倉
if macd[-1]>0 and macd[-2]<0 and context.portfolio.market_value == 0:
#執(zhí)行全倉買入
order_value(g.security,context.portfolio.available_cash)
#打印日志
log.info("買入 %s" % (g.security))
#判斷死叉,且有持倉
if macd[-2]>0 and macd[-1]<0 and context.portfolio.market_value > 0:
#執(zhí)行清倉
order_target(g.security,0)
#打印日志
log.info("賣出 %s" % (g.security))
#MACD計算函數
def get_macd(stock):
#獲取數據
price = history(stock, ['close'], 500, '1d', True, 'pre', is_panel=1)['close']
#talib庫調用MACD計算方法
DIFF, DEA, MACD = talib.MACD(price.values,
fastperiod = g.Short, slowperiod = g.Long, signalperiod = g.M)
#輸出MACD值
return MACD
【案例】三因子選股模型import pandas as pd
import numpy as np
import datetime
def init(context):
# 設置最大持股數
context.max_stocks = 10
#記錄天數,隔20個交易日調倉
g.day = 0
def handle_bar(context, bar_dict):
if g.day%20 !=0:
return None
g.day = g.day +1
# 每個調倉日先清倉持有的股票
for security in list(context.portfolio.positions.keys()):
order_target(security, 0)
# 首先獲得當前日期
time = get_datetime()
date = time.strftime('%Y%m%d')
# 獲得股票池列表
sample = get_index_stocks('000300.SH',date)
# 創(chuàng)建字典用于存儲因子值
df = {'security':[], 1:[], 2:[], 3:[], 'score':[]}
# 因子選擇
for security in sample:
q=query(
profit.roic,# 投資回報率
valuation.pb,# 市凈率
valuation.ps_ttm,# 市銷率
).filter(
profit.symbol==security
)
# 缺失值填充為0
fdmt = get_fundamentals(q, date=date).fillna(0)
# 判斷是否有數據
if (not (fdmt['profit_roic'].empty or
fdmt['valuation_pb'].empty or
fdmt['valuation_ps_ttm'].empty)):
# 計算并填充因子值
df['security'].append(security)
df[1].append(fdmt['profit_roic'][0])# 因子1:投資回報率
df[2].append(fdmt['valuation_pb'][0])# 因子2:市凈率
df[3].append(fdmt['valuation_ps_ttm'][0])#因子3:市銷率
for i in range(1, 4):
# 因子極值處理,中位數去極值法
m = np.mean(df[i])
s = np.std(df[i])
for j in range(len(df[i])):
if df[i][j] <= m-3*s:
df[i][j] = m-3*s
if df[i][j] >= m+3*s:
df[i][j] = m+3*s
m = np.mean(df[i])
s = np.std(df[i])
# 因子無量綱處理,標準化法
for j in range(len(df[i])):
df[i][j] = (df[i][j]-m)/s
# 計算綜合因子得分
for i in range(len(df['security'])):
# 等權重計算(注意因子方向)
s = (df[1][i]-df[2][i]-df[3][i])
df['score'].append(s)
# 按綜合因子得分由大到小排序
df = pd.DataFrame(df).sort_values(by ='score', ascending=False)
# 等權重分配資金
cash = context.portfolio.available_cash/context.max_stocks
# 買入新調倉股票
for security in df[:context.max_stocks]['security']:
order_target_value(security, cash)
|