Files
2025-02-07 19:13:59 +03:30

283 lines
16 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+------------------------------------------------------------------+
//| OLAPQuotes.mqh |
//| Copyright © 2016-2020, Marketeer |
//| https://www.mql5.com/en/users/marketeer |
//| Online Analytical Processing of trading hypercubes |
//| https://www.mql5.com/en/articles/6602 |
//| https://www.mql5.com/en/articles/6603 |
//| https://www.mql5.com/en/articles/7535 |
//| https://www.mql5.com/en/articles/7656 |
//| rev. 25.02.2020 |
//+------------------------------------------------------------------+
#include "OLAPCommon.mqh"
#define SELECTORS QUOTE_SELECTORS
#define ENUM_FIELDS QUOTE_RECORD_FIELDS
#define DEFAULT_SELECTOR_TYPE SELECTOR_SHAPE
#define DEFAULT_SELECTOR_FIELD FIELD_NONE
#define DEFAULT_AGGREGATOR_TYPE AGGREGATOR_COUNT
#define DEFAULT_AGGREGATOR_FIELD FIELD_NONE
// QUOTE SELECTORS
enum QUOTE_SELECTORS
{
SELECTOR_NONE, // none
SELECTOR_SHAPE, // type
SELECTOR_INDEX, // ordinal number
/* below datetime field assumed */
SELECTOR_MONTH, // month-of-year
SELECTOR_WEEKDAY, // day-of-week
SELECTOR_DAYHOUR, // hour-of-day
SELECTOR_HOURMINUTE, // minute-of-hour
/* the next require a field as parameter */
SELECTOR_SCALAR, // scalar(field)
SELECTOR_QUANTS, // quants(field)
SELECTOR_FILTER // filter(field)
};
enum QUOTE_RECORD_FIELDS
{
FIELD_NONE, // none
FIELD_INDEX, // index
FIELD_SHAPE, // type (bearish/flat/bullish)
FIELD_DATETIME, // datetime
FIELD_PRICE_OPEN, // open price
FIELD_PRICE_HIGH, // high price
FIELD_PRICE_LOW, // low price
FIELD_PRICE_CLOSE, // close price
FIELD_PRICE_RANGE_OC,// price range (OC)
FIELD_PRICE_RANGE_HL,// price range (HL)
FIELD_SPREAD, // spread
FIELD_TICK_VOLUME, // tick volume
FIELD_REAL_VOLUME, // real volume
FIELD_CUSTOM1, // custom 1
FIELD_CUSTOM2, // custom 2
FIELD_CUSTOM3, // custom 3
FIELD_CUSTOM4, // custom 4
QUOTE_RECORD_FIELDS_LAST //  
}; // ^ invisible non-breaking space to hide this element name (it's auxiliary)
class QuoteSelector: public BaseSelector<QUOTE_RECORD_FIELDS>
{
public:
QuoteSelector(const QUOTE_RECORD_FIELDS field): BaseSelector(field)
{
}
};
class ShapeSelector: public QuoteSelector
{
public:
ShapeSelector(): QuoteSelector(FIELD_SHAPE)
{
_typename = typename(this);
}
virtual bool select(const Record *r, int &index) const
{
index = (int)r.get(selector);
index += 1; // shift from -1, 0, +1 to [0..2]
return index >= getMin() && index <= getMax();
}
virtual int getRange() const
{
return 3; // 0 through 2
}
virtual string getLabel(const int index) const
{
const static string types[3] = {"bearish", "flat", "bullish"};
return types[index];
}
};
class QuotesRecord: public Record
{
protected:
static int counter; // number of bars
const static char datatypes[QUOTE_RECORD_FIELDS_LAST];
void fillByQuotes(const MqlRates &rate)
{
set(FIELD_INDEX, counter++);
set(FIELD_SHAPE, rate.close > rate.open ? +1 : (rate.close < rate.open ? -1 : 0));
set(FIELD_DATETIME, (double)rate.time);
set(FIELD_PRICE_OPEN, rate.open);
set(FIELD_PRICE_HIGH, rate.high);
set(FIELD_PRICE_LOW, rate.low);
set(FIELD_PRICE_CLOSE, rate.close);
set(FIELD_PRICE_RANGE_OC, (rate.close - rate.open) / _Point);
set(FIELD_PRICE_RANGE_HL, (rate.high - rate.low) * MathSign(rate.close - rate.open) / _Point);
set(FIELD_SPREAD, (double)rate.spread);
set(FIELD_TICK_VOLUME, (double)rate.tick_volume);
set(FIELD_REAL_VOLUME, (double)rate.real_volume);
}
public:
QuotesRecord(): Record(QUOTE_RECORD_FIELDS_LAST)
{
}
QuotesRecord(const MqlRates &rate): Record(QUOTE_RECORD_FIELDS_LAST)
{
fillByQuotes(rate);
}
static int getRecordCount()
{
return counter;
}
static void reset()
{
counter = 0;
}
virtual string legend(const int index) const override
{
if(index >= 0 && index < QUOTE_RECORD_FIELDS_LAST)
{
return legendFromEnum<QUOTE_RECORD_FIELDS>((QUOTE_RECORD_FIELDS)index);
//return EnumToString((QUOTE_RECORD_FIELDS)index);
}
return "unknown";
}
static char datatype(const int index)
{
return datatypes[index];
}
};
static int QuotesRecord::counter = 0;
const static char QuotesRecord::datatypes[QUOTE_RECORD_FIELDS_LAST] =
{
0, // none
'i', // index, serial number
'i', // type (-1 down/0/+1 up)
't', // datetime
'd', // open price
'd', // high price
'd', // low price
'd', // close price
'd', // range OC
'd', // range HL
'i', // spread
'i', // tick
'i', // real
'd', // custom 1
'd', // custom 2
'd', // custom 3
'd' // custom 4
};
template<typename T>
class QuotesDataAdapter: public DataAdapter
{
private:
int size;
int cursor;
public:
QuotesDataAdapter()
{
reset();
}
virtual void reset() override
{
size = MathMin(Bars(_Symbol, _Period), TerminalInfoInteger(TERMINAL_MAXBARS));
cursor = size - 1;
T::reset();
}
virtual int reservedSize() const override
{
return size;
}
virtual Record *getNext() override
{
if(cursor >= 0)
{
MqlRates rate[1];
if(CopyRates(_Symbol, _Period, cursor, 1, rate) > 0)
{
cursor--;
return new T(rate[0]);
}
Print(__FILE__, " ", __LINE__, " ", GetLastError());
return NULL;
}
return NULL;
}
virtual bool isOwner() const override
{
return false;
}
virtual int getFieldCount() const override
{
return QUOTE_RECORD_FIELDS_LAST;
}
};
class OLAPEngineQuotes: public OLAPEngine<QUOTE_SELECTORS,QUOTE_RECORD_FIELDS>
{
protected:
virtual Selector<QUOTE_RECORD_FIELDS> *createSelector(const QUOTE_SELECTORS selector, const QUOTE_RECORD_FIELDS field) override
{
switch(selector)
{
case SELECTOR_SHAPE:
return new ShapeSelector();
case SELECTOR_INDEX:
return new SerialNumberSelector<QUOTE_RECORD_FIELDS,QuotesRecord>(FIELD_INDEX);
case SELECTOR_MONTH:
return new MonthSelector<QUOTE_RECORD_FIELDS>(FIELD_DATETIME);
case SELECTOR_WEEKDAY:
return new WorkWeekDaySelector<QUOTE_RECORD_FIELDS>(FIELD_DATETIME);
case SELECTOR_DAYHOUR:
return new DayHourSelector<QUOTE_RECORD_FIELDS>(FIELD_DATETIME);
case SELECTOR_HOURMINUTE:
return new DayHourSelector<QUOTE_RECORD_FIELDS>(FIELD_DATETIME);
case SELECTOR_SCALAR:
return field != FIELD_NONE ? new BaseSelector<QUOTE_RECORD_FIELDS>(field) : NULL;
case SELECTOR_QUANTS:
return field != FIELD_NONE ? new QuantizationSelector<QUOTE_RECORD_FIELDS>(field, quantGranularity) : NULL;
case SELECTOR_FILTER:
return field != FIELD_NONE ? new FilterSelector<QUOTE_RECORD_FIELDS>(field) : NULL;
}
return NULL;
}
virtual void initialize() override
{
Print("Bars read: ", QuotesRecord::getRecordCount());
}
public:
OLAPEngineQuotes(): OLAPEngine() {}
OLAPEngineQuotes(DataAdapter *ptr): OLAPEngine(ptr) {}
};
#ifndef RECORD_CLASS
#define RECORD_CLASS QuotesRecord
#endif
QuotesDataAdapter<RECORD_CLASS> _defaultQuotesAdapter;
OLAPEngineQuotes _defaultEngine;