From f92dda0e485020e9db3b074c74f879f5d5a7fb46 Mon Sep 17 00:00:00 2001 From: Adrian Kierzkowski Date: Fri, 3 Dec 2021 17:31:06 +0100 Subject: [PATCH] Probably working scenario of AMA <- IndicatorTfDummy(IndicatorCandle) <- IndicatorTickReal(IndicatorTick) <- platform ticks' ask/bid prices. --- Indicator/IndicatorCandle.h | 10 +++- Indicator/IndicatorTick.h | 66 +++++---------------- Indicator/tests/IndicatorTf.test.mq5 | 32 ++++++---- Indicator/tests/classes/IndicatorTickReal.h | 63 +++++++++++++++----- Indicators/Indi_AMA.mqh | 3 +- 5 files changed, 93 insertions(+), 81 deletions(-) diff --git a/Indicator/IndicatorCandle.h b/Indicator/IndicatorCandle.h index e229d0539..34dbc37c6 100644 --- a/Indicator/IndicatorCandle.h +++ b/Indicator/IndicatorCandle.h @@ -53,6 +53,7 @@ class IndicatorCandle : public IndicatorBase { void Init() { icdata.AddFlags(DICT_FLAG_FILL_HOLES_UNSORTED); icdata.SetOverflowListener(IndicatorCandleOverflowListener, 10); + icparams.SetMaxModes(4); } public: @@ -91,7 +92,9 @@ class IndicatorCandle : public IndicatorBase { CandleOCTOHLC _candle = icdata.GetByKey(_candle_time); if (!_candle.IsValid()) { - Print(GetName(), ": Missing candle at shift ", _index, " (", _candle_time, ")"); + Print(GetFullName(), ": Missing candle at shift ", _index, " (", TimeToString(_candle_time), ")"); + } else { + Print(GetFullName(), ": Retrieving candle at shift ", _index, " (", TimeToString(_candle_time), ")"); } return CandleToEntry(_candle_time, _candle); @@ -166,6 +169,9 @@ class IndicatorCandle : public IndicatorBase { void UpdateCandle(long _tick_timestamp, double _price) { long _candle_timestamp = CalcCandleTimestamp(_tick_timestamp); + Print("Updating candle for ", GetFullName(), " at candle ", TimeToString(_candle_timestamp), " from tick at ", + TimeToString(_tick_timestamp)); + CandleOCTOHLC _candle(_price, _price, _price, _price, _tick_timestamp, _tick_timestamp); if (icdata.KeyExists(_candle_timestamp)) { // Candle already exists. @@ -180,7 +186,7 @@ class IndicatorCandle : public IndicatorBase { * Calculates candle's timestamp from tick's timestamp. */ long CalcCandleTimestamp(long _tick_timestamp) { - return _tick_timestamp - _tick_timestamp % (icparams.GetSecsPerCandle() * 1000); + return _tick_timestamp - _tick_timestamp % (icparams.GetSecsPerCandle()); } /** diff --git a/Indicator/IndicatorTick.h b/Indicator/IndicatorTick.h index 5d7dd0925..4cf53be6b 100644 --- a/Indicator/IndicatorTick.h +++ b/Indicator/IndicatorTick.h @@ -53,6 +53,8 @@ class IndicatorTick : public IndicatorBase { void Init() { itdata.AddFlags(DICT_FLAG_FILL_HOLES_UNSORTED); itdata.SetOverflowListener(IndicatorTickOverflowListener, 10); + // Ask and Bid price. + itparams.SetMaxModes(2); } public: @@ -106,61 +108,23 @@ class IndicatorTick : public IndicatorBase { * @return * Returns IndicatorDataEntry struct filled with indicator values. */ - IndicatorDataEntry GetEntry(int _timestamp = 0) { + IndicatorDataEntry GetEntry(int _timestamp = 0) override { ResetLastError(); - TickAB _entry = itdata.GetByKey(_timestamp); - /* - IndicatorDataEntry _entry = itdata.GetByKey(_timestamp); - if (!_entry.IsValid() && !_entry.CheckFlag(INDI_ENTRY_FLAG_INSUFFICIENT_DATA)) { - _entry.Resize(itparams.GetMaxModes()); - _entry.timestamp = _timestamp; - for (int _mode = 0; _mode < (int)itparams.GetMaxModes(); _mode++) { - switch (itparams.GetDataValueType()) { - case TYPE_BOOL: - case TYPE_CHAR: - case TYPE_INT: - _entry.values[_mode] = GetValue(_mode, _timestamp); - break; - case TYPE_LONG: - _entry.values[_mode] = GetValue(_mode, _timestamp); - break; - case TYPE_UINT: - _entry.values[_mode] = GetValue(_mode, _timestamp); - break; - case TYPE_ULONG: - _entry.values[_mode] = GetValue(_mode, _timestamp); - break; - case TYPE_DOUBLE: - _entry.values[_mode] = GetValue(_mode, _timestamp); - break; - case TYPE_FLOAT: - _entry.values[_mode] = GetValue(_mode, _timestamp); - break; - case TYPE_STRING: - case TYPE_UCHAR: - default: - SetUserError(ERR_INVALID_PARAMETER); - break; - } - } - GetEntryAlter(_entry, _timestamp); - _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, IsValidEntry(_entry)); - if (_entry.IsValid()) { - itdata.Add(_entry, _timestamp); - istate.is_changed = false; - istate.is_ready = true; - } else { - _entry.AddFlags(INDI_ENTRY_FLAG_INSUFFICIENT_DATA); - } + if (itdata.KeyExists(_timestamp)) { + TickAB _tick = itdata.GetByKey(_timestamp); + return TickToEntry(_timestamp, _tick); } - if (_LastError != ERR_NO_ERROR) { - istate.is_ready = false; - ResetLastError(); + + // No tick at given timestamp. Returning invalid entry. + IndicatorDataEntry _entry(itparams.GetMaxModes()); + GetEntryAlter(_entry, _timestamp); + + for (int i = 0; i < itparams.GetMaxModes(); ++i) { + _entry.values[i] = (double)0; } + + _entry.SetFlag(INDI_ENTRY_FLAG_IS_VALID, false); return _entry; - */ - IndicatorDataEntry _foo; - return _foo; } /** diff --git a/Indicator/tests/IndicatorTf.test.mq5 b/Indicator/tests/IndicatorTf.test.mq5 index 7f9aa4d4d..739855fae 100644 --- a/Indicator/tests/IndicatorTf.test.mq5 +++ b/Indicator/tests/IndicatorTf.test.mq5 @@ -51,33 +51,36 @@ int OnInit() { indicators.Add(indi_tick = new IndicatorTickReal(_Symbol)); // 1-second candles. - indicators.Add(indi_tf = new IndicatorTfDummy(1)); + // indicators.Add(indi_tf = new IndicatorTfDummy(1)); // 1:1 candles from platform using current timeframe. indicators.Add(indi_tf_orig_sim = new IndicatorTfDummy(ChartTf::TfToSeconds(PERIOD_CURRENT))); // 1-second candles. - indicators.Add(indi_ama = new Indi_AMA()); + // indicators.Add(indi_ama = new Indi_AMA()); + + IndiAMAParams _ama_params; + _ama_params.applied_price = PRICE_OPEN; // AMA on platform candles. - indicators.Add(indi_ama_orig_sim = new Indi_AMA()); + indicators.Add(indi_ama_orig_sim = new Indi_AMA(_ama_params)); // Original built-in or OnCalculate()-based AMA indicator on platform OHLCs. - indicators.Add(indi_ama_orig = new Indi_AMA()); + indicators.Add(indi_ama_orig = new Indi_AMA(_ama_params)); // Candles will be initialized from tick's history. - indi_tf.Ptr().SetDataSource(indi_tick.Ptr()); + // indi_tf.Ptr().SetDataSource(indi_tick.Ptr()); indi_tf_orig_sim.Ptr().SetDataSource(indi_tick.Ptr()); // AMA will work on the candle indicator. - indi_ama.Ptr().SetDataSource(indi_tf.Ptr()); + // indi_ama.Ptr().SetDataSource(indi_tf.Ptr()); // AMA will work on the simulation of real candles. indi_ama_orig_sim.Ptr().SetDataSource(indi_tf_orig_sim.Ptr()); // Checking if there are candles for last 100 ticks. - Print(indi_tf.Ptr().GetName(), "'s historic candles (from 100 ticks):"); - Print(indi_tf.Ptr().CandlesToString()); + // Print(indi_tf.Ptr().GetName(), "'s historic candles (from 100 ticks):"); + // Print(indi_tf.Ptr().CandlesToString()); return (INIT_SUCCEEDED); } @@ -85,13 +88,18 @@ int OnInit() { * Implements OnTick(). */ void OnTick() { - indicators.Tick(); string o = DoubleToStr(iOpen(_Symbol, PERIOD_CURRENT, 0), 5); string h = DoubleToStr(iHigh(_Symbol, PERIOD_CURRENT, 0), 5); string l = DoubleToStr(iLow(_Symbol, PERIOD_CURRENT, 0), 5); string c = DoubleToStr(iClose(_Symbol, PERIOD_CURRENT, 0), 5); + string time = TimeToString(iTime(_Symbol, PERIOD_CURRENT, 0)); + + Util::Print("Tick: " + IntegerToString((long)iTime(_Symbol, PERIOD_CURRENT, 0)) + " (" + time + "), real = " + o + + ", " + h + ", " + l + ", " + c); + + indicators.Tick(); - Util::Print("Tick: real = " + o + ", " + h + ", " + l + ", " + c + "\n" + indicators.ToString(0)); + Util::Print(indicators.ToString(0)); } /** @@ -99,6 +107,6 @@ void OnTick() { */ void OnDeinit(const int reason) { // Printing all grouped candles. - Print(indi_tf.Ptr().GetName(), "'s all candles:"); - Print(indi_tf.Ptr().CandlesToString()); + // Print(indi_tf.Ptr().GetName(), "'s all candles:"); + // Print(indi_tf.Ptr().CandlesToString()); } diff --git a/Indicator/tests/classes/IndicatorTickReal.h b/Indicator/tests/classes/IndicatorTickReal.h index f0719417c..668ddc1bf 100644 --- a/Indicator/tests/classes/IndicatorTickReal.h +++ b/Indicator/tests/classes/IndicatorTickReal.h @@ -29,6 +29,10 @@ #pragma once #endif +// Includes. +#include "../../../Chart.struct.static.h" +#include "../../IndicatorTick.h" + // Params for real tick-based indicator. struct IndicatorTickRealParams : IndicatorParams { IndicatorTickRealParams() : IndicatorParams(INDI_TICK, 3, TYPE_DOUBLE) {} @@ -48,25 +52,54 @@ class IndicatorTickReal : public IndicatorTick // Feeding base indicator with historic entries of this indicator. Print(GetFullName(), " became a data source for ", _base_indi.GetFullName()); - int _ticks_to_emit = 100; +#ifndef __MQL4__ + int _ticks_to_emit = 1000; + + Print(_base_indi.GetFullName(), " will be now filled with ", _ticks_to_emit, + " historical entries generated by " + GetFullName()); + + static MqlTick _ticks[]; + ArrayResize(_ticks, 0); + + int _tries = 10; + int _num_copied = -1; + + while (_tries-- > 0) { + _num_copied = CopyTicks(GetSymbol(), _ticks, COPY_TICKS_ALL); - Print(_base_indi.GetFullName(), " will be now filled with 100 historical entries generated by " + GetFullName()); + if (_num_copied == -1) { + Sleep(1000); + } else { + break; + } + } - // For testing purposes we are emitting 100 last ticks. - for (int i = 0; i < MathMin(Bars(GetSymbol(), GetTf()), _ticks_to_emit); ++i) { - long _timestamp = ChartStatic::iTime(GetSymbol(), GetTf(), _ticks_to_emit - i - 1); - double _bid = ChartStatic::iClose(GetSymbol(), GetTf(), _ticks_to_emit - i - 1); - TickAB _tick(0.0f, _bid); - // We can't call EmitTick() here, as tick would go to multiple sources at the same time! - _base_indi.OnDataSourceEntry(TickToEntry(_timestamp, _tick)); + for (int i = 0; i < _num_copied; ++i) { + TickAB _tick(_ticks[i].ask, _ticks[i].bid); + // We can't call EmitEntry() here, as tick would go to multiple sources at the same time! + _base_indi.OnDataSourceEntry(TickToEntry(_ticks[i].time, _tick)); } - }; +#endif + } void OnTick() override { - long _timestamp = ChartStatic::iTime(GetSymbol(), GetTf()); - double _bid = ChartStatic::iClose(GetSymbol(), GetTf()); - // MT doesn't provide historical ask prices, so we're filling tick with bid price only. - TickAB _tick(_bid, _bid); - EmitEntry(TickToEntry(_timestamp, _tick)); +#ifdef __MQL4__ + // Refreshes Ask/Bid constants. + RefreshRates(); + double _ask = Ask; + double _bid = Bid; + long _time = TimeCurrent(); +#else + static MqlTick _ticks[]; + // Copying only the last tick. + int _num_copied = CopyTicks(GetSymbol(), _ticks, COPY_TICKS_INFO, 0, 1); + + Print("TickReal: ", TimeToString(_ticks[0].time), " = ", _ticks[0].bid); + double _ask = _ticks[0].ask; + double _bid = _ticks[0].bid; + long _time = _ticks[0].time; +#endif + TickAB _tick(_ask, _bid); + EmitEntry(TickToEntry(_time, _tick)); } }; diff --git a/Indicators/Indi_AMA.mqh b/Indicators/Indi_AMA.mqh index 25028689a..861d08cda 100644 --- a/Indicators/Indi_AMA.mqh +++ b/Indicators/Indi_AMA.mqh @@ -96,7 +96,8 @@ class Indi_AMA : public Indicator { int _mode = 0, int _shift = 0, IndicatorBase *_obj = NULL) { INDICATOR_CALCULATE_POPULATE_PARAMS_AND_CACHE_SHORT_DS( _indi, _symbol, _tf, _ap, - Util::MakeKey("INDI_AMA", _ama_period, _fast_ema_period, _slow_ema_period, _ama_shift, (int)_ap)); + Util::MakeKey("INDI_AMA_ON_" + _indi.GetFullName(), _ama_period, _fast_ema_period, _slow_ema_period, _ama_shift, + (int)_ap)); return iAMAOnArray(INDICATOR_CALCULATE_POPULATED_PARAMS_SHORT, _ama_period, _fast_ema_period, _slow_ema_period, _ama_shift, _mode, _shift, _cache); }