366 lines
13 KiB
MQL5
366 lines
13 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Sendel-one.mq5 |
|
|
//| Copyright 2022, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Dikke banaan |
|
|
//+------------------------------------------------------------------+
|
|
class Utils
|
|
{
|
|
public:
|
|
ENUM_ORDER_TYPE GetCurrentTrend();
|
|
void OpenNewPosition(ENUM_ORDER_TYPE type, int NumberOfDoublings, double MartingaleMultiplierValue, double TargetProfit);
|
|
void Martingale(ENUM_ORDER_TYPE type, double MartingaleMultiplierValue, double TargetProfit);
|
|
bool ShouldMartingale(ENUM_ORDER_TYPE type, int TradeExpireTime);
|
|
int OpenPositionsCount(ENUM_ORDER_TYPE type);
|
|
|
|
private:
|
|
void OpenNewPosition(ENUM_ORDER_TYPE type, double LotSize, double TargetProfit);
|
|
void ModifyAllTargetPrices(ENUM_ORDER_TYPE type, double TargetProfit);
|
|
ulong GetLatestTicket(ENUM_ORDER_TYPE type);
|
|
double BreakEvenPriceOpenOrders(ENUM_ORDER_TYPE type);
|
|
|
|
double CalculateBeginLotSize(int NumberOfDoublings, double MartingaleMultiplier);
|
|
bool typesMatch(ENUM_ORDER_TYPE type1, ENUM_POSITION_TYPE type2);
|
|
ENUM_ORDER_TYPE CalculateTrend();
|
|
};
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Where all the magic happens, should we do a buy or sell order
|
|
//+------------------------------------------------------------------+
|
|
ENUM_ORDER_TYPE Utils::GetCurrentTrend()
|
|
{
|
|
return CalculateTrend();
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void Utils::OpenNewPosition(ENUM_ORDER_TYPE type, int NumberOfDoublings, double MartingaleMultiplierValue, double TargetProfit)
|
|
{
|
|
double LotSize = CalculateBeginLotSize(NumberOfDoublings, MartingaleMultiplierValue);
|
|
OpenNewPosition(type, LotSize, TargetProfit);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
void Utils::OpenNewPosition(ENUM_ORDER_TYPE type, double LotSize, double TargetProfit)
|
|
{
|
|
|
|
//MQL5 structures
|
|
MqlTick latest_price;
|
|
MqlTradeRequest mrequest;
|
|
MqlTradeResult mresult;
|
|
MqlTradeCheckResult mcheck;
|
|
MqlRates mrate[];
|
|
|
|
ZeroMemory(latest_price);
|
|
ZeroMemory(mrequest);
|
|
ZeroMemory(mresult);
|
|
ZeroMemory(mcheck);
|
|
|
|
if(LotSize == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
mrequest.action=TRADE_ACTION_DEAL; // immediate order execution
|
|
mrequest.price = NormalizeDouble(latest_price.ask,Digits()); // latest Bid price
|
|
mrequest.symbol = _Symbol; // currency pair
|
|
mrequest.volume = LotSize; // number of lots to trade
|
|
mrequest.magic = 12345; // Order Magic Number
|
|
mrequest.type = type; // Sell Order or buy order
|
|
mrequest.type_filling = ORDER_FILLING_IOC; // Order execution type
|
|
mrequest.deviation=1000; // Deviation from current price
|
|
|
|
// check if we have enough funds
|
|
if (OrderCheck(mrequest,mcheck))
|
|
{
|
|
//--- send order
|
|
if(OrderSend(mrequest,mresult))
|
|
{
|
|
Alert("A ", (string)type,"order has been successfully placed with Ticket#:",mresult.order,"!!");
|
|
// modify target price
|
|
ModifyAllTargetPrices(type, TargetProfit);
|
|
}
|
|
else
|
|
{
|
|
|
|
Alert("The ", (string)type, " order request could not be completed -error:", GetLastError());
|
|
ResetLastError();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void Utils::Martingale(ENUM_ORDER_TYPE type, double MartingaleMultiplierValue, double TargetProfit)
|
|
{
|
|
ulong LastTicket = GetLatestTicket(type);
|
|
|
|
if(LastTicket == 0)
|
|
{
|
|
return; // no ticket found
|
|
}
|
|
|
|
PositionSelectByTicket(LastTicket); // copies the ticket in memory
|
|
double Lotsize = PositionGetDouble(POSITION_VOLUME);
|
|
double Price = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double CurrentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
|
|
|
|
double NewLotSize = Lotsize;
|
|
if(OpenPositionsCount(type) > 1)
|
|
{
|
|
// first trade should have same lot size, second should use martingale
|
|
NewLotSize = NormalizeDouble(Lotsize * MartingaleMultiplierValue, 2);
|
|
}
|
|
|
|
double deviation = MathAbs(CurrentPrice - Price);
|
|
|
|
if(deviation > 0.001)
|
|
{
|
|
OpenNewPosition(type, NewLotSize, TargetProfit);
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool Utils::ShouldMartingale(ENUM_ORDER_TYPE type, int TradeExpireTime)
|
|
{
|
|
|
|
ulong Ticket = GetLatestTicket(type);
|
|
|
|
if(Ticket == 0) // no last sell ticket found
|
|
{
|
|
return false;
|
|
}
|
|
|
|
PositionSelectByTicket(Ticket); // copies the ticket in memory
|
|
int SecondsDifference=(int)(TimeCurrent() - PositionGetInteger(POSITION_TIME_UPDATE));
|
|
return SecondsDifference > TradeExpireTime;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void Utils::ModifyAllTargetPrices(ENUM_ORDER_TYPE type, double targetProfit)
|
|
{
|
|
|
|
double NewTP = 0;
|
|
double NewSL = 0;
|
|
|
|
if(type == ORDER_TYPE_BUY)
|
|
{
|
|
NewTP = NormalizeDouble(BreakEvenPriceOpenOrders(type) + targetProfit * _Point,_Digits);
|
|
}
|
|
else
|
|
{
|
|
NewTP = NormalizeDouble(BreakEvenPriceOpenOrders(type) - targetProfit * _Point,_Digits);
|
|
}
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket>0)
|
|
{
|
|
PositionSelectByTicket(ticket); // copies the ticket in memory
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if(typesMatch(type, posType))
|
|
{
|
|
//set target price of completed order
|
|
MqlTradeRequest modifyRequest;
|
|
MqlTradeResult modifyRequestResult;
|
|
|
|
ZeroMemory(modifyRequest);
|
|
ZeroMemory(modifyRequestResult);
|
|
|
|
modifyRequest.action = TRADE_ACTION_SLTP;
|
|
modifyRequest.position = ticket;
|
|
modifyRequest.symbol = _Symbol;
|
|
modifyRequest.tp = NormalizeDouble(NewTP,_Digits);
|
|
|
|
if(OrderSend(modifyRequest,modifyRequestResult))
|
|
{
|
|
Alert("A ",type," order has been successfully modified with Ticket#:",ticket, " and target price:",NewTP);
|
|
}
|
|
else
|
|
{
|
|
Alert("The ",type," order request could not be completed -error:", GetLastError());
|
|
ResetLastError();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
ulong Utils::GetLatestTicket(ENUM_ORDER_TYPE type)
|
|
{
|
|
|
|
datetime LastTicketTime;
|
|
ZeroMemory(LastTicketTime);
|
|
|
|
ulong LastTicket = 0;
|
|
|
|
// check if there are any open trades, if so we set a "shouldMartingaleOpenTrades" boolean
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket>0)
|
|
{
|
|
PositionSelectByTicket(ticket); // copies the ticket in memory
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if(typesMatch(type, posType))
|
|
{
|
|
datetime time = (datetime) PositionGetInteger(POSITION_TIME);
|
|
if(LastTicketTime == NULL || PositionGetInteger(POSITION_TIME) > LastTicketTime)
|
|
{
|
|
LastTicketTime = time;
|
|
LastTicket = ticket;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return LastTicket;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double Utils::BreakEvenPriceOpenOrders(ENUM_ORDER_TYPE type)
|
|
{
|
|
//----
|
|
double Lots = 0.0;
|
|
double LotsPrice = 0.0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket>0)
|
|
{
|
|
PositionSelectByTicket(ticket); // copies the ticket in memory
|
|
if(PositionGetInteger(POSITION_TYPE) == type)
|
|
{
|
|
Lots = Lots + PositionGetDouble(POSITION_VOLUME);
|
|
LotsPrice = LotsPrice + (PositionGetDouble(POSITION_PRICE_OPEN) * PositionGetDouble(POSITION_VOLUME));
|
|
}
|
|
}
|
|
}
|
|
double average = NormalizeDouble(LotsPrice/Lots, _Digits);
|
|
return(average);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
int Utils::OpenPositionsCount(ENUM_ORDER_TYPE type)
|
|
{
|
|
int Count = 0;
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
ulong ticket = PositionGetTicket(i);
|
|
if(ticket>0)
|
|
{
|
|
PositionSelectByTicket(ticket); // copies the ticket in memory
|
|
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
|
if(typesMatch(type, posType))
|
|
{
|
|
Count = Count + 1;
|
|
}
|
|
}
|
|
}
|
|
return Count;
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
double Utils::CalculateBeginLotSize(int NumberOfDoublings, double MartingaleMultiplierValue)
|
|
{
|
|
|
|
double MaxDrawdownPercent = 20.0;
|
|
long Equity = (long) AccountInfoDouble(ACCOUNT_EQUITY);
|
|
long Leverage = (long) AccountInfoInteger(ACCOUNT_LEVERAGE);
|
|
double CorrectedEquity = (Equity - (Equity * (MaxDrawdownPercent / 100))) * Leverage; // include max drawdown
|
|
double CurrentSumbolPrice = SymbolInfoDouble(_Symbol,SYMBOL_BID);
|
|
|
|
if(CurrentSumbolPrice == 0.0)
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
double CompleteLotPrice = CurrentSumbolPrice * 100000;
|
|
double MaxPurchasableLot = CorrectedEquity / CompleteLotPrice;
|
|
|
|
double BeginLotSize = (MaxPurchasableLot / MathPow(MartingaleMultiplierValue, NumberOfDoublings)) / 2;
|
|
|
|
return NormalizeDouble(BeginLotSize, 2);
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool Utils::typesMatch(ENUM_ORDER_TYPE type1, ENUM_POSITION_TYPE type2)
|
|
{
|
|
|
|
if(type1 == ORDER_TYPE_BUY && type2 == POSITION_TYPE_BUY)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(type1 == ORDER_TYPE_SELL && type2 == POSITION_TYPE_SELL)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
// look at the last 10 bars en check the trend
|
|
ENUM_ORDER_TYPE Utils::CalculateTrend()
|
|
{
|
|
|
|
//double LowRecent = iClose(_Symbol,PERIOD_D1, 1);
|
|
//double LowPast = iClose(_Symbol, PERIOD_D1, 20);
|
|
|
|
//if(LowRecent < LowPast)
|
|
//{
|
|
return ORDER_TYPE_SELL;
|
|
//}
|
|
//else
|
|
//{
|
|
// ORDER_TYPE_BUY;
|
|
//}
|
|
}
|
|
//+------------------------------------------------------------------+
|