ent-testing/ent-testing-indicator-class.mq5

402 lines
14 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 14:53:02 +02:00
//+------------------------------------------------------------------+
//| 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 <Trade\Trade.mqh>
#include <Trade\AccountInfo.mqh>
#include <StdLibErr.mqh>
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();
}
//+------------------------------------------------------------------+