1583 lines
31 KiB
Plaintext
1583 lines
31 KiB
Plaintext
//+------------------------------------------------------------------+
|
||
|
||
//| Derivative.mq5 |
|
||
|
||
//| EA: finite-difference d1–d3 of price + optional demo signals |
|
||
|
||
//| (Former indicator — attach as Expert Advisor on chart.) |
|
||
|
||
//+------------------------------------------------------------------+
|
||
|
||
#property copyright "Lab"
|
||
|
||
#property link ""
|
||
|
||
#property version "3.00"
|
||
|
||
#property strict
|
||
|
||
#include <Trade/Trade.mqh>
|
||
|
||
#include <Canvas/Canvas.mqh>
|
||
|
||
#property description "DERIVATIVE_CALC EA v3 — derivatives + optional trades; canvas strip or legacy DerivativePlots."
|
||
|
||
#property description "Canvas mode draws d1/d2/d3 at bottom without indicators; legacy mode optional."
|
||
|
||
enum ENUM_DERIVATIVE_VIEW
|
||
|
||
{
|
||
|
||
DERIVATIVE_ALL = 0,
|
||
|
||
DERIVATIVE_LEVEL_1 = 1,
|
||
|
||
DERIVATIVE_LEVEL_2 = 2,
|
||
|
||
DERIVATIVE_LEVEL_3 = 3
|
||
|
||
};
|
||
|
||
input group "=== Instrument ==="
|
||
|
||
input string InpSymbol = ""; // blank = chart symbol
|
||
|
||
input group "=== Series ==="
|
||
|
||
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_CURRENT; // PERIOD_CURRENT = chart TF
|
||
|
||
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
|
||
|
||
input group "=== Layout (reporting focus) ==="
|
||
|
||
input ENUM_DERIVATIVE_VIEW InpWhichDerivative = DERIVATIVE_ALL; // Which values drive Comment / optional trade filter
|
||
|
||
input group "=== Calculus discretization ==="
|
||
|
||
input int InpDiffStep = 1;
|
||
|
||
input bool InpNormalizePoints = true;
|
||
|
||
input group "=== Smoothing ==="
|
||
|
||
input int InpSmoothPeriod = 0;
|
||
|
||
input group "=== On-chart guide (labels on main window) ==="
|
||
|
||
input bool InpShowHelpPanel = true;
|
||
|
||
input color InpHelpTitleColor = clrWhite;
|
||
|
||
input color InpHelpBodyColor = clrSilver;
|
||
|
||
input group "=== Display ==="
|
||
|
||
input bool InpShowComment = true; // Status line + d1/d2/d3 on chart
|
||
|
||
input int InpCommentThrottleMs = 200; // Min real-time ms between Comment() calls (0=off). Visual tester floods redraws without this.
|
||
|
||
input bool InpDebugTrace = false; // Experts/Journal: derivatives + attach diagnostics
|
||
|
||
input group "=== Canvas strip (EA draws d1/d2/d3 — no indicator .ex5) ==="
|
||
|
||
input bool InpUseCanvasPlots = true; // Three stacked strips at bottom (bitmap on main window)
|
||
|
||
input int InpCanvasPlotBars = 320; // Bars across width (series 0 = current)
|
||
|
||
input int InpCanvasPanelHeight = 210; // Total pixel height for three strips
|
||
|
||
input int InpCanvasBottomMargin = 28; // From chart bottom (CORNER_LEFT_LOWER)
|
||
|
||
input int InpCanvasSideMargin = 4; // Left/right inset
|
||
|
||
input int InpCanvasRedrawMs = 350; // Min ms between canvas rebuilds
|
||
|
||
input color InpCanvasBgColor = clrBlack;
|
||
|
||
input color InpCanvasGridColor = clrDimGray;
|
||
|
||
input group "=== Legacy: DerivativePlots indicator (optional) ==="
|
||
|
||
input bool InpAutoAttachDerivativePlots = false; // Requires DerivativePlots.ex5 in Indicators
|
||
|
||
input bool InpAttachPlotsInTester = false; // Non-visual tester: set true if .ex5 present
|
||
|
||
input string InpPlotsIndicatorPath = "DerivativePlots"; // .ex5 basename in Indicators folder
|
||
|
||
input bool InpPlotsSeparateWindows = false; // Three iCustom instances + stacked subwindows
|
||
|
||
input bool InpPlotsUnifyYScale = true; // DerivativePlots InpUnifyPlotYScale
|
||
|
||
input group "=== Optional demo trading (off by default) ==="
|
||
|
||
input bool InpTradeEnabled = false;
|
||
|
||
input double InpLots = 0.01;
|
||
|
||
input ulong InpMagic = 931001;
|
||
|
||
input int InpSlippagePoints = 30;
|
||
|
||
input int InpAtrPeriod = 14;
|
||
|
||
input double InpSlAtrMult = 2.0;
|
||
|
||
input double InpTpAtrMult = 3.0;
|
||
|
||
CTrade g_trade;
|
||
|
||
datetime g_lastBarTime = 0;
|
||
|
||
string g_chartSymbol = "";
|
||
|
||
bool g_pendingDerivativePlotsAttach = false;
|
||
|
||
bool g_derivativePlotsFailedToLoad = false;
|
||
|
||
bool g_derivPlotsAttachDone = false;
|
||
|
||
uint g_lastCommentWallMs = 0;
|
||
|
||
CCanvas g_deriv_canvas;
|
||
|
||
bool g_deriv_canvas_created = false;
|
||
|
||
uint g_lastCanvasRedrawMs = 0;
|
||
|
||
const string HELPER_FAMILY = "DerivRead";
|
||
|
||
const string DERIV_CANVAS_OBJ = "DerivEA_CanvasStrip_v3";
|
||
|
||
void CommentThrottled(const string text)
|
||
|
||
{
|
||
|
||
if(InpCommentThrottleMs <= 0)
|
||
|
||
{
|
||
|
||
Comment(text);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
const uint now = GetTickCount();
|
||
|
||
if(g_lastCommentWallMs != 0 && (now - g_lastCommentWallMs) < (uint)InpCommentThrottleMs)
|
||
|
||
return;
|
||
|
||
g_lastCommentWallMs = now;
|
||
|
||
Comment(text);
|
||
|
||
}
|
||
|
||
string DerivativePlotsMissingHint()
|
||
|
||
{
|
||
|
||
if(!g_derivativePlotsFailedToLoad || !InpAutoAttachDerivativePlots)
|
||
|
||
return "";
|
||
|
||
const string want = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Indicators\\" + InpPlotsIndicatorPath + ".ex5";
|
||
|
||
return "\n--- DerivativePlots NOT loaded ---\nPlace compiled file:\n" + want +
|
||
|
||
"\n(Navigator: Indicators -> right-click -> Open folder -> paste .mq5, Compile.)";
|
||
|
||
}
|
||
|
||
string HelpPrefix()
|
||
|
||
{
|
||
|
||
return HELPER_FAMILY + "_EA_L" + IntegerToString((int)InpWhichDerivative) + "_";
|
||
|
||
}
|
||
|
||
void DeleteOurHelpObjects()
|
||
|
||
{
|
||
|
||
const string px = HelpPrefix();
|
||
|
||
ObjectDelete(0, px + "title");
|
||
|
||
ObjectDelete(0, px + "body");
|
||
|
||
ObjectDelete(0, px + "interp");
|
||
|
||
}
|
||
|
||
bool LabelCreateMain(const string name, const int corner, const int xd, const int yd,
|
||
|
||
const string text, const color clr, const int fontSize, const int anchor)
|
||
|
||
{
|
||
|
||
if(!ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0))
|
||
|
||
return false;
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xd);
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_YDISTANCE, yd);
|
||
|
||
ObjectSetString(0, name, OBJPROP_TEXT, text);
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
|
||
|
||
ObjectSetString(0, name, OBJPROP_FONT, "Consolas");
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
|
||
|
||
ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
void TryBuildHelpPanel()
|
||
|
||
{
|
||
|
||
if(!InpShowHelpPanel)
|
||
|
||
{
|
||
|
||
DeleteOurHelpObjects();
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
const string px = HelpPrefix();
|
||
|
||
ObjectDelete(0, px + "title");
|
||
|
||
ObjectDelete(0, px + "body");
|
||
|
||
ObjectDelete(0, px + "interp");
|
||
|
||
const int x0 = 8;
|
||
|
||
string title = "DERIVATIVE_CALC EA — readout\n";
|
||
|
||
string body = "";
|
||
|
||
string interp = "";
|
||
|
||
if(InpWhichDerivative == DERIVATIVE_ALL)
|
||
|
||
{
|
||
|
||
body =
|
||
|
||
"d1 = slope of price / step h (velocity)\n"
|
||
|
||
"d2 = change of d1 (acceleration)\n"
|
||
|
||
"d3 = change of d2 (jerk)\n"
|
||
|
||
"See Experts log + Comment line for numbers.";
|
||
|
||
interp = "Optional demo trades use Which derivative + sign rules (inputs).";
|
||
|
||
}
|
||
|
||
else if(InpWhichDerivative == DERIVATIVE_LEVEL_1)
|
||
|
||
{
|
||
|
||
title = "EA focus: d1 only\n";
|
||
|
||
body = "d1 > 0 : rising over h bars; < 0 falling; cross 0 : flip.";
|
||
|
||
interp = "Demo buy bias if d1>0 & d2>0 when trade enabled.";
|
||
|
||
}
|
||
|
||
else if(InpWhichDerivative == DERIVATIVE_LEVEL_2)
|
||
|
||
{
|
||
|
||
title = "EA focus: d2 only\n";
|
||
|
||
body = "d2 : momentum building (+) or fading (-) vs d1.";
|
||
|
||
interp = "Use with price context.";
|
||
|
||
}
|
||
|
||
else
|
||
|
||
{
|
||
|
||
title = "EA focus: d3 only\n";
|
||
|
||
body = "d3 : noisy; regime / climax hints.";
|
||
|
||
interp = "Large |d3| → acceleration changing fast.";
|
||
|
||
}
|
||
|
||
if(!LabelCreateMain(px + "title", CORNER_LEFT_UPPER, x0, 20, title, InpHelpTitleColor, 10, ANCHOR_LEFT_UPPER))
|
||
|
||
return;
|
||
|
||
if(!LabelCreateMain(px + "body", CORNER_LEFT_UPPER, x0, 42, body, InpHelpBodyColor, 8, ANCHOR_LEFT_UPPER))
|
||
|
||
{
|
||
|
||
ObjectDelete(0, px + "title");
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
if(!LabelCreateMain(px + "interp", CORNER_LEFT_LOWER, x0, 8, interp, InpHelpBodyColor, 8, ANCHOR_LEFT_LOWER))
|
||
|
||
{
|
||
|
||
ObjectDelete(0, px + "title");
|
||
|
||
ObjectDelete(0, px + "body");
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
double AppliedFromRates(const MqlRates &r)
|
||
|
||
{
|
||
|
||
switch(InpAppliedPrice)
|
||
|
||
{
|
||
|
||
case PRICE_OPEN: return r.open;
|
||
|
||
case PRICE_HIGH: return r.high;
|
||
|
||
case PRICE_LOW: return r.low;
|
||
|
||
case PRICE_CLOSE: return r.close;
|
||
|
||
case PRICE_MEDIAN: return (r.high + r.low) * 0.5;
|
||
|
||
case PRICE_TYPICAL: return (r.high + r.low + r.close) / 3.0;
|
||
|
||
case PRICE_WEIGHTED:return (r.high + r.low + r.close + r.close) / 4.0;
|
||
|
||
default: return r.close;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
void SmoothPriceArray(const int total, const double &src[], double &dst[])
|
||
|
||
{
|
||
|
||
ArrayResize(dst, total);
|
||
|
||
const int p = InpSmoothPeriod;
|
||
|
||
if(p <= 1)
|
||
|
||
{
|
||
|
||
ArrayCopy(dst, src);
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
const double alpha = 2.0 / (p + 1.0);
|
||
|
||
const int oldest = total - 1;
|
||
|
||
double ema = src[oldest];
|
||
|
||
dst[oldest] = ema;
|
||
|
||
for(int i = oldest - 1; i >= 0; i--)
|
||
|
||
{
|
||
|
||
ema = alpha * src[i] + (1.0 - alpha) * ema;
|
||
|
||
dst[i] = ema;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
double SrcAt(const int i, const bool useSmooth, const double &smooth[], const double &raw[])
|
||
|
||
{
|
||
|
||
return useSmooth ? smooth[i] : raw[i];
|
||
|
||
}
|
||
|
||
bool ComputeDerivatives(const string sym, const ENUM_TIMEFRAMES tf,
|
||
|
||
double &out_d1, double &out_d2, double &out_d3)
|
||
|
||
{
|
||
|
||
out_d1 = out_d2 = out_d3 = 0.0;
|
||
|
||
const int h = MathMax(InpDiffStep, 1);
|
||
|
||
const int needBars = 50 + h * 6;
|
||
|
||
MqlRates rates[];
|
||
|
||
ArraySetAsSeries(rates, true);
|
||
|
||
const int n = CopyRates(sym, tf, 0, needBars, rates);
|
||
|
||
if(n < h * 3 + 5)
|
||
|
||
return false;
|
||
|
||
double raw[];
|
||
|
||
ArrayResize(raw, n);
|
||
|
||
ArraySetAsSeries(raw, true);
|
||
|
||
for(int i = 0; i < n; i++)
|
||
|
||
raw[i] = AppliedFromRates(rates[i]);
|
||
|
||
double smoothed[];
|
||
|
||
SmoothPriceArray(n, raw, smoothed);
|
||
|
||
const bool useSmooth = (InpSmoothPeriod > 1);
|
||
|
||
const double scale = InpNormalizePoints ? SymbolInfoDouble(sym, SYMBOL_POINT) : 1.0;
|
||
|
||
if(scale <= 0.0)
|
||
|
||
return false;
|
||
|
||
const int i = 1;
|
||
|
||
if(i + h >= n)
|
||
|
||
return false;
|
||
|
||
const double d1_i = (SrcAt(i, useSmooth, smoothed, raw) - SrcAt(i + h, useSmooth, smoothed, raw)) / ((double)h * scale);
|
||
|
||
if(i + 2 * h >= n)
|
||
|
||
{
|
||
|
||
out_d1 = d1_i;
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
const double d1_ip = (SrcAt(i + h, useSmooth, smoothed, raw) - SrcAt(i + 2 * h, useSmooth, smoothed, raw)) / ((double)h * scale);
|
||
|
||
const double d2_i = (d1_i - d1_ip) / ((double)h * scale);
|
||
|
||
if(i + 3 * h >= n)
|
||
|
||
{
|
||
|
||
out_d1 = d1_i;
|
||
|
||
out_d2 = d2_i;
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
const double d1_ip2 = (SrcAt(i + 2 * h, useSmooth, smoothed, raw) - SrcAt(i + 3 * h, useSmooth, smoothed, raw)) / ((double)h * scale);
|
||
|
||
const double d2_ip = (d1_ip - d1_ip2) / ((double)h * scale);
|
||
|
||
const double d3_i = (d2_i - d2_ip) / ((double)h * scale);
|
||
|
||
out_d1 = d1_i;
|
||
|
||
out_d2 = d2_i;
|
||
|
||
out_d3 = d3_i;
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
double CanvasSeriesAt(const int row, const int si,
|
||
|
||
const double &d1[], const double &d2[], const double &d3[])
|
||
|
||
{
|
||
|
||
if(row == 0)
|
||
|
||
return d1[si];
|
||
|
||
if(row == 1)
|
||
|
||
return d2[si];
|
||
|
||
return d3[si];
|
||
|
||
}
|
||
|
||
bool ComputeDerivativeSeries(const string sym, const ENUM_TIMEFRAMES tf,
|
||
|
||
const int plotBars,
|
||
|
||
double &d1[], double &d2[], double &d3[])
|
||
|
||
{
|
||
|
||
const int h = MathMax(InpDiffStep, 1);
|
||
|
||
const int need = plotBars + h * 4 + 10;
|
||
|
||
MqlRates rates[];
|
||
|
||
ArraySetAsSeries(rates, true);
|
||
|
||
const int n = CopyRates(sym, tf, 0, need, rates);
|
||
|
||
if(n < h * 3 + 5)
|
||
|
||
return false;
|
||
|
||
double raw[];
|
||
|
||
ArrayResize(raw, n);
|
||
|
||
ArraySetAsSeries(raw, true);
|
||
|
||
for(int i = 0; i < n; i++)
|
||
|
||
raw[i] = AppliedFromRates(rates[i]);
|
||
|
||
double smoothed[];
|
||
|
||
SmoothPriceArray(n, raw, smoothed);
|
||
|
||
const bool useSmooth = (InpSmoothPeriod > 1);
|
||
|
||
const double scale = InpNormalizePoints ? SymbolInfoDouble(sym, SYMBOL_POINT) : 1.0;
|
||
|
||
if(scale <= 0.0)
|
||
|
||
return false;
|
||
|
||
ArrayResize(d1, plotBars);
|
||
|
||
ArrayResize(d2, plotBars);
|
||
|
||
ArrayResize(d3, plotBars);
|
||
|
||
ArrayInitialize(d1, EMPTY_VALUE);
|
||
|
||
ArrayInitialize(d2, EMPTY_VALUE);
|
||
|
||
ArrayInitialize(d3, EMPTY_VALUE);
|
||
|
||
const int d1Count = MathMin(plotBars, n - h);
|
||
|
||
for(int si = 0; si < d1Count; si++)
|
||
|
||
d1[si] = (SrcAt(si, useSmooth, smoothed, raw) - SrcAt(si + h, useSmooth, smoothed, raw)) / ((double)h * scale);
|
||
|
||
for(int si = 0; si < plotBars; si++)
|
||
|
||
{
|
||
|
||
if(si + 2 * h >= n || si + h >= d1Count)
|
||
|
||
break;
|
||
|
||
d2[si] = (d1[si] - d1[si + h]) / ((double)h * scale);
|
||
|
||
}
|
||
|
||
for(int si = 0; si < plotBars; si++)
|
||
|
||
{
|
||
|
||
if(si + 3 * h >= n)
|
||
|
||
break;
|
||
|
||
if(si + h >= plotBars)
|
||
|
||
break;
|
||
|
||
if(d2[si] == EMPTY_VALUE || d2[si + h] == EMPTY_VALUE)
|
||
|
||
continue;
|
||
|
||
d3[si] = (d2[si] - d2[si + h]) / ((double)h * scale);
|
||
|
||
}
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
void UpdateDerivativeCanvasStrip()
|
||
|
||
{
|
||
|
||
if(!InpUseCanvasPlots)
|
||
|
||
return;
|
||
|
||
ENUM_TIMEFRAMES tf = InpSignalTF;
|
||
|
||
if(tf == PERIOD_CURRENT)
|
||
|
||
tf = (ENUM_TIMEFRAMES)Period();
|
||
|
||
double d1[], d2[], d3[];
|
||
|
||
if(!ComputeDerivativeSeries(g_chartSymbol, tf, InpCanvasPlotBars, d1, d2, d3))
|
||
|
||
return;
|
||
|
||
const int chartW = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
|
||
|
||
if(chartW < 80)
|
||
|
||
return;
|
||
|
||
const int panelW = MathMax(60, chartW - InpCanvasSideMargin * 2);
|
||
|
||
const int panelH = MathMax(90, InpCanvasPanelHeight);
|
||
|
||
const int x0 = InpCanvasSideMargin;
|
||
|
||
const int y0 = InpCanvasBottomMargin;
|
||
|
||
if(!g_deriv_canvas_created)
|
||
|
||
{
|
||
|
||
if(!g_deriv_canvas.CreateBitmapLabel(0, 0, DERIV_CANVAS_OBJ, x0, y0, panelW, panelH, COLOR_FORMAT_ARGB_NORMALIZE))
|
||
|
||
{
|
||
|
||
if(InpDebugTrace)
|
||
|
||
Print("DERIVATIVE_CALC: canvas CreateBitmapLabel failed err=", GetLastError());
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
ObjectSetInteger(0, DERIV_CANVAS_OBJ, OBJPROP_CORNER, CORNER_LEFT_LOWER);
|
||
|
||
ObjectSetInteger(0, DERIV_CANVAS_OBJ, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER);
|
||
|
||
ObjectSetInteger(0, DERIV_CANVAS_OBJ, OBJPROP_SELECTABLE, false);
|
||
|
||
ObjectSetInteger(0, DERIV_CANVAS_OBJ, OBJPROP_HIDDEN, true);
|
||
|
||
g_deriv_canvas_created = true;
|
||
|
||
}
|
||
|
||
else
|
||
|
||
{
|
||
|
||
g_deriv_canvas.Resize(panelW, panelH);
|
||
|
||
ObjectSetInteger(0, DERIV_CANVAS_OBJ, OBJPROP_XDISTANCE, x0);
|
||
|
||
ObjectSetInteger(0, DERIV_CANVAS_OBJ, OBJPROP_YDISTANCE, y0);
|
||
|
||
}
|
||
|
||
g_deriv_canvas.Erase(ColorToARGB(InpCanvasBgColor, 255));
|
||
|
||
const int rows = 3;
|
||
|
||
const int rowH = MathMax(24, panelH / rows);
|
||
|
||
const uint clrLines[3] = {
|
||
|
||
ColorToARGB(clrDodgerBlue, 235),
|
||
|
||
ColorToARGB(clrOrange, 235),
|
||
|
||
ColorToARGB(clrMagenta, 235)
|
||
|
||
};
|
||
|
||
const string tags[3] = { "d1 velocity", "d2 acceleration", "d3 jerk" };
|
||
|
||
const int nPts = MathMin(InpCanvasPlotBars, ArraySize(d1));
|
||
|
||
if(nPts < 3)
|
||
|
||
{
|
||
|
||
g_deriv_canvas.Update();
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
for(int r = 0; r < rows; r++)
|
||
|
||
{
|
||
|
||
const int yBase = r * rowH;
|
||
|
||
const int midY = yBase + rowH / 2;
|
||
|
||
g_deriv_canvas.LineAA(0.0, (double)midY, (double)(panelW - 1), (double)midY, ColorToARGB(InpCanvasGridColor, 70));
|
||
|
||
double vmin = DBL_MAX;
|
||
|
||
double vmax = -DBL_MAX;
|
||
|
||
for(int si = 0; si < nPts; si++)
|
||
|
||
{
|
||
|
||
const double v = CanvasSeriesAt(r, si, d1, d2, d3);
|
||
|
||
if(v == EMPTY_VALUE || !MathIsValidNumber(v))
|
||
|
||
continue;
|
||
|
||
if(v < vmin)
|
||
|
||
vmin = v;
|
||
|
||
if(v > vmax)
|
||
|
||
vmax = v;
|
||
|
||
}
|
||
|
||
if(vmin == DBL_MAX)
|
||
|
||
continue;
|
||
|
||
if(MathAbs(vmax - vmin) < 1e-15)
|
||
|
||
{
|
||
|
||
vmin -= 1.0;
|
||
|
||
vmax += 1.0;
|
||
|
||
}
|
||
|
||
g_deriv_canvas.FontSet("Consolas", -90);
|
||
|
||
g_deriv_canvas.TextOut(4, yBase + 2, tags[r], ColorToARGB(clrSilver, 220));
|
||
|
||
const double denom = (double)MathMax(1, nPts - 1);
|
||
|
||
for(int si = 0; si < nPts - 1; si++)
|
||
|
||
{
|
||
|
||
const double v0 = CanvasSeriesAt(r, si, d1, d2, d3);
|
||
|
||
const double v1 = CanvasSeriesAt(r, si + 1, d1, d2, d3);
|
||
|
||
if(v0 == EMPTY_VALUE || v1 == EMPTY_VALUE)
|
||
|
||
continue;
|
||
|
||
const double xf0 = (double)(panelW - 1) * (double)(nPts - 1 - si) / denom;
|
||
|
||
const double xf1 = (double)(panelW - 1) * (double)(nPts - 2 - si) / denom;
|
||
|
||
const double t0 = (v0 - vmin) / (vmax - vmin);
|
||
|
||
const double t1 = (v1 - vmin) / (vmax - vmin);
|
||
|
||
const int py0 = yBase + 3 + (int)((double)(rowH - 6) * (1.0 - t0));
|
||
|
||
const int py1 = yBase + 3 + (int)((double)(rowH - 6) * (1.0 - t1));
|
||
|
||
g_deriv_canvas.LineAA(xf0, (double)py0, xf1, (double)py1, clrLines[r]);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
g_deriv_canvas.Update();
|
||
|
||
ChartRedraw(0);
|
||
|
||
}
|
||
|
||
bool HasOurPosition(const string sym)
|
||
|
||
{
|
||
|
||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||
|
||
{
|
||
|
||
const ulong t = PositionGetTicket(i);
|
||
|
||
if(t == 0 || !PositionSelectByTicket(t))
|
||
|
||
continue;
|
||
|
||
if(PositionGetString(POSITION_SYMBOL) != sym)
|
||
|
||
continue;
|
||
|
||
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||
|
||
continue;
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
double AtrPoints(const string sym, const ENUM_TIMEFRAMES tf)
|
||
|
||
{
|
||
|
||
const int h = iATR(sym, tf, InpAtrPeriod);
|
||
|
||
if(h == INVALID_HANDLE)
|
||
|
||
return 0.0;
|
||
|
||
double b[];
|
||
|
||
ArraySetAsSeries(b, true);
|
||
|
||
if(CopyBuffer(h, 0, 1, 1, b) != 1)
|
||
|
||
{
|
||
|
||
IndicatorRelease(h);
|
||
|
||
return 0.0;
|
||
|
||
}
|
||
|
||
IndicatorRelease(h);
|
||
|
||
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||
|
||
return (pt > 0.0 ? b[0] / pt : 0.0);
|
||
|
||
}
|
||
|
||
void RemoveDerivativePlotsIndicatorsFromChart()
|
||
|
||
{
|
||
|
||
const int nw = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
|
||
|
||
for(int w = nw - 1; w >= 0; w--)
|
||
|
||
{
|
||
|
||
const int nc = ChartIndicatorsTotal(0, w);
|
||
|
||
for(int k = nc - 1; k >= 0; k--)
|
||
|
||
{
|
||
|
||
const string nm = ChartIndicatorName(0, w, k);
|
||
|
||
if(StringFind(nm, "DERIV_") >= 0 ||
|
||
|
||
StringFind(nm, "DERIV_PLOTS") >= 0 ||
|
||
|
||
StringFind(nm, InpPlotsIndicatorPath) >= 0 ||
|
||
|
||
StringFind(nm, "DerivativePlots") >= 0)
|
||
|
||
ChartIndicatorDelete(0, w, nm);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
bool DerivativePlotsAlreadyOnChart()
|
||
|
||
{
|
||
|
||
const int nw = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
|
||
|
||
for(int w = 0; w < nw; w++)
|
||
|
||
{
|
||
|
||
const int nc = ChartIndicatorsTotal(0, w);
|
||
|
||
for(int k = 0; k < nc; k++)
|
||
|
||
{
|
||
|
||
const string nm = ChartIndicatorName(0, w, k);
|
||
|
||
if(StringFind(nm, "DERIV_") >= 0 || StringFind(nm, "DERIV_PLOTS") >= 0 ||
|
||
|
||
StringFind(nm, InpPlotsIndicatorPath) >= 0)
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
// Pass every DerivativePlots input (same order as .mq5) so each WhichDerivative gets its own handle.
|
||
|
||
int MakeDerivativePlotsHandle(const string sym, const ENUM_TIMEFRAMES tf,
|
||
|
||
const ENUM_DERIVATIVE_VIEW which, const bool unifyY)
|
||
|
||
{
|
||
|
||
return iCustom(sym, tf, InpPlotsIndicatorPath,
|
||
|
||
InpAppliedPrice,
|
||
|
||
which,
|
||
|
||
unifyY,
|
||
|
||
InpDiffStep,
|
||
|
||
InpNormalizePoints,
|
||
|
||
InpSmoothPeriod,
|
||
|
||
true,
|
||
|
||
InpDebugTrace,
|
||
|
||
false);
|
||
|
||
}
|
||
|
||
bool AttachDerivativePlotsIndicator(const string sym, const ENUM_TIMEFRAMES tf)
|
||
|
||
{
|
||
|
||
if(!InpAutoAttachDerivativePlots)
|
||
|
||
return false;
|
||
|
||
if(g_derivPlotsAttachDone)
|
||
|
||
return true;
|
||
|
||
const string wantPath = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Indicators\\" + InpPlotsIndicatorPath + ".ex5";
|
||
|
||
// ChartIndicatorAdd(chart, subwindow, handle). Subwindow index: use ChartWindowsTotal() before each add
|
||
|
||
// so new panes are appended below existing windows (ATR etc.). Fixed 1,2,3 collides with other indicators.
|
||
|
||
const bool unifyPass = InpPlotsSeparateWindows ? false : InpPlotsUnifyYScale;
|
||
|
||
if(InpPlotsSeparateWindows)
|
||
|
||
{
|
||
|
||
ResetLastError();
|
||
|
||
const int ind1 = MakeDerivativePlotsHandle(sym, tf, DERIVATIVE_LEVEL_1, unifyPass);
|
||
|
||
if(ind1 == INVALID_HANDLE)
|
||
|
||
{
|
||
|
||
g_derivativePlotsFailedToLoad = true;
|
||
|
||
Print("DERIVATIVE_CALC: iCustom(", InpPlotsIndicatorPath, ", d1) failed err=", GetLastError(),
|
||
|
||
". Required:\n ", wantPath);
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg iCustom d1 sym=%s tf=%s", sym, EnumToString(tf));
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
ResetLastError();
|
||
|
||
const int ind2 = MakeDerivativePlotsHandle(sym, tf, DERIVATIVE_LEVEL_2, unifyPass);
|
||
|
||
if(ind2 == INVALID_HANDLE)
|
||
|
||
{
|
||
|
||
IndicatorRelease(ind1);
|
||
|
||
g_derivativePlotsFailedToLoad = true;
|
||
|
||
Print("DERIVATIVE_CALC: iCustom(", InpPlotsIndicatorPath, ", d2) failed err=", GetLastError(),
|
||
|
||
". Required:\n ", wantPath);
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
ResetLastError();
|
||
|
||
const int ind3 = MakeDerivativePlotsHandle(sym, tf, DERIVATIVE_LEVEL_3, unifyPass);
|
||
|
||
if(ind3 == INVALID_HANDLE)
|
||
|
||
{
|
||
|
||
IndicatorRelease(ind1);
|
||
|
||
IndicatorRelease(ind2);
|
||
|
||
g_derivativePlotsFailedToLoad = true;
|
||
|
||
Print("DERIVATIVE_CALC: iCustom(", InpPlotsIndicatorPath, ", d3) failed err=", GetLastError(),
|
||
|
||
". Required:\n ", wantPath);
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg triple iCustom handles ind1=%d ind2=%d ind3=%d (should differ)",
|
||
|
||
ind1, ind2, ind3);
|
||
|
||
RemoveDerivativePlotsIndicatorsFromChart();
|
||
|
||
int sw = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
|
||
|
||
ResetLastError();
|
||
|
||
const bool ok1 = ChartIndicatorAdd(0, sw, ind1);
|
||
|
||
const int err1 = GetLastError();
|
||
|
||
sw = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
|
||
|
||
ResetLastError();
|
||
|
||
const bool ok2 = ChartIndicatorAdd(0, sw, ind2);
|
||
|
||
const int err2 = GetLastError();
|
||
|
||
sw = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
|
||
|
||
ResetLastError();
|
||
|
||
const bool ok3 = ChartIndicatorAdd(0, sw, ind3);
|
||
|
||
const int err3 = GetLastError();
|
||
|
||
IndicatorRelease(ind1);
|
||
|
||
IndicatorRelease(ind2);
|
||
|
||
IndicatorRelease(ind3);
|
||
|
||
if(!ok1 || !ok2 || !ok3)
|
||
|
||
{
|
||
|
||
g_derivativePlotsFailedToLoad = true;
|
||
|
||
Print("DERIVATIVE_CALC: ChartIndicatorAdd (3 panes) failed ok=", ok1, ",", ok2, ",", ok3,
|
||
|
||
" err=", err1, ",", err2, ",", err3, ". File: ", wantPath);
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
g_derivativePlotsFailedToLoad = false;
|
||
|
||
g_derivPlotsAttachDone = true;
|
||
|
||
ChartRedraw(0);
|
||
|
||
Print("DERIVATIVE_CALC: DerivativePlots attached as three stacked subwindows (indices chosen from CHART_WINDOWS_TOTAL).");
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg triple attach OK sym=%s tf=%s", sym, EnumToString(tf));
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
ResetLastError();
|
||
|
||
const int ind = MakeDerivativePlotsHandle(sym, tf, InpWhichDerivative, unifyPass);
|
||
|
||
if(ind == INVALID_HANDLE)
|
||
|
||
{
|
||
|
||
g_derivativePlotsFailedToLoad = true;
|
||
|
||
Print("DERIVATIVE_CALC: iCustom(\"", InpPlotsIndicatorPath, "\") failed err=", GetLastError(),
|
||
|
||
". MT5 could not read the compiled indicator. Required file:\n ", wantPath,
|
||
|
||
"\nCopy lab\\\\EAs\\\\DerivativePlots.mq5 into that Indicators folder, open in MetaEditor, press Compile (F7).");
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg iCustom sym=%s tf=%s applied=%d which=%d unify=%s h=%d norm=%s sm=%d",
|
||
|
||
sym, EnumToString(tf), (int)InpAppliedPrice, (int)InpWhichDerivative,
|
||
|
||
InpPlotsUnifyYScale ? "on" : "off",
|
||
|
||
InpDiffStep, InpNormalizePoints ? "on" : "off", InpSmoothPeriod);
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
RemoveDerivativePlotsIndicatorsFromChart();
|
||
|
||
int sw = (int)ChartGetInteger(0, CHART_WINDOWS_TOTAL);
|
||
|
||
ResetLastError();
|
||
|
||
const bool ok = ChartIndicatorAdd(0, sw, ind);
|
||
|
||
const int errAfterAdd = GetLastError();
|
||
|
||
IndicatorRelease(ind);
|
||
|
||
if(!ok)
|
||
|
||
{
|
||
|
||
g_derivativePlotsFailedToLoad = true;
|
||
|
||
Print("DERIVATIVE_CALC: ChartIndicatorAdd failed err=", errAfterAdd,
|
||
|
||
". Expected file present: ", wantPath);
|
||
|
||
return false;
|
||
|
||
}
|
||
|
||
g_derivativePlotsFailedToLoad = false;
|
||
|
||
g_derivPlotsAttachDone = true;
|
||
|
||
ChartRedraw(0);
|
||
|
||
Print("DERIVATIVE_CALC: subwindow indicator attached (inputs synced from EA).");
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg attach OK handle_was_valid ChartIndicatorAdd err=%d sym=%s tf=%s sw=%d",
|
||
|
||
errAfterAdd, sym, EnumToString(tf), sw);
|
||
|
||
return true;
|
||
|
||
}
|
||
|
||
void TryDemoTrade(const string sym, const ENUM_TIMEFRAMES tf,
|
||
|
||
const double d1, const double d2, const double d3)
|
||
|
||
{
|
||
|
||
if(!InpTradeEnabled || HasOurPosition(sym))
|
||
|
||
return;
|
||
|
||
bool wantBuy = false;
|
||
|
||
bool wantSell = false;
|
||
|
||
switch(InpWhichDerivative)
|
||
|
||
{
|
||
|
||
case DERIVATIVE_ALL:
|
||
|
||
case DERIVATIVE_LEVEL_1:
|
||
|
||
wantBuy = (d1 > 0.0 && d2 > 0.0);
|
||
|
||
wantSell = (d1 < 0.0 && d2 < 0.0);
|
||
|
||
break;
|
||
|
||
case DERIVATIVE_LEVEL_2:
|
||
|
||
wantBuy = (d2 > 0.0);
|
||
|
||
wantSell = (d2 < 0.0);
|
||
|
||
break;
|
||
|
||
default:
|
||
|
||
wantBuy = (d3 > 0.0);
|
||
|
||
wantSell = (d3 < 0.0);
|
||
|
||
break;
|
||
|
||
}
|
||
|
||
if(!wantBuy && !wantSell)
|
||
|
||
return;
|
||
|
||
MqlTick tick;
|
||
|
||
if(!SymbolInfoTick(sym, tick))
|
||
|
||
return;
|
||
|
||
const double atrPts = AtrPoints(sym, tf);
|
||
|
||
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||
|
||
const double slPts = MathMax(atrPts * InpSlAtrMult, 10.0);
|
||
|
||
const double tpPts = MathMax(atrPts * InpTpAtrMult, 10.0);
|
||
|
||
double sl = 0.0, tp = 0.0;
|
||
|
||
if(wantBuy)
|
||
|
||
{
|
||
|
||
sl = tick.ask - slPts * pt;
|
||
|
||
tp = tick.ask + tpPts * pt;
|
||
|
||
g_trade.Buy(InpLots, sym, tick.ask, sl, tp, "DERIVATIVE_CALC demo");
|
||
|
||
}
|
||
|
||
else if(wantSell)
|
||
|
||
{
|
||
|
||
sl = tick.bid + slPts * pt;
|
||
|
||
tp = tick.bid - tpPts * pt;
|
||
|
||
g_trade.Sell(InpLots, sym, tick.bid, sl, tp, "DERIVATIVE_CALC demo");
|
||
|
||
}
|
||
|
||
}
|
||
|
||
int OnInit()
|
||
|
||
{
|
||
|
||
g_derivPlotsAttachDone = false;
|
||
|
||
g_lastCommentWallMs = 0;
|
||
|
||
g_chartSymbol = InpSymbol;
|
||
|
||
StringTrimLeft(g_chartSymbol);
|
||
|
||
StringTrimRight(g_chartSymbol);
|
||
|
||
if(StringLen(g_chartSymbol) == 0)
|
||
|
||
g_chartSymbol = _Symbol;
|
||
|
||
if(!SymbolSelect(g_chartSymbol, true))
|
||
|
||
{
|
||
|
||
Print("DERIVATIVE_CALC EA: cannot select symbol ", g_chartSymbol);
|
||
|
||
return INIT_FAILED;
|
||
|
||
}
|
||
|
||
g_trade.SetExpertMagicNumber((long)InpMagic);
|
||
|
||
g_trade.SetDeviationInPoints(InpSlippagePoints);
|
||
|
||
g_trade.SetTypeFillingBySymbol(g_chartSymbol);
|
||
|
||
ENUM_TIMEFRAMES tf = InpSignalTF;
|
||
|
||
if(tf == PERIOD_CURRENT)
|
||
|
||
tf = (ENUM_TIMEFRAMES)Period();
|
||
|
||
Print("DERIVATIVE_CALC EA started on ", g_chartSymbol, " ", EnumToString(tf),
|
||
|
||
". This is an Expert Advisor — not the Accelerator indicator.");
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg chart_TF=%s signal_TF=%s normalize=%s h=%d sm=%d tester=%s visual=%s",
|
||
|
||
EnumToString((ENUM_TIMEFRAMES)Period()), EnumToString(tf),
|
||
|
||
InpNormalizePoints ? "on" : "off", InpDiffStep, InpSmoothPeriod,
|
||
|
||
MQLInfoInteger(MQL_TESTER) ? "yes" : "no",
|
||
|
||
MQLInfoInteger(MQL_VISUAL_MODE) ? "yes" : "no");
|
||
|
||
DeleteOurHelpObjects();
|
||
|
||
TryBuildHelpPanel();
|
||
|
||
// Do not call iCustom / ChartIndicatorAdd here — Strategy Tester treats failed indicator load in OnInit as a critical error.
|
||
|
||
// Attachment runs on first OnTick instead (see g_pendingDerivativePlotsAttach).
|
||
|
||
if(InpUseCanvasPlots)
|
||
|
||
{
|
||
|
||
RemoveDerivativePlotsIndicatorsFromChart();
|
||
|
||
EventSetMillisecondTimer(120);
|
||
|
||
}
|
||
|
||
else
|
||
|
||
EventKillTimer();
|
||
|
||
if(InpAutoAttachDerivativePlots && !InpUseCanvasPlots)
|
||
|
||
{
|
||
|
||
RemoveDerivativePlotsIndicatorsFromChart();
|
||
|
||
const bool in_tester = (MQLInfoInteger(MQL_TESTER) != 0);
|
||
|
||
const bool visual = (MQLInfoInteger(MQL_VISUAL_MODE) != 0);
|
||
|
||
const bool skip_tester_attach = (in_tester && !visual && !InpAttachPlotsInTester);
|
||
|
||
if(skip_tester_attach)
|
||
|
||
{
|
||
|
||
Print("DERIVATIVE_CALC: non-visual Strategy Tester - skipping DerivativePlots attach. ",
|
||
|
||
"Use visual mode for subwindow plots, or set InpAttachPlotsInTester=true if DerivativePlots.ex5 is in MQL5\\Indicators\\.");
|
||
|
||
}
|
||
|
||
else
|
||
|
||
{
|
||
|
||
g_pendingDerivativePlotsAttach = true;
|
||
|
||
Print("DERIVATIVE_CALC: DerivativePlots attach scheduled on first tick (OnInit cannot safely load custom indicators in tester).");
|
||
|
||
}
|
||
|
||
}
|
||
|
||
return INIT_SUCCEEDED;
|
||
|
||
}
|
||
|
||
void OnDeinit(const int reason)
|
||
|
||
{
|
||
|
||
EventKillTimer();
|
||
|
||
if(g_deriv_canvas_created)
|
||
|
||
{
|
||
|
||
g_deriv_canvas.Destroy();
|
||
|
||
g_deriv_canvas_created = false;
|
||
|
||
}
|
||
|
||
ObjectDelete(0, DERIV_CANVAS_OBJ);
|
||
|
||
g_derivPlotsAttachDone = false;
|
||
|
||
DeleteOurHelpObjects();
|
||
|
||
Comment("");
|
||
|
||
}
|
||
|
||
void OnTimer()
|
||
|
||
{
|
||
|
||
if(!InpUseCanvasPlots)
|
||
|
||
return;
|
||
|
||
const uint now = GetTickCount();
|
||
|
||
if(InpCanvasRedrawMs > 0 && g_lastCanvasRedrawMs != 0 &&
|
||
|
||
(now - g_lastCanvasRedrawMs) < (uint)InpCanvasRedrawMs)
|
||
|
||
return;
|
||
|
||
g_lastCanvasRedrawMs = now;
|
||
|
||
UpdateDerivativeCanvasStrip();
|
||
|
||
}
|
||
|
||
void OnTick()
|
||
|
||
{
|
||
|
||
ENUM_TIMEFRAMES tf = InpSignalTF;
|
||
|
||
if(tf == PERIOD_CURRENT)
|
||
|
||
tf = (ENUM_TIMEFRAMES)Period();
|
||
|
||
if(g_pendingDerivativePlotsAttach && !InpUseCanvasPlots)
|
||
|
||
{
|
||
|
||
g_pendingDerivativePlotsAttach = false;
|
||
|
||
AttachDerivativePlotsIndicator(g_chartSymbol, tf);
|
||
|
||
}
|
||
|
||
const datetime barOpen = iTime(g_chartSymbol, tf, 0);
|
||
|
||
if(barOpen == 0)
|
||
|
||
return;
|
||
|
||
if(barOpen == g_lastBarTime)
|
||
|
||
return;
|
||
|
||
g_lastBarTime = barOpen;
|
||
|
||
double d1 = 0.0, d2 = 0.0, d3 = 0.0;
|
||
|
||
if(!ComputeDerivatives(g_chartSymbol, tf, d1, d2, d3))
|
||
|
||
{
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg ComputeDerivatives FAILED sym=%s tf=%s bar=%s pt=%.12g",
|
||
|
||
g_chartSymbol, EnumToString(tf), TimeToString(barOpen, TIME_DATE | TIME_MINUTES),
|
||
|
||
SymbolInfoDouble(g_chartSymbol, SYMBOL_POINT));
|
||
|
||
if(InpShowComment)
|
||
|
||
CommentThrottled("DERIVATIVE_CALC: not enough bars yet on " + g_chartSymbol + " " + EnumToString(tf) +
|
||
|
||
DerivativePlotsMissingHint());
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
if(InpDebugTrace)
|
||
|
||
PrintFormat("DERIVATIVE_CALC dbg bar=%s sym=%s chart_TF=%s signal_TF=%s | d1=%.8g d2=%.8g d3=%.8g | pt=%.12g norm=%s h=%d",
|
||
|
||
TimeToString(barOpen, TIME_DATE | TIME_MINUTES), g_chartSymbol,
|
||
|
||
EnumToString((ENUM_TIMEFRAMES)Period()), EnumToString(tf),
|
||
|
||
d1, d2, d3, SymbolInfoDouble(g_chartSymbol, SYMBOL_POINT),
|
||
|
||
InpNormalizePoints ? "on" : "off", InpDiffStep);
|
||
|
||
if(InpShowComment)
|
||
|
||
{
|
||
|
||
string c = "DERIVATIVE_CALC EA | " + g_chartSymbol +
|
||
|
||
"\nChart TF: " + EnumToString((ENUM_TIMEFRAMES)Period()) +
|
||
|
||
" Signal TF (inputs): " + EnumToString(tf) +
|
||
|
||
"\nd1=" + DoubleToString(d1, 4) + " d2=" + DoubleToString(d2, 4) + " d3=" + DoubleToString(d3, 4) +
|
||
|
||
"\n(InpWhichDerivative=" + IntegerToString((int)InpWhichDerivative) +
|
||
|
||
" h=" + IntegerToString(InpDiffStep) + " sm=" + IntegerToString(InpSmoothPeriod) + ")" +
|
||
|
||
(InpUseCanvasPlots
|
||
|
||
? "\nPlots: EA canvas strip (bottom of chart)."
|
||
|
||
: ("\nPlots below: DerivativePlots indicator." + DerivativePlotsMissingHint()));
|
||
|
||
CommentThrottled(c);
|
||
|
||
}
|
||
|
||
TryDemoTrade(g_chartSymbol, tf, d1, d2, d3);
|
||
|
||
}
|
||
|
||
//+------------------------------------------------------------------+
|
||
|