Skip to content

Commit

Permalink
WIP. Testing RSI over IndicatorTfDummy over Indi_TickProvider. Now we…
Browse files Browse the repository at this point in the history
… need to implement missing extern functions.
  • Loading branch information
nseam committed Mar 1, 2023
1 parent 65b8f29 commit 478b640
Show file tree
Hide file tree
Showing 12 changed files with 135 additions and 57 deletions.
5 changes: 5 additions & 0 deletions Array.extern.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,11 @@ bool ArraySetAsSeries(ARRAY_REF(T, _array), bool _flag) {
return true;
}

template <typename T>
bool ArrayGetAsSeries(ARRAY_REF(T, _array)) {
return _array.getIsSeries();
}

template <typename T>
int ArrayMaximum(const ARRAY_REF(T, _array), int _start = 0, unsigned int _count = WHOLE_ARRAY) {
Print("Not yet implemented: ", __FUNCTION__, " returns 0.");
Expand Down
26 changes: 21 additions & 5 deletions Indicator/IndicatorData.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class IndicatorData : public IndicatorBase {
int last_tick_index; // Index of the last tick.
long first_tick_time_ms; // Time of the first ask/bid tick.
void* mydata;
bool last_tick_result; // Result of the last Tick() invocation.
ENUM_INDI_VS_TYPE retarget_ap_av; // Value storage type to be used as applied price/volume.
ARRAY(Ref<IValueStorage>, value_storages);
ARRAY(WeakRef<IndicatorData>, listeners); // List of indicators that listens for events from this one.
Expand Down Expand Up @@ -865,10 +866,10 @@ class IndicatorData : public IndicatorBase {
HasSpecificValueStorage(INDI_VS_TYPE_TICK_VOLUME);
}

void Tick(int _global_tick_index) {
bool Tick(int _global_tick_index) {
if (last_tick_index == _global_tick_index) {
// We've already ticked.
return;
return last_tick_result;
}

if (_global_tick_index == 0) {
Expand All @@ -884,13 +885,24 @@ class IndicatorData : public IndicatorBase {
GetDataSource() PTR_DEREF Tick(_global_tick_index);
}

last_tick_result = false;

// Also ticking all used indicators if they've not yet ticked.
for (DictStructIterator<int, Ref<IndicatorData>> iter = indicators.Begin(); iter.IsValid(); ++iter) {
iter.Value() REF_DEREF Tick(_global_tick_index);
// If any of the attached indicators ticks then we signal that the tick happened, even if this indicator doesn't
// tick. It is because e.g., RSI could use Candle indicator and Candle could use Tick indicator. Ticking RSI
// doesn't signal tick in RSI, nor Candle, but only Tick indicator and only if new tick occured in the Tick
// indicator. In other words: Only Tick indicator returns true in its OnTick(). Also, in OnTick() it sends a tick
// into Candle indicator which aggregates ticks. RSI doesn't have OnTick() and we can't know if there is new RSI
// value. The only way to know that is to Tick all indicators in hierarchy and if one of them returns true in
// OnTick() then we know that we have new value for RSI.
last_tick_result |= iter.Value() REF_DEREF Tick(_global_tick_index);
}

// Overridable OnTick() method.
OnTick(_global_tick_index);
last_tick_result |= OnTick(_global_tick_index);

return last_tick_result;
}

/**
Expand Down Expand Up @@ -1835,7 +1847,11 @@ class IndicatorData : public IndicatorBase {
/**
* Called when new tick is retrieved from attached data source.
*/
virtual void OnTick(int _global_tick_index) {}
virtual bool OnTick(int _global_tick_index) {
// We really don't know if new tick have happened. Let's just return false and let the Platform's Tick() method tick
// the Tick indicator in order to know if new tick was signalled.
return false;
}

/**
* Called if data source is requested, but wasn't yet set. May be used to initialize indicators that must operate on
Expand Down
8 changes: 5 additions & 3 deletions Indicator/tests/classes/Indicators.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,14 @@ class Indicators {

int Size() { return (int)_indis.Size(); }

void Clear() { _indis.Clear(); }

/**
* Executes OnTick() on every added indicator.
*/
void Tick(int _global_tick_index) {
for (unsigned int i = 0; i < _indis.Size(); ++i) {
_indis[i].Ptr().OnTick(_global_tick_index);
_indis[i].Ptr() PTR_DEREF OnTick(_global_tick_index);
}
}

Expand All @@ -68,8 +70,8 @@ class Indicators {
string ToString(int _shift = 0) {
string _result;
for (unsigned int i = 0; i < _indis.Size(); ++i) {
IndicatorDataEntry _entry = _indis[i].Ptr().GetEntry(_shift);
_result += _indis[i].Ptr().GetFullName() + " = " + _entry.ToString<double>() + "\n";
IndicatorDataEntry _entry = _indis[i].Ptr() PTR_DEREF GetEntry(_shift);
_result += _indis[i].Ptr() PTR_DEREF GetFullName() + " = " + _entry.ToString<double>() + "\n";
}
return _result;
}
Expand Down
4 changes: 2 additions & 2 deletions Indicators/Indi_Drawer.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ class Indi_Drawer : public Indicator<IndiDrawerParams> {
/**
* Called when new tick is retrieved from attached data source.
*/
void OnTick(int _global_tick_index) override {
Indicator<IndiDrawerParams>::OnTick(_global_tick_index);
bool OnTick(int _global_tick_index) override {
return Indicator<IndiDrawerParams>::OnTick(_global_tick_index);

/* @fixme
TaskActionEntry action(INDI_ACTION_SET_VALUE);
Expand Down
6 changes: 4 additions & 2 deletions Indicators/Indi_PriceFeeder.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ class Indi_PriceFeeder : public Indicator<IndiPriceFeederParams> {
/**
* Called when new tick is retrieved from attached data source.
*/
void OnTick(int _global_tick_index) override {
Indicator<IndiPriceFeederParams>::OnTick(_global_tick_index);
bool OnTick(int _global_tick_index) override {
bool _result = Indicator<IndiPriceFeederParams>::OnTick(_global_tick_index);

if (idparams.IsDrawing()) {
int _max_modes = Get<int>(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_MAX_MODES));
Expand All @@ -110,5 +110,7 @@ class Indi_PriceFeeder : public Indicator<IndiPriceFeederParams> {
// draw.DrawLineTo(GetName() + "_" + IntegerToString(i), GetBarTime(0), _entry.values[i].GetDbl());
}
}

return _result;
}
};
54 changes: 31 additions & 23 deletions Indicators/Indi_RSI.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,6 @@
#include "../Indicator/Indicator.h"
#include "Price/Indi_Price.mqh"

#ifndef __MQL4__
// Defines global functions (for MQL4 backward compability).
double iRSI(string _symbol, int _tf, int _period, int _ap, int _shift) {
ResetLastError();
return Indi_RSI::iRSI(_symbol, (ENUM_TIMEFRAMES)_tf, _period, (ENUM_APPLIED_PRICE)_ap, _shift);
}
double iRSIOnArray(double &_arr[], int _total, int _period, int _abs_shift) {
ResetLastError();
return Indi_RSI::iRSIOnArray(_arr, _total, _period, _abs_shift);
}
#endif

// Structs.
struct IndiRSIParams : IndicatorParams {
protected:
Expand All @@ -45,14 +33,14 @@ struct IndiRSIParams : IndicatorParams {

public:
IndiRSIParams(int _period = 14, ENUM_APPLIED_PRICE _ap = PRICE_OPEN, int _shift = 0)
: applied_price(_ap), IndicatorParams(INDI_RSI) {
: IndicatorParams(INDI_RSI), applied_price(_ap) {
shift = _shift;
SetCustomIndicatorName("Examples\\RSI");
SetPeriod(_period);
};
IndiRSIParams(IndiRSIParams &_params) { THIS_REF = _params; };
// Getters.
ENUM_APPLIED_PRICE GetAppliedPrice() override { return applied_price; }
ENUM_APPLIED_PRICE GetAppliedPrice() { return applied_price; }
int GetPeriod() { return period; }
// Setters.
void SetPeriod(int _period) { period = _period; }
Expand Down Expand Up @@ -126,8 +114,13 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
ENUM_APPLIED_PRICE _applied_price = PRICE_CLOSE, int _shift = 0, IndicatorData *_obj = NULL) {
#ifdef __MQL4__
return ::iRSI(_symbol, _tf, _period, _applied_price, _shift);
#else // __MQL5__
#elif __MQL5__
INDICATOR_BUILTIN_CALL_AND_RETURN(::iRSI(_symbol, _tf, _period, _applied_price), 0, _shift);
#else
RUNTIME_ERROR(
"In C++ Indi_RSI::iRSI() method couldn't be used directly. Please use an On-Indicator mode and attach "
"indicator via Platform::Add/AddWithDefaultBindings().");
return DBL_MAX;
#endif
}

Expand All @@ -140,13 +133,14 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
ENUM_APPLIED_PRICE _applied_price = PRICE_CLOSE, int _shift = 0,
Indi_RSI *_obj = NULL) {
int i;
double indi_values[];
ARRAY(double, indi_values);
ArrayResize(indi_values, _period);

double result;

for (i = _shift; i < (int)_shift + (int)_period; i++) {
indi_values[_shift + _period - (i - _shift) - 1] = _indi[i][_obj.GetParams().indi_mode];
indi_values[_shift + _period - (i - _shift) - 1] =
_indi PTR_DEREF GetSpecificAppliedPriceValueStorage(_applied_price) PTR_DEREF Fetch(i);
}

result = iRSIOnArray(indi_values, 0, _period - 1, 0);
Expand All @@ -159,7 +153,7 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
*
* @see https://school.stockcharts.com/doku.php?id=technical_indicators:relative_strength_index_rsi
*
* Reson behind iRSI with SSMA and not just iRSIOnArray() (from above website):
* Reason behind iRSI with SMMA and not just iRSIOnArray() (from above website):
*
* "Taking the prior value plus the current value is a smoothing technique
* similar to that used in calculating an exponential moving average. This
Expand All @@ -182,7 +176,7 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
}

int i;
double indi_values[];
ARRAY(double, indi_values);
ArrayResize(indi_values, _period);

double result;
Expand Down Expand Up @@ -238,7 +232,7 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
new_data.avg_gain = (last_data.avg_gain * (_period - 1) + curr_gain) / _period;
new_data.avg_loss = (last_data.avg_loss * (_period - 1) + curr_loss) / _period;

_target.aux_data.Set(_bar_time_curr, new_data);
_target PTR_DEREF aux_data.Set(_bar_time_curr, new_data);

if (new_data.avg_loss == 0.0) {
// @fixme Why 0 loss?
Expand All @@ -255,7 +249,7 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
/**
* Calculates RSI on the array of values.
*/
static double iRSIOnArray(double &array[], int total, int period, int shift) {
static double iRSIOnArray(ARRAY_REF(double, array), int total, int period, int shift) {
#ifdef __MQL4__
return ::iRSIOnArray(array, total, period, shift);
#else
Expand Down Expand Up @@ -308,9 +302,9 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
* Note that in MQL5 Applied Price must be passed as the last parameter
* (before mode and shift).
*/
virtual IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) {
IndicatorDataEntryValue GetEntryValue(int _mode = 0, int _abs_shift = 0) override {
double _value = EMPTY_VALUE;
double _res[];
ARRAY(double, _res);
switch (Get<ENUM_IDATA_SOURCE_TYPE>(STRUCT_ENUM(IndicatorDataParams, IDATA_PARAM_IDSTYPE))) {
case IDATA_BUILTIN:
_value = Indi_RSI::iRSI(GetSymbol(), GetTf(), iparams.GetPeriod(), iparams.GetAppliedPrice(),
Expand All @@ -327,7 +321,21 @@ class Indi_RSI : public Indicator<IndiRSIParams> {
_value = Indi_RSI::iRSIOnIndicator(THIS_PTR, GetDataSource(), GetSymbol(), GetTf(), iparams.GetPeriod(),
iparams.GetAppliedPrice(), ToRelShift(_abs_shift));
break;
default:
RUNTIME_ERROR("Invalid indicator IDATA_* type!");
}
return _value;
}
};

#ifndef __MQL4__
// Defines global functions (for MQL4 backward compability).
double iRSI(string _symbol, int _tf, int _period, int _ap, int _shift) {
ResetLastError();
return Indi_RSI::iRSI(_symbol, (ENUM_TIMEFRAMES)_tf, _period, (ENUM_APPLIED_PRICE)_ap, _shift);
}
double iRSIOnArray(ARRAY_REF(double, _arr), int _total, int _period, int _abs_shift) {
ResetLastError();
return Indi_RSI::iRSIOnArray(_arr, _total, _period, _abs_shift);
}
#endif
24 changes: 12 additions & 12 deletions Indicators/Price/Indi_Price.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@
struct PriceIndiParams : IndicatorParams {
ENUM_APPLIED_PRICE ap;
// Struct constructor.
PriceIndiParams(ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0) : ap(_ap), IndicatorParams(INDI_PRICE) {
PriceIndiParams(ENUM_APPLIED_PRICE _ap = PRICE_TYPICAL, int _shift = 0) : IndicatorParams(INDI_PRICE), ap(_ap) {
SetShift(_shift);
};
PriceIndiParams(PriceIndiParams &_params) { THIS_REF = _params; };
PriceIndiParams(PriceIndiParams &_params) : IndicatorParams() { THIS_REF = _params; };
// Getters.
ENUM_APPLIED_PRICE GetAppliedPrice() override { return ap; }
ENUM_APPLIED_PRICE GetAppliedPrice() { return ap; }
// Setters.
void SetAppliedPrice(ENUM_APPLIED_PRICE _ap) { ap = _ap; }
};
Expand All @@ -60,7 +60,7 @@ class Indi_Price : public Indicator<PriceIndiParams> {
/**
* Returns possible data source types. It is a bit mask of ENUM_INDI_SUITABLE_DS_TYPE.
*/
virtual unsigned int GetSuitableDataSourceTypes() {
unsigned int GetSuitableDataSourceTypes() override {
// We can work only with Candle-based indicator attached.
return INDI_SUITABLE_DS_TYPE_CANDLE;
}
Expand Down Expand Up @@ -111,21 +111,21 @@ class Indi_Price : public Indicator<PriceIndiParams> {
case INDI_VS_TYPE_PRICE_ASK: // Tick.
case INDI_VS_TYPE_PRICE_BID: // Tick.
return GetPlatformPrices(GetSymbol(), iparams.GetAppliedPrice(), GetTf(), iparams.GetShift())
.GetValueStorage(0);
PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_OPEN: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_OPEN, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_OPEN, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_HIGH: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_HIGH, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_HIGH, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_LOW: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_LOW, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_LOW, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_CLOSE: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_CLOSE, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_CLOSE, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_MEDIAN: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_MEDIAN, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_MEDIAN, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_TYPICAL: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_TYPICAL, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_TYPICAL, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
case INDI_VS_TYPE_PRICE_WEIGHTED: // Candle.
return GetPlatformPrices(GetSymbol(), PRICE_WEIGHTED, GetTf(), iparams.GetShift()).GetValueStorage(0);
return GetPlatformPrices(GetSymbol(), PRICE_WEIGHTED, GetTf(), iparams.GetShift()) PTR_DEREF GetValueStorage(0);
default:
// Trying in parent class.
return Indicator<PriceIndiParams>::GetSpecificValueStorage(_type);
Expand Down
6 changes: 4 additions & 2 deletions Indicators/Tick/Indi_TickMt.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistory
return false;
}

void OnTick(int _global_tick_index) override {
bool OnTick(int _global_tick_index) override {
#ifdef __MQL4__
// Refreshes Ask/Bid constants.
RefreshRates();
Expand All @@ -175,7 +175,7 @@ class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistory
EmitEntry(_entry);
// Appending tick into the history.
AppendEntry(_entry);
return;
return false;
}

#ifdef __debug_verbose__
Expand All @@ -197,5 +197,7 @@ class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistory
AppendEntry(_entry);

// Print("Added tick!");

return true;
}
};
6 changes: 4 additions & 2 deletions Indicators/Tick/Indi_TickProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,10 @@ class Indi_TickProvider : public IndicatorTick<Indi_TickProviderParams, double,

int BufferSize() { return ArraySize(buffer); }

void OnTick(int _global_tick_index) override {
bool OnTick(int _global_tick_index) override {
if (current_index >= ArraySize(buffer)) {
// No more ticks.
return;
return false;
}

TickTAB<double> _tick = buffer[current_index++];
Expand All @@ -117,6 +117,8 @@ class Indi_TickProvider : public IndicatorTick<Indi_TickProviderParams, double,
EmitEntry(_entry);
// Appending tick into the history.
AppendEntry(_entry);

return true;
}
};

Expand Down
4 changes: 3 additions & 1 deletion Indicators/Tick/Indi_TickRandom.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,15 @@ class Indi_TickRandom : public IndicatorTick<Indi_TickRandomParams, double, Item
return false;
}

void OnTick(int _global_tick_index) override {
bool OnTick(int _global_tick_index) override {
float _ask = 1.0f + (1.0f / 32767 * MathRand());
float _bid = 1.0f + (1.0f / 32767 * MathRand());
TickAB<double> _tick(_ask, _bid);
IndicatorDataEntry _entry(TickToEntry(TimeCurrent(), _tick));
EmitEntry(_entry);
// Appending tick into the history.
AppendEntry(_entry);

return true;
}
};
Loading

0 comments on commit 478b640

Please sign in to comment.