//+------------------------------------------------------------------+ //| Optimatic.mq4 | //| Copyright © 2009, Marketeer | //| based on original code of IndicatorOptimizer by Sergey Kravchuk | //| http://forum.mql4.com, http://forum.alpari.ru | //+------------------------------------------------------------------+ #property copyright "Copyright © 2009, Marketeer, Sergey Kravchuk" #property link "http://forum.mql4.com" #include <BreakPoint.mqh> // ìàêñèìàëüíî äîïóñòèìîå êîëè÷åñòâî áàðîâ äëÿ ðàñ÷åòà #define MAXBARS 10000 // åäèíèöû ðàñ÷åòà è âûâîäà â ëîã äëÿ ïðîñàäêè bool ProfitInPoints = true; // ïàðàìåòðû ïàêåòíîé îïòèìèçàöèè extern string OptimizationGroup = "[Optimization]"; extern int Variation = 50; // ïðîöåíò ðàçáðîñà ïàðàìåòðîâ îò çàäàííûõ ñðåäíèõ extern int MaxDimension = 0; // ìàêñèìàëüíîå êîëè÷åñòâî øàãîâ îáñ÷åòà ïî êàæäîìó ïàðàìåòðó extern double MinProfitFactor = 1.5; extern double MinProfitLossRatio = 1.0; extern int DrawdownLimit = 30; // % extern int MaxProfitDisbalance = 100; // % extern double MinCntProfit = 0; // per day extern int MaxLossSeries = 5; extern int StartBar = 25; // êîëè÷åñòâî îáñ÷èòûâàåìûõ áàðîâ extern bool MultiSignal = false; extern bool PrintDetails = false; extern bool KeepLastGoodResults = false; extern bool UseOptimapic = true; extern int OptimaticPeriod = PERIOD_D1; // PERIOD_W1, PERIOD_MN1 extern int TP = 0; extern int SL = 0; extern int StartDepo = 1000; // íà÷àëüíûé äåïîçèò äëÿ ðàñ÷åòà ïðîñàäîê, â âàëþòå äåïîçèòà (îíëàéí) èëè â áàçîâîé âàëþòå (íàïð. EUR äëÿ EURJPY, îôôëàéí) //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— //ïàðàìåòðû ðàñ÷åòîâ îïåðàöèé, ïðèáûëè è óáûòêîâ double LotForCalc = 0.1; // ðàçìåð ëîòà äëÿ ðàñ÷åòà â âàëþòå double ToCurrency; // êîýôôèöèåíò ïåðåñ÷åòà ïóíêòîâ â âàëþòó bool SymbolCurrency = false; // èñïîëüçîâàíèå áàçîâîé âàëþòû âìåñòî âàëþòû äåïîçèòà â îôôëàéíå double sBuy[], sCloseBuy[], sSell[], sCloseSell[]; // ìàññèâû äëÿ ñèãíàëîâ int sBuyCnt, sSellCnt, sBuyCloseCnt, sSellCloseCnt; // ñ÷åò÷èêè ñèãíàëîâ int i, DisplayBars; double Ballance[]; // áàëàíñ ïî áàðàì double Equity[]; // ëèêâèäíîñòü ïî áàðàì double Profit, MaxProfit, Drawdown, MaxPeak; double NetProfit, NetLoss; int MaxDrawdownBar; int CntProfit, CntLoose; int MaxLossPoints, MaxProfitPoints; int MaxSuccesiveLossCount; //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // ìàññèâû äëÿ îïòèìèçàöèè ïàðàìåòðîâ //bool FirstRun; // ïðåäïîëàãàåòñÿ èñïîëüçîâàòü äëÿ êðàòêîãî äîîáó÷åíèÿ int MAXPARAMS; double MinP[]; double GoodParams[]; double BadParams[]; double Params[]; // could we use multidimentional array? double MaxP[]; int Sizes[]; int Cursors[]; int Steps[]; // need overload for double? //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // // Public: ÁËÎÊ ÏÐÎÖÅÄÓÐ È ÔÓÍÊÖÈÉ ÄËß ÂÛÇÎÂÀ ÈÇ ÝÊÑÏÅÐÒÀ È ÓÏÐÀÂËÅÍÈß ÁÈÁËÈÎÒÅÊÎÉ Optimatic // //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // âûçûâàåòñÿ èç ôóíêöèè init ñ óêàçàíèåì ÷èñëà îïòèìèçèðóåìûõ ïàðàìåòðîâ int optilibinit(int MaxParams) { if(IsOptimization()) { UseOptimapic = false; return(0); // do not run OptimaticLib while built-in optimization is running } // êîýôôèöèåíò äëÿ ïåðåâîäà ïóíêòîâ â âàëþòó äåïîçèòà ToCurrency = MarketInfo(Symbol(),MODE_TICKVALUE)*LotForCalc; if(ToCurrency == 0.0) // â îôôëàéíå ñ÷èòàåì â áàçîâîé âàëþòå èíñòðóìåíòà ïî òåêóùåìó êóðñó { // áàçîâàÿ óäîáíà äëÿ ïîêàçà ñóìì â EUR è GBP äëÿ áåçäîëëàðîâûõ èíñòðóìåíòîâ if(Close[0] == 0) Print("ZERO PRICE!" + TimeToStr(Time[0])); ToCurrency = (LotForCalc * MarketInfo(Symbol(),MODE_LOTSIZE)*Point)/Close[0]; SymbolCurrency = true; } MAXPARAMS = MaxParams; ArrayResize(MinP, MAXPARAMS); ArrayResize(GoodParams, MAXPARAMS); ArrayResize(BadParams, MAXPARAMS); ArrayResize(Params, MAXPARAMS); ArrayResize(MaxP, MAXPARAMS); ArrayResize(Sizes, MAXPARAMS); ArrayResize(Cursors, MAXPARAMS); ArrayResize(Steps, MAXPARAMS); if(OptimaticPeriod < PERIOD_D1) OptimaticPeriod = PERIOD_W1; return(0); } static bool AverageValues = false; // ìîæåò âûçûâàòüñÿ èç ýêñïåðòà îïöèîíàëüíî äëÿ îïðåäåëåíèÿ ôàêòà óñðåäíåíèÿ ïàðàìåòðîâ bool optilibisaverage() { return(AverageValues); } static int PreviousDay = -1; // âîçâðàùàåò ïðèçíàê òîãî, ÷òî íàñòàëî âðåìÿ çàïóñêà î÷åðåäíîé îïòèìèçàöèè bool optilibistime() { bool WeekStart = (TimeDayOfWeek(Time[0]) == 1); if(OptimaticPeriod != PERIOD_W1) WeekStart = true; bool MonthStart = (TimeDay(Time[0]) == 1); if(OptimaticPeriod != PERIOD_MN1) MonthStart = true; bool Result = (TimeDayOfYear(Time[0]) != PreviousDay && WeekStart && MonthStart) || PreviousDay == -1; return(Result); } // íåïîñðåäñòâåííî âûïîëíåíèå îïòèìèçàöèè ïàðàìåòðîâ int optilibstart() { HideTestIndicators(true); PreviousDay = TimeDayOfYear(Time[0]); if(!KeepLastGoodResults) { for(int i = 0; i < MAXPARAMS; i++) GoodParams[i] = 0; } CalculateDateRange(); } // ïîëó÷åíèå çíà÷åíèé îïòèìèçèðîâàííûõ ïàðàìåòðîâ void optilibgetresults(double &result[]) { for(int i = 0; i < MAXPARAMS; i++) result[i] = GoodParams[i]; } // ïîçâîëÿåò çàáàíèòü ïîñëåäíèé "îïòèìàëüíûé" íàáîð ïàðàìåòðîâ, åñëè îí îêàçàëñÿ ïðîèãðûøíûì void optilibbanlastresults(bool Reset = false) { if(Reset) { ArrayInitialize(BadParams, 0); //Print("Bad params reset"); } else { //Print("Bad params SET"); for(int i = 0; i < MAXPARAMS; i++) { BadParams[i] = GoodParams[i]; //Print("BadParams[", i, "]=", BadParams[i]); } } } //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // // Private: ÁËÎÊ ÂÍÓÒÐÅÍÍÈÕ ÏÐÎÖÅÄÓÐ È ÔÓÍÊÖÈÉ ÁÈÁËÈÎÒÅÊÈ Optimatic // //————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // ôóíêöèÿ îäíîêðàòíîãî ðàñ÷åòà ïîêàçàòåëåé òîðãîâëè ïðè çàäàííûõ ïàðàìåòðàõ void OptIterator(double InParams[]) { int i1, i2; double P1, P2; CntProfit = 0; CntLoose = 0; Profit = 0; NetProfit = 0; NetLoss = 0; // ïîäãîòîâèì ñ÷åò÷èêè ñèãíàëîâ sBuyCnt=0; sSellCnt=0; sBuyCloseCnt=0; sSellCloseCnt=0; //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— if(StartBar > 0) DisplayBars = StartBar; else DisplayBars = MAXBARS; // êîëè÷åñòâî îáñ÷èòûâàåìûõ áàðîâ if(DisplayBars > MAXBARS) DisplayBars = MAXBARS; // CALLBACK: ôóíêöèÿ îáðàòíîãî âûçîâà, äîëæíà áûòü îïðåäåëåíà â ýêñïåðòå // óñòàíàâëèâàåò â ýêñïåðòå íàáîð ïàðàìåòðîâ èíäèêàòîðîâ íà òåêóùóþ èòåðàöèþ OptimaticOnSetParams(InParams); // will do something like that: // MAPeriod = Params[0]; // RSIPeriod = Params[1]; // RSILevel = Params[2]; // îòâåäåì ïàìÿòü ïîä ñèãíàëüíûå ìàññèâû è îáíóëèì èõ çíà÷åíèÿ ArrayResize(sBuy, DisplayBars + 1); ArrayInitialize(sBuy,0); ArrayResize(sSell, DisplayBars + 1); ArrayInitialize(sSell,0); ArrayResize(sCloseBuy, DisplayBars + 1); ArrayInitialize(sCloseBuy,0); ArrayResize(sCloseSell, DisplayBars + 1); ArrayInitialize(sCloseSell,0); ArrayResize(Ballance, DisplayBars + 2); ArrayResize(Equity, DisplayBars + 2); //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // ïîäãîòîâèì ðàññ÷åò áàëëàíñà ArrayInitialize(Ballance,0); ArrayInitialize(Equity,0); MaxProfit = 0; Drawdown = 0; MaxPeak = 0; MaxLossPoints = 0; MaxProfitPoints = 0; // çàïîëíèì çíà÷åíèÿìè ñèãíàëüíûå ìàññèâû è ïîñ÷èòàåì èõ êîëè÷åñòâî for(i = DisplayBars; i >= 0; i--) { bool BuySignal; bool SellSignal; bool BuyExitSignal; bool SellExitSignal; BuySignal = false; SellSignal = false; BuyExitSignal = false; SellExitSignal = false; // CALLBACK: ôóíêöèÿ îáðàòíîãî âûçîâà, äîëæíà áûòü îïðåäåëåíà â ýêñïåðòå // ïîëó÷àåò èç ýêñïåðòà ñèãíàëû ïðè òåêóùèõ çíà÷åíèÿõ ïàðàìåòðîâ íà áàðå i OptimaticCallback(i, BuySignal, SellSignal, BuyExitSignal, SellExitSignal); if(BuySignal) sBuy[i]=1; if(BuyExitSignal) sCloseBuy[i]=1; if(SellSignal) sSell[i]=1; if(SellExitSignal) sCloseSell[i]=1; } //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // óäàëèì ïîâòîðÿþùèåñÿ ñèãíàëû îñòàâèâ òîëüêî ñàìûå ïåðâûå - ëåâûå ïî ãðàôèêó for(i=0;i<DisplayBars;i++) { if(sBuy[i]==sBuy[i+1]) sBuy[i]=0; if(sSell[i]==sSell[i+1]) sSell[i]=0; if(sCloseBuy[i]==sCloseBuy[i+1]) sCloseBuy[i]=0; if(sCloseSell[i]==sCloseSell[i+1]) sCloseSell[i]=0; } // äîáàâèì ïðèíóäèòåëüíîå çàêðûòèå íà ãðàíèöå äèàïàçîíà sCloseBuy[0]=1; sCloseSell[0]=1; //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // ïîñ÷èòàåì êîëè÷åñòâî ñèãíàëîâ for(i=0;i<DisplayBars;i++) { if(sBuy [i]!=0) sBuyCnt++; if(sCloseBuy [i]!=0) sBuyCloseCnt++; if(sSell[i]!=0) sSellCnt++; if(sCloseSell[i]!=0) sSellCloseCnt++; } //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // ðàññòàâèì ìåòêè, íàðèñóåì ÇÇ è ïîñ÷èòàåì ïðèáûëü //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // îáðàáîòàåì ïîêóïêè for(i=DisplayBars-1;i>=0;i--) // ïîéäåì ñîáèðàòü òî÷êè { // íàéäåì î÷åðåäíóþ òî÷êó îòêðûòèÿ è çàïîìíèì åå ìåñòî è öåíó for(i1=i;i1>=0;i1--) { if(sBuy[i1]!=0) { break; } } P1=Open[i1]; P1/=Point; // íàéäåì î÷åðåäíóþ òî÷êó çàêðûòèÿ ïîêóïêè è çàïîìíèì åå ìåñòî è öåíó for(i2=i1-1;i2>=0;i2--) { if(sCloseBuy[i2]!=0) { break; } if(TP > 0) // åñëè óñòàíîâëåí TP, ïðîâåðèì åãî { if(High[i2]/Point - P1 > TP) { sCloseBuy[i2] = 2; break; } } if(SL > 0) // åñëè óñòàíîâëåí SL, ïðîâåðèì åãî { if(P1 - Low[i2]/Point > SL) { sCloseBuy[i2] = 4; break; } } } if(i2<0) i2=0; // äëÿ ïîñëåäíåé íåçàêðûòîé ïîçû ñ÷èòàåì çàêðûòèå íà òåêóùåé öåíå if(MultiSignal) { i=i1; // åñëè õîòèì ãåíåðèòü ïàðàëåëëüíûå ïîçèöèè, íå ïðîïóñêàåì áàðû äî çàêðûòèÿ òåêóùåé } else { i=i2; // íîâûé áàð äëÿ ïðîäîëæåíèÿ ïîèñêà òî÷åê îòêðûòèÿ } // îïðåäåëèì öåíû äëÿ ðèñîâàíèÿ P2=Open[i2]; P2/=Point; // ïðèâåäåì öåíû ê ïóíêòàì // îïðåäåëÿåì ïðîôèò è çàïîëíÿåì ñîîòâåòñòâóþùèé áóôåð if(i1>=0) { Profit=Profit+P2-P1; // ñîáåðåì ñóììàðíûé ïðîôèò if(P2-P1>=0) { NetProfit += P2-P1; CntProfit++; if(P2-P1 > MaxProfitPoints) { MaxProfitPoints = P2-P1; } } else { NetLoss += P1-P2; CntLoose++; // ïîñ÷èòàåì ÷èñëî îðäåðîâ if(P1-P2 > MaxLossPoints) { MaxLossPoints = P1-P2; } } Ballance[i2] += P2-P1; } } //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // îáðàáîòàåì ïðîäàæè for(i=DisplayBars-1;i>=0;i--) // ïîéäåì ñîáèðàòü òî÷êè { // íàéäåì î÷åðåäíóþ òî÷êó îòêðûòèÿ è çàïîìíèì åå ìåñòî è öåíó for(i1=i;i1>=0;i1--) { if(sSell[i1]!=0) { break; } } P1=Open[i1]; P1/=Point; // íàéäåì î÷åðåäíóþ òî÷êó çàêðûòèÿ ïîêóïêè è çàïîìíèì åå ìåñòî è öåíó for(i2=i1-1;i2>=0;i2--) { if(sCloseSell[i2]!=0) { break; } if(TP > 0) // åñëè óñòàíîâëåí TP, ïðîâåðèì åãî { if(P1 - Low[i2]/Point > TP) { sCloseSell[i2] = 2; break; } } if(SL > 0) // åñëè óñòàíîâëåí SL, ïðîâåðèì åãî { if(High[i2]/Point - P1 > SL) { sCloseSell[i2] = 4; break; } } } if(i2<0) i2=0; // äëÿ ïîñëåäíåé íåçàêðûòîé ïîçû ñ÷èòàåì çàêðûòèå íà òåêóùåé öåíå if(MultiSignal) { i=i1; // åñëè õîòèì ãåíåðèòü ïàðàëåëëüíûå ïîçèöèè, íå ïðîïóñêàåì áàðû äî çàêðûòèÿ òåêóùåé } else { i=i2; // íîâûé áàð äëÿ ïðîäîëæåíèÿ ïîèñêà òî÷åê îòêðûòèÿ } // îïðåäåëèì öåíû äëÿ ðèñîâàíèÿ â çàâèñèìîñòè îò ïàðàìåòðà îïòèìèñòè÷íîñòè Optimizm P2=Open[i2]; P2/=Point; // ïðèâåäåì öåíû ê ïóíêòàì // åñëè îáå òî÷êè åñòü - îïðåäåëÿåì ïðîôèò è çàïîëíÿåì ñîîòâåòñòâóþùèé áóôåð if(i1>=0) { Profit=Profit+P1-P2; // ñîáåðåì ñóììàðíûé ïðîôèò if(P1-P2>=0) { NetProfit += P1-P2; CntProfit++; if(P1-P2 > MaxProfitPoints) { MaxProfitPoints = P1-P2; } } else { NetLoss += P2-P1; CntLoose++; // ïîñ÷èòàåì ÷èñëî îðäåðîâ if(P2-P1 > MaxLossPoints) { MaxLossPoints = P2-P1; } } Ballance[i2] += P1-P2; } } //——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— // ïîñòðîèì ãðàôèê áàëëàíñà // ñîáåðåì ïîñëåäîâàòåëüíî è ïðîññóìèðóåì ïðîôèòû // ïîñ÷èòàåì ïðîñàäêó è ñòàòèñòèêó ïî ïðèáûëüíûì è óáûòî÷íûì ñäåëêàì if(ToCurrency == 0.0) Print("ZERO ToCurrency!" + TimeToStr(Time[0])); Ballance[DisplayBars+1] = StartDepo/ToCurrency; Equity[DisplayBars+1] = StartDepo/ToCurrency; bool bBuy = false; bool bSell = false; int OpenBar = -1; int SuccesiveLossCount = 0; int count = 0; datetime LossDateTime = 0; datetime LossDateTimeMax = 0; for(i=DisplayBars-1;i>=0;i--) { if(sCloseBuy[i] != 0) { if(Ballance[i] < 0) { SuccesiveLossCount++; if(LossDateTime == 0) { LossDateTime = Time[OpenBar]; } if(SuccesiveLossCount > MaxSuccesiveLossCount) { MaxSuccesiveLossCount = SuccesiveLossCount; LossDateTimeMax = LossDateTime; } } else if(bBuy) { SuccesiveLossCount = 0; LossDateTime = 0; } if(bBuy) { bBuy = false; OpenBar = -1; } } if(sCloseSell[i] != 0) { if(Ballance[i] < 0) { SuccesiveLossCount++; if(LossDateTime == 0) { LossDateTime = Time[OpenBar]; } if(SuccesiveLossCount > MaxSuccesiveLossCount) { MaxSuccesiveLossCount = SuccesiveLossCount; LossDateTimeMax = LossDateTime; } } else if(bSell) { SuccesiveLossCount = 0; LossDateTime = 0; } if(bSell) { bSell = false; OpenBar = -1; } } if(sBuy[i] != 0) { if(!bBuy) { bBuy = true; OpenBar = i; } } if(sSell[i] != 0) { if(!bSell) { bSell = true; OpenBar = i; } } double Swap = 0, DaySwap = 0; if(bBuy) DaySwap = MarketInfo(Symbol(), MODE_SWAPLONG); if(bSell) DaySwap = MarketInfo(Symbol(), MODE_SWAPSHORT); int bar = iBarShift(NULL,0,Time[i]); if(TimeDayOfWeek(iTime(NULL,0,bar)) != TimeDayOfWeek(iTime(NULL,0,bar+1)) && OpenBar != bar) { switch(MarketInfo(Symbol(),MODE_PROFITCALCMODE)) { case 0: if(TimeDayOfWeek(iTime(NULL,0,bar))==4) Swap = 3*DaySwap; else Swap = DaySwap; break; case 1: if(TimeDayOfWeek(iTime(NULL,0,bar))==1) Swap = 3*DaySwap; else Swap = DaySwap; } } double divider = (LotForCalc * MarketInfo(Symbol(),MODE_LOTSIZE)*Point); if(divider == 0.0) Print("ZERO divider " + TimeToStr(Time[0])); int SwapPoints = Swap/divider; double profitloss = 0; double spread = 0; if(bBuy) { profitloss = (iHigh(NULL,0,bar) - Open[OpenBar]); } if(bSell) { spread = Point * MarketInfo(Symbol(),MODE_SPREAD); profitloss = (Open[OpenBar] - iLow(NULL,0,bar) - spread); } if(bBuy || bSell) { profitloss /= Point; profitloss += SwapPoints; } Ballance[i+1] = Ballance[i+2] + Ballance[i]; Equity[i+1] = Ballance[i+1] + profitloss; } for(i=0;i<DisplayBars-2;i++) { Ballance[i] = Ballance[i+1]; Equity[i] = Equity[i+1]; } // ñ÷èòàåì ïðîñàäêó for(i = DisplayBars-1; i >= 0; i--) { CalculateDrawdown(Equity[i], i); } if(!ProfitInPoints) { Drawdown *= ToCurrency; MaxPeak *= ToCurrency; } } void CalculateDrawdown(double Equity, int Bar) { if(MaxProfit < Equity) { MaxProfit = Equity; } if(Drawdown < (MaxProfit - Equity)) { Drawdown = MaxProfit - Equity; MaxPeak = MaxProfit; MaxDrawdownBar = Bar; } } // óâåëè÷åíèå çíà÷åíèé ïàðàìåòðîâ, ïî îäíîìó ïàðàìåòðó çà îäèí âûçîâ int Increment(double &Params[], int Sizes[], int &Cursors[]) { int i; for(i = 0; i < MAXPARAMS; i++) { if(Cursors[i] < Sizes[i] - 1) { Cursors[i]++; Params[i]+=Steps[i]; return(1); } else { Cursors[i] = 0; Params[i] = MinP[i]; if(Cursors[i+1] < Sizes[i+1] - 1) { Cursors[i+1]++; Params[i+1]+=Steps[i+1]; return(1); } } } return(0); } void CalculateDateRange() { // TODO: new parameters should run a test on different period? // CALLBACK: ôóíêöèÿ îáðàòíîãî âûçîâà, äîëæíà áûòü îïðåäåëåíà â ýêñïåðòå // ïîëó÷àåò èç ýêñïåðòà íà÷àëüíûå çíà÷åíèÿ ïàðàìåòðîâ OptimaticOnInitParams(Params); // will do something like that: // Params[0] = MAPeriod; // Params[1] = RSIPeriod; // Params[2] = RSILevel; ArrayInitialize(MinP, 0); ArrayInitialize(GoodParams, 0); ArrayInitialize(MaxP, 0); ArrayInitialize(Sizes, 0); ArrayInitialize(Cursors, 0); ArrayInitialize(Steps, 0); double AveValue[]; int AveCount[]; ArrayResize(AveValue, MAXPARAMS); ArrayResize(AveCount, MAXPARAMS); ArrayInitialize(AveValue, 0); ArrayInitialize(AveCount, 0); int TotalDays = StartBar / (1440 / Period()); if(TotalDays == 0) TotalDays = 1; Print(TotalDays + " days optimization"); int TotalCount = 1; for(int i = 0; i < MAXPARAMS; i++) { double Range = Params[i] * Variation / 100; MinP[i] = NormalizeDouble(Params[i] - Range, 0); MaxP[i] = NormalizeDouble(Params[i] + Range, 0); if(MinP[i] <= 0) // TODO: should we support negative parameters? { MinP[i] = 1; MaxP[i] = 1 + 2*Range; } if(MaxP[i] < MinP[i]) MaxP[i] = MinP[i]; Params[i] = MinP[i]; Cursors[i] = 0; Sizes[i] = MaxP[i] - MinP[i] + 1; if(MaxDimension > 0 && Sizes[i] > MaxDimension) Steps[i] = Sizes[i]/MaxDimension + 1; else Steps[i] = 1; Sizes[i] = (MaxP[i] - MinP[i] + 1)/Steps[i]; TotalCount *= Sizes[i]; Print("i=", i, " ", Sizes[i], " ", MinP[i], "-", MaxP[i], " st:", Steps[i]); } double MaxProfitFactor = 0; double GoodProfitFactor = 0; double MinGoodDrawdownPercent = 10000000; int Count = 0; //FirstRun = true; bool NewGoodParams = false; while(!IsStopped()) { MaxSuccesiveLossCount = 0; OptIterator(Params); //if(FirstRun) //{ ////Variation /= 2; ////StartBar /= 2; //FirstRun = false; //} double DrawdownPercent; if(MaxPeak == 0.0) { DrawdownPercent = 100; } else { DrawdownPercent = 100*Drawdown/MaxPeak; } string GoodSet; int ProfitDisbalance; if(MaxProfitPoints > 0 && CntProfit > 0) { ProfitDisbalance = 100 * MaxProfitPoints / NetProfit; // simple formula, not using counter and average profit // another forumla is average profit / most profitable order profit // ProfitDisbalance = 100 - 100 *(NetProfit/CntProfit) / MaxProfitPoints; } else { ProfitDisbalance = 100; } double ProfitFactor; if(NetLoss > 0) { ProfitFactor = 1.0 * NetProfit / NetLoss; } else { ProfitFactor = 2.0 * NetProfit / 1000; // 5- and 3-digits const, change to 100 for 4-and 2-digits } if(ProfitFactor > MaxProfitFactor) MaxProfitFactor = ProfitFactor; double ProfitLossRatio; if(CntLoose > 0) { ProfitLossRatio = 1.0 * CntProfit / CntLoose; } else { ProfitLossRatio = 2.0 * CntProfit; } // TODO: we ñould use a complex function which combines all factors with given weights if(DrawdownPercent <= DrawdownLimit && ProfitFactor >= MinProfitFactor && ProfitLossRatio >= MinProfitLossRatio && ProfitDisbalance <= MaxProfitDisbalance && 1.0*CntProfit/TotalDays >= MinCntProfit && MaxSuccesiveLossCount <= MaxLossSeries) { GoodSet = "***"; bool PassCondition = false; //if(DrawdownPercent < MinGoodDrawdownPercent) // TODO: DrawdownMinimax mode if(ProfitFactor > GoodProfitFactor) PassCondition = true; else { if(ProfitFactor == GoodProfitFactor) { if(DrawdownPercent < MinGoodDrawdownPercent) { ArrayInitialize(AveValue, 0); ArrayInitialize(AveCount, 0); PassCondition = true; } else if(DrawdownPercent == MinGoodDrawdownPercent) { // if PF and DD is equal for several param sets, calculate average values for result params AverageValues = true; for(i = 0; i < MAXPARAMS; i++) { int NewCount; NewCount = AveCount[i] + 1; AveValue[i] = (AveValue[i]*AveCount[i] + Params[i])/NewCount; AveCount[i] = NewCount; GoodParams[i] = NormalizeDouble(AveValue[i], 0); // TODO: ParamsAccuracy } MinGoodDrawdownPercent = DrawdownPercent; GoodProfitFactor = ProfitFactor; NewGoodParams = true; string StrParamsDeb; StrParamsDeb = ""; for(i = 0; i < MAXPARAMS; i++) { StrParamsDeb = StrParamsDeb + DoubleToStr(Params[i], 0) + " (" + DoubleToStr(AveValue[i], 2) + "[" + AveCount[i]+ "]) "; } Print("AVE: ", StrParamsDeb); } } } if(PassCondition) { MinGoodDrawdownPercent = DrawdownPercent; GoodProfitFactor = ProfitFactor; for(i = 0; i < MAXPARAMS; i++) { GoodParams[i] = Params[i]; } NewGoodParams = true; AverageValues = false; } } else { if(PrintDetails) { GoodSet = "PRB:"; if(DrawdownPercent > DrawdownLimit) GoodSet = GoodSet + " DD"; if(ProfitFactor < MinProfitFactor) GoodSet = GoodSet + " PF"; if(ProfitLossRatio < MinProfitLossRatio) GoodSet = GoodSet + " LR"; if(ProfitDisbalance > MaxProfitDisbalance) GoodSet = GoodSet + " DB"; if(1.0*CntProfit/TotalDays < MinCntProfit) GoodSet = GoodSet + " MC"; if(MaxSuccesiveLossCount > MaxLossSeries) GoodSet = GoodSet + " LS"; } // TODO: we can remember bad (x, y, z) params and skip all following tests near this point, // such as (x+1, y, z), (x+1, y+1, z+1) } if(PrintDetails) { string StrParams; StrParams = ""; for(i = 0; i < MAXPARAMS; i++) { StrParams = StrParams + DoubleToStr(Params[i], 0) + " "; } Print(StrParams, ": ", Profit, " PF=", DoubleToStr(ProfitFactor, 2), " LR[p/l]=", DoubleToStr(ProfitLossRatio, 2), " DD=", DoubleToStr(DrawdownPercent, 2), "% MC=", DoubleToStr(1.0*CntProfit/TotalDays, 2), "(", CntProfit, ")", " DB=", ProfitDisbalance, " LS=", MaxSuccesiveLossCount, " ", GoodSet); } // move to next combination of parameters, if it's the end of all values - then exit the loop if(Increment(Params, Sizes, Cursors) == 0) break; // we could use recursion, but abstain from it if(ParamsAreBanned(Params)) // check for a banned set of parameters, if it's the case - repeat again { Print("Skipping bad params:" + PrintParams(Params)); if(Increment(Params, Sizes, Cursors) == 0) break; } if(TotalCount == 0) Print("ZERO TotalCount ", TimeToStr(Time[0])); int result = ShowProgress(TimeToStr(Time[0], TIME_DATE) + " Iteration " + DoubleToStr(Count, 0) + "/" + DoubleToStr(TotalCount, 0) + " " + DoubleToStr(Count*100/TotalCount, 0) + "%", Count*100/TotalCount); if(result == 1) break; Count++; } ShowProgress("Finished", 101); string ParamStr; ParamStr = PrintParams(GoodParams); string Decision = "Params:" + ParamStr + " | " + "Drawdown:" + DoubleToStr(MinGoodDrawdownPercent, 2) + " | " + "Profit Factor:" + DoubleToStr(GoodProfitFactor, 2) + " of " + DoubleToStr(MaxProfitFactor, 2); if(!NewGoodParams && KeepLastGoodResults) { Decision = Decision + " OLD!"; } //Comment(Decision); Print(Decision); } string PrintParams(double Params[]) { string ParamStr; for(int i = 0; i < MAXPARAMS; i++) ParamStr = ParamStr + " " + DoubleToStr(Params[i], 2); return(ParamStr); } bool ParamsAreBanned(double Params[]) { for(int i = 0; i < MAXPARAMS; i++) { if(BadParams[i] != Params[i]) return(false); } return(true); } //—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
Sample
Analysis
Market Information Used:
Series array that contains close prices for each bar
Series array that contains open time of each bar
Series array that contains open prices of each bar
Series array that contains the highest prices of each bar
Series array that contains the lowest prices of each bar
Indicator Curves created:
Indicators Used:
Custom Indicators Used:
Order Management characteristics:
Other Features: