forked from LengKundee/MQL5-Google-Onedrive
130 lines
4.4 KiB
MQL5
130 lines
4.4 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| ManagePositions.mqh |
|
|
//| Jules |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Jules"
|
|
#property strict
|
|
|
|
#include <Trade\PositionInfo.mqh>
|
|
#include <Trade\SymbolInfo.mqh>
|
|
#include <Trade\Trade.mqh>
|
|
|
|
class CPositionManager
|
|
{
|
|
private:
|
|
CPositionInfo m_positionInfo;
|
|
CSymbolInfo m_symbolInfo;
|
|
CTrade* m_trade; // Pointer to external CTrade
|
|
|
|
public:
|
|
void Init(CTrade* tradeObj)
|
|
{
|
|
m_trade = tradeObj;
|
|
}
|
|
|
|
void Manage(long magic, string symbol,
|
|
bool useBreakEven, double breakEvenTriggerPips, double breakEvenOffsetPips,
|
|
bool useTrail, double trailingStopStartPips, double trailingStopDistancePips)
|
|
{
|
|
if(m_trade == NULL) return;
|
|
if(!m_symbolInfo.Name(symbol)) return;
|
|
if(!m_symbolInfo.RefreshRates()) return;
|
|
|
|
double point = m_symbolInfo.Point();
|
|
int digits = m_symbolInfo.Digits();
|
|
|
|
// Calculate point value for pips (assuming standard 1 pip = 10 points)
|
|
// For JPY pairs or others, usually it's consistent if we use Point * 10 for "Pip"
|
|
// But let's use the standard "10 points = 1 pip" convention.
|
|
double pipSize = point * 10.0;
|
|
|
|
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
|
{
|
|
if(!m_positionInfo.SelectByIndex(i)) continue;
|
|
if(m_positionInfo.Magic() != magic) continue;
|
|
if(m_positionInfo.Symbol() != symbol) continue;
|
|
|
|
double openPrice = m_positionInfo.PriceOpen();
|
|
double currentSL = m_positionInfo.StopLoss();
|
|
double currentTP = m_positionInfo.TakeProfit();
|
|
ulong ticket = m_positionInfo.Ticket();
|
|
|
|
double newSL = currentSL;
|
|
double profitPips = 0.0;
|
|
|
|
if(m_positionInfo.PositionType() == POSITION_TYPE_BUY)
|
|
{
|
|
double bid = m_symbolInfo.Bid();
|
|
profitPips = (bid - openPrice) / pipSize;
|
|
|
|
// Break Even
|
|
if(useBreakEven && profitPips >= breakEvenTriggerPips)
|
|
{
|
|
double beLevel = openPrice + (breakEvenOffsetPips * pipSize);
|
|
// Move SL to BE if current SL is below BE
|
|
if(currentSL < beLevel || currentSL == 0)
|
|
newSL = beLevel;
|
|
}
|
|
|
|
// Trailing
|
|
if(useTrail && profitPips >= trailingStopStartPips)
|
|
{
|
|
double trailLevel = bid - (trailingStopDistancePips * pipSize);
|
|
// Move SL up if trail level is higher than current SL
|
|
if(trailLevel > newSL || newSL == 0)
|
|
newSL = trailLevel;
|
|
}
|
|
}
|
|
else if(m_positionInfo.PositionType() == POSITION_TYPE_SELL)
|
|
{
|
|
double ask = m_symbolInfo.Ask();
|
|
profitPips = (openPrice - ask) / pipSize;
|
|
|
|
// Break Even
|
|
if(useBreakEven && profitPips >= breakEvenTriggerPips)
|
|
{
|
|
double beLevel = openPrice - (breakEvenOffsetPips * pipSize);
|
|
// Move SL to BE if current SL is above BE
|
|
if(currentSL > beLevel || currentSL == 0)
|
|
newSL = beLevel;
|
|
}
|
|
|
|
// Trailing
|
|
if(useTrail && profitPips >= trailingStopStartPips)
|
|
{
|
|
double trailLevel = ask + (trailingStopDistancePips * pipSize);
|
|
// Move SL down if trail level is lower than current SL
|
|
if(trailLevel < newSL || newSL == 0)
|
|
newSL = trailLevel;
|
|
}
|
|
}
|
|
|
|
if(MathAbs(newSL - currentSL) > point)
|
|
{
|
|
// Normalize
|
|
newSL = NormalizeDouble(newSL, digits);
|
|
|
|
// Check stops level
|
|
double stopLevel = (double)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
|
|
bool valid = true;
|
|
|
|
if(m_positionInfo.PositionType() == POSITION_TYPE_BUY)
|
|
{
|
|
if(m_symbolInfo.Bid() - newSL < stopLevel) valid = false;
|
|
}
|
|
else
|
|
{
|
|
if(newSL - m_symbolInfo.Ask() < stopLevel) valid = false;
|
|
}
|
|
|
|
if(valid)
|
|
{
|
|
if(m_trade->PositionModify(ticket, newSL, currentTP))
|
|
{
|
|
Print("ManagePositions: Position #", ticket, " SL moved to ", newSL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|