Article-23677-EGARCH-MQL5-V.../np_graphics.mqh
2026-07-24 23:03:06 +02:00

528 lines
21 KiB
MQL5

//+------------------------------------------------------------------+
//| np_graphics.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#include<Graphics\Graphic.mqh>
#include "np.mqh"
//---
namespace np
{
//+------------------------------------------------------------------+
//| Plot a single matrix where each column is treated as a curve |
//| The X-axis defaults to the 1-based index of the row number |
//+------------------------------------------------------------------+
template<typename T>
CGraphic* plotMatrix(matrix<T>&in, string plot_name, string legend="col", string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true, int linewidth=5, ENUM_CURVE_TYPE curve_type=CURVE_LINES)
{
// Initialize canvas object dynamically
CGraphic* graph = new CGraphic();
// Prepare chart display properties
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
// Create the visual container on the designated chart/subwindow
if(!graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
int cols = (int)in.Cols(), rows = (int)in.Rows();
string colnames[];
// Split the comma-separated legend string into individual curve names
int split = StringSplit(legend, StringGetCharacter(",", 0), colnames);
if(split < cols)
Print(__FUNCTION__, " WARNING: Invalid legend labels ");
T x[], y[];
ArrayResize(x, rows);
// Populate X-axis values sequentially from 1 up to the total number of rows
for(int i=0; i<rows; ++i)
x[i] = T(i+1);
CColorGenerator generator;
// Iterate over each column of the matrix to plot it as a separate curve
for(int i=0; i<cols; ++i)
{
// Extract the column data into a standard MQL array
if(!np::vecAsArray(in.Col(i), y))
{
Print(__FUNCTION__, " error ", GetLastError());
ObjectDelete(chart_id, plot_name);
delete graph;
return NULL;
}
// Determine the label name for the current curve and add it to the graph
CCurve* newcurve = graph.CurveAdd(x, y, generator.Next(), curve_type, split<cols ? legend+string(i+1) : colnames[i]);
newcurve.PointsFill(points_fill);
newcurve.LinesWidth(linewidth);
}
// Configure Axis labels, fonts, and render the complete plot
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(13);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(13);
graph.FontSet("Lucida Console", 13);
graph.CurvePlotAll();
graph.Update();
return graph;
}
//+------------------------------------------------------------------+
//| Plot matching dimensions from two matrices (X and Y coordinates) |
//| Allows plotting either by columns or by rows |
//+------------------------------------------------------------------+
template<typename T>
CGraphic* plotMatrices(matrix<T>&x, matrix<T>&y, string plot_name, bool by_column = false, string x_axis_label="x_axis", string y_axis_label = "y_axis", string legend="col", ENUM_CURVE_TYPE curve_type = CURVE_LINES, int line_width = 5, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true)
{
// Verify dimensionality match depending on the tracking direction (row vs column)
if((by_column && x.Rows()!=y.Rows()) || (!by_column && x.Cols()!=y.Cols()))
{
Print(__FUNCTION__, " invalid inputs ");
return NULL;
}
CGraphic* graph = new CGraphic();
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
if(!graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
int cols = (int)y.Cols(), rows = (int)y.Rows();
string colnames[];
// Split legend string for labels
int split = StringSplit(legend, StringGetCharacter(",", 0), colnames);
if((by_column && split<cols) || (!by_column && split<rows))
Print(__FUNCTION__, " WARNING: Invalid legend labels ");
T xx[], yy[];
CColorGenerator generator;
int ncurves = (by_column) ? cols : rows;
// Loop through curves extraction from rows or columns
for(int i=0; i<ncurves; ++i)
{
// Dynamically extract data depending on tracking configuration
if((by_column && (!np::vecAsArray(y.Col(i), yy) || !np::vecAsArray(x.Col(i), xx))) ||
(!by_column && (!np::vecAsArray(y.Row(i), yy) || !np::vecAsArray(x.Row(i), xx))))
{
Print(__FUNCTION__, " error ", GetLastError());
ObjectDelete(chart_id, plot_name);
delete graph;
return NULL;
}
// Add curve data with custom properties
CCurve* newcurve = graph.CurveAdd(xx, yy, generator.Next(), curve_type, ((by_column && split<cols) || (!by_column && split<rows)) ? legend+string(i+1) : colnames[i]);
newcurve.LinesWidth(line_width);
}
// Complete plot design rules
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(13);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(13);
graph.FontSet("Lucida Console", 13);
graph.CurvePlotAll();
graph.Update();
return graph;
}
//+------------------------------------------------------------------+
//| Renders line matrices then automatically removes them after |
//| a specified duration (timed display) |
//+------------------------------------------------------------------+
template<typename T>
void plotmatrices(matrix<T>&x, matrix<T>&y, string plot_name, bool by_column = false, string x_axis_label="x_axis", string y_axis_label = "y_axis", string legend="col", ENUM_CURVE_TYPE curve_type = CURVE_LINES, int line_width = 5, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true, int duration = 20)
{
// Generate the plot using the primary drawing function
CGraphic *plot = plotMatrices(x, y, plot_name, by_column, x_axis_label, y_axis_label, legend, curve_type, line_width, chart_id, sub_win, x1, y1, x2, y2, chart_show);
if(plot!=NULL)
{
Sleep(duration*1000); // Keep graphic active for the assigned runtime duration
plot.Destroy(); // Clear visual objects from the framework
delete plot; // Deallocate memory
ChartRedraw(chart_id); // Update user workspace UI
return;
}
}
//+------------------------------------------------------------------+
//| Variation of plotMatrices with custom lines width scaling |
//| Features CURVE_POINTS_AND_LINES as its default drawing strategy |
//+------------------------------------------------------------------+
template<typename T>
CGraphic* plotMatrices(matrix<T>&x, matrix<T>&y, string plot_name, bool by_column = false, string x_axis_label="x_axis", string y_axis_label = "y_axis", string legend="col", bool fill_points = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true)
{
if((by_column && x.Rows()!=y.Rows()) || (!by_column && x.Cols()!=y.Cols()))
{
Print(__FUNCTION__, " invalid inputs ");
return NULL;
}
CGraphic* graph = new CGraphic();
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
if(!graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
int cols = (int)y.Cols(), rows = (int)y.Rows();
string colnames[];
int split = StringSplit(legend, StringGetCharacter(",", 0), colnames);
if((by_column && split<cols) || (!by_column && split<rows))
Print(__FUNCTION__, " WARNING: Invalid legend labels ");
T xx[], yy[];
CColorGenerator generator;
int ncurves = (by_column) ? cols : rows;
for(int i=0; i<ncurves; ++i)
{
if((by_column && (!np::vecAsArray(y.Col(i), yy) || !np::vecAsArray(x.Col(i), xx))) ||
(!by_column && (!np::vecAsArray(y.Row(i), yy) || !np::vecAsArray(x.Row(i), xx))))
{
Print(__FUNCTION__, " error ", GetLastError());
ObjectDelete(chart_id, plot_name);
delete graph;
return NULL;
}
// Plots using point markers combined with structured path lines
CCurve* newcurve = graph.CurveAdd(xx, yy, generator.Next(), CURVE_POINTS_AND_LINES, ((by_column && split<cols) || (!by_column && split<rows)) ? legend+string(i+1) : colnames[i]);
newcurve.PointsFill(fill_points);
newcurve.LinesWidth(ncurves-i); // Dynamic calculation: Earlier lines appear bolder than subsequent tracks
}
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(13);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(13);
graph.FontSet("Lucida Console", 13);
graph.CurvePlotAll();
graph.Update();
return graph;
}
//+------------------------------------------------------------------+
//| Temporary mixed point/line plot displaying matrices for a time |
//| window before automated internal deletion |
//+------------------------------------------------------------------+
template<typename T>
void plotmatrices(matrix<T>&x, matrix<T>&y, string plot_name, bool by_column = false, string x_axis_label="x_axis", string y_axis_label = "y_axis", string legend="col", bool fill_points=true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true, int duration = 20)
{
CGraphic *plot = plotMatrices(x, y, plot_name, by_column, x_axis_label, y_axis_label, legend, fill_points, chart_id, sub_win, x1, y1, x2, y2, chart_show);
if(plot!=NULL)
{
Sleep(duration*1000);
plot.Destroy();
delete plot;
ChartRedraw(chart_id);
return;
}
}
//+------------------------------------------------------------------+
//| Plots a single shared X vector against an array of Y vectors |
//+------------------------------------------------------------------+
CGraphic* plotXYs(vector&x, vector&y[], string& curve_names[], string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=750, int y2=500, bool chart_show=true, int line_width = 1, ENUM_CURVE_TYPE curve_type=CURVE_POINTS_AND_LINES)
{
double xa[];
double ya[];
CGraphic* graph = new CGraphic();
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
// Enforce mapping condition: Every vector element array slice must map directly to a layout string
if(y.Size() != curve_names.Size() || !graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CColorGenerator generator;
for(uint i = 0; i<y.Size(); ++i)
{
if(!np::vecAsArray(y[i], ya))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
// Map the base X vector to array format up to the boundary depth matching current Y slice size
if(!np::vecAsArray(x, xa, 0, (int)ya.Size()))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CCurve* newcurve = graph.CurveAdd(xa, ya, generator.Next(), curve_type, curve_names[i]);
newcurve.LinesWidth(line_width);
}
// Format typography and layout spaces for enhanced legend identification details
graph.HistoryNameWidth(200);
graph.HistoryNameSize(15);
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(15);
graph.XAxis().ValuesSize(15);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(15);
graph.YAxis().ValuesSize(15);
graph.FontSet("Lucida Console", 18);
graph.CurvePlotAll();
graph.Update();
return graph;
}
//+------------------------------------------------------------------+
//| Temporary visualization tracking a single X against an array |
//| of Y vectors over a predefined execution delay timeframe |
//+------------------------------------------------------------------+
void plotxys(vector&x, vector&y[], string& curve_names[], string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = false, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=750, int y2=500, bool chart_show=true, int line_width = 3, ENUM_CURVE_TYPE curve_type=CURVE_LINES, int duration = 20)
{
CGraphic *plot = plotXYs(x, y, curve_names, plot_name, x_axis_label, y_axis_label, points_fill, chart_id, sub_win, x1, y1, x2, y2, chart_show, line_width, curve_type);
if(plot!=NULL)
{
Sleep(duration*1000);
plot.Destroy();
delete plot;
ChartRedraw(chart_id);
return;
}
}
//+------------------------------------------------------------------+
//| Standard XY plot matching single X vector against a single Y vector|
//+------------------------------------------------------------------+
template<typename T>
CGraphic* plotXY(vector<T>&x, vector<T>&y, string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=750, int y2=500, bool chart_show=true, int line_width = 1, ENUM_CURVE_TYPE curve_type=CURVE_POINTS_AND_LINES)
{
T xa[];
T ya[];
// Verify coordinate structure sizes align symmetrically before processing data arrays
if(x.Size()!=y.Size() || !np::vecAsArray(x, xa) || !np::vecAsArray(y, ya))
{
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CGraphic* graph = new CGraphic();
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
if(!graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CColorGenerator generator;
CCurve* newcurve = graph.CurveAdd(xa, ya, generator.Next(), curve_type, plot_name);
newcurve.PointsFill(points_fill);
newcurve.LinesWidth(line_width);
graph.HistoryNameWidth(200);
graph.HistoryNameSize(15);
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(15);
graph.XAxis().ValuesSize(15);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(15);
graph.YAxis().ValuesSize(15);
graph.FontSet("Lucida Console", 15);
graph.CurvePlotAll();
graph.Update();
return graph;
}
//+------------------------------------------------------------------+
//| Time-limited display function wrapper for a basic XY line plot |
//+------------------------------------------------------------------+
template<typename T>
void plotxy(vector<T>&x, vector<T>&y, string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=750, int y2=500, bool chart_show=true, int line_width = 1, ENUM_CURVE_TYPE curve_type=CURVE_POINTS_AND_LINES, int duration = 20)
{
CGraphic *plot = plotXY(x, y, plot_name, x_axis_label, y_axis_label, points_fill, chart_id, sub_win, x1, y1, x2, y2, chart_show, line_width, curve_type);
if(plot!=NULL)
{
Sleep(duration*1000);
plot.Destroy();
delete plot;
ChartRedraw(chart_id);
return;
}
}
//+------------------------------------------------------------------+
//| Generates a scatterplot (only rendering independent point markers)|
//+------------------------------------------------------------------+
template<typename T>
CGraphic* scatterplot(vector<T>&x, vector<T>&y, string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true)
{
T xa[];
T ya[];
if(x.Size()!=y.Size() || !np::vecAsArray(x, xa) || !np::vecAsArray(y, ya))
{
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CGraphic* graph = new CGraphic();
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
if(!graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CColorGenerator generator;
// Explicitly uses CURVE_POINTS to exclude connecting lines completely
CCurve* newcurve = graph.CurveAdd(xa, ya, generator.Next(), CURVE_POINTS, plot_name);
newcurve.PointsFill(points_fill);
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(15);
graph.XAxis().ValuesSize(15);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(15);
graph.YAxis().ValuesSize(15);
graph.FontSet("Lucida Console", 18);
graph.CurvePlotAll();
graph.Update();
return graph;
}
//+------------------------------------------------------------------+
//| Time-limited display function wrapper for a scatter plot canvas |
//+------------------------------------------------------------------+
template<typename T>
void scatter_plot(vector<T>&x, vector<T>&y, string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", bool points_fill = true, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true, int duration=20)
{
CGraphic *plot = scatterplot(x, y, plot_name, x_axis_label, y_axis_label, points_fill, chart_id, sub_win, x1, y1, x2, y2, chart_show);
if(plot!=NULL)
{
Sleep(duration*1000);
plot.Destroy();
delete plot;
ChartRedraw(chart_id);
return;
}
}
//+------------------------------------------------------------------+
//| Processes a vector dataset to generate a structured Histogram plot|
//+------------------------------------------------------------------+
template<typename T>
CGraphic* histogram_plot(vector<T>&x, ulong num_bins, string plot_name, string x_axis_label="x_axis", string y_axis_label = "y_axis", int histogram_width = 6, long chart_id=0, int sub_win=0, int x1=30, int y1=40, int x2=550, int y2=310, bool chart_show=true)
{
T xa[];
T ya[];
// Ensure statistical variance is valid; checking that dataset size has sufficient volume per bin
if(x.Size() < num_bins * 10)
{
Print(__FUNCTION__, " invalid parameter: num_bins. Invalid relative to size of dataset");
return NULL;
}
// Calculate step metrics for assigning data points to bins
double range = x.Max() - x.Min();
double width = range / double(num_bins);
if(width == 0)
{
Print(__FUNCTION__, " width is zero ");
return NULL;
}
ArrayResize(xa, (int)num_bins);
ArrayResize(ya, (int)num_bins);
// Calculate and establish the center position coordinate for each bin interval
for(ulong i=0; i<num_bins; ++i)
{
xa[i] = x.Min() + (i + 0.5) * width;
ya[i] = 0.0;
}
// Sort data instances into corresponding frequency distribution slots
for(ulong i=0; i<x.Size(); ++i)
{
ulong ind = ulong((x[i] - x.Min()) / width);
if(ind >= num_bins)
ind = num_bins - 1; // Safeguard boundary overflow anomalies
++ya[ind];
}
CGraphic* graph = new CGraphic();
ChartRedraw(chart_id);
ChartSetInteger(chart_id, CHART_SHOW, chart_show);
if(!graph.Create(chart_id, plot_name, sub_win, x1, y1, x2, y2))
{
delete graph;
Print(__FUNCTION__, " Failed to Create graphical object on the Main chart Err ", GetLastError());
return NULL;
}
CColorGenerator generator;
// Render values specifically formatted using CURVE_HISTOGRAM visual blocks
CCurve* newcurve = graph.CurveAdd(xa, ya, generator.Next(), CURVE_HISTOGRAM, plot_name);
newcurve.HistogramWidth(histogram_width);
graph.XAxis().Name(x_axis_label);
graph.XAxis().NameSize(15);
graph.XAxis().ValuesSize(15);
graph.YAxis().Name(y_axis_label);
graph.YAxis().NameSize(15);
graph.YAxis().ValuesSize(15);
graph.FontSet("Lucida Console", 18);
graph.CurvePlotAll();
graph.Update();
return graph;
}
}