Storing and searching data of previous 3 candles - page 2

 
Alain Verleyen:
Exactly. (was writing previous post while you posted this).

Okay, another go... avoiding any unnecessary re-calculation of MA values; any unnecessary use of iBarShift(); and watching for MT4 asynchronously back-filling on start-up with intermediate history data since it was last run.

This is more complicated that I'd probably actually use myself. I'd sacrifice some unnecessary re-calculation of static MA values (which MT4 probably caches itself) in favour of code simplicity and cleanliness. And even despite that, it feels more complex than it needs to be; I blame last night's alcohol.


#property strict

input int CrossoverPeriod = 3;
input int FastEMAPeriod = 14;   
input int SlowEMAPeriod = 50;   

// States of crosses: cross up compared to previous bar; cross down; no cross
enum CrossValues {
   CrossUp = 1,
   CrossDown = -1,
   CrossNone = 0
};

// Time of previous tick, used for detecting formation of new bars
datetime glbPreviousTick = 0;

// Number of bars reported by MT4, to watch for asynchronous history back-fill
int glbLastBarCount = 0;

// Caches of MA values, to avoid recalculating anything which can't change
double glbPreviousBarFastEma = -1, glbPreviousBarSlowEma = -1;
double glbCurrentFastEma = -1, glbCurrentSlowEma = -1;

// Cache of the bar-index and type of the most recent cross
int glbLastCrossBarIndex = 9999; // Dummy value indicating no cross; fails to meet condition glbLastCrossBarIndex < CrossoverPeriod
CrossValues glbLastCrossType = CrossNone;


void OnInit()
{
   glbPreviousTick = 0;
   glbLastCrossBarIndex = 9999;
   glbLastCrossType = CrossNone;
}


