From dbdaf3122061a80f2556c3fb724c1eb70dbd32a8 Mon Sep 17 00:00:00 2001 From: biohazardxxx Date: Sat, 4 Jul 2026 00:00:01 +0200 Subject: [PATCH 1/5] MtApi5: add pending order command ids 320-325 Co-Authored-By: Claude Fable 5 --- MtApi5/MtProtocol/Mt5CommandType.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/MtApi5/MtProtocol/Mt5CommandType.cs b/MtApi5/MtProtocol/Mt5CommandType.cs index 4af91bdb..1edc5a70 100755 --- a/MtApi5/MtProtocol/Mt5CommandType.cs +++ b/MtApi5/MtProtocol/Mt5CommandType.cs @@ -260,6 +260,13 @@ internal enum Mt5CommandType OrderCheck = 303, Buy = 304, Sell = 305, - GetSymbols = 306 + GetSymbols = 306, + + BuyLimit = 320, + SellLimit = 321, + BuyStop = 322, + SellStop = 323, + OrderModify = 324, + OrderDelete = 325 } } From 8b5dacc9d77631fb9cc4da9115254bc510c02c7c Mon Sep 17 00:00:00 2001 From: biohazardxxx Date: Sat, 4 Jul 2026 00:00:01 +0200 Subject: [PATCH 2/5] MtApi5: add pending order client methods BuyLimit, SellLimit, BuyStop, SellStop, OrderModify and OrderDelete mirror the existing CTrade-based Buy/Sell methods; expiration is sent as unix seconds via Mt5TimeConverter. Co-Authored-By: Claude Fable 5 --- MtApi5/MtApi5Client.cs | 151 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/MtApi5/MtApi5Client.cs b/MtApi5/MtApi5Client.cs index b75bd2a0..613f2255 100755 --- a/MtApi5/MtApi5Client.cs +++ b/MtApi5/MtApi5Client.cs @@ -860,6 +860,157 @@ public bool Sell(out MqlTradeResult? result, double volume, string? symbol = nul result = response?.Result; return response != null && response.RetVal; } + + /// + /// Places a Buy Limit pending order (buy at a price lower than the current market price) with specified parameters + /// + /// output result + /// Requested order volume. + /// Order execution price. + /// Order symbol. If it is not specified, the current symbol will be used. + /// Stop Loss price. + /// Take Profit price. + /// Order expiration type. + /// Order expiration time (used with typeTime ORDER_TIME_SPECIFIED or ORDER_TIME_SPECIFIED_DAY). + /// Comment. + /// true - successful check of the structures, otherwise - false. + public bool BuyLimit(out MqlTradeResult? result, double volume, double price, string? symbol = null, double sl = 0.0, double tp = 0.0, ENUM_ORDER_TYPE_TIME typeTime = ENUM_ORDER_TYPE_TIME.ORDER_TIME_GTC, DateTime? expiration = null, string? comment = null) + { + Dictionary cmdParams = new() { { "Volume", volume }, { "Price", price }, { "Sl", sl }, { "Tp", tp }, + { "TypeTime", (int)typeTime }, { "Expiration", Mt5TimeConverter.ConvertToMtTime(expiration) } }; + if (symbol != null) + cmdParams["Symbol"] = symbol; + if (comment != null) + cmdParams["Comment"] = comment; + + var response = SendCommand>(ExecutorHandle, Mt5CommandType.BuyLimit, cmdParams); + + result = response?.Result; + return response != null && response.RetVal; + } + + /// + /// Places a Sell Limit pending order (sell at a price higher than the current market price) with specified parameters + /// + /// output result + /// Requested order volume. + /// Order execution price. + /// Order symbol. If it is not specified, the current symbol will be used. + /// Stop Loss price. + /// Take Profit price. + /// Order expiration type. + /// Order expiration time (used with typeTime ORDER_TIME_SPECIFIED or ORDER_TIME_SPECIFIED_DAY). + /// Comment. + /// true - successful check of the structures, otherwise - false. + public bool SellLimit(out MqlTradeResult? result, double volume, double price, string? symbol = null, double sl = 0.0, double tp = 0.0, ENUM_ORDER_TYPE_TIME typeTime = ENUM_ORDER_TYPE_TIME.ORDER_TIME_GTC, DateTime? expiration = null, string? comment = null) + { + Dictionary cmdParams = new() { { "Volume", volume }, { "Price", price }, { "Sl", sl }, { "Tp", tp }, + { "TypeTime", (int)typeTime }, { "Expiration", Mt5TimeConverter.ConvertToMtTime(expiration) } }; + if (symbol != null) + cmdParams["Symbol"] = symbol; + if (comment != null) + cmdParams["Comment"] = comment; + + var response = SendCommand>(ExecutorHandle, Mt5CommandType.SellLimit, cmdParams); + + result = response?.Result; + return response != null && response.RetVal; + } + + /// + /// Places a Buy Stop pending order (buy at a price higher than the current market price) with specified parameters + /// + /// output result + /// Requested order volume. + /// Order execution price. + /// Order symbol. If it is not specified, the current symbol will be used. + /// Stop Loss price. + /// Take Profit price. + /// Order expiration type. + /// Order expiration time (used with typeTime ORDER_TIME_SPECIFIED or ORDER_TIME_SPECIFIED_DAY). + /// Comment. + /// true - successful check of the structures, otherwise - false. + public bool BuyStop(out MqlTradeResult? result, double volume, double price, string? symbol = null, double sl = 0.0, double tp = 0.0, ENUM_ORDER_TYPE_TIME typeTime = ENUM_ORDER_TYPE_TIME.ORDER_TIME_GTC, DateTime? expiration = null, string? comment = null) + { + Dictionary cmdParams = new() { { "Volume", volume }, { "Price", price }, { "Sl", sl }, { "Tp", tp }, + { "TypeTime", (int)typeTime }, { "Expiration", Mt5TimeConverter.ConvertToMtTime(expiration) } }; + if (symbol != null) + cmdParams["Symbol"] = symbol; + if (comment != null) + cmdParams["Comment"] = comment; + + var response = SendCommand>(ExecutorHandle, Mt5CommandType.BuyStop, cmdParams); + + result = response?.Result; + return response != null && response.RetVal; + } + + /// + /// Places a Sell Stop pending order (sell at a price lower than the current market price) with specified parameters + /// + /// output result + /// Requested order volume. + /// Order execution price. + /// Order symbol. If it is not specified, the current symbol will be used. + /// Stop Loss price. + /// Take Profit price. + /// Order expiration type. + /// Order expiration time (used with typeTime ORDER_TIME_SPECIFIED or ORDER_TIME_SPECIFIED_DAY). + /// Comment. + /// true - successful check of the structures, otherwise - false. + public bool SellStop(out MqlTradeResult? result, double volume, double price, string? symbol = null, double sl = 0.0, double tp = 0.0, ENUM_ORDER_TYPE_TIME typeTime = ENUM_ORDER_TYPE_TIME.ORDER_TIME_GTC, DateTime? expiration = null, string? comment = null) + { + Dictionary cmdParams = new() { { "Volume", volume }, { "Price", price }, { "Sl", sl }, { "Tp", tp }, + { "TypeTime", (int)typeTime }, { "Expiration", Mt5TimeConverter.ConvertToMtTime(expiration) } }; + if (symbol != null) + cmdParams["Symbol"] = symbol; + if (comment != null) + cmdParams["Comment"] = comment; + + var response = SendCommand>(ExecutorHandle, Mt5CommandType.SellStop, cmdParams); + + result = response?.Result; + return response != null && response.RetVal; + } + + /// + /// Modifies the parameters of a previously placed pending order + /// + /// output result + /// Ticket of the pending order to be modified. + /// New order execution price. + /// New Stop Loss price. + /// New Take Profit price. + /// Order expiration type. + /// Order expiration time (used with typeTime ORDER_TIME_SPECIFIED or ORDER_TIME_SPECIFIED_DAY). + /// Limit order price for the StopLimit order. + /// true - successful check of the structures, otherwise - false. + public bool OrderModify(out MqlTradeResult? result, ulong ticket, double price, double sl, double tp, ENUM_ORDER_TYPE_TIME typeTime = ENUM_ORDER_TYPE_TIME.ORDER_TIME_GTC, DateTime? expiration = null, double stoplimit = 0.0) + { + Dictionary cmdParams = new() { { "Ticket", ticket }, { "Price", price }, { "Sl", sl }, { "Tp", tp }, + { "TypeTime", (int)typeTime }, { "Expiration", Mt5TimeConverter.ConvertToMtTime(expiration) }, { "Stoplimit", stoplimit } }; + + var response = SendCommand>(ExecutorHandle, Mt5CommandType.OrderModify, cmdParams); + + result = response?.Result; + return response != null && response.RetVal; + } + + /// + /// Removes a previously placed pending order + /// + /// output result + /// Ticket of the pending order to be deleted. + /// true - successful check of the structures, otherwise - false. + public bool OrderDelete(out MqlTradeResult? result, ulong ticket) + { + Dictionary cmdParams = new() { { "Ticket", ticket } }; + + var response = SendCommand>(ExecutorHandle, Mt5CommandType.OrderDelete, cmdParams); + + result = response?.Result; + return response != null && response.RetVal; + } #endregion #region Account Information functions From 4e1bf38dd1d80343ebb716088d6d49a269f09cc8 Mon Sep 17 00:00:00 2001 From: biohazardxxx Date: Sat, 4 Jul 2026 00:00:01 +0200 Subject: [PATCH 3/5] MQL5: add pending order command handlers and executor registration (320-325) Co-Authored-By: Claude Fable 5 --- mq5/MtApi5.mq5 | 214 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 212 insertions(+), 2 deletions(-) diff --git a/mq5/MtApi5.mq5 b/mq5/MtApi5.mq5 index 18c0b55e..eae61b4e 100644 --- a/mq5/MtApi5.mq5 +++ b/mq5/MtApi5.mq5 @@ -381,7 +381,14 @@ int preinit() ADD_EXECUTOR(304, Buy); ADD_EXECUTOR(305, Sell); ADD_EXECUTOR(306, GetSymbols); - + + ADD_EXECUTOR(320, BuyLimit); + ADD_EXECUTOR(321, SellLimit); + ADD_EXECUTOR(322, BuyStop); + ADD_EXECUTOR(323, SellStop); + ADD_EXECUTOR(324, OrderModify); + ADD_EXECUTOR(325, OrderDelete); + return (0); } @@ -3449,7 +3456,210 @@ string Execute_Sell() result_value_jo.put("RetVal", new JSONBool(ok)); result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); - return CreateSuccessResponse(result_value_jo); + return CreateSuccessResponse(result_value_jo); +} + +string Execute_BuyLimit() +{ + GET_JSON_PAYLOAD(jo); + GET_DOUBLE_JSON_VALUE(jo, "Volume", volume); + GET_DOUBLE_JSON_VALUE(jo, "Price", price); + GET_DOUBLE_JSON_VALUE(jo, "Sl", sl); + GET_DOUBLE_JSON_VALUE(jo, "Tp", tp); + GET_INT_JSON_VALUE(jo, "TypeTime", type_time); + GET_LONG_JSON_VALUE(jo, "Expiration", expiration); + + //Symbol + string symbol = Symbol(); + if (jo.p.getValue("Symbol") != NULL) + symbol = jo.p.getString("Symbol"); + + //Comment + string comment = ""; + if (jo.p.getValue("Comment") != NULL) + comment = jo.p.getString("Comment"); + +#ifdef __DEBUG_LOG__ + PrintFormat("%s: symbol = %s, volume = %f, price = %f, sl = %f, tp = %f, type_time = %d, expiration = %I64d, comment = %s", + __FUNCTION__, symbol, volume, price, sl, tp, type_time, expiration, comment); +#endif + + CTrade trade; + bool ok = trade.BuyLimit(volume, price, symbol, sl, tp, (ENUM_ORDER_TYPE_TIME)type_time, (datetime)expiration, comment); + + MqlTradeResult trade_result={0}; + trade.Result(trade_result); + + JSONObject* result_value_jo = new JSONObject(); + result_value_jo.put("RetVal", new JSONBool(ok)); + result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); + + return CreateSuccessResponse(result_value_jo); +} + +string Execute_SellLimit() +{ + GET_JSON_PAYLOAD(jo); + GET_DOUBLE_JSON_VALUE(jo, "Volume", volume); + GET_DOUBLE_JSON_VALUE(jo, "Price", price); + GET_DOUBLE_JSON_VALUE(jo, "Sl", sl); + GET_DOUBLE_JSON_VALUE(jo, "Tp", tp); + GET_INT_JSON_VALUE(jo, "TypeTime", type_time); + GET_LONG_JSON_VALUE(jo, "Expiration", expiration); + + //Symbol + string symbol = Symbol(); + if (jo.p.getValue("Symbol") != NULL) + symbol = jo.p.getString("Symbol"); + + //Comment + string comment = ""; + if (jo.p.getValue("Comment") != NULL) + comment = jo.p.getString("Comment"); + +#ifdef __DEBUG_LOG__ + PrintFormat("%s: symbol = %s, volume = %f, price = %f, sl = %f, tp = %f, type_time = %d, expiration = %I64d, comment = %s", + __FUNCTION__, symbol, volume, price, sl, tp, type_time, expiration, comment); +#endif + + CTrade trade; + bool ok = trade.SellLimit(volume, price, symbol, sl, tp, (ENUM_ORDER_TYPE_TIME)type_time, (datetime)expiration, comment); + + MqlTradeResult trade_result={0}; + trade.Result(trade_result); + + JSONObject* result_value_jo = new JSONObject(); + result_value_jo.put("RetVal", new JSONBool(ok)); + result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); + + return CreateSuccessResponse(result_value_jo); +} + +string Execute_BuyStop() +{ + GET_JSON_PAYLOAD(jo); + GET_DOUBLE_JSON_VALUE(jo, "Volume", volume); + GET_DOUBLE_JSON_VALUE(jo, "Price", price); + GET_DOUBLE_JSON_VALUE(jo, "Sl", sl); + GET_DOUBLE_JSON_VALUE(jo, "Tp", tp); + GET_INT_JSON_VALUE(jo, "TypeTime", type_time); + GET_LONG_JSON_VALUE(jo, "Expiration", expiration); + + //Symbol + string symbol = Symbol(); + if (jo.p.getValue("Symbol") != NULL) + symbol = jo.p.getString("Symbol"); + + //Comment + string comment = ""; + if (jo.p.getValue("Comment") != NULL) + comment = jo.p.getString("Comment"); + +#ifdef __DEBUG_LOG__ + PrintFormat("%s: symbol = %s, volume = %f, price = %f, sl = %f, tp = %f, type_time = %d, expiration = %I64d, comment = %s", + __FUNCTION__, symbol, volume, price, sl, tp, type_time, expiration, comment); +#endif + + CTrade trade; + bool ok = trade.BuyStop(volume, price, symbol, sl, tp, (ENUM_ORDER_TYPE_TIME)type_time, (datetime)expiration, comment); + + MqlTradeResult trade_result={0}; + trade.Result(trade_result); + + JSONObject* result_value_jo = new JSONObject(); + result_value_jo.put("RetVal", new JSONBool(ok)); + result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); + + return CreateSuccessResponse(result_value_jo); +} + +string Execute_SellStop() +{ + GET_JSON_PAYLOAD(jo); + GET_DOUBLE_JSON_VALUE(jo, "Volume", volume); + GET_DOUBLE_JSON_VALUE(jo, "Price", price); + GET_DOUBLE_JSON_VALUE(jo, "Sl", sl); + GET_DOUBLE_JSON_VALUE(jo, "Tp", tp); + GET_INT_JSON_VALUE(jo, "TypeTime", type_time); + GET_LONG_JSON_VALUE(jo, "Expiration", expiration); + + //Symbol + string symbol = Symbol(); + if (jo.p.getValue("Symbol") != NULL) + symbol = jo.p.getString("Symbol"); + + //Comment + string comment = ""; + if (jo.p.getValue("Comment") != NULL) + comment = jo.p.getString("Comment"); + +#ifdef __DEBUG_LOG__ + PrintFormat("%s: symbol = %s, volume = %f, price = %f, sl = %f, tp = %f, type_time = %d, expiration = %I64d, comment = %s", + __FUNCTION__, symbol, volume, price, sl, tp, type_time, expiration, comment); +#endif + + CTrade trade; + bool ok = trade.SellStop(volume, price, symbol, sl, tp, (ENUM_ORDER_TYPE_TIME)type_time, (datetime)expiration, comment); + + MqlTradeResult trade_result={0}; + trade.Result(trade_result); + + JSONObject* result_value_jo = new JSONObject(); + result_value_jo.put("RetVal", new JSONBool(ok)); + result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); + + return CreateSuccessResponse(result_value_jo); +} + +string Execute_OrderModify() +{ + GET_JSON_PAYLOAD(jo); + GET_ULONG_JSON_VALUE(jo, "Ticket", ticket); + GET_DOUBLE_JSON_VALUE(jo, "Price", price); + GET_DOUBLE_JSON_VALUE(jo, "Sl", sl); + GET_DOUBLE_JSON_VALUE(jo, "Tp", tp); + GET_INT_JSON_VALUE(jo, "TypeTime", type_time); + GET_LONG_JSON_VALUE(jo, "Expiration", expiration); + GET_DOUBLE_JSON_VALUE(jo, "Stoplimit", stoplimit); + +#ifdef __DEBUG_LOG__ + PrintFormat("%s: ticket = %I64u, price = %f, sl = %f, tp = %f, type_time = %d, expiration = %I64d, stoplimit = %f", + __FUNCTION__, ticket, price, sl, tp, type_time, expiration, stoplimit); +#endif + + CTrade trade; + bool ok = trade.OrderModify(ticket, price, sl, tp, (ENUM_ORDER_TYPE_TIME)type_time, (datetime)expiration, stoplimit); + + MqlTradeResult trade_result={0}; + trade.Result(trade_result); + + JSONObject* result_value_jo = new JSONObject(); + result_value_jo.put("RetVal", new JSONBool(ok)); + result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); + + return CreateSuccessResponse(result_value_jo); +} + +string Execute_OrderDelete() +{ + GET_JSON_PAYLOAD(jo); + GET_ULONG_JSON_VALUE(jo, "Ticket", ticket); + +#ifdef __DEBUG_LOG__ + PrintFormat("%s: ticket = %I64u", __FUNCTION__, ticket); +#endif + + CTrade trade; + bool ok = trade.OrderDelete(ticket); + + MqlTradeResult trade_result={0}; + trade.Result(trade_result); + + JSONObject* result_value_jo = new JSONObject(); + result_value_jo.put("RetVal", new JSONBool(ok)); + result_value_jo.put("Result", MqlTradeResultToJson(trade_result)); + + return CreateSuccessResponse(result_value_jo); } string Execute_GetSymbols() From ec2b1a1dd8413c2f9940854333f7297708ea2ccb Mon Sep 17 00:00:00 2001 From: biohazardxxx Date: Sat, 4 Jul 2026 00:00:01 +0200 Subject: [PATCH 4/5] MtApi5TestClient: add manual BuyLimit and OrderDelete test actions Co-Authored-By: Claude Fable 5 --- TestClients/MtApi5TestClient/MainWindow.xaml | 2 ++ TestClients/MtApi5TestClient/ViewModel.cs | 33 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/TestClients/MtApi5TestClient/MainWindow.xaml b/TestClients/MtApi5TestClient/MainWindow.xaml index c30fe43e..32bb5a04 100755 --- a/TestClients/MtApi5TestClient/MainWindow.xaml +++ b/TestClients/MtApi5TestClient/MainWindow.xaml @@ -529,6 +529,8 @@