Introduction to MQL5 and Position Properties
Brief Overview of MQL5 and its Capabilities
MetaQuotes Language 5 (MQL5) is a powerful programming language specifically designed for developing trading applications on the MetaTrader 5 platform. It’s a C++-like language that allows traders and developers to create Expert Advisors (EAs) for automated trading, custom indicators for technical analysis, scripts for single execution tasks, and libraries of functions. MQL5 offers advanced features compared to its predecessor, MQL4, including object-oriented programming, multi-currency and multi-asset testing capabilities, and access to the Market Depth.
Understanding Position Properties in MQL5
In MQL5, the concept of a ‘Position’ represents an open trade on a specific financial instrument. Unlike MQL4 where orders had types like OPBUY or OPSELL and their status (open, pending, closed) was managed implicitly or through order properties, MQL5 introduces distinct objects for Orders (requests to buy/sell), Deals (execution of an order), and Positions (the resulting open trade). Each Position has several properties that describe its state, such as the ticket number, symbol, type (Buy or Sell), volume, open price, current profit/loss, and others. These properties can be accessed using dedicated functions like PositionGetTicket(), PositionGetSymbol(), PositionGetType(), etc.
Why Knowing the Current Price of a Position is Important
Knowing the current market price relevant to an open position is fundamental for various algorithmic trading tasks. This price is necessary to calculate the floating profit or loss in real-time, implement trailing stops, manage risk based on current market levels, make decisions about closing the position, or evaluate the market’s movement relative to the position’s entry point. While a position object stores the ‘current’ profit/loss, this value is calculated by the terminal based on the current market price. Developers often need direct access to the live Bid or Ask price for more complex logic.
Methods to Retrieve the Current Price of a Position
To obtain the current price relevant to a specific open position, you need two key pieces of information: the symbol of the position and the current market data for that symbol. MQL5 provides functions to facilitate this.
Using PositionGetSymbol() to Identify the Symbol
Every position is associated with a specific trading symbol (e.g., EURUSD, Gold, BTCUSD). Before you can get the current price, you must know which symbol the position is on. The PositionGetSymbol() function is used for this purpose. You typically first select a position using functions like PositionSelect() or PositionSelectByTicket(), and then call PositionGetSymbol() to retrieve the symbol name as a string.
string symbol = PositionGetSymbol(position_ticket);
if(symbol == "")
{
// Handle error: Could not get symbol for position_ticket
Print("Error getting symbol for ticket ", position_ticket, ". Error code: ", _LastError);
return;
}
Employing SymbolInfoTick() to Obtain Current Tick Data
The SymbolInfoTick() function is the primary way to retrieve the very latest tick data (Bid, Ask, Last, Volume) for a specific symbol. It takes the symbol name as the first parameter and a MqlTick structure by reference as the second parameter. If successful, it fills the MqlTick structure with the most recent tick information.
MqlTick latest_tick;
bool success = SymbolInfoTick(symbol, latest_tick);
if(!success)
{
// Handle error: Could not get tick data
Print("Error getting tick data for symbol ", symbol, ". Error code: ", _LastError);
return;
}
The MqlTick structure contains various fields, including:
time: Time of the tick.bid: Current Bid price.ask: Current Ask price.last: Price of the last deal (relevant for exchange instruments).volume: Volume of the last deal.
Accessing Bid and Ask Prices from Tick Data
Once SymbolInfoTick() successfully populates the MqlTick structure, you can directly access the bid and ask fields to get the current Bid and Ask prices for the symbol. For a Buy position, the current price relevant to its floating P/L calculation and potential closing price is typically the Bid price. For a Sell position, it’s the Ask price.
double current_bid = latest_tick.bid;
double current_ask = latest_tick.ask;
double current_price_for_position;
if (PositionGetType(position_ticket) == POSITION_TYPE_BUY)
{
current_price_for_position = current_bid;
} else if (PositionGetType(position_ticket) == POSITION_TYPE_SELL)
{
current_price_for_position = current_ask;
} else
{
// Handle unexpected position type - though in MT5 positions are only BUY or SELL
Print("Unexpected position type for ticket ", position_ticket);
return;
}
Print("Current relevant price for position ", position_ticket, ": ", current_price_for_position);
Practical Implementation: MQL5 Code Examples
Here are practical examples demonstrating how to integrate these steps.
Example 1: Retrieving Current Price for a Specific Position
This example shows how to get the current price for a position identified by its ticket number.
void GetPriceForPosition(ulong ticket)
{
// 1. Select the position by ticket
if (!PositionSelectByTicket(ticket))
{
Print("Position with ticket ", ticket, " not found. Error: ", _LastError);
return;
}
// 2. Get the symbol name for the position
string symbol = PositionGetSymbol(ticket);
if (symbol == "")
{
Print("Failed to get symbol for position ", ticket, ". Error: ", _LastError);
return;
}
// 3. Get the latest tick data for the symbol
MqlTick latest_tick;
if (!SymbolInfoTick(symbol, latest_tick))
{
Print("Failed to get tick data for symbol ", symbol, ". Error: ", _LastError);
return;
}
// 4. Determine the relevant price based on position type
long position_type = PositionGetType(ticket);
double current_price;
if (position_type == POSITION_TYPE_BUY)
{
current_price = latest_tick.bid;
Print("Position ", ticket, " (", symbol, " BUY) - Current Bid: ", latest_tick.bid);
} else if (position_type == POSITION_TYPE_SELL)
{
current_price = latest_tick.ask;
Print("Position ", ticket, " (", symbol, " SELL) - Current Ask: ", latest_tick.ask);
} else
{
Print("Position ", ticket, " (", symbol, ") - Unexpected type: ", position_type);
return;
}
// You now have the current_price relevant to this position
// double open_price = PositionGetDouble(POSITION_PROPERTY_PRICE_OPEN);
// double current_profit = PositionGetDouble(POSITION_PROPERTY_PROFIT);
// Print("Open Price: ", open_price, ", Current Price: ", current_price, ", Floating P/L: ", current_profit);
}
// Example usage (e.g., in OnStart or OnTick)
// ulong my_position_ticket = ...; // Get a ticket from somewhere, e.g., from a Deal transaction
// GetPriceForPosition(my_position_ticket);
Example 2: Getting Price for All Open Positions
This example iterates through all open positions and prints the relevant current price for each.
void GetPricesForAllPositions()
{
int total_positions = PositionsTotal();
Print("Total open positions: ", total_positions);
for (int i = 0; i < total_positions; i++)
{
// 1. Select the position by index
// PositionGetTicket(i) gets the ticket of the position at index i
ulong position_ticket = PositionGetTicket(i);
if (position_ticket == 0)
{
Print("Failed to get ticket for position at index ", i, ". Error: ", _LastError);
continue; // Skip to the next position
}
// PositionSelectByIndex(i) can also be used, but PositionGetTicket(i) is often simpler
if (!PositionSelectByTicket(position_ticket))
{
Print("Failed to select position with ticket ", position_ticket, ". Error: ", _LastError);
continue;
}
// 2. Get the symbol name
string symbol = PositionGetSymbol(position_ticket);
if (symbol == "")
{
Print("Failed to get symbol for position ", position_ticket, ". Error: ", _LastError);
continue;
}
// 3. Get the latest tick data for the symbol
MqlTick latest_tick;
if (!SymbolInfoTick(symbol, latest_tick))
{
Print("Failed to get tick data for symbol ", symbol, ". Error: ", _LastError);
continue;
}
// 4. Determine and print the relevant price
long position_type = PositionGetType(position_ticket);
double current_price;
if (position_type == POSITION_TYPE_BUY)
{
current_price = latest_tick.bid;
Print("Pos ", position_ticket, " (", symbol, " BUY) - Current Bid: ", latest_tick.bid);
} else if (position_type == POSITION_TYPE_SELL)
{
current_price = latest_tick.ask;
Print("Pos ", position_ticket, " (", symbol, " SELL) - Current Ask: ", latest_tick.ask);
} else
{
Print("Pos ", position_ticket, " (", symbol, ") - Unexpected type: ", position_type);
}
}
}
// Example usage (e.g., in OnTick)
// GetPricesForAllPositions();
Error Handling and Validation in Price Retrieval
Robust code requires proper error handling. When retrieving position properties or tick data, always check the return values of the functions. PositionSelectByTicket() and PositionSelectByIndex() return false if the position cannot be selected. PositionGetSymbol() can return an empty string on failure after a position is selected. SymbolInfoTick() returns false if it fails to retrieve tick data. After any function call that might fail, checking the _LastError variable provides detailed information about the cause of the failure. Appropriate error messages or logging are crucial for debugging Expert Advisors and scripts.
// Example basic error check pattern
if (!FunctionThatMightFail(...))
{
Print("Function failed. Error code: ", _LastError);
// Decide how to handle the error: retry, log, stop execution, etc.
}
It’s also important to remember that SymbolInfoTick() provides the last received tick. In volatile markets or during low liquidity periods, the tick data might not be perfectly ‘real-time’ or immediately reflect the absolute latest price movement until a new tick arrives.
Advanced Techniques and Considerations
Polling Price Data Frequency (OnTick, Timers)
For strategies that require continuous monitoring of the market price relative to a position, retrieving tick data is typically done within the OnTick() event handler of an Expert Advisor. This function is called whenever a new tick is received for the chart symbol. If you need price data for symbols other than the chart symbol, or if your application is a script or indicator that doesn’t have OnTick triggered by other symbols, you might consider using a timer (EventSetTimer()) to periodically check prices using SymbolInfoTick().
However, be mindful of performance. Calling SymbolInfoTick() frequently for many symbols can consume resources. Only retrieve the data when necessary for your logic. For instance, if you only need to update a trailing stop level every 5 minutes, use a timer or check the time elapsed since the last update within OnTick.
Getting Price for Symbols Other Than the Chart Symbol
The examples above implicitly assume you might be getting the price for a position on the current chart’s symbol or handling multiple symbols within an EA running on one chart. The SymbolInfoTick() function explicitly takes the symbol name as a parameter, meaning you can request tick data for any symbol available in the Market Watch, regardless of the chart the EA is attached to.
This is powerful for managing multi-symbol portfolios from a single EA instance. Simply pass the position’s symbol name, retrieved using PositionGetSymbol(), directly to SymbolInfoTick().
Performance Considerations (avoiding excessive calls)
Retrieving tick data for multiple symbols on every tick of the chart symbol can lead to performance bottlenecks, especially if your EA is attached to an active chart (like EURUSD M1) and manages positions on many other symbols. To optimize:
- Process Symbols Efficiently: Only iterate through positions and get tick data when it’s truly needed, perhaps only on a specific time interval (using a timer or checking current time within
OnTick) rather than on every tick. - Store Tick Data: If you need the latest tick data for a symbol multiple times within a single
OnTickcall or processing cycle, retrieve it once withSymbolInfoTick()and store it temporarily in a variable or a map (map<string, MqlTick>) to avoid redundant function calls. - Focus on Relevant Symbols: When iterating through all positions, skip the process for symbols where no decision or action is currently pending based on the current price.
Error Handling & Data Validity Check (Advanced)
Beyond checking the return value of SymbolInfoTick(), you might consider the validity of the retrieved tick data itself. Although less common with SymbolInfoTick than historical data functions, checking latest_tick.time can ensure you aren’t using stale data if there haven’t been recent ticks. Also, confirm latest_tick.bid and latest_tick.ask are valid price values (e.g., not zero or a very large/small unexpected number, although SymbolInfoTick is generally reliable for this). Always handle potential _LastError values like 4301 (ERRMARKETCLOSED) or 4302 (ERRNODATA) which indicate market state or data availability issues.
Conclusion
Summary of Key Methods for Getting Position Prices
Retrieving the current price relevant to an MQL5 position is a straightforward process involving these steps:
- Identify the symbol of the position using
PositionGetSymbol()after selecting the position. - Fetch the latest tick data for that symbol using
SymbolInfoTick(), which populates anMqlTickstructure. - Access the
bidoraskfield of theMqlTickstructure, depending on whether the position is Buy or Sell, to get the current relevant market price.
These steps can be applied to a single position or iterated over all open positions managed by the trading account.
Best Practices and Recommendations
- Always include robust error handling by checking function return values and
_LastError. - Retrieve tick data only when needed to optimize performance, especially when managing multiple symbols.
- Understand that
SymbolInfoTick()provides the last tick, which is effectively the current market snapshot but may not update continuously in quiet markets. - For real-time updates tied to price movements, perform the price retrieval logic within the
OnTick()function of an EA running on the symbol’s chart or use a timer for non-chart symbols. - Be aware of market state (open/closed) and data availability, as
SymbolInfoTick()will fail if no data is available.
Further Resources for MQL5 Development
For further exploration of MQL5 programming, consult the official MQL5 Reference documentation available within the MetaEditor help system (F1 key). The MQL5.community website offers articles, forums, and a code base with numerous examples. Understanding the MetaTrader 5 architecture, including the distinction between Orders, Deals, and Positions, is key to effective MQL5 development for automated trading strategies.