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