//+---------------------------------------------------------------------+
//|                                                CTradeStatistics.mqh |
//|                                                        jafferwilson |
//|                          https://www.mql5.com/en/users/jafferwilson |
//+---------------------------------------------------------------------+
#property copyright "jafferwilson"
#property link      "https://www.mql5.com/en/users/jafferwilson"

#define NUMBER_OF_TRY_GET_HISTORY 20

#include <Trade\DealInfo.mqh>
#include <Arrays\ArrayDouble.mqh>
enum ENUM_CALC_STATE
  {
   CALC_INIT,
   CALC_TICK,
   CALC_DEINIT
  };
enum deal_result
  {
   NO_VALUE=0,
   WIN=1,
   LOSS
  };
enum ENUM_STATISTICS_PLUS
  {
   STAT_BALANCE=10000,
   STAT_EQUITY,
   STAT_AHPR,
   STAT_GHPR,
   STAT_Z_SCORE,
   STAT_LR_CORRELATION,
   STAT_LR_STANDARD_ERROR,
   STAT_AVG_PROFIT_TRADE,
   STAT_AVG_LOSS_TRADE,
   STAT_BALANCE_DD_ABSOLUTE
  };
class CTradeStatistics
  {
private:
   CDealInfo         m_deal;
   CArrayDouble      m_profit_data;    
   CArrayDouble      m_balance_data;   
   CArrayDouble      m_sharp_balance;
   CArrayDouble      m_balance_line;   
   string            m_err_msg;

   //---
   double            m_initial_deposit;
   double            m_withdrawal;
   int               m_deals;
   int               m_trades;

   double            m_profit_factor;

   double            m_profit;
   double            m_gross_profit;
   double            m_gross_loss;

   double            m_expected_payoff;

   double            m_balance_min;
   double            m_balance_dd;
   double            m_balance_dd_percent;
   double            m_balance_dd_relative;
   double            m_balance_dd_relative_percent;
   double            m_balance_dd_absolute;
   //---
   double            m_equity_min;
   double            m_equity_dd;
   double            m_equity_dd_percent;
   double            m_equity_dd_relative;
   double            m_equity_dd_relative_percent;
   double            m_equity_dd_absolute;
   //---
   int               m_short_trades;
   int               m_profit_short_trades;

   int               m_long_trades;
   int               m_profit_long_trades;

   int               m_profit_trades;
   double            m_profit_trades_percent;

   int               m_loss_trades;
   double            m_loss_trades_percent;

   //---
   int               m_profit_trades_avg_con;
   int               m_loss_trades_avg_con;

   //---
   double            m_larg_profit_trade;
   double            m_larg_loss_trade;

   //---
   double            m_con_profit_max;
   int               m_con_profit_max_trades;
   double            m_max_con_wins;
   int               m_max_con_profit_trades;

   double            m_con_loss_max;
   int               m_con_loss_max_trades;
   double            m_max_con_losses;
   int               m_max_con_loss_trades;

   //---
   double            m_recovery_factor;
   double            m_shape_ratio;
   double            m_min_margin_level;

   //---
   double            m_z_score;
   double            m_z_score_percent;

   double            m_sharpe_ratio;

   //---
   double            m_ghpr;
   double            m_ghpr_percent;

   double            m_ahpr;
   double            m_ahpr_percent;

   double            m_lr_correlation;
   double            m_lr_standard_error;

   //---
   int               m_series_count;
   int               m_wins_series_count;
   int               m_loss_series_count;

protected:
   bool              CalculateRL(CArrayDouble &data,CArrayDouble &line,double &Standard_Error,double &Correlation);
   void              CalcEquityDrawdown(bool finally);
   double            CalcZScorePercent(double z_score);

public:
   double            InitialDeposit() {return(m_initial_deposit);}
   double            Withdrawal() {return(m_withdrawal);}
   double            Profit() {return(m_profit);};
   double            GrossProfit() {return(m_gross_profit);}
   double            GrossLoss() {return(m_gross_loss);}
   //---
   double            LargestProfitTrade() {return(m_larg_profit_trade);}
   double            LargestLossTrade() {return(m_larg_loss_trade);};
   double            ConProfitMax() {return(m_con_profit_max);}
   int               ConProfitMaxTrades() {return(m_con_profit_max_trades);}
   double            MaxConWins() {return(m_max_con_wins);};
   int               MaxConProfitTrades() {return(m_max_con_profit_trades);}
   double            ConLossMax() {return(m_con_loss_max);}
   int               ConLossMaxTrades() {return(m_con_loss_max_trades);}
   double            MaxConLosses() {return(m_max_con_losses);}
   int               MaxConLossTrades() {return(m_max_con_loss_trades);}
   //---
   double            BalanceMin() {return(m_balance_min);};
   double            BalanceDD() {return(m_balance_dd);};
   double            BalanceDDPercent() {return(m_balance_dd_percent);};
   double            BalanceDDRelative() {return(m_balance_dd_relative);};
   double            BalanceDDRelativePercent() {return(m_balance_dd_relative_percent);};
   //---
   double            EquityMin() {return(m_equity_min);};
   double            EquityDD() {return(m_equity_dd);};
   double            EquityDDPercent() {return(m_equity_dd_percent);};
   double            EquityDDRelative() {return(m_equity_dd_relative);};
   double            EquityDDRelativePercent() {return(m_equity_dd_relative_percent);};
   //---
   double            ExpectedPayoff() {return(m_expected_payoff);}
   double            ProfitFactor() {return(m_profit_factor);}
   double            RecoveryFactor() {return(m_recovery_factor);}
   double            SharpeRatio() {return(m_sharpe_ratio);}
   double            MinMarginLevel() {return(m_min_margin_level);}
   //---
   int               Deals() {return(m_deals);}
   int               Trades() {return(m_trades);}
   int               ProfitTrades() {return(m_profit_trades);};
   int               LossTrades() {return(m_loss_trades);};
   int               ShortTrades() {return(m_short_trades);};
   int               LongTrades() {return(m_long_trades);};
   //---
   int               ProfitShortTrades() {return(m_profit_short_trades);}
   int               ProfitLongTrades() {return(m_profit_long_trades);}
   //---
   int               ProfitTradesAvgCon() {return(m_profit_trades_avg_con);}
   int               LossTradesAvgCon() {return(m_loss_trades_avg_con);}

   double            AHPR() {return(m_ahpr);}
   double            AHPRPercent() {return(m_ahpr_percent);}

   double            GHPR() {return(m_ghpr);}
   double            GHPRPercent() {return(m_ghpr_percent);}

   double            ZScore() {return(m_z_score);}
   double            ZScorePercent() {return(m_z_score_percent);}

   double            LRCorrelation() {return(m_lr_correlation);}
   double            LRStandardError() {return(m_lr_standard_error);}

   bool              Calculate(datetime time_start,datetime time_end,double initial_deposit);
   void              PrintStatistics();
   string            GetLastErrorString() {return(m_err_msg);};
   bool              CalculateEquityDD(ENUM_CALC_STATE state);
   double            Percent(double value,double divider);
   double            Divide(double value,double divider);
  };
