Apollo-One/Utils.mqh
super.admin c94b358fb0 convert
2025-05-30 14:40:22 +02:00

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;
//}
}
//+------------------------------------------------------------------+