Files
EA_with_Python/Include/MyLib/Trading/HITPositionLifecycleManager.mqh
T
2026-06-07 22:06:30 +09:00

173 lines
4.4 KiB
Plaintext

#ifndef HIT_POSITION_LIFECYCLE_MANAGER_MQH
#define HIT_POSITION_LIFECYCLE_MANAGER_MQH
#include <Trade/Trade.mqh>
// Ticket-based position lifecycle manager for hedging accounts.
class CHITPositionLifecycleManager
{
private:
string m_symbol;
ulong m_magic;
ulong m_deviation_points;
int m_close_after_seconds;
CTrade m_trade;
public:
CHITPositionLifecycleManager()
{
m_symbol = _Symbol;
m_magic = 0;
m_deviation_points = 10;
m_close_after_seconds = 0;
}
bool Init(const string symbol,
const ulong magic,
const ulong deviation_points,
const int close_after_seconds)
{
m_symbol = symbol;
m_magic = magic;
m_deviation_points = deviation_points;
m_close_after_seconds = close_after_seconds;
if(m_symbol == "")
{
Print("HITPositionLifecycleManager init failed: symbol is empty.");
return false;
}
ResetLastError();
if(!SymbolSelect(m_symbol, true))
{
Print("HITPositionLifecycleManager init failed: symbol=", m_symbol,
" err=", GetLastError());
return false;
}
m_trade.SetExpertMagicNumber(m_magic);
m_trade.SetDeviationInPoints((int)m_deviation_points);
m_trade.SetTypeFillingBySymbol(m_symbol);
return true;
}
void SetCloseAfterSeconds(const int close_after_seconds)
{
m_close_after_seconds = close_after_seconds;
}
int CountTargetPositions()
{
int count = 0;
for(int i = PositionsTotal() - 1; i >= 0; --i)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(!IsTargetSelectedPosition())
continue;
count++;
}
return count;
}
bool CloseExpiredPositions()
{
if(m_close_after_seconds <= 0)
return false;
bool result = false;
const datetime current_time = TimeCurrent();
for(int i = PositionsTotal() - 1; i >= 0; --i)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
ResetLastError();
if(!PositionSelectByTicket(ticket))
{
Print("HITPositionLifecycleManager select failed: ticket=", ticket,
" err=", GetLastError());
continue;
}
if(!IsTargetSelectedPosition())
continue;
const datetime entry_time = (datetime)PositionGetInteger(POSITION_TIME);
if(entry_time <= 0)
continue;
if(current_time - entry_time < m_close_after_seconds)
continue;
if(CloseSelectedPosition(ticket, "time limit"))
result = true;
}
return result;
}
private:
bool IsTargetSelectedPosition()
{
if(PositionGetString(POSITION_SYMBOL) != m_symbol)
return false;
if((ulong)PositionGetInteger(POSITION_MAGIC) != m_magic)
return false;
return true;
}
string PositionTypeText(const ENUM_POSITION_TYPE type)
{
if(type == POSITION_TYPE_BUY)
return "BUY";
if(type == POSITION_TYPE_SELL)
return "SELL";
return "UNKNOWN";
}
bool CloseSelectedPosition(const ulong ticket,
const string reason)
{
const ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double volume = PositionGetDouble(POSITION_VOLUME);
ResetLastError();
if(!m_trade.PositionClose(ticket, m_deviation_points))
{
Print("HITPositionLifecycleManager close failed: ticket=", ticket,
" symbol=", m_symbol,
" magic=", m_magic,
" type=", PositionTypeText(type),
" volume=", DoubleToString(volume, 2),
" reason=", reason,
" retcode=", m_trade.ResultRetcode(),
" err=", GetLastError());
return false;
}
Print("HITPositionLifecycleManager closed position: ticket=", ticket,
" symbol=", m_symbol,
" magic=", m_magic,
" type=", PositionTypeText(type),
" volume=", DoubleToString(volume, 2),
" reason=", reason,
" retcode=", m_trade.ResultRetcode());
return true;
}
};
#endif