Files
Financial-Markets-research/Tiger.mq5
T
2025-07-11 08:43:07 +03:00

601 lines
21 KiB
Plaintext

//+------------------------------------------------------------------+
//| Tiger.mq5 |
//| Copyright 2023, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include "NewCandleDetector.mqh"
#include "GraphicalObjectsManager.mqh"
#include "Zone.mqh"
#include "ZoneContainer.mqh"
#include "MarketObserver.mqh"
#include "BuyEntryManager.mqh"
#include "LotSizeCalculator.mqh"
input double rangeDistance ;
input double resistanceExtendAboveCandle;
input double supportExtendBelowCandle ;
input double stopLossLimit ;
double input riskInDollars ;
double input netTakeProfit ;
bool input allowSecondChanceStopLoss ;
double input bottomWickSize ;
double input smallBottomWickSize ;
double input buyStopPipsTrigger ;
bool input MOD_CLEAN = false ;
bool waitingForBottomWickToForm = false ;
bool waitingForSmallBottomWickToForm = false ;
bool buyStopValid = false ;
bool closedPartialOnNegativeDirection = false ;
bool securedTenPips = false ;
#include <Trade\Trade.mqh>
CTrade tradeTiger ;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
// GUI CLASSES INITIALIZATION
GraphicalObjectsManager* objectsManager = new GraphicalObjectsManager();
// CONTAINERS INITIALIZATION
ZoneContainer* zoneContainer = new ZoneContainer() ;
// ALGORITHM CLASSES INITIALIZATION
MarketObserver* marketObserver = new MarketObserver(zoneContainer);
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
NewCandleDetector newCandleDetector("PERIOD_M30");
BuyEntryManager buyEntryMan ;
LotSizeCalculator lotSizeCalc ;
Zone* temporaryRestestSupportZone = new Zone();
int OnInit()
{
//--- create timer
EventSetTimer(60);
iClose(_Symbol,PERIOD_M15,5);
iClose(_Symbol,PERIOD_H1,5);
/*datetime d1=D'2023.09.01 00:00:00'; // Year Month Day Hours Minutes Seconds
Print("the dateTime is: " + d1);
Zone* newZone = new Zone() ;
newZone.setHigherEdgePrice(1930);
newZone.setLowerEdgePrice(1925);
newZone.setLeftEdge(iTime(_Symbol,PERIOD_CURRENT,2));
newZone.setRightEdge(d1);
newZone.setId("1");
newZone.setTimeFrame("PERIOD_M30");
zoneContainer.addZone(newZone);
objectsManager.drawRectangleInStrategyTester(1,iTime(_Symbol,PERIOD_CURRENT,2),d1,1930,1925);
zoneContainer.printZonesSortedArray(); */
//---
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//--- destroy timer
EventKillTimer();
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
//---
if(securedTenPips == false ){
secureTenPips(0.3);
}
if(waitingForBottomWickToForm){
if(iClose(_Symbol,PERIOD_CURRENT,1) - SymbolInfoDouble(_Symbol, SYMBOL_BID) >= bottomWickSize){
waitingForBottomWickToForm = false ;
Print("Bottom Wick in length of at least " + bottomWickSize + " was formed !");
buyStopValid = true ;
}
}
if(buyStopValid){
if( SymbolInfoDouble(_Symbol, SYMBOL_ASK) > iHigh(_Symbol,PERIOD_CURRENT,1) + buyStopPipsTrigger){ // here we take a buy entry based on break and retest wick. the Algo will first try to put the stop loss under the low of the temporary support zone
// if that stop loss was too high , then it will put it under the low of the wick that was recently made. (the wick should be at least bottomWickSize which is currently 1)
double stopLossUnderTemporarySupportZone = temporaryRestestSupportZone.getLowerEdge() - 0.5 ; // under the temporary support zone
double stopLossUnderWick = iLow(_Symbol,PERIOD_CURRENT,0) - 0.5; // the low of the current candle
if(!((SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossUnderTemporarySupportZone) > stopLossLimit)){ // on this case a wick was formed price starts to break the previous candle high, im taking a buy
//and im putting the stop loss below the whole zone (which is support now)
double lotsToEnter = lotSizeCalc.calculateLotSize(riskInDollars,stopLossUnderTemporarySupportZone);
buyEntryMan.takeBuyTradeTiger(lotsToEnter,stopLossUnderTemporarySupportZone,2);
closedPartialOnNegativeDirection = false ;
securedTenPips = false ;
buyStopValid = false ;
Print("Took a wick retest buy type 1");
}
else if(!((SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossUnderWick) > stopLossLimit)){ // on this case a wick was formed price starts to break the previous candle high, im taking a buy
//and im putting the stop loss below the wick (which is at least bottomWickSize)
double lotsToEnter = lotSizeCalc.calculateLotSize(riskInDollars,stopLossUnderWick);
buyEntryMan.takeBuyTradeTiger(lotsToEnter,stopLossUnderWick,2);
closedPartialOnNegativeDirection = false ;
securedTenPips = false ;
buyStopValid = false ;
Print("Took a wick retest buy type 2");
}
}
}
if(newCandleDetector.isNewCandle())
{
if(iClose(_Symbol,PERIOD_CURRENT,1) < iOpen(_Symbol,PERIOD_CURRENT,1) && closedPartialOnNegativeDirection == false){ // if the m30 candle closed bearish
closePartialsOfAllTrades(0.5);
closedPartialOnNegativeDirection = true ;
}
buyStopValid = false ;
waitingForBottomWickToForm = false ;
bool finishedDeleting = false ;
MqlDateTime mqldt ;
datetime currTime = TimeCurrent(mqldt);
if(candleClosedBelowRetestZone(PERIOD_CURRENT)){
temporaryRestestSupportZone.setType("TYPE_INVALID");
deleteZoneGuiOnly("999");
}
if(zoneContainer.getNumberOfActiveZones() != 0) // if there are zones found
{
if(marketObserver.candleClosedAboveResistanceByIndex(0,PERIOD_CURRENT)) // check if candle closed above the zone
{
datetime _leftEdgeRestestZone = iTime(_Symbol,PERIOD_CURRENT,4);
datetime _rightEdgeRetestZone = D'2023.11.01 00:00:00';
while(!finishedDeleting && zoneContainer.getNumberOfActiveZones() != 0) // this while is used in the case of a candle closing above more than 1 zone at once
{
/* temporaryRestestSupportZone.setHigherEdgePrice(zoneContainer.getZoneByIndex(0).getHigherEdge());
temporaryRestestSupportZone.setLowerEdgePrice(zoneContainer.getZoneByIndex(0).getLowerEdge());
temporaryRestestSupportZone.setLeftEdge(_leftEdgeRestestZone);
temporaryRestestSupportZone.setRightEdge(_rightEdgeRetestZone);
temporaryRestestSupportZone.setId("999"); // the + "t" stands for temporary and it is made to keep the zones id's unique
temporaryRestestSupportZone.setTimeFrame("PERIOD_M30");
temporaryRestestSupportZone.setType("TYPE_SUPPORT");
deleteZoneWithGUI((zoneContainer.getZoneByIndex(0).getId())); // delete the zone with the GUI */
if(zoneContainer.getNumberOfActiveZones() != 0 && marketObserver.candleClosedAboveResistanceByIndex(0,PERIOD_CURRENT))
{
zoneContainer.getZoneByIndex(0).getHigherEdge();
temporaryRestestSupportZone.setHigherEdgePrice(zoneContainer.getZoneByIndex(0).getHigherEdge());
temporaryRestestSupportZone.setLowerEdgePrice(zoneContainer.getZoneByIndex(0).getLowerEdge());
temporaryRestestSupportZone.setLeftEdge(_leftEdgeRestestZone);
temporaryRestestSupportZone.setRightEdge(_rightEdgeRetestZone);
temporaryRestestSupportZone.setId("999"); // the + "t" stands for temporary and it is made to keep the zones id's unique
temporaryRestestSupportZone.setTimeFrame("PERIOD_M30");
temporaryRestestSupportZone.setType("TYPE_SUPPORT");
deleteZoneWithGUI((zoneContainer.getZoneByIndex(0).getId())); // delete the zone with the GUI
}
else
{
finishedDeleting = true ;
}
}
int idAsInt = StringToInteger(temporaryRestestSupportZone.getId());
if((mqldt.hour > 13 && mqldt.hour < 22) || (mqldt.hour > 3 && mqldt.hour < 7) || (mqldt.hour > 8 && mqldt.hour < 12) )
{
double stopLossPrice = iLow(_Symbol,PERIOD_CURRENT,1);
stopLossPrice = stopLossPrice -1;
double secondChanceStopLossPrice = iLow(_Symbol,PERIOD_M15,1);
secondChanceStopLossPrice = secondChanceStopLossPrice -1;
if(!(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLossPrice > stopLossLimit)) // enter only if the M30 previous candle satisfied the condition of the proper stop loss
{
//Print("Entered here 2!");
double lotsToEnter = lotSizeCalc.calculateLotSize(riskInDollars,stopLossPrice);
buyEntryMan.takeBuyTradeTiger(lotsToEnter,stopLossPrice,netTakeProfit);
closedPartialOnNegativeDirection = false ;
securedTenPips = false ;
}
else
if(!(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - secondChanceStopLossPrice > stopLossLimit) && secondChanceStopLossPrice < temporaryRestestSupportZone.getLowerEdge() - 0.5) // if the M30 candle failed , then try the M15 candle
{
if(allowSecondChanceStopLoss == true)
{
double lotsToEnter = lotSizeCalc.calculateLotSize(riskInDollars,secondChanceStopLossPrice);
buyEntryMan.takeBuyTradeTiger(lotsToEnter,secondChanceStopLossPrice,netTakeProfit);
closedPartialOnNegativeDirection = false ;
securedTenPips = false ;
}
}
else // both M30 and M15 candles closed too far from the zone, so im gonna wait for retracement , and support to be built on the zone, or a bottom wick to form
{
objectsManager.drawRectangleInStrategyTester(idAsInt,_leftEdgeRestestZone,_rightEdgeRetestZone,
temporaryRestestSupportZone.getHigherEdge(),temporaryRestestSupportZone.getLowerEdge(),clrGray);
waitingForBottomWickToForm = true ;
}
}
}
}
if(candleClosedBullish(PERIOD_M30,2) && candleClosedBearish(PERIOD_M30,1))
{
// create a rectangle with a unique name
int currentIdCounter = zoneContainer.getZonesIdCounter();
datetime _leftEdge = iTime(_Symbol,PERIOD_CURRENT,2);
datetime _rightEdge = D'2023.11.01 00:00:00';
double resistancePrice = iOpen(_Symbol,PERIOD_CURRENT,1);
if((zoneContainer.getNumberOfActiveZones() != 0) && (zoneContainer.getZoneByIndex(0).getLowerEdge() - resistancePrice >= rangeDistance))
{
Zone* newZone = new Zone() ;
newZone.setHigherEdgePrice(resistancePrice + resistanceExtendAboveCandle);
newZone.setLowerEdgePrice(resistancePrice);
newZone.setLeftEdge(_leftEdge);
newZone.setRightEdge(_rightEdge);
newZone.setId(IntegerToString(currentIdCounter));
newZone.setTimeFrame("PERIOD_M30");
newZone.setType("TYPE_RESISTANCE");
zoneContainer.addResistanceZoneTiger(newZone);
zoneContainer.incrementZonesIdCounter();
objectsManager.drawRectangleInStrategyTester(currentIdCounter,_leftEdge,_rightEdge,resistancePrice + resistanceExtendAboveCandle,resistancePrice,clrBeige);
//Print("Number of active zones is: " + zoneContainer.getNumberOfActiveZones());
//zoneContainer.printZonesSortedArray();
}
else if((zoneContainer.getNumberOfActiveZones() != 0) && (zoneContainer.getZoneByIndex(0).getLowerEdge() - resistancePrice < rangeDistance)){
double currentHighEdge = resistancePrice + resistanceExtendAboveCandle ; // this is a high edge of a resistance, but its not considered as a zone, because it doesnt have clean range above
double currentLowEdge = resistancePrice; // this is a low edge of a resistance, but its not considered as a zone, because it doesnt have clean range above
}
else
if(zoneContainer.getNumberOfActiveZones() == 0) // this means this is the first zone to add to the data strucutre
{
//Print("Entered here 2");
Zone* newZone = new Zone() ;
newZone.setHigherEdgePrice(resistancePrice + resistanceExtendAboveCandle);
newZone.setLowerEdgePrice(resistancePrice);
newZone.setLeftEdge(_leftEdge);
newZone.setRightEdge(_rightEdge);
newZone.setId(IntegerToString(currentIdCounter));
newZone.setTimeFrame("PERIOD_M30");
newZone.setType("TYPE_RESISTANCE");
zoneContainer.addResistanceZoneTiger(newZone);
zoneContainer.incrementZonesIdCounter();
objectsManager.drawRectangleInStrategyTester(currentIdCounter,_leftEdge,_rightEdge,resistancePrice + resistanceExtendAboveCandle,resistancePrice,clrBeige);
//zoneContainer.printZonesSortedArray();
}
}
Print("Number of active zones is: " + zoneContainer.getNumberOfActiveZones());
Print("Zones are : ");
zoneContainer.printZonesSortedArray();
}
}
//+------------------------------------------------------------------+
//| Timer function |
//+------------------------------------------------------------------+
void OnTimer()
{
//---
}
//+------------------------------------------------------------------+
//| Trade function |
//+------------------------------------------------------------------+
void OnTrade()
{
//---
}
//+------------------------------------------------------------------+
//| TradeTransaction function |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
const MqlTradeRequest& request,
const MqlTradeResult& result)
{
//---
}
//+------------------------------------------------------------------+
//| Tester function |
//+------------------------------------------------------------------+
double OnTester()
{
//---
double ret=0.0;
//---
//---
return(ret);
}
//+------------------------------------------------------------------+
//| TesterInit function |
//+------------------------------------------------------------------+
void OnTesterInit()
{
//---
}
//+------------------------------------------------------------------+
//| TesterPass function |
//+------------------------------------------------------------------+
void OnTesterPass()
{
//---
}
//+------------------------------------------------------------------+
//| TesterDeinit function |
//+------------------------------------------------------------------+
void OnTesterDeinit()
{
//---
}
//+------------------------------------------------------------------+
//| ChartEvent function |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam)
{
//---
}
//+------------------------------------------------------------------+
//| BookEvent function |
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
//---
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool resistanceIsFound()
{
//if()
return false ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool candleClosedBullish(ENUM_TIMEFRAMES _timeFrame, int _index)
{
double candleClose = iClose(_Symbol,_timeFrame,_index);
double candleOpen = iOpen(_Symbol,_timeFrame,_index);
if(candleClose >candleOpen) // the candle closed bullish
{
return true ;
}
return false ;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool candleClosedBearish(ENUM_TIMEFRAMES _timeFrame,int _index)
{
double candleClose = iClose(_Symbol,_timeFrame,_index);
double candleOpen = iOpen(_Symbol,_timeFrame,_index);
if(candleClose < candleOpen) // the candle closed bullish
{
return true ;
}
return false ;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void deleteZoneWithGUI(string _id)
{
zoneContainer.deleteZone(_id);
if(!ObjectDelete(_Symbol,_id))
{
Print("Failed to delete object error: " + GetLastError());
}
}
void deleteZoneGuiOnly(string _id)
{
if(!ObjectDelete(_Symbol,_id))
{
Print("Failed to delete object error: " + GetLastError());
}
}
//+------------------------------------------------------------------+
bool candleClosedBelowRetestZone(ENUM_TIMEFRAMES tf){
double previousCandleClose = iClose(_Symbol,tf,1);
if(previousCandleClose < temporaryRestestSupportZone.getLowerEdge() ){
return true ;
}
return false ;
}
void closePartialsOfAllTrades(double partial){
for(int i = PositionsTotal()-1 ; i>=0 ; i--){
ulong posTicket = PositionGetTicket(i);
if(PositionSelectByTicket(posTicket)){
double positionVolum = PositionGetDouble(POSITION_VOLUME);
double partialToClose = positionVolum * partial ;
tradeTiger.PositionClosePartial(posTicket,NormalizeDouble(partialToClose,2));
}
else{
Print("Failed to select position, Error: " + GetLastError());
}
}
}
void secureTenPips(double partial){
for(int i = PositionsTotal()-1 ; i>=0 ; i--){
ulong posTicket = PositionGetTicket(i);
if(PositionSelectByTicket(posTicket)){
double positionVolume = PositionGetDouble(POSITION_VOLUME);
if((SymbolInfoDouble(_Symbol, SYMBOL_BID) - PositionGetDouble(POSITION_PRICE_OPEN)) > 1){
double partialToClose = positionVolume * partial ;
tradeTiger.PositionClosePartial(posTicket,NormalizeDouble(partialToClose,2));
securedTenPips = true ;
Print("secured 10 pips");
}
}
else{
Print("Failed to select position, Error: " + GetLastError());
}
}
}