Files
2025-02-07 19:15:50 +03:30

242 lines
7.9 KiB
Plaintext

//+------------------------------------------------------------------+
//| OLAPcore.mqh |
//| Copyright © 2019, Marketeer |
//| https://www.mql5.com/en/users/marketeer |
//| Online Analytical Processing of trading hypercubes |
//| https://www.mql5.com/ru/articles/6602 |
//| https://www.mql5.com/ru/articles/6603 |
//+------------------------------------------------------------------+
#include <OLAP/OLAPcube.mqh>
#include <OLAP/HTMLcube.mqh>
#include <OLAP/CSVcube.mqh>
class DaysRangeSelector: public DateTimeSelector<TRADE_RECORD_FIELDS>
{
protected:
int granulatity;
public:
DaysRangeSelector(const int n): DateTimeSelector<TRADE_RECORD_FIELDS>(FIELD_DURATION, 7), granulatity(n)
{
_typename = typename(this);
}
virtual int getRange() const
{
return granulatity;
}
virtual bool select(const Record *r, int &index) const
{
double d = r.get(selector);
int days = (int)(d / (60 * 60 * 24));
index = MathMin(days, granulatity - 1);
return true;
}
virtual string getLabel(const int index) const
{
return index < granulatity - 1 ? ((index < 10 ? " ": "") + (string)index + "D") : ((string)index + "D+");
}
};
class OLAPWrapper
{
protected:
Selector<TRADE_RECORD_FIELDS> *createSelector(const SELECTORS selector, const TRADE_RECORD_FIELDS field)
{
switch(selector)
{
case SELECTOR_TYPE:
return new TypeSelector();
case SELECTOR_SYMBOL:
return new SymbolSelector();
case SELECTOR_SERIAL:
return new SerialNumberSelector();
case SELECTOR_MAGIC:
return new MagicSelector();
case SELECTOR_PROFITABLE:
return new ProfitableSelector();
case SELECTOR_DURATION:
return new DaysRangeSelector(15); // up to 14 days
case SELECTOR_WEEKDAY:
return field != FIELD_NONE ? new WeekDaySelector(field) : NULL;
case SELECTOR_DAYHOUR:
return field != FIELD_NONE ? new DayHourSelector(field) : NULL;
case SELECTOR_HOURMINUTE:
return field != FIELD_NONE ? new DayHourSelector(field) : NULL;
case SELECTOR_SCALAR:
return field != FIELD_NONE ? new TradeSelector(field) : NULL;
case SELECTOR_QUANTS:
return field != FIELD_NONE ? new QuantizationSelector(field) : NULL;
}
return NULL;
}
public:
void process(
const SELECTORS &selectorArray[], const TRADE_RECORD_FIELDS &selectorField[],
const AGGREGATORS AggregatorType, const TRADE_RECORD_FIELDS AggregatorField, Display &display,
const SORT_BY SortBy = SORT_BY_NONE,
const double Filter1value1 = 0, const double Filter1value2 = 0)
{
int selectorCount = 0;
for(int i = 0; i < MathMin(ArraySize(selectorArray), 3); i++)
{
selectorCount += selectorArray[i] != SELECTOR_NONE;
}
if(selectorCount == 0)
{
Alert("No selectors. Setup at least one of them.");
return;
}
// filter section not used yet >>>
SELECTORS Filter1 = SELECTOR_NONE;
TRADE_RECORD_FIELDS Filter1Field = FIELD_NONE;
if(ArraySize(selectorArray) > 3)
{
Filter1 = selectorArray[3];
}
if(ArraySize(selectorField) > 3)
{
Filter1Field = selectorField[3];
}
// <<< filter section not used
HistoryDataAdapter<CustomTradeRecord> history;
HTMLReportAdapter<CustomTradeRecord> report;
CSVReportAdapter<CustomTradeRecord> external;
DataAdapter *adapter = &history;
if(ReportFile != "")
{
if(StringFind(ReportFile, ".htm") > 0 && report.load(ReportFile))
{
adapter = &report;
}
else
if(StringFind(ReportFile, ".csv") > 0 && external.load(ReportFile))
{
adapter = &external;
}
else
{
Alert("Unknown file format: ", ReportFile);
return;
}
}
else
{
Print("Analyzing account history");
}
Analyst<TRADE_RECORD_FIELDS> *analyst;
Selector<TRADE_RECORD_FIELDS> *selectors[];
ArrayResize(selectors, selectorCount);
for(int i = 0; i < selectorCount; i++)
{
selectors[i] = createSelector(selectorArray[i], selectorField[i]);
if(selectors[i] == NULL)
{
Print("Selector ", i, " is empty. Setup selectors successively (don't leave a hole in-between), specify a field when required");
return;
}
}
// filter section not used yet >>>
Filter<TRADE_RECORD_FIELDS> *filters[];
if(Filter1 != SELECTOR_NONE)
{
ArrayResize(filters, 1);
Selector<TRADE_RECORD_FIELDS> *filterSelector = createSelector(Filter1, Filter1Field);
if(Filter1value1 != Filter1value2)
{
filters[0] = new FilterRange<TRADE_RECORD_FIELDS>(filterSelector, Filter1value1, Filter1value2);
}
else
{
filters[0] = new Filter<TRADE_RECORD_FIELDS>(filterSelector, Filter1value1);
}
}
// <<< filter section not used
Aggregator<TRADE_RECORD_FIELDS> *aggregator;
// MQL does not support a 'class info' metaclass.
// Otherwise we could use an array of classes instead of the switch
switch(AggregatorType)
{
case AGGREGATOR_SUM:
aggregator = new SumAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_AVERAGE:
aggregator = new AverageAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_MAX:
aggregator = new MaxAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_MIN:
aggregator = new MinAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_COUNT:
aggregator = new CountAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_PROFITFACTOR:
aggregator = new ProfitFactorAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_PROGRESSIVE:
aggregator = new ProgressiveTotalAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
case AGGREGATOR_IDENTITY:
aggregator = new IdentityAggregator<TRADE_RECORD_FIELDS>(AggregatorField, selectors, filters);
break;
}
analyst = new Analyst<TRADE_RECORD_FIELDS>(adapter, aggregator, display);
analyst.acquireData();
Print("Symbol number: ", TradeRecord::getSymbolCount());
for(int i = 0; i < TradeRecord::getSymbolCount(); i++)
{
Print(i, "] ", TradeRecord::getSymbol(i));
}
Print("Magic number: ", TradeRecord::getMagicCount());
for(int i = 0; i < TradeRecord::getMagicCount(); i++)
{
Print(i, "] ", TradeRecord::getMagic(i));
}
Print("Filters: ", aggregator.getFilterTitles());
Print("Selectors: ", selectorCount);
analyst.build();
analyst.display(SortBy, AggregatorType == AGGREGATOR_IDENTITY);
delete analyst;
delete aggregator;
for(int i = 0; i < selectorCount; i++)
{
delete selectors[i];
}
for(int i = 0; i < ArraySize(filters); i++)
{
delete filters[i].getSelector();
delete filters[i];
}
}
};