Summary:
Q1: How to make the code work?
A1: Rewrite the iteration for
-loop so as not to exit instantly in the first loop
( ref. the illustrated iteration flow / barrier below )
Q2: How to write correct code for accessing array with Boolean operations inside iteration?
A2: There is no such problem at all. If possible, always avoid testing array values, before being assigned some value
( ref. the trap below )
Q3: What is the solution, please?
A3: Repair the ill-formed for
-loop + always assign a meaningful value into a variable before using it's value in further processing or comparison.
How the algorithmisation may approach the unambiguous part of the features:
//+------------------------------------------------------------------+
//| MQL4 strict |
//+------------------------------------------------------------------+
#property strict // MQL4 compilation mode { strict }
//+------------------------------------------------------------------+
//| a global-scope |
//+------------------------------------------------------------------+
bool aTrendUpGTprev[], // MQL4 dynamic array
aTrendDnLTprev[]; // MQL4 dynamic array
int TrendBar = 5,
SlowPeriod = 14;
void OnInit()
{ ArraySetAsSeries( aTrendDnLTprev, True ); // MQL4 Reference / Array Functions / ArraySetAsSeries
ArraySetAsSeries( aTrendUpGTprev, True );
}
bool isNewBar( const int TF = PERIOD_CURRENT )
{
static datetime lastBar = EMPTY;
datetime currBar = iTime( _Symbol, TF, 0 );
if ( lastBar != currBar ) // .TEST
{ lastBar = currBar; // .UPD
updateMA( TF ); // .UPD
return( true ); // .RET .T.
}
return( false ); // .RET .F.
}
void updateMA( const int TF = PERIOD_CURRENT )
{
static bool isFirstCall = True;
static double SlowMA_0,
SlowMA_1;
if ( isFirstCall ) // .TEST
{ isFirstCall = False; // .SET / LOCK FOREVER
// --------------------------------------------------------------------------------------------------------
// INITIAL PRE-FILL FOR ANY-DEPTH TREND-CALCULUS
// SHUFFLE-LOGIC LATCH-MOV PRINCIPALLY AVOIDS ANY-DEPTH RE-CALCULATION(S) - COMPUTATIONALLY EFFICIENT
SlowMA_0 = NormalizeDouble( iMA( _Symbol,
TF,
SlowPeriod,
0,
MODE_EMA,
PRICE_OPEN, // OPEN ?
TrendBar
),
Digits // no need to Normalize here, as the values do not enter any XTO
);
for ( int iii = TrendBar - 1; iii > 0, iii-- )
{ SlowMA_1 = SlowMA_0;
SlowMA_0 = SlowMA_0 = NormalizeDouble( iMA( _Symbol, TF, SlowPeriod, 0, MODE_EMA, PRICE_OPEN, iii ), Digits ); // no need to Normalize here, as the values do not enter any XTO
aTrendUpGTprev[iii] = ( SlowMA_0 >= SlowMA_1 ); // bi-state { True | False }
aTrendDnLTprev[iii] = ( SlowMA_0 <= SlowMA_1 ); // bi-state { True | False }
}
}
// -------------------------------------------------------------------------------------------------------------
// SHUFFLE-LOGIC LATCH-MOV PRINCIPALLY AVOIDS ANY-DEPTH RE-CALCULATION(S) - COMPUTATIONALLY EFFICIENT
// PROTECTED FROM DESYNC/ARTIFACTS FROM RE-ENTRY(-IES) WITHIN THE SAME BAR - SAFE-MODE
static datetime lastBar = EMPTY;
datetime currBar = iTime( _Symbol, TF, 0 );
if ( lastBar != currBar ) // .TEST
{ lastBar = currBar; // .UPD
SlowMA_1 = SlowMA_0; // .MOV [1] << [0]
SlowMA_0 = SlowMA_0 = NormalizeDouble( iMA( _Symbol, TF, SlowPeriod, 0, MODE_EMA, PRICE_OPEN, 0 ), Digits ); // .NEW [0]
aTrendUpGTprev[0] = ( SlowMA_0 >= SlowMA_1 ); // bi-state { True | False }
aTrendDnLTprev[0] = ( SlowMA_0 <= SlowMA_1 ); // bi-state { True | False }
}
// -------------------------------------------------------------------------------------------------------------
// MQL4 ( "new" MQL4 ) requirement to return even from a void fun() >>> MQL4 Reference / Updated MQL4
return( NULL );
}
void OnTick()
{
if ( isNewBar( PERIOD_M5 ) ) // returns bool value per-se
{
// if ( MA( "Up", PERIOD_M5 ) ) Alert ( "Trend Up good" ); // Either this
// else if ( !MA( "Up", PERIOD_M5 ) ) Alert ( "Trend Up bias" ); // or this.
// else Alert ( "No Trend" ); // This is an un-reachable state within above defined logic
// ----------------------------------------------------------------- // --------------------------------------------------------
// There is no principal chance
// to get a tri-state logic
// under one boolean var
bool isUP = aTrendUpGTprev[TrendBar];
bool isDN = aTrendDnLTprev[TrendBar];
for ( int iii = TrendBar - 1; iii > 0; iii-- )
{
isUP &= aTrendUpGTprev[iii];
isDN &= aTrendDnLTprev[iii];
}
if ( isUP ) Alert ( "Trend Up good" ); // if Slow[n] <= Slow[...] <= Slow[3] <= Slow[2] <= Slow[1] then true
if ( isDN ) Alert ( "Trend Dn good" );
}
}
Problem is not an access to Array cell[i]
from inside for
loop
As given above, your code as-is does not meet what you strive to do.
bool MA( const string Direction, const int TF )
{
double Slow[];
ArrayResize( Slow, TrendMinDurationBar + 1 );
for ( int i = TrendBar; i > 1; i-- )
{ Slow[i] = NormalizeDouble( iMA( _Symbol, TF, SlowPeriod, 0, MODE_EMA, PRICE_OPEN, i ),
Digits
);
// Alert( "DataSlow" + (string)i + ": " + DoubleToStr( Slow[i], Digits ) );
if ( Direction == "Up" )
{
if ( Slow[i] <= Slow[i-1] ) return( true );
else if ( Slow[i] > Slow[i-1] ) return( false );
}
}
return( false );
}
It starts to iterate over an i
-range ( 5, 4, 3, 2 )
in bool MA()
and test a sequence of comparisons:
only in an "Up"
-case:
[5]<=[4] |-> RET .T.
, _____________ either this
[5]> [4] |-> RET .F.
, _____________ or this is & must be a reason for RET {.T. | .F.}
. . . . . . . . . . . . . . . . . . . . . . _ _ _ _ _ _ _ _ _ code never gets past this
[4]<=[3] |-> RET .T.
,
[4]> [3] |-> RET .F.
,
[3]<=[2] |-> RET .T.
,
[3]> [2] |-> RET .F.
You might already noticed, that dynamic arrays in MQL4
do not have initialised value known, the more after ArrayResize()
.
This makes the conditions { [5]<=[4] | [5]> [4] }
fairly vulnerable to issues out of your control, as your code has assigned a value into Slow[5]
but not into [4]
yet and there you go.
Dynamic array memory-allocation / resize lottery side-effects expose your code to a principally uncontrollable value, accessed in [4]
in both comparisons { [5]<=[4] | [5]> [4] }
, which in principle is anything else but the iMA(...)
.
As your observations indicate the [4]
being lower than an already assigned iMA()
value into [5]
, you were sort of lucky that the randomness of the un-initialised memory ( containing uncontrollable residual binary values ) contained by no other means but just by chance some residual-bitmap, that happily was interpreted as a IEEE-float32 number, lower than the calculated & stored iMA()
value compared in cell [5]
with.
Update:
What would I do if I were to implement the idea?
After several hundred man*years in the MQL4
-domain, Technical Indicators
have taught us to rather spend some time to separate computationaly expensive parts from cheaper parts of the algo and focus the design tuning around the more expensive parts.
So:
rather think about avoiding a waste to re-calculate values already once computed a static double Slow[]
would allow you to save the values once computed on a NewBar()
event or use the same idea step forwards to save not the "tail" of Slow[5..2]
, and re-compare them upon deciding in [0]
, but produce & re-use not the iMA()
4B-values, but rather their already made pair-wise comparisons, which solely decide the actual return value:
static bool aTrendUpPrevGT[];
static bool aTrendDnPrevLT[];
which will re-articulate the if MA()
to update just the recent bar step
and
return ( aTrendUpPrevGT[5]
&& aTrendUpPrevGT[4]
&& aTrendUpPrevGT[3]
&& aTrendUpPrevGT[2]
)
A proper use of ArraySetAsSeries()
and correct indexing steps are obvious to be adapted