Every MQL4 programmer knows the standard trio for trade management:
MagicNumber for the EA's identity, OrderTicket() for the unique ID, and OrderComment() for adding a human-readable note. Most tutorials present these as equally reliable tools. I used to treat them that way too, until a routine debugging session turned into a major headache and exposed a fundamental flaw in one of these pillars: the OrderComment() function.The Day OrderComment() Betrayed Me
The scenario was textbook. I had an EA running multiple sub-strategies. To manage them, I didn't just use the
MagicNumber (which was the same for all, identifying the EA as a whole), I used the OrderComment() to tag the order with a specific strategy ID, like "STRAT_A" or "STRAT_B". My logic was simple: loop through orders, check the MagicNumber, then check the OrderComment() to apply the correct management logic.Everything worked perfectly in the strategy tester. Then I went live.
A few days in, I noticed a trade tagged "STRAT_A" was being ignored by the EA's management loop. It wasn't being trailed, and its SL wasn't being adjusted. I manually checked the order in the terminal's trade history. The comment wasn't "STRAT_A". It was "STRAT_A[tp]". My broker's server, upon hitting the take profit, had appended
[tp] to the comment to indicate how the order was closed. This seemingly innocent addition broke my string comparison logic which used a strict equality check, OrderComment() == "STRAT_A".Reference: This issue is well documented in the MQL5 community forums. A user in a 2023 thread explicitly noted the problem: "I printed comment of an order closed by Take profit... what I got when printed: // result = '0[tp]'". The explanation given by experienced developers like William Roeder was clear: this is a "Broker specific modification" and "Not a good idea to use comments as filters". Forum users confirmed that brokers can and do add suffixes like
[sl], [tp], or [br] to the comment field.This is the first, critical, and rarely discussed pitfall: the
OrderComment() field is not immutable. While you set it on OrderSend(), the broker's server has full authority to modify it after the trade is closed. This makes it an unreliable identifier for any logic that needs to execute after a trade is finished, which is often when you need to track its performance or impact most.The Comment is Not a Database
The fundamental mistake here is treating
OrderComment() as a stable, user-controlled database field. It's not. As one developer on a forum correctly points out, "The comment in an order is optional and cannot be changed once the order is open". This immutability during the order's life cycle is a feature, but the post-closure modification by the broker is a bug in our assumption of total control.To make matters worse, in MQL5, the situation is similar. The
MqlTradeRequest structure includes a comment field, but there is no standard function to modify an open position's comment. If an order is already open, the comment is set in stone.Building a Robust Alternative: The State Machine Approach
The solution isn't to stop using comments. They are extremely useful for quick visual checks in the terminal history. The solution is to stop relying on them for programmatic logic.
Instead of using
OrderComment() as a key identifier, I shifted to a different architecture: an internal state machine. This is a technique I've rarely seen discussed in beginner EA guides, but it's incredibly effective and solves this problem elegantly.Here's the core idea. You don't rely on the broker's data for your strategy's internal state. You maintain a separate, local data structure (an array or a struct) that holds all the information you need about your orders.
``
cpp
//+------------------------------------------------------------------+
//| StateManager.mq4 |
//+------------------------------------------------------------------+
#property copyright "FXEAR.com"
#property link "https://www.fxear.com"
#property version "1.00"
// External Magic Number to identify the EA's orders
extern int MagicNumber = 123456;
// Define a structure to hold our custom order state
struct OrderState {
int ticket; // The order ticket (our primary key)
string strategyID; // e.g., "A", "B", "C"
int entrySignalID; // The specific signal that triggered this trade
int subState; // e.g., 0 = normal, 1 = trailing activated, 2 = pending modification
};
// A global array to manage our state. In a real EA, this would be dynamic.
OrderState stateArray[100];
int stateCount = 0;
//+------------------------------------------------------------------+
//| Function to add or update an order's state |
//+------------------------------------------------------------------+
void UpdateOrderState(int ticket, string strategyID, int signalID, int subState = 0) {
// Check if this ticket already exists in our state
for(int i = 0; i < stateCount; i++) {
if(stateArray[i].ticket == ticket) {
// Update the existing state
stateArray[i].strategyID = strategyID;
stateArray[i].entrySignalID = signalID;
stateArray[i].subState = subState;
return;
}
}
// If it's a new ticket, add it to the array
if(stateCount < 100) {
stateArray[stateCount].ticket = ticket;
stateArray[stateCount].strategyID = strategyID;
stateArray[stateCount].entrySignalID = signalID;
stateArray[stateCount].subState = subState;
stateCount++;
}
}
//+------------------------------------------------------------------+
//| Function to get a strategy ID for a ticket |
//+------------------------------------------------------------------+
string GetStrategyIDFromState(int ticket) {
for(int i = 0; i < stateCount; i++) {
if(stateArray[i].ticket == ticket) {
return stateArray[i].strategyID;
}
}
return "";
}
//+------------------------------------------------------------------+
//| Placeholder for a more advanced, persistent state manager |
//| The state should be saved to a file to survive terminal restarts |
//+------------------------------------------------------------------+
// The OnTick function would then use this state manager.
void OnTick() {
// ... trading logic ...
// When opening a trade:
int ticket = OrderSend(Symbol(), OP_BUY, 0.1, Ask, 3, 0, 0, "EA_BUY", MagicNumber, 0, clrNONE);
if(ticket > 0) {
// We store our own state. The comment is just a human-readable label.
UpdateOrderState(ticket, "STRAT_A", 123, 0);
}
// ... later, in your management loop ...
for(int i = 0; i < OrdersTotal(); i++) {
if(OrderSelect(i, SELECT_BY_POS) && OrderMagicNumber() == MagicNumber) {
// We do NOT rely on OrderComment().
// Instead, we get the strategy from our internal state.
string myStrategy = GetStrategyIDFromState(OrderTicket());
// Now we can manage the order based on our own reliable state.
if(myStrategy == "STRAT_A") {
// Apply Strategy A management logic...
} else if(myStrategy == "STRAT_B") {
// Apply Strategy B management logic...
}
}
}
}
`
This approach is essentially the "order fingerprint" strategy described by advanced EA developers. It decouples your EA's logic from the unreliable OrderComment() field, making it immune to broker modifications.
The File I/O Solution for Persistence
The code above has a limitation: the stateArray is stored in memory. If your EA restarts (e.g., the terminal is closed and reopened), the state is lost. For a truly robust system, you must save this state to a file.
This is the part of EA development that's rarely covered in "beginner" guides, but is essential for a professional system. By saving a CSV or binary file containing your orders' tickets and their associated strategy IDs, you can rebuild the state exactly as it was upon restart.
A Unique Perspective: Use MagicNumber as a Multi-Dimensional Key
The common advice for handling multiple strategies is to use a different MagicNumber for each strategy. While this is a simpler workaround than a state manager, it has its own hidden drawback. It treats MagicNumber as a single data point. However, you can't store complex information in it. If you want to know not only which strategy opened the order, but which signal within that strategy, and what its current state is, a single integer is insufficient.
A clever and less-discussed approach is to use the MagicNumber as a "base" and encode additional info into a separate, global-level array or file. This is essentially the state manager pattern, which I consider a more scalable and professional solution for multi-strategy EAs.
Conclusion
The OrderComment()` function is a tool for your eyes, not for your code. Its unreliability is a silent killer in many EA projects. By shifting your architecture to an internal state machine, you regain full control over your order management logic. This approach is more work upfront, but it is the only way to guarantee your EA's behavior is predictable and not subject to the whims of a broker's server modifications. The forum posts and scattered community threads about this issue serve as a warning: don't build your core logic on a foundation of sand.本文首发于FXEAR.com,原创内容,未经授权禁止转载。