Optimised EmEditor Macro to transpose and transform tab delimited data - performance

I currently have a large tab delimited data set that looks like this (n is simply a number, there are more than 4 Headers), an initial Headers column that repeats, followed by at most, 2 columns of data (sometimes only one):
Input file:
Hdr1 A1 B1
Hdr2 A2 B2
Hdr3 A3 B3
Hdrn An Bn
Hdr1 C1
Hdr2 C2
Hdr3 C3
Hdrn Cn
Hdr1 D1 E1
Hdr2 D2 E2
Hdr2 D3 E3
Hdrn Dn En
I need to transpose and transform the data so output looks similar to this (so the repeating headers are removed and the data remains):
Hdr1 Hdr2 Hdr3 Hdrn
A1 A2 A3 An
B1 B2 B3 Bn
C1 C2 C3 Cn
D1 D2 D3 Dn
E1 E2 E3 En
Any ideas for how to do this with an Optimised EmEditor javascript Macro would be much appreciated.

Here is a JavaScript macro for you:
sSeparator = "Hdr1";
sDelimiter = "\t";
sNL = "\r\n";
Redraw = false;
document.CellMode = true; // Must be cell selection mode
editor.ExecuteCommandByID(4323); // Clear All Bookmarks in This Document
document.Filter("^" + sSeparator,0,eeFindReplaceCase | eeFindReplaceRegExp,0,0,0);
editor.ExecuteCommandByID(3927); // Bookmark All
document.Filter("",0,0,0,0,0); // Reset Filter
if( document.BookmarkCount == 0 ) {
alert( "Cannot find any lines that begin with \"" + sSeparator + "\"" );
Quit();
}
document.selection.StartOfDocument(false);
x1 = 1;
y1 = 1;
nLines = 0;
nMaxCol = document.GetColumns();
str = "";
for( ;; ) {
bStop = false;
if( document.selection.NextBookmark() ) { // if next bookmark found
y2 = document.selection.GetActivePointY(eePosCellLogical);
}
else {
y2 = document.GetLines(); // if bookmark does NOT exist at end of document
bStop = true;
}
if( nLines == 0 ) {
nLines = y2 - y1;
}
else {
if( nLines != y2 - y1 ) {
alert( "Number of lines between " + sSeparator + " is not same. Check the format of the input file." );
Quit();
}
}
for( iCol = x1; iCol <= nMaxCol; ++iCol ) {
s = document.GetCell( y1, iCol, eeCellIncludeQuotes );
if( s.length == 0 ) {
break;
}
str += s;
str += sDelimiter;
str += document.GetColumn( iCol, sDelimiter, eeCellIncludeQuotes, y1 + 1, y2 - y1 - 1 );
str += sNL;
}
y1 = y2;
if( bStop ) {
break;
}
x1 = 2; // don't need the first column except the first time
}
editor.NewFile();
document.selection.Text = str; // insert copied text
editor.ExecuteCommandByID(22529); // set TSV mode
To run this, save this code as, for instance, Transpose.jsee, and then select this file from Select... in the Macros menu. Finally, select Run Transpose.jsee in the Macros menu.

Related

Optimised Function to copy columns of selected data by a variable amount (up/down)

