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);
| |||
}
| |||
//+------------------------------------------------------------------+
|