double CTradeStatistics::Percent(double value,double divider)
  {
   if(MathAbs(value)<=FLT_EPSILON)
      return(0);
   return(100*value/divider);
  }
double CTradeStatistics::Divide(double value,double divider)
  {
   if(MathAbs(value)<=FLT_EPSILON)
      return(0);
   return(value/divider);
  }
bool CTradeStatistics::Calculate(datetime time_start=0,datetime time_end=0,double initial_deposit=0.0)
  {
   m_err_msg="";
   if(time_end==0)
      time_end=TimeTradeServer();
   m_initial_deposit=initial_deposit;

   m_withdrawal=0;
   m_profit=0;
   m_gross_profit=0;
   m_gross_loss=0;

   m_short_trades=0;
   m_larg_profit_trade=0;
   m_long_trades=0;
   m_larg_loss_trade=0;
   m_profit_short_trades=0;
   m_profit_long_trades=0;
   m_larg_profit_trade=0;
   m_larg_loss_trade=0;

   m_con_profit_max=0;
   m_con_profit_max_trades=0;
   m_con_loss_max=0;
   m_con_loss_max_trades=0;
   m_max_con_losses=0;
   m_max_con_loss_trades=0;
   m_balance_min=0.0;
   m_balance_dd_absolute=0;
   m_balance_dd=0;
   m_balance_dd_percent=0;
   m_balance_dd_relative=0;
   m_balance_dd_relative_percent=0;

   m_expected_payoff=0;
   m_profit_factor=0;
   m_recovery_factor=0;
   m_sharpe_ratio=0;

   m_deals=0;
   m_trades=0;
   m_profit_trades=0;
   m_loss_trades=0;
   m_short_trades=0;
   m_long_trades=0;

   m_series_count=0;
   m_wins_series_count=0;
   m_loss_series_count=0;
   m_profit_trades_avg_con=0;
   m_loss_trades_avg_con=0;

   double min_peak = 0.0;
   double max_peak = 0.0;

   double balance=0,sharp_balance=0;
   double profit=0;

   double sequential=0.0;
   int profit_length=0, loss_length=0;

   deal_result result=NO_VALUE;

   m_balance_data.Clear();
   m_profit_data.Clear();
   m_sharp_balance.Clear();

   int try_=0;
   bool res=HistorySelect(time_start,time_end);
   if(!res && try_<NUMBER_OF_TRY_GET_HISTORY)
     {
      Sleep(100);
      res=HistorySelect(time_start,time_end);
      try_++;
     }
   if(!res)
     {
      m_err_msg="Unable to get the trade history";
      return(false);
     }

   int deals_total=HistoryDealsTotal();
   for(int i=0; i<deals_total; i++)
     {
      if(!m_deal.SelectByIndex(i))
        {
         m_err_msg="Error function SelectByIndex()";
         return(false);
        }

      if(m_deal.DealType()==DEAL_TYPE_BALANCE)
        {
         //--- current balance
         profit=NormalizeDouble(m_deal.Profit()+m_deal.Swap(),2);
         balance+=profit;
         m_balance_data.Add(balance);

         if(min_peak == 0.0)
            min_peak = balance;
         else
            min_peak+=profit;
         if(max_peak == 0.0)
            max_peak = balance;
         else
            max_peak+=profit;
         if(profit>=0.0)
           {
            if(m_balance_min==0.0)
               m_balance_min=balance;
            else
               m_balance_min+=profit;
            result = WIN;
            if(initial_deposit==0.0)
               if(m_initial_deposit==0.0)
                 {
                  m_initial_deposit=m_deal.Profit();
                  sharp_balance += profit;
                  m_sharp_balance.Add(sharp_balance);
                 }
               else
                  m_initial_deposit+=m_deal.Profit();
           }
         else
           {
            m_withdrawal-=m_deal.Profit();
            result=LOSS;
           }

        }

      if(m_deal.DealType()==DEAL_TYPE_BUY || m_deal.DealType()==DEAL_TYPE_SELL)
        {
         m_deals++;
         profit=NormalizeDouble(m_deal.Profit()+m_deal.Swap()+m_deal.Commission(),2);
         m_profit_data.Add(profit);
         if(profit>=0.0)
           {
            m_gross_profit+=profit;
           }
         else
           {
            m_gross_loss+=profit;
           }
         balance=balance+profit;
         m_balance_data.Add(balance);

         if(balance<m_balance_min)
            m_balance_min=balance;

         if(min_peak == 0.0)
            min_peak = balance;
         if(max_peak == 0.0)
            max_peak = balance;
         double drawdown=max_peak-balance;
         double drawdown_percent=drawdown/max_peak*100.0;

         //--- price dd maximum
         if(drawdown>m_balance_dd)
           {
            m_balance_dd=drawdown;
            m_balance_dd_percent=drawdown_percent;
           }

         if(drawdown_percent>m_balance_dd_relative_percent)
           {
            m_balance_dd_relative_percent=drawdown_percent;
            m_balance_dd_relative=drawdown;
           }

         if(max_peak<balance)
            max_peak=balance;
         if(m_deal.Entry()==DEAL_ENTRY_OUT || m_deal.Entry()==DEAL_ENTRY_INOUT)
           {
            sharp_balance += profit;
            m_sharp_balance.Add(sharp_balance);
            m_trades++;

            if(profit>=0.0)
               m_profit_trades++;

            if(profit<0)
               m_loss_trades++;
            if(profit>=0.0)
              {
               if(profit>m_larg_profit_trade)
                  m_larg_profit_trade=profit;

               if(result!=WIN)
                 {
                  result=WIN;
                  m_series_count++;
                  m_wins_series_count++;
                  profit_length=0;
                  sequential=0.0;
                 }

               sequential+=profit;
               profit_length++;
               //---
               if(m_max_con_profit_trades<profit_length)
                 {
                  m_max_con_profit_trades=profit_length;
                  m_max_con_wins=sequential;
                 }
               //---
               if(m_con_profit_max<sequential)
                 {
                  m_con_profit_max = sequential;
                  m_con_profit_max_trades = profit_length;
                 }

              }
            else
              {
               if(profit<m_larg_loss_trade)
                  m_larg_loss_trade=profit;


               if(result!=LOSS)
                 {
                  result=LOSS;
                  m_series_count++;
                  m_loss_series_count++;
                  loss_length=0;
                  sequential=0.0;
                 }

               sequential+=profit;
               loss_length++;
               if(m_max_con_loss_trades<loss_length)
                 {
                  m_max_con_loss_trades=loss_length;
                  m_max_con_losses=sequential;
                 }
               if(m_con_loss_max>sequential)
                 {
                  m_con_loss_max=sequential;
                  m_con_loss_max_trades=loss_length;
                 }
              }

            if(m_deal.DealType()==DEAL_TYPE_SELL)
              {
               if(profit>=0.0)
                  m_profit_long_trades++;
               m_long_trades++;
              }

            if(m_deal.DealType()==DEAL_TYPE_BUY)
              {
               if(profit>=0.0)
                  m_profit_short_trades++;
               m_short_trades++;
              }
           }
        }
     }

//+------------------------------------------------------------------+
//|   AHPR, GHPR, Sharpe_Ratio                                       |
//+------------------------------------------------------------------+
   m_ahpr=0;
   m_ahpr_percent=0;

   m_ghpr=0;
   m_ghpr_percent=0;

//---
   int limit=m_sharp_balance.Total();
   if(limit>1)
     {
      double hpr=0;
      double hpr2=0;

      double HPR[];
      ArrayResize(HPR,limit-1);
      for(int i=1; i<limit; i++)
        {
         if(m_sharp_balance.At(i)!=0.0)
           {
            HPR[i-1]=m_sharp_balance.At(i)/m_sharp_balance.At(i-1);
            m_ahpr+=HPR[i-1];
           }
        }
      m_ahpr=m_ahpr/(limit-1);
      m_ahpr_percent=(m_ahpr-1)*100;

      m_ghpr=MathPow(m_sharp_balance.At(limit-1)/m_initial_deposit,1.0/(limit-1));
      m_ghpr_percent=(m_ghpr-1)*100;

      //--- Sharpe_Ratio
      m_sharpe_ratio=0;
      if(limit>2)
        {
         double Std=0.0;
         double RiskFreeRate=0.0;
         for(int i=0; i<limit-2; i++)
           {
            Std+=(m_ahpr-HPR[i])*(m_ahpr-HPR[i]);
           }
         Std=MathPow(Std/(limit-2),0.5);

         m_sharpe_ratio=(m_ahpr -(1.0+RiskFreeRate))/Std;
        }
     }

//+------------------------------------------------------------------+
//|   Z-Score                                                        |
//+------------------------------------------------------------------+
   m_z_score=0.0;
   m_z_score_percent=0.0;
   long N=m_profit_data.Total();

   if(N>2)
     {
      long W = 0;
      long L = 0;
      long R = 0;
      deal_result outcome=NO_VALUE;

      for(int i=0; i<N; i++)
        {
         if(m_profit_data.At(i)>=0.0)
           {
            if(outcome!=WIN)
              {
               outcome=WIN;
               R++;
              }
            W++;
           }
         else
           {
            if(outcome!=LOSS)
              {
               outcome=LOSS;
               R++;
              }
            L++;
           }
        }
      double P=2.0*W*L;

      //--- check for division by zero
      if(MathAbs(MathSqrt((P*(P-N))/(N-1))) >= FLT_EPSILON)
        {
         m_z_score=(N*(R-0.5)-P)/MathSqrt((P*(P-N))/(N-1));
         m_z_score_percent=CalcZScorePercent(m_z_score)*100;
        }
     }

//+------------------------------------------------------------------+
//|   Other                                                          |
//+------------------------------------------------------------------+

//--- Total_Net_Profit
   m_profit=m_gross_profit+m_gross_loss;

//--- Profit Factor
   m_profit_factor=0.0;
   if(MathAbs(m_gross_loss)>FLT_EPSILON)
      m_profit_factor=MathAbs(m_gross_profit/m_gross_loss);

//---
   m_profit_trades_percent=0;
   m_loss_trades_percent=0;
   m_expected_payoff=0;
   if(m_trades>0)
     {
      m_expected_payoff=m_profit/m_trades;
      m_profit_trades_percent = (double)m_profit_trades/m_trades*100;
      m_loss_trades_percent   = (double)m_loss_trades/m_trades*100;
     }

   m_profit_trades_avg_con=0;
   if(m_wins_series_count>0)
      m_profit_trades_avg_con=(int)MathRound((double)m_profit_trades/m_wins_series_count);

   m_loss_trades_avg_con=0;
   if(m_loss_series_count>0)
      m_loss_trades_avg_con=(int)MathRound((double)m_loss_trades/m_loss_series_count);

   if(MathAbs(m_balance_dd)>FLT_EPSILON)
      m_recovery_factor=m_profit/m_balance_dd;

   CalculateRL(m_balance_data,m_balance_line,m_lr_standard_error,m_lr_correlation);

   return(true);
  }

