//+------------------------------------------------------------------+ //| 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 #include // 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 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=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 //+------------------------------------------------------------------+