‘Selecting’ a Position “copies data about a position into the program environment, and further calls of PositionGetDouble(), PositionGetInteger() and PositionGetString() return the earlier copied data. This means that the position itself may no longer exist (or its volume, direction, etc. has changed), but data of this position still can be obtained. To ensure receipt of fresh data about a position, it is recommended to call PositionSelect() right before referring to them.”
https://www.mql5.com/en/docs/trading/positionselect
It basically populates a custom struct with information about the Position you ‘selected’, and that the data in this struct persists until you update it with a fresh ‘selection’.
There are 4 ways to ‘select’ a Position.
bool PositionSelect(string symbol)
bool PositionSelectByTicket(ulong ticket)
string PositionGetSymbol(int index)
ulong PositionGetTicket(int index)
If there are multiple Positions present, for an EA to close only the Positions opened by it, we need to search through all of them and check which are tagged with that EA’s Magic Number.
To do this, we need a loop to check through each.
input MagicNumber = 123;
void OnStart(){
//WRONG
for(int i = PositionsTotal() - 1; i >= 0; i--){
if(PositionSelect(_Symbol) && PositionGetInteger(POSITION_MAGIC) == MagicNumber){
//There is no Position index shifting here and the PositionSelect will, by default, select the lowest Ticket
//By virtue of the following 'close Position logic', the Position with the lowest Ticket is removed and the loop works unintentionally, but this is still horrible coding
close Position logic;
}
}
//CORRECT
for(int i = PositionsTotal() - 1; i >= 0; i--){
if(PositionGetSymbol(i) == Symbol() && PositionGetInteger(POSITION_MAGIC) == MagicNumber){
//This works great if the EA only opens Positions on one Symbol. It is marginally faster than below as we do not need to resolve PositionGetInteger(POSITION_MAGIC) if PositionGetSymbol(i) != Symbol()
close Position logic;
}
}
//CORRECT
for(int i = PositionsTotal() - 1; i >= 0; i--){
if(PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber){
//this works for a multi-Symbol EA
close Position logic;
}
}
Conversely, if the trigger to open a Position occurs, we need to check that an existing Position by the EA does not exist. (as we may not want to have more than one Position open at any point in time)
bool PositionExists = false;
//for one Symbol EA
for(int i = PositionsTotal() - 1; i >= 0; i--){
if(PositionGetSymbol(i) != Symbol())continue; //we go to the next Position
else{ //if(PositionGetSymbol(i) == Symbol())
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber){
PositionExists = true;
break; //we exit the loop once an existing EA Position is found (this saves unnecessary searching)
}
}
}
//for multi-Symbol EA
for(int i = PositionsTotal() - 1; i >= 0; i--){
if(PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber){
PositionExists = true;
break; //we exit the loop once the existing EA Position is found (this saves unnecessary searching)
}
}
if(PositionExists == false){
order entry logic;
}
An important point, before we call PositionGetInteger(POSITION_MAGIC), we must call PositionGetSymbol(i) or PositionGetTicket(i) first.
This works:
if(PositionGetTicket(i) > 0 && PositionGetInteger(POSITION_MAGIC) == MagicNumber)
This does not:
if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetTicket(i) > 0)
Thank you for the help! Much appreciated and good info!
Glad you find some utility with it!