2013年11月5日 星期二

程式碼自動產生器的開發 Part 2

EasyTrader ArtNo 042
從Part1 的內容中,整個程式碼自動產生器的架構大概是這樣:

1.參數設定:
LongORshort (1), { 1 作多 -1 作空 }
setNetProf(150000), { 最低淨利 }
setMaxDD(500000), {最大MDD 限制 }
setMinTrade(20), {最少交易次數 }
setWinRate(20), {最低勝率 }
setPFvalue(0.0), {最低獲利因子 }
setMaxTrade(1200), {最多交易次數限制}

2.變數設定
2-1 進場規則使用的變數 , 由於需要電腦隨機亂數選取 , 所以即使看似簡單的 PriceL[BarL] >= PriceR[BarR]的公式中 PriceL, PriceR,BarL,BarR 還有運算符號的儲存都要使用到陣列,在開發過程中先使用較少的陣列元素 10 , 初設值都為 0


{-----陣列變數-----}
PriceL[10](0), {公式左邊 0 開盤價 1 最高價 , 2 最低價 , 3 收盤價 }
也就是說 PriceL[0] = 0 時,代表公式左邊要放 Open 當PriceL[0] = 2 時,代表公式左邊要放 Low

PriceR[10](0), { 公式右邊 0 開盤價 1 最高價 , 2 最低價 , 3 收盤價 }
CompareSign[10](0), { 運算符號 0 代表 >=, 1 代表 <= }
BarLeft[10](0), {公式左邊價格回溯Bar 根數}
BarRight[10](0); { 公式右邊價格回溯Bar 根數 }

所以 Open[1] <= Low[2] 的公式 ,電腦選出來的是 PriceL = 0 , BarLeft =1 , CompareSign = 1 ,PriceR = 2 , BarRight = 2

2-2 決定公式的組數(例如亂數選出 3 ,意思是要有 3組不同的公式都符合條件才會下單
2-3 決定出場的規則


{ ------ 一般變數 -------}

NRules(0), { 公式組數 }
ExitType(0), { 出場規則 1 SetStopLoss , 2 SetPercentTarget ,3 Highest/Lowest Range Value,4 for SetProfitTarget}

TradeStopLoss(0), { 停損點數 }
Barlength(0) , { 區間Bar數量 }
TradeProfit(0) , { 停利點數}
PullBack(0), { 回落百分比}

3.主程式
3-1.亂數選取陣列變數與一般變數的值
3-2.依據選取數值計算並判斷是否符合公式組數的條件
3-3.符合條件的公式組下多單或空單,並依電腦選定的出場方式與參數出場
3-4.計算淨利,交易次數,MDD,勝率,獲利因子是否符合設定的輸出條件
3-5.完全符合的所有變數輸出成為可執行的交易策略程式碼

在整個過程中很重要的啟動點是亂數的選取,在這裡先介紹幾個在程式碼自動產生器裡會用到的內建函數
1. Random(N) ,它會傳回 0~N中間的任意數 ,例如 Random(10) 傳回的可能是 1.23 也可能是 5.1
2. Round(N,decimal) , 它會傳回依 Decimal小數位數的N值 ,例如 Round(5.12482,2) 傳回 5.12 , 若是 Round(5.12482,3) 傳回 5.125 會作四捨五入
3. IntPortion(N), 它會傳回 N值的整數部份 ,例如 IntPortion(5.1248), 傳回 5

當我們在寫程式的過程中常常會在相同或不同程式裡用到的公式,都可以寫成自訂函數( user function),以便日後使用,以下為程式碼自動產生器中使用的自訂函數(取得所有主程式需要的隨機變數

{Function: _MagicQS168_fVars. Called by strategy _MagicQS168.}

Inputs:
PriceL[max1](NumericArrayRef),
PriceR[max2](NumericArrayRef),
CompareSign[max3](NumericArrayRef),
BarLeft[max4](NumericArrayRef),
BarRight[max5](NumericArrayRef),
XRules(NumericRef),
XBarExit(NumericRef),
ExitType(NumericRef),
TradeProfit(NumericRef),
PullBack(NumericRef),
BarLength(NumericRef);

Var: LoopNo(0); { loop counter }

XRules = _MagicQS168_fValue(1, 10);
XBarExit = _MagicQS168_fValue(1, 20);
ExitType = _MagicQS168_fValue(1, 4);

TradeProfit = (IntPortion(_MagicQS168_fValue(20, 150)/10)+1) * 10;
PullBack = (IntPortion(_MagicQS168_fValue(0, 90)/10)+2) * 10;
BarLength = (IntPortion(_MagicQS168_fValue(0, 50)/10)+1) * 10 ;

{ --- 陣列內容放入 Open/High/Low/Close 的代號 ,運算符號代號 ----}

For LoopNo = 0 to NRules - 1 Begin
   PriceL[LoopNo] =_MagicQS168_fValue(0, 3);
   PriceR[LoopNo] = _MagicQS168_fValue(0, 3);
   CompareSign[LoopNo] = _MagicQS168_fValue(0, 1);
   BarLeft[LoopNo] = _MagicQS168_fValue(0, 20);
   BarRight[LoopNo] = _MagicQS168_fValue(0, 20);
End;

_MagicQS168_fVars = true;

透過這個函數可得到的數學上的基本排列組合有 128,000 組(不包含出場參數組合)可供電腦隨機挑出來作績效評估 Xrules(10) * PriceL(4) * BarLeft(20) * CompareSign(2) * PriceR(4) * BarRight(20) 也就是說同樣的主程式每次回測都可以選出符合基本設定的不同程式群組輸出

這個函數跟一般我們使用的內建函數有一點不同 , 一般常用的函數例如 RSI(Close,5) , Highest(High,3) ,Average(Close,10) 的回傳值只有一個值 ,可能是數值,可能是邏輯值(True or False),也可能是文字字串,依函數本身特性而定, 當我們需要同一個函數回傳很多不同的變數值時,就需要一點點小技巧了, 說明如下

這是常用內建計算平均值的函數 Average
{*******************************************************************
Description: Simple Moving Average
Provided By: Omega Research, Inc. (c) Copyright 1999
********************************************************************}
Inputs: Price(NumericSeries), Length(NumericSimple);
Variables: Sum(0), Counter(0);
Sum = 0;
For counter = 0 To Length - 1 Begin
   Sum = Sum + Price[counter];
End;

If Length > 0 Then
Average = Sum / Length
Else
Average = 0;

它會將傳入的兩個參數 Price , Length 經計算後以函數本身的名稱傳回給主程式使用

Price(NumericSeries)代表Average函數的這個參數可使用的是數值序列 Close[1] , High[2] ...
Length(NumericSimple)代表Average函數的這個參數只能使用的是數值

再看看自訂函數中的參數括號內的是
PriceL[max1](NumericArrayRef),NRules(NumericRef), 對的 ! 就是 Ref這個英文 ,它代表的意義是從主程式傳進來的這個變數在函數內經過運算後,也能在主程式內使用最後的運算數字

例如 主程式
VarA = 1 ;
VarB = 5 ;
Value1 = _Myfunction( VarA ,VarB) ;

自訂函數 _MyFunction
input : VarA(NumericSimple) , VarB(NumericRef) ;

Value2 = VarA + 3 ;
Value3 = VarB + 4 ;
VarB = Value3 ;
_Myfunction = Value2 ;

回到主程式 Value1 = 4 , VarB = 9 同時傳回兩個數字

{---------- 待續 -----------}

1 則留言: