//+------------------------------------------------------------------+ //| BlackCrows WhiteSoldiers MFI.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.mql5.com" #property version "1.00" #include #include #define SIGNAL_BUY 1 // Buy signal #define SIGNAL_NOT 0 // no trading signal #define SIGNAL_SELL -1 // Sell signal #define CLOSE_LONG 2 // signal to close Long #define CLOSE_SHORT -2 // signal to close Short //--- Input parameters input int InpAverBodyPeriod=12; // period for calculating average candlestick size input int InpPeriodMFI =37; // MFI period input ENUM_APPLIED_VOLUME InpVolume=VOLUME_TICK; // volume type //--- trade parameters input uint InpDuration=10; // position holding time in bars input uint InpSL =200; // Stop Loss in points input uint InpTP =200; // Take Profit in points input uint InpSlippage=10; // slippage in points //--- money management parameters input double InpLot =0.1; // lot //--- Expert ID input long InpMagicNumber=120200; // Magic Number //--- global variables int ExtAvgBodyPeriod; // average candlestick calculation period int ExtSignalOpen =0; // Buy/Sell signal int ExtSignalClose =0; // signal to close a position string ExtPatternInfo =""; // current pattern information string ExtDirection =""; // position opening direction bool ExtPatternDetected=false; // pattern detected bool ExtConfirmed =false; // pattern confirmed bool ExtCloseByTime =true; // requires closing by time bool ExtCheckPassed =true; // status checking error //--- indicator handle int ExtIndicatorHandle=INVALID_HANDLE; //--- service objects CTrade ExtTrade; CSymbolInfo ExtSymbolInfo; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("InpSL=", InpSL); Print("InpTP=", InpTP); //--- set parameters for trading operations ExtTrade.SetDeviationInPoints(InpSlippage); // slippage ExtTrade.SetExpertMagicNumber(InpMagicNumber); // Expert Advisor ID ExtTrade.LogLevel(LOG_LEVEL_ERRORS); // logging level ExtAvgBodyPeriod=InpAverBodyPeriod; //--- indicator initialization ExtIndicatorHandle=iMFI(_Symbol, _Period, InpPeriodMFI, InpVolume); if(ExtIndicatorHandle==INVALID_HANDLE) { Print("Error creating MFI indicator"); return(INIT_FAILED); } //--- OK return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- release indicator handle IndicatorRelease(ExtIndicatorHandle); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- save the next bar start time; all checks at bar opening only static datetime next_bar_open=0; //--- Phase 1 - check the emergence of a new bar and update the status if(TimeCurrent()>=next_bar_open) { //--- get the current state of environment on the new bar // namely, set the values of global variables: // ExtPatternDetected - pattern detection // ExtConfirmed - pattern confirmation // ExtSignalOpen - signal to open // ExtSignalClose - signal to close // ExtPatternInfo - current pattern information if(CheckState()) { //--- set the new bar opening time next_bar_open=TimeCurrent(); next_bar_open-=next_bar_open%PeriodSeconds(_Period); next_bar_open+=PeriodSeconds(_Period); //--- report the emergence of a new bar only once within a bar if(ExtPatternDetected && ExtConfirmed) Print(ExtPatternInfo); } else { //--- error getting the status, retry on the next tick return; } } //--- Phase 2 - if there is a signal and no position in this direction if(ExtSignalOpen && !PositionExist(ExtSignalOpen)) { Print("\r\nSignal to open position ", ExtDirection); PositionOpen(); if(PositionExist(ExtSignalOpen)) ExtSignalOpen=SIGNAL_NOT; } //--- Phase 3 - close if there is a signal to close if(ExtSignalClose && PositionExist(ExtSignalClose)) { Print("\r\nSignal to close position ", ExtDirection); CloseBySignal(ExtSignalClose); if(!PositionExist(ExtSignalClose)) ExtSignalClose=SIGNAL_NOT; } //--- Phase 4 - close upon expiration if(ExtCloseByTime && PositionExpiredByTimeExist()) { CloseByTime(); ExtCloseByTime=PositionExpiredByTimeExist(); } } //+------------------------------------------------------------------+ //| Get the current environment and check for a pattern | //+------------------------------------------------------------------+ bool CheckState() { //--- check if there is a pattern if(!CheckPattern()) { Print("Error, failed to check pattern"); return(false); } //--- check for confirmation if(!CheckConfirmation()) { Print("Error, failed to check pattern confirmation"); return(false); } //--- if there is no confirmation, cancel the signal if(!ExtConfirmed) ExtSignalOpen=SIGNAL_NOT; //--- check if there is a signal to close a position if(!CheckCloseSignal()) { Print("Error, failed to check the closing signal"); return(false); } //--- if positions are to be closed after certain holding time in bars if(InpDuration) ExtCloseByTime=true; // set flag to close upon expiration //--- all checks done return(true); } //+------------------------------------------------------------------+ //| Open a position in the direction of the signal | //+------------------------------------------------------------------+ bool PositionOpen() { ExtSymbolInfo.Refresh(); ExtSymbolInfo.RefreshRates(); double price=0; //--- Stop Loss and Take Profit are not set by default double stoploss=0.0; double takeprofit=0.0; int digits=ExtSymbolInfo.Digits(); double point=ExtSymbolInfo.Point(); double spread=ExtSymbolInfo.Ask()-ExtSymbolInfo.Bid(); //--- uptrend if(ExtSignalOpen==SIGNAL_BUY) { price=NormalizeDouble(ExtSymbolInfo.Ask(), digits); //--- if Stop Loss is set if(InpSL>0) { if(spread>=InpSL*point) { PrintFormat("StopLoss (%d points) <= current spread = %.0f points. Spread value will be used", InpSL, spread/point); stoploss = NormalizeDouble(price-spread, digits); } else stoploss = NormalizeDouble(price-InpSL*point, digits); } //--- if Take Profit is set if(InpTP>0) { if(spread>=InpTP*point) { PrintFormat("TakeProfit (%d points) < current spread = %.0f points. Spread value will be used", InpTP, spread/point); takeprofit = NormalizeDouble(price+spread, digits); } else takeprofit = NormalizeDouble(price+InpTP*point, digits); } if(!ExtTrade.Buy(InpLot, Symbol(), price, stoploss, takeprofit)) { PrintFormat("Failed %s buy %G at %G (sl=%G tp=%G) failed. Ask=%G error=%d", Symbol(), InpLot, price, stoploss, takeprofit, ExtSymbolInfo.Ask(), GetLastError()); return(false); } } //--- downtrend if(ExtSignalOpen==SIGNAL_SELL) { price=NormalizeDouble(ExtSymbolInfo.Bid(), digits); //--- if Stop Loss is set if(InpSL>0) { if(spread>=InpSL*point) { PrintFormat("StopLoss (%d points) <= current spread = %.0f points. Spread value will be used", InpSL, spread/point); stoploss = NormalizeDouble(price+spread, digits); } else stoploss = NormalizeDouble(price+InpSL*point, digits); } //--- if Take Profit is set if(InpTP>0) { if(spread>=InpTP*point) { PrintFormat("TakeProfit (%d points) < current spread = %.0f points. Spread value will be used", InpTP, spread/point); takeprofit = NormalizeDouble(price-spread, digits); } else takeprofit = NormalizeDouble(price-InpTP*point, digits); } if(!ExtTrade.Sell(InpLot, Symbol(), price, stoploss, takeprofit)) { PrintFormat("Failed %s sell at %G (sl=%G tp=%G) failed. Bid=%G error=%d", Symbol(), price, stoploss, takeprofit, ExtSymbolInfo.Bid(), GetLastError()); ExtTrade.PrintResult(); Print(" "); return(false); } } return(true); } //+------------------------------------------------------------------+ //| Close a position based on the specified signal | //+------------------------------------------------------------------+ void CloseBySignal(int type_close) { //--- if there is no signal to close, return successful completion if(type_close==SIGNAL_NOT) return; //--- if there are no positions opened by our EA if(PositionExist(ExtSignalClose)==0) return; //--- closing direction long type; switch(type_close) { case CLOSE_SHORT: type=POSITION_TYPE_SELL; break; case CLOSE_LONG: type=POSITION_TYPE_BUY; break; default: Print("Error! Signal to close not detected"); return; } //--- check all positions and close ours based on the signal int positions=PositionsTotal(); for(int i=positions-1; i>=0; i--) { ulong ticket=PositionGetTicket(i); if(ticket!=0) { //--- get the name of the symbol and the position id (magic) string symbol=PositionGetString(POSITION_SYMBOL); long magic =PositionGetInteger(POSITION_MAGIC); //--- if they correspond to our values if(symbol==Symbol() && magic==InpMagicNumber) { if(PositionGetInteger(POSITION_TYPE)==type) { ExtTrade.PositionClose(ticket, InpSlippage); ExtTrade.PrintResult(); Print(" "); } } } } } //+------------------------------------------------------------------+ //| Close positions upon holding time expiration in bars | //+------------------------------------------------------------------+ void CloseByTime() { //--- if there are no positions opened by our EA if(PositionExist(ExtSignalOpen)==0) return; //--- check all positions and close ours based on the holding time in bars int positions=PositionsTotal(); for(int i=positions-1; i>=0; i--) { ulong ticket=PositionGetTicket(i); if(ticket!=0) { //--- get the name of the symbol and the position id (magic) string symbol=PositionGetString(POSITION_SYMBOL); long magic =PositionGetInteger(POSITION_MAGIC); //--- if they correspond to our values if(symbol==Symbol() && magic==InpMagicNumber) { //--- position opening time datetime open_time=(datetime)PositionGetInteger(POSITION_TIME); //--- check position holding time in bars if(BarsHold(open_time)>=(int)InpDuration) { Print("\r\nTime to close position #", ticket); ExtTrade.PositionClose(ticket, InpSlippage); ExtTrade.PrintResult(); Print(" "); } } } } } //+------------------------------------------------------------------+ //| Returns true if there are open positions | //+------------------------------------------------------------------+ bool PositionExist(int signal_direction) { bool check_type=(signal_direction!=SIGNAL_NOT); //--- what positions to search ENUM_POSITION_TYPE search_type=WRONG_VALUE; if(check_type) switch(signal_direction) { case SIGNAL_BUY: search_type=POSITION_TYPE_BUY; break; case SIGNAL_SELL: search_type=POSITION_TYPE_SELL; break; case CLOSE_LONG: search_type=POSITION_TYPE_BUY; break; case CLOSE_SHORT: search_type=POSITION_TYPE_SELL; break; default: //--- entry direction is not specified; nothing to search return(false); } //--- go through the list of all positions int positions=PositionsTotal(); for(int i=0; i=(int)InpDuration)) return(true); } } } //--- open position not found return(false); } //+------------------------------------------------------------------+ //| Checks position closing time in bars | //+------------------------------------------------------------------+ int BarsHold(datetime open_time) { //--- first run a basic simple check if(TimeCurrent()-open_timeAvgBody(1)) && // long black (Open(2)-Close(2)>AvgBody(1)) && (Open(1)-Close(1)>AvgBody(1)) && (MidPoint(2)AvgBody(1)) && // long white (Close(2)-Open(2)>AvgBody(1)) && (Close(1)-Open(1)>AvgBody(1)) && (MidPoint(2)>MidPoint(3)) && // higher midpoints (MidPoint(1)>MidPoint(2))) { ExtPatternDetected=true; ExtSignalOpen=SIGNAL_BUY; ExtPatternInfo="\r\n3 White Soldiers detected"; ExtDirection="Buy"; return(true); } //--- result of checking return(ExtCheckPassed); } //+------------------------------------------------------------------+ //| Returns true in case of successful confirmation check | //+------------------------------------------------------------------+ bool CheckConfirmation() { ExtConfirmed=false; //--- if there is no pattern, do not search for confirmation if(!ExtPatternDetected) return(true); //--- get the value of the stochastic indicator to confirm the signal double signal=MFI(1); if(signal==EMPTY_VALUE) { //--- failed to get indicator value, check failed return(false); } //--- check the Buy signal if(ExtSignalOpen==SIGNAL_BUY && (signal<40)) { ExtConfirmed=true; ExtPatternInfo+="\r\n Confirmed: MFI<40"; } //--- check the Sell signal if(ExtSignalOpen==SIGNAL_SELL && (signal>60)) { ExtConfirmed=true; ExtPatternInfo+="\r\n Confirmed: MFI>60"; } //--- successful completion of the check return(true); } //+------------------------------------------------------------------+ //| Check if there is a signal to close | //+------------------------------------------------------------------+ bool CheckCloseSignal() { ExtSignalClose=false; //--- if there is a signal to enter the market, do not check the signal to close if(ExtSignalOpen!=SIGNAL_NOT) return(true); //--- check if there is a signal to close a long position if(((MFI(1)<70) && (MFI(2)>70)) || ((MFI(1)<30) && (MFI(2)>30))) { //--- there is a signal to close a long position ExtSignalClose=CLOSE_LONG; ExtDirection="Long"; } //--- check if there is a signal to close a short position if(((MFI(1)>30) && (MFI(2)<30)) || ((MFI(1)>70) && (MFI(2)<70))) { //--- there is a signal to close a short position ExtSignalClose=CLOSE_SHORT; ExtDirection="Short"; } //--- successful completion of the check return(true); } //+------------------------------------------------------------------+ //| MFI indicator value at the specified bar | //+------------------------------------------------------------------+ double MFI(int index) { double indicator_values[]; if(CopyBuffer(ExtIndicatorHandle, 0, index, 1, indicator_values)<0) { //--- if the copying fails, report the error code PrintFormat("Failed to copy data from the MFI indicator, error code %d", GetLastError()); return(EMPTY_VALUE); } return(indicator_values[0]); } //+------------------------------------------------------------------+