//+------------------------------------------------------------------+
//|   Laplas array                                                   |
//+------------------------------------------------------------------+
const double Laplas[][2]=
  {
     {0.00,0.00000},{0.01,0.00798},{0.02,0.01596},{0.03,0.02393},{0.04,0.03191},{0.05,0.03988},
     {0.06,0.04784},{0.07,0.05581},{0.08,0.06376},{0.09,0.07171},{0.10,0.07966},{0.11,0.08759},
     {0.12,0.09552},{0.13,0.10348},{0.14,0.11134},{0.15,0.11924},{0.16,0.12712},{0.17,0.13499},
     {0.18,0.14285},{0.19,0.15069},{0.20,0.15852},{0.21,0.16633},{0.22,0.17413},{0.23,0.18191},
     {0.24,0.18967},{0.25,0.19741},{0.26,0.20514},{0.27,0.21284},{0.28,0.22052},{0.29,0.22818},
     {0.30,0.23582},{0.31,0.24344},{0.32,0.25103},{0.33,0.25860},{0.34,0.26614},{0.35,0.27366},
     {0.36,0.28115},{0.37,0.28862},{0.38,0.29605},{0.39,0.30346},{0.40,0.31084},{0.41,0.31819},
     {0.42,0.32552},{0.43,0.33280},{0.44,0.34006},{0.45,0.34729},{0.46,0.35448},{0.47,0.36164},
     {0.48,0.36877},{0.49,0.37587},{0.50,0.38292},{0.51,0.38995},{0.52,0.39694},{0.53,0.40389},
     {0.54,0.41080},{0.55,0.41768},{0.56,0.42452},{0.57,0.43132},{0.58,0.43809},{0.59,0.44481},
     {0.60,0.45149},{0.61,0.45814},{0.62,0.46474},{0.63,0.47131},{0.64,0.47783},{0.65,0.48431},
     {0.66,0.49075},{0.67,0.49714},{0.68,0.50350},{0.69,0.50981},{0.70,0.51607},{0.71,0.52230},
     {0.72,0.52848},{0.73,0.53461},{0.74,0.54070},{0.75,0.54675},{0.76,0.55275},{0.77,0.55870},
     {0.78,0.56461},{0.79,0.57047},{0.80,0.57629},{0.81,0.58206},{0.82,0.58778},{0.83,0.59346},
     {0.84,0.59909},{0.85,0.60468},{0.86,0.61021},{0.87,0.61570},{0.88,0.62114},{0.89,0.62653},
     {0.90,0.63188},{0.91,0.63718},{0.92,0.64243},{0.93,0.64763},{0.94,0.65278},{0.95,0.65789},
     {0.96,0.66294},{0.97,0.66795},{0.98,0.67291},{0.99,0.67783},{1.00,0.68269},{1.01,0.68750},
     {1.02,0.69227},{1.03,0.69699},{1.04,0.70166},{1.05,0.70628},{1.06,0.71086},{1.07,0.71538},
     {1.08,0.71986},{1.09,0.72429},{1.10,0.72867},{1.11,0.73300},{1.12,0.73729},{1.13,0.74152},
     {1.14,0.74571},{1.15,0.74986},{1.16,0.75395},{1.17,0.75800},{1.18,0.76200},{1.19,0.76595},
     {1.20,0.76986},{1.21,0.77372},{1.22,0.77754},{1.23,0.78130},{1.24,0.78502},{1.25,0.78870},
     {1.26,0.79233},{1.27,0.79592},{1.28,0.79945},{1.29,0.80295},{1.30,0.80640},{1.31,0.80980},
     {1.32,0.81316},{1.33,0.81648},{1.34,0.81975},{1.35,0.82298},{1.36,0.82617},{1.37,0.82931},
     {1.38,0.83241},{1.39,0.83547},{1.40,0.83849},{1.41,0.84146},{1.42,0.84439},{1.43,0.84728},
     {1.44,0.85013},{1.45,0.85294},{1.46,0.85571},{1.47,0.85844},{1.48,0.86113},{1.49,0.86378},
     {1.50,0.86639},{1.51,0.86696},{1.52,0.87149},{1.53,0.87398},{1.54,0.87644},{1.55,0.87886},
     {1.56,0.88124},{1.57,0.88358},{1.58,0.88589},{1.59,0.88817},{1.60,0.89040},{1.61,0.89260},
     {1.62,0.89477},{1.63,0.89690},{1.64,0.89899},{1.65,0.90106},{1.66,0.90309},{1.67,0.90508},
     {1.68,0.90704},{1.69,0.90897},{1.70,0.91087},{1.71,0.91273},{1.72,0.91457},{1.73,0.91637},
     {1.74,0.91814},{1.75,0.91988},{1.76,0.92159},{1.77,0.92327},{1.78,0.92492},{1.79,0.92655},
     {1.80,0.92814},{1.81,0.92970},{1.82,0.93124},{1.83,0.93275},{1.84,0.93423},{1.85,0.93569},
     {1.86,0.93711},{1.87,0.93852},{1.88,0.93989},{1.89,0.94124},{1.90,0.94257},{1.91,0.94387},
     {1.92,0.94514},{1.93,0.94639},{1.94,0.94762},{1.95,0.94882},{1.96,0.95000},{1.97,0.95116},
     {1.98,0.95230},{1.99,0.95341},{2.00,0.95450},{2.01,0.95557},{2.02,0.95662},{2.03,0.95764},
     {2.04,0.95865},{2.05,0.95964},{2.06,0.96060},{2.07,0.96155},{2.08,0.96247},{2.09,0.96338},
     {2.10,0.96427},{2.11,0.96514},{2.12,0.96599},{2.13,0.96683},{2.14,0.96765},{2.15,0.96844},
     {2.16,0.96923},{2.17,0.96999},{2.18,0.97074},{2.19,0.97148},{2.20,0.97219},{2.21,0.97289},
     {2.22,0.97358},{2.23,0.97425},{2.24,0.97491},{2.25,0.97555},{2.26,0.97618},{2.27,0.97679},
     {2.28,0.97739},{2.29,0.97798},{2.30,0.97855},{2.31,0.97911},{2.32,0.97966},{2.33,0.98019},
     {2.34,0.98072},{2.35,0.98123},{2.36,0.98172},{2.37,0.98221},{2.38,0.98269},{2.39,0.98315},
     {2.40,0.98360},{2.41,0.98405},{2.42,0.98448},{2.43,0.98490},{2.44,0.98531},{2.45,0.98571},
     {2.46,0.98611},{2.47,0.98649},{2.48,0.98686},{2.49,0.98723},{2.50,0.98758},{2.51,0.98793},
     {2.52,0.98826},{2.53,0.98859},{2.54,0.98891},{2.55,0.98923},{2.56,0.98953},{2.57,0.98983},
     {2.58,0.99012},{2.59,0.99040},{2.60,0.99068},{2.61,0.99095},{2.62,0.99121},{2.63,0.99146},
     {2.64,0.99171},{2.65,0.99195},{2.66,0.99219},{2.67,0.99241},{2.68,0.99263},{2.69,0.99285},
     {2.70,0.99307},{2.71,0.99327},{2.72,0.99347},{2.73,0.99367},{2.74,0.99386},{2.75,0.99404},
     {2.76,0.99422},{2.77,0.99439},{2.78,0.99456},{2.79,0.99473},{2.80,0.99489},{2.81,0.99505},
     {2.82,0.99520},{2.83,0.99535},{2.84,0.99549},{2.85,0.99563},{2.86,0.99576},{2.87,0.99590},
     {2.88,0.99602},{2.89,0.99615},{2.90,0.99627},{2.91,0.99639},{2.92,0.99650},{2.93,0.99661},
     {2.94,0.99672},{2.95,0.99682},{2.96,0.99692},{2.97,0.99702},{2.98,0.99712},{2.99,0.99721},
     {3.00,0.99730},{3.01,0.99739}
  };
