Skip to content

Commit

Permalink
WIP. Added candle grouping when receiving ticks via OnDataSourceEntry().
Browse files Browse the repository at this point in the history
  • Loading branch information
nseam committed Nov 23, 2021
1 parent 7696319 commit 1d2427a
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 18 deletions.
2 changes: 1 addition & 1 deletion Buffer/BufferCandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
* Class to store struct data.
*/
template <typename TV>
class BufferCandle : public BufferStruct<CandleOHLC<TV>> {
class BufferCandle : public BufferStruct<CandleOCTOHLC<TV>> {
protected:
protected:
/* Protected methods */
Expand Down
32 changes: 32 additions & 0 deletions Candle.struct.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct CandleOHLC
#endif
{
T open, high, low, close;

// Struct constructors.
CandleOHLC(T _open = 0, T _high = 0, T _low = 0, T _close = 0) : open(_open), high(_high), low(_low), close(_close) {}
CandleOHLC(ARRAY_REF(T, _prices)) {
Expand Down Expand Up @@ -216,6 +217,37 @@ struct CandleOHLC
string ToCSV() { return StringFormat("%g,%g,%g,%g", open, high, low, close); }
};

/* Structure for storing OHLC values with open and close timestamp. */
template <typename T>
struct CandleOCTOHLC : CandleOHLC<T> {
long open_timestamp, close_timestamp;

// Struct constructors.
CandleOCTOHLC(T _open = 0, T _high = 0, T _low = 0, T _close = 0, long _open_timestamp = -1,
long _close_timestamp = -1)
: CandleOHLC(_open, _high, _low, _close), open_timestamp(_open_timestamp), close_timestamp(_close_timestamp) {}

// Updates OHLC values taking into consideration tick's timestamp.
void Update(long _timestamp, T _price) {
if (_timestamp < open_timestamp) {
open_timestamp = _timestamp;
open = _price;
}
if (_timestamp > close_timestamp) {
close_timestamp = _timestamp;
close = _price;
}
high = MathMax(high, _price);
low = MathMin(low, _price);
}

// Returns timestamp of open price.
long GetOpenTimestamp() { return open_timestamp; }

// Returns timestamp of close price.
long GetCloseTimestamp() { return close_timestamp; }
};

/* Structore for storing OHLC values with timestamp. */
template <typename T>
struct CandleTOHLC : CandleOHLC<T> {
Expand Down
52 changes: 48 additions & 4 deletions Indicator/IndicatorCandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,20 +154,55 @@ class IndicatorCandle : public IndicatorBase {
* Sends historic entries to listening indicators. May be overriden.
*/
void EmitHistory() override {
for (DictStructIterator<long, CandleOHLC<TV>> iter(icdata.Begin()); iter.IsValid(); ++iter) {
IndicatorDataEntry _entry = CandleToEntry(iter.Value());
for (DictStructIterator<long, CandleOCTOHLC<TV>> iter(icdata.Begin()); iter.IsValid(); ++iter) {
IndicatorDataEntry _entry = CandleToEntry(iter.Key(), iter.Value());
EmitEntry(_entry);
}
}

/**
* @todo
*/
IndicatorDataEntry CandleToEntry(CandleOHLC<TV>& _candle) {
IndicatorDataEntry _entry;
IndicatorDataEntry CandleToEntry(long _timestamp, CandleOCTOHLC<TV>& _candle) {
IndicatorDataEntry _entry(4);
_entry.timestamp = _timestamp;
_entry.values[0] = _candle.open;
_entry.values[1] = _candle.high;
_entry.values[2] = _candle.low;
_entry.values[3] = _candle.close;
_entry.SetFlags(INDI_ENTRY_FLAG_IS_VALID);
return _entry;
}

/**
* Adds tick's price to the matching candle and updates its OHLC values.
*/
void UpdateCandle(long _tick_timestamp, double _price) {
long _candle_timestamp = CalcCandleTimestamp(_tick_timestamp);

CandleOCTOHLC<double> _candle;
if (icdata.KeyExists(_candle_timestamp)) {
_candle = icdata.GetByKey(_candle_timestamp);
_candle.Update(_tick_timestamp, _price);
} else {
_candle = CandleOCTOHLC<double>(_price, _price, _price, _price, _tick_timestamp, _tick_timestamp);
}

icdata.Set(_candle_timestamp, _candle);
}

/**
* Calculates candle's timestamp from tick's timestamp.
*/
long CalcCandleTimestamp(long _tick_timestamp) {
return _tick_timestamp - _tick_timestamp % (icparams.GetSecsPerCandle() * 1000);
}

/**
* Called when data source emits new entry (historic or future one).
*/
virtual void OnDataSourceEntry(IndicatorDataEntry& entry) { UpdateCandle(entry.timestamp, entry[0]); };

/**
* Sets indicator data source.
*/
Expand All @@ -181,6 +216,15 @@ class IndicatorCandle : public IndicatorBase {
indi_src.Ptr().OnBecomeDataSourceFor(THIS_PTR);
}

string CandlesToString() {
string _result;
for (DictStructIterator<long, CandleOCTOHLC<TV>> iter(icdata.Begin()); iter.IsValid(); ++iter) {
IndicatorDataEntry _entry = CandleToEntry(iter.Key(), iter.Value());
_result += IntegerToString(iter.Key()) + ": " + _entry.ToString<double>() + "\n";
}
return _result;
}

/* Virtual methods */

/**
Expand Down
10 changes: 7 additions & 3 deletions Indicator/IndicatorTick.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,16 +81,20 @@ class IndicatorTick : public IndicatorBase {
*/
void EmitHistory() override {
for (DictStructIterator<long, TickAB<TV>> iter(itdata.Begin()); iter.IsValid(); ++iter) {
IndicatorDataEntry _entry = TickToEntry(iter.Value());
IndicatorDataEntry _entry = TickToEntry(iter.Key(), iter.Value());
EmitEntry(_entry);
}
}

/**
* @todo
*/
IndicatorDataEntry TickToEntry(TickAB<TV>& _tick) {
IndicatorDataEntry _entry;
IndicatorDataEntry TickToEntry(long _timestamp, TickAB<TV>& _tick) {
IndicatorDataEntry _entry(2);
_entry.timestamp = _timestamp;
_entry.values[0] = _tick.ask;
_entry.values[1] = _tick.bid;
_entry.SetFlags(INDI_ENTRY_FLAG_IS_VALID);
return _entry;
}

Expand Down
36 changes: 26 additions & 10 deletions Indicator/tests/IndicatorTf.test.mq5
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,12 @@
#include "../IndicatorTf.h"
#include "../IndicatorTick.h"

// Structs.
// Parasms for dummy tick-based indicator.
struct IndicatorTickDummyParams : IndicatorParams {
IndicatorTickDummyParams() : IndicatorParams(INDI_TICK, 3, TYPE_DOUBLE) {}
};

// Dummy tick-based indicator.
class IndicatorTickDummy : public IndicatorTick<IndicatorTickDummyParams, double> {
public:
IndicatorTickDummy(string _symbol, int _shift = 0, string _name = "")
Expand All @@ -49,20 +50,24 @@ class IndicatorTickDummy : public IndicatorTick<IndicatorTickDummyParams, double
// Feeding base indicator with historic entries of this indicator.
Print(GetName(), " became a data source for ", _base_indi.GetName());

IndicatorDataEntry _entry;
EmitEntry(_entry);
EmitEntry(_entry);
EmitEntry(_entry);
EmitEntry(TickToEntry(1000, TickAB<double>(1.0f, 1.01f)));
EmitEntry(TickToEntry(1500, TickAB<double>(1.5f, 1.51f)));
EmitEntry(TickToEntry(2000, TickAB<double>(2.0f, 2.01f)));
EmitEntry(TickToEntry(3000, TickAB<double>(3.0f, 3.01f)));
EmitEntry(TickToEntry(4000, TickAB<double>(4.0f, 4.01f)));
EmitEntry(TickToEntry(4100, TickAB<double>(4.1f, 4.11f)));
EmitEntry(TickToEntry(4200, TickAB<double>(4.2f, 4.21f)));
EmitEntry(TickToEntry(4800, TickAB<double>(4.8f, 4.81f)));
};
};

// Structs.
// Params for dummy candle-based indicator.
struct IndicatorTfDummyParams : IndicatorTfParams {
IndicatorTfDummyParams(uint _spc = 60) : IndicatorTfParams(_spc) {}
};

/**
* Price Indicator.
* Dummy candle-based indicator.
*/
class IndicatorTfDummy : public IndicatorTf<IndicatorTfDummyParams> {
public:
Expand All @@ -72,20 +77,31 @@ class IndicatorTfDummy : public IndicatorTf<IndicatorTfDummyParams> {

string GetName() override { return "IndicatorTfDummy(" + IntegerToString(icparams.spc) + ")"; }

void OnDataSourceEntry(IndicatorDataEntry& entry) override { Print(GetName(), " got new entry!"); };
void OnDataSourceEntry(IndicatorDataEntry& entry) override {
// When overriding OnDataSourceEntry() we have to remember to call parent
// method, because IndicatorCandle also need to invoke it in order to
// create/update matching candle.
IndicatorTf<IndicatorTfDummyParams>::OnDataSourceEntry(entry);

Print(GetName(), " got new tick at ", entry.timestamp, ": ", entry.ToString<double>());
}
};

/**
* Implements OnInit().
*/
int OnInit() {
// @todo

Ref<IndicatorTickDummy> indi_tick = new IndicatorTickDummy(_Symbol);

// 1-second candles.
Ref<IndicatorTfDummy> indi_tf = new IndicatorTfDummy(1);

// Candles will take data from tick indicator.
indi_tf.Ptr().SetDataSource(indi_tick.Ptr());

// Printing all grouped candles.
Print(indi_tf.Ptr().GetName(), "'s candles:");
Print(indi_tf.Ptr().CandlesToString());

return (INIT_SUCCEEDED);
}

0 comments on commit 1d2427a

Please sign in to comment.