370 lines
No EOL
15 KiB
MQL5
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;
|
|
} |