Visual Backtest Reports Different Signals Once Stopped

 

Hello,

 

I am having a strange problem. I watch the visual backtest and look at the signals, then when I stop it new signals appear that weren't there during the visual backtest?  The ones that show once I stop are the right signals I want to use!

 

I have attached a picture showing the new signals that appear after I stop the backtest (in pink):

Visual Backtest Confusion 

 

This is a simple fractal moving average EA that reads from a single indicator.

 

Here is the indicator source:

//
//+------------------------------------------------------------------+
// FractalAMA
//
// Description:  Fractal Adaptive Moving Average - by John Ehlers
// Version 1.1 7/17/2006
//
// Heavily modified and reprogrammed by Matt Kennel (mbkennelfx@gmail.com)
//
// Notes:
// October 2005 Issue - "FRAMA - Fractal Adaptive Moving Average"
//  Length will be forced to be an even number. Odd numbers will be bumped up to the
//  next even number.
// Formula Parameters:     Defaults:
// RPeriod                              16

#property  copyright "Copyright © 2005, MrPip "  // and mbkennel
#property  link      "http://www.metaquotes.net/"

//---- indicator settings
#property  indicator_chart_window
#property  indicator_buffers 8
#property indicator_color1 Red
#property indicator_color2 Blue      
#property indicator_color3 White
#property indicator_color4 White
#property indicator_color5 White
#property indicator_color6 White
#property indicator_color7 White
#property indicator_color8 White

//---- buffers
double ExtMapBuffer1[];
double ExtMapBuffer2[];
double ExtMapBuffer3[];
double ExtMapBuffer4[];
double ExtMapBuffer5[];
double ExtMapBuffer6[];
double ExtMapBuffer7[];
double ExtMapBuffer8[];

extern int RPeriod = 16; //Averaging period
extern double multiplier = 4.6;
extern double signal_multiplier = 2.5;
extern int dRPeriod = 10; //Differential averaging period
extern int check_slope_entry = true;

//Arrow properties
extern bool Show_Arrow = true;
string ArrowsIdentifier = "EFTarrow";
color  ArrowUpColor     = Aqua;
color  ArrowDownColor   = Red;
int    ArrowUpWidth     = 1;
int    ArrowDownWidth   = 1;  