void OnTick()
{
   // We need to scan for crosses in historic bars if this is the first tick, or if MT4 has
   // asynchronously back-filled with historic data after loading the EA
   if (!glbPreviousTick || Bars > glbLastBarCount + 1) {
      // Look for old crosses between bar 1 and bar N-1
      
      // Start by getting the MAs at bar N (outside the period 0 to N-1, for comparison
      // against N-1)
      double vOldFastEma = GetMA(FastEMAPeriod, CrossoverPeriod);
      double vOldSlowEma = GetMA(SlowEMAPeriod, CrossoverPeriod);

      // Special case where CrossoverPeriod = 1 (i.e. cross in current bar only)
      if (CrossoverPeriod == 1) {
         // Don't need to look at historic bars. Do need to put the MA values for bar #1
         // into the caches glbPreviousBarFastEma and glbPreviousBarLowEma for later comparison
         // against the current bar
         glbPreviousBarFastEma = vOldFastEma;
         glbPreviousBarSlowEma = vOldSlowEma;
         
      } else {
         // Work downwards, moving forwards in time, looking for crosses on completed historic bars
         for (int i = CrossoverPeriod - 1; i >= 1; i--) {
            // Get "current" EMA values; see note at end of loop
            glbPreviousBarFastEma = GetMA(FastEMAPeriod, i);
            glbPreviousBarSlowEma = GetMA(SlowEMAPeriod, i);

            if (glbPreviousBarFastEma > glbPreviousBarSlowEma && vOldFastEma < vOldSlowEma) {
               // Cross up between i+1 and i
               glbLastCrossBarIndex = i;
               glbLastCrossType = CrossUp;
               
            } else if (glbPreviousBarFastEma < glbPreviousBarSlowEma && vOldFastEma > vOldSlowEma) {
               // Cross down between i+1 and i
               glbLastCrossBarIndex = i;
               glbLastCrossType = CrossDown;
            
            } else {
               // No cross between i+1 and i
            }
   
            // Put "current" into "old" for the next iteration of the loop      
            vOldFastEma = glbPreviousBarFastEma;
            vOldSlowEma = glbPreviousBarSlowEma;
         }

         // The loop above also has the end result that the MA values for the last complete bar, #1, 
         // are now in the caches glbPreviousBarFastEma and glbPreviousBarLowEma, for comparison against the
         // current bar, avoiding unnecessary recalculation of iMA() for closed bars
      }
      
   } else {
      // Not the first tick. Has a new bar formed?
      if (TimeCurrent() / PeriodSeconds(Period()) != glbPreviousTick / PeriodSeconds(Period())) {      
         // Transfer the final MA values for what's now the old bar into the caches for the previous bar
         glbPreviousBarFastEma = glbCurrentFastEma;
         glbPreviousBarSlowEma = glbCurrentSlowEma;
         
         // If we had identified a cross, increment its bar index.
         // (Can also increment the dummy initial value 9999 upwards)
         glbLastCrossBarIndex++;
      }
   }

   // Store the time of the current tick (for detecting formation of new bars, and identifying
   // first tick vs subsequent ticks), and the number of bars reported by MT4 (to watch
   // for back-filling of the history)
   glbPreviousTick = TimeCurrent();
   glbLastBarCount = Bars;
   
   // Get the current intra-bar MA values, putting them into global variables so that,
   // on the start of a new bar, they can be transferred into the last-bar cache
   glbCurrentFastEma = GetMA(FastEMAPeriod, 0);
   glbCurrentSlowEma = GetMA(SlowEMAPeriod, 0);
   
   // Look for a cross in the current bar compared to the last complete bar (#1),
   // using the cached values glbPreviousBarFastEma and glbPreviousBarSlowEma
   if (glbCurrentFastEma > glbCurrentSlowEma && glbPreviousBarFastEma < glbPreviousBarSlowEma) {
      // Cross up in current bar
      glbLastCrossBarIndex = 0;
      glbLastCrossType = CrossUp;
   
   } else if (glbCurrentFastEma < glbCurrentSlowEma && glbPreviousBarFastEma > glbPreviousBarSlowEma) {
      // Cross down in current bar
      glbLastCrossBarIndex = 0;
      glbLastCrossType = CrossDown;
   
   } else {
      // No cross in current bar. Keep any record of an older cross.
   }

   // -----------------------------------------------------
   // glbLastCrossBarIndex and glbLastCrossType hold the zero-based bar index and the type
   // of the most recent cross. There's therefore a potentially valid signal 
   // if glbLastCrossBarIndex < CrossoverPeriod
   
   if (glbLastCrossBarIndex < CrossoverPeriod) {
      // Got a cross within the last N bars, with direction identified by glbLastCrossType
   }
}

double GetMA(int ForBarPeriod, int AtBarIndex)
{
   return iMA(Symbol(), Period(), ForBarPeriod, 0, MODE_EMA, PRICE_CLOSE, AtBarIndex);
}
 
JC:

Okay, another go... avoiding any unnecessary re-calculation of MA values; any unnecessary use of iBarShift(); and watching for MT4 asynchronously back-filling on start-up with intermediate history data since it was last run.

This is more complicated that I'd probably actually use myself. I'd sacrifice some unnecessary re-calculation of static MA values (which MT4 probably caches itself) in favour of code simplicity and cleanliness. And even despite that, it feels more complex than it needs to be; I blame last night's alcohol.


Ok now add the stochastic crossover checking
 

Thanks guys appreciate the help, I have been trying to keep up and researching the functions your using, as I said still fairly new to this.

Sorry if my description was a bit vague, but yes my aim was to identify the two crosses happening within a certain number of bars to each other.

 
amac22:

Thanks guys appreciate the help, I have been trying to keep up and researching the functions your using, as I said still fairly new to this.

Sorry if my description was a bit vague, but yes my aim was to identify the two crosses happening within a certain number of bars to each other.

Going back to my very first response, the complexity of the code depends whether you want to maximise speed and avoid things like unnecessary recalculation of historic MA values which can't change. This will have minimal or zero impact in live use, but will affect the speed of backtesting, if that's what you want to do with the code.

The most recent stuff above could be very much simpler (and easier to read) if it weren't attempting to avoid any unnecessary operation.

But the various code above does illustrate various ways of determining when the most recent MA cross happened. You then add in something similar for the stochastic, and only take action if both events lie within a certain number of bars; are in the same direction etc.

 
JC:

Going back to my very first response, the complexity of the code depends whether you want to maximise speed and avoid things like unnecessary recalculation of historic MA values which can't change. [...]

[ ** completely untested; treat only as an example ... ** ]

For example, the following code handles both the MA cross and the stochastic cross, and is much cleaner in terms of tidy subdivision into functions. But it's much less optimised than the code above, and repeatedly recalculates values which can't have changed (though MT4 may itself cache these, with the result that the difference might be minimal).

#property strict

input int CrossoverPeriod = 3;
input int FastEMAPeriod = 14;   
input int SlowEMAPeriod = 50;   
input int StochasticKPeriod = 8;
input int StochasticDPeriod = 3;
input int StochasticSlowing = 3;

// States of crosses: cross up compared to previous bar; cross down; no cross
enum CrossValues {
   CrossUp = 1,
   CrossDown = -1,
   CrossNone = 0
};

// Defines the most recent cross event: its type, and the zero-based bar index
// where it happened. BarIndex is set to 9999 (and thus outside CrossoverPeriod)
// if no recent cross is identified.
struct CrossEvent {
   int BarIndex;
   CrossValues CrossType;
};



void OnTick()
{
   // Get information about the most recent MA and stochastic crosses
   CrossEvent MACross = GetMostRecentMACross();
   CrossEvent StochCross = GetMostRecentStochasticCross();
   
   // Check that both crosses lie within the required recent period
   if (MACross.BarIndex < CrossoverPeriod && StochCross.BarIndex < CrossoverPeriod) {
      // Could also check that the crosses are in the same direction; both up or
      // both down...
      if (MACross.CrossType == StochCross.CrossType) {
         // The crosses are in the same direction
      } else {
         // The crosses are *not* in the same direction
      }
   } else {
      // The MA cross and/or stochastic cross lie outside the last N
      // bars as defined by CrossoverPeriod
   }
}

// Gets information about the most recent MA cross, as a CrossEvent structure.
// Returns dummy values (.BarIndex=9999; CrossType=CrossNone) if there has
// been no cross within the last N bars as defined by CrossoverPeriod
CrossEvent GetMostRecentMACross()
{
   CrossEvent retval;
   
   for (int i = 0; i < CrossoverPeriod; i++) {
      CrossValues type = GetMACrossAsAtBar(i, FastEMAPeriod, SlowEMAPeriod);
      if (type != CrossNone) {
         retval.BarIndex = i;
         retval.CrossType = type;
         return retval;
      }
   }
   
   // No cross within required period
   retval.BarIndex = 9999; // Dummy value
   retval.CrossType = CrossNone;
   return retval;
}

// Gets information about the most recent stochastic cross, as a CrossEvent structure.
// Returns dummy values (.BarIndex=9999; CrossType=CrossNone) if there has
// been no cross within the last N bars as defined by CrossoverPeriod
CrossEvent GetMostRecentStochasticCross()
{
   CrossEvent retval;
   
   for (int i = 0; i < CrossoverPeriod; i++) {
      CrossValues type = GetStochasticCrossAsAtBar(i, StochasticKPeriod, StochasticDPeriod, StochasticSlowing);
      if (type != CrossNone) {
         retval.BarIndex = i;
         retval.CrossType = type;
         return retval;
      }
   }
   
   // No cross within required period
   retval.BarIndex = 9999; // Dummy value
   retval.CrossType = CrossNone;
   return retval;
}


// Looks for a MA cross at bar N, i.e. compares the MA values in bar N
// to the values in bar N+1
// Returns +1 for the "fast" EMA crossing above the "slow" EMA between
// bars N+1 and N; -1 for a cross below; or zero for no cross
CrossValues GetMACrossAsAtBar(int barindex, int FastPeriod, int SlowPeriod)
{
   double vFast_Later = iMA(Symbol(), Period(), FastPeriod, 0, MODE_EMA, PRICE_CLOSE, barindex);
   double vFast_Older = iMA(Symbol(), Period(), FastPeriod, 0, MODE_EMA, PRICE_CLOSE, barindex + 1);

   double vSlow_Later = iMA(Symbol(), Period(), SlowPeriod, 0, MODE_EMA, PRICE_CLOSE, barindex);
   double vSlow_Older = iMA(Symbol(), Period(), SlowPeriod, 0, MODE_EMA, PRICE_CLOSE, barindex + 1);

   if (vFast_Later > vSlow_Later && vFast_Older < vSlow_Older) {
      return CrossUp;
   } else if (vFast_Later < vSlow_Later && vFast_Older > vSlow_Older) {
      return CrossDown;
   } else {
      return CrossNone;
   }
}

// Looks for a stochastic cross at bar N, i.e. compares the main and signal values in bar N
// to the values in bar N+1
// Returns +1 for the main line crossing above the signal line; -1 for a cross below; 
// or zero for no cross
CrossValues GetStochasticCrossAsAtBar(int barindex, int KPeriod, int DPeriod, int Slowing)
{
   double vStochMain_Later = iStochastic(Symbol(), Period(), KPeriod, DPeriod, Slowing, MODE_EMA, 0, MODE_MAIN, barindex);
   double vStochMain_Older = iStochastic(Symbol(), Period(), KPeriod, DPeriod, Slowing, MODE_EMA, 0, MODE_MAIN, barindex + 1);

   double vStochSignal_Later = iStochastic(Symbol(), Period(), KPeriod, DPeriod, Slowing, MODE_EMA, 0, MODE_SIGNAL, barindex);
   double vStochSignal_Older = iStochastic(Symbol(), Period(), KPeriod, DPeriod, Slowing, MODE_EMA, 0, MODE_SIGNAL, barindex);

   if (vStochMain_Later > vStochSignal_Later && vStochMain_Older < vStochSignal_Older) {
      return CrossUp;
   } else if (vStochMain_Later < vStochSignal_Later && vStochMain_Older > vStochSignal_Older) {
      return CrossDown;
   } else {
      return CrossNone;
   }
}
 
JC:

Going back to my very first response, the complexity of the code depends whether you want to maximise speed and avoid things like unnecessary recalculation of historic MA values which can't change. This will have minimal or zero impact in live use, but will affect the speed of backtesting, if that's what you want to do with the code.

The most recent stuff above could be very much simpler (and easier to read) if it weren't attempting to avoid any unnecessary operation.

But the various code above does illustrate various ways of determining when the most recent MA cross happened. You then add in something similar for the stochastic, and only take action if both events lie within a certain number of bars; are in the same direction etc.


Yes it is my intention to maximise speed and avoid unnecessary re-calculation, which is why I first thought storing signals in an array was the most efficient. But I was wary of continually adding to an array with an infinite size thinking it would slow the program down eventually if I leave it running continually.

Thanks for the examples above you are giving me plenty of homework to go through which is great.

 
amac22:

Yes it is my intention to maximise speed and avoid unnecessary re-calculation [...]

Again going back to my initial response, it also depends whether (a) you want to look at interim indicator/cross values on each tick during the current bar (which can then change back/un-cross again later in the bar), or only (b) to evaluate signals when a bar closes.

The various example code above does (a). If you only want to do (b), and will therefore only be calculating MA and stochastic values once per bar, then the benefit of caching/optimisation such as the #11 example is going to be absolutely tiny compared to the simpler #15 example, even in backtesting.

 

I have found the above thread while looking for some help.  I would like to be able to compare the last two moving average cross prices (slow MA).  

How would I be able to change the methods shown in this thread to be able to do this?

The methods seem to only get the last MA cross.  How would you create an array to store the last two crosses that could then be read and used to get the proce data from to then compare?

Any help would be appreciated.