Is it possible to create a javascript function that can be used in macros, that can move data by an amount when moving columns of data by variable rows. For example, by passing a parameter (iRow?) to something like setcolumn and it would insert the data either above (-1 to lose the first value and then insert which is like moving the column data up one line up for example) or +2 (to blank the first 2 rows column data and then paste the rest of the data, which is like moving it down 2 columns?
Ideally optimised for speed when working on very large amounts of data (millions of rows). Couple of examples. Col1 is source data, Col2 is copying that column and moving up by one row (-1). Col3 is copying that initial col and moving down by two (+2):
Data
Data-1
Data+2
00000001
00000002
00000002
00000003
00000003
00000004
00000001
00000004
00000005
00000002
00000005
00000006
00000003
00000006
00000007
00000004
00000007
00000008
00000005
00000008
00000009
00000006
00000009
00000010
00000007
00000010
00000008
I wrote a JavaScript for EmEditor macro. MoveCells( -1, true ) copies the selected cells up to right, and MoveCells( 1, false ) moves the selected cells down.
// yShift : Specify how many rows to shift (move) the cell selection ( >0 : down, <0 : up )
function MoveCells( yShift, bCopy )
{
if( !yShift || yShift == 0 ) {
Quit();
}
if( !document.CellMode ) { // Must be cell selection mode
alert( "Cell selection mode must be turned on" );
Quit();
}
xTop = document.selection.GetTopPointX(eePosCellLogical);
yTop = document.selection.GetTopPointY(eePosCellLogical);
xBottom = document.selection.GetBottomPointX(eePosCellLogical);
yBottom = document.selection.GetBottomPointY(eePosCellLogical);
yLines = document.GetLines(); // retrieve the number of lines
if( document.GetLine( yLines ).length == 0 ) { // -1 if the last line is empty
--yLines;
}
if( yTop < 0 || xTop < 0 || xBottom < 0 || yBottom < 0 ) {
alert( "Incorrect selection" );
Quit();
}
if( xTop != xBottom ) {
alert( "More than one columns are selected" );
Quit();
}
bOldRedraw = Redraw;
Redraw = false;
bOldCombineHistory = CombineHistory;
CombineHistory = false;
yFirstLine = document.HeadingLines + 1;
if( (yShift < 0 && yTop + yShift < yFirstLine) || (yShift > 0 && yBottom + yShift > yLines) ) {
if( yShift < 0 ) {
document.selection.SetActivePoint( eePosCellLogical, xTop, yFirstLine ); // move to the first line
nCount = yFirstLine - (yTop + yShift);
for( i = 0; i < nCount; ++i ) {
document.selection.LineOpen(true);
}
yTop += nCount;
yBottom += nCount;
}
else {
document.selection.SetActivePoint( eePosCellLogical, xTop, yLines ); // move to the first line
nCount = yBottom + yShift - yLines;
for( i = 0; i < nCount; ++i ) {
document.selection.LineOpen(false);
}
}
}
sDelimiter = document.Csv.Delimiter; // retrieve the delimiter
str = document.GetColumn( xTop, sDelimiter, eeCellIncludeQuotes, yTop, yBottom - yTop + 1 ); // get cell selections from top to bottom, separated by delimiter
if( bCopy ) {
++xTop
document.InsertColumn( xTop );
}
if( yShift > 0 ) { // shift down
for( i = 0; i < yShift; ++i ) { // insert delimiters before the copied string
str = sDelimiter + str;
}
document.SetColumn( xTop, str, sDelimiter, eeDontQuote, yTop );
}
else { // shift up
for( i = 0; i < -yShift; ++i ) { // add delimiters to the copied string
str += sDelimiter;
}
document.SetColumn( xTop, str, sDelimiter, eeDontQuote, yTop + yShift );
}
document.selection.SetActivePoint( eePosCellLogical, xTop, yTop + yShift ); // move the current selection
document.selection.SetActivePoint( eePosCellLogical, xTop, yBottom + yShift, true );
Redraw = bOldRedraw;
CombineHistory = bOldCombineHistory;
}
// Here is the main code
MoveCells( -1, true ); // Copy the selection up to right
MoveCells( 2, false ); // Move the selection down
To run this, save this code as, for instance, Macro.jsee, and then select this file from Select... in the Macros menu. Finally, select Run Macro.jsee in the Macros menu while the current CSV document is active.
References:
Macro: GetColumn Method
Macro: SetColumn Method

How to implement snapping effect and collision detection between two objects using Threejs?

We are able to detect the collision but could not implement a snapping/magnetic effect like Snap edges of objects to each other and prevent overlap
we need help with 3D objects here and we are using Vec3 for the active object's position.
With the following approach, collision detection is working perfectly for all cases, and magnetic effect is somehow working - not perfectly.
It's working well when the object is moving along x or z-axis but when the object's movement is in diagonal direction (moving along x and z-axis simultaneously) that is where the problem comes.
Though am not satisfied with the following approach that's why am looking for new approach to implement both magnetic and collision detection features.
It is not necessary to have the solution in Threejs, any general solution or algorithm of coordinates can be converted into Threejs.
let collide = this.detectCollisionCubes(activeObject, collidingObject, vec3);
let magneticEffect = new MagneticEffect(activeObject, vec3, collidingObject);
vec3 = magneticEffect.setNewPosition();
activeObject.position.copy(vec3);
detectCollisionCubes = function(a, d, vec3){
// a is active object's positon
// d is colliding object
let aHeight = Math.abs(a.getHeight());
let aWidth = Math.abs(a.getWidth());
let aDepth = Math.abs(a.getDepth());
let b1 = vec3.y - aHeight / 2;
let t1 = vec3.y + aHeight / 2;
let r1 = vec3.x + aWidth / 2;
let l1 = vec3.x - aWidth / 2;
let f1 = vec3.z - aDepth / 2;
let B1 = vec3.z + aDepth / 2;
let dHeight = Math.abs(d.getHeight());
let dWidth = Math.abs(d.getWidth());
let dDepth = Math.abs(d.getDepth());
let b2 = d.position.y - dHeight / 2;
let t2 = d.position.y + dHeight / 2;
let r2 = d.position.x + dWidth / 2;
let l2 = d.position.x - dWidth / 2;
let f2 = d.position.z - dDepth / 2;
let B2 = d.position.z + dDepth / 2;
if (t1 < b2 || r1 < l2 || b1 > t2 || l1 > r2 || f1 > B2 || B1 < f2) {
return false;
}
return true;
}
Trying to create magnetic effect via
this.currentObject = currentObject;
this.collisionObject = collisionObject;
this.collisionType = null;
this.objectType = null;
this.currentPosition = currentPosition;
this.currentObjectHeight = Math.abs(currentObject.getHeight());
this.currentObjectWidth = Math.abs(currentObject.getWidth());
this.collisionObjectHeight = Math.abs(collisionObject.getHeight());
this.collisionObjectWidth = Math.abs(collisionObject.getWidth());
this.collisionObjectDepth = Math.abs(collisionObject.getDepth());
this.objectTop = currentObject.position.y + (this.currentObjectHeight/2);
this.objectBottom = currentObject.position.y - (this.currentObjectHeight/2);
this.collideTop = collisionObject.position.y + (this.collisionObjectHeight/2);
this.collideBottom = collisionObject.position.y - (this.collisionObjectHeight/2);
this.zAxisDifference = Math.abs(Math.abs(currentPosition.z) - Math.abs(collisionObject.position.z));
this.xAxisDifference = Math.abs(Math.abs(currentPosition.x) - Math.abs(collisionObject.position.x));
// Extra code here
if (
this.objectTop < this.collideBottom
) {
this.collisionType = collisionTypes.verticalBottom;
} else if (
this.objectBottom > this.collideTop
) {
this.collisionType = collisionTypes.verticalTop;
} else if (
this.currentPosition.x > this.collisionObject.position.x &&
this.zAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalXLeft;
} else if (
this.currentPosition.x < this.collisionObject.position.x &&
this.zAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalXRight;
} else if (
this.currentPosition.z > this.collisionObject.position.z &&
this.xAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalZLeft;
} else if (
this.currentPosition.z < this.collisionObject.position.z &&
this.xAxisDifference < 2
) {
this.collisionType = collisionTypes.horizentalZRight;
}
MagneticEffect.prototype.setNewPosition = function () {
if (this.collisionType === collisionTypes.verticalBottom) {
this.currentPosition.y = this.collideBottom + 0.5;
} else if (this.collisionType === collisionTypes.verticalTop) {
this.currentPosition.y = this.collideTop - 0.5;
} else if (this.collisionType === collisionTypes.horizentalXRight) {
this.currentPosition.x = this.collisionObject.position.x - this.collisionObjectWidth - 0.5;
} else if (this.collisionType === collisionTypes.horizentalXLeft) {
this.currentPosition.x = this.collisionObject.position.x + this.collisionObjectWidth + 0.5;
} else if (this.collisionType === collisionTypes.horizentalZRight) {
this.currentPosition.z = this.collisionObject.position.z - this.collisionObjectWidth - 0.5;
} else if (this.collisionType === collisionTypes.horizentalZLeft) {
this.currentPosition.z = this.collisionObject.position.z + this.collisionObjectWidth + 0.5;
}
return this.currentPosition;
};

Is there a way to make this code faster and if possible avoid loops?

A1, B1, C1, A2, B2 and C2 are 6 matrix with the same dimensions 4435X2000.
I have to find the values i, j and k for which A1(k,2000) == A2(i,j) and B1(k,2000) == B2(i,j) and C1(k,2000) == C2(i,j) , with the condition X(k)==1 and Y(i,j)==1
The objective is to find: counter, L, T and D
Is there a way to make this code faster? Can I avoid loops?
counter=0;
L(1)=0;
T(1)=0;
D(1)=0;
for k=1:4435
if X(k)==1 % X is a vector (4435x1)
F(k,:) = [A1(k,2000) B1(k,2000) C1(k,2000)]
for i=1:4435
for j=100:1999
if Y(i,j)==1 % Y is a matrix (4435x1999)
if F(k,:) == [A2(i,j) B2(i,j) C2(i,j)]
counter = counter+1;
L(counter)=k;
T(counter)=i;
D(counter)=j;
end
end
end
end
end
end
I want a solution that will save me at least 80% of the computation time!
and not have the error message: Out of memory
See how this works out for you -
%// Store X-Y data by calling X() and Y() functions
X_data = X(1:4435);
Y_data = Y(1:4435,100:1999);
range1 = 100:1999 %// define range for columns
A2 = A2(:,range1); %// Crop out A2, B2, C2 based on column-range
B2 = B2(:,range1);
C2 = C2(:,range1);
Y_data = Y_data(:,range1)==1;
%// Indices for dim-3
idx_X = find(X_data==1)
%// Map X==1 onto A1, B1, C1
A1Lr = A1(X_data==1,end)
B1Lr = B1(X_data==1,end)
C1Lr = C1(X_data==1,end)
%// Setup output array to store L, T, D as single Nx3 output array
out = zeros(sum(Y_data(:))*numel(A1Lr),3);
%// Try out(sum(Y_data(:)==1)*numel(A1Lr),3)=0; instead for speed!
%// Start collecting output indices
count = 1;
for iter1 = 1:numel(A1Lr)
[R,C] = find(Y_data & A2==A1Lr(iter1) & B2==B1Lr(iter1) & C2==C1Lr(iter1));
nR = numel(R);
out(count:count+nR-1,:) = [R C repmat(iter1,nR,1)];
count = count + nR;
end
out(find(out(:,1)==0,1):end,:)=[];
%// Packup the outputs
T = out(:,1)
D = out(:,2) + range1(1)-1
L = idx_X(out(:,3))
It is very difficult to determine what your code is actually supposed to accomplish, without really working to interpret your code. However, I'll give it a crack:
% Determine where X is true.
XTrue = X == 1;
% Extract values from A1,B1,C1 where X is true.
F ( XTrue , 1 : 3 ) = [ A1(XTrue,2000) B1(XTrue,2000) C1(XTrue,2000) ];
% Determine where Y is true.
YTrueIndex = find ( Y == 1 );
% Determine where the extracted values match
counter = [];
L = [];
T = [];
D = [];
for ( ii = 1 : length(YTrueIndex) )
indexCurrent = YTrueIndex(ii)
FRowsThatMatch = F(:,1)==A2(indexCurrent) & F(:,2)==B2(indexCurrent) & F(:,3)==C2(indexCurrent);
matchCount = length ( find ( FRowsThatMatch ) );
if ( matchCount > 0 )
counter = counter + matchCount;
[ i , j ] = ind2sub ( size ( Y ) , indexCurrent );
L = [ L , find ( FRowsThatMatch ) ];
T = [ T , ones(matchCount,1)*i ];
D = [ D , ones(matchCount,2)*j ];
end
end

Using stack to find the greatest common divisor

I am trying to implement an alogorithm to find the greatest common divisor using a stack: I am unable to formulate the correct answer based on my logic below. Please help. Here is my code:
def d8(a,b)
if (a==b)
return a
end
s = Stack.new
s.push(b)
s.push(a)
c1 = s.pop
c2 = s.pop
while c1!=c2
if s.count>0
c1 = s.pop
c2 = s.pop
end
if c1== c2
return c1
elsif c1>c2
c1 = c1-c2
s.push(c2)
s.push(c1)
else
c2 = c2 -c1
s.push(c2)
s.push(c1)
end
end
return nil
end
GCD cannot be nil. Two integers always have a GCD. So the logic in the function is already incorrect just because under some condition it has a return nil.
Looking at this return nil condition, it is happening when c1 == c2 (it will exit the while loop). At the same time, inside the while loop, you return a value if c1 == c2. These two cases are in logical contradiction. In other words, you are exiting the while loop on the c1 == c2 condition and treating that condition as invalid before your if c1 == c2 condition can trigger and treat the condition as valid and return the correct answer.
Simplifying the logic a little, you get:
def d8(a,b)
return a if a == b # Just a simpler way of doing a small if statement
s = Stack.new # "Stack" must be a gem, not std Ruby; "Array" will work here
s.push(b)
s.push(a)
#c1 = s.pop # These two statements aren't really needed because of the first
#c2 = s.pop # "if" condition in the while loop
while c1 != c2
if s.count > 0
c1 = s.pop
c2 = s.pop
end
# if c1 == c2 isn't needed because the `while` condition takes care of it
if c1 > c2
c1 = c1 - c2
else
c2 = c2 - c1
end
# These pushes are the same at the end of both if conditions, so they
# can be pulled out
s.push(c2)
s.push(c1)
end
return c1 # This return occurs when c1 == c2
end
This will work, but it becomes more obvious that the use of a stack is superfluous and serves no purpose at all in the algorithm. s.count > 0 will always be true, and you are popping variables off right after you push them (basically a no-op). So this is equivalent to:
def d8(a,b)
return a if a == b
c1 = a
c2 = b
while c1 != c2
if c1 > c2
c1 = c1 - c2
else
c2 = c2 - c1
end
end
return c1
end
Java code for it would be
public static int gcd (int p, int q) {
StackGeneric<Integer> stack = new StackGeneric<Integer>();
int temp;
stack.push(p);
stack.push(q);
while (true) {
q = stack.pop();
p = stack.pop();
if (q == 0) {
break;
}
temp = q;
q = p % q;
p = temp;
stack.push(p);
stack.push(q);
}
return p;
}
Replace the function call in recursive solution with the while loop and iterate it till the second argument becomes 0, as it happens with the recursive function call
public static int gcd (int p, int q) {
if (q == 0) {
return p;
}
return gcd(q, p % q);
}

How to Quantise an indicator in MQL4?

I am failing over and over trying to get this indicator to run quantised with 2 buffers in mql4. After a long time reading I have put 2 extra buffers in to squish it :/ because:
the indicator is sitting between 0.1430-0.1427 at present but doesn't have a fixed top and bottom.
I can't seem to suss it; cool indicator but won't play fair!
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 CLR_NONE
#property indicator_color4 CLR_NONE
//#property indicator_minimum 0
//#property indicator_maximum 100
extern int P = 13;
extern int T = 3000;
extern double P2 = 0.001;
//int MIN = 0;
//int MAX = 100;
double G[];
double R[];
double B3[];
double B4[];
int init(){
IndicatorBuffers(4);
SetIndexBuffer( 0, G );SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 1, Lime );
SetIndexBuffer( 1, R );SetIndexStyle( 1, DRAW_LINE, STYLE_SOLID, 1, Red );
SetIndexBuffer( 2, B3 );SetIndexStyle( 2, DRAW_NONE );
SetIndexBuffer( 3, B4 );SetIndexStyle( 3, DRAW_NONE );
return(0);
}
int start(){
if ( T >= Bars ) T = Bars;
SetIndexDrawBegin( 0, Bars - T + P + 1 );
SetIndexDrawBegin( 1, Bars - T + P + 1 );
SetIndexDrawBegin( 2, Bars - T + P + 1 );
SetIndexDrawBegin( 3, Bars - T + P + 1 );
int Z, C, Opt = IndicatorCounted();
if ( Bars <= 38 ) return(0);
if ( Opt < P ){
for ( Z = 1; Z <= 0; Z++ ) G[T-Z] = 0.0;
for ( Z = 1; Z <= 0; Z++ ) R[T-Z] = 0.0;
}
Z = T - P - 1;
while( Z >= 0 ){
double A, S1, S2;
S1 = 0.0; for ( C = 0; C <= P - 1; C++ ){ S1 = S1 + ( High[Z+C] + Low[Z+C] ) / 2;}
S2 = 0.0; for ( C = 0; C <= P - 1; C++ ){ S2 = S2 + ( ( High[Z+C] + Low[Z+C] ) * ( C+1 ) / 2 );}
A = S1 / S2;
// if ( A < MIN ){ MIN = A;}
// if ( A > MAX ){ MAX = A;}
// A = ( MIN / MAX ) * 100;
G[Z] = A;
if ( Z > 0 ){ R[Z-1] = A;}
Z--;
}
for ( int N = T-P-2; N >= 0; N-- ){
if ( N > 0 ){
if ( G[N-1] > G[N] ){ R[N] = EMPTY_VALUE; continue;}
if ( G[N-1] < G[N] ){ G[N] = R[N]; continue;}
}
B3[0] = G[0] + P2;
B4[0] = G[0] - P2; //forced quantise using 2 extra buffers
}
return(0);
}
Let´s split the task first
0) indicator logic
1) indicator quantisation step
2) indicator performance
MQL4 Custom Indicator programming relies on deeper understanding of underlying MetaTrader4 Terminal platform. Each external Market Event, changing a traded instrument price, is signalled to a lcoalhost by a network delivery of a QUOTE ... message from MetaTrader4 Server. This is aka Tick and it triggers a call to a function originally called start(), in newer New-MQL4.56789 renamed to OnTick().
The below modified MQL4 listing contains remarks for core-logic disambiguation, which must precede all the below listed steps.
1) indicator quantisation step
While the code is still very inefficient ( as per [2] below ) the logic does not include any straight hurdle form having the output quantised to any form thereof { binary | ternary | arbitrary-number-of-states }-quantised system. Whence the indicator core-logic is cleared, the quantisation step is just a trivial conversion from R(1) to I(1).
2) indicator performance
Any Tick arrival may, but need not modify either High[0] or Low[0], which are the only variable parts of the proposed Custom Indicator calculus.
This is the core idea on how to reduce the scope of re-calculations, that the MQL4 code has to realise per tick. In recent versions of MT4, all Custom Indicators share a single thread, the more stress has been put on efficient algorithmisation of Custom Indicators, at these may block the platform's trading decisions on poor, inefficient code-loops and convolution/recursion re-executions.
#property indicator_separate_window
#property indicator_buffers 4
#property indicator_color1 Lime
#property indicator_color2 Red
#property indicator_color3 CLR_NONE
#property indicator_color4 CLR_NONE
extern int P = 13;
extern int T = 3000;
extern double P2 = 0.001;
double G[]; // 0: LINE
double R[]; // 1: LINE
double B3[]; // 2: BUT NEVER PAINTED, NEVER CONSUMED _?_
double B4[]; // 3: BUT NEVER PAINTED, NEVER CONSUMED _?_
int init(){
IndicatorBuffers(4);
SetIndexBuffer( 0, G );SetIndexStyle( 0, DRAW_LINE, STYLE_SOLID, 1, Lime );
SetIndexBuffer( 1, R );SetIndexStyle( 1, DRAW_LINE, STYLE_SOLID, 1, Red );
SetIndexBuffer( 2, B3 );SetIndexStyle( 2, DRAW_NONE );
SetIndexBuffer( 3, B4 );SetIndexStyle( 3, DRAW_NONE );
return(0);
}
int start(){
if ( Bars <= 38 ) return(0); // JIT/RET in case Bars < 39 --^ --^ --^ --^
if ( T >= Bars ) T = Bars; // (TRIM´d) T < Bars .OR. = Bars
int aDrawBegins = Bars - T + P + 1; // ( extern P = 13 ) + 1 + ( Bars - ( extern T = 3000 if T < Bars else Bars ) )
//tIndexDrawBegin( 0, Bars - T + P + 1 ); // PREF: ( reused 4x )
SetIndexDrawBegin( 0, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 1, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 2, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
SetIndexDrawBegin( 3, aDrawBegins ); // Draw 14+ last candles -- PREF: why a per tick hard-coded SHIFTING / enforced re-draw?
double A, S1, S2; // auxiliary var for bar-mid-price calculi
int Z; // auxiliary stepper
int Opt = IndicatorCounted(); // Opt ( NEVER RE-USED )
if ( Opt < P ){ // if ( ( extern P = 13 ) > IndicatorCounted() )
// ----------------------- ??? ----------------------------------------------------- NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
for ( Z = 1; Z <= 0; Z++ ) G[T-Z] = 0.0; // .STO G[T-Z], 0., BUT NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
for ( Z = 1; Z <= 0; Z++ ) R[T-Z] = 0.0; // .STO R[T-Z], 0., BUT NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
// ----------------------- ??? ----------------------------------------------------- NEVER EXEC´d: for( Z = 1++ v/s Z <= 0 )
}
Z = T - P - 1; // .STO Z, ( T = Bars (TRIM´d) ) - ( extern P = 13 ) - 1
while( Z >= 0 ){ // .DEC Z
// !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PERF: very inefficient to RE-calc STATIC ( ( extern P = 13 ) - 1 )-DEEP CONVOLUTIONS per tick !!
S1 = 0.0; for ( int C = 0; C <= P - 1; C++ ){ S1 = S1 + ( High[Z+C] + Low[Z+C] ) / 2; }
S2 = 0.0; for ( int C = 0; C <= P - 1; C++ ){ S2 = S2 + ( ( High[Z+C] + Low[Z+C] ) * ( C+1 ) / 2 );}
// !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // PERF: very inefficient to RE-calc STATIC ( ( extern P = 13 ) - 1 )-DEEP CONVOLUTIONS per tick !!
A = S1 / S2;
G[Z] = A; // .STO G[Z], A if Z >= 0
if ( Z > 0 ){ R[Z-1] = A;} // .STO R[Z-1], A if Z > 0
Z--;
}
for ( int N = T - P - 2; N >= 0; N-- ){ // .STO N, ( T = Bars (TRIM´d) ) - ( extern P = 13 ) - 2
if ( N > 0 ){ // N > 0:
if ( G[N-1] > G[N] ){ R[N] = EMPTY_VALUE; continue;} // .BLNK R[N], EMPTY if G[N-1] > G[N]
if ( G[N-1] < G[N] ){ G[N] = R[N]; continue;} // .SET G[N], R[N] if G[N-1] < G[N]
}
// ?? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WHY MANY-TIMES RE-ASSIGNED A CONST. VALUE HERE, INSIDE A FOR(){...}-loop body? -------------- ??
B3[0] = G[0] + P2; // .STO B3[0], G[0] + ( extern P2 = 0.001 )
B4[0] = G[0] - P2; // .STO B4[0], G[0] - ( extern P2 = 0.001 )
// forced quantise using 2 extra buffers
// ?? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // WHY MANY-TIMES RE-ASSIGNED A CONST. VALUE HERE, INSIDE A FOR(){...}-loop body? -------------- ??
}
return(0);
}
New-MQL4.56789 syntax
The OnCalculate() function is called only in custom indicators when it's necessary to calculate the indicator values by the Calculate event. This usually happens when a new tick is received for the symbol, for which the indicator is calculated. This indicator is not required to be attached to any price chart of this symbol.
The first rates_total parameter contains the number of bars, available to the indicator for calculation, and corresponds to the number of bars available in the chart.
We should note the connection between the return value of OnCalculate() and the second input parameter prev_calculated. During the OnCalculate() function call, the prev_calculated parameter contains a value returned by OnCalculate() during previous call. This allows for economical algorithms for calculating the custom indicator in order to avoid repeated calculations for those bars that haven't changed since the previous run of this function.
For this, it is usually enough to return the value of the rates_total parameter, which contains the number of bars in the current function call. If since the last call of OnCalculate() the price data has changed (a deeper history downloaded or history blanks filled), the value of the input parameter prev_calculated will be set to zero by the terminal.
//+------------------------------------------------------------------+
//| Custom indicator iteration function |
//+------------------------------------------------------------------+
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[]
)
{
// Get the number of bars available now for the current Symbol and chart period
int barsNow = Bars( _Symbol, PERIOD_CURRENT );
// .RET value of prev_calculated for a next call
return( rates_total );
}

Resources