//+------------------------------------------------------------------+ //| Expert.mqh | //| Copyright 2000-2023, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include #include "ExpertSignalCustom.mqh" //+------------------------------------------------------------------+ //| Class CExpert. | //| Purpose: Base class expert advisor. | //| Derives from class CExpert. | //+------------------------------------------------------------------+ class CExpertCustom : public CExpert { protected: CExpertSignalCustom* GetCustomSignal() { return dynamic_cast(m_signal); } bool CloseAndDeleteAllForSymbol(void); public: CExpertCustom(void); ~CExpertCustom(void); //--- event handlers virtual void OnTick(void) override; virtual void OnTimer(void) override; virtual void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) override; //--- initialization trading objects virtual bool InitSignal(CExpertSignal *signal = NULL) override; //--- methods of creating the indicator and timeseries virtual bool SetPriceSeries(CiOpen *open, CiHigh *high, CiLow *low, CiClose *close) override; virtual bool SetOtherSeries(CiSpread *spread, CiTime *time, CiTickVolume *tick_volume, CiRealVolume *real_volume) override; //--- initialization trading objects virtual bool InitTrade(ulong magic, CExpertTrade *trade = NULL) override; protected: //--- refreshing virtual bool Refresh(void) override; //--- processing (main method) virtual bool Processing(void) override; }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CExpertCustom::CExpertCustom(void) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CExpertCustom::~CExpertCustom(void) { } //+------------------------------------------------------------------+ //| Setting pointers of price timeseries. | //+------------------------------------------------------------------+ bool CExpertCustom::SetPriceSeries(CiOpen *open, CiHigh *high, CiLow *low, CiClose *close) override { //--- check the initialization phase if(m_init_phase != INIT_PHASE_VALIDATION) { //Print(__FUNCTION__+": changing of timeseries is forbidden"); return(false); } //--- check pointers if((IS_OPEN_SERIES_USAGE && open == NULL) || (IS_HIGH_SERIES_USAGE && high == NULL) || (IS_LOW_SERIES_USAGE && low == NULL) || (IS_CLOSE_SERIES_USAGE && close == NULL)) { Print(__FUNCTION__ + ": NULL pointer"); return(false); } m_open = open; m_high = high; m_low = low; m_close = close; //--- ok return(true); } //+------------------------------------------------------------------+ //| Setting pointers of other timeseries. | //+------------------------------------------------------------------+ bool CExpertCustom::SetOtherSeries(CiSpread *spread, CiTime *time, CiTickVolume *tick_volume, CiRealVolume *real_volume) override { //--- check the initialization phase if(m_init_phase != INIT_PHASE_VALIDATION) { //Print(__FUNCTION__+": changing of timeseries is forbidden"); return(false); } //--- check pointers if((IS_SPREAD_SERIES_USAGE && spread == NULL) || (IS_TIME_SERIES_USAGE && time == NULL) || (IS_TICK_VOLUME_SERIES_USAGE && tick_volume == NULL) || (IS_REAL_VOLUME_SERIES_USAGE && real_volume == NULL)) { Print(__FUNCTION__ + ": NULL pointer"); return(false); } m_spread = spread; m_time = time; m_tick_volume = tick_volume; m_real_volume = real_volume; //--- ok return(true); } //+------------------------------------------------------------------+ //| Initialization signal object | //+------------------------------------------------------------------+ bool CExpertCustom::InitSignal(CExpertSignal *signal) { if(m_signal != NULL) delete m_signal; //--- if(signal == NULL) { if((m_signal = new CExpertSignalCustom) == NULL) return(false); } else m_signal = signal; //--- initializing signal object if(!m_signal.Init(GetPointer(m_symbol), m_period, m_adjusted_point)) return(false); m_signal.EveryTick(m_every_tick); m_signal.Magic(m_magic); //--- ok return(true); } //+------------------------------------------------------------------+ //| Refreshing data for processing | //+------------------------------------------------------------------+ bool CExpertCustom::Refresh(void) { MqlDateTime time; //--- refresh rates if(!m_symbol.RefreshRates()) return(false); //--- check need processing TimeToStruct(m_symbol.Time(), time); if(m_period_flags != WRONG_VALUE && m_period_flags != 0) if((m_period_flags & TimeframesFlags(time)) == 0) return(false); m_last_tick_time = time; //--- refresh indicators m_indicators.Refresh(); //--- ok return(true); } //+------------------------------------------------------------------+ //| Main function | //+------------------------------------------------------------------+ bool CExpertCustom::Processing(void) { //--- calculate signal direction once //Print("Setting direction..."); m_signal.SetDirection(); //Print("Done."); //--- check if open positions if(SelectPosition()) { //--- open position is available //--- check the possibility of reverse the position if(CheckReverse()) return(true); //--- check the possibility of closing the position/delete pending orders if(!CheckClose()) { //--- check the possibility of modifying the position if(CheckTrailingStop()) return(true); //--- return without operations return(false); } } //--- check if plased pending orders int total = OrdersTotal(); if(total != 0) { for(int i = total - 1; i >= 0; i--) { m_order.SelectByIndex(i); if(m_order.Symbol() != m_symbol.Name()) continue; if(m_order.OrderType() == ORDER_TYPE_BUY_LIMIT || m_order.OrderType() == ORDER_TYPE_BUY_STOP) { //--- check the ability to delete a pending order to buy if(CheckDeleteOrderLong()) return(true); //--- check the possibility of modifying a pending order to buy if(CheckTrailingOrderLong()) return(true); } else { //--- check the ability to delete a pending order to sell if(CheckDeleteOrderShort()) return(true); //--- check the possibility of modifying a pending order to sell if(CheckTrailingOrderShort()) return(true); } //--- return without operations return(false); } } //--- check the possibility of opening a position/setting pending order if(CheckOpen()) return(true); //--- return without operations return(false); } //+------------------------------------------------------------------+ //| OnTick handler | //+------------------------------------------------------------------+ void CExpertCustom::OnTick(void) { //--- check process flag if(!m_on_tick_process) return; //--- close positions and orders at specified time if(targetDayOfWeek != -1 && targetMinutes != -1 && targetHour != -1) { // Buffer in minutes for checking the condition int bufferMinutes = 1; // ±1 minute buffer // Get current server time datetime currentTimeValue = TimeCurrent(); MqlDateTime currentTime; TimeToStruct(currentTimeValue, currentTime); // Convert to MqlDateTime // Extract the current hour and minute int currentHour = currentTime.hour; int currentMinute = currentTime.min; // Check if the current day matches the target day if(currentTime.day_of_week == targetDayOfWeek || targetDayOfWeek == CLOSE_EVERYDAY) { // Check if the current time is within the buffer range around the target time if(currentHour == targetHour) { // Check if the current minute falls within the ±1 minute buffer of the target minute if(currentMinute >= (targetMinutes - bufferMinutes) && currentMinute <= (targetMinutes + bufferMinutes)) { // If it matches, call CloseAndDeleteAllForSymbol if(CloseAndDeleteAllForSymbol()) { // Log or handle the successful close Print("Positions and orders closed."); } } } } } CExpertSignalCustom* customSignal = GetCustomSignal(); if(customSignal != NULL) customSignal.OnTickHandler(); //--- updated quotes and indicators // Print("Refreshing data..."); if(!Refresh()) return; // Print("Done."); //--- expert processing // Print("Processing..."); Processing(); // Print("Done."); } //+------------------------------------------------------------------+ //| OnChartEvent handler | //+------------------------------------------------------------------+ void CExpertCustom::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- check process flag if(!m_on_chart_event_process) return; CExpertSignalCustom* customSignal = GetCustomSignal(); if(customSignal != NULL) customSignal.OnChartEventHandler(id, lparam, dparam, sparam); } //+------------------------------------------------------------------+ //| Close all positions and delete all pending orders for Symbol() | //+------------------------------------------------------------------+ bool CExpertCustom::CloseAndDeleteAllForSymbol() { bool result = false; // Track if any action was successfully performed // Iterate through all positions for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong positionTicket = PositionGetTicket(i); // Check if the position matches the current symbol and close it if(PositionSelect(_Symbol) && m_trade.PositionClose(positionTicket)) { result = true; // A position was successfully closed } } // Iterate through all pending orders for(int j = OrdersTotal() - 1; j >= 0; j--) { ulong orderTicket = OrderGetTicket(j); // Check if the order matches the current symbol and delete it if(OrderSelect(orderTicket) && m_trade.OrderDelete(orderTicket)) { result = true; // An order was successfully deleted } } return result; // Return true if any action was performed, false otherwise } //+------------------------------------------------------------------+ //| OnTimer handler | //+------------------------------------------------------------------+ void CExpertCustom::OnTimer(void) { //--- check process flag if(!m_on_timer_process) return; CExpertSignalCustom* customSignal = GetCustomSignal(); if(customSignal != NULL && dbm.OpenDatabase() != false) { customSignal.ProcessBufferedSignals(); customSignal.UpdateSignalsWeights(); if(!IsBacktesting) dbm.CloseDatabase(); } else { // Handle the case where m_signal is not a CExpertSignalCustom } } //+------------------------------------------------------------------+ //| Initialization trade object | //+------------------------------------------------------------------+ bool CExpertCustom::InitTrade(ulong magic, CExpertTrade *trade = NULL) { if(m_trade != NULL) delete m_trade; //--- if(trade == NULL) { if((m_trade = new CExpertTrade) == NULL) return(false); } else m_trade = trade; //--- tune trade object m_trade.SetSymbol(GetPointer(m_symbol)); m_trade.SetExpertMagicNumber(magic); m_trade.SetMarginMode(); m_trade.SetAsyncMode(true); //--- set default deviation for trading in adjusted points m_trade.SetDeviationInPoints((ulong)(3 * m_adjusted_point / m_symbol.Point())); //--- ok return(true); } //+------------------------------------------------------------------+