forked from airat77786/MQL5Book
288 lines
13 KiB
MQL5
288 lines
13 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| PendingOrderModify.mq5 |
| |||
//| Copyright 2022, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#property copyright "2022, MetaQuotes Ltd."
| |||
#property link "https://www.mql5.com"
| |||
#property description "Construct MqlTradeRequest for specified pending order type and call OrderSend with TRADE_ACTION_PENDING."
| |||
#property description "Modify the order by TRADE_ACTION_MODIFY on every new day with Stop Loss and Take Profit autodetected from previous day range."
| |||
| |||
#define SHOW_WARNINGS // output extended info into the log
| |||
#define WARNING Print // use simple Print for warnings (instead of a built-in format with line numbers etc.)
| |||
| |||
// uncomment the following line to prevent early returns after
| |||
// failed checkups in MqlStructRequestSync methods, so
| |||
// incorrect requests will be sent and retcodes received from the server
| |||
// #define RETURN(X)
| |||
| |||
#include "..\..\Include\MqlTradeSync.mqh"
| |||
| |||
enum ENUM_ORDER_TYPE_PENDING
| |||
{ // Captions in UI
| |||
PENDING_BUY_STOP = ORDER_TYPE_BUY_STOP, // ORDER_TYPE_BUY_STOP
| |||
PENDING_SELL_STOP = ORDER_TYPE_SELL_STOP, // ORDER_TYPE_SELL_STOP
| |||
PENDING_BUY_LIMIT = ORDER_TYPE_BUY_LIMIT, // ORDER_TYPE_BUY_LIMIT
| |||
PENDING_SELL_LIMIT = ORDER_TYPE_SELL_LIMIT, // ORDER_TYPE_SELL_LIMIT
| |||
PENDING_BUY_STOP_LIMIT = ORDER_TYPE_BUY_STOP_LIMIT, // ORDER_TYPE_BUY_STOP_LIMIT
| |||
PENDING_SELL_STOP_LIMIT = ORDER_TYPE_SELL_STOP_LIMIT, // ORDER_TYPE_SELL_STOP_LIMIT
| |||
};
| |||
| |||
input string Symbol; // Symbol (empty = current _Symbol)
| |||
input double Volume; // Volume (0 = minimal lot)
| |||
input ENUM_ORDER_TYPE_PENDING Type = PENDING_BUY_STOP;
| |||
input ENUM_ORDER_TYPE_TIME Expiration = ORDER_TIME_GTC;
| |||
input datetime Until = 0;
| |||
input ulong Magic = 1234567890;
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Expert initialization function |
| |||
//+------------------------------------------------------------------+
| |||
int OnInit()
| |||
{
| |||
if(AccountInfoInteger(ACCOUNT_TRADE_MODE) != ACCOUNT_TRADE_MODE_DEMO)
| |||
{
| |||
Alert("This is a test EA! Run it on a DEMO account only!");
| |||
return INIT_FAILED;
| |||
}
| |||
return INIT_SUCCEEDED;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Helper function to find a compatible order |
| |||
//+------------------------------------------------------------------+
| |||
ulong GetMyOrder(const string name, const ulong magic)
| |||
{
| |||
for(int i = 0; i < OrdersTotal(); ++i)
| |||
{
| |||
ulong t = OrderGetTicket(i);
| |||
if(OrderGetInteger(ORDER_MAGIC) == magic
| |||
&& OrderGetString(ORDER_SYMBOL) == name)
| |||
{
| |||
return t;
| |||
}
| |||
}
| |||
| |||
return 0;
| |||
}
| |||
| |||
// distance for orders of different types from current market price,
| |||
// indices are ENUM_ORDER_TYPE, values to multiply by daily range
| |||
static double Coefficients[] =
| |||
{
| |||
0 , // ORDER_TYPE_BUY - not used
| |||
0 , // ORDER_TYPE_SELL - not used
| |||
-0.5, // ORDER_TYPE_BUY_LIMIT - below price
| |||
+0.5, // ORDER_TYPE_SELL_LIMIT - above price
| |||
+1.0, // ORDER_TYPE_BUY_STOP - far above price
| |||
-1.0, // ORDER_TYPE_SELL_STOP - far below price
| |||
+0.7, // ORDER_TYPE_BUY_STOP_LIMIT - middle price above current
| |||
-0.7, // ORDER_TYPE_SELL_STOP_LIMIT - middle price below current
| |||
0 , // ORDER_TYPE_CLOSE_BY - not used
| |||
};
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Prepare MqlTradeRequest struct and call OrderSend with it |
| |||
//| to place a pending order of specific type and properties |
| |||
//+------------------------------------------------------------------+
| |||
uint PlaceOrder(const ENUM_ORDER_TYPE type,
| |||
const string symbol, const double lot, const double range,
| |||
ENUM_ORDER_TYPE_TIME expiration, datetime until,
| |||
const ulong magic = 0)
| |||
{
| |||
// default values
| |||
const double volume = lot == 0 ? SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN) : lot;
| |||
const double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
| |||
| |||
// price-related values
| |||
const double price = TU::GetCurrentPrice(type, symbol) + range * Coefficients[type];
| |||
// origin is filled only for *_STOP_LIMIT-orders
| |||
const bool stopLimit = type == ORDER_TYPE_BUY_STOP_LIMIT || type == ORDER_TYPE_SELL_STOP_LIMIT;
| |||
const double origin = stopLimit ? TU::GetCurrentPrice(type, symbol) : 0;
| |||
| |||
TU::TradeDirection dir(type);
| |||
const int sltp = (int)(range / 2 / point);
| |||
| |||
const double stop = sltp == 0 ? 0 : dir.negative(stopLimit ? origin : price, sltp * point);
| |||
const double take = sltp == 0 ? 0 : dir.positive(stopLimit ? origin : price, sltp * point);
| |||
| |||
MqlTradeRequestSync request(symbol);
| |||
| |||
// fill optional fields
| |||
request.magic = magic;
| |||
| |||
ResetLastError();
| |||
// fill and check relevant fields, send request
| |||
ulong order = 0;
| |||
switch(type)
| |||
{
| |||
case ORDER_TYPE_BUY_STOP:
| |||
order = request.buyStop(volume, price, stop, take, expiration, until);
| |||
break;
| |||
case ORDER_TYPE_SELL_STOP:
| |||
order = request.sellStop(volume, price, stop, take, expiration, until);
| |||
break;
| |||
case ORDER_TYPE_BUY_LIMIT:
| |||
order = request.buyLimit(volume, price, stop, take, expiration, until);
| |||
break;
| |||
case ORDER_TYPE_SELL_LIMIT:
| |||
order = request.sellLimit(volume, price, stop, take, expiration, until);
| |||
break;
| |||
case ORDER_TYPE_BUY_STOP_LIMIT:
| |||
order = request.buyStopLimit(volume, price, origin, stop, take, expiration, until);
| |||
break;
| |||
case ORDER_TYPE_SELL_STOP_LIMIT:
| |||
order = request.sellStopLimit(volume, price, origin, stop, take, expiration, until);
| |||
break;
| |||
}
| |||
| |||
if(order != 0 && request.completed())
| |||
{
| |||
Print("OK order placed: #=", order);
| |||
}
| |||
Print(TU::StringOf(request));
| |||
Print(TU::StringOf(request.result));
| |||
return request.result.retcode;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Prepare MqlTradeRequest struct and call OrderSend with it |
| |||
//| to place a pending order of specific type and properties |
| |||
//+------------------------------------------------------------------+
| |||
uint ModifyOrder(const ulong ticket, const double range,
| |||
ENUM_ORDER_TYPE_TIME expiration, datetime until)
| |||
{
| |||
// default values
| |||
const string symbol = OrderGetString(ORDER_SYMBOL);
| |||
const double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
| |||
| |||
// price-related values
| |||
const ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE);
| |||
const double price = TU::GetCurrentPrice(type, symbol) + range * Coefficients[type];
| |||
// origin is filled only for *_STOP_LIMIT-orders
| |||
const bool stopLimit = type == ORDER_TYPE_BUY_STOP_LIMIT || type == ORDER_TYPE_SELL_STOP_LIMIT;
| |||
const double origin = stopLimit ? TU::GetCurrentPrice(type, symbol) : 0;
| |||
| |||
TU::TradeDirection dir(type);
| |||
const int sltp = (int)(range / 2 / point);
| |||
const double stop = sltp == 0 ? 0 : dir.negative(stopLimit ? origin : price, sltp * point);
| |||
const double take = sltp == 0 ? 0 : dir.positive(stopLimit ? origin : price, sltp * point);
| |||
| |||
MqlTradeRequestSync request(symbol);
| |||
| |||
/*
| |||
// uncomment this fragment to alter filling mode in a round robin manner
| |||
ENUM_ORDER_TYPE_FILLING filling =
| |||
(ENUM_ORDER_TYPE_FILLING)((OrderGetInteger(ORDER_TYPE_FILLING) + 1) % 3);
| |||
request.type_filling = filling;
| |||
// NB: make sure all filling modes are allowed for selected symbol
| |||
*/
| |||
| |||
ResetLastError();
| |||
// fill and check relevant fields, send request
| |||
if(request.modify(ticket, price, stop, take, expiration, until, origin)
| |||
&& request.completed())
| |||
{
| |||
Print("OK order modified: #=", ticket);
| |||
}
| |||
| |||
Print(TU::StringOf(request));
| |||
Print(TU::StringOf(request.result));
| |||
return request.result.retcode;
| |||
}
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Tick event handler |
| |||
//+------------------------------------------------------------------+
| |||
void OnTick()
| |||
{
| |||
// run once on a new day
| |||
static datetime lastDay = 0;
| |||
static const uint DAYLONG = 60 * 60 * 24; // seconds per day (24 hours)
| |||
if(TimeTradeServer() / DAYLONG * DAYLONG == lastDay) return;
| |||
| |||
// noncritical error handler by autoadjusted timeout
| |||
const static int DEFAULT_RETRY_TIMEOUT = 1; // seconds
| |||
static int RetryFrequency = DEFAULT_RETRY_TIMEOUT;
| |||
static datetime RetryRecordTime = 0;
| |||
if(TimeTradeServer() - RetryRecordTime < RetryFrequency) return;
| |||
| |||
const string symbol = StringLen(Symbol) == 0 ? _Symbol : Symbol;
| |||
const double range = iHigh(symbol, PERIOD_D1, 1) - iLow(symbol, PERIOD_D1, 1);
| |||
Print("Autodetected daily range: ", (float)range);
| |||
| |||
uint retcode = 0;
| |||
ulong ticket = GetMyOrder(symbol, Magic);
| |||
if(!ticket)
| |||
{
| |||
retcode = PlaceOrder((ENUM_ORDER_TYPE)Type, symbol, Volume,
| |||
range, Expiration, Until, Magic);
| |||
}
| |||
else
| |||
{
| |||
retcode = ModifyOrder(ticket, range, Expiration, Until);
| |||
}
| |||
const TRADE_RETCODE_SEVERITY severity = TradeCodeSeverity(retcode);
| |||
if(severity >= SEVERITY_INVALID)
| |||
{
| |||
Alert("Can't place/modify pending order, EA is stopped");
| |||
RetryFrequency = INT_MAX;
| |||
}
| |||
else if(severity >= SEVERITY_RETRY)
| |||
{
| |||
RetryFrequency += (int)sqrt(RetryFrequency + 1);
| |||
RetryRecordTime = TimeTradeServer();
| |||
PrintFormat("Problems detected, waiting for better conditions (timeout enlarged to %d seconds)",
| |||
RetryFrequency);
| |||
}
| |||
else
| |||
{
| |||
if(RetryFrequency > DEFAULT_RETRY_TIMEOUT)
| |||
{
| |||
RetryFrequency = DEFAULT_RETRY_TIMEOUT;
| |||
PrintFormat("Timeout restored to %d second", RetryFrequency);
| |||
}
| |||
lastDay = TimeTradeServer() / DAYLONG * DAYLONG;
| |||
}
| |||
}
| |||
//+------------------------------------------------------------------+
| |||
/*
| |||
example output (XAUUSD, default settings):
| |||
| |||
2022.01.03 01:05:00 Autodetected daily range: 14.37
| |||
2022.01.03 01:05:00 buy stop 0.01 XAUUSD at 1845.73 sl: 1838.55 tp: 1852.91 (1830.63 / 1831.36)
| |||
2022.01.03 01:05:00 OK order placed: #=2
| |||
2022.01.03 01:05:00 TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1845.73, SL=1838.55, TP=1852.91, ORDER_TIME_GTC, M=1234567890
| |||
2022.01.03 01:05:00 DONE, #=2, V=0.01, Bid=1830.63, Ask=1831.36, Request executed
| |||
2022.01.04 01:05:00 Autodetected daily range: 33.5
| |||
2022.01.04 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1836.56]
| |||
2022.01.04 01:05:00 OK order modified: #=2
| |||
2022.01.04 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1836.56, SL=1819.81, TP=1853.31, ORDER_TIME_GTC, #=2
| |||
2022.01.04 01:05:00 DONE, #=2, @ 1836.56, Bid=1819.81, Ask=1853.31, Request executed, Req=1
| |||
2022.01.05 01:05:00 Autodetected daily range: 18.23
| |||
2022.01.05 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1832.56]
| |||
2022.01.05 01:05:00 OK order modified: #=2
| |||
2022.01.05 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1832.56, SL=1823.45, TP=1841.67, ORDER_TIME_GTC, #=2
| |||
2022.01.05 01:05:00 DONE, #=2, @ 1832.56, Bid=1823.45, Ask=1841.67, Request executed, Req=2
| |||
...
| |||
2022.01.11 01:05:00 Autodetected daily range: 11.96
| |||
2022.01.11 01:05:00 order modified [#2 buy stop 0.01 XAUUSD at 1812.91]
| |||
2022.01.11 01:05:00 OK order modified: #=2
| |||
2022.01.11 01:05:00 TRADE_ACTION_MODIFY, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1812.91, SL=1806.93, TP=1818.89, ORDER_TIME_GTC, #=2
| |||
2022.01.11 01:05:00 DONE, #=2, @ 1812.91, Bid=1806.93, Ask=1818.89, Request executed, Req=6
| |||
2022.01.11 18:10:58 order [#2 buy stop 0.01 XAUUSD at 1812.91] triggered
| |||
2022.01.11 18:10:58 deal #2 buy 0.01 XAUUSD at 1812.91 done (based on order #2)
| |||
2022.01.11 18:10:58 deal performed [#2 buy 0.01 XAUUSD at 1812.91]
| |||
2022.01.11 18:10:58 order performed buy 0.01 at 1812.91 [#2 buy stop 0.01 XAUUSD at 1812.91]
| |||
2022.01.11 20:28:59 take profit triggered #2 buy 0.01 XAUUSD 1812.91 sl: 1806.93 tp: 1818.89 [#3 sell 0.01 XAUUSD at 1818.89]
| |||
2022.01.11 20:28:59 deal #3 sell 0.01 XAUUSD at 1818.91 done (based on order #3)
| |||
2022.01.11 20:28:59 deal performed [#3 sell 0.01 XAUUSD at 1818.91]
| |||
2022.01.11 20:28:59 order performed sell 0.01 at 1818.91 [#3 sell 0.01 XAUUSD at 1818.89]
| |||
2022.01.12 01:05:00 Autodetected daily range: 23.28
| |||
2022.01.12 01:05:00 buy stop 0.01 XAUUSD at 1843.77 sl: 1832.14 tp: 1855.40 (1820.14 / 1820.49)
| |||
2022.01.12 01:05:00 OK order placed: #=4
| |||
2022.01.12 01:05:00 TRADE_ACTION_PENDING, XAUUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1843.77, SL=1832.14, TP=1855.40, ORDER_TIME_GTC, M=1234567890
| |||
2022.01.12 01:05:00 DONE, #=4, V=0.01, Bid=1820.14, Ask=1820.49, Request executed, Req=7
| |||
| |||
*/
| |||
//+------------------------------------------------------------------+
|