//+------------------------------------------------------------------+ //| Strategy.mqh | //| Copyright 2016, Vasiliy Sokolov, St-Petersburg, Russia | //| https://www.mql5.com/en/users/c-4 | //+------------------------------------------------------------------+ #property copyright "Copyright 2016, Vasiliy Sokolov." #property link "https://www.mql5.com/en/users/c-4" #property strict #define SOURCE ("ID " + (string)ExpertMagic() + " " + __FUNCTION__) #include #include "XML\XMLBase.mqh" // Work with XML #include "Logs.mqh" // Logging #ifdef __HT__ #include "Position.mqh" #else #include "PositionMT5.mqh" // A class of common positions #endif #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" #include "PendingOrders.mqh" #include "Symbol.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. CTradeEnvironment m_environment; // The trading environment of the portfolio. CArrayObj m_bars_detectors; // 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 RecalcStatistic(PositionsStat &positions); int LastWorkExpDay(MqlDateTime &dt); protected: bool m_multi_symbols; CTradeState m_state; // Returns the trading state. 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 string GetCurrentContract(string symbol); bool CheckCurrentSL(double sl,ENUM_POSITION_TYPE type); COrdersEnvironment PendingOrders; /* 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); virtual void SupportPendingBuy(const MarketEvent &event, CPendingOrder* order); virtual void SupportPendingSell(const MarketEvent &event, CPendingOrder* order); virtual void OnEvent(const MarketEvent& event); virtual bool IsTrackEvents(const MarketEvent &event); int PositionsTotal(ENUM_POSITION_TYPE pos_type, string symbol="", int magic = 0); int OrdersTotal(ENUM_POSITION_TYPE pos_type, string symbol="", int magic = 0); int OrdersTotal(ENUM_ORDER_TYPE order_type, string symbol="", int magic = 0); public: CMoneyManagment MM; // Money management module CTrailing* Trailing; // Module for managing the trailing stop for all positions CStrategy(void); CStrategy(string symbol,string exp_name,uint magic,ENUM_TIMEFRAMES tf); ~CStrategy(void); /*Common properties*/ static CStrategy *GetStrategy(string name); CSymbol WS; 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*/ virtual bool OnInit(void); 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_multi_symbols = false; m_last_changed = 0; m_expert_magic = 0; m_timeframe=PERIOD_CURRENT; Log=CLog::GetLog(); WS.Name(Symbol()); WS.Period(Period()); } //+------------------------------------------------------------------+ //| Basic class with the required parameters. | //+------------------------------------------------------------------+ CStrategy::CStrategy(string symbol,string exp_name,uint magic,ENUM_TIMEFRAMES tf) : WS(symbol, tf) { m_multi_symbols = false; m_last_changed=0; Log=CLog::GetLog(); ExpertSymbol(symbol); ExpertName(exp_name); Timeframe(tf); ExpertMagic(magic); } //+------------------------------------------------------------------+ //| Destructor. Removes the trailing stop module if used | //+------------------------------------------------------------------+ CStrategy::~CStrategy(void) { if(CheckPointer(Trailing)!=POINTER_INVALID) delete Trailing; } //+------------------------------------------------------------------+ //| 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) { } //+------------------------------------------------------------------+ //| Override this method to systematically manage | //| a pending buy order | //| IN: | //| event - the structure described the event, upon receipt | //| of which the method was called. | //| order - the position that you need to manage. | //+------------------------------------------------------------------+ void CStrategy::SupportPendingBuy(const MarketEvent &event, CPendingOrder *order) { } //+------------------------------------------------------------------+ //| Override this method to systematically manage | //| a pending sell order | //| IN: | //| event - the structure described the event, upon receipt | //| of which the method was called. | //| order - the position that you need to manage. | //+------------------------------------------------------------------+ void CStrategy::SupportPendingSell(const MarketEvent &event, CPendingOrder *order) { } //+------------------------------------------------------------------+ //| General OnEvent event. Raised when any event is received | //| regardless of the trading mode and expert settings. | //+------------------------------------------------------------------+ void CStrategy::OnEvent(const MarketEvent &event) { } //+------------------------------------------------------------------+ //| Filters incoming events. If the passed event is not | //| processed by the strategy, returns false; if it is processed | //| returns true. | //+------------------------------------------------------------------+ bool CStrategy::IsTrackEvents(const MarketEvent &event) { //We handle only opening of a new bar on the working symbol and timeframe if(event.type != MARKET_EVENT_BAR_OPEN)return false; if(event.period != Timeframe())return false; if(event.symbol != ExpertSymbol())return false; return true; } //+------------------------------------------------------------------+ //| Delegates the child strategy to parse its specific | //| parameters found in the 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); OnSymbolChanged(m_expert_symbol); WS.Name(symbol); } //+------------------------------------------------------------------+ //| Called by the strategy manager upon the system event | //| 'new tick'. | //+------------------------------------------------------------------+ void CStrategy::OnTick(void) { NewTickDetect(); NewBarsDetect(); } //+------------------------------------------------------------------+ //| This event is called when a strategy is being added to the list | //| of strategies. It is understood that when the function is called,| //| all the basic parameters of the EA, such as the symbol name , | //| magic number and timeframe will be installed. So EA will be able | //| to configure in this method its internal data. | //| If OnInit of the strategy returns false, the strategy will not be| //| added to the list. | //+------------------------------------------------------------------+ bool CStrategy::OnInit(void) { return true; } //+------------------------------------------------------------------+ //| 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(); OnEvent(m_event); 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; OnEvent(m_event); 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_state.GetTradeState()) { m_state.SetTradeState(D'00:00', D'23:59', ALL_DAYS_OF_WEEK, state); string text="The mode of the current strategy has been changed to "+EnumToString(m_state.GetTradeState())+ ". 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(); // Managing active positions for(int i=ActivePositions.Total()-1; i>=0; i--) { CPosition *pos=ActivePositions.At(i); if(pos.ExpertMagic()!=m_expert_magic)continue; if(!m_multi_symbols && pos.Symbol()!=ExpertSymbol())continue; if(CheckPointer(Trailing)!=POINTER_INVALID) { if(CheckPointer(pos.Trailing)==POINTER_INVALID) { pos.Trailing = Trailing.Copy(); pos.Trailing.SetPosition(pos); } pos.Trailing.Modify(); if(!pos.IsActive()) continue; } if(pos.Direction()==POSITION_TYPE_BUY) SupportBuy(event,pos); else SupportSell(event,pos); if(m_trade_state==TRADE_STOP && pos.IsActive()) ExitByStopRegim(pos); } // Deleting pending orders when changing mode if(PendingOrders.Total() > 0 && (m_trade_state == TRADE_STOP || m_trade_state == TRADE_BUY_ONLY || m_trade_state == TRADE_SELL_ONLY)) { for(int p = PendingOrders.Total()-1; p >= 0; p--) { CPendingOrder* pend = PendingOrders.GetOrder(p); if(!pend.IsMain(ExpertSymbol(), ExpertMagic()))continue; bool needDelete = m_trade_state == TRADE_STOP; // SELL_ONLY - delete pending buy orders if(!needDelete) { needDelete = m_trade_state == TRADE_SELL_ONLY && (pend.Type() == ORDER_TYPE_BUY_STOP || pend.Type() == ORDER_TYPE_BUY_LIMIT); } // BUY_ONLY - delete pending sell orders if(!needDelete) { needDelete = m_trade_state == TRADE_BUY_ONLY && (pend.Type() == ORDER_TYPE_SELL_STOP || pend.Type() == ORDER_TYPE_SELL_LIMIT); } if(needDelete) pend.Delete(); } } for(int i = PendingOrders.Total()-1; i >= 0; i--) { CPendingOrder* order = PendingOrders.GetOrder(i); if(order.ExpertMagic()!=m_expert_magic)continue; if(order.Direction() == POSITION_TYPE_BUY) SupportPendingBuy(event, order); else SupportPendingSell(event, order); } } //+------------------------------------------------------------------+ //| 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=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; } //+------------------------------------------------------------------+ //| Returns the total number of positions by the specified criterion | //+------------------------------------------------------------------+ int CStrategy::PositionsTotal(ENUM_POSITION_TYPE pos_type, string symbol="", int magic = 0) { int total = 0; for(int i = 0; i < ActivePositions.Total(); i++) { CPosition* pos = ActivePositions.At(i); if(magic != 0 && magic != pos.ExpertMagic())continue; if(symbol != "" && symbol != pos.Symbol())continue; if(pos.Direction() != pos_type)continue; total++; } return total; } //+------------------------------------------------------------------+ //| Return the total number of pending orders with the direction | //| equal to specified | //+------------------------------------------------------------------+ int CStrategy::OrdersTotal(ENUM_POSITION_TYPE pos_type, string symbol="", int magic = 0) { int total = 0; for(int i = 0; i < PendingOrders.Total(); i++) { CPendingOrder* order = PendingOrders.GetOrder(i); if(magic != 0 && magic != order.ExpertMagic())continue; if(symbol != "" && symbol != order.Symbol())continue; if(order.Direction() != pos_type)continue; total++; } return total; } //+------------------------------------------------------------------+ //| Return the total number of pending orders with the type | //| equal to specified | //+------------------------------------------------------------------+ int CStrategy::OrdersTotal(ENUM_ORDER_TYPE order_type, string symbol="", int magic = 0) { int total = 0; for(int i = 0; i < PendingOrders.Total(); i++) { CPendingOrder* order = PendingOrders.GetOrder(i); if(magic != 0 && magic != order.ExpertMagic())continue; if(symbol != "" && symbol != order.Symbol())continue; if(order.Type() != order_type)continue; total++; } return total; } #include "StrategiesList.mqh" //+------------------------------------------------------------------+