351 lines
27 KiB
MQL5
351 lines
27 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| Expert.mqh |
|
|
//| Copyright 2000-2023, MetaQuotes Ltd. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
#include <Expert\Expert.mqh>
|
|
#include "ExpertSignalCustom.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| Class CExpert. |
|
|
//| Purpose: Base class expert advisor. |
|
|
//| Derives from class CExpert. |
|
|
//+------------------------------------------------------------------+
|
|
class CExpertCustom : public CExpert
|
|
{
|
|
protected:
|
|
CExpertSignalCustom* GetCustomSignal()
|
|
{
|
|
return dynamic_cast<CExpertSignalCustom*>(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);
|
|
}
|
|
//+------------------------------------------------------------------+
|