190 lines
11 KiB
Plaintext
190 lines
11 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| OLAPGUI_Trades.mqh |
|
|
//| Copyright (c) 2019-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/7656 |
|
|
//+------------------------------------------------------------------+
|
|
|
|
#include <OLAP/GUI/OLAPGUI.mqh>
|
|
|
|
|
|
#define MAX_ALGO_CHOICES 19 // NB: this is TRADE_RECORD_FIELDS_LAST, which is most lengthy one in this row
|
|
#define MAX_AXES_CHOICES 18 // a combination of selectors and trade fields
|
|
|
|
|
|
template<typename S, typename F>
|
|
class OLAPDialog: public OLAPDialogBase
|
|
{
|
|
private:
|
|
OLAPEngine<S,F> *olapcore;
|
|
OLAPDisplay *olapdisplay;
|
|
|
|
public:
|
|
OLAPDialog(OLAPEngine<S,F> &olapimpl);
|
|
~OLAPDialog(void);
|
|
virtual int process() override;
|
|
virtual void setup() override;
|
|
void setPrintLog(const bool printLog)
|
|
{
|
|
if(CheckPointer(olapdisplay) != POINTER_INVALID) olapdisplay.setPrintLog(printLog);
|
|
}
|
|
};
|
|
|
|
|
|
template<typename S, typename F>
|
|
OLAPDialog::OLAPDialog(OLAPEngine<S,F> &olapimpl)
|
|
{
|
|
curveType = CURVE_POINTS;
|
|
olapcore = &olapimpl;
|
|
olapdisplay = new OLAPDisplay(&this);
|
|
}
|
|
|
|
template<typename S, typename F>
|
|
OLAPDialog::~OLAPDialog(void)
|
|
{
|
|
delete olapdisplay;
|
|
}
|
|
|
|
|
|
template<typename S, typename F>
|
|
void OLAPDialog::setup() override
|
|
{
|
|
static const string _settings[ALGO_NUMBER][MAX_ALGO_CHOICES] =
|
|
{
|
|
// enum AGGREGATORS 1:1, default - sum
|
|
{"sum", "average", "max", "min", "count", "profit factor", "progressive total", "identity", "variance"},
|
|
// enum TRADE_RECORD_FIELDS 1:1, default - profit amount
|
|
{"none", "ordinal", "ticket", "symbol", "type",
|
|
"open datetime", "close datetime", "duration", "open price", "close price",
|
|
"magic number", "lot", "profit amount", "profit percent", "profit points",
|
|
"commission", "swap", "custom 1", "custom 2"},
|
|
// enum SORT_BY, default - none
|
|
{"none", "value ascending", "value descending", "label ascending", "label descending"},
|
|
// enum ENUM_CURVE_TYPE partially, default - points
|
|
{"points", "lines", "points/lines", "steps", "histogram"}
|
|
};
|
|
|
|
static const int _defaults[ALGO_NUMBER] = {0, 12, 0, 0};
|
|
|
|
static const string _selectors[MAX_AXES_CHOICES] =
|
|
{
|
|
"(«selector»/field)", "«ordinal»", "«symbol»", "«type»", "«magic number»",
|
|
"«day of week open»", "«day of week close»", "«hour of day open»", "«hour of day close»", "«duration»",
|
|
"lot", "profit amount", "profit percent", "profit points", "commission",
|
|
"swap", "custom 1", "custom 2"
|
|
};
|
|
|
|
ArrayResize(settings, MAX_ALGO_CHOICES);
|
|
|
|
for(int i = 0; i < ALGO_NUMBER; i++)
|
|
{
|
|
if(i == 1)
|
|
{
|
|
TradeRecord r;
|
|
for(int j = 0; j < MAX_ALGO_CHOICES; j++)
|
|
{
|
|
settings[j][i] = r.legend(j);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int j = 0; j < MAX_ALGO_CHOICES; j++)
|
|
{
|
|
settings[j][i] = _settings[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
ArrayCopy(selectors, _selectors);
|
|
ArrayCopy(defaults, _defaults);
|
|
}
|
|
|
|
template<typename S, typename F>
|
|
int OLAPDialog::process() override
|
|
{
|
|
SELECTORS Selectors[4];
|
|
ENUM_FIELDS Fields[4];
|
|
AGGREGATORS at = (AGGREGATORS)m_algo[0].Value();
|
|
ENUM_FIELDS af = (ENUM_FIELDS)(AGGREGATORS)m_algo[1].Value();
|
|
SORT_BY sb = (SORT_BY)m_algo[2].Value();
|
|
|
|
ArrayInitialize(Selectors, SELECTOR_NONE);
|
|
ArrayInitialize(Fields, FIELD_NONE);
|
|
|
|
int matches[10] =
|
|
{
|
|
SELECTOR_NONE,
|
|
SELECTOR_SERIAL,
|
|
SELECTOR_SYMBOL,
|
|
SELECTOR_TYPE,
|
|
SELECTOR_MAGIC,
|
|
SELECTOR_WEEKDAY,
|
|
SELECTOR_WEEKDAY,
|
|
SELECTOR_DAYHOUR,
|
|
SELECTOR_DAYHOUR,
|
|
SELECTOR_DURATION
|
|
};
|
|
|
|
int subfields[] =
|
|
{
|
|
FIELD_LOT,
|
|
FIELD_PROFIT_AMOUNT,
|
|
FIELD_PROFIT_PERCENT,
|
|
FIELD_PROFIT_POINT,
|
|
FIELD_COMMISSION,
|
|
FIELD_SWAP,
|
|
FIELD_CUSTOM_1,
|
|
FIELD_CUSTOM_2
|
|
};
|
|
|
|
for(int i = 0; i < AXES_NUMBER; i++)
|
|
{
|
|
if(!m_axis[i].IsVisible()) continue;
|
|
int v = (int)m_axis[i].Value();
|
|
if(v < 10) // selectors (every one is specialized for a field already)
|
|
{
|
|
Selectors[i] = (SELECTORS)matches[v];
|
|
if(v == 5 || v == 7) Fields[i] = FIELD_OPEN_DATETIME;
|
|
else if(v == 6 || v == 8) Fields[i] = FIELD_CLOSE_DATETIME;
|
|
}
|
|
else // pure fields
|
|
{
|
|
Selectors[i] = at == AGGREGATOR_IDENTITY ? SELECTOR_SCALAR : SELECTOR_QUANTS;
|
|
Fields[i] = (TRADE_RECORD_FIELDS)subfields[v - 10];
|
|
}
|
|
}
|
|
|
|
int dimension = 0;
|
|
for(int i = 0; i < AXES_NUMBER; i++)
|
|
{
|
|
if(Selectors[i] != SELECTOR_NONE) dimension++;
|
|
}
|
|
|
|
m_plot.CurvesRemoveAll();
|
|
AxisCustomizer *customX = NULL;
|
|
AxisCustomizer *customY = NULL;
|
|
|
|
if(at == AGGREGATOR_IDENTITY || at == AGGREGATOR_COUNT) af = FIELD_NONE;
|
|
|
|
if(at != AGGREGATOR_PROGRESSIVE)
|
|
{
|
|
customX = new AxisCustomizer(m_plot.getGraphic(), false, Selectors[0] == SELECTOR_DURATION, (dimension > 1 && SORT_VALUE(sb)));//, (dimension > 1 && SORT_VALUE(sb))
|
|
}
|
|
|
|
if((af == FIELD_DURATION)
|
|
|| (at == AGGREGATOR_IDENTITY
|
|
&& (Selectors[1] == SELECTOR_DURATION
|
|
|| (Selectors[0] == SELECTOR_DURATION && Selectors[1] == SELECTOR_NONE))))
|
|
{
|
|
customY = new AxisCustomizer(m_plot.getGraphic(), true, true);
|
|
}
|
|
|
|
m_plot.InitXAxis(customX);
|
|
m_plot.InitYAxis(customY);
|
|
m_button_ok.Text("Processing...");
|
|
return olapcore.process(Selectors, Fields, at, af, olapdisplay, sb);
|
|
}
|