int limit_RP = RPeriod+1;
int limit_RPd = limit_RP+1;
int limit_dRP = dRPeriod + limit_RPd;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
void OnInit()
  {

  
//---- 1 additional buffers are used for counting.
   IndicatorBuffers(8);
  
//---- drawing settings
   SetIndexBuffer(0,ExtMapBuffer1);
   SetIndexStyle(0,DRAW_LINE,STYLE_SOLID,2);
   SetIndexShift(0,0);
   SetIndexLabel(0, "FRAMA");
   IndicatorDigits(MarketInfo(Symbol(),MODE_DIGITS));
  
   SetIndexBuffer(1,ExtMapBuffer2);
   SetIndexStyle(1,DRAW_LINE,STYLE_SOLID,2);
   SetIndexShift(1,0);
   SetIndexLabel(1, "FRAMA Signal");
  
   SetIndexBuffer(2,ExtMapBuffer3);
   SetIndexStyle(2,DRAW_LINE,STYLE_SOLID,1);
   SetIndexShift(2,0);
   SetIndexLabel(2, "dFRAMA");
  
   SetIndexBuffer(3,ExtMapBuffer4);
   SetIndexStyle(3,DRAW_LINE,STYLE_SOLID,1);
   SetIndexShift(3,0);
   SetIndexLabel(3, "dFRAMA+");

   SetIndexBuffer(4,ExtMapBuffer5);
   SetIndexStyle(4,DRAW_LINE,STYLE_SOLID,1);
   SetIndexShift(4,0);
   SetIndexLabel(4, "dFRAMA-");

   SetIndexBuffer(5,ExtMapBuffer6);
   SetIndexStyle(5,DRAW_LINE,STYLE_SOLID,1);
   SetIndexShift(5,0);
   SetIndexLabel(5, "dFRAMA+Avg");

   SetIndexBuffer(6,ExtMapBuffer7);
   SetIndexStyle(6,DRAW_LINE,STYLE_SOLID,1);
   SetIndexShift(6,0);
   SetIndexLabel(6, "dFRAMA-Avg");
  
   SetIndexBuffer(7,ExtMapBuffer8);
   SetIndexStyle(7,DRAW_LINE,STYLE_SOLID,1);
   SetIndexShift(7,0);
   SetIndexLabel(7, "Cross");

//---- initialization done
}

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime& time[],
                const double& open[],
                const double& high[],
                const double& low[],
                const double& close[],
                const long& tick_volume[],
                const long& volume[],
                const int& spread[])
{


   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(high, true);
   ArraySetAsSeries(low, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(tick_volume, true);
   ArraySetAsSeries(volume, true);
   ArraySetAsSeries(spread, true);
   ArraySetAsSeries(ExtMapBuffer1, true);
   ArraySetAsSeries(ExtMapBuffer2, true);
   ArraySetAsSeries(ExtMapBuffer3, true);
   ArraySetAsSeries(ExtMapBuffer4, true);
   ArraySetAsSeries(ExtMapBuffer5, true);
   ArraySetAsSeries(ExtMapBuffer6, true);
   ArraySetAsSeries(ExtMapBuffer7, true);
   ArraySetAsSeries(ExtMapBuffer8, true);
  
     RefreshRates();
     int counted=prev_calculated;

     if (Bars < RPeriod) return(0);
    
     //int maxshift = Bars-RPeriod-1;
     //int limit= maxshift - counted_bars;
     int limit = Bars - 1 - MathMax(RPeriod, counted);
     int limit2 = Bars - 1 - MathMax(limit_RP, counted);
     int limit3 = Bars - 1- MathMax(limit_dRP, counted);
     if (limit < 1) limit = 1;
    
     int N = MathFloor(RPeriod/2)*2;  // Force N to even number
     double frama = Close[limit];
     double signal = frama;

     ExtMapBuffer1[limit] = Close[limit];
     ExtMapBuffer2[limit] = Close[limit];

     for(int shift = limit; shift >= 0; shift--) {
        double dimension_estimate = DEst(shift,N);    
      
        double alpha = MathExp(-multiplier*(dimension_estimate-1.0));
        double alphas = MathExp(-signal_multiplier*(dimension_estimate-1.0));
        
        if (alpha > 1.0) alpha = 1.0;
        if (alpha < 0.01) alpha = 0.01;
        if (alphas > 1.0) alphas = 1.0;
        if (alphas < 0.01) alphas = 0.01;
          
        frama = alpha* Close[shift] + (1.0-alpha)* ExtMapBuffer1[shift+1];
        signal = alphas * frama + (1.0 - alphas)* ExtMapBuffer2[shift+1];  

        ExtMapBuffer1[shift] = frama;
        ExtMapBuffer2[shift] = signal;
        
      
      }
      
      for(int shift2 = limit2; shift2>=0; shift2--) {
        ExtMapBuffer3[shift2] = ExtMapBuffer1[shift2] - ExtMapBuffer1[shift2+1];
        if(ExtMapBuffer3[shift2]> 0) {
            ExtMapBuffer4[shift2] = ExtMapBuffer3[shift2];
            ExtMapBuffer5[shift2] = 0;
        } else if(ExtMapBuffer3[shift2]< 0) {
            ExtMapBuffer5[shift2] = ExtMapBuffer3[shift2];
            ExtMapBuffer4[shift2] = 0;
        }
        else {
         ExtMapBuffer4[shift2] = 0;
         ExtMapBuffer5[shift2] = 0;
        }
        
        //Cross
        if(ExtMapBuffer1[shift2] > ExtMapBuffer2[shift2] && ExtMapBuffer1[shift2+1] <= ExtMapBuffer2[shift2+1]) {
         ExtMapBuffer8[shift2] = -1;
        }
        else if(ExtMapBuffer1[shift2] < ExtMapBuffer2[shift2] && ExtMapBuffer1[shift2+1] >= ExtMapBuffer2[shift2+1]) {
         ExtMapBuffer8[shift2] = 1;
        }
        else {
         ExtMapBuffer8[shift2] = 0;
        }
      }
      
      for(int shift3 = limit3; shift3>=0; shift3--) {
         ExtMapBuffer6[shift3]=iMAOnArray(ExtMapBuffer4, 0, dRPeriod, 0, MODE_EMA, shift3);
         ExtMapBuffer7[shift3]=iMAOnArray(ExtMapBuffer5, 0, dRPeriod, 0, MODE_EMA, shift3);
      
      double check_slope = 1;
      if(check_slope_entry) {
         if(ExtMapBuffer6[shift3]>=ExtMapBuffer4[shift3] || ExtMapBuffer7[shift3]<=ExtMapBuffer5[shift3]) {
            check_slope = 1;
         } else {
            check_slope = 0;
         }
      }  
      //Arrows
      if (Show_Arrow)
      {
         double dist = iATR(NULL,0,20,shift3)/2.0;
         ObjectDelete(ArrowsIdentifier+DoubleToString(Time[0]));        
         string name = ArrowsIdentifier+DoubleToString(Time[shift3]);
         if (ExtMapBuffer8[shift3] == -1 && check_slope > 0)
         {
            ObjectCreate(name,OBJ_ARROW,0, Time[shift3],Low[shift3]-dist );
            ObjectSet(name,OBJPROP_ARROWCODE,225);
            ObjectSet(name,OBJPROP_COLOR,ArrowUpColor);
            ObjectSet(name,OBJPROP_WIDTH,ArrowUpWidth);
          }
         else if(ExtMapBuffer8[shift3] == 1 && check_slope > 0)
         {
            ObjectCreate(name,OBJ_ARROW,0, Time[shift3],High[shift3]+dist );
            ObjectSet(name,OBJPROP_ARROWCODE,226);
            ObjectSet(name,OBJPROP_COLOR,ArrowDownColor);
            ObjectSet(name,OBJPROP_WIDTH,ArrowDownWidth);
         }
         else
         {
         }
       }
      }
      
   return(rates_total);
}


double DEst(int shift, int n) {
   //  
   double R1, R2, R3;
   int n2 = n/2;
  
   R3 = Range(shift,n) / n;
   R1 = Range(shift,n2) / n2;
   R2 = Range(shift+n2,n2) / n2;
  
   return(  (MathLog(R1+R2)-MathLog(R3) )* 1.442695 ) ; // log_2(e) = 1.442694

  


}

double Range(int i, int k) {
  
      return( High[Highest(NULL,0,MODE_HIGH,k,i)] - Low[Lowest(NULL,0,MODE_LOW,k,i)] );
}

 

And here is the EA source:

 

//This version uses stochastic slopes as entry points
//+------------------------------------------------------------------+
//|                                                  MACD Sample.mq4 |
//|                   Copyright 2005-2014, MetaQuotes Software Corp. |
//|                                              http://www.mql4.com |
//+------------------------------------------------------------------+
#property copyright   "BPLTURNER"
#property link        "NO."

extern int RPeriod = 16; //Averaging period
extern double multiplier = 4.6;
extern double signal_multiplier = 2.5;
extern int dRPeriod = 10; //Differential averaging period
extern int check_slope_entry = true;

//Arrow properties
extern bool Show_Arrow = true;
string ArrowsIdentifier = "EFTarrow";
color  ArrowUpColor     = Aqua;
color  ArrowDownColor   = Red;
int    ArrowUpWidth     = 1;
int    ArrowDownWidth   = 1;  

//Risk management parameters
input string Risk_Parameters = "--- Risk Management ---";
input bool OpenNewBar = false;
input double StopLossM      = 3;
input double TakeProfitM    =0;
input double TrailingStopM  =0.25;
input double Risk          =0.1;
input double MaximumSlippage = 3;
input double MaximumOrders = 1;

double StopLoss = 0;
double TakeProfit = 0;
double TrailingStop = 0;

double X = 0;
double ATR = 0;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
static datetime _lastBarTime = 0;           // DateTime of the last new bar which opened
  
double OneLotMargin = MarketInfo(Symbol(),MODE_MARGINREQUIRED);
double FreeMargin = AccountFreeMargin();
double lotMM = FreeMargin/OneLotMargin*Risk;
double LotStep = MarketInfo(Symbol(),MODE_LOTSTEP);
double Lots = NormalizeDouble(lotMM/LotStep,0)*LotStep;

void OnTick(void)
{
   if(Bars<100)
     {
      Print("bars less than 100");
      return;
     }
    

   int    cnt,ticket,total;
//---
// initial data checks
// it is important to make sure that the expert works with a normal
// chart and the user did not make any mistakes setting external
// variables (Lots, StopLoss, TakeProfit,
// TrailingStop) in our case, we check TakeProfit
// on a chart of less than 100 bars
//---

//   if(TakeProfit<10)
//     {
//      Print("TakeProfit less than 10");
//      return;
//     }
//--- to simplify the coding and speed up access data are put into internal variables

   //Calculate our stop loss, trailing and profit using the ATR and multipliers
   ATR = iCustom(Symbol(), 0, "ATR", RPeriod, 0, 0);
   ATR = MathPow(ATR, 1.1);
   StopLoss=StopLossM*ATR/Point;
   TakeProfit=TakeProfitM*ATR/Point;
   TrailingStop=TrailingStopM*ATR/Point;
  
   RefreshRates();
   X = iCustom(Symbol(),0, "FRAMA", RPeriod,multiplier,signal_multiplier,dRPeriod,check_slope_entry,Show_Arrow,ArrowsIdentifier,ArrowUpColor,ArrowDownColor,ArrowUpWidth,ArrowDownWidth, 7, 0);
  
   //exit1=iCustom(Symbol(),0,"StochasticTrigger",K1,D1,S1,U1,L1,K2,D2,S2,U2,L2,6,0);
   //exit2=iCustom(Symbol(),0,"StochasticTrigger",K1,D1,S1,U1,L1,K2,D2,S2,U2,L2,7,0);

   //Price calculation parameters
   PrintFormat("X: %f", X);
   //Calculate buy/sell based on our signals
   int bs = 0;
   if(X<0)
   {
      bs=-1;
   }
  
   else if (X>0)
   {
      bs=1;
   }
   else
   {
      bs=0;
   }
  
   //Calculate our exit signals
   int exitSell = 0;
   int exitBuy = 0;
   if(X<0) //Either we've cross under a long term stochastic, or we went back into overbought territory
   {
      exitSell = 1;
   }
  
   else if (X>0) //Either we've cross over a long term stochastic, or we went back into oversold territory
   {
      exitBuy = 1;
   }
   else
   {
      exitSell = 0;
      exitBuy = 0;
   }

   //Debug information
   //PrintFormat("StochX1: %f, StochX2: %f, BS: %f", stochX1, stochX2, bs);

   //See if we're opening on new bars only
   if(OpenNewBar && !NewBar()) {
      bs = 0;
   }
  
   total=OrdersTotal();
   //if(total<1 && total<MaximumOrders)
   if(total<MaximumOrders)
     {
      //--- no opened orders identified
      if(AccountFreeMargin()<(1000*Lots))
        {
         Print("We have no money. Free Margin = ",AccountFreeMargin());
         return;
        }
      //--- check for long position (BUY) possibility
      double stoplevel=0;
      double profitlevel=0;
      if(bs<0)
        {
         //If using stop loss
         if(StopLoss>0)
         {
            stoplevel = Ask-StopLoss*Point;
         }
         else
         {
            stoplevel=0;
         }
         //If using take profit
         if(TakeProfit>0)
         {
            profitlevel = Ask+TakeProfit*Point;
         }
         else
         {
            profitlevel=0;
         }

         ticket=OrderSend(Symbol(),OP_BUY,Lots,Ask,MaximumSlippage,stoplevel,profitlevel,"Money Volume",16384,0,Green);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
               Print("BUY Order Opened : ",OrderOpenPrice());
           }
         else
            Print("Error Opening BUY Order : ",GetLastError());
         return;
        }
      //--- check for short position (SELL) possibility
      if(bs>0)
        {
         //If using stop loss
         if(StopLoss>0)
         {
            stoplevel = Bid+StopLoss*Point;
         }
         else
         {
            stoplevel=0;
         }
         //If using take profit
         if(TakeProfit>0)
         {
            profitlevel = Bid-TakeProfit*Point;
         }
         else
         {
            profitlevel=0;
         }

         ticket=OrderSend(Symbol(),OP_SELL,Lots,Bid,MaximumSlippage,stoplevel,profitlevel,"Money Volume",16384,0,Red);
         if(ticket>0)
           {
            if(OrderSelect(ticket,SELECT_BY_TICKET,MODE_TRADES))
               Print("SELL Order Opened : ",OrderOpenPrice());
           }
         else
            Print("Error Opening SELL Order : ",GetLastError());
        }
      //--- exit from the "no opened orders" block
      return;
     }
//--- it is important to enter the market correctly, but it is more important to exit it correctly...  
   for(cnt=0;cnt<total;cnt++)
     {
      if(!OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES))
         continue;
      if(OrderType()<=OP_SELL &&   // check for opened position
         OrderSymbol()==Symbol())  // check for symbol
        {
         //--- long position is opened
         if(OrderType()==OP_BUY)
           {
            //--- should it be closed?
            if(exitBuy > 0)
              {
               //--- close order and exit
               if(!OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet))
                  Print("OrderClose error ",GetLastError());
               return;
              }
            //--- check for trailing stop
            if(TrailingStop>0)
              {
               if(Bid-OrderOpenPrice()>Point*TrailingStop)
                 {
                  if(OrderStopLoss()<Bid-Point*TrailingStop)
                    {
                     //--- modify order and exit
                     if(!OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green))
                        Print("OrderModify error ",GetLastError());
                     return;
                    }
                 }
              }
           }
         else // go to short position
           {
            //--- should it be closed?
            if(exitSell > 0)
              {
               //--- close order and exit
               if(!OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet))
                  Print("OrderClose error ",GetLastError());
               return;
              }
            //--- check for trailing stop
            if(TrailingStop>0)
              {
               if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
                 {
                  if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                    {
                     //--- modify order and exit
                     if(!OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red))
                        Print("OrderModify error ",GetLastError());
                     return;
                    }
                 }
              }
           }
        }
     }
