//+------------------------------------------------------------------+ //| IndianTrailingStopLoss.mq5 | //| Jay Davis | //| 512jay.github.io | //+------------------------------------------------------------------+ #property copyright "Jay Davis" #property link "512jay.github.io" #property version "3.18" // Add the first trade details on ITSL like BUY, PRICE and STOP LOSS enum TradeDirection { Buy, Sell, BuyLimit, // BuyLimit Pending buy with entry below current price SellLimit, // SellLimit Pending sell with entry above current price BuyStop, // BuyStop Pending buy with entry above current price SellStop // SellStop Pending sell with entry below current price }; enum EA_MODE { Normal, // Normal Reverse_Entries, // Reverse Entries Reverse_Signal // Reverse Signal (Fool's Gold) }; input group "|---------- General Settings -----------|" input EA_MODE Mode = Normal; #include "../GrayMatrixLibraries/Virtualization.mqh" #include "../GrayMatrixLibraries/Trade.mqh" input string DatabaseName = "DATABASE"; //\AppData\Roaming\MetaQuotes\Terminal\Common\DATABASE input long MagicNumber = 7775; // Magic Number input TradeDirection InitialDirection = Buy; // Initial Direction to Trade input double Price = 0; // At Price input double Stoploss = 0; // Inital StopLoss extern ENUM_ORDER_TYPE DirectionToTrade = (ENUM_ORDER_TYPE) InitialDirection; input string InpSym = NULL; // Charting Symbol input double BuyInitialLotSize = 0.01; // BUY Initial Lot Size input double SellInitialLotSize = 0.01; // SELL Initial Lot Size #include "SLM.mqh" #include "AddOnOrdersManagement.mqh" #include "../GrayMatrixLibraries/WhatsAppAPI.mqh" GrayMatrixTrade trade; Virtual Virt[]; double MostRecentOpenedPrice; int IdleMovements = 1, SeriesOrderNumber = 0; datetime TimeStopLossWasLastModified; double MinMovement = MinimumPointsForStopLossMove * _Point; string Sym; bool InitialOrderTaken; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { InitialOrderTaken = false; Print("********* Starting... ", __FILE__, " ********************************* "); Print("Virt Size = ", ArraySize(Virt)); if(ErrorsWithInputs()) return(INIT_PARAMETERS_INCORRECT); CreateDataBase(DatabaseName, false); InitializeVirtualObjectArray(Virt); Sym = _Symbol; string symbols[]; ArrayResize(symbols, 1); symbols[0] = Sym; FillVirtualOrderArray(Virt, symbols); trade.SetExpertMagicNumber(MagicNumber); trade.LogLevel(LOG_LEVEL_ALL); if(MQLInfoInteger(MQL_TESTER)) trade.LogLevel(LOG_LEVEL_NO); CheckForInitialOrder(); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0); Comment(""); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { if(!CheckForInitialOrder()) return; int size = ArraySize(Virt); //Print("Virt Size = ", size); if(NoOpenVirtualOrders(Virt)) { PlaceOrder(Opposite(Virt[size - 1].request.type)); } //Dir = Opposite(Dir); ModifyStopLoss(); CheckStops(); DisplayLines(Virt[FirstActive(Virt, Sym)]); Comment(CurrentTradeTradeTypeStopLoss(Virt), " Cur Dir: ", EnumToString(Virt[size - 1].request.type)); } //+------------------------------------------------------------------+ //| True if initial order has been taken if not it tries to take it | //+------------------------------------------------------------------+ bool CheckForInitialOrder() { if(InitialOrderTaken || UpdateFromResumeTableAtStart) return true; ENUM_ORDER_TYPE direction = (ENUM_ORDER_TYPE) InitialDirection; if(Price == 0 && Stoploss == 0) if(PlaceOrder(direction)) InitialOrderTaken = true; if(direction == ORDER_TYPE_BUY_LIMIT && SymbolInfoDouble(_Symbol, SYMBOL_ASK) <= Price) if(PlaceOrder(ORDER_TYPE_BUY)) InitialOrderTaken = true; if(direction == ORDER_TYPE_SELL_LIMIT && SymbolInfoDouble(_Symbol, SYMBOL_BID) >= Price) if(PlaceOrder(ORDER_TYPE_SELL)) InitialOrderTaken = true; if(direction == ORDER_TYPE_BUY_STOP && SymbolInfoDouble(_Symbol, SYMBOL_ASK) >= Price) if(PlaceOrder(ORDER_TYPE_BUY)) InitialOrderTaken = true; if(direction == ORDER_TYPE_SELL_STOP && SymbolInfoDouble(_Symbol, SYMBOL_BID) <= Price) if(PlaceOrder(ORDER_TYPE_SELL)) InitialOrderTaken = true; return InitialOrderTaken; } //+------------------------------------------------------------------+ //| Takes the trade in the direction indicated | //+------------------------------------------------------------------+ bool PlaceOrder(ENUM_ORDER_TYPE dir) { ENUM_ORDER_TYPE direction = dir; if(Mode == Reverse_Signal) direction = Opposite(dir); if(dir == ORDER_TYPE_BUY) return PlaceABuyOrder(); if(dir == ORDER_TYPE_SELL) return PlaceASellOrder(); return false; } //+------------------------------------------------------------------+ //| Returns the opposite order type | //+------------------------------------------------------------------+ ENUM_ORDER_TYPE Opposite(ENUM_ORDER_TYPE dir) { if(dir == ORDER_TYPE_BUY) return ORDER_TYPE_SELL; else // dir = ORDER_TYPE_SELL return ORDER_TYPE_BUY; } //+------------------------------------------------------------------+ //| Prints new trade message to journal | //+------------------------------------------------------------------+ void PrintNewTradeMessage(ENUM_ORDER_TYPE dir, double sl) { Print(" |||---->>> Dir = ", EnumToString(dir), " ATR = ", AverageTrueRange(Sym, TimeframeOfATR, PeriodsInATR), " Sell% = ", GetPercentage(Sym, dir, SellStopLossPercentage), " StopLoss = ", sl); } //+------------------------------------------------------------------+ //| Places a sell market order | //+------------------------------------------------------------------+ bool PlaceASellOrder() { //Print(__FUNCTION__); double sl = GetStopLoss(Sym, ORDER_TYPE_SELL, false); if(InitialOrderTaken == false && Stoploss != 0) sl = Stoploss; double bid = SymbolInfoDouble(Sym, SYMBOL_BID); double size = LotSizer(Sell); string comment = "Sell #1"; VirtualSell(Virt, size, Sym, bid, sl, 0, comment); int arraySize = ArraySize(Virt); NoticeMessage("ITSL", Virt[arraySize - 1], 2); DisplayLines(Virt[arraySize - 1]); if(Mode == Reverse_Entries) { if(trade.Buy(size, Sym, 0, 0, 0, comment)) return true; } if(trade.Sell(size, Sym, bid, useVirtualStops ? 0 : sl, 0, comment)) return true; return false; } //+------------------------------------------------------------------+ //| Set stoploss lines | //+------------------------------------------------------------------+ void DisplayLines(Virtual & virt) { //Print(__FUNCTION__); if(virt.request.type == ORDER_TYPE_BUY) { HLineCreate(0, "StopLoss", 0, virt.request.sl, clrLawnGreen, STYLE_DASHDOTDOT, 5); HLineCreate(0, "Opened", 0, 0, clrMediumBlue, STYLE_SOLID, 3); ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrBlack); } else { HLineCreate(0, "StopLoss", 0, virt.request.sl, clrHotPink, STYLE_DASHDOTDOT, 5); HLineCreate(0, "Opened", 0, 0, clrFuchsia, STYLE_SOLID, 3); ChartSetInteger(0, CHART_COLOR_BACKGROUND, clrBlack); } } //+------------------------------------------------------------------+ //| Places a buy market order | //+------------------------------------------------------------------+ bool PlaceABuyOrder() { //Print(__FUNCTION__); double sl = GetStopLoss(Sym, ORDER_TYPE_BUY, false); if(InitialOrderTaken == false && Stoploss != 0) sl = Stoploss; double ask = SymbolInfoDouble(Sym, SYMBOL_ASK); double size = LotSizer(Buy); string comment = "Buy #1"; VirtualBuy(Virt, size, Sym, ask, sl, 0, comment); int arraySize = ArraySize(Virt); NoticeMessage("ITSL", Virt[arraySize - 1], 2); DisplayLines(Virt[arraySize - 1]); if(Mode == Reverse_Entries) { if(trade.Sell(size, Sym, 0, 0, 0, comment)) return true; } if(trade.Buy(size, Sym, ask, useVirtualStops ? 0 : sl, 0, comment)) return true; return false; } //+------------------------------------------------------------------+ //| Cycles through the virtual orders and checks if any have hit the | //| stoploss if so it issues a close order | //+------------------------------------------------------------------+ void CheckStops() { //Print(__FUNCTION__); int size = ArraySize(Virt); for(int i = 0; i < size; i++) { if(Virt[i].active) { if(Virt[i].request.type == ORDER_TYPE_BUY) if(SymbolInfoDouble(Sym, SYMBOL_BID) <= Virt[i].request.sl && Virt[i].request.sl != 0) { Print(Sym, " Bid = ", SymbolInfoDouble(Sym, SYMBOL_BID), " <= Stoploss ", Virt[i].request.sl); if(VirtualOrderClose(Virt, i)) { NoticeMessage("ITSL", Virt[i], 4, " switching Direction"); trade.PositionClose(Virt[i].request.symbol); } } if(Virt[i].request.type == ORDER_TYPE_SELL) if(SymbolInfoDouble(Sym, SYMBOL_ASK) >= Virt[i].request.sl && Virt[i].request.sl != 0) { Print(Sym, " Ask = ", SymbolInfoDouble(Sym, SYMBOL_BID), " <= Stoploss ", Virt[i].request.sl); if(VirtualOrderClose(Virt, i)) { NoticeMessage("ITSL", Virt[i], 4, " switching Direction"); trade.PositionClose(Virt[i].request.symbol); } } } } } //+------------------------------------------------------------------+ //| Calculates the virtual profits that occured after a back test | //+------------------------------------------------------------------+ double OnTester() { double profit = 0; int size = ArraySize(Virt); for(int i = 0; i < size; i++) { profit += Virt[i].profit; if(verb) printf("Virt[%d] profit %.2f ticket #%d : Balance $%.2f Entry %.2f : Exit %.2f", i, Virt[i].profit, Virt[i].request.order, profit, Virt[i].request.price, Virt[i].close); } printf("Profit %.2f on %d orders", profit, size); return (profit); } //+------------------------------------------------------------------+ //| Returns the proper lots size for the order being placed | //+------------------------------------------------------------------+ double LotSizer(TradeDirection dir, double multiplier = 1) { Print(__FUNCTION__); double size = SellInitialLotSize, volumeMin = SymbolInfoDouble(Sym, SYMBOL_VOLUME_MIN), volumeMax = SymbolInfoDouble(Sym, SYMBOL_VOLUME_MAX); if(dir == Buy) size = BuyInitialLotSize; for(int i = 0; i < SeriesOrderNumber; i++) size = size + size * multiplier; //if(size > volumeMax) // return volumeMax; //else if(size < volumeMin) return volumeMin; else return NormalizeDouble (size, 2); } //+------------------------------------------------------------------+ //| Checks if the account has enough money for the trade | //+------------------------------------------------------------------+ bool CheckMoneyForTrade(string symb, double lots, ENUM_ORDER_TYPE type) { Print(__FUNCTION__); //--- Getting the opening price MqlTick mqltick; SymbolInfoTick(symb, mqltick); double price = mqltick.ask; if(type == ORDER_TYPE_SELL) price = mqltick.bid; //--- values of the required and free margin double margin, free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE); //--- call of the checking function if(!OrderCalcMargin(type, symb, lots, price, margin)) { //--- something went wrong, report and return false Print("Error in ", __FUNCTION__, " code=", GetLastError()); Print(__FUNCSIG__, symb, " ", lots, " ", type); return(false); } //--- if there are insufficient funds to perform the operation if(margin > free_margin) return(false); //--- checking successful return(true); } //+------------------------------------------------------------------+ //| Checks for errors with inputs | //+------------------------------------------------------------------+ bool ErrorsWithInputs() { bool inputError = false; if(Price == 0 && (InitialDirection > Sell)) { Print("A Price must be specified for ", EnumToString(InitialDirection), " order type!"); Alert("A Price must be specified for ", EnumToString(InitialDirection), " order type!"); inputError = true; } if(!UpdateFromResumeTableAtStart) // If not resuming { if((BuyStopLossPercentage <= 0 || SellStopLossPercentage <= 0) || (BuyIdleMinutesRequiringAMove <= 0 && BuyMoveStopLossIfIdle) || (SellIdleMinutesRequiringAMove <= 0 && SellMoveStopLossIfIdle)) { Print("Inputs Error Check: Stop Loss Percentage and Idle Minutes Settings"); inputError = true; } if(inputError) Alert("Error with inputs! EA Stopping"); } return inputError; } //+------------------------------------------------------------------+