MQL5Book/Include/AutoIndicator.mqh

270 lines
11 KiB
MQL5
Raw Permalink Normal View History

2025-05-30 16:09:41 +02:00
//+------------------------------------------------------------------+
//| AutoIndicator.mqh |
//| Copyright © 2017-2020, Marketeer |
//| https://www.mql5.com/en/users/marketeer |
//+------------------------------------------------------------------+
#include "RTTI.mqh"
#include "MqlParamBuilder.mqh"
//+------------------------------------------------------------------+
//| Defines for making indicators' IDs with metadata |
//+------------------------------------------------------------------+
#define MAKE_IND(P,B,W,ID) (int)((W << 24) | ((B & 0xFF) << 16) | ((P & 0xFF) << 8) | (ID & 0xFF))
#define IND_PARAMS(X) ((X >> 8) & 0xFF)
#define IND_BUFFERS(X) ((X >> 16) & 0xFF)
#define IND_WINDOW(X) ((uchar)(X >> 24))
#define IND_ID(X) ((ENUM_INDICATOR)(X & 0xFF))
//+------------------------------------------------------------------+
//| All indicators (types and metadata) |
//+------------------------------------------------------------------+
enum IndicatorType
{
iCustom_ = MAKE_IND(0, 0, 0, IND_CUSTOM), // {iCustom}(...)[?]
iAC_ = MAKE_IND(0, 1, 1, IND_AC), // iAC( )[1]*
iAD_volume = MAKE_IND(1, 1, 1, IND_AD), // iAD(volume)[1]*
iADX_period = MAKE_IND(1, 3, 1, IND_ADX), // iADX(period)[3]*
iADXWilder_period = MAKE_IND(1, 3, 1, IND_ADXW), // iADXWilder(period)[3]*
iAlligator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price = MAKE_IND(8, 3, 0, IND_ALLIGATOR), // iAlligator(jawP,jawS,teethP,teethS,lipsP,lipsS,method,price)[3]
iAMA_period_fast_slow_shift_price = MAKE_IND(5, 1, 0, IND_AMA), // iAMA(period,fast,slow,shift,price)[1]
iAO_ = MAKE_IND(0, 1, 1, IND_AO), // iAO( )[1]*
iATR_period = MAKE_IND(1, 1, 1, IND_ATR), // iATR(period)[1]*
iBands_period_shift_deviation_price = MAKE_IND(4, 3, 0, IND_BANDS), // iBands(period,shift,deviation,price)[3]
iBearsPower_period = MAKE_IND(1, 1, 1, IND_BEARS), // iBearsPower(period)[1]*
iBullsPower_period = MAKE_IND(1, 1, 1, IND_BULLS), // iBullsPower(period)[1]*
iBWMFI_volume = MAKE_IND(1, 1, 1, IND_BWMFI), // iBWMFI(volume)[1]*
iCCI_period_price = MAKE_IND(2, 1, 1, IND_CCI), // iCCI(period,price)[1]*
iChaikin_fast_slow_method_volume = MAKE_IND(4, 1, 1, IND_CHAIKIN), // iChaikin(fast,slow,method,volume)[1]*
iDEMA_period_shift_price = MAKE_IND(3, 1, 0, IND_DEMA), // iDEMA(period,shift,price)[1]
iDeMarker_period = MAKE_IND(1, 1, 1, IND_DEMARKER), // iDeMarker(period)[1]*
iEnvelopes_period_shift_method_price_deviation = MAKE_IND(5, 2, 0, IND_ENVELOPES), // iEnvelopes(period,shift,method,price,deviation)[2]
iForce_period_method_volume = MAKE_IND(3, 1, 1, IND_FORCE), // iForce(period,method,volume)[1]*
iFractals_ = MAKE_IND(0, 2, 0, IND_FRACTALS), // iFractals( )[2]
iFrAMA_period_shift_price = MAKE_IND(3, 1, 0, IND_FRAMA), // iFrAMA(period,shift,price)[1]
iGator_jawP_jawS_teethP_teethS_lipsP_lipsS_method_price = MAKE_IND(8, 4, 1, IND_GATOR), // iGator(jawP,jawS,teethP,teethS,lipsP,lipsS,method,price)[4]*
iIchimoku_tenkan_kijun_senkou = MAKE_IND(3, 5, 0, IND_ICHIMOKU), // iIchimoku(tenkan,kijun,senkou)[5]
iMomentum_period_price = MAKE_IND(2, 1, 1, IND_MOMENTUM), // iMomentum(period,price)[1]*
iMFI_period_volume = MAKE_IND(2, 1, 1, IND_MFI), // iMFI(period,volume)[1]*
iMA_period_shift_method_price = MAKE_IND(4, 1, 0, IND_MA), // iMA(period,shift,method,price)[1]
iMACD_fast_slow_signal_price = MAKE_IND(4, 2, 1, IND_MACD), // iMACD(fast,slow,signal,price)[2]*
iOBV_volume = MAKE_IND(1, 1, 1, IND_OBV), // iOBV(volume)[1]*
iOsMA_fast_slow_signal_price = MAKE_IND(4, 1, 1, IND_OSMA), // iOsMA(fast,slow,signal,price)[1]*
iRSI_period_price = MAKE_IND(2, 1, 1, IND_RSI), // iRSI(period,price)[1]*
iRVI_period = MAKE_IND(1, 2, 1, IND_RVI), // iRVI(period)[2]*
iSAR_step_maximum = MAKE_IND(2, 1, 0, IND_SAR), // iSAR(step,maximum)[1]
iStdDev_period_shift_method_price = MAKE_IND(4, 1, 1, IND_STDDEV), // iStdDev(period,shift,method,price)[1]*
iStochastic_K_D_slowing_method_price = MAKE_IND(5, 2, 1, IND_STOCHASTIC), // iStochastic(K,D,slowing,method,price)[2]*
iTEMA_period_shift_price = MAKE_IND(3, 1, 0, IND_TEMA), // iTEMA(period,shift,price)[1]
iTriX_period_price = MAKE_IND(2, 1, 1, IND_TRIX), // iTriX(period,price)[1]*
iVIDyA_momentum_smooth_shift_price = MAKE_IND(4, 1, 0, IND_VIDYA), // iVIDyA(momentum,smooth,shift,price)[1]
iVolumes_volume = MAKE_IND(1, 1, 1, IND_VOLUMES), // iVolumes(volume)[1]*
iWPR_period = MAKE_IND(1, 1, 1, IND_WPR), // iWPR(period)[1]*
};
//+------------------------------------------------------------------+
//| Indicator builder |
//+------------------------------------------------------------------+
class AutoIndicator
{
protected:
IndicatorType type; // selected indicator type
string symbol; // optional work symbol
ENUM_TIMEFRAMES tf; // optional work timeframe
MqlParamBuilder builder; // helper object to build array of parameters
int handle; // indicator handle
string name; // custom indicator name
// helper to check parameters of built-in indicators
bool isNumerical(const int i)
{
if(builder.typeOf(i) == TYPE_STRING || builder.typeOf(i) == TYPE_BOOL)
{
return false;
}
return true;
}
// helper to check parameters of built-in indicators
bool checkForNumericals()
{
const int pnum = builder.size(); // ArraySize(params);
for(int i = 0; i < pnum; i++)
{
if(!isNumerical(i))
{
Print(EnumToString(type) + " parameter ", (i + 1), " must be a number, ",
EnumToString(builder.typeOf(i)), " given");
return false;
}
}
return true;
}
// check number and types of parameters of built-in indicators
bool checkStandard()
{
const int pnum = builder.size(); // ArraySize(params);
if(IND_PARAMS(type) != pnum)
{
Print(EnumToString(type) + " requires " + (string)IND_PARAMS(type) + " parameters, " + (string)pnum + " given");
return false;
}
// built-in indicators have numeric parameters only
return checkForNumericals();
}
// return integer constant for specific textual descrpition
int lookUpLiterals(const string &s)
{
if(s == "sma") return MODE_SMA;
else if(s == "ema") return MODE_EMA;
else if(s == "smma") return MODE_SMMA;
else if(s == "lwma") return MODE_LWMA;
else if(s == "close") return PRICE_CLOSE;
else if(s == "open") return PRICE_OPEN;
else if(s == "high") return PRICE_HIGH;
else if(s == "low") return PRICE_LOW;
else if(s == "median") return PRICE_MEDIAN;
else if(s == "typical") return PRICE_TYPICAL;
else if(s == "weighted") return PRICE_WEIGHTED;
else if(s == "lowhigh") return STO_LOWHIGH; // 0
else if(s == "closeclose") return STO_CLOSECLOSE; // 1
else if(s == "tick") return VOLUME_TICK; // 0
else if(s == "real") return VOLUME_REAL; // 1
return -1;
}
// parse comma separated list of parameters 'p1,p2,p3,...'
// into array of elements of appropriate types,
// for example, '1.0' goes to double, 123 makes int, "text" means string,
// true/false are booleans, "2021.01.01" is a datetime
int parseParameters(const string &list)
{
string sparams[];
const int n = StringSplit(list, ',', sparams);
for(int i = 0; i < n; i++)
{
// normalize the string (trim it and change to lower case)
StringTrimLeft(sparams[i]);
StringTrimRight(sparams[i]);
StringToLower(sparams[i]);
if(StringGetCharacter(sparams[i], 0) == '"'
&& StringGetCharacter(sparams[i], StringLen(sparams[i]) - 1) == '"')
{
// anything inside quotes is a string
builder << StringSubstr(sparams[i], 1, StringLen(sparams[i]) - 2);
}
else
{
string part[];
int p = StringSplit(sparams[i], '.', part);
if(p == 2) // double/float
{
builder << StringToDouble(sparams[i]);
}
else if(p == 3) // datetime
{
builder << StringToTime(sparams[i]);
}
else if(sparams[i] == "true")
{
builder << true;
}
else if(sparams[i] == "false")
{
builder << false;
}
else // int
{
int x = lookUpLiterals(sparams[i]);
if(x == -1)
{
x = (int)StringToInteger(sparams[i]);
}
builder << x;
}
}
}
if(type != iCustom_)
{
checkStandard(); // show warning for wrong types or number of parameters
}
return n;
}
// calls IndicatorCreate for prepared list of parameters
int create()
{
MqlParam p[];
// fill array 'p' with parameters acquired by builder
builder >> p;
if(type == iCustom_)
{
// insert name of custom indicator at the beginning
ArraySetAsSeries(p, true);
const int n = ArraySize(p);
ArrayResize(p, n + 1);
p[n].type = TYPE_STRING;
p[n].string_value = name;
ArraySetAsSeries(p, false);
}
return IndicatorCreate(symbol, tf, IND_ID(type), ArraySize(p), p);
}
public:
AutoIndicator(const IndicatorType t, const string custom, const string parameters,
const string s = NULL, const ENUM_TIMEFRAMES p = 0):
type(t), name(custom), symbol(s), tf(p), handle(INVALID_HANDLE)
{
if(type != iCustom_ && StringLen(name) > 0)
{
PrintFormat("Indicator name '%s' will be discarded, because of built-in type selection", name);
name = NULL;
}
if(type == iCustom_ && StringLen(name) == 0)
{
Print("Custom indicator name is missing");
}
else
{
PrintFormat("Initializing %s(%s) %s, %s",
(type == iCustom_ ? name : EnumToString(type)), parameters,
(symbol == NULL ? _Symbol : symbol), EnumToString(tf == 0 ? _Period : tf));
// parse string with indicator parameters
parseParameters(parameters);
// then create and store its handle
handle = create();
}
}
int getHandle() const
{
return handle;
}
string getName() const
{
if(type != iCustom_)
{
const string s = EnumToString(type);
const int p = StringFind(s, "_");
if(p > 0) return StringSubstr(s, 0, p);
return s;
}
return name;
}
};
//+------------------------------------------------------------------+