Files

685 lines
62 KiB
Plaintext
Raw Permalink Normal View History

2018-06-23 11:25:42 +02:00
//+------------------------------------------------------------------+
//| Strategy.mqh |
//| Copyright 2015, Vasiliy Sokolov. |
//| http://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, Vasiliy Sokolov."
#property link "http://www.mql5.com"
#property strict
#define SOURCE ("ID " + (string)ExpertMagic() + " " + __FUNCTION__)
#include <Object.mqh>
#include <XML\XMLBase.mqh> // Work with XML
#include "Logs.mqh" // Logging
#include "PositionMT5.mqh" // A class of common positions
#include "TradeEnvironment.mqh" // A class for detecting changes in trading environment
#include "NewBarDetector.mqh" // New bar detector
#include "NewTickDetector.mqh" // New tick detector
#include "Series.mqh" // Provides easy access to OHLCV of the data series
#include "TradeControl.mqh" // Trading module with additional methods of control of open positions
#include "TradeState.mqh" // Trading module with additional methods of control of open positions
#include "MoneyManagment.mqh"
//+------------------------------------------------------------------+
//| Defiles the type of market event. |
//+------------------------------------------------------------------+
enum ENUM_MARKET_EVENT_TYPE
{
MARKET_EVENT_TICK, // Arrival of a new tick for the current symbol
MARKET_EVENT_BAR_OPEN, // Opening of a new bar of the current instrument
MARKET_EVENT_TIMER, // Timer
MARKET_EVENT_BOOK_EVENT // Depth of Market change (including tick arrival).
};
//+------------------------------------------------------------------+
//| Parameters of the event that caused method call. |
//+------------------------------------------------------------------+
struct MarketEvent
{
ENUM_MARKET_EVENT_TYPE type; // Event type.
ENUM_TIMEFRAMES period; // Timeframe of the chart the event belongs to (only for MARKET_EVENT_BAR_OPEN).
string symbol; // Name of the symbol on which the event occurred. For all events except
// MARKET_EVENT_BOOK_EVENT, symbol name corresponds to the current instrument.
};
//+------------------------------------------------------------------+
//| Main statistics of open positions of the strategy (instance) |
//+------------------------------------------------------------------+
struct PositionsStat
{
int open_buy; // Total number of open positions of a Buy strategy
int open_sell; // Total number of open positions of a Sell strategy
int open_total; // Total number of open positions of the strategy
int open_complex; // The total number of complex positions belonging to this strategy
};
//+------------------------------------------------------------------+
//| Basic class of the layer strategy. |
//+------------------------------------------------------------------+
class CStrategy : public CObject
{
private:
MarketEvent m_event; // Last or current market event.
ulong m_last_changed; // Time of the last change of trading environment in micro seconds since launch.
uint m_expert_magic; // A unique ID of the Expert Advisor.
string m_expert_name; // Expert Advisor name.
string m_expert_symbol; // The symbol the EA is running on
ENUM_TIMEFRAMES m_timeframe; // The timeframe of the strategy
ENUM_TRADE_STATE m_trade_state; // EA's trading state.
CTradeState m_state; // Returns the trading state.
CTradeEnvironment m_environment; // The trading environment of the portfolio.
CArrayObj m_bars_detecors; // Contain new bar detectors.
CArrayObj m_ticks_detectors; // Contain new tick detectors.
void RebuildPositions(void);
void CallInit(const MarketEvent &event);
void CallSupport(const MarketEvent &event);
void SpyEnvironment(void);
virtual void ExitByStopRegim(CPosition *pos);
void NewBarsDetect(void);
void NewTickDetect(void);
void InitSeries(string symbol,ENUM_TIMEFRAMES period);
void RecalcStatistic(PositionsStat &positions);
int LastWorkExpDay(MqlDateTime &dt);
protected:
CArrayObj ActivePositions; // The list of COMMON active classical positions.
CArrayObj ComplexPositions; // The list of COMMON complex positions consisting of many classical positions
PositionsStat positions; // Statistics of the strategy's positions
CTradeControl Trade; // Trading class (CStrategy has no trading logic).
CArrayObj Modules; // Additional support modules
static CLog* Log; // EA logs
/* MQL4-type access to quotes */
CTime Time;
COpen Open;
CHigh High;
CLow Low;
CClose Close;
CVolume Volume;
string GetCurrentContract(string symbol);
bool CheckCurrentSL(double sl,ENUM_POSITION_TYPE type);
/* Subscription to events "opening of a new bar" and "formation of a new tick" */
bool AddBarOpenEvent(string symbol,ENUM_TIMEFRAMES timeframe);
bool AddTickEvent(string symbol);
void CheckVolumes(void);
/* The EA must redraw its indicators when the symbol and timeframe change,
for this it should monitor appropriate events
*/
virtual void OnSymbolChanged(string symbol);
virtual void OnTimeframeChanged(ENUM_TIMEFRAMES tf);
/* Trading functions to override */
virtual void InitBuy(const MarketEvent &event);
virtual void InitSell(const MarketEvent &event);
virtual void InitComplexPos(const MarketEvent &event);
virtual void SupportBuy(const MarketEvent &event,CPosition *pos);
virtual void SupportSell(const MarketEvent &event,CPosition *pos);
public:
CMoneyManagment MM; // Money management module
CStrategy(void);
CStrategy(string symbol,string exp_name,uint magic,ENUM_TIMEFRAMES tf);
/*Common properties*/
static CStrategy *GetStrategy(string name);
virtual bool ParseXmlParams(CXmlElement *params);
uint ExpertMagic(void);
void ExpertMagic(uint ExpertMagic);
string ExpertName(void);
void ExpertName(string name);
virtual string ExpertNameFull(void);
string ExpertSymbol();
void ExpertSymbol(string symbol);
void Timeframe(ENUM_TIMEFRAMES period);
ENUM_TIMEFRAMES Timeframe(void);
ENUM_TRADE_STATE TradeState(void);
void TradeState(ENUM_TRADE_STATE state);
/*External management*/
void Buy(double vol);
void Sell(double vol);
/*Passing events*/
void OnTick(void);
void OnTimer(void);
void OnBookEvent(string symbol);
virtual void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result){;}
virtual int Compare(const CObject *node,const int mode=0) const;
};
/*Placing static variables*/
CLog *CStrategy::Log;
//+------------------------------------------------------------------+
//| Basic class of the layer strategy. |
//+------------------------------------------------------------------+
CStrategy::CStrategy(void)
{
m_last_changed = 0;
m_expert_magic = 0;
m_timeframe=PERIOD_CURRENT;
Log=CLog::GetLog();
}
//+------------------------------------------------------------------+
//| Basic class with the required parameters. |
//+------------------------------------------------------------------+
CStrategy::CStrategy(string symbol,string exp_name,uint magic,ENUM_TIMEFRAMES tf)
{
m_last_changed=0;
Log=CLog::GetLog();
ExpertSymbol(symbol);
ExpertName(exp_name);
Timeframe(tf);
ExpertMagic(magic);
}
//+------------------------------------------------------------------+
//| If the basic symbol has changed, the Expert Advisor needs to |
//| redraw its indicators and other internal data to work with |
//| this symbol, for this it needs to override this handler |
//| of symbol change |
//+------------------------------------------------------------------+
void CStrategy::OnSymbolChanged(string symbol)
{
}
//+------------------------------------------------------------------+
//| If the working timeframe has changed, the Expert Advisor needs |
//| to redraw its indicators and other internal data to work |
//| with this timeframe, for which it needs to override this |
//| handler of timeframe change |
//+------------------------------------------------------------------+
void CStrategy::OnTimeframeChanged(ENUM_TIMEFRAMES tf)
{
}
//+------------------------------------------------------------------+
//| Override the method using the strategy rule, which being met |
//| a LONG position should be opened. The position opening should |
//| also be performed straight in this method |
//| IN: |
//| event - the structure described the event, upon receipt |
//| of which the method was called. |
//+------------------------------------------------------------------+
void CStrategy::InitBuy(const MarketEvent &event)
{
}
//+------------------------------------------------------------------+
//| Override the method using the strategy rule, which being met |
//| a SHORT position should be opened. The position opening should |
//| also be performed straight in this method |
//| IN: |
//| event - the structure described the event, upon receipt |
//| of which the method was called. |
//+------------------------------------------------------------------+
void CStrategy::InitSell(const MarketEvent &event)
{
}
//+------------------------------------------------------------------+
//| Override the method using the strategy rule, which being met |
//| a COMPLEX (or arbitrage) position should be opened. |
//| The opening of the position should be performed |
//| right in this method |
//| IN: |
//| event - the structure described the event, upon receipt |
//| of which the method was called. |
//+------------------------------------------------------------------+
void CStrategy::InitComplexPos(const MarketEvent &event)
{
}
//+------------------------------------------------------------------+
//| Override the method using the strategy rule, which being met |
//| you should close the LONG position passed as the second |
//| parameter. The closing of position should also be performed |
//| right in this method |
//| IN: |
//| event - the structure described the event, upon receipt |
//| of which the method was called. |
//| pos - the position that you need to manage. |
//+------------------------------------------------------------------+
void CStrategy::SupportBuy(const MarketEvent &event,CPosition *pos)
{
}
//+------------------------------------------------------------------+
//| Override the method using the strategy rule, which being met |
//| you should close the SHORT position passed as the second |
//| parameter. The closing of position should also be performed |
//| right in this method |
//| IN: |
//| event - the structure described the event, upon receipt |
//| of which the method was called. |
//| pos - the position that you need to manage. |
//+------------------------------------------------------------------+
void CStrategy::SupportSell(const MarketEvent &event,CPosition *pos)
{
}
//+------------------------------------------------------------------+
//| Delegates the child strategy to parse its specific |
//| parameters found in the <Params> section |
//+------------------------------------------------------------------+
bool CStrategy::ParseXmlParams(CXmlElement *xmlParams)
{
string text="Found specific xml-settings, but "+ExpertName()+" strategy does not handle them. Override the method ParseXmlParams";
CMessage *msg=new CMessage(MESSAGE_WARNING,SOURCE,text);
Log.AddMessage(msg);
return false;
}
//+------------------------------------------------------------------+
//| Sets a unique identifier of the Expert Advisor. |
//+------------------------------------------------------------------+
void CStrategy::ExpertMagic(uint ExpertMagic)
{
m_expert_magic=ExpertMagic;
Trade.SetExpertMagicNumber(m_expert_magic);
}
//+------------------------------------------------------------------+
//| Returns the unique identifier of the EA. |
//+------------------------------------------------------------------+
uint CStrategy::ExpertMagic(void)
{
return m_expert_magic;
}
//+------------------------------------------------------------------+
//| Returns the name of the Expert Advisor (the name must be |
//| previously set by the EA using the appropriate method). |
//+------------------------------------------------------------------+
string CStrategy::ExpertName(void)
{
return m_expert_name;
}
//+------------------------------------------------------------------+
//| Using this method, the EA sets its name. |
//+------------------------------------------------------------------+
void CStrategy::ExpertName(string name)
{
m_expert_name=name;
}
//+------------------------------------------------------------------+
//| Returns the full (unique) name of the EA. |
//| (This method must be overridden in the derived class) |
//+------------------------------------------------------------------+
string CStrategy::ExpertNameFull(void)
{
return ExpertName();
}
//+------------------------------------------------------------------+
//| Returns the working symbol of the EA. |
//+------------------------------------------------------------------+
string CStrategy::ExpertSymbol(void)
{
if(m_expert_symbol==NULL || m_expert_symbol=="")
return _Symbol;
return m_expert_symbol;
}
//+------------------------------------------------------------------+
//| Sets the working symbol of the EA. |
//+------------------------------------------------------------------+
void CStrategy::ExpertSymbol(string symbol)
{
m_expert_symbol=GetCurrentContract(symbol);
InitSeries(symbol,Timeframe());
OnSymbolChanged(m_expert_symbol);
}
//+------------------------------------------------------------------+
//| Called by the strategy manager upon the system event |
//| 'new tick'. |
//+------------------------------------------------------------------+
void CStrategy::OnTick(void)
{
NewTickDetect();
NewBarsDetect();
}
//+------------------------------------------------------------------+
//| Called by the strategy manager upon the system event |
//| 'OnTimer'. |
//+------------------------------------------------------------------+
void CStrategy::OnTimer(void)
{
m_event.symbol=Symbol();
m_event.type=MARKET_EVENT_TIMER;
m_event.period=(ENUM_TIMEFRAMES)Period();
CallSupport(m_event);
CallInit(m_event);
NewTickDetect();
NewBarsDetect();
}
//+------------------------------------------------------------------+
//| Called by the strategy manager upon the system event |
//| 'OnBookEvent'. |
//+------------------------------------------------------------------+
void CStrategy::OnBookEvent(string symbol)
{
m_event.symbol=symbol;
m_event.type=MARKET_EVENT_BOOK_EVENT;
m_event.period=PERIOD_CURRENT;
CallSupport(m_event);
CallInit(m_event);
NewTickDetect();
NewBarsDetect();
}
//+------------------------------------------------------------------+
//| On behalf of the EA, buy the volume of vol |
//+------------------------------------------------------------------+
void CStrategy::Buy(double vol)
{
Trade.Buy(vol,ExpertSymbol(),"hand buy");
}
//+------------------------------------------------------------------+
//| On behalf of the EA, sell the volume of vol |
//+------------------------------------------------------------------+
void CStrategy::Sell(double vol)
{
Trade.Sell(vol,ExpertSymbol(),"hand sell");
}
//+------------------------------------------------------------------+
//| Returns the current trading state of the EA.   |
//+------------------------------------------------------------------+
ENUM_TRADE_STATE CStrategy::TradeState(void)
{
return m_trade_state;
}
//+------------------------------------------------------------------+
//| Sets the current trading state of the EA.   |
//+------------------------------------------------------------------+
void CStrategy::TradeState(ENUM_TRADE_STATE state)
{
if(state!=m_trade_state)
{
m_trade_state=state;
string text="The mode of the current strategy has been changed to "+EnumToString(m_trade_state)+
". The changes will come into force at receipt of new events";
CMessage *msg=new CMessage(MESSAGE_INFO,SOURCE,text);
Log.AddMessage(msg);
}
}
//+------------------------------------------------------------------+
//| Calls position opening logic provided that the trading |
//| state does not explicitly restrict this. |
//+------------------------------------------------------------------+
void CStrategy::CallInit(const MarketEvent &event)
{
m_trade_state=m_state.GetTradeState();
if(m_trade_state == TRADE_STOP)return;
if(m_trade_state == TRADE_WAIT)return;
if(m_trade_state == TRADE_NO_NEW_ENTRY)return;
SpyEnvironment();
InitComplexPos(event);
if(m_trade_state==TRADE_BUY_AND_SELL || m_trade_state==TRADE_BUY_ONLY)
InitBuy(event);
if(m_trade_state==TRADE_BUY_AND_SELL || m_trade_state==TRADE_SELL_ONLY)
InitSell(event);
}
//+------------------------------------------------------------------+
//| Calls position maintenance logic provided that the trading |
//| state isn't equal to TRADE_WAIT |
//+------------------------------------------------------------------+
void CStrategy::CallSupport(const MarketEvent &event)
{
m_trade_state=m_state.GetTradeState();
if(m_trade_state == TRADE_WAIT)return;
SpyEnvironment();
for(int i=ActivePositions.Total()-1; i>=0; i--)
{
CPosition *pos=ActivePositions.At(i);
if(pos.ExpertMagic()!=m_expert_magic)continue;
if(pos.Symbol()!=ExpertSymbol())continue;
if(pos.Direction()==POSITION_TYPE_BUY)
SupportBuy(event,pos);
else
SupportSell(event,pos);
if(m_trade_state==TRADE_STOP && pos.IsActive())
ExitByStopRegim(pos);
}
}
//+------------------------------------------------------------------+
//| Tracks changes in the trading environment. |
//+------------------------------------------------------------------+
void CStrategy::SpyEnvironment(void)
{
if(m_environment.ChangeEnvironment())
{
printf(ExpertNameFull()+". Trading environment has changed. Rebuild the environment");
RebuildPositions();
RecalcStatistic(positions);
m_environment.RememberEnvironment();
}
}
//+------------------------------------------------------------------+
//| Recalculates statistics of positions and fills in appropriate |
//| structure. |
//+------------------------------------------------------------------+
void CStrategy::RecalcStatistic(PositionsStat &pos)
{
pos.open_buy=0;
pos.open_sell=0;
pos.open_total=0;
pos.open_complex=0;
for(int i=0; i<ActivePositions.Total(); i++)
{
CPosition *cpos=ActivePositions.At(i);
if(cpos.ExpertMagic()!=ExpertMagic())continue;
pos.open_total+=1;
if(cpos.Direction()==POSITION_TYPE_BUY)
pos.open_buy++;
else
pos.open_sell++;
}
}
//+------------------------------------------------------------------+
//| Rearranges lists of positions |
//+------------------------------------------------------------------+
void CStrategy::RebuildPositions(void)
{
ActivePositions.Clear();
for(int i=0; i<PositionsTotal(); i++)
{
string symbol=PositionGetSymbol(i);
PositionSelect(symbol);
CPosition *pos=new CPosition();
ActivePositions.Add(pos);
}
}
//+------------------------------------------------------------------+
//| Sets working timeframe of the strategy. |
//+------------------------------------------------------------------+
void CStrategy::Timeframe(ENUM_TIMEFRAMES period)
{
m_timeframe=period;
InitSeries(ExpertSymbol(),m_timeframe);
OnTimeframeChanged(m_timeframe);
}
//+------------------------------------------------------------------+
//| Returns working timeframe of strategy. |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CStrategy::Timeframe(void)
{
return m_timeframe;
}
//+------------------------------------------------------------------+
//| Sets OHLCV series for the current instrument and timeframe. |
//+------------------------------------------------------------------+
void CStrategy::InitSeries(string symbol,ENUM_TIMEFRAMES period)
{
Time.Symbol(symbol);
Time.Timeframe(period);
Open.Symbol(symbol);
Open.Timeframe(period);
High.Symbol(symbol);
High.Timeframe(period);
Low.Symbol(symbol);
Low.Timeframe(period);
Close.Symbol(symbol);
Close.Timeframe(period);
Volume.Symbol(symbol);
Volume.Timeframe(period);
}
//+------------------------------------------------------------------+
//| Closes position by the Stop mode. |
//+------------------------------------------------------------------+
void CStrategy::ExitByStopRegim(CPosition *pos)
{
ResetLastError();
CMessage *msg_info=new CMessage(MESSAGE_INFO,ExpertName(),"Try close position #"+(string)pos.ID()+" by stop regim...");
Log.AddMessage(msg_info);
bool res=pos.CloseAtMarket("Exit by StopRegim");
if(res)
CMessage *msg=new CMessage(MESSAGE_INFO,ExpertName(),"Out of position #"+(string)pos.ID()+" successfully completed.");
// If position closure failed, the pos.CloseAtMarket method will notify if this.
}
//+------------------------------------------------------------------+
//| Detects emergence of a new bar and generates an appropriate |
//| event for the EA. |
//+------------------------------------------------------------------+
void CStrategy::NewBarsDetect(void)
{
if(m_bars_detecors.Total()==0)
AddBarOpenEvent(ExpertSymbol(),Timeframe());
for(int i=0; i<m_bars_detecors.Total(); i++)
{
CBarDetector *bar=m_bars_detecors.At(i);
if(bar.IsNewBar())
{
m_event.period = bar.Timeframe();
m_event.symbol = bar.Symbol();
m_event.type=MARKET_EVENT_BAR_OPEN;
CallSupport(m_event);
CallInit(m_event);
}
}
}
//+------------------------------------------------------------------+
//| Detects the arrival of new ticks of multi-instruments. |
//+------------------------------------------------------------------+
void CStrategy::NewTickDetect(void)
{
if(m_ticks_detectors.Total()==0)
AddTickEvent(ExpertSymbol());
for(int i=0; i<m_ticks_detectors.Total(); i++)
{
CTickDetector *tick=m_ticks_detectors.At(i);
if(tick.IsNewTick())
{
m_event.period=PERIOD_CURRENT;
m_event.type=MARKET_EVENT_TICK;
m_event.symbol=tick.Symbol();
CallSupport(m_event);
CallInit(m_event);
}
}
}
//+------------------------------------------------------------------+
//| Subscribes the EA to receive an event of new bar opening |
//| for the specified 'symbol' and 'timeframe'. If the EA is already |
//| subscribed to the event, method returns false. If subscription |
//| successful, returns true. |
//+------------------------------------------------------------------+
bool CStrategy::AddBarOpenEvent(string symbol,ENUM_TIMEFRAMES timeframe)
{
for(int i=0; i<m_bars_detecors.Total(); i++)
{
CBarDetector *d=m_bars_detecors.At(i);
if(d.Symbol()==symbol && d.Timeframe()==timeframe)
{
string text="You are already subscribed to the opening bars of said symbol and timeframe.";
CMessage *message=new CMessage(MESSAGE_INFO,__FUNCTION__,text);
return false;
}
}
datetime time[];
if(CopyTime(symbol,timeframe,0,3,time)==0)
{
string text="A symbol "+symbol+" that you want to monitor is not available in the terminal."+
" Make sure that the name of the instrument and its timeframe"+EnumToString(timeframe)+" are correct.";
CMessage *message=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
return false;
}
CBarDetector *bar=new CBarDetector(symbol,timeframe);
return m_bars_detecors.Add(bar);
}
//+------------------------------------------------------------------+
//| Subscribes the EA to receive the 'new tick' event |
//| for a selected symbol. If the EA is already subscribed to the |
//| event, method returns false. If subscription |
//| successful, returns true. |
//+------------------------------------------------------------------+
bool CStrategy::AddTickEvent(string symbol)
{
for(int i=0; i<m_bars_detecors.Total(); i++)
{
CTickDetector *d=m_ticks_detectors.At(i);
if(d.Symbol()==symbol)
{
string text="You are already subscribed to new tick event of said symbol.";
CMessage *message=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
return false;
}
}
datetime time[];
if(CopyTime(symbol,PERIOD_D1,0,3,time)==0)
{
string text="A symbol "+symbol+" that you want to monitor is not available in the terminal."+
" Make sure that the name of the instrument are correct.";
CMessage *message=new CMessage(MESSAGE_WARNING,__FUNCTION__,text);
return false;
}
CTickDetector *tick=new CTickDetector(symbol);
return m_ticks_detectors.Add(tick);
}
//+------------------------------------------------------------------+
//| Returns the full name of the current contract for FORTS that |
//| corresponds to the passed symbol. E.g., if the current date is |
//| is 02.10.2015, and the passed symbol is "Si", the method returns |
//| the name of nearest contract: Si-12.15. |
//| Before returning symbol name, it checks its actual availability |
//| for trading; and if it is unavailable, the method returns
//| NULL. Otherwise returns the full name of the contract. |
//+------------------------------------------------------------------+
string CStrategy::GetCurrentContract(string symbol)
{
datetime time[];
if(SymbolInfoInteger(symbol,SYMBOL_SELECT))
return symbol;
MqlDateTime dt={0};
TimeCurrent(dt);
int mon=0;
int year=0;
if((dt.mon==12 && dt.day>=14) || dt.mon==1 || dt.mon==2 || (dt.mon==3 && dt.day<14))
{
mon=3;
year=dt.year+1;
}
if((dt.mon==3 && dt.day>=14) || dt.mon==4 || dt.mon==5 || (dt.mon==6 && dt.day<14))
{
mon=6;
year=dt.year;
}
if((dt.mon==6 && dt.day>=14) || dt.mon==7 || dt.mon==8 || (dt.mon==9 && dt.day<14))
{
mon=9;
year=dt.year;
}
if((dt.mon==9 && dt.day>=14) || dt.mon==10 || dt.mon==11 || (dt.mon==12 && dt.day<14))
{
mon=12;
year=dt.year;
}
if(mon==0 || year==0)
{
string text="The current date is outside the execution of futures";
CMessage *msg=new CMessage(MESSAGE_ERROR,__FUNCTION__,text);
return NULL;
}
//Let's try to generate a symbol and receive its details
string full_symbol=symbol+"-"+(string)mon+"."+StringSubstr((string)year,2);
if(!SymbolInfoInteger(full_symbol,SYMBOL_SELECT))
{
string text="Symbol "+symbol+" is not not selected in market watch. Check the name of the instrument";
CMessage *msg=new CMessage(MESSAGE_ERROR,__FUNCTION__,text);
Log.AddMessage(msg);
return NULL;
}
return full_symbol;
}
//+------------------------------------------------------------------+
//| Overrides magic based comparison |
//+------------------------------------------------------------------+
int CStrategy::Compare(const CObject *obj,const int mode=0)const
{
const CStrategy *str=obj;
if(m_expert_magic > str.m_expert_magic)return 1;
if(m_expert_magic < str.m_expert_magic)return -1;
return 0;
}
#include <Strategy\StrategyFactory.mqh>
//+------------------------------------------------------------------+