OptimaticLib





//+------------------------------------------------------------------+
//|                                                    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: