MobinMQL/Include/Expert/ExpertSignal.mqh
2025-07-22 14:47:41 +03:00

464 lines
19 KiB
MQL5

//+------------------------------------------------------------------+
//| ExpertSignal.mqh |
//| Copyright 2000-2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#include "ExpertBase.mqh"
//+------------------------------------------------------------------+
//| Macro definitions. |
//+------------------------------------------------------------------+
//--- check if a market model is used
#define IS_PATTERN_USAGE(p) ((m_patterns_usage&(((int)1)<<p))!=0)
//+------------------------------------------------------------------+
//| Class CExpertSignal. |
//| Purpose: Base class trading signals. |
//| Derives from class CExpertBase. |
//+------------------------------------------------------------------+
class CExpertSignal : public CExpertBase
{
protected:
//--- variables
double m_base_price; // base price for detection of level of entering (and/or exit?)
//--- variables for working with additional filters
CArrayObj m_filters; // array of additional filters (maximum number of fileter is 64)
//--- Adjusted parameters
double m_weight; // "weight" of a signal in a combined filter
int m_patterns_usage; // bit mask of using of the market models of signals
int m_general; // index of the "main" signal (-1 - no)
long m_ignore; // bit mask of "ignoring" the additional filter
long m_invert; // bit mask of "inverting" the additional filter
int m_threshold_open; // threshold value for opening
int m_threshold_close;// threshold level for closing
double m_price_level; // level of placing a pending orders relatively to the base price
double m_stop_level; // level of placing of the "stop loss" order relatively to the open price
double m_take_level; // level of placing of the "take profit" order relatively to the open price
int m_expiration; // time of expiration of a pending order in bars
double m_direction; // weighted direction
public:
CExpertSignal(void);
~CExpertSignal(void);
//--- methods of access to protected data
void BasePrice(double value) { m_base_price=value; }
int UsedSeries(void);
//--- methods of setting adjustable parameters
void Weight(double value) { m_weight=value; }
void PatternsUsage(int value) { m_patterns_usage=value; }
void General(int value) { m_general=value; }
void Ignore(long value) { m_ignore=value; }
void Invert(long value) { m_invert=value; }
void ThresholdOpen(int value) { m_threshold_open=value; }
void ThresholdClose(int value) { m_threshold_close=value; }
void PriceLevel(double value) { m_price_level=value; }
void StopLevel(double value) { m_stop_level=value; }
void TakeLevel(double value) { m_take_level=value; }
void Expiration(int value) { m_expiration=value; }
//--- method of initialization of the object
void Magic(ulong value);
//--- method of verification of settings
virtual bool ValidationSettings(void);
//--- method of creating the indicator and timeseries
virtual bool InitIndicators(CIndicators *indicators);
//--- methods for working with additional filters
virtual bool AddFilter(CExpertSignal *filter);
//--- methods for generating signals of entering the market
virtual bool CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration);
virtual bool CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration);
//--- methods for detection of levels of entering the market
virtual bool OpenLongParams(double &price,double &sl,double &tp,datetime &expiration);
virtual bool OpenShortParams(double &price,double &sl,double &tp,datetime &expiration);
//--- methods for generating signals of exit from the market
virtual bool CheckCloseLong(double &price);
virtual bool CheckCloseShort(double &price);
//--- methods for detection of levels of exit from the market
virtual bool CloseLongParams(double &price);
virtual bool CloseShortParams(double &price);
//--- methods for generating signals of reversal of positions
virtual bool CheckReverseLong(double &price,double &sl,double &tp,datetime &expiration);
virtual bool CheckReverseShort(double &price,double &sl,double &tp,datetime &expiration);
//--- methods for generating signals of modification of pending orders
virtual bool CheckTrailingOrderLong(COrderInfo *order,double &price) { return(false); }
virtual bool CheckTrailingOrderShort(COrderInfo *order,double &price) { return(false); }
//--- methods of checking if the market models are formed
virtual int LongCondition(void) { return(0); }
virtual int ShortCondition(void) { return(0); }
virtual double Direction(void);
void SetDirection(void) { m_direction=Direction(); }
};
//+------------------------------------------------------------------+
//| Constructor |
//+------------------------------------------------------------------+
CExpertSignal::CExpertSignal(void) : m_base_price(0.0),
m_general(-1), // no "main" signal
m_weight(1.0),
m_patterns_usage(-1), // all models are used
m_ignore(0), // all additional filters are used
m_invert(0),
m_threshold_open(50),
m_threshold_close(100),
m_price_level(0.0),
m_stop_level(0.0),
m_take_level(0.0),
m_expiration(0),
m_direction(EMPTY_VALUE)
{
}
//+------------------------------------------------------------------+
//| Destructor |
//+------------------------------------------------------------------+
CExpertSignal::~CExpertSignal(void)
{
}
//+------------------------------------------------------------------+
//| Get flags of used timeseries |
//+------------------------------------------------------------------+
int CExpertSignal::UsedSeries(void)
{
if(m_other_symbol || m_other_period)
return(0);
//--- check of the flags of using timeseries in the additional filters
int total=m_filters.Total();
//--- loop by the additional filters
for(int i=0;i<total;i++)
{
CExpertSignal *filter=m_filters.At(i);
//--- check pointer
if(filter==NULL)
return(false);
m_used_series|=filter.UsedSeries();
}
//---
return(m_used_series);
}
//+------------------------------------------------------------------+
//| Sets magic number for object and its dependent objects |
//+------------------------------------------------------------------+
void CExpertSignal::Magic(ulong value)
{
int total=m_filters.Total();
//--- loop by the additional filters
for(int i=0;i<total;i++)
{
CExpertSignal *filter=m_filters.At(i);
//--- check pointer
if(filter==NULL)
continue;
filter.Magic(value);
}
//---
CExpertBase::Magic(value);
}
//+------------------------------------------------------------------+
//| Validation settings protected data |
//+------------------------------------------------------------------+
bool CExpertSignal::ValidationSettings(void)
{
if(!CExpertBase::ValidationSettings())
return(false);
//--- check of parameters in the additional filters
int total=m_filters.Total();
//--- loop by the additional filters
for(int i=0;i<total;i++)
{
CExpertSignal *filter=m_filters.At(i);
//--- check pointer
if(filter==NULL)
return(false);
if(!filter.ValidationSettings())
return(false);
}
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Create indicators |
//+------------------------------------------------------------------+
bool CExpertSignal::InitIndicators(CIndicators *indicators)
{
//--- check pointer
if(indicators==NULL)
return(false);
//---
CExpertSignal *filter;
int total=m_filters.Total();
//--- gather information about using of timeseries
for(int i=0;i<total;i++)
{
filter=m_filters.At(i);
m_used_series|=filter.UsedSeries();
}
//--- create required timeseries
if(!CExpertBase::InitIndicators(indicators))
return(false);
//--- initialization of indicators and timeseries in the additional filters
for(int i=0;i<total;i++)
{
filter=m_filters.At(i);
filter.SetPriceSeries(m_open,m_high,m_low,m_close);
filter.SetOtherSeries(m_spread,m_time,m_tick_volume,m_real_volume);
if(!filter.InitIndicators(indicators))
return(false);
}
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Setting an additional filter |
//+------------------------------------------------------------------+
bool CExpertSignal::AddFilter(CExpertSignal *filter)
{
//--- check pointer
if(filter==NULL)
return(false);
//--- primary initialization of the filter
if(!filter.Init(m_symbol,m_period,m_adjusted_point))
return(false);
//--- add the filter to the array of filters
if(!m_filters.Add(filter))
return(false);
filter.EveryTick(m_every_tick);
filter.Magic(m_magic);
//--- succeed
return(true);
}
//+------------------------------------------------------------------+
//| Generating a buy signal |
//+------------------------------------------------------------------+
bool CExpertSignal::CheckOpenLong(double &price,double &sl,double &tp,datetime &expiration)
{
bool result =false;
//--- the "prohibition" signal
if(m_direction==EMPTY_VALUE)
return(false);
//--- check of exceeding the threshold value
if(m_direction>=m_threshold_open)
{
//--- there's a signal
result=true;
//--- try to get the levels of opening
if(!OpenLongParams(price,sl,tp,expiration))
result=false;
}
//--- zeroize the base price
m_base_price=0.0;
//--- return the result
return(result);
}
//+------------------------------------------------------------------+
//| Generating a sell signal |
//+------------------------------------------------------------------+
bool CExpertSignal::CheckOpenShort(double &price,double &sl,double &tp,datetime &expiration)
{
bool result =false;
//--- the "prohibition" signal
if(m_direction==EMPTY_VALUE)
return(false);
//--- check of exceeding the threshold value
if(-m_direction>=m_threshold_open)
{
//--- there's a signal
result=true;
//--- try to get the levels of opening
if(!OpenShortParams(price,sl,tp,expiration))
result=false;
}
//--- zeroize the base price
m_base_price=0.0;
//--- return the result
return(result);
}
//+------------------------------------------------------------------+
//| Detecting the levels for buying |
//+------------------------------------------------------------------+
bool CExpertSignal::OpenLongParams(double &price,double &sl,double &tp,datetime &expiration)
{
CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL;
//---
if(general==NULL)
{
//--- if a base price is not specified explicitly, take the current market price
double base_price=(m_base_price==0.0) ? m_symbol.Ask() : m_base_price;
price =m_symbol.NormalizePrice(base_price-m_price_level*PriceLevelUnit());
sl =(m_stop_level==0.0) ? 0.0 : m_symbol.NormalizePrice(price-m_stop_level*PriceLevelUnit());
tp =(m_take_level==0.0) ? 0.0 : m_symbol.NormalizePrice(price+m_take_level*PriceLevelUnit());
expiration+=m_expiration*PeriodSeconds(m_period);
return(true);
}
//---
return(general.OpenLongParams(price,sl,tp,expiration));
}
//+------------------------------------------------------------------+
//| Detecting the levels for selling |
//+------------------------------------------------------------------+
bool CExpertSignal::OpenShortParams(double &price,double &sl,double &tp,datetime &expiration)
{
CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL;
//---
if(general==NULL)
{
//--- if a base price is not specified explicitly, take the current market price
double base_price=(m_base_price==0.0) ? m_symbol.Bid() : m_base_price;
price =m_symbol.NormalizePrice(base_price+m_price_level*PriceLevelUnit());
sl =(m_stop_level==0.0) ? 0.0 : m_symbol.NormalizePrice(price+m_stop_level*PriceLevelUnit());
tp =(m_take_level==0.0) ? 0.0 : m_symbol.NormalizePrice(price-m_take_level*PriceLevelUnit());
expiration+=m_expiration*PeriodSeconds(m_period);
return(true);
}
//---
return(general.OpenShortParams(price,sl,tp,expiration));
}
//+------------------------------------------------------------------+
//| Generating a signal for closing of a long position |
//+------------------------------------------------------------------+
bool CExpertSignal::CheckCloseLong(double &price)
{
bool result =false;
//--- the "prohibition" signal
if(m_direction==EMPTY_VALUE)
return(false);
//--- check of exceeding the threshold value
if(-m_direction>=m_threshold_close)
{
//--- there's a signal
result=true;
//--- try to get the level of closing
if(!CloseLongParams(price))
result=false;
}
//--- zeroize the base price
m_base_price=0.0;
//--- return the result
return(result);
}
//+------------------------------------------------------------------+
//| Generating a signal for closing a short position |
//+------------------------------------------------------------------+
bool CExpertSignal::CheckCloseShort(double &price)
{
bool result =false;
//--- the "prohibition" signal
if(m_direction==EMPTY_VALUE)
return(false);
//--- check of exceeding the threshold value
if(m_direction>=m_threshold_close)
{
//--- there's a signal
result=true;
//--- try to get the level of closing
if(!CloseShortParams(price))
result=false;
}
//--- zeroize the base price
m_base_price=0.0;
//--- return the result
return(result);
}
//+------------------------------------------------------------------+
//| Detecting the levels for closing a long position |
//+------------------------------------------------------------------+
bool CExpertSignal::CloseLongParams(double &price)
{
CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL;
//---
if(general==NULL)
{
//--- if a base price is not specified explicitly, take the current market price
price=(m_base_price==0.0) ? m_symbol.Bid() : m_base_price;
return(true);
}
//---
return(general.CloseLongParams(price));
}
//+------------------------------------------------------------------+
//| Detecting the levels for closing a short position |
//+------------------------------------------------------------------+
bool CExpertSignal::CloseShortParams(double &price)
{
CExpertSignal *general=(m_general!=-1) ? m_filters.At(m_general) : NULL;
//---
if(general==NULL)
{
//--- if a base price is not specified explicitly, take the current market price
price=(m_base_price==0.0)?m_symbol.Ask():m_base_price;
return(true);
}
//--- ok
return(general.CloseShortParams(price));
}
//+------------------------------------------------------------------+
//| Generating a signal for reversing a long position |
//+------------------------------------------------------------------+
bool CExpertSignal::CheckReverseLong(double &price,double &sl,double &tp,datetime &expiration)
{
double c_price;
//--- check the signal of closing a long position
if(!CheckCloseLong(c_price))
return(false);
//--- check the signal of opening a short position
if(!CheckOpenShort(price,sl,tp,expiration))
return(false);
//--- difference between the close and open prices must not exceed two spreads
if(c_price!=price)
return(false);
//--- there's a signal
return(true);
}
//+------------------------------------------------------------------+
//| Generating a signal for reversing a short position |
//+------------------------------------------------------------------+
bool CExpertSignal::CheckReverseShort(double &price,double &sl,double &tp,datetime &expiration)
{
double c_price;
//--- check the signal of closing a short position
if(!CheckCloseShort(c_price))
return(false);
//--- check the signal of opening a long position
if(!CheckOpenLong(price,sl,tp,expiration))
return(false);
//--- difference between the close and open prices must not exceed two spreads
if(c_price!=price)
return(false);
//--- there's a signal
return(true);
}
//+------------------------------------------------------------------+
//| Detecting the "weighted" direction |
//+------------------------------------------------------------------+
double CExpertSignal::Direction(void)
{
long mask;
double direction;
double result=m_weight*(LongCondition()-ShortCondition());
int number=(result==0.0)? 0 : 1; // number of "voted"
//---
int total=m_filters.Total();
//--- loop by filters
for(int i=0;i<total;i++)
{
//--- mask for bit maps
mask=((long)1)<<i;
//--- check of the flag of ignoring the signal of filter
if((m_ignore&mask)!=0)
continue;
CExpertSignal *filter=m_filters.At(i);
//--- check pointer
if(filter==NULL)
continue;
direction=filter.Direction();
//--- the "prohibition" signal
if(direction==EMPTY_VALUE)
return(EMPTY_VALUE);
//--- check of flag of inverting the signal of filter
if((m_invert&mask)!=0)
result-=direction;
else
result+=direction;
number++;
}
//--- normalization
if(number!=0)
result/=number;
//--- return the result
return(result);
}
//+------------------------------------------------------------------+