#include #include #include #include // Static class objects CSymbolInfo symbolInfo; CPositionInfo position; CTrade trade; CDealInfo deal; enum ENUM_ENTRY_STRAT { SupportResistance, ThreeLastCandles, Both, }; enum ENUM_EXIT_STRAT { StoplossTakeProfit, TrailingSL }; enum ENUM_MY_TIMEFRAMES { M1 = PERIOD_M1, M5 = PERIOD_M5, M15 = PERIOD_M15, M30 = PERIOD_M30, H1 = PERIOD_H1, H4 = PERIOD_H4, H12 = PERIOD_H12, D1 = PERIOD_D1 }; enum ENUM_TRIGGER { Buy, Sell, DoNothing }; input group "Money Management"; input double IN_LotMultiplier = 4.0; //Lot Multiplier input group "Trade Settings"; input ENUM_EXIT_STRAT IN_ExitStrat = StoplossTakeProfit; //Exit-Strategy input int IN_TakeProfit = 200; //Takeprofit in points (Set to 1000 when using TrailingSL) input int IN_StopLoss = 100; //Stoploss in points input int IN_TrailingStopLossTrigger = 50; //Trailing SL Trigger in points input int IN_MaxSlippage = 3; //Max Slippage in points input ENUM_MY_TIMEFRAMES IN_Timeframe = H1; //Timeframe input ulong IN_MagicNumber = 220193; //Magic number input group "Long Wick Candle"; input bool ActiveLongWick = true; //Use Long Wick Candle input ENUM_ENTRY_STRAT EntryStratLWC = Both; //Keylevel input int MaxBodySizeLWC = 10; //Max Body Size in points input int MinWickLenLWC = 50; //Min Wick Length in points input group "Inverted Long Wick Candle"; input bool ActiveInvertedWick = true; //Use Inverted Long Wick Candle input ENUM_ENTRY_STRAT EntryStratILW = Both; //Keylevel input int MaxBodySizeILW = 10; //Max Body Size in points input int MinWickLenILW = 50; //Min Wick Length in points input group "Inside Bar"; input bool ActiveInsideBar = true; //Use Inverted Long Wick Candle input int MinBodySizeINB = 10; //Minimum Body Size in points input group "Support & Resistance"; input int FirstCandle = 3; //First Candle for Lookup Area input int LastCandle = 30; //Last Candle for Lookup Area //input group "Time Settings" // //input group "Exception 1" //input bool ActiveExc1 = false; //No Trade Exception 1 //input int startDay1 = 1; //Day, Monday = 1, 0 = Off //input int startHour1 = 0; //Start hour //input int startMinute1 = 0; //Start Minute //input int endHour1 = 24; //End hour //input int endMinute1 = 0; //End Minute // //input group "Exception 2" //input bool ActiveExc2 = false; //No Trade Exception 2 //input int startDay2 = 1; //Start Day, Monday = 1, 0 = Off //input int startHour2 = 0; //Start hour //input int startMinute2 = 0; //Start Minute //input int endHour2 = 24; //End hour //input int endMinute2 = 0; //End Minute // //input group "Exception 3" //input bool ActiveExc3 = false; //No Trade Exception 2 //input int startDay3 = 1; //Start Day, Monday = 1, 0 = Off //input int startHour3 = 0; //Start hour //input int startMinute3 = 0; //Start Minute //input int endHour3 = 24; //End hour //input int endMinute3 = 0; //End Minute input group "Debug Settings" input bool IN_PrintDebug = true; //Print debug messages struct STRUCT_PENDING_TRADE { ENUM_POSITION_TYPE pos_type; ulong orderTicket; // When this is true we need to prevent trading since this order has not been confirmed yet bool isPending; // When this is true we can remove the entry from the array bool isConfirmed; // Constructor for new/blank array entries STRUCT_PENDING_TRADE() { pos_type = WRONG_VALUE; orderTicket = 0; isPending = false; isConfirmed = false; } }; STRUCT_PENDING_TRADE PendingTrades[]; int OnInit() { // Init CSymbolInfo variable for easy access ResetLastError(); if (!symbolInfo.Name(_Symbol)) { Print(__FILE__, " ", __FUNCTION__, ", ERROR: CSymbolInfo.Name"); return(INIT_FAILED); } RefreshRates(); trade.SetExpertMagicNumber(IN_MagicNumber); return(INIT_SUCCEEDED); } void OnTick() { double equity = AccountInfoDouble(ACCOUNT_EQUITY); //Guthaben nach Abzug der offenen Positionen double dynamicLotSize = NormalizeDouble(equity / 100000, 2); double dynamicTradeVolume = dynamicLotSize * IN_LotMultiplier; if (PositionsTotal() != 0 && IN_ExitStrat == TrailingSL) TrailingStopLoss(); int countPendingOrders = ArraySize(PendingTrades); if (countPendingOrders > 0) { for (int i = countPendingOrders - 1; i >= 0; i--) { // Remove all confirmed orders from the array if (PendingTrades[i].isPending && PendingTrades[i].isConfirmed) { ArrayRemove(PendingTrades, i, 1); continue; } // Open new order if (!PendingTrades[i].isPending) { OpenPosition(i, dynamicTradeVolume); } } return; } if (!RefreshRates()) return; //Here are 3 possible time filters to set when not to trade // MqlDateTime mdt; // TimeCurrent(mdt); // // int day = mdt.day_of_week; // int hour = mdt.hour; // int min = mdt.min; // // if(ActiveExc1 && day == startDay1) // { // if(hour >= startHour1 && min >= startMinute1 && hour <= endHour1 && min <= endMinute1) return; // } // if(ActiveExc2 && day == startDay2) // { // if(hour >= startHour2 && min >= startMinute2 && hour <= endHour2 && min <= endMinute2) return; // } // if(ActiveExc3 && day == startDay3) // { // if(hour >= startHour3 && min >= startMinute3 && hour <= endHour3 && min <= endMinute3) return; // } // We only look for a new trigger when we don't already have an open position if (AnyOpenPositions()) return; // Also need to check our PendingTrades array if we have recently placed an order that hasn't been confirmed yet countPendingOrders = ArraySize(PendingTrades); if (countPendingOrders > 0) return; ENUM_TRIGGER trigger = GetTrigger(SupLevel(), ResLevel()); if (trigger == DoNothing) return; if (IN_PrintDebug) Print(__FILE__, " " ,__FUNCTION__, "Trigger: ", trigger); // Add new order to PendingTrades array ArrayResize(PendingTrades, countPendingOrders + 1); switch (trigger) { case Buy: PendingTrades[countPendingOrders].pos_type = POSITION_TYPE_BUY; break; case Sell: PendingTrades[countPendingOrders].pos_type = POSITION_TYPE_SELL; break; default: break; } } ENUM_TRIGGER GetTrigger(double supportLevel, double resistenceLevel) { // Now check for a possible trade trigger condition // TODO // Is defined by the candle patter that needs the most candles int startCandle = 1; int maxCandles = 4; double candleHigh[]; ArraySetAsSeries(candleHigh, true); CopyHigh(_Symbol, (ENUM_TIMEFRAMES)IN_Timeframe, startCandle, maxCandles, candleHigh); double candleLow[]; ArraySetAsSeries(candleLow, true); CopyLow(_Symbol, (ENUM_TIMEFRAMES)IN_Timeframe, startCandle, maxCandles, candleLow); double candleOpen[]; ArraySetAsSeries(candleOpen, true); CopyOpen(_Symbol, (ENUM_TIMEFRAMES)IN_Timeframe, startCandle, maxCandles, candleOpen); double candleClose[]; ArraySetAsSeries(candleClose, true); CopyClose(_Symbol, (ENUM_TIMEFRAMES)IN_Timeframe, startCandle, maxCandles, candleClose); int candleBodySize[]; ArrayResize(candleBodySize, maxCandles); int wickLengthUp[]; ArrayResize(wickLengthUp, maxCandles); int wickLengthDown[]; ArrayResize(wickLengthDown, maxCandles); for (int i = 0; i < maxCandles; i++) { if(candleOpen[i] < candleClose[i]) //If open < close, the candle is bullish { candleBodySize[i] = (candleClose[i] - candleOpen[i]) /_Point; //Divided by _Point gives the difference in points which is more easy to calculate with wickLengthUp[i] = (candleHigh[i] - candleClose[i]) / _Point; wickLengthDown[i] = (candleOpen[i] - candleLow[i]) / _Point; } else if (candleOpen[i] == candleClose[i]) { candleBodySize[i] = 0; wickLengthUp[i] = (candleHigh[i] - candleClose[i]) / _Point; wickLengthDown[i] = (candleOpen[i] - candleLow[i]) / _Point; } else //Else candle is bearish { candleBodySize[i] = (candleOpen[i] - candleClose[i]) / _Point; wickLengthUp[i] = (candleHigh[i] - candleOpen[i]) / _Point; wickLengthDown[i] = (candleClose[i] - candleLow[i]) / _Point; } } bool ThreeGreenCandles = false; bool ThreeRedCandles = false; if(candleOpen[3] < candleClose[3] && candleOpen[2] < candleClose[2] && candleOpen[1] < candleClose[1]) ThreeGreenCandles = true; if(candleOpen[3] > candleClose[3] && candleOpen[2] > candleClose[2] && candleOpen[1] > candleClose[1]) ThreeRedCandles = true; if(ActiveLongWick) { switch (EntryStratLWC) { case SupportResistance: { if (candleBodySize[0] < MaxBodySizeLWC && wickLengthUp[0] > MinWickLenLWC && candleHigh[0] > resistenceLevel) return Sell; if (candleBodySize[0] < MaxBodySizeLWC && wickLengthDown[0] > MinWickLenLWC && candleLow[0] < supportLevel) return Buy; } break; case ThreeLastCandles: { if (candleBodySize[0] < MaxBodySizeLWC && wickLengthUp[0] > MinWickLenLWC && ThreeGreenCandles == true) return Sell; if (candleBodySize[0] < MaxBodySizeLWC && wickLengthDown[0] > MinWickLenLWC && ThreeRedCandles == true) return Buy; } break; case Both: { if (candleBodySize[0] < MaxBodySizeLWC && wickLengthUp[0] > MinWickLenLWC && (candleHigh[0] > resistenceLevel || ThreeGreenCandles == true)) return Sell; if (candleBodySize[0] < MaxBodySizeLWC && wickLengthDown[0] > MinWickLenLWC && (candleLow[0] < supportLevel || ThreeRedCandles == true)) return Buy; } break; } } if(ActiveInvertedWick) { switch (EntryStratILW) { case SupportResistance: { if (candleBodySize[0] < MaxBodySizeILW && wickLengthDown[0] > MinWickLenILW && candleHigh[0] > resistenceLevel) return Sell; if (candleBodySize[0] < MaxBodySizeILW && wickLengthUp[0] > MinWickLenILW && candleLow[0] < supportLevel) return Buy; } break; case ThreeLastCandles: if(ActiveInvertedWick) { if (candleBodySize[0] < MaxBodySizeILW && wickLengthDown[0] > MinWickLenILW && ThreeGreenCandles == true) return Sell; if (candleBodySize[0] < MaxBodySizeILW && wickLengthUp[0] > MinWickLenILW && candleLow[0] < supportLevel && ThreeRedCandles == true) return Buy; } break; case Both: if(ActiveInvertedWick) { if (candleBodySize[0] < MaxBodySizeILW && wickLengthDown[0] > MinWickLenILW && (candleHigh[0] > resistenceLevel || ThreeGreenCandles == true)) return Sell; if (candleBodySize[0] < MaxBodySizeILW && wickLengthUp[0] > MinWickLenILW && candleLow[0] < supportLevel && (candleLow[0] < supportLevel || ThreeRedCandles == true)) return Buy; } break; } } if(ActiveInsideBar) { if (candleBodySize[0] > MinBodySizeINB && candleHigh[0] < candleHigh[1] && candleLow[0] > candleLow[1] && candleHigh[1] > resistenceLevel) return Sell; if (candleBodySize[0] > MinBodySizeINB && candleLow[0] > candleLow[1] && candleHigh[0] < candleHigh[1] && candleLow[1] < supportLevel) return Buy; } return DoNothing; } //Erster Versuch Support und Resistance Level zu identifizieren //Man kann den höchsten und niedrigsten Wert aus einem Array ziehen //Großer Nachteil ist die fixe Größe des Arrays, die müsste variabel sein, bis ein neues Level gefunden wird //Da fehlt mir aber noch die Idee, wie genau man das umsetzen kann double SupLevel() { double arrCandles[]; ArraySetAsSeries(arrCandles, true); CopyClose(_Symbol,(ENUM_TIMEFRAMES)IN_Timeframe,FirstCandle,LastCandle,arrCandles); double CandleLow = arrCandles[ArrayMinimum(arrCandles,0,WHOLE_ARRAY)]; return CandleLow; } double ResLevel() { double arrCandles[]; ArraySetAsSeries(arrCandles, true); CopyClose(_Symbol,(ENUM_TIMEFRAMES)IN_Timeframe,FirstCandle,LastCandle,arrCandles); double CandleHigh = arrCandles[ArrayMaximum(arrCandles,0,WHOLE_ARRAY)]; return CandleHigh; } void TrailingStopLoss() { for (int i = PositionsTotal() - 1; i >= 0; i--) { ulong posTicket = PositionGetTicket(i); if (PositionSelectByTicket(posTicket) && PositionGetString(POSITION_SYMBOL) == _Symbol) { ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double posOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN); double posTP = PositionGetDouble(POSITION_TP); double posSL = PositionGetDouble(POSITION_SL); double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(posType == POSITION_TYPE_BUY) { if(bid > posOpenPrice + IN_TrailingStopLossTrigger * _Point) { double NewSL = bid - IN_TrailingStopLossTrigger * _Point; NewSL = NormalizeDouble(NewSL, _Digits); if (NewSL > posSL) { trade.PositionModify(posTicket, NewSL, posTP); } } } else if (posType == POSITION_TYPE_SELL) { if (ask < posOpenPrice - IN_TrailingStopLossTrigger * _Point) { double NewSL = ask + IN_TrailingStopLossTrigger * _Point; NewSL = NormalizeDouble(NewSL, _Digits); if (NewSL < posSL || posSL == 0) { trade.PositionModify(posTicket, NewSL, posTP); } } } } } } // Sets the "isConfirmed" flag in our PendingTrades array void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { ENUM_TRADE_TRANSACTION_TYPE type = trans.type; if (type == TRADE_TRANSACTION_DEAL_ADD) { ResetLastError(); if (HistoryDealSelect(trans.deal)) { deal.Ticket(trans.deal); } else { Print(__FILE__, " ", __FUNCTION__, ", ERROR: ", "HistoryDealSelect(", trans.deal, ") error: ", GetLastError()); return; } if (deal.Symbol() == symbolInfo.Name() && deal.Magic() == IN_MagicNumber) { if (deal.DealType() == DEAL_TYPE_BUY || deal.DealType() == DEAL_TYPE_SELL) { int countPendingOrders = ArraySize(PendingTrades); if (countPendingOrders > 0) { for (int i = 0; i < countPendingOrders; i++) { if (PendingTrades[i].isPending) { if (PendingTrades[i].orderTicket == deal.Order()) { Print(__FILE__, " ", __FUNCTION__, ", OK: ", " Transaction confirmed"); PendingTrades[i].isConfirmed = true; break; } } } } } } } } bool RefreshRates() { // Refreshes the symbol quotes data if (!symbolInfo.RefreshRates()) { Print(__FILE__, " ", __FUNCTION__, ", ERROR: ", "RefreshRates error"); return false; } // Sometimes 0 is returned, no idea why yet if (symbolInfo.Ask() == 0 || symbolInfo.Bid() == 0) { Print(__FILE__, " ", __FUNCTION__, ", ERROR: ", "Ask == 0.0 OR Bid == 0.0"); return false; } return true; } void OpenPosition(int index, double tradeVolume) { if (!RefreshRates()) return; bool success = false; string debugType = ""; if (PendingTrades[index].pos_type == POSITION_TYPE_BUY) { debugType = "Buy"; success = trade.Buy(tradeVolume, symbolInfo.Name(), symbolInfo.Ask(), symbolInfo.Ask() - IN_StopLoss * _Point,symbolInfo.Ask() + IN_TakeProfit * _Point); } else if (PendingTrades[index].pos_type == POSITION_TYPE_SELL) { debugType = "Sell"; success = trade.Sell(tradeVolume, symbolInfo.Name(), symbolInfo.Bid(), symbolInfo.Bid() + IN_StopLoss * _Point,symbolInfo.Bid() - IN_TakeProfit * _Point); } if (success) { if (trade.ResultRetcode() == TRADE_RETCODE_DONE) { PendingTrades[index].isPending = true; PendingTrades[index].orderTicket = trade.ResultOrder(); } else { PendingTrades[index].isPending = false; if (IN_PrintDebug) Print(__FILE__, " ", __FUNCTION__, ", ERROR: ", debugType, " -> false, Deal Nr: ", trade.ResultDeal(), " Result Retcode: ", trade.ResultRetcode(), ", descr: ", trade.ResultRetcodeDescription()); } if (IN_PrintDebug) PrintResultTrade(); } else { PendingTrades[index].isPending = false; if (IN_PrintDebug) Print(__FILE__, " ", __FUNCTION__, ", ERROR: ", debugType, " -> false. Result Retcode: ", trade.ResultRetcode(), ", descrt: ", trade.ResultRetcodeDescription()); } } void PrintResultTrade() { Print(__FILE__, " ", __FUNCTION__, ", Symbol: ", symbolInfo.Name() + ", " + "Code of request result: " + IntegerToString(trade.ResultRetcode()) + ", " + "Code of request result as a string: " + trade.ResultRetcodeDescription(), "trade execution mode: " + symbolInfo.TradeExecutionDescription()); Print("deal ticket: " + IntegerToString(trade.ResultDeal()) + ", " + "Order ticket: " + IntegerToString(trade.ResultOrder()) + ", " + "Order retcode external: " + IntegerToString(trade.ResultRetcodeExternal()) + ", " + "Volume of deal or order: " + DoubleToString(trade.ResultVolume(), 2)); Print("Price, confirmed by broker: " + DoubleToString(trade.ResultPrice(), symbolInfo.Digits()) + ", " + "Current bid price: " + DoubleToString(symbolInfo.Bid(), symbolInfo.Digits()) + " (the requote): " + DoubleToString(trade.ResultBid(), symbolInfo.Digits()) + ", " + "Current ask price: " + DoubleToString(symbolInfo.Ask(), symbolInfo.Digits()) + " (the requote): " + DoubleToString(trade.ResultAsk(), symbolInfo.Digits())); Print("Broker comment: " + trade.ResultComment()); } bool AnyOpenPositions() { for (int i = PositionsTotal() - 1; i >= 0; i--) { if (position.SelectByIndex(i)) { if (position.Symbol() == symbolInfo.Name() && position.Magic() == IN_MagicNumber) { return true; } } } return false; }