如何用AmiBroker做績效回測(作者 michael-knight)
這是下面這篇文章的更新版。http://www.coco-in.net/viewthread.php?tid=1060&extra=page%3D1
新增如何用AB做自動交易、訂單行為管理說明。
作者是michael-knight,小娃把它排版貼上來。
michael-knight大很希望和大家多認識、多交流AB的技術。 Amibroker客制化回朔測試,使用者能精細控制回朔測試引擎。參照Code 3, AFL於Pass1進行信號和數值陣列收集,AFL函式Applystop規範的出場價格點,此時動作類為”actionBacktest” ,第二回合跑客制化程序(Pass2),此時動作類型為”actionPortfolio”
。Individual-level客製化回朔測試,每檔股票會執行一次客制化程序;Portfolio-level客製化回朔測試,僅執行一次客制化程序,舉3檔股票範例其執行流程如表格 4。Portfolio-level對每檔股票掃過pass1後,有成交的時間點依聯集方式產生一組新的時間序列,序列大小當作Barcount,每檔股票的陣列變數如價格陣列長度都一致調成Barcount,沒有成交明細的時間點其陣列值以N/A或NULL{empty}填入,如表格 5範例示意,當掃描每根K棒時,依序處理該時間點每檔股,此時正在處理的股票其價格陣列變數以SetForeign函式取得,用完後再以RestorePriceArrays回存。
將圖表 6客制化程序致能,並指定存放客制化程序的檔案,圖表 1所示AA視窗按下”Backtest”(Individual or Portfolio),AFL執行時期就會進入客制化回朔測試;另一種方式可以在AFL開啟客制化回朔測試,進入CB後,Amibroker以COM元件開放CBI客制化回朔測試介面,物件諸元backtester、trade、signal, CB下用函式GetBacktesterObject才會有意義,否則回傳Null。 Backtester operationEquity levelSignal processingMethodUIBacktestIndividual6種AA->S->P不勾選portfolioAA->B_IPortfolioAA->B_PCustom Backtester interfaceIndividual6種AA->S->P勾選portfolio並指定客制化回朔測試操作AA->B_IPortfolioAA->B_P
如何用Amibroker進行回朔測試,了解預設的回策引擎詳細方塊功能,進一步能做客制化參數控制買賣交易,可達到單筆交易大小、投資部位管控,讓回朔測試能更貼近實際交易情境。
耳朵好癢,有人叫我。
我不會轉成AB。我會看圖說故事。
下面註解的中文字是我寫的。
{
{這種符號是註解}
Livermore Market Method Signal
Ref: Chapters VIII & IX of "How to Trade in Stocks", Jesse L. Livermore
v1 ghkramer - 5Oct03
v1.1 ghkramer - 11Oct03, Added switch to deactive Rule 10 logic
===============
Program Overview
This program classifies price action into the following states based upon
rules in Ref:
- Up Trend
- Natural Rally
- Secondary Rally
- Down Trend
- Natural Reaction
- Secondary Reaction
State change is determined by a user specified threshold of price change.
The program also determines a number of pivot points:
- Peak Up Trend Price
- Peak Natural Rally Price
- Bottom Down Trend Price
- Bottom Natural Reaction Price
- Key Price (requires two stocks, i.e., 2 or more data streams (Rule 7), not yet implemented)
This program may be used as a basis for a number of studies:
- trend paint bars,
- pivot price indicator lines
- strategies (trend following and/or breakout/breakdown)
For a detailed explanation of the system see Ref.
This program reflects the author's interpretation of Livermore's writing.
There is no guarantee the this program accurately or fully incorporates
Livermore's Market Key system.
}
input: PtsPctATR(0), Threshold(6), ATRLength(14), TradeTrends(1);
{輸入變成有4個,要最佳化時可以用這個變數來跑。}
{ PtsPctATR: 0 for Threshold in points,
1 for Threshold in Percent
2 for Threshold in multiples of ATR
Threshold: in Points (if PtsPct = 0)
in Percent (if PtsPct = 1)
in ATR Multiples (if PtsPct = 2)
ATRLength(14) : only used when PtsPctATR = 2
TradeTrends: 1 = Trade Up and Down Trends only
0 = Long for Up Trends and Rallies, Short for Dn Trends and Reactions
Note: Livermore's system used a threshold of 6 points for stocks priced over $30.
This is the default (PtsPctATR = 0, Threshold = 6).
}
var:
{內部變數}
SecondaryRally(0), NaturalRally(0), UpTrend(0),
SecondaryReaction(0), NaturalReaction(0), DnTrend(0),
DnTrendBL(0), NaturalRallyBL(0), {BL = Black Line}
UpTrendRL(0), NaturalReactionRL(0), {RL = Red Line}
InSecRally(false), InNatRally(false), InUpTrend(false),
InSecReact(false), InNatReact(false), InDnTrend(false),
ResumeUpTrend(false), ResumeDnTrend(false),
MA10(0), Thresh(0), HalfThresh(0),
UseRule10(false), {set to true to use Livermore's Rule 10, note: system may become unstable}
Debug(false); {set to true for output}
{initialization}
{初始化部份}
if (CurrentBar = 1) then
begin
if (PtsPctATR = 0) {use Points} then
begin
Thresh = HalfThresh ;
HalfThresh = Thresh/2;
{HalfThresh的數值等於HalfThresh除以2}
end;
SecondaryRally = Close;
NaturalRally = Close;
UpTrend = Close;
SecondaryReaction = Close;
NaturalReaction = Close;
DnTrend = Close;
{然後這一串變數,都初始化為今天的收盤價}
end;
if (CurrentBar <= 21) then {initialization continued}
begin
MA10 = Average(Close, 10);
{MA10的數值為今天的10日收盤平均價}
if (CurrentBar = 21) then
begin
if (MA10 > MA10) then {assume UpTrend}
{奇怪了..MA10和MA10 有何不同?看不懂}
begin
InUpTrend = true;
{這種情況下,我們設InUpTrend為"真",即突破的上升趨勢}
UpTrend = C;
end
else {assume DnTrend}
begin
InDnTrend = true;
DnTrend = C;
end;
end;
end
else {Main}
begin {calc current Threshold if required}
if (PtsPctATR = 1) then {use Percent change Thresh, calc on every bar }
begin
Thresh = Threshold*Close/100;
HalfThresh = Thresh/2;
end
else if (PtsPctATR = 2) then {use ATR multiples for Thresh, calc on every bar }
begin
Thresh = Threshold*AvgTrueRange(14);
{Thresh為Threshold乘上14天的ATR}
HalfThresh = Thresh/2;
end;
{Process by Current State}
{------Up Trend State-------}
if InUpTrend then
begin
if (Close > (NaturalReaction + Threshold)) then
NaturalReactionRL = NaturalReaction; {Rule 4b}
if ResumeUpTrend then {Rule 10 logic. Note: system becomes unstable if used}
begin
if (Close > (UpTrendRL + HalfThresh)) then
begin
ResumeUpTrend = false; {Rule 10a}
UpTrend = Close;
if Debug then print(date, Close, " Node 1", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < (UpTrendRL - HalfThresh)) then {UpTrend Over, return to NaturalReaction}
begin
ResumeUpTrend = false;
InUpTrend = false; {Rule 10b}
InNatReact = true;
NaturalReaction = Close;
if Debug then print(date, Close, " Node 2", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end;
end
else if (Close < (UpTrend - Thresh)) then {start NaturalReaction}
begin {Rules 4a, 6a}
InUpTrend = false;
InNatReact = true;
NaturalReaction = Close;
UpTrendRL = UpTrend; {Pivot Pt, Rule 8}
ResumeUpTrend = false;
if Debug then print(date, Close, " Node 3", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > UpTrend) then {remain in UpTrend, record higher price}
UpTrend = Close; {Rule 1, 4b, 6d}
end {InUpTrend}
{------Natural Rally State-------}
else if InNatRally then
begin
if (Close > (NaturalReaction + Threshold)) then
NaturalReactionRL = NaturalReaction; {Rule 4b}
if (Close > UpTrend) then {resume UpTrend}
begin {Rules 6d, 6f}
InUpTrend = true;
InNatRally = false;
UpTrend = Close;
if UseRule10 then ResumeUpTrend = true;
if Debug then print(date, Close, " Node 4", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > (NaturalRallyBL + HalfThresh)) then {resume UpTrend}
begin {Rules 5a}
InUpTrend = true;
InNatRally = false;
UpTrend = Close;
if UseRule10 then ResumeUpTrend = true;
if Debug then print(date, Close, " Node 5", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < DnTrend) then {start DnTrend}
begin {Rule 6b}
InNatRally = false;
InDnTrend = true;
DnTrend = Close;
NaturalRallyBL = Close; {rule 4d}
if Debug then print(date, Close, " Node 6", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < (NaturalRally - Thresh)) then
begin
if (Close < NaturalReaction) then {start NaturalReaction}
begin {Rules 4d, 6b}
InNatRally = false;
InNatReact = true;
NaturalReaction = Close;
NaturalRallyBL = Close; {rule 4d} {Pivot Pt, Rule 9b}
if Debug then print(date, Close, " Node 7", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else {start SecondaryReaction}
begin {Rule 6h}
InNatRally = false;
InSecReact = true;
SecondaryReaction = Close;
if Debug then print(date, Close, " Node 8", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end;
end
else if (Close > NaturalRally) then {remain in NaturalRally, record higher price}
NaturalRally = Close; {Rule 3, 6c, 6d}
end {InNatRally}
{------Secondary Rally State-------}
else if InSecRally then
begin
if (Close > UpTrend) then {resume UpTrend}
begin {Rules 6d, 6f}
InUpTrend = true;
InSecRally = false;
UpTrend = Close;
if UseRule10 then ResumeUpTrend = true;
if Debug then print(date, Close, " Node 9", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > (NaturalRallyBL + HalfThresh)) then {resume UpTrend}
begin {Rules 5a}
InUpTrend = true;
InSecRally = false;
UpTrend = Close;
if UseRule10 then ResumeUpTrend = true;
if Debug then print(date, Close, " Node 10", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > NaturalRally) then {start NaturalRally}
begin {Rule 6g}
InSecReact = false;
InNatRally = true;
NaturalRally = Close;
if Debug then print(date, Close, " Node 11", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < DnTrend) then {start DnTrend}
begin {Rule 6b}
InSecRally = false;
InDnTrend = true;
DnTrend = Close;
NaturalRallyBL = Close; {rule 4d} {Pivot Pt, Rule 9b}
if Debug then print(date, Close, " Node 12", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > SecondaryRally) then {remain in SecondaryRally, record higher price}
SecondaryRally = Close; {Rule 3, 6g}
end {InSecRally}
{------Down Trend State-------}
else if InDnTrend then
begin
if (Close < (NaturalRally - Threshold)) then
NaturalRallyBL = NaturalRally; {Rule 4d}
if ResumeDnTrend then {Rule 10 logic. Note: system becomes unstable if used}
begin
if (Close < (DnTrendBL - HalfThresh)) then
begin
ResumeDnTrend = false; {Rule 10a}
DnTrend = Close; {Rule 2, 6b}
if Debug then print(date, Close, " Node 13", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > (DnTrendBL + HalfThresh)) then {DnTrend Over, return to NaturalRally}
begin
ResumeDnTrend = false;
InDnTrend = false; {Rule 10c}
InNatRally = true;
NaturalRally = Close;
if Debug then print(date, Close, " Node 14", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end;
end
else if (Close > (DnTrend + Thresh)) then {start NaturalRally}
begin {Rules 4c, 6c}
InDnTrend = false;
InNatRally = true;
NaturalRally = Close;
DnTrendBL = DnTrend; {Pivot Pt, Rule 8}
ResumeDnTrend = false;
if Debug then print(date, Close, " Node 15", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < DnTrend) then {remain in DnTrend, record lower price}
DnTrend = Close; {Rule 2, 6b}
end {InSecRally}
{------Natural Reaction State-------}
else if InNatReact then
begin
if (Close < (NaturalRally - Threshold)) then
NaturalRallyBL = NaturalRally; {Rule 4d}
if (Close < DnTrend) then {resume DnTrend}
begin {Rule 6b, 6e}
InDnTrend = true;
InNatReact = false;
DnTrend = Close;
if UseRule10 then ResumeDnTrend = true;
if Debug then print(date, Close, " Node 16", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < (NaturalReactionRL - HalfThresh)) then {resume DnTrend}
begin {Rules 5b}
InDnTrend = true;
InNatReact = false;
DnTrend = Close;
if UseRule10 then ResumeDnTrend = true;
if Debug then print(date, Close, " Node 17", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > UpTrend) then {start UpTrend}
begin {rule 6d}
InNatReact = false;
InUpTrend = true;
UpTrend = Close;
NaturalReactionRL = Close; {Rule 4b} {Pivot Pt, Rule 9c}
if Debug then print(date, Close, " Node 18", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > (NaturalReaction + Thresh)) then
begin
if (Close > NaturalRally) then {start NaturalRally}
begin {Rules 4b, 6d}
InNatReact = false;
InNatRally = true;
NaturalRally = Close;
NaturalReactionRL = Close; {Rule 4b} {Pivot Pt, Rule 9c}
if Debug then print(date, Close, " Node 19", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else {start SecondaryRally}
begin {Rule 6g}
InNatReact = false;
InSecRally = true;
SecondaryRally = Close;
if Debug then print(date, Close, " Node 20", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end;
end
else if (Close < NaturalReaction) then {remain in NaturalReaction, record lower price}
NaturalReaction = Close; {Rules 3, 6a, 6b}
end {InNatReact}
{------Secondary Reaction State-------}
else if InSecReact then
begin
if (Close < DnTrend) then {resume DnTrend}
begin {Rules 6b, 6e}
InDnTrend = true;
InSecReact = false;
DnTrend = Close;
if UseRule10 then ResumeDnTrend = true;
if Debug then print(date, Close, " Node 21", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < (NaturalReactionRL - HalfThresh)) then {resume DnTrend}
begin {Rules 5b}
InDnTrend = true;
InSecReact = false;
DnTrend = Close;
if UseRule10 then ResumeDnTrend = true;
if Debug then print(date, Close, " Node 22", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close > UpTrend) then {start UpTrend}
begin {Rule 6d}
InSecReact = false;
InUpTrend = true;
UpTrend = Close;
NaturalReactionRL = Close; {Rule 4b} {Pivot Pt, Rule 9c}
if Debug then print(date, Close, " Node 23", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < NaturalReaction) then {start NaturalReaction}
begin {Rule 6h}
InSecReact = false;
InNatReact = true;
NaturalReaction = Close;
if Debug then print(date, Close, " Node 24", UpTrend, DnTrend, NaturalRally, NaturalReaction, UpTrendRL, DnTrendBL, NaturalRallyBL, NaturalReactionRL);
end
else if (Close < SecondaryReaction) then {remain in SecondaryReaction, record lower price}
SecondaryReaction = Close; {Rule 6h}
end {InSecReact}
{------Error State-------}
else
begin
{should not get here!!!}
if Debug then
print("Error in State Logic, Date: ", Date, " Time: ", Time);
end; {Error state}
end; {main}
{------Trading Logic-------}
{For TS 2000i only, non-op or delete for TS 6+ }
{
if (TradeTrends = 1) then
begin
if InUpTrend then
Buy next bar at open
else if (Marketposition = 1) then
ExitLong next bar at open;
if InDnTrend then
Sell next bar at open
else if (Marketposition = -1) then
ExitShort next bar at open;
end
else {Trade Trends, Rallies, and Reactions}
begin
if (InUpTrend or InNatRally or InSecRally) then
begin
Buy next bar at open;
ExitShort next bar at open;
end;
if (InDnTrend or InNatReact or InSecReact) then
begin
Sell next bar at open;
ExitLong next bar at open;
end;
end;
}
{ For TS 6+ versions}
if (TradeTrends = 1) then
begin
if InUpTrend then
Buy next bar at open
else if (Marketposition = 1) then
Sell next bar at open;
if InDnTrend then
Sell Short next bar at open
else if (Marketposition = -1) then
Buy to Cover next bar at open;
end
else
begin
if (InUpTrend or InNatRally or InSecRally) then
begin
Buy next bar at open;
Buy to Cover next bar at open;
end;
if (InDnTrend or InNatReact or InSecReact) then
begin
Sell Short next bar at open;
Sell next bar at open;
end;
end;
........
會寫的人加油...不會寫...
{:4_93:} 1.AA:Automatic Analysis window2.BS:Backtest Setting Window,除了在此設定回朔測試時的參數與選項外,AFL下直接調用函式SetOption將覆蓋BS內的設置。3.AT:自動交易,auto trading4.交易信號種類:進場信號buy/short、出場信號sell/cover,此四個均為陣列變數,buy用來建立多頭倉位,sell信號做多頭部位平倉,short用於建立空頭部位,cover則是將空頭部位做平倉動作。每種交易信號對應實際交易價格,BS視窗下Trades裡指定交易價格。回朔測試有六種處理交易信號模式,經處理後每個交易信號在回朔測試引擎內對應一個signal物件。5.訂單型態:實際處理交易信號時,掛出訂單包含交易價格、訂單型態。支援限價單IOC(Immediate or cancel)和pending Order嗎6.除錯訊息:ViewàLog,在此視窗下子選單”Trace Output”包含internal和external選項,選用external時,內建函式_TRACE及printf將把訊息輸出至作業系統內的訊息spool,需安裝DebugView來看;選用internal,內建函式_TRACE及printf將把訊息顯示於Amibroker的log視窗。如下
7.Initial Equity:做回朔測試時之初始參數之一,帳戶初始淨值,即原始可下單金額,AA視窗下按下Setting進入”Backtest setting”對話盒,於”Genaral tab”下可設置此參數,或於AFL下直接調用函式SetOption。AA視窗下按下”Individual Backtest” ,多檔股票此參數將被視作每檔股票初始淨值;AA視窗下按下”Portfolio Backtest” ,多檔股票此參數被視為所有股票共同的初始淨值。8.每筆交易部位大小:Positionsize,此變數為陣列,管控回朔測試時每筆交易部位大小(金額或淨值百分比,表示法請參閱函式SetPositionSize)。9.最大開倉筆數:調用函式SetOption。當有交易信號,而且當前總開倉筆數小於最大開倉筆數且可下單資金大於Positionsize,則新增一筆成交,否則無法成交。10.Individual-level or Portfolio-level Backtest:AA視窗à”Backtest”,此兩者都會進入AFL執行預設或客制化回朔測試,”Individual” 意指每檔股票的回朔測試參數是獨立存放互不影響,”Portfolio”(整體或一籃子)意指所有股票的共用同一份回朔測試參數。11.PositionScore:陣列變數,每一根K棒對應一個ranking,於Portfolio-level回朔測試,於第n根K棒,多檔股票存在交易信號,ranking數值愈高的股票優先成交。 一、Amibroker Formula language:有六種時機會執行AFL,分別為Indicator、Commentary、Scan、Explore、Backtest、PortfolioBacktest動作類型,習慣上AFL並有些函式只於某種動作類型下呼叫是有意義的,我們會調用函式Status取得當前動作類型。1.Indicator:當指標圖表需作更新或視窗重繪事件時,會執行該圖表對應的AFL。
圖表 1
AA視窗
2.Commentary:選單AnalysisàCommentary,Formula tab視窗下以writeif、writeval、printf函式將訊息打印至Commentary tab,主要用於以文字敘述方式提供當前投資策略建議。3.Scan:如圖表 1按下”Scan”扭,可搭配左方選項設定AFL執行週期,因此可結合AT。至少需設置buy/sell/ short/cover四種交易信號之一,AB分析AFL裡交易信號陣列變數之運算式,buy/sell/ short/cover交易信號陣列變數為真的bar其結果打印至分析輸出視窗。4.Explore:如圖表 1按下”Explore”扭,可搭配左方選項設定AFL執行週期,因此可結合AT。AB分析AFL裡Filter陣列變數運算式,將Filter為真的bar打印至分析輸出視窗。5.Backtest:執行預設回朔測試分析,如圖表 1按下”Backtest”扭選用回朔測試參數選項可區分成個別設定與一籃子集體設定(”Individual”或”Portfolio”)。拆成三個大步驟來看backtester內部運作I.參數始化:從原始可用淨值、每筆交易部位大小、最大開倉數目II.處理交易信號與實際交易:回朔測試引擎有六種處理交易信號的方式,分別為Regular、RegularRaw、RegularRawMulti、RegularRaw2、RegularRawMulti2、rotation,預設為Regular,函式SetBacktestMode設置處理方式。III.輸出模擬結果6.Portfolio Backtester Interface:此動作類型主要用於客制化回朔測試,AB採2-pass來實現回朔測試,第一回合收集處理交易信號,第二回合使用者能客制化操作backtester、trade、signal、stat這些物件的屬性及方法,如圖表 2
所示,backtester於AFL內以COM元件方式呈現其界面給使用者,其它物件都須透過backtester物件來間接操作。客制化回朔測試應用情境例如在回朔測試報表添加輸出參數,管控每一根K棒當下的狀況調整每筆交易部位大小,總投資額佔總淨值比例,使用者精細操作回朔測試引擎動作,往下我們將探討backtester運作流程細部框架。有兩種進入客制化回朔測試的方法,AAà”Setting”à”Portfolio tab”對話盒,勾選啟用客制化回朔測試處理,並把客制化回朔測試處理寫在一支獨立的*.AFL,選定該檔案;或者使用函式SetOption、SetCustomBacktestProc兩者之一來進入客制化回朔測試。
// only for custom backtest,choose one of follow 2 lines<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
//SetOption("UseCustomBacktestProc", True );
//SetCustomBacktestProc("");
If( Status("action") == actionIndicator) {
printf(“Current working action: Indicator”);
}
else
if( Status("action") == actionCommentary ) {
printf(“Current working action: Commentary”);
}
else
if( Status("action") == actionScan) {
printf(“Current working action: Scan”);
Filter = <expression>
}
else
if( Status("action") == actionExplore) {
printf(“Current working action: Explore”);
}
else
if( Status("action") == actionBacktest) {
printf(“Current working action: Backtest”);
}
else
if( Status("action") == actionPortfolio ) {
printf(“Current working action: cunstom backtest”);
//add custom backtest here
}
Code 1 AFL動作類型
圖表 2
回朔測試引擎內部主要物件關係 一、信號處理方式:1.Regular模式:此回朔測試模式每檔股票僅持有一筆部位,假設選擇只交易多頭部位,回朔測試引擎將針對buy/sell信號做處理,消除多餘的進場和出場信號。每一檔股票處理信號可以狀態圖來描述行為如圖表 3
,B/S是輸入,S0表示未持有多頭部位,S1代表持有一筆多頭部位,表格 1
為狀態轉移表,因為只有兩種狀態,只需一個D型flip-flop就能表示當前狀態值,輸出以下列式子表示
圖表 3
Regular模式持有部位變化之狀態圖(最多持有一筆未平倉)
InuputOutputPI(present input)PSNSPO(present output)B(buy)S(sell)qq+B_newS_new00S0S0(hold clear)0001S0S0(hold clear)0010S0S1(setup)1011S0S1(setup)1000S1S1(hold setup)0001S1S0(clear)0110S1S1(hold setup)0011S1S0(clear)01
表格 1
regular mode trade state transition table 表格 2
多檔股票以regular模式進行portfolio和individual回朔測試
Regular模式下,執行多檔股票回朔測試,我們詳細列出信號處理的每個步驟如表格 2
,條列說明回朔測試引擎動作l決定此時AFL動作類型是預設或客制化回朔測試。lIndividual-level或Portfolio-level回朔測試參數選項:初始淨值、每筆交易部位大小、最大開倉筆數,所有股票個別還是共用設定。l交易倉位類型:只交易多倉、只交易空倉、空倉和多倉。此範例只交易多倉,所以僅處理buy/sell信號,交易信號可設定延遲週期l如表格 1
所述,由S0轉成S1產生B_new信號,S1轉成S0產生S_new信號,一個B_new之後再配上一個S_new稱為一筆完整交易。Regular模式將連續相同的進場或出場信號簡化成一次,最終以B_new和S_new當作正式的交易信號。lPortfolio-level backtest,掃描每一根K棒,按照PositionScore排序股票,ranking高者優先處理交易,若下單可用淨值小於每筆交易部位大小或開倉總數達到最大開倉筆數,則無法再建立新倉。處理交易僅依照交易信號、匹配交易價格來決定是否成交。lIndividual-level backtest,每檔股票擁有獨立的回朔測試參數選項,每檔個股交易處理並不互相干涉,因此不需以PositionScore/Ranking來排序。 1.RegularRaw模式:Raw故名思義就是最原始,所有進場信號都會被保留,每檔股票僅持有一筆部位,連續2次進場信號間的所有出場信號則只保留一個,意即碰到出場信號則平倉所有部位,詳列6檔股票原始交易信號,Portfolio-level交易回朔測試如上述以PositionScore高低做為交易優權依據;Individual-level交易回朔測試,其結果與Regular模式一樣,此乃因為每檔股票至多建立一筆單,連續的相同信號只能發生一次交易。2.RegularRawMulti模式:與RegularRaw比較,此模式一樣以未經處理的進場信號來做建倉,Multi意指每檔股票可持有多筆部位,連續相同進場信號允許分批建多筆倉位;但一連串出場信號則視作一次平倉出場。以只建立多頭倉位看,部位變化狀態圖如圖表 4
為MaxOpenPositions=3時,有買進信號則部位增加,賣出時部位則減少。
圖表 4
部位變化狀態圖
表格 3
多檔股票以RegularRaw模式進行portfolio和individual回朔測試 4. RegularRaw2&RegularRaw2Multi:Raw2與前述Raw兩者主要差異在於出場信號處理,當使用預設回朔測試時出場信號一發生則立即將該股票的部位全數出清;Raw2則是不消除任何連串出場信號,每一筆出場信號都會保留在backtester物件裡的signal物件,當執行操作最低階的客制化回朔測試,可以根據限定的條件決定交易信號是否要做處理,能充份控制平倉數目,就如同分批調節部位,因此RegularRaw2Multi能實現分批進場和出場,很適合一般實際交易情境。 三、回朔測試引擎框架圖表 1所示AA視窗按下”Backtest”(Individual or Portfolio),AFL動作進入”actionBacktest” ,此時執行預設回朔測試,AFL內設定回朔測試參數選項會覆蓋圖表 5
視窗內對應的設定,交易部位類型僅交易多倉、僅交易空倉、多空倉均交易,於AFL內需指定多單交易信號Buy/Sell、空單交易信號Short/Cover,若無指定回朔測試處理交易信號方式,預設為Regular模式。Portfolio回朔測試使用PositionScore為多檔個股交易優先權排序。接著回朔測試引擎將逐筆掃描K棒,處理實際交易(開倉或平倉),記錄部位資訊、淨值陣列。 if ( Status("action") == actionBacktest ) { //1. AA視窗Backtest(Individual or Portfolio)
Buy = C < 39.00; Sell = 0; Short = 0; Cover = 0; /*2. trading position type, Buy/Sell or /Short/Cover assignment*/
SetBacktestMode(Regular); //set backtest mode,default is Regular
SetPositionSize(…); //assign PositionSize
SetOption(“Initial Equity”, 100000); /*3. initial equity*/
SetOption(“MaxOpenPositions”, 3); /*4. allowed max open positions */
BuyPrice/SellPrice/ShortPrice/CoverPrice; /* 5. assign trade price */
printf(“Current working action: Backtest”);
}
Code 2
AFL預設回朔測試