//+------------------------------------------------------------------+
//|   CalcZScorePercent                                              |
//+------------------------------------------------------------------+
double CTradeStatistics::CalcZScorePercent(double z_score)
  {
   int total=ArrayRange(Laplas,0);
   double value=NormalizeDouble(MathAbs(z_score),2);

   if(value>Laplas[total-2][0])
      return(Laplas[total-1][1]);

   for(int i=0; i<total; i++)
      if(Laplas[i][0]==value)
         return(Laplas[i][1]);

   return(0);
  }
//+------------------------------------------------------------------+
//|   PrintStatistics                                                |
//+------------------------------------------------------------------+
void CTradeStatistics::PrintStatistics()
  {

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_INITIAL_DEPOSIT),TesterStatistics(STAT_INITIAL_DEPOSIT),InitialDeposit());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_WITHDRAWAL),TesterStatistics(STAT_WITHDRAWAL),Withdrawal());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_PROFIT),TesterStatistics(STAT_PROFIT),Profit());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_GROSS_PROFIT),TesterStatistics(STAT_GROSS_PROFIT),GrossProfit());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_GROSS_LOSS),TesterStatistics(STAT_GROSS_LOSS),GrossLoss());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_MAX_PROFITTRADE),TesterStatistics(STAT_MAX_PROFITTRADE),LargestProfitTrade());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_MAX_LOSSTRADE),TesterStatistics(STAT_MAX_LOSSTRADE),LargestLossTrade());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_CONPROFITMAX),TesterStatistics(STAT_CONPROFITMAX),ConProfitMax());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_CONPROFITMAX_TRADES),TesterStatistics(STAT_CONPROFITMAX_TRADES),ConProfitMaxTrades());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_MAX_CONWINS),TesterStatistics(STAT_MAX_CONWINS),MaxConWins());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_MAX_CONPROFIT_TRADES),TesterStatistics(STAT_MAX_CONPROFIT_TRADES),MaxConProfitTrades());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_CONLOSSMAX),TesterStatistics(STAT_CONLOSSMAX),ConLossMax());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_CONLOSSMAX_TRADES),TesterStatistics(STAT_CONLOSSMAX_TRADES),ConLossMaxTrades());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_MAX_CONLOSSES),TesterStatistics(STAT_MAX_CONLOSSES),MaxConLosses());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_MAX_CONLOSS_TRADES),TesterStatistics(STAT_MAX_CONLOSS_TRADES),MaxConLossTrades());

