516 lines
15 KiB
MQL5
516 lines
15 KiB
MQL5
|
|
/*
|
||
|
|
Tillson T3
|
||
|
|
Copyright (C) 2025 boyvlad <https://www.mql5.com/en/users/boyvlad/>
|
||
|
|
|
||
|
|
This program is free software: you can redistribute it and/or modify
|
||
|
|
it under the terms of the GNU General Public License as published by
|
||
|
|
the Free Software Foundation, either version 3 of the License, or
|
||
|
|
(at your option) any later version.
|
||
|
|
|
||
|
|
This program is distributed in the hope that it will be useful,
|
||
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
|
GNU General Public License for more details.
|
||
|
|
|
||
|
|
You should have received a copy of the GNU General Public License
|
||
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
|
*/
|
||
|
|
|
||
|
|
//--- Do not remove!
|
||
|
|
#property tester_everytick_calculate
|
||
|
|
//---
|
||
|
|
#property indicator_chart_window
|
||
|
|
#property indicator_buffers 2
|
||
|
|
#property indicator_plots 1
|
||
|
|
//---
|
||
|
|
#property indicator_label1 "T3"
|
||
|
|
#property indicator_type1 DRAW_LINE
|
||
|
|
#property indicator_color1 clrViolet
|
||
|
|
#property indicator_style1 STYLE_SOLID
|
||
|
|
#property indicator_width1 3
|
||
|
|
//---
|
||
|
|
#define VER_MAJOR 1
|
||
|
|
#define VER_MINOR 0
|
||
|
|
#define MIN_MQL_BUILD 5370
|
||
|
|
//---
|
||
|
|
#property version string(VER_MAJOR)"."string(VER_MINOR)
|
||
|
|
#property description "Compiler build: "string(__MQLBUILD__)
|
||
|
|
#property copyright "boyvlad"
|
||
|
|
#property link "https://www.mql5.com/en/users/boyvlad/"
|
||
|
|
//---
|
||
|
|
|
||
|
|
#ifdef __MQL4__
|
||
|
|
#property strict
|
||
|
|
#ifndef ERR_SUCCESS
|
||
|
|
#define ERR_SUCCESS ERR_NO_ERROR
|
||
|
|
#endif
|
||
|
|
#endif
|
||
|
|
|
||
|
|
class CArrayHelper
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
template<typename T>
|
||
|
|
static void deleteEach(T& arr[])
|
||
|
|
{
|
||
|
|
for(int i = ArraySize(arr) - 1; i >= 0; i--)
|
||
|
|
delete arr[i];
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
static bool add(T value, T& arr[], const int reserveSize)
|
||
|
|
{
|
||
|
|
int size = ArraySize(arr);
|
||
|
|
return addEx(value, arr, size, reserveSize);
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
static bool expand(T& arr[], const int reserveSize)
|
||
|
|
{
|
||
|
|
int size = ArraySize(arr);
|
||
|
|
return expandEx(arr, size, reserveSize);
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
static bool addEx(T value, T& arr[], int& arrSize, const int reserveSize)
|
||
|
|
{
|
||
|
|
if(!expandEx(arr, arrSize, reserveSize))
|
||
|
|
return false;
|
||
|
|
arr[arrSize - 1] = value;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
template<typename T>
|
||
|
|
static bool expandEx(T& arr[], int& size, const int reserveSize)
|
||
|
|
{
|
||
|
|
ResetLastError();
|
||
|
|
int returned = ArrayResize(arr, ++size, reserveSize);
|
||
|
|
if(returned == size && _LastError == ERR_SUCCESS)
|
||
|
|
return true;
|
||
|
|
PrintFormat(__FUNCSIG__" error %i, newSize %i, result %i", _LastError, size, returned);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
class CFormedBarTime
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
void set(datetime a_value) { value = a_value; }
|
||
|
|
|
||
|
|
bool onNewBar(const int rates_total, const datetime &time[])
|
||
|
|
{
|
||
|
|
if(value == time[rates_total - 3])
|
||
|
|
{
|
||
|
|
value = time[rates_total - 2];
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
printUnexpectedValue(rates_total, time);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
void printUnexpectedValue(const int rates_total, const datetime &time[])
|
||
|
|
{
|
||
|
|
PrintFormat("CFormedBarTime: Unexpected value %s. time[-3] %s, time[-2] %s, rates_total %i", TimeToString(value),
|
||
|
|
TimeToString(time[rates_total - 3]), TimeToString(time[rates_total - 2]), rates_total);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
datetime value;
|
||
|
|
public:
|
||
|
|
CFormedBarTime() : value(0) {}
|
||
|
|
};
|
||
|
|
|
||
|
|
//---
|
||
|
|
#define _reserveSize 25
|
||
|
|
|
||
|
|
class CSubIndiBase;
|
||
|
|
|
||
|
|
class CSubIndiRegistry
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
static void clear()
|
||
|
|
{
|
||
|
|
// Even with drawBegin = 0, at least 2 bars on the chart are required
|
||
|
|
// Tracking the last formed bar (CFormedBarTime, for example) requires 3 bars on the chart
|
||
|
|
barsRequired = 3;
|
||
|
|
//---
|
||
|
|
numOfPtrs = 0;
|
||
|
|
ArrayResize(ptrs, numOfPtrs, _reserveSize);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void register(CSubIndiBase& instance)
|
||
|
|
{
|
||
|
|
if(instance.barsRequired > barsRequired)
|
||
|
|
barsRequired = instance.barsRequired;
|
||
|
|
CArrayHelper::addEx(&instance, ptrs, numOfPtrs, _reserveSize);
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool checkRatesTotal(int rates_total, int prev_calculated)
|
||
|
|
{
|
||
|
|
if(rates_total < barsRequired)
|
||
|
|
{
|
||
|
|
PrintFormat("Not enough bars on the chart to calculate the indicator. Required: %i, available: %i.", barsRequired, rates_total);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if(rates_total < prev_calculated)
|
||
|
|
{
|
||
|
|
PrintFormat(__FUNCTION__" Something went wrong. rates_total: %i, prev_calculated: %i", rates_total, prev_calculated);
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
static int barsRequired;
|
||
|
|
static CSubIndiBase* ptrs[];
|
||
|
|
static int numOfPtrs;
|
||
|
|
};
|
||
|
|
|
||
|
|
int CSubIndiRegistry::barsRequired;
|
||
|
|
CSubIndiBase* CSubIndiRegistry::ptrs[];
|
||
|
|
int CSubIndiRegistry::numOfPtrs = 0;
|
||
|
|
|
||
|
|
//---
|
||
|
|
#undef _reserveSize
|
||
|
|
|
||
|
|
class CSubIndiBase
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
virtual void assignToAllBuffers(int barIdx, double a_value) = 0;
|
||
|
|
|
||
|
|
protected:
|
||
|
|
int prepareBeforeCalculating(const int rates_total, const int prev_calculated)
|
||
|
|
{
|
||
|
|
int idxRecalcFrom;
|
||
|
|
switch(rates_total - prev_calculated)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
case 1:
|
||
|
|
idxRecalcFrom = prev_calculated - 1;
|
||
|
|
if(idxRecalcFrom < drawBegin)
|
||
|
|
{
|
||
|
|
idxRecalcFrom = drawBegin;
|
||
|
|
clearLeftFromDrawBegin();
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
idxRecalcFrom = drawBegin;
|
||
|
|
clearLeftFromDrawBegin();
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
return idxRecalcFrom;
|
||
|
|
}
|
||
|
|
|
||
|
|
void clearLeftFromDrawBegin()
|
||
|
|
{
|
||
|
|
for(int i = 0; i < drawBegin; i++)
|
||
|
|
assignToAllBuffers(i, emptyValue);
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
const int barsRequired;
|
||
|
|
const int drawBegin;
|
||
|
|
const double emptyValue;
|
||
|
|
protected:
|
||
|
|
CSubIndiBase(int a_barsRequired, double a_emptyValue)
|
||
|
|
: barsRequired(a_barsRequired < 1 ? 1 : a_barsRequired),
|
||
|
|
drawBegin(barsRequired - 1),
|
||
|
|
emptyValue(a_emptyValue)
|
||
|
|
{
|
||
|
|
if(a_barsRequired < 1)
|
||
|
|
PrintFormat(__FUNCTION__": invalid barsRequired value (%i) received; corrected to minimum allowed (%i)", a_barsRequired, barsRequired);
|
||
|
|
CSubIndiRegistry::register(this);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
class CIndiParamsBase
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
int period;
|
||
|
|
double volumeFactor;
|
||
|
|
ENUM_APPLIED_PRICE applyTo;
|
||
|
|
};
|
||
|
|
|
||
|
|
class CAppliedPrice : public CSubIndiBase
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
static double calculate(int barIdx, ENUM_APPLIED_PRICE a_mode, const double &open[], const double &high[], const double &low[],
|
||
|
|
const double &close[])
|
||
|
|
{
|
||
|
|
switch(a_mode)
|
||
|
|
{
|
||
|
|
case PRICE_OPEN: return open[barIdx];
|
||
|
|
case PRICE_HIGH: return high[barIdx];
|
||
|
|
case PRICE_LOW: return low[barIdx];
|
||
|
|
case PRICE_MEDIAN: return (high[barIdx] + low[barIdx]) / 2.0;
|
||
|
|
case PRICE_TYPICAL: return (high[barIdx] + low[barIdx] + close[barIdx]) / 3.0;
|
||
|
|
case PRICE_WEIGHTED: return (high[barIdx] + low[barIdx] + close[barIdx] + close[barIdx]) / 4.0;
|
||
|
|
default: return close[barIdx];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void onCalculate(const int rates_total, const int prev_calculated, const double &open[], const double &high[], const double &low[],
|
||
|
|
const double &close[])
|
||
|
|
{
|
||
|
|
for(int i = prepareBeforeCalculating(rates_total, prev_calculated); i < rates_total; i++)
|
||
|
|
buffer[i] = calculate(i, mode, open, high, low, close);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
void assignToAllBuffers(int barIdx, double a_value) override { buffer[barIdx] = a_value; }
|
||
|
|
|
||
|
|
private:
|
||
|
|
const ENUM_APPLIED_PRICE mode;
|
||
|
|
public:
|
||
|
|
double buffer[];
|
||
|
|
public:
|
||
|
|
CAppliedPrice(ENUM_APPLIED_PRICE a_mode) : CSubIndiBase(1, EMPTY_VALUE), mode(a_mode) {}
|
||
|
|
};
|
||
|
|
|
||
|
|
class CIndicatorBase
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
int onCalculate(const int rates_total, const int prev_calculated, const double &open[], const double &high[], const double &low[],
|
||
|
|
const double &close[], const datetime &time[])
|
||
|
|
{
|
||
|
|
if(!CSubIndiRegistry::checkRatesTotal(rates_total, prev_calculated))
|
||
|
|
return 0;
|
||
|
|
internalOnCalculate(rates_total, prev_calculated, open, high, low, close, time);
|
||
|
|
return rates_total;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
virtual void internalOnCalculate(const int rates_total, const int prev_calculated, const double &open[], const double &high[],
|
||
|
|
const double &low[], const double &close[], const datetime &time[]) = 0;
|
||
|
|
|
||
|
|
protected:
|
||
|
|
CIndicatorBase() { CSubIndiRegistry::clear(); }
|
||
|
|
};
|
||
|
|
|
||
|
|
namespace T3
|
||
|
|
{
|
||
|
|
class CStateBase
|
||
|
|
{
|
||
|
|
protected:
|
||
|
|
void operator=(const CStateBase& other) {}
|
||
|
|
|
||
|
|
protected:
|
||
|
|
const double alpha;
|
||
|
|
const double alphaInv;
|
||
|
|
const double coeff6;
|
||
|
|
const double coeff5;
|
||
|
|
const double coeff4;
|
||
|
|
const double coeff3;
|
||
|
|
public:
|
||
|
|
CStateBase(int period, double volumeFactor)
|
||
|
|
: alpha(2.0 / (1.0 + period)),
|
||
|
|
alphaInv(1.0 - alpha),
|
||
|
|
coeff6(-MathPow(volumeFactor, 3.0)),
|
||
|
|
coeff5(3.0 * (MathPow(volumeFactor, 2.0) + MathPow(volumeFactor, 3.0))),
|
||
|
|
coeff4(3.0 * (2.0 * MathPow(volumeFactor, 2.0) + volumeFactor + MathPow(volumeFactor, 3.0))),
|
||
|
|
coeff3(1.0 + 3.0 * volumeFactor + MathPow(volumeFactor, 3.0) + 3.0 * MathPow(volumeFactor, 2.0)) {}
|
||
|
|
};
|
||
|
|
|
||
|
|
class CState : public CStateBase
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
void advance(double &bufferValue, double sourceValue)
|
||
|
|
{
|
||
|
|
ema1 = alpha * sourceValue + alphaInv * ema1;
|
||
|
|
ema2 = alpha * ema1 + alphaInv * ema2;
|
||
|
|
ema3 = alpha * ema2 + alphaInv * ema3;
|
||
|
|
ema4 = alpha * ema3 + alphaInv * ema4;
|
||
|
|
ema5 = alpha * ema4 + alphaInv * ema5;
|
||
|
|
ema6 = alpha * ema5 + alphaInv * ema6;
|
||
|
|
bufferValue = coeff6 * ema6 + coeff5 * ema5 - coeff4 * ema4 + coeff3 * ema3;
|
||
|
|
}
|
||
|
|
|
||
|
|
static double forecast(double sourceValue, const CState& prev)
|
||
|
|
{
|
||
|
|
const double ema1 = prev.alpha * sourceValue + prev.alphaInv * prev.ema1;
|
||
|
|
const double ema2 = prev.alpha * ema1 + prev.alphaInv * prev.ema2;
|
||
|
|
const double ema3 = prev.alpha * ema2 + prev.alphaInv * prev.ema3;
|
||
|
|
const double ema4 = prev.alpha * ema3 + prev.alphaInv * prev.ema4;
|
||
|
|
const double ema5 = prev.alpha * ema4 + prev.alphaInv * prev.ema5;
|
||
|
|
const double ema6 = prev.alpha * ema5 + prev.alphaInv * prev.ema6;
|
||
|
|
return prev.coeff6 * ema6 + prev.coeff5 * ema5 - prev.coeff4 * ema4 + prev.coeff3 * ema3;
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
double ema1;
|
||
|
|
double ema2;
|
||
|
|
double ema3;
|
||
|
|
double ema4;
|
||
|
|
double ema5;
|
||
|
|
double ema6;
|
||
|
|
public:
|
||
|
|
CState(double &bufferValue, double sourceValue, const CStateBase &config)
|
||
|
|
: CStateBase(config),
|
||
|
|
ema1(sourceValue),
|
||
|
|
ema2(sourceValue),
|
||
|
|
ema3(sourceValue),
|
||
|
|
ema4(sourceValue),
|
||
|
|
ema5(sourceValue),
|
||
|
|
ema6(sourceValue) { bufferValue = sourceValue; }
|
||
|
|
|
||
|
|
CState(const CStateBase &config) : CStateBase(config) {}
|
||
|
|
};
|
||
|
|
|
||
|
|
class CSubIndi : public CSubIndiBase
|
||
|
|
{
|
||
|
|
int prepareBeforeCalculating(int, int) = delete;
|
||
|
|
|
||
|
|
void assignToAllBuffers(int barIdx, double a_value) override { buffer[barIdx] = a_value; }
|
||
|
|
|
||
|
|
void recalculAll(const int rates_total, const double &source[], const datetime &time[])
|
||
|
|
{
|
||
|
|
clearLeftFromDrawBegin();
|
||
|
|
CState loopState(buffer[drawBegin], source[drawBegin], config);
|
||
|
|
const int idxOfFormedBar = rates_total - 2;
|
||
|
|
for(int i = drawBegin + 1; i <= idxOfFormedBar; i++)
|
||
|
|
loopState.advance(buffer[i], source[i]);
|
||
|
|
formedBar = loopState;
|
||
|
|
timeOfFormedBar.set(time[idxOfFormedBar]);
|
||
|
|
calculCurrentBar(rates_total, source);
|
||
|
|
}
|
||
|
|
|
||
|
|
void calculCurrentBar(const int rates_total, const double &source[])
|
||
|
|
{
|
||
|
|
buffer[rates_total - 1] = CState::forecast(source[rates_total - 1], formedBar);
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
void onCalculate(const int rates_total, const int prev_calculated, const double &source[], const datetime &time[])
|
||
|
|
{
|
||
|
|
switch(rates_total - prev_calculated)
|
||
|
|
{
|
||
|
|
case 0:
|
||
|
|
calculCurrentBar(rates_total, source);
|
||
|
|
break;
|
||
|
|
case 1:
|
||
|
|
if(timeOfFormedBar.onNewBar(rates_total, time))
|
||
|
|
{
|
||
|
|
formedBar.advance(buffer[rates_total - 2], source[rates_total - 2]);
|
||
|
|
calculCurrentBar(rates_total, source);
|
||
|
|
}
|
||
|
|
else recalculAll(rates_total, source, time);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
recalculAll(rates_total, source, time);
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
public:
|
||
|
|
double buffer[];
|
||
|
|
private:
|
||
|
|
const CStateBase config;
|
||
|
|
CState formedBar;
|
||
|
|
CFormedBarTime timeOfFormedBar;
|
||
|
|
public:
|
||
|
|
CSubIndi(int period, double volumeFactor, const CSubIndiBase& source)
|
||
|
|
: CSubIndiBase(source.barsRequired, EMPTY_VALUE),
|
||
|
|
config(period, volumeFactor),
|
||
|
|
formedBar(config) {}
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
//--- INDICATOR_DATA
|
||
|
|
#define _buffIdxT3 0
|
||
|
|
//--- INDICATOR_CALCULATIONS
|
||
|
|
#define _buffIdxApplPrc 1
|
||
|
|
//--- Plots
|
||
|
|
#define _plotIdxT3 0
|
||
|
|
//---
|
||
|
|
|
||
|
|
class CIndicator : public CIndicatorBase
|
||
|
|
{
|
||
|
|
private:
|
||
|
|
void internalOnCalculate(const int rates_total, const int prev_calculated, const double &open[], const double &high[],
|
||
|
|
const double &low[], const double &close[], const datetime &time[])
|
||
|
|
{
|
||
|
|
appliedPrice.onCalculate(rates_total, prev_calculated, open, high, low, close);
|
||
|
|
t3.onCalculate(rates_total, prev_calculated, appliedPrice.buffer, time);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void setPlot(int plotIdx, const CSubIndiBase &ref)
|
||
|
|
{
|
||
|
|
PlotIndexSetDouble(plotIdx, PLOT_EMPTY_VALUE, ref.emptyValue);
|
||
|
|
}
|
||
|
|
|
||
|
|
private:
|
||
|
|
CAppliedPrice appliedPrice;
|
||
|
|
T3::CSubIndi t3;
|
||
|
|
public:
|
||
|
|
CIndicator(const CIndiParamsBase ¶ms)
|
||
|
|
: appliedPrice(params.applyTo),
|
||
|
|
t3(params.period, params.volumeFactor, appliedPrice)
|
||
|
|
{
|
||
|
|
SetIndexBuffer(_buffIdxT3, t3.buffer, INDICATOR_DATA);
|
||
|
|
SetIndexBuffer(_buffIdxApplPrc, appliedPrice.buffer, INDICATOR_CALCULATIONS);
|
||
|
|
setPlot(_plotIdxT3, t3);
|
||
|
|
PlotIndexSetString(_plotIdxT3, PLOT_LABEL, StringFormat("T3(%i, %.3f)", params.period, params.volumeFactor));
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
//---
|
||
|
|
#undef _buffIdxT3
|
||
|
|
#undef _buffIdxApplPrc
|
||
|
|
#undef _plotIdxT3
|
||
|
|
|
||
|
|
input(name="Period") int inpPeriod = 5;
|
||
|
|
input(name="Volume factor") double inpVolumeFactor = 0.7;
|
||
|
|
input(name="Apply to") ENUM_APPLIED_PRICE inpApplyTo = PRICE_CLOSE;
|
||
|
|
|
||
|
|
class CIndiParams final : public CIndiParamsBase
|
||
|
|
{
|
||
|
|
public:
|
||
|
|
CIndiParams() : CIndiParamsBase()
|
||
|
|
{
|
||
|
|
period = inpPeriod;
|
||
|
|
volumeFactor = inpVolumeFactor;
|
||
|
|
applyTo = inpApplyTo;
|
||
|
|
//---
|
||
|
|
if(period < 2)
|
||
|
|
{
|
||
|
|
Print("Period corrected to 2");
|
||
|
|
period = 2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
CIndicator* Indicator;
|
||
|
|
|
||
|
|
int OnInit()
|
||
|
|
{
|
||
|
|
CIndiParams params;
|
||
|
|
Indicator = new CIndicator(params);
|
||
|
|
return checkCompilerBuild() ? INIT_SUCCEEDED : INIT_FAILED;
|
||
|
|
}
|
||
|
|
|
||
|
|
int OnCalculate(const int rates_total,
|
||
|
|
const int prev_calculated,
|
||
|
|
const datetime &time[],
|
||
|
|
const double &open[],
|
||
|
|
const double &high[],
|
||
|
|
const double &low[],
|
||
|
|
const double &close[],
|
||
|
|
const long &tick_volume[],
|
||
|
|
const long &volume[],
|
||
|
|
const int &spread[])
|
||
|
|
{
|
||
|
|
return Indicator.onCalculate(rates_total, prev_calculated, open, high, low, close, time);
|
||
|
|
}
|
||
|
|
|
||
|
|
void OnDeinit(const int reason)
|
||
|
|
{
|
||
|
|
delete Indicator;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool checkCompilerBuild()
|
||
|
|
{
|
||
|
|
if(__MQLBUILD__ >= MIN_MQL_BUILD)
|
||
|
|
return true;
|
||
|
|
Alert("Outdated compiler build "string(__MQLBUILD__)"! Recompile using compiler build "string(MIN_MQL_BUILD)" or later");
|
||
|
|
return false;
|
||
|
|
}
|