//+------------------------------------------------------------------+ //| ent-testing-indicator-class.mq5 | //| Entr04y | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Entr04y" #property link "https://www.mql5.com" #property version "1.00" //Include Functions #include #include #include CTrade Trade; CAccountInfo AcctInfo; // Input Variables input int InpMagicNumber = 42000001; input string InpTradeComment = __FILE__; input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE; input bool Debug = true; input string TradeSymbols = "ALL"; // Global Vars int TicksReceivedCount = 0; int TicksProcessedCount = 0; static datetime TimeLastTickProcessed[]; string indicatorMetrics = ""; // reset metrics variable for each onTick string SymbolsProcessedThisIteration; int chalks = 0; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ double StartingCapital = AccountInfoDouble(ACCOUNT_BALANCE); string AllSymbolsString = "AUDCAD|AUDCHF|AUDJPY|AUDNZD|AUDUSD|CADJPY|CHFJPY|EURAUD|EURCAD|EURCHF|EURGBP|EURJPY|EURNZD|EURUSD|GBPAUD|GBPCAD|GBPCHF|GBPJPY|GBPNZD|GBPUSD|NZDCAD|NZDCHF|NZDJPY|NZDUSD|USDCAD|USDCHF|USDJPY"; int NumberOfTradeableSymbols; string SymbolArray[]; ulong OpenTradeOrderTicket[]; //+------------------------------------------------------------------+ //| Set up Indicators | //+------------------------------------------------------------------+ // Indicator Includes. Indicator includes should implment the following methods: // Init(ENUM_APPLIED_PRICE), <-- which price value to use // IndicatorHandler(string), <-- current symbol name // GetOpenSignal(int,string), <-- handler for the symbol, current symbol name // to maintain compatibility with the rest of the EA // Configuration of indicator parameters should be done in the header file, this is for // ease of switching out indicators as they will all have different requirements for parameters. // C1 (Confirmation One) // Change the include file and the Class on the next line to switch indicators #include "indicator_includes/MacdSignal-Class.mqh" CMacdSignal C1Indicator; int HandleC1[]; //+------------------------------------------------------------------+ //| Money Management - Stop Loss and Position Size | //+------------------------------------------------------------------+ // Get ATR input bool InpFixedEquity = false; input double InpFixedATR = 0; input double InpRiskPerTrade = 2.0; input double InpStopLossFactor = 1.5; input double InpTakeProfitFactor = 1.0; input int ATRPeriod = 14; input double AtrTakeProfitMultiplier = 1; input double AtrStopLossMultiplier = 1.5; int ATRHandle[]; // ATR double GetAtr(int Handler) { const int StartCandle = 0; const int RequiredCandles = 3; const int IndexAtr = 0; double BufferATR[]; bool fillATR = CopyBuffer(Handler, IndexAtr, StartCandle, RequiredCandles, BufferATR); double currentATR = NormalizeDouble(BufferATR[1], 10); return currentATR; } //+------------------------------------------------------------------+ //| Calculate position size | //+------------------------------------------------------------------+ double CalculatePositionSize(string CurrentSymbol, double CurrentATR) { double ATRPoints; double WorkingCapital = 0.0; double RawLotSize; double LotSize; if(InpFixedEquity) WorkingCapital = StartingCapital; else WorkingCapital = AccountInfoDouble(ACCOUNT_EQUITY); double ExchangeRate = 1; string AccountCurrency = AccountInfoString(ACCOUNT_CURRENCY); string CounterCurrency = StringSubstr(CurrentSymbol,3,3); string ExchangeCurrency = AccountCurrency + CounterCurrency; if(AccountCurrency != CounterCurrency) ExchangeRate = SymbolInfoDouble(ExchangeCurrency, SYMBOL_ASK); if(ExchangeRate == 0) ExchangeRate = 1; if(InpFixedATR != 0) ATRPoints = InpFixedATR; else ATRPoints = CurrentATR / SymbolInfoDouble(CurrentSymbol, SYMBOL_POINT); double CapitalRiskPerTrade = WorkingCapital * (InpRiskPerTrade / 100); double RiskValue = (ExchangeRate / 1) * CapitalRiskPerTrade; double StopLossPoints = ATRPoints * InpStopLossFactor; double RiskPerPoint = RiskValue / StopLossPoints; if(CounterCurrency == "JPY") RawLotSize = NormalizeDouble((RiskPerPoint / 100),2); else RawLotSize = NormalizeDouble(RiskPerPoint, 2); double MinVolume = SymbolInfoDouble(CurrentSymbol,SYMBOL_VOLUME_MIN); double MaxVolume = SymbolInfoDouble(CurrentSymbol,SYMBOL_VOLUME_MAX); double StepVolume = SymbolInfoDouble(CurrentSymbol,SYMBOL_VOLUME_STEP); if(RawLotSize > MaxVolume) { LotSize = MaxVolume; if(Debug) Print("Lot Size: ",RawLotSize, " is Larger than MaxVolume: ", MaxVolume, " Changing lot size to: ", LotSize); } else if(RawLotSize < MinVolume) { LotSize = 0; if(Debug) Print("Lot Size: ",RawLotSize, " is smaller than MinVolume: ", MinVolume, " Changing lot size to: ", LotSize); } else { int NumberOfSteps = (int)(RawLotSize / StepVolume); LotSize = (NumberOfSteps * StepVolume); if(Debug) Print("Adjusting raw lot size: ",RawLotSize, " to be compliant with step volume: ", StepVolume, " Modified lot size is: ", LotSize); } return LotSize; } //+------------------------------------------------------------------+ //| Execute Trade Entries | //+------------------------------------------------------------------+ bool ExecuteTradeEntry(ENUM_ORDER_TYPE orderType, string CurrentSymbol, int ATRHandler) { double price = 0; double stopLossPrice = 0; double takeProfitPrice = 0; double CurrentATR = GetAtr(ATRHandler); if(orderType == ORDER_TYPE_BUY) { price = NormalizeDouble(SymbolInfoDouble(CurrentSymbol, SYMBOL_ASK), Digits()); stopLossPrice = NormalizeDouble(price - (CurrentATR * AtrStopLossMultiplier), Digits()); takeProfitPrice = NormalizeDouble(price + (CurrentATR * AtrTakeProfitMultiplier), Digits()); } else if(orderType == ORDER_TYPE_SELL) { price = NormalizeDouble(SymbolInfoDouble(CurrentSymbol, SYMBOL_BID), Digits()); stopLossPrice = NormalizeDouble(price + (CurrentATR * AtrStopLossMultiplier), Digits()); takeProfitPrice = NormalizeDouble(price - (CurrentATR * AtrTakeProfitMultiplier), Digits()); } // Get Lot Size double lotSize = CalculatePositionSize(CurrentSymbol,CurrentATR); if(AcctInfo.FreeMarginCheck(CurrentSymbol,orderType,lotSize,price) <= 0) { lotSize = 0; Print("Not enough free margin for the trade"); } if(lotSize >= .01) { //Execute Trades if(PositionSelect(CurrentSymbol)) { chalks += 1; Trade.PositionClose(CurrentSymbol); } Trade.PositionOpen(CurrentSymbol, orderType, lotSize, price, stopLossPrice, takeProfitPrice, InpTradeComment); } else Print("LotSize is: ", lotSize, " Which is too small"); // Error handling return(true); } //+------------------------------------------------------------------+ //| Execute Trade Exits | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Helper functions | //+------------------------------------------------------------------+ void ResizeCoreArrays() { ArrayResize(OpenTradeOrderTicket, NumberOfTradeableSymbols); ArrayResize(TimeLastTickProcessed, NumberOfTradeableSymbols); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void ResizeIndicatorHandleArrays() { ArrayResize(ATRHandle, NumberOfTradeableSymbols); ArrayResize(HandleC1, NumberOfTradeableSymbols); } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void OutputStatusToScreen() { double OffsetInHours = (double)(TimeCurrent() - TimeGMT()) / 3600; string OutputText = "\n\r"; OutputText += "MT5 Server Time: " + TimeToString(TimeCurrent(), TIME_DATE|TIME_SECONDS) + " (OPerating at UTC/GMT" + StringFormat("%+.1f", OffsetInHours) + "\n\r\n\r"; OutputText += Symbol() + " Ticks Received: " + IntegerToString(TicksReceivedCount) + "\n\r"; OutputText += "Ticks Processed Across all " + IntegerToString(NumberOfTradeableSymbols) + " symbols: " + IntegerToString(TicksProcessedCount) + "\n\r\n\r"; OutputText += "Symbols: "; for(int SymbolLoop=0; SymbolLoop < NumberOfTradeableSymbols; SymbolLoop++) { OutputText += " " + SymbolArray[SymbolLoop]; } OutputText += "\n\rTrading Timeframe: " + EnumToString(Period()) + "\n\r"; if(SymbolsProcessedThisIteration != "") SymbolsProcessedThisIteration = "\n\r" + SymbolsProcessedThisIteration; OutputText += "\n\rSymbols Processed This Tick: " + SymbolsProcessedThisIteration; Comment(OutputText); return; } //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ int OnInit() { //--- //Initialize indicators C1Indicator.Init(); // Set up trading symbols if(TradeSymbols == "CURRENT") { NumberOfTradeableSymbols = 1; ArrayResize(SymbolArray, 1); SymbolArray[0] = Symbol(); } else { string TradeSymbolsToUse = ""; if(TradeSymbols == "ALL") TradeSymbolsToUse = AllSymbolsString; else TradeSymbolsToUse = TradeSymbols; NumberOfTradeableSymbols = StringSplit(TradeSymbolsToUse, '|', SymbolArray); } ResizeCoreArrays(); ResizeIndicatorHandleArrays(); ArrayInitialize(TimeLastTickProcessed, D'1971.01.01 00:00'); Trade.SetExpertMagicNumber(InpMagicNumber); // Ensure trading symbols are in the Market watch window // and create handles for each symbol for(int SymbolLoop = 0; SymbolLoop < NumberOfTradeableSymbols; SymbolLoop++) { bool addsuccess = SymbolSelect(SymbolArray[SymbolLoop], true); if(addsuccess) Print("Successfully added ", SymbolArray[SymbolLoop], " to MarketWatch, or it was already added"); else Print("Error adding ", SymbolArray[SymbolLoop], " to MarketWatch"); // Set up handles for ATR indicator ATRHandle[SymbolLoop] = iATR(SymbolArray[SymbolLoop],Period(),ATRPeriod); // Set up handles for confirmation indicators HandleC1[SymbolLoop] = C1Indicator.IndicatorHandler(SymbolArray[SymbolLoop]); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Print("Number of Chalks: ", chalks); for(int SymbolLoop = 0; SymbolLoop < NumberOfTradeableSymbols; SymbolLoop++) { IndicatorRelease(ATRHandle[SymbolLoop]); IndicatorRelease(HandleC1[SymbolLoop]); } } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- SymbolsProcessedThisIteration = ""; TicksReceivedCount++; for(int SymbolLoop = 0; SymbolLoop < NumberOfTradeableSymbols; SymbolLoop++) { //if(Debug) //Print("Processing Symbol: ", SymbolArray[SymbolLoop]); //Get C1 status if(TimeLastTickProcessed[SymbolLoop] != iTime(SymbolArray[SymbolLoop], Period(), 0)) { if(Debug) Print("New Candle for: ", SymbolArray[SymbolLoop]); string OpenSignalC1 = C1Indicator.GetOpenSignal(HandleC1[SymbolLoop], SymbolArray[SymbolLoop]); TimeLastTickProcessed[SymbolLoop] = iTime(SymbolArray[SymbolLoop], Period(),0); TicksProcessedCount++; SymbolsProcessedThisIteration += SymbolArray[SymbolLoop] + "\n\r"; //---Enter Trades---// if(OpenSignalC1 == "LONG") { ExecuteTradeEntry(ORDER_TYPE_BUY, SymbolArray[SymbolLoop], ATRHandle[SymbolLoop]); if(Debug) Print("Got Long Signal for: ", SymbolArray[SymbolLoop]); } else if(OpenSignalC1 == "SHORT") { ExecuteTradeEntry(ORDER_TYPE_SELL, SymbolArray[SymbolLoop], ATRHandle[SymbolLoop]); if(Debug) Print("Got Short Signal for: ", SymbolArray[SymbolLoop]); } else { if(Debug) Print("Got No Trade Signal for: ",SymbolArray[SymbolLoop]); } } } if(!MQLInfoInteger(MQL_TESTER)) OutputStatusToScreen(); } //+------------------------------------------------------------------+