UTE/Strategy/ComplexPosition.mqh
super.admin bd7e405a90 convert
2025-05-30 16:34:43 +02:00

370 lines
No EOL
15 KiB
MQL5

//+------------------------------------------------------------------+
//| ComplexPosition.mqh |
//| Copyright 2015, Vasiliy Sokolov. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link "http://www.mql5.com"
#include <Object.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Strategy\TradeControl.mqh>
#include <Strategy\Logs.mqh>
#include <Strategy\Message.mqh>
#include <Strategy\Position.mqh>
#include <Strategy\Target.mqh>
//+------------------------------------------------------------------+
//| Комплексная позиция для работы со статистическим арбитражем. |
//+------------------------------------------------------------------+
class CComplexPosition : public CObject
{
private:
CArrayObj m_targets; // Список заданий
CArrayObj m_positions; // Список открытых позиций для активной позиции
uint m_magic; // Магический номер эксперта
bool m_correct; // Признак того, что позиция сформирована корректно.
CTradeControl* m_trade; // Торговый модуль.
datetime m_time; // Время выполения
CLog* Log; // Логирование
bool CheckSymbol(string symbol);
bool CheckValid(void);
bool Execute(string symbol, double volume, string ts);
double Price(string symbol, double volume);
string GetHash(void);
public:
CComplexPosition(void);
uint ExpertMagic(void);
void ExpertMagic(uint magic);
bool Execute(void);
datetime ExecuteTime(void);
bool AddTarget(string symbol, double volume);
void Clear(void);
double Price(void);
bool IsActive(void);
string EntryComment(void);
bool AddPosition(CPosition* pos);
CComplexPosition* Clone();
bool EqualTargets(CComplexPosition* cp);
bool IsCorrect(void);
bool CloseAtMarket(void);
double Profit(void);
int PositionsTotal();
CPosition* PositionAt(int);
};
//+------------------------------------------------------------------+
//| Создает копию текущей комплексной позиции содержащую аналогичный |
//| набор таргетов. Активные позиции не копируются, т.к. по |
//| определению находятся в единственном экземпляре. |
//| Т.е. возвращемая этим методом комплексная позиция всегда вернет |
//| false при вызове метода IsActive |
//+------------------------------------------------------------------+
CComplexPosition* CComplexPosition::Clone(void)
{
CComplexPosition* cp = new CComplexPosition();
for(int i = 0; i < m_targets.Total(); i++)
{
CTarget* ct = m_targets.At(i);
cp.m_targets.Add(new CTarget(ct.Symbol(), ct.Volume()));
}
cp.m_magic = m_magic;
cp.m_correct = m_correct;
return cp;
}
//+------------------------------------------------------------------+
//| Конструктор по-умолчанию. |
//+------------------------------------------------------------------+
CComplexPosition::CComplexPosition(void) : m_magic(0),
m_correct(true)
{
Log = CLog::GetLog();
m_trade.SetAsyncMode(true);
}
//+------------------------------------------------------------------+
//| Возвращает уникальный идентификатор эксперата, которому |
//| принадлежит текущая комплексная позиция. |
//+------------------------------------------------------------------+
uint CComplexPosition::ExpertMagic(void)
{
return m_magic;
}
//+------------------------------------------------------------------+
//| Устанавливает уникальный идентификатор эксперата, которому |
//| принадлежит текущая комплексная позиция. |
//+------------------------------------------------------------------+
void CComplexPosition::ExpertMagic(uint magic)
{
m_magic = magic;
m_trade.SetExpertMagicNumber(magic);
}
//+------------------------------------------------------------------+
//| Добавляет новый таргет к сценарию. |
//+------------------------------------------------------------------+
bool CComplexPosition::AddTarget(string symbol,double volume)
{
if(m_correct == false)
{
string text = "With the formation of an integrated position error. Adding new characters is not possible." +
"Clean the position of its formation and try again.";
CMessage* msg = new CMessage(MESSAGE_ERROR, __FUNCTION__, text);
Log.AddMessage(msg);
return m_correct;
}
if(!CheckSymbol(symbol))
{
string text = "Symbol " + symbol + " unavailable. Check correct symbol";
CMessage* msg = new CMessage(MESSAGE_ERROR, __FUNCTION__, text);
Log.AddMessage(msg);
return m_correct = false;
}
m_correct = m_targets.Add(new CTarget(symbol, volume));
return m_correct;
}
//+------------------------------------------------------------------+
//| Проверяет предложенный символ на корректность. |
//+------------------------------------------------------------------+
bool CComplexPosition::CheckSymbol(string symbol)
{
datetime times[];
if(CopyTime(symbol, PERIOD_CURRENT, 0, 1, times) < 1)
return false;
return true;
}
//+------------------------------------------------------------------+
//| Очищает сценарий позиции. |
//+------------------------------------------------------------------+
void CComplexPosition::Clear(void)
{
if(IsActive())return;
m_correct = true;
m_time = 0;
m_magic = 0;
m_targets.Clear();
}
//+------------------------------------------------------------------+
//| Возвращает истину если сценарий для текущей позиции составлен |
//| корректно и ложь в противном случае. |
//+------------------------------------------------------------------+
bool CComplexPosition::IsCorrect(void)
{
return m_correct;
}
//+------------------------------------------------------------------+
//| Асинхронно исполняет комплексную позицию. |
//+------------------------------------------------------------------+
bool CComplexPosition::Execute(void)
{
if(!CheckValid())return false;
m_trade.SetExpertMagicNumber(m_magic);
m_trade.SetAsyncMode(true);
string hash = GetHash();
bool res = true;
int total = m_targets.Total();
for(int i = 0; i < total; i++)
{
CTarget* target = m_targets.At(i);
if(!Execute(target.Symbol(), target.Volume(), hash))
res = false;
}
m_time = TimeCurrent();
return res;
}
//+------------------------------------------------------------------+
//| Асинхронно исполняет указанный объем по указанному символу. |
//| Отрицательный объем означает Sell, положительный - Buy |
//+------------------------------------------------------------------+
bool CComplexPosition::Execute(string symbol, double volume, string ts)
{
bool res = false;
string op = volume > 0.0 ? "Buy" : "Sell";
if(volume > 0.0)
res = m_trade.Buy(volume, symbol, ts);
else
res = m_trade.Sell(MathAbs(volume), symbol, ts);
if(!res)
{
string vol = DoubleToString(MathAbs(volume), 2);
string text = op + " " + vol + " failed. Reason " + m_trade.ResultRetcodeDescription() + " (" + (string)m_trade.ResultRetcode()+ ")";
CMessage* msg = new CMessage(MESSAGE_ERROR, __FUNCTION__, text);
msg.Retcode(m_trade.ResultRetcode());
Log.AddMessage(msg);
}
return res;
}
//+------------------------------------------------------------------+
//| Проверяет корректность сформированной позиции перед выполнением. |
//+------------------------------------------------------------------+
bool CComplexPosition::CheckValid(void)
{
bool res = true;
if(m_magic == 0)
{
string text = "Magic number of complex position not set. Set magic and try again";
CMessage* msg = new CMessage(MESSAGE_ERROR, __FUNCTION__, text);
Log.AddMessage(msg);
res = false;
}
if(!m_correct)
{
string text = "Position is not formed correctly. Execution is not possible";
CMessage* msg = new CMessage(MESSAGE_ERROR, __FUNCTION__, text);
Log.AddMessage(msg);
res = false;
}
if(m_targets.Total() == 0)
{
string text = "Position has no targets. Add at least one target";
CMessage* msg = new CMessage(MESSAGE_ERROR, __FUNCTION__, text);
Log.AddMessage(msg);
res = false;
}
return res;
}
//+------------------------------------------------------------------+
//| Формирует уникальный хеш для комплексной позиции. |
//+------------------------------------------------------------------+
string CComplexPosition::GetHash(void)
{
uint time = (uint)TimeCurrent();
uint msc = GetTickCount()%1000;
string hash = "CP[" + (string)ExpertMagic() + ":" + (string)time + ":" + (string)msc + "]";
return hash;
}
//+------------------------------------------------------------------+
//| Возвращает текущую цену рыночно-нейтральной позиции. В отличии |
//| от обычной позиции, цена у рыночно-нейтральной позиции может |
//| быть отрицательной |
//+------------------------------------------------------------------+
double CComplexPosition::Price(void)
{
double price = 0.0;
for(int i = 0; i < m_targets.Total(); i++)
{
CTarget* target = m_targets.At(i);
price += Price(target.Symbol(), target.Volume());
}
return price;
}
//+------------------------------------------------------------------+
//| Возвращает средневзвешенную на объем цену по заданному |
//| инструменту symbol. |
//+------------------------------------------------------------------+
double CComplexPosition::Price(string symbol,double volume)
{
double close[];
double price = 0.0;
if(volume > 0)
price = SymbolInfoDouble(symbol, SYMBOL_ASK);
else
price = SymbolInfoDouble(symbol, SYMBOL_BID);
price *= volume;
return price;
}
//+------------------------------------------------------------------+
//| Возвращает истину, если текущая позиция является активной. |
//| Возвращает ложь в противном случае. |
//+------------------------------------------------------------------+
bool CComplexPosition::IsActive(void)
{
return m_positions.Total() > 0;
}
//+------------------------------------------------------------------+
//| Возвращает входящий комментарий-хеш комплексной позиции. |
//| Возвращает пустую строку если комплексная позиция не активна |
//+------------------------------------------------------------------+
string CComplexPosition::EntryComment(void)
{
if(m_positions.Total() == 0)
return "";
CPosition* pos = m_positions.At(0);
return pos.EntryComment();
}
//+------------------------------------------------------------------+
//| Добавляет в текущую комплексную позицию новую активную позицию. |
//+------------------------------------------------------------------+
bool CComplexPosition::AddPosition(CPosition *pos)
{
if(m_positions.Total() > 0)
{
if(EntryComment() != pos.EntryComment())
{
string text = "The added position #" + (string)pos.ID() +
" is not compatible with the current comment complex position. Adding impossible.";
CMessage* msg = new CMessage(MESSAGE_WARNING, __FUNCTION__, text);
Log.AddMessage(msg);
return false;
}
if(ExpertMagic() != pos.ExpertMagic())
{
string text = "The added position #" + (string)pos.ID() +
" is not compatible with the current expert number complex position. Adding impossible.";
CMessage* msg = new CMessage(MESSAGE_WARNING, __FUNCTION__, text);
Log.AddMessage(msg);
return false;
}
}
m_magic = pos.ExpertMagic();
m_positions.Add(pos);
double vol = pos.Volume();
if(pos.Direction() == POSITION_TYPE_SELL)
vol *= (-1);
return m_targets.Add(new CTarget(pos.Symbol(), vol));
}
//+------------------------------------------------------------------+
//| Возвращает истину, если сценарий переданной позиции равен |
//| сценарию текущей позиции. |
//+------------------------------------------------------------------+
bool CComplexPosition::EqualTargets(CComplexPosition *cp)
{
if(cp.m_targets.Total() != m_targets.Total())return false;
for(int i = 0; i < m_targets.Total(); i++)
{
CTarget* me = m_targets.At(i);
CTarget* tg = cp.m_targets.At(i);
if(me != tg)return false;
}
return true;
}
//+------------------------------------------------------------------+
//| Вовзвращает время исполнения комплексной позиции. Если позиция |
//| является активной, возвращается время последней исполненной |
//| позиции входящей в комплексную позицию. Если позиция является |
//| отложенной, но запущенной на выполнение - запомненное время |
//| при выполнении. Если позиция еще не выполнялась - возвращает ноль|
//+------------------------------------------------------------------+
datetime CComplexPosition::ExecuteTime(void)
{
if(m_time == 0 && m_positions.Total() > 0)
{
CPosition* pos = m_positions.At(m_positions.Total()-1);
return pos.TimeOpen();
}
return m_time;
}
//+------------------------------------------------------------------+
//| Закрывает комплексную позицию по рынку. Закрытие происходит |
//| в асинхронном режиме. |
//+------------------------------------------------------------------+
bool CComplexPosition::CloseAtMarket(void)
{
bool res = true;
for(int i = 0; i < m_positions.Total(); i++)
{
CPosition* pos = m_positions.At(i);
if(!pos.CloseAtMarket(pos.Volume(), 0, "exit from cp", true))
res = false;
}
m_time = TimeCurrent();
return res;
}
//+------------------------------------------------------------------+
//| Возвращает профит текущей комплексной позиции. |
//+------------------------------------------------------------------+
double CComplexPosition::Profit(void)
{
double profit = 0;
for(int i = 0; i < m_positions.Total(); i++)
{
CPosition* pos = m_positions.At(i);
profit += pos.Profit();
}
return profit;
}