OptimaticLib

Author: Copyright � 2009, Marketeer, Sergey Kravchuk
3 Views
0 Downloads
0 Favorites
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);
}

//—————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————


Comments