I wrote a custom indicator Speed.mq4
as follows:
double SpeedBuffer[]; // a Custom Indicator BUFFER
int OnInit() {
SetIndexBuffer( 0, SpeedBuffer ); // an access INDEX 0->BUFFER
...
}
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[]
) {
int start;
if ( prev_calculated == 0 ) start = 1;
else start = prev_calculated - 1;
for ( int i = start; i < rates_total - 1 ; i++ ) { // CPU-WASTED BY AN INEFFICIENT BACK-STEPPING IN TIME
double curTypical = typical( high[i], low[i], close[i] );
double prevTypical = typical( high[i+1],low[i+1],close[i+1] ); // CPU-WASTED, NO NEED TO RECALC ...
double curSpeed = curTypical - prevTypical;
SpeedBuffer[i] = curSpeed;
}
//--- return value of prev_calculated for next call
return( rates_total );
}
The indicator works fine in the application and the chart is plotted correctly.
When I try retrieving the last value in the ExpertAdvisor I always receive the same value:
double speed = iCustom( NULL, 0, "Speed", 2, 0, 0 );
Print( "speed is: " + speed );
prints:
speed is: 2147483647
It's always the same number. I'm not sure where's the problem.
from the Print
in the indicator I can see the values are calculated correctly. but when I use iCustom I receive only that value.
iCustom()
mechanicsMQL4 and even the New-MQL4 ( sometime called an MQL4.5 ) use a rather complicated interfacing model to handle Expert Advisor call / Custom Indicator computations.
A first thing to realise is, that iCustom()
is not a call to a function, but rather a method, that indirectly "asks" a by-a-filename referred Custom Indicator to retrieve one, specific, value from a "pre-calculated" DataSTORE.
While it may sound complicated, it is the very nature of a CPU-efficient calculation factory, that the Custom Indicators were designed for in the early days of MQL4 world.
iCustom()
is thus just a syntax-sugar to initiate the retrieval method, that gets the relevant piece of pre-calculated value back into the hands of Expert Advisor.
Custom Indicators put all the pre-calculated values into BUFFER(s), co-aligned with the TimeSeries style of ordering the DataSTORE cells ( [0] == "Now, the current Bar", going forwards [1], [2], [3], ... deeper and deeper back into the history )
The iCustom()
passes the shift
value, as a number of bars -- i.e. how deep into the history the retrieving method has to go, so as to pick the requested value from the respective BUFFER, and also the BUFFER identification INDEX ( 0 in our case above, as there is just a single BUFFER, with INDEX == 0
). This is used for the sake of the EA being fully agnostic of the Custom Indicator internal variable names et al.
Just ask which BufferINDEX for which BarNUMBER one wants the value to be read from.
The first line of the code says:
double SpeedBuffer[]; // a Custom Indicator BUFFER
If not handled otherwise in OnInit(){}
all cells in the SpeedBuffer will have
EMPTY_VALUE
Empty value in an indicator buffer has by default this value == 2147483647 (0x7FFFFFFF)
as has been objected above.
Q.E.D.
One may state in OnInit(){ ArrayInitialize( SpeedBuffer, 0.123456 ); }
to have any other value for a cell-initialisation ( that for a TimeSeries-alligned BUFFERs happens upon each new-Bar event ( all cells get reshuffled by one backwards & cell[0] becomes "empty", pre-loaded with a default value discussed here ) ).
One may also add a step in an indicator OnCalculate(){ ... SpeedBuffer[0] = -9.87654; ...}
so as to avoid leaving the cell[0] in a context-inconsistent state, not in a "just"-initialised state / value.
Nevertheless, the responsibility of the value retrieval is on the Expert Advisor side, as it fills the parameters of the iCustom()
interface-proxy.
One may use preventive steps as shown in >>> https://stackoverflow.com/a/26389823/3666197 to minimise a risk of improper parameters ordering / values once calling an external Custom Indicator(s) to retrieve a set of values.
This simple method may save you literally tens of man*days of testing/debugging once a rich-extern
-parametrised Custom Indicator with several indicator buffers has fallen into suspects due to serving "wrong" numbers ( just due to iCustom()
call parameters being "invisibly" out of proper order/context )
Another thing to keep in mind when a custom indicator shows different values on the chart then are reported to the ExpertAdvisor, is that the execution flow through OnCalculate() needs to be different for an ExpertAdvisor than it is for a chart; specifically, the chart initially kicks off a call to OnCalculate() with prev_calculated = 0, whereas an EA (whether run with Strategy Tester or live) will always have prev_calculated = rates_total-1, so that the number of bars to compute the indicator value for is rates_total - prev_calcualted = 1 (ie. just the current bar).
You do account for this in your code, by setting start, but in general for a complicated indicator (that often involves referencing more than just the previous bar) one needs to be mindful of this difference and never assume that if the indicator looks good on the chart that means it's actually working. It needs to be tested separately with an EA.
I reviewed my code and finally figured that:
double speed=iCustom(NULL,0,"Speed",2,0,0);
was using the last value, which was not yet calculated by the custom indicator,
changing it to:
double speed=iCustom(NULL,0,"Speed",2,0,1);
did the trick.
User contributions licensed under CC BY-SA 3.0