464 lines
19 KiB
MQL5
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);
|
|
}
|
|
//+------------------------------------------------------------------+
|