401 lines
14 KiB
MQL5
401 lines
14 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| 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();
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|