//--- Balance
   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_BALANCEMIN),TesterStatistics(STAT_BALANCEMIN),BalanceMin());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_BALANCE_DD),TesterStatistics(STAT_BALANCE_DD),BalanceDD());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_BALANCEDD_PERCENT),TesterStatistics(STAT_BALANCEDD_PERCENT),BalanceDDPercent());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_BALANCE_DD_RELATIVE),TesterStatistics(STAT_BALANCE_DD_RELATIVE),BalanceDDRelative());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_BALANCE_DDREL_PERCENT),TesterStatistics(STAT_BALANCE_DDREL_PERCENT),BalanceDDRelativePercent());

//--- Equity
   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_EQUITYMIN),TesterStatistics(STAT_EQUITYMIN),EquityMin());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_EQUITY_DD),TesterStatistics(STAT_EQUITY_DD),EquityDD());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_EQUITYDD_PERCENT),TesterStatistics(STAT_EQUITYDD_PERCENT),EquityDDPercent());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_EQUITY_DD_RELATIVE),TesterStatistics(STAT_EQUITY_DD_RELATIVE),EquityDDRelative());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_EQUITY_DDREL_PERCENT),TesterStatistics(STAT_EQUITY_DDREL_PERCENT),EquityDDRelativePercent());

   /*
      PrintFormat("%s: %.2f - %.2f",EnumToString(ACCOUNT_BALANCE),AccountInfoDouble(ACCOUNT_BALANCE),Balance);

      PrintFormat("%s: %.2f - %.2f",EnumToString(ACCOUNT_PROFIT),AccountInfoDouble(ACCOUNT_PROFIT),Profit);

      PrintFormat("%s: %.2f - %.2f",EnumToString(ACCOUNT_EQUITY),AccountInfoDouble(ACCOUNT_EQUITY),Equity);
   */

//---
   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_EXPECTED_PAYOFF),TesterStatistics(STAT_EXPECTED_PAYOFF),ExpectedPayoff());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_PROFIT_FACTOR),TesterStatistics(STAT_PROFIT_FACTOR),ProfitFactor());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_RECOVERY_FACTOR),TesterStatistics(STAT_RECOVERY_FACTOR),RecoveryFactor());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_SHARPE_RATIO),TesterStatistics(STAT_SHARPE_RATIO),SharpeRatio());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_MIN_MARGINLEVEL),TesterStatistics(STAT_MIN_MARGINLEVEL),MinMarginLevel());

// PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_CUSTOM_ONTESTER),TesterStatistics(STAT_CUSTOM_ONTESTER),CustomOnTester());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_DEALS),TesterStatistics(STAT_DEALS),Deals());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_TRADES),TesterStatistics(STAT_TRADES),Trades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_PROFIT_TRADES),TesterStatistics(STAT_PROFIT_TRADES),ProfitTrades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_LOSS_TRADES),TesterStatistics(STAT_LOSS_TRADES),LossTrades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_SHORT_TRADES),TesterStatistics(STAT_SHORT_TRADES),ShortTrades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_LONG_TRADES),TesterStatistics(STAT_LONG_TRADES),LongTrades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_PROFIT_SHORTTRADES),TesterStatistics(STAT_PROFIT_SHORTTRADES),ProfitShortTrades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_PROFIT_LONGTRADES),TesterStatistics(STAT_PROFIT_LONGTRADES),ProfitLongTrades());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_PROFITTRADES_AVGCON),TesterStatistics(STAT_PROFITTRADES_AVGCON),ProfitTradesAvgCon());

   PrintFormat("%s: %.0f - %.0f",EnumToString(STAT_LOSSTRADES_AVGCON),TesterStatistics(STAT_LOSSTRADES_AVGCON),LossTradesAvgCon());

//--- GHPR
   PrintFormat("%s: %.4f(%.2f) - %.4f(%.2f)",EnumToString(STAT_GHPR),0,0,GHPR(),GHPRPercent());

//--- AHPR
   PrintFormat("%s: %.4f(%.2f) - %.4f(%.2f)",EnumToString(STAT_AHPR),0,0,AHPR(),AHPRPercent());

//--- Z SCORE
   PrintFormat("%s: %.2f(%.2f) - %.2f(%.2f)",EnumToString(STAT_Z_SCORE),0,0,ZScore(),ZScorePercent());

//--- LR
   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_LR_CORRELATION),0,LRCorrelation());

   PrintFormat("%s: %.2f - %.2f",EnumToString(STAT_LR_STANDARD_ERROR),0,LRStandardError());

  }
bool CTradeStatistics::CalculateEquityDD(ENUM_CALC_STATE state)
  {
   switch(state)
     {
      case CALC_INIT:
         m_equity_min=AccountInfoDouble(ACCOUNT_EQUITY);
         m_equity_dd=0.0;
         m_equity_dd_percent=0.0;
         m_equity_dd_relative=0.0;
         m_equity_dd_relative_percent=0.0;
         m_min_margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
         break;

      case CALC_TICK:
         CalcEquityDrawdown(false);
         break;

      case CALC_DEINIT:
         CalcEquityDrawdown(true);
         break;
     }

   return(true);
  }
//+------------------------------------------------------------------+
//|   CalcEquityDrawdown                                             |
//+------------------------------------------------------------------+
void  CTradeStatistics::CalcEquityDrawdown(bool finally)
  {

   static double maxpeak = 0.0;
   static double minpeak = 0.0;

   double equity=AccountInfoDouble(ACCOUNT_EQUITY);
   if(equity<m_equity_min)
      m_equity_min=equity;

   if(AccountInfoDouble(ACCOUNT_MARGIN_LEVEL)<m_min_margin_level)
      m_min_margin_level=AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);

   if(maxpeak == 0.0)
      maxpeak = equity;
   if(minpeak == 0.0)
      minpeak = equity;

   if((maxpeak<equity) || (finally))
     {
      double drawdown=maxpeak-minpeak;
      double drawdown_percent=drawdown/maxpeak*100.0;

      if(m_equity_dd_relative_percent<drawdown_percent)
        {
         m_equity_dd_relative_percent=drawdown_percent;
         m_equity_dd_relative=drawdown;
        }

      if(m_equity_dd<drawdown)
        {
         m_equity_dd=drawdown;
         m_equity_dd_percent=drawdown_percent;
        }

      maxpeak = equity;
      minpeak = equity;
     }

   if(minpeak>equity)
      minpeak=equity;
  }
bool CTradeStatistics::CalculateRL(CArrayDouble &data,CArrayDouble &line,double &Standard_Error,double &Correlation)
  {
   int total=data.Total();
   if(total<=2)
      return(true);

   double s1=0;
   double s2=0;
   double a1=0;
   double a2=0;
   double b1=0;
   double b2= total;

   for(int i=1; i<=total; i++)
     {
      s1 = s1 + data.At(i-1)*i;
      s2 = s2 + data.At(i-1);
      a1 = a1+i*i;
      a2 = a2+i;
     }

   b1=a2;

   double dif = a1*b2-a2*b1;
   double A   = (s1*b2-s2*b1) / dif;
   double B   = (a1*s2-a2*s1) / dif;

   int sign=1;
   if(A<0.0)
      sign=-1;

   line.Clear();
   string str="";
   for(int i = 1; i<=total; i++)
     {
      str=str+StringFormat("| %i %.2f",i,A*i+B);
      line.Add(A*i+B);
     }

   Standard_Error=0;
   double sum=0;
   for(int i=0; i<total; i++)
     {

      double delta=MathAbs(data.At(i)-line.At(i));
      sum=sum+delta*delta;
      //PrintFormat("B=%.2f L=%.2f D=%.2f",m_balance_data.At(i),BalanceLine.At(i),delta);
     }
   Standard_Error=MathSqrt(sum/(total-2));


//--- LR Correlation
   double avg_balance=0;
   double avg_line=0;
   for(int i=0; i<total; i++)
     {
      avg_balance=avg_balance+data.At(i);
      avg_line=avg_line+line.At(i);
     }

   avg_balance = avg_balance / total;
   avg_line    = avg_line / total;


   double cov=0;
   double Sx=0;
   double Sy=0;

   for(int i=0; i<total; i++)
     {
      cov = cov +(data.At(i)-avg_balance) *(line.At(i)-avg_line);
      Sx  = Sx + MathPow(data.At(i) - avg_balance, 2);
      Sy  = Sy + MathPow(line.At(i) - avg_line, 2);
     }
   cov = (cov / total);
   Sx  = MathSqrt(Sx / total);
   Sy  = MathSqrt(Sy / total);

   Correlation=sign*cov/(Sx*Sy);

   return(false);
  }
