Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MT5 code fixes for MT4 indicator support #754

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Indicator/IndicatorData.h
Original file line number Diff line number Diff line change
Expand Up @@ -1438,6 +1438,13 @@ class IndicatorData : public IndicatorBase {
return false;
}

/**
* Fetches historic ticks for a given index (absolute shift) range.
*/
virtual bool FetchHistoryByIndexRange(int _index_from, int _index_to, ARRAY_REF(TickTAB<double>, _out_ticks)) {
return false;
}

/**
* Fetches historic ticks for a given start time and minimum number of tick to retrieve.
*/
Expand Down
5 changes: 5 additions & 0 deletions Indicator/IndicatorTf.provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,14 @@ class ItemsHistoryTfCandleProvider : public ItemsHistoryCandleProvider<TV> {
long _candle_length_ms = (long)spc * 1000;
long _ticks_to_ms = _ticks_from_ms + _candle_length_ms - 1;

// We will try to fetch history by two methods.
// 1. By time range if IndicatorTick supports that way.
if (!_indi_tick PTR_DEREF FetchHistoryByTimeRange(_ticks_from_ms, _ticks_to_ms, _ticks)) {
// 2. By number of bars if IndicatorTick supports that way.
// if (!_indi_tick PTR_DEREF FetchHistoryByIndexRange(_ticks_from_index, _ticks_to_ms, _ticks))) {
// There is no more ticks in the history, giving up.
break;
//}
}

if (ArraySize(_ticks) > 0) {
Expand Down
46 changes: 44 additions & 2 deletions IndicatorLegacy.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,67 @@

#ifdef __MQL4__

#include <EA31337-classes/IndicatorBase.h>
#include <EA31337-classes/Indicator/IndicatorData.h>
#include <EA31337-classes/Platform.h>
#include <EA31337-classes/Std.h>
#include <EA31337-classes/Storage/ObjectsCache.h>
#include <EA31337-classes/Util.h>

#ifndef INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER
#define INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER
#endif

#ifndef INDICATOR_LEGACY_VERSION_RELEASE_BUFFER
#define INDICATOR_LEGACY_VERSION_RELEASE_BUFFER
#endif

#ifdef INDICATOR_LEGACY_VERSION_MT5

#ifndef INDICATOR_LEGACY_VERSION_SHORT

/**
* Replacement for future OnCalculate(). Currently not used, but could be handy in the future.
* Replacement for future OHLC-based OnCalculate().
*/
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[]) {
// We need to call Platform::Tick() and maybe also IndicatorData::EmitHistory() before.
Platform::OnCalculate(rates_total, prev_calculated);

INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER;

int _num_calculated =
OnCalculateMT5(rates_total, prev_calculated, time, open, high, low, close, tick_volume, volume, spread);

INDICATOR_LEGACY_VERSION_RELEASE_BUFFER;

return _num_calculated;
}

#else

/**
* Replacement for future price-based OnCalculate().
*/
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double& price[]) {
// We need to call Platform::Tick() and maybe also IndicatorData::EmitHistory() before.
Platform::OnCalculate(rates_total, prev_calculated);

INDICATOR_LEGACY_VERSION_ACQUIRE_BUFFER;

// NOTE: If compiler sees an error here about parameter conversion then you
// probably must do:
// #define INDICATOR_LEGACY_VERSION_SHORT
// before including IndicatorLegacy.h
int _num_calculated = OnCalculateMT5(rates_total, prev_calculated, begin, price);

INDICATOR_LEGACY_VERSION_RELEASE_BUFFER;

return _num_calculated;
}

#endif

#define OnCalculate OnCalculateMT5

/**
Expand Down
76 changes: 73 additions & 3 deletions Indicators/Tick/Indi_TickMt.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ struct Indi_TickMtParams : IndicatorParams {

// MT platform's tick-based indicator.
class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistoryTickProvider<double>> {
// Caching _to_ms in FetchHistoryByTimeRange() in order to start from last
// shift and don't loop over the same bars again.
long _cache_fetch_history_shift_to_ms;
// Shift to start with if given _to_ms is less that cached _cache_fetch_history_shift_to_ms.
long _cache_fetch_history_shift_shift;

public:
Indi_TickMt(Indi_TickMtParams &_p, ENUM_IDATA_SOURCE_TYPE _idstype = IDATA_BUILTIN, IndicatorData *_indi_src = NULL,
int _indi_src_mode = 0)
Expand All @@ -61,7 +67,10 @@ class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistory
/**
* Initializes the class.
*/
void Init() {}
void Init() {
_cache_fetch_history_shift_to_ms = 0;
_cache_fetch_history_shift_shift = 0;
}

string GetName() override { return "Indi_TickMt"; }

Expand Down Expand Up @@ -121,11 +130,60 @@ class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistory
virtual bool FetchHistoryByTimeRange(long _from_ms, long _to_ms, ARRAY_REF(TickTAB<double>, _out_ticks)) {
ArrayResize(_out_ticks, 0);

#ifdef __MQL4__
// Searching from current bar to older ones.
int _shift;

if (_to_ms <= _cache_fetch_history_shift_to_ms) {
_shift = _cache_fetch_history_shift_shift;
} else {
_shift = 0;
}

string _symbol = GetSymbol();

while (true) {
double _time = (double)iTime(_symbol, PERIOD_M1, _shift);

if (_time == 0) {
// Invalid time.
break;
}

long _time_ms = (long)_time * 1000;

if (_time_ms > _to_ms) {
// No yet get into valid time range.
++_shift;
continue;
}

if (_time_ms < _from_ms) {
// No more ticks.
break;
}

TickTAB<double> _tick_o(_time_ms, iOpen(_Symbol, PERIOD_M1, _shift));
TickTAB<double> _tick_h(_time_ms, iHigh(_Symbol, PERIOD_M1, _shift));
TickTAB<double> _tick_l(_time_ms, iLow(_Symbol, PERIOD_M1, _shift));
TickTAB<double> _tick_c(_time_ms, iClose(_Symbol, PERIOD_M1, _shift));
ArrayPushObject(_out_ticks, _tick_o);
ArrayPushObject(_out_ticks, _tick_h);
ArrayPushObject(_out_ticks, _tick_l);
ArrayPushObject(_out_ticks, _tick_c);
++_shift;
}

if (_shift != -1) {
_cache_fetch_history_shift_to_ms = _to_ms;
_cache_fetch_history_shift_shift = _shift;
}

return ArraySize(_out_ticks) != 0;
#else
static MqlTick _tmp_ticks[];
ArrayResize(_tmp_ticks, 0);

// There's no history in MQL4.
#ifndef __MQL4__
int _tries = 10;

while (_tries > 0) {
Expand Down Expand Up @@ -154,6 +212,18 @@ class Indi_TickMt : public IndicatorTick<Indi_TickMtParams, double, ItemsHistory
return false;
}

/**
* Fetches historic ticks for a given index (absolute shift) range.
*/
virtual bool FetchHistoryByIndexRange(int _index_from, int _index_to, ARRAY_REF(TickTAB<double>, _out_ticks)) {
return false;
}

/**
* Sends historic entries to listening indicators. May be overriden.
*/
virtual void EmitHistory() {}

void OnTick(int _global_tick_index) override {
#ifdef __MQL4__
// Refreshes Ask/Bid constants.
Expand Down
42 changes: 42 additions & 0 deletions Platform.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ class Platform {
// Whether to clear passed periods on consecutive Platform::UpdateTime().
static bool time_clear_flags;

// Whether history for all the indicators was emitted.
static bool emitted_history;

// List of added indicators.
static DictStruct<long, Ref<IndicatorData>> indis;

Expand Down Expand Up @@ -108,6 +111,38 @@ class Platform {
++global_tick_index;
}

/**
* Called by indicators' OnCalculate() method in order to prepare history via
* IndicatorData::EmitHistory() and to call Tick() for each OnCalculate()
* call so Tick indicator can emit new tick and Candle indicator can update
* or add new candle data to be used by all indicators added to the platform
* via Platform::Add...().
*/
static void OnCalculate(const int rates_total, const int prev_calculated) {
if (!emitted_history) {
for (DictStructIterator<long, Ref<IndicatorData>> _iter = indis.Begin(); _iter.IsValid(); ++_iter) {
EmitHistory(_iter.Value().Ptr());
}
emitted_history = true;
}

// We're ready for a tick.
Tick();
}

/**
* Emits history for parent indicators in hierarchy and then for the indicator itself.
*/
static void EmitHistory(IndicatorData *_indi) {
IndicatorData *_parent = _indi PTR_DEREF GetDataSource(false);

if (_parent != nullptr) {
EmitHistory(_parent);
}

_indi PTR_DEREF EmitHistory();
}

/**
* Returns dictionary of added indicators (keyed by unique id).
*/
Expand Down Expand Up @@ -319,6 +354,7 @@ DateTime Platform::time = 0;
unsigned int Platform::time_flags = 0;
bool Platform::time_clear_flags = true;
int Platform::global_tick_index = 0;
bool Platform::emitted_history = false;
DictStruct<long, Ref<IndicatorData>> Platform::indis;
DictStruct<long, Ref<IndicatorData>> Platform::indis_dflt;

Expand Down Expand Up @@ -353,3 +389,9 @@ DictStruct<long, Ref<IndicatorData>> Platform::indis_dflt;
}

#define TEST_INDICATOR_DEFAULT_BINDINGS(C) TEST_INDICATOR_DEFAULT_BINDINGS_PARAMS(C, )

// Auto-initializer for Platform class.
class PlatformAutoInitializer {
public:
PlatformAutoInitializer() { Platform::Init(); }
} _platform_auto_initializer;
Loading