266 lines
10 KiB
MQL5
266 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=0;i<total;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(),2);
|
|
}
|
|
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(),2);
|
|
}
|
|
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();
|
|
//---
|
|
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);
|
|
}
|
|
//+------------------------------------------------------------------+
|