588 lines
20 KiB
Plaintext
588 lines
20 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| BuyTradeManager.mqh |
|
|
//| 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 <TradeManagersAttributes.mqh>
|
|
#include <Telegram_Handler.mqh>
|
|
#include <Trade/Trade.mqh>
|
|
CTrade tradeLongInTradeManager;
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
class BuyTradeManager
|
|
{
|
|
private:
|
|
int tradeId;
|
|
ulong rejectionByOneCandleTradeArray[3];
|
|
|
|
string firstSecureMethod ;
|
|
bool firstSecureMethodFound;
|
|
int numberOfTakeProfits ;
|
|
bool securedFirstLevelProfit ;
|
|
bool securedSecondLevelProfit;
|
|
double partialToClose ;
|
|
bool isTradeRunning ;
|
|
|
|
|
|
public:
|
|
BuyTradeManager();
|
|
~BuyTradeManager();
|
|
|
|
void fillRejectionByOneCandleTradeArray(int _index, int _id) {rejectionByOneCandleTradeArray[_index] = _id ;}
|
|
ulong getRejectionByOneCandleTradeTicket(int _index) {return rejectionByOneCandleTradeArray[_index] ;}
|
|
void printRejectionByOneCandleTradeArray();
|
|
|
|
void checkAndSecureTradeIfNeeded(double _ratio, double _riskInDollars);
|
|
string findFirstSecureMethod();
|
|
|
|
bool reachedProfitRatio(int _indexInArr);
|
|
bool closeTrade(int _indexInArr);
|
|
bool positionBreakEven(int _indexInArr);
|
|
|
|
|
|
void addTpAtIndex(int _index, double tpPrice) {tpArray[_index] = tpPrice;}
|
|
int countRemainingTakeProfits();
|
|
bool reachedClosestTakeProfit();
|
|
void deleteClosestTp();
|
|
int findClosestTpIndex();
|
|
|
|
|
|
void cleanOrderDataStructures();
|
|
|
|
void setTradeRunning(bool _setting) {isTradeRunning = _setting ;}
|
|
bool tradeIsRunning() {return isTradeRunning ;}
|
|
|
|
double tpArray[3];
|
|
|
|
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
BuyTradeManager::BuyTradeManager()
|
|
{
|
|
|
|
for(int i=0; i<3; i++)
|
|
{
|
|
tpArray[i] = -1;
|
|
}
|
|
securedFirstLevelProfit = false ;
|
|
securedSecondLevelProfit = false ;
|
|
firstSecureMethodFound = false ;
|
|
firstSecureMethod = "NOT_FOUND" ;
|
|
partialToClose = -1 ;
|
|
isTradeRunning = false ;
|
|
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
BuyTradeManager::~BuyTradeManager()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void BuyTradeManager:: printRejectionByOneCandleTradeArray()
|
|
{
|
|
|
|
Print("Printing the tickets :");
|
|
for(int i=0 ; i<3 ; i++)
|
|
{
|
|
Print((int)rejectionByOneCandleTradeArray[i]);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void BuyTradeManager:: checkAndSecureTradeIfNeeded(double _ratio, double _riskInDollars)
|
|
{
|
|
if(securedFirstLevelProfit == false) // in this case im searching for 2 cases: either RR 1:1.5 , OR reaching the first TP , if any of them happened im gonna close half the order and updatae the remaining stop losses
|
|
{
|
|
|
|
|
|
if(!firstSecureMethodFound)
|
|
{
|
|
|
|
firstSecureMethod = findFirstSecureMethod();
|
|
if(firstSecureMethod != "FAILED")
|
|
{
|
|
|
|
firstSecureMethodFound = true ;
|
|
}
|
|
else
|
|
if(firstSecureMethod == "FAILED")
|
|
{
|
|
Print("Failed to find the first secure method, go check the function : findFirstSecureMehod() !");
|
|
}
|
|
|
|
}
|
|
|
|
if(firstSecureMethodFound == true)
|
|
{
|
|
|
|
if(firstSecureMethod == "RATIO_METHOD")
|
|
{
|
|
cleanOrderDataStructures(); // this is used each time before we are choosing an order by ticket
|
|
if(reachedProfitRatio(0))
|
|
{
|
|
ChartRedraw(); // Make sure the chart is up to date
|
|
|
|
if(closeTrade(0)) // close the half
|
|
{
|
|
|
|
rejectionByOneCandleTradeArray[0] = -1;
|
|
Print("Closed Half Of the position due to reaching the first secure Ratio!");
|
|
}
|
|
else
|
|
{
|
|
Print("Failed Closing Half of the order by reaching the first secure Ratio !");
|
|
}
|
|
|
|
cleanOrderDataStructures(); // this is used each time before we are choosing an order by ticket
|
|
if(positionBreakEven(1)) // put the first quarter at break even
|
|
{
|
|
|
|
Print(" First quarter has been put into break even due to reaching the first secure Ratio!");
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to Put the first quarter into break even");
|
|
}
|
|
|
|
numberOfTakeProfits = countRemainingTakeProfits();
|
|
|
|
Print("Number of remaining tps is: " + numberOfTakeProfits);
|
|
|
|
securedFirstLevelProfit = true ;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
else
|
|
if(firstSecureMethod == "FIRST_TP_METHOD")
|
|
{
|
|
if(countRemainingTakeProfits() > 1) // there is at least 2 tp in the tp Array (because if this is the last tp then we want to close the whole order and not just a part of it)
|
|
{
|
|
if(reachedClosestTakeProfit()) // 2. Check if reached the first Tp
|
|
{
|
|
ChartRedraw(); // Make sure the chart is up to date
|
|
ChartScreenShot(0, fileName, 1024, 768, ALIGN_RIGHT);
|
|
|
|
// close the first half and update remaining partials sl's and delete the closest take profit from the array
|
|
cleanOrderDataStructures(); // this is used each time before we are choosing an order by ticket
|
|
if(closeTrade(0)) // close the half
|
|
{
|
|
|
|
rejectionByOneCandleTradeArray[0] = -1;
|
|
Print("Closed Half Of the position due to reaching the first TP!");
|
|
|
|
}
|
|
else
|
|
{
|
|
Print("Failed Closing Half of the order by reaching the first TP !");
|
|
}
|
|
cleanOrderDataStructures(); // this is used each time before we are choosing an order by ticket
|
|
if(positionBreakEven(1)) // put the first quarter at break even
|
|
{
|
|
|
|
Print(" First quarter has been put into break even due to reaching the first TP!");
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to Put the first quarter into break even");
|
|
}
|
|
deleteClosestTp();
|
|
numberOfTakeProfits = countRemainingTakeProfits(); // update the number of remaining take profits
|
|
// calculate the remaining tps
|
|
|
|
Print("Number of remaining tps is: " + numberOfTakeProfits);
|
|
Print("TP ARRY: " + tpArray[0] + ", " + tpArray[1] + " , " + tpArray[2]);
|
|
|
|
securedFirstLevelProfit = true ;
|
|
|
|
|
|
}
|
|
}
|
|
else
|
|
if(numberOfTakeProfits = countRemainingTakeProfits() == 1)
|
|
{
|
|
if(reachedClosestTakeProfit())
|
|
{
|
|
// close everything remained
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
else
|
|
if(securedFirstLevelProfit == true) // after securing the first level i just want to close the remaining partials by reaching the tp's
|
|
{
|
|
|
|
if(countRemainingTakeProfits() == 1)
|
|
{
|
|
if(reachedClosestTakeProfit())
|
|
{
|
|
// close everything
|
|
cleanOrderDataStructures();
|
|
for(int i=0 ; i<3 ; i++)
|
|
{
|
|
closeTrade(i);
|
|
}
|
|
Print("Closed every thing because price reached the last Take Profit !");
|
|
|
|
ChartRedraw(); // Make sure the chart is up to date
|
|
|
|
|
|
deleteClosestTp();
|
|
}
|
|
}
|
|
else
|
|
if(countRemainingTakeProfits() > 1)
|
|
{
|
|
if(reachedClosestTakeProfit())
|
|
{
|
|
cleanOrderDataStructures();
|
|
if(PositionSelectByTicket(rejectionByOneCandleTradeArray[1])) // DEALING WITH THE FIRST QUARTER
|
|
{
|
|
double currentLot = PositionGetDouble(POSITION_VOLUME);
|
|
if(partialToClose == -1)
|
|
{
|
|
partialToClose = currentLot/numberOfTakeProfits ;
|
|
}
|
|
if(tradeLongInTradeManager.PositionClosePartial(rejectionByOneCandleTradeArray[1],NormalizeDouble(partialToClose,2)))
|
|
{
|
|
uint res = tradeLongInTradeManager.ResultRetcode();
|
|
if(res == 10009)
|
|
{
|
|
Print("Closed Partials successfully on the first quarter !");
|
|
}
|
|
else
|
|
{
|
|
Print("The returned code is: " + res + " go check what it means !");
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to select position by ticket! , Error: " + GetLastError());
|
|
Print("The EA tried to close partials on the first quarter, but it is closed, probably due to break even and thats why it couldnt select the order !");
|
|
}
|
|
|
|
|
|
cleanOrderDataStructures();
|
|
if(PositionSelectByTicket(rejectionByOneCandleTradeArray[2])) // DEALING WITH THE SECOND QUARTER
|
|
{
|
|
if(PositionGetDouble(POSITION_SL) < PositionGetDouble(POSITION_PRICE_OPEN)) // this is to put the second quarter at break even if its not already at break even
|
|
{
|
|
positionBreakEven(2);
|
|
|
|
}
|
|
double currentLot = PositionGetDouble(POSITION_VOLUME);
|
|
if(partialToClose == -1)
|
|
{
|
|
partialToClose = currentLot/numberOfTakeProfits ;
|
|
}
|
|
if(tradeLongInTradeManager.PositionClosePartial(rejectionByOneCandleTradeArray[2],NormalizeDouble(partialToClose,2)))
|
|
{
|
|
uint res = tradeLongInTradeManager.ResultRetcode();
|
|
if(res == 10009)
|
|
{
|
|
Print("Closed Partials successfully on the second quarter!");
|
|
}
|
|
else
|
|
{
|
|
Print("The returned code is: " + res + " go check what it means !");
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
deleteClosestTp();
|
|
Print("Number Of Remained tps is: " + countRemainingTakeProfits());
|
|
|
|
ChartRedraw(); // Make sure the chart is up to date
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
string BuyTradeManager:: findFirstSecureMethod()
|
|
{
|
|
|
|
cleanOrderDataStructures(); // this is used each time before we are choosing an order by ticket
|
|
if(PositionSelectByTicket(rejectionByOneCandleTradeArray[0]))
|
|
{
|
|
double positionStopLoss = PositionGetDouble(POSITION_SL);
|
|
double positionOpenPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double firstTakeProfit = tpArray[findClosestTpIndex()];
|
|
if(((firstTakeProfit-positionOpenPrice)/(positionOpenPrice-positionStopLoss) > firstSecureRatio)) // firstTp > ratio
|
|
{
|
|
Print("Found the first profit secure method !");
|
|
Print("The first TP RR is: 1:" + (firstTakeProfit-positionOpenPrice)/(positionOpenPrice-positionStopLoss) + "the firstSecureRatio is: 1:" + firstSecureRatio);
|
|
Print("The tp ratio is greater than the firstSecure so:");
|
|
Print("The first secure method is RATIO_METHOD");
|
|
return"RATIO_METHOD";
|
|
}
|
|
else
|
|
if((firstTakeProfit-positionOpenPrice)/(positionOpenPrice-positionStopLoss)< firstSecureRatio)
|
|
{
|
|
Print("Found the first profit secure method !");
|
|
Print("The first TP RR is: 1:" + (firstTakeProfit-positionOpenPrice)/(positionOpenPrice-positionStopLoss) + "the firstSecureRatio is: 1:" + firstSecureRatio);
|
|
Print("The tp ratio is less than the firstSecure so:");
|
|
Print("The first secure method is FIRST_TP_METHOD");
|
|
return "FIRST_TP_METHOD" ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to select position by ticket 1, Error: " + GetLastError());
|
|
}
|
|
|
|
return "FAILED" ;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool BuyTradeManager:: reachedProfitRatio(int _indexInArr)
|
|
{
|
|
|
|
ulong _ticket = rejectionByOneCandleTradeArray[_indexInArr];
|
|
if(_ticket != -1)
|
|
{
|
|
if(PositionSelectByTicket(_ticket))
|
|
{
|
|
if(PositionGetDouble(POSITION_PROFIT)/(0.5*riskDollars) >= firstSecureRatio) // checking if the first half of the contract reached the ratio of first secure profit
|
|
{
|
|
Print("reached profit ratio !");
|
|
return true ;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to select the position by ticket 4 ,Error: " + GetLastError());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false ;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool BuyTradeManager:: closeTrade(int _indexInArr)
|
|
{
|
|
|
|
|
|
ulong _ticket = rejectionByOneCandleTradeArray[_indexInArr];
|
|
if(_ticket != -1)
|
|
{
|
|
if(PositionSelectByTicket(_ticket))
|
|
{
|
|
|
|
// close the first half and update remaining partials sl's
|
|
if(tradeLongInTradeManager.PositionClose(_ticket))
|
|
{
|
|
return true ;
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
Print("Failed to close positon, Error:" + GetLastError());
|
|
return false;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
Print("Failed to select position by ticket , Error: "+ GetLastError());
|
|
return false;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
return false ;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool BuyTradeManager:: positionBreakEven(int _indexInArr)
|
|
{
|
|
|
|
ulong _ticket = rejectionByOneCandleTradeArray[_indexInArr];
|
|
if(_ticket != -1)
|
|
{
|
|
if(PositionSelectByTicket(_ticket)) // select the first quarter
|
|
{
|
|
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
|
double positionTp = PositionGetDouble(POSITION_TP);
|
|
double positionSl = PositionGetDouble(POSITION_SL);
|
|
|
|
if(tradeLongInTradeManager.PositionModify(_ticket,openPrice,positionTp))
|
|
{
|
|
|
|
|
|
return true ;
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to modify oder , ERROR: " + GetLastError());
|
|
return false ;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Print("Failed to select position by ticket ");
|
|
return false ;
|
|
}
|
|
}
|
|
|
|
|
|
return false ;
|
|
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int BuyTradeManager:: countRemainingTakeProfits()
|
|
{
|
|
|
|
int counter = 0 ;
|
|
for(int i=0; i<3 ; i++)
|
|
{
|
|
if(tpArray[i] != -1)
|
|
{
|
|
counter++ ;
|
|
}
|
|
}
|
|
|
|
return counter;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void BuyTradeManager:: deleteClosestTp()
|
|
{
|
|
|
|
int closestTpIndex =findClosestTpIndex();
|
|
tpArray[closestTpIndex] = -1;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
int BuyTradeManager:: findClosestTpIndex()
|
|
{
|
|
int closestTpIndex = 0;
|
|
int closestTpValue = 1000000 ; // dummy value
|
|
|
|
for(int i=0; i<3; i++)
|
|
{
|
|
if(tpArray[i] != -1 && tpArray[i] < closestTpValue)
|
|
{
|
|
closestTpValue = tpArray[i];
|
|
closestTpIndex = i ;
|
|
}
|
|
|
|
}
|
|
|
|
return closestTpIndex ;
|
|
}
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
bool BuyTradeManager:: reachedClosestTakeProfit()
|
|
{
|
|
|
|
|
|
double closestTakeProfitValue = tpArray[findClosestTpIndex()];
|
|
//Print("im in reached closest take profit function ,the current price is: " + SymbolInfoDouble(_Symbol, SYMBOL_BID) + " and the tp is: " + closestTakeProfitValue);
|
|
if(SymbolInfoDouble(_Symbol, SYMBOL_BID) >= closestTakeProfitValue) // we are looking for the BID , because reaching a tp in a buy trade means we are selling, so we want our take profit value, to be at least as the BID price (because we want to find someone to buy it from us at this price)
|
|
{
|
|
return true ;
|
|
}
|
|
return false ;
|
|
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
void BuyTradeManager:: cleanOrderDataStructures()
|
|
{
|
|
// this is to make sure, that every closed order is removed from the data strucutre !
|
|
|
|
for(int i=0; i<3; i++)
|
|
{
|
|
if(!PositionSelectByTicket(rejectionByOneCandleTradeArray[i]))
|
|
{
|
|
if(GetLastError() == 4753)
|
|
{
|
|
rejectionByOneCandleTradeArray[i] = -1 ;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
|
|
|