//---
  }
//+------------------------------------------------------------------+
void OnInit()
{
   _lastBarTime = iTime(Symbol(),0,0);
}


bool NewBar() {
if (iTime(Symbol(),Period(),0) != _lastBarTime) {
  _lastBarTime = iTime(Symbol(), Period(),0);  
  
  return (true);
} else
  return (false);
}

 

Can anyone tell me why these signals appear after I stop the backtest?  They're the right signals that I want to use! 

 
bplturner Can anyone tell me why these signals appear after I stop the backtest?  They're the right signals that I want to use! 
Because your indicator is broken, and those signals only appear after the test on a chart refresh.
  1. RefreshRates();
    Unnecessary in a indicator as indicators can not sleep and predefined variables can not be out of date.
  2. int counted=prev_calculated;
    int limit = Bars - 1 - MathMax(RPeriod, counted);
    :
    return(rates_total);
    After the first run, counted = prev_calculated = Bars so limit is negative and you calculate nothing. See How to do your lookbacks correctly.
  3. if (limit < 1) limit = 1;
    Kluge because of #2. Remove!

  4. ExtMapBuffer1[limit] = Close[limit];
    ExtMapBuffer2[limit] = Close[limit];
    You only want to initialize buffers once, when prev_calculated is zero.

  5.      int limit = Bars - 1 - MathMax(RPeriod, counted);
         int limit2 = Bars - 1 - MathMax(limit_RP, counted);
         int limit3 = Bars - 1- MathMax(limit_dRP, counted);
    :
    double dimension_estimate = DEst(shift,N);    
    :
    frama = alpha* Close[shift] + (1.0-alpha)* ExtMapBuffer1[shift+1];
    :
    ExtMapBuffer3[shift2] = ExtMapBuffer1[shift2] - ExtMapBuffer1[shift2+1];
    :
    ExtMapBuffer6[shift3]=iMAOnArray(ExtMapBuffer4, 0, dRPeriod, 0, MODE_EMA, shift3);
    double dist = iATR(NULL,0,20,shift3)/2.0;

    The look back for limit is maximum of one and lookback of DEst (whatever that is.) Not RPeriod.
  6. The look back for limit2 is lookback of limit plus one. Not limit_RP.
  7. The look back for limit3 is Maximum of 20, and lookback of limit2 plus dRPeriod-1.
  8. See How to do your lookbacks correctly.
  9. ObjectDelete(ArrowsIdentifier+DoubleToString(Time[0]));        
    string name = ArrowsIdentifier+DoubleToString(Time[shift3]);
    Why are you deleting arrow zero and creating arrow shift3?
  10. Why are you creating objects instead of using a arrow buffer?
  11. double FreeMargin = AccountFreeMargin();
    double lotMM = FreeMargin/OneLotMargin*Risk;
    These are not changing. Assign them in OnTick
  12. Margin has nothing to do with risk.
    • You place the stop where it needs to be - where the reason for the trade is no longer valid. E.g. trading a support bounce the stop goes below the support.
    • Account Balance * percent/100 = RISK = OrderLots * (|OrderOpenPrice - OrderStopLoss| * DeltaPerLot + CommissionPerLot) (Note OOP-OSL includes the SPREAD, and DeltaPerLot is usually around $10/pip but it takes account of the exchange rates of the pair vs. your account currency.)
    • Do NOT use TickValue by itself - DeltaPerLot
    • You must normalize lots properly and check against min and max.
    • You must also check FreeMargin to avoid stop out

  13. ATR = iCustom(Symbol(), 0, "ATR", RPeriod, 0, 0);
    X = iCustom(Symbol(),0, "FRAMA", RPeriod, multiplier, signal_multiplier, dRPeriod, check_slope_entry, Show_Arrow, ArrowsIdentifier, ArrowUpColor, ArrowDownColor, ArrowUpWidth, ArrowDownWidth, 7, 0);
    Are you sure you want to look at bar zero and not all completed bars? Is there a difference between that indicator and iATR - Technical Indicators - MQL4 Reference?
  14. You should write a self documenting function instead of calling iCustom directly, see Detailed explanation of iCustom - MQL4 forum
 

Thank you whroeder1.  As always, your comments are extremely helpful.  I took the FRAMA function from an old MT4 script available on the internet and tried to make it valid with the "new way".  I'll look into your comments and update them as you've suggested.

 

Thanks again. 

Reason: