Files
All-MQL5-code/Experts/Ref/UnExpert/Strategy/ComplexPosition.mqh
T
2018-06-20 15:35:55 +02:00

370 lines
29 KiB
Plaintext

//+------------------------------------------------------------------+
//| 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>
//+------------------------------------------------------------------+
//| Complex position for working with the statistical arbitrage. |
//+------------------------------------------------------------------+
class CComplexPosition : public CObject
{
private:
CArrayObj m_targets; // The list of tasks
CArrayObj m_positions; // The list of open positions for the active position
uint m_magic; // The EA magic number
bool m_correct; // An indication of a correctly formed position.
CTradeControl* m_trade; // Trading module.
datetime m_time; // Execution time
CLog* Log; // Logging
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);
};
//+------------------------------------------------------------------+
//| Creates a copy of current complex position containing a similar |
//| set of targets. Active positions are not copied, because |
//| there should be only one instance of them. |
//| I.e. a complex position returned by the method not always returns|
//| false when calling the IsActive method |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Default constructor. |
//+------------------------------------------------------------------+
CComplexPosition::CComplexPosition(void) : m_magic(0),
m_correct(true)
{
Log = CLog::GetLog();
m_trade.SetAsyncMode(true);
}
//+------------------------------------------------------------------+
//| Returns the unique ID of the Expert Advisor |
//| the current complex position belongs to. |
//+------------------------------------------------------------------+
uint CComplexPosition::ExpertMagic(void)
{
return m_magic;
}
//+------------------------------------------------------------------+
//| Sets the unique ID of the Expert Advisor |
//| the current complex position belongs to. |
//+------------------------------------------------------------------+
void CComplexPosition::ExpertMagic(uint magic)
{
m_magic = magic;
m_trade.SetExpertMagicNumber(magic);
}
//+------------------------------------------------------------------+
//| Adds a new target to the scenario. |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Checks the correctness of the offered symbol. |
//+------------------------------------------------------------------+
bool CComplexPosition::CheckSymbol(string symbol)
{
datetime times[];
if(CopyTime(symbol, PERIOD_CURRENT, 0, 1, times) < 1)
return false;
return true;
}
//+------------------------------------------------------------------+
//| Clears position scenario. |
//+------------------------------------------------------------------+
void CComplexPosition::Clear(void)
{
if(IsActive())return;
m_correct = true;
m_time = 0;
m_magic = 0;
m_targets.Clear();
}
//+------------------------------------------------------------------+
//| Returns true if the scenario for the current position is |
//|correct or false otherwise. |
//+------------------------------------------------------------------+
bool CComplexPosition::IsCorrect(void)
{
return m_correct;
}
//+------------------------------------------------------------------+
//| Asynchronously executes complex position. |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Asynchronously executes given volume of specified symbol. |
//| Negative volume means Sell, positive volume means 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;
}
//+------------------------------------------------------------------+
//| Checks the correctness of generated position before execution. |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Forms a unique hash for the complex position. |
//+------------------------------------------------------------------+
string CComplexPosition::GetHash(void)
{
uint time = (uint)TimeCurrent();
uint msc = GetTickCount()%1000;
string hash = "CP[" + (string)ExpertMagic() + ":" + (string)time + ":" + (string)msc + "]";
return hash;
}
//+------------------------------------------------------------------+
//| Returns current price of the market neutral position Unlike |
//| the usual position, the price of a market-neutral position can |
//| be negative |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Returns the weighted average price for the specified |
//| 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;
}
//+------------------------------------------------------------------+
//| Returns true if the position is active. |
//| Otherwise it returns false. |
//+------------------------------------------------------------------+
bool CComplexPosition::IsActive(void)
{
return m_positions.Total() > 0;
}
//+------------------------------------------------------------------+
//| Returns the incoming hash comment of complex position. |
//| Returns an empty string if the complex position is not active |
//+------------------------------------------------------------------+
string CComplexPosition::EntryComment(void)
{
if(m_positions.Total() == 0)
return "";
CPosition* pos = m_positions.At(0);
return pos.EntryComment();
}
//+------------------------------------------------------------------+
//| Adds a new active position to the current complex position. |
//+------------------------------------------------------------------+
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));
}
//+------------------------------------------------------------------+
//| Returns true if the scenario of the passed position is equal |
//| current position scenario |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Returns the complex position execution time. If the position |
//| is active, returns the time of the last executed |
//| position included in complex position. If a position is |
//| pending, but its execution started, returns saved time during |
//| execution. If position has not been executed yet, returns zero |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Closes the complex position by market. Closure is performed |
//| in asynchronous mode. |
//+------------------------------------------------------------------+
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;
}
//+------------------------------------------------------------------+
//| Returns profit of the current complex position |
//+------------------------------------------------------------------+
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;
}