270 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			270 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
//+------------------------------------------------------------------+
 | 
						|
//|                                           CalculateBreakeven.mqh |
 | 
						|
//|                                  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 <Trade\PositionInfo.mqh>
 | 
						|
 | 
						|
#include "BreakEvenResult.mqh"
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
class CalculateBreakeven
 | 
						|
  {
 | 
						|
private:
 | 
						|
   int               Total_Buy_Trades;
 | 
						|
   double            Total_Buy_Size;
 | 
						|
   double            Total_Buy_Price;
 | 
						|
   double            Buy_Profit;
 | 
						|
   //---
 | 
						|
   int               Total_Sell_Trades;
 | 
						|
   double            Total_Sell_Size;
 | 
						|
   double            Total_Sell_Price;
 | 
						|
   double            Sell_Profit;
 | 
						|
   int               PipAdjust,NrOfDigits;
 | 
						|
   double            point;
 | 
						|
public:
 | 
						|
                     CalculateBreakeven();
 | 
						|
                    ~CalculateBreakeven();
 | 
						|
 | 
						|
   BreakEvenHedgeTicketsResult GetHedge(double lotsize,long magicnumber);
 | 
						|
   BreakEvenResult   GetBreakEven(const BreakEvenInput &breakEvenInput);
 | 
						|
  };
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CalculateBreakeven::CalculateBreakeven()
 | 
						|
  {
 | 
						|
   if(NrOfDigits==5 || NrOfDigits==3)
 | 
						|
      PipAdjust=10;
 | 
						|
   else
 | 
						|
      if(NrOfDigits==4 || NrOfDigits==2)
 | 
						|
         PipAdjust=1;
 | 
						|
//---
 | 
						|
   point=Point()*PipAdjust;
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CalculateBreakeven::~CalculateBreakeven()
 | 
						|
  {
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
BreakEvenHedgeTicketsResult CalculateBreakeven::GetHedge(double lotsize,long magicnumber)
 | 
						|
  {
 | 
						|
   CPositionInfo m_position;
 | 
						|
 | 
						|
   BreakEvenHedgeTicketsResult result;
 | 
						|
   int total=PositionsTotal();
 | 
						|
   if(magicnumber==0)
 | 
						|
      return result;
 | 
						|
//---
 | 
						|
   datetime oldestbuy=TimeCurrent();
 | 
						|
   datetime oldestsell=TimeCurrent();
 | 
						|
   double oldestbuylotsize=0;
 | 
						|
   double oldestsellLotsize=0;
 | 
						|
   ulong oldestBuyTicket=0;
 | 
						|
   ulong oldestSellTicket=0;
 | 
						|
   double lotSizeFilledBuy=0;
 | 
						|
   double lotSizeFilledSell=0;
 | 
						|
   lotsize=NormalizeDouble(lotsize,2);
 | 
						|
 | 
						|
   for(int i=total-1;i>=0;i--)
 | 
						|
     {
 | 
						|
      int ord=m_position.SelectByIndex(i);
 | 
						|
 | 
						|
      if(m_position.PositionType()==POSITION_TYPE_BUY && m_position.Symbol()==Symbol()  && magicnumber!=m_position.Magic())
 | 
						|
        {
 | 
						|
         if(lotSizeFilledBuy<lotsize)
 | 
						|
           {
 | 
						|
            if(m_position.Volume()<=(lotsize-lotSizeFilledBuy))
 | 
						|
              {
 | 
						|
               result.lotsizes.Add(m_position.Volume());
 | 
						|
               result.ticketnumbers.Add(m_position.Ticket());
 | 
						|
               result.Buy.Add(1);
 | 
						|
               lotSizeFilledBuy=NormalizeDouble(lotSizeFilledBuy+m_position.Volume(),1);
 | 
						|
              }
 | 
						|
         else if(m_position.Volume()>(lotsize-lotSizeFilledBuy) && ((lotsize-lotSizeFilledBuy)>0))
 | 
						|
             {
 | 
						|
              result.lotsizes.Add(lotsize-lotSizeFilledBuy);
 | 
						|
             result.ticketnumbers.Add(m_position.Ticket());
 | 
						|
               result.Buy.Add(1);
 | 
						|
              lotSizeFilledBuy=lotsize;
 | 
						|
             }
 | 
						|
           }
 | 
						|
 | 
						|
        }
 | 
						|
      if(m_position.PositionType()==POSITION_TYPE_SELL && m_position.Symbol()==Symbol()  && magicnumber!=m_position.Magic())
 | 
						|
 | 
						|
         {
 | 
						|
         
 | 
						|
          if(lotSizeFilledSell<lotsize)
 | 
						|
           {
 | 
						|
            if(m_position.Volume()<=lotsize-lotSizeFilledSell)
 | 
						|
              {
 | 
						|
               result.lotsizes.Add(m_position.Volume());
 | 
						|
               result.ticketnumbers.Add(m_position.Ticket());
 | 
						|
               result.Buy.Add(0);
 | 
						|
               lotSizeFilledSell=NormalizeDouble(lotSizeFilledSell+m_position.Volume(),1);
 | 
						|
              }
 | 
						|
          else if(m_position.Volume()>(lotsize-lotSizeFilledSell) && ((lotsize-lotSizeFilledSell)>0))
 | 
						|
             {
 | 
						|
              result.lotsizes.Add(lotsize-lotSizeFilledSell);
 | 
						|
             result.ticketnumbers.Add(m_position.Ticket());
 | 
						|
               result.Buy.Add(0);
 | 
						|
              lotSizeFilledSell=lotsize;
 | 
						|
             }
 | 
						|
           }
 | 
						|
         
 | 
						|
         }
 | 
						|
 | 
						|
     }
 | 
						|
   return result;
 | 
						|
  }
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
BreakEvenResult CalculateBreakeven::GetBreakEven(const BreakEvenInput &breakEvenInput)
 | 
						|
  {
 | 
						|
 | 
						|
 | 
						|
   Total_Buy_Trades=0;
 | 
						|
   Total_Buy_Size=0;
 | 
						|
   Total_Buy_Price=0;
 | 
						|
   Buy_Profit=0;
 | 
						|
//---
 | 
						|
   Total_Sell_Trades=0;
 | 
						|
   Total_Sell_Size=0;
 | 
						|
   Total_Sell_Price=0;
 | 
						|
   Sell_Profit=0;
 | 
						|
 | 
						|
   BreakEvenResult result;
 | 
						|
   BreakEvenHedgeTicketsResult hedges=GetHedge(breakEvenInput.HedgeToBreakDownLots,breakEvenInput.MagicNumber);
 | 
						|
   CPositionInfo m_position;
 | 
						|
   double Pip_Value=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE)*PipAdjust;
 | 
						|
   double Pip_Size=SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE)*PipAdjust;
 | 
						|
//---
 | 
						|
   int total=PositionsTotal();
 | 
						|
   if (breakEvenInput.MagicNumber==3)
 | 
						|
   {
 | 
						|
    int h=0;
 | 
						|
   }
 | 
						|
//---
 | 
						|
   for(int i=0;i<total;i++)
 | 
						|
     {
 | 
						|
      int ord=m_position.SelectByIndex(i);
 | 
						|
        {
 | 
						|
         if(m_position.PositionType()==POSITION_TYPE_BUY && m_position.Symbol()==Symbol()  && breakEvenInput.Buys==true &&((breakEvenInput.MagicNumber==0)||(m_position.Magic()==breakEvenInput.MagicNumber)))
 | 
						|
           {
 | 
						|
            Total_Buy_Trades++;
 | 
						|
            Total_Buy_Price+=m_position.PriceOpen()*m_position.Volume();
 | 
						|
            Total_Buy_Size += m_position.Volume();
 | 
						|
            Buy_Profit+=m_position.Profit()+m_position.Swap()+m_position.Commission();
 | 
						|
            result.TicketsToClose.Add(m_position.Ticket());
 | 
						|
            result.TicketsLotSizeToClose.Add(m_position.Volume());
 | 
						|
           }
 | 
						|
         if(m_position.PositionType()==POSITION_TYPE_BUY && m_position.Symbol()==Symbol()  && breakEvenInput.Buys==true &&((breakEvenInput.MagicNumber!=0)&&(m_position.Magic()<=breakEvenInput.MagicNumber)))
 | 
						|
           {
 | 
						|
            long temptic=m_position.Ticket();
 | 
						|
            int index=-1;
 | 
						|
            for(int y=0;y<hedges.ticketnumbers.Total();y++)
 | 
						|
              {
 | 
						|
               if(hedges.ticketnumbers.At(y)==temptic)
 | 
						|
                  index=y;
 | 
						|
 | 
						|
              }
 | 
						|
 | 
						|
            if(index>-1)
 | 
						|
              {
 | 
						|
               double portionvolume=hedges.lotsizes.At(index);
 | 
						|
               Total_Buy_Trades++;
 | 
						|
               Total_Buy_Price+=m_position.PriceOpen()*portionvolume;
 | 
						|
               Total_Buy_Size += portionvolume;
 | 
						|
               Buy_Profit+=(m_position.Profit()*portionvolume)/m_position.Volume()+(m_position.Swap()*portionvolume)/m_position.Volume()+(m_position.Commission()*portionvolume)/m_position.Volume();
 | 
						|
               result.TicketsToClose.Add(m_position.Ticket());
 | 
						|
               result.TicketsLotSizeToClose.Add(portionvolume);
 | 
						|
              }
 | 
						|
           }
 | 
						|
 | 
						|
         if(m_position.PositionType()==POSITION_TYPE_SELL && m_position.Symbol()==Symbol() && breakEvenInput.Sells==true && ((breakEvenInput.MagicNumber==0)||(m_position.Magic()==breakEvenInput.MagicNumber)))
 | 
						|
           {
 | 
						|
            Total_Sell_Trades++;
 | 
						|
            Total_Sell_Size+=m_position.Volume();
 | 
						|
            Total_Sell_Price+=m_position.PriceOpen()*m_position.Volume();
 | 
						|
            Sell_Profit+=m_position.Profit()+m_position.Swap()+m_position.Commission();
 | 
						|
            result.TicketsToClose.Add(m_position.Ticket());
 | 
						|
            result.TicketsLotSizeToClose.Add(m_position.Volume());
 | 
						|
           }
 | 
						|
 | 
						|
         if(m_position.PositionType()==POSITION_TYPE_SELL && m_position.Symbol()==Symbol() && breakEvenInput.Sells==true && ((breakEvenInput.MagicNumber!=0)&&(m_position.Magic()<=breakEvenInput.MagicNumber)))
 | 
						|
           {
 | 
						|
            long temptic=m_position.Ticket();
 | 
						|
            int index=-1;
 | 
						|
            for(int y=0;y<hedges.ticketnumbers.Total();y++)
 | 
						|
              {
 | 
						|
               if(hedges.ticketnumbers.At(y)==temptic)
 | 
						|
                  index=y;
 | 
						|
 | 
						|
              }
 | 
						|
 | 
						|
 | 
						|
            if(index>-1)
 | 
						|
              {
 | 
						|
               double portionvolume=hedges.lotsizes.At(index);
 | 
						|
               Total_Sell_Trades++;
 | 
						|
               Total_Sell_Size+=portionvolume;
 | 
						|
               Total_Sell_Price+=m_position.PriceOpen()*portionvolume;
 | 
						|
               Sell_Profit+=(m_position.Profit()*portionvolume)/m_position.Volume()+(m_position.Swap()*portionvolume)/m_position.Volume()+(m_position.Commission()*portionvolume)/m_position.Volume();
 | 
						|
               result.TicketsToClose.Add(m_position.Ticket());
 | 
						|
               result.TicketsLotSizeToClose.Add(portionvolume);
 | 
						|
              }
 | 
						|
           }
 | 
						|
        }
 | 
						|
     }
 | 
						|
   if(Total_Buy_Price>0)
 | 
						|
     {
 | 
						|
      Total_Buy_Price/=Total_Buy_Size;
 | 
						|
     }
 | 
						|
 | 
						|
   if(Total_Sell_Price>0)
 | 
						|
     {
 | 
						|
      Total_Sell_Price/=Total_Sell_Size;
 | 
						|
     }
 | 
						|
 | 
						|
   result.Net_Trades=Total_Buy_Trades+Total_Sell_Trades;
 | 
						|
   result.Net_Lots=Total_Buy_Size-Total_Sell_Size;
 | 
						|
   result.Net_Result=Buy_Profit+Sell_Profit;
 | 
						|
 | 
						|
 | 
						|
   if(result.Net_Trades>0 &&  result.Net_Lots!=0)
 | 
						|
     {
 | 
						|
      result.distance=(((result.Net_Result-breakEvenInput.ProfitInMoney)/(MathAbs(result.Net_Lots*SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE))))*SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE));
 | 
						|
      if(result.Net_Lots>0)
 | 
						|
        {
 | 
						|
         result.Average_Price= SymbolInfoDouble(_Symbol, SYMBOL_BID)- result.distance;
 | 
						|
        }
 | 
						|
      if(result.Net_Lots<0)
 | 
						|
        {
 | 
						|
         result.Average_Price= SymbolInfoDouble(_Symbol, SYMBOL_ASK)+ result.distance;
 | 
						|
        }
 | 
						|
     }
 | 
						|
   if(result.Net_Trades>0 && result.Net_Lots==0)
 | 
						|
     {
 | 
						|
      result.distance=(result.Net_Result/((SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE)))*SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE));
 | 
						|
      result.Average_Price=SymbolInfoDouble(_Symbol, SYMBOL_BID)-result.distance;
 | 
						|
     }
 | 
						|
 | 
						|
 | 
						|
   return(result);
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 |