I'm trying to create a simple EA tool in MT4 so that I can drag and drop two lines on the chart, and based on input parameters it calculates lot size, and then when I press execute, it enters a limit order with the entry price and stop loss price.
Please edit your post and add your code via the CODE S button.
Did you check that your lot is a multiple of your brokers VOLUME_STEP? Also, did you try adding your sl to the initial order, instead of adding it later? Some brokers you have to delete a pending order and then create a new order, and cannot modify an order that is already listed until it has been filled.[BROKER] StopLevel (points): 0
A stoplevel equal to 0 with a high probability does not mean that it is actually equal to 0, but rather that the broker does not provide information about the stoplevel.
https://docs.mql4.com/trading/ordersend
At placing of a pending order, the open price cannot be too close to the market. The minimal distance of the pending price from the current market one in points can be obtained using the MarketInfo() function with the MODE_STOPLEVEL parameter. In case of false open price of a pending order, the error 130 (ERR_INVALID_STOPS) will be generated.
A zero value of MODE_STOPLEVEL means either absence of any restrictions on the minimal distance for Stop Loss/Take Profit or the fact that a trade server utilizes some external mechanisms for dynamic level control, which cannot be translated in the client terminal. In the second case, GetLastError() can return error 130, because MODE_STOPLEVEL is actually "floating" here.Please edit your post and add your code via the CODE S button.
Did you check that your lot is a multiple of your brokers VOLUME_STEP? Also, did you try adding your sl to the initial order, instead of adding it later? Some brokers you have to delete a pending order and then create a new order, and cannot modify an order that is already listed until it has been filled.I'm trying to create a simple EA tool in MT4 so that I can drag and drop two lines on the chart, and based on input parameters it calculates lot size, and then when I press execute, it enters a limit order with the entry price and stop loss price.
As you can see I'm consistently getting the error 130 which suggests from MQL4 documentation that the SL is incorrect in someway. This makes no sense as I've logged the brokers rules and they expect 0.0 SL? I've also checked and tested the limit order being a multitude of distances away from current price and still no help.
I've tested several different ways, and have logged several different things in an attempt to narrow down the issue. I thought maybe it was a decimal issue but I see it is sending the correct .xx price based on the broker and market (US100.cash) I'm trying to trade.
//+------------------------------------------------------------------+ //| Expert Advisor: DragDropTradeTool.mq4 | //| Purpose: Drag-and-drop trade execution tool for US100 (NQ) | //| Rewritten from scratch | //+------------------------------------------------------------------+ #property strict #property indicator_chart_window input double RiskPerTradeUSD = 100.0; // Amount to risk per trade in USD input int Slippage = 3; string entryLine = "ENTRY_LINE"; string stopLine = "STOP_LINE"; string executeButton = "EXECUTE_TRADE"; bool tradeExecuted = false; //+------------------------------------------------------------------+ int OnInit() { int mSLS = (int)MarketInfo(Symbol(), MODE_STOPLEVEL); Print("[BROKER] Minimum StopLevel (points): ", mSLS); CreateLines(); CreateExecuteButton(); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { ObjectDelete(entryLine); ObjectDelete(stopLine); ObjectDelete(executeButton); Comment(""); } void OnTick() { if (tradeExecuted) return; double entry = ObjectGet(entryLine, OBJPROP_PRICE1); double stop = ObjectGet(stopLine, OBJPROP_PRICE1); double pointRisk = MathAbs(entry - stop); double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); if (pointRisk == 0 || tickValue == 0) return; double rawLot = (RiskPerTradeUSD / pointRisk) / (tickValue * 100); double lotSize = NormalizeDouble(MathFloor(rawLot / lotStep) * lotStep, 2); Comment("Entry: ", DoubleToString(entry, 2), "\nStop: ", DoubleToString(stop, 2), "\nLot Size: ", DoubleToString(lotSize, 2), "\nRisk (USD): ", RiskPerTradeUSD); } void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK && sparam == executeButton) { Print("[EXECUTE] Button clicked"); double entry = ObjectGet(entryLine, OBJPROP_PRICE1); double stop = ObjectGet(stopLine, OBJPROP_PRICE1); double pointRisk = MathAbs(entry - stop); double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); if (pointRisk == 0 || tickValue == 0) return; double rawLot = (RiskPerTradeUSD / pointRisk) / (tickValue * 100); double lotSize = NormalizeDouble(MathFloor(rawLot / lotStep) * lotStep, 2); int type = (entry > stop) ? OP_SELL : OP_BUY; int digits = (int)MarketInfo(Symbol(), MODE_DIGITS); double price = NormalizeDouble(entry, digits); double sl = NormalizeDouble(stop, digits); Print("[CALC] Entry=", DoubleToString(entry, 2), ", Stop=", DoubleToString(stop, 2), ", PointRisk=", DoubleToString(pointRisk, 2)); Print("[CALC] TickValue=", tickValue, ", RawLot=", rawLot, ", FinalLot=", lotSize, ", RiskPerTradeUSD=", RiskPerTradeUSD); Print("[ORDER] Sending Market Order: Symbol=", Symbol(), ", Type=", type, ", Lot=", lotSize, ", Price=", DoubleToString(price, digits)); int stopLevelPoints = (int)MarketInfo(Symbol(), MODE_STOPLEVEL); double stopLevelPriceDistance = stopLevelPoints * Point; double currentPrice = (type == OP_BUYLIMIT) ? Ask : Bid; double priceDiff = MathAbs(price - currentPrice); if (priceDiff < stopLevelPriceDistance) { Print("[WARNING] Entry price too close to market. Required distance: ", DoubleToString(stopLevelPriceDistance, digits)); Print("[FIX] Adjusting price to be valid by broker rules..."); price = (type == OP_BUYLIMIT) ? currentPrice + stopLevelPriceDistance : currentPrice - stopLevelPriceDistance; price = NormalizeDouble(price, digits); } Print("[BROKER] StopLevel (points): ", stopLevelPoints); Print("[BROKER] CurrentPrice=", DoubleToString(currentPrice, digits), ", EntryPrice=", DoubleToString(price, digits), ", Distance=", DoubleToString(priceDiff, digits), ", Required Min=", DoubleToString(stopLevelPriceDistance, digits)); int ticket = OrderSend(Symbol(), type, lotSize, price, Slippage, 0, 0, "DragDropTradeMarket", 0, 0, clrGreen); if (ticket > 0) { Print("Trade placed successfully! Ticket = ", ticket); // Attempt to modify order after placement (ECN-safe SL attachment) bool modified = OrderModify(ticket, price, sl, 0, 0, clrRed); if (!modified) Print("OrderModify failed! Error = ", GetLastError()); else Print("Stop loss added post-order with OrderModify()."); } if (ticket < 0) Print("OrderSend failed! Error = ", GetLastError()); tradeExecuted = true; } } void CreateLines() { double price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); ObjectCreate(entryLine, OBJ_HLINE, 0, 0, price); ObjectSetInteger(0, entryLine, OBJPROP_COLOR, clrDeepSkyBlue); ObjectSetInteger(0, entryLine, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, entryLine, OBJPROP_WIDTH, 2); ObjectSetInteger(0, entryLine, OBJPROP_SELECTABLE, true); ObjectSetInteger(0, entryLine, OBJPROP_SELECTED, true); ObjectCreate(stopLine, OBJ_HLINE, 0, 0, price - 50 * Point); ObjectSetInteger(0, stopLine, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, stopLine, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, stopLine, OBJPROP_WIDTH, 2); ObjectSetInteger(0, stopLine, OBJPROP_SELECTABLE, true); ObjectSetInteger(0, stopLine, OBJPROP_SELECTED, true); } void CreateExecuteButton() { ObjectCreate(0, executeButton, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0, executeButton, OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSetInteger(0, executeButton, OBJPROP_XDISTANCE, 20); ObjectSetInteger(0, executeButton, OBJPROP_YDISTANCE, 40); ObjectSetInteger(0, executeButton, OBJPROP_XSIZE, 100); ObjectSetInteger(0, executeButton, OBJPROP_YSIZE, 24); ObjectSetString(0, executeButton, OBJPROP_TEXT, "EXECUTE"); ObjectSetString(0, executeButton, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, executeButton, OBJPROP_FONTSIZE, 12); ObjectSetInteger(0, executeButton, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, executeButton, OBJPROP_BGCOLOR, clrGreen); ObjectSetInteger(0, executeButton, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, executeButton, OBJPROP_BACK, false); ObjectSetInteger(0, executeButton, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, executeButton, OBJPROP_SELECTED, false); ObjectSetInteger(0, executeButton, OBJPROP_HIDDEN, false); ObjectSetInteger(0, executeButton, OBJPROP_ZORDER, 0); }
Below are my most recent logged errors.
Apologies, i've added in comment as can't see edit. Yes RE the VOLUME_STEP, I've had it entering fine with the correct size on market orders, just can't add a pending order. And also yes, initially I was adding the SL to the order, but was getting the same error, so tried adding it after.
Apologies all, I've just realised I've been a complete idiot.
My logic here:
int type = (entry > stop) ? OP_SELL : OP_BUY; is completely backwards and of course won't allow an entry to be submitted. Flipped them over and worked fine. It was such a daft error, I hadn't even considered it until I looked properly at the logs. Thanks for your time.
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
I'm trying to create a simple EA tool in MT4 so that I can drag and drop two lines on the chart, and based on input parameters it calculates lot size, and then when I press execute, it enters a limit order with the entry price and stop loss price.
As you can see I'm consistently getting the error 130 which suggests from MQL4 documentation that the SL is incorrect in someway. This makes no sense as I've logged the brokers rules and they expect 0.0 SL? I've also checked and tested the limit order being a multitude of distances away from current price and still no help.
I've tested several different ways, and have logged several different things in an attempt to narrow down the issue. I thought maybe it was a decimal issue but I see it is sending the correct .xx price based on the broker and market (US100.cash) I'm trying to trade.
#property strict #property indicator_chart_window input double RiskPerTradeUSD = 100.0; // Amount to risk per trade in USD input int Slippage = 3; string entryLine = "ENTRY_LINE"; string stopLine = "STOP_LINE"; string executeButton = "EXECUTE_TRADE"; bool tradeExecuted = false; //+------------------------------------------------------------------+ int OnInit() { int mSLS = (int)MarketInfo(Symbol(), MODE_STOPLEVEL); Print("[BROKER] Minimum StopLevel (points): ", mSLS); CreateLines(); CreateExecuteButton(); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { ObjectDelete(entryLine); ObjectDelete(stopLine); ObjectDelete(executeButton); Comment(""); } void OnTick() { if (tradeExecuted) return; double entry = ObjectGet(entryLine, OBJPROP_PRICE1); double stop = ObjectGet(stopLine, OBJPROP_PRICE1); double pointRisk = MathAbs(entry - stop); double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); if (pointRisk == 0 || tickValue == 0) return; double rawLot = (RiskPerTradeUSD / pointRisk) / (tickValue * 100); double lotSize = NormalizeDouble(MathFloor(rawLot / lotStep) * lotStep, 2); Comment("Entry: ", DoubleToString(entry, 2), "\nStop: ", DoubleToString(stop, 2), "\nLot Size: ", DoubleToString(lotSize, 2), "\nRisk (USD): ", RiskPerTradeUSD); } void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if (id == CHARTEVENT_OBJECT_CLICK && sparam == executeButton) { Print("[EXECUTE] Button clicked"); double entry = ObjectGet(entryLine, OBJPROP_PRICE1); double stop = ObjectGet(stopLine, OBJPROP_PRICE1); double pointRisk = MathAbs(entry - stop); double tickValue = MarketInfo(Symbol(), MODE_TICKVALUE); double lotStep = MarketInfo(Symbol(), MODE_LOTSTEP); if (pointRisk == 0 || tickValue == 0) return; double rawLot = (RiskPerTradeUSD / pointRisk) / (tickValue * 100); double lotSize = NormalizeDouble(MathFloor(rawLot / lotStep) * lotStep, 2); int type = (entry > stop) ? OP_SELLLIMIT : OP_BUYLIMIT; int digits = (int)MarketInfo(Symbol(), MODE_DIGITS); double price = NormalizeDouble(entry, digits); double sl = NormalizeDouble(stop, digits); Print("[CALC] Entry=", DoubleToString(entry, 2), ", Stop=", DoubleToString(stop, 2), ", PointRisk=", DoubleToString(pointRisk, 2)); Print("[CALC] TickValue=", tickValue, ", RawLot=", rawLot, ", FinalLot=", lotSize, ", RiskPerTradeUSD=", RiskPerTradeUSD); Print("[ORDER] Sending Limit Order: Symbol=", Symbol(), ", Type=", type, ", Lot=", lotSize, ", Price=", DoubleToString(price, digits), ", SL=", DoubleToString(sl, digits)); int stopLevelPoints = (int)MarketInfo(Symbol(), MODE_STOPLEVEL); double stopLevelPriceDistance = stopLevelPoints * Point; double currentPrice = (type == OP_BUYLIMIT) ? Ask : Bid; double priceDiff = MathAbs(price - currentPrice); if (priceDiff < stopLevelPriceDistance) { Print("[WARNING] Entry price too close to market. Required distance: ", DoubleToString(stopLevelPriceDistance, digits)); Print("[FIX] Adjusting price to be valid by broker rules..."); price = (type == OP_BUYLIMIT) ? currentPrice + stopLevelPriceDistance : currentPrice - stopLevelPriceDistance; price = NormalizeDouble(price, digits); } Print("[BROKER] StopLevel (points): ", stopLevelPoints); Print("[BROKER] CurrentPrice=", DoubleToString(currentPrice, digits), ", EntryPrice=", DoubleToString(price, digits), ", Distance=", DoubleToString(priceDiff, digits), ", Required Min=", DoubleToString(stopLevelPriceDistance, digits)); int ticket = OrderSend(Symbol(), type, lotSize, price, Slippage, 0, 0, "DragDropTrade", 0, 0, clrGreen); if (ticket > 0) { Print("Trade placed successfully! Ticket = ", ticket); // Attempt to modify order after placement (ECN-safe SL attachment) bool modified = OrderModify(ticket, price, sl, 0, 0, clrRed); if (!modified) Print("OrderModify failed! Error = ", GetLastError()); else Print("Stop loss added post-order with OrderModify()."); } if (ticket < 0) Print("OrderSend failed! Error = ", GetLastError()); tradeExecuted = true; } } void CreateLines() { double price = SymbolInfoDouble(Symbol(), SYMBOL_ASK); ObjectCreate(entryLine, OBJ_HLINE, 0, 0, price); ObjectSetInteger(0, entryLine, OBJPROP_COLOR, clrDeepSkyBlue); ObjectSetInteger(0, entryLine, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, entryLine, OBJPROP_WIDTH, 2); ObjectSetInteger(0, entryLine, OBJPROP_SELECTABLE, true); ObjectSetInteger(0, entryLine, OBJPROP_SELECTED, true); ObjectCreate(stopLine, OBJ_HLINE, 0, 0, price - 50 * Point); ObjectSetInteger(0, stopLine, OBJPROP_COLOR, clrRed); ObjectSetInteger(0, stopLine, OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, stopLine, OBJPROP_WIDTH, 2); ObjectSetInteger(0, stopLine, OBJPROP_SELECTABLE, true); ObjectSetInteger(0, stopLine, OBJPROP_SELECTED, true); } void CreateExecuteButton() { ObjectCreate(0, executeButton, OBJ_BUTTON, 0, 0, 0); ObjectSetInteger(0, executeButton, OBJPROP_CORNER, CORNER_LEFT_LOWER); ObjectSetInteger(0, executeButton, OBJPROP_XDISTANCE, 20); ObjectSetInteger(0, executeButton, OBJPROP_YDISTANCE, 40); ObjectSetInteger(0, executeButton, OBJPROP_XSIZE, 100); ObjectSetInteger(0, executeButton, OBJPROP_YSIZE, 24); ObjectSetString(0, executeButton, OBJPROP_TEXT, "EXECUTE"); ObjectSetString(0, executeButton, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, executeButton, OBJPROP_FONTSIZE, 12); ObjectSetInteger(0, executeButton, OBJPROP_COLOR, clrWhite); ObjectSetInteger(0, executeButton, OBJPROP_BGCOLOR, clrGreen); ObjectSetInteger(0, executeButton, OBJPROP_BORDER_COLOR, clrBlack); ObjectSetInteger(0, executeButton, OBJPROP_BACK, false); ObjectSetInteger(0, executeButton, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, executeButton, OBJPROP_SELECTED, false); ObjectSetInteger(0, executeButton, OBJPROP_HIDDEN, false); ObjectSetInteger(0, executeButton, OBJPROP_ZORDER, 0); }
Below are my most recent logged errors.