//+------------------------------------------------------------------+
//| 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);
}
//
Comments