This commit is contained in:
zhutoutoutousan
2026-05-27 14:59:00 +02:00
parent b5acd37754
commit 3f75a08848
122 changed files with 5259 additions and 12459 deletions
@@ -0,0 +1,26 @@
; CandleChartPattern/main.mq5 — Strategy Tester → Inputs → Load
; Format: Name=Value||From||Step||To||Optimize(Y/N)
; Value = load default (aligned with EA + Desktop 123.set 2026.05.14). From/Step/To used when Y.
;
; === Market ===
InpSymbol=
InpLots=0.01||0.01||0.01||0.2||N
InpMagic=771001||771001||1||771001||N
InpSlippagePoints=30||30||1||300||N
InpMaxSpreadPoints=50||10||5||200||Y
; === Timeframes ===
; Enum timeframes: keep fixed during optimization (change manually if needed).
InpSignalTF=15||0||0||49153||N
InpConfirmTF=16385||0||0||49153||N
; === Patterns (signal TF, shift 1) ===
InpUseEngulfing=true||false||0||true||Y
InpUseHammerPin=true||false||0||true||Y
InpMinBodyPoints=5.0||2.0||0.5||25.0||Y
InpHammerWickRatio=2.0||1.2||0.1||4.0||Y
; === HTF confirmation ===
InpRequireHtfCandleDir=true||false||0||true||Y
InpRequireHtfPattern=false||false||0||true||Y
; === Behaviour ===
InpOnlyOnePosition=true||false||0||true||N
InpCloseOnReverseSignal=true||false||0||true||Y
InpCloseOnAdversePattern=true||false||0||true||Y
+332
View File
@@ -0,0 +1,332 @@
//+------------------------------------------------------------------+
//| CandleChartPattern.mq5 |
//| Lab EA: candle patterns on signal TF + HTF confirmation. |
//| No SL/TP. Exit on opposite signal or adverse pattern. |
//+------------------------------------------------------------------+
#property copyright "Lab"
#property link ""
#property version "1.01"
#property strict
#include <Trade/Trade.mqh>
input group "=== Market ==="
input string InpSymbol = ""; // empty = chart symbol
input double InpLots = 0.01;
input int InpMagic = 771001;
input int InpSlippagePoints = 30;
input int InpMaxSpreadPoints = 50; // 0 = ignore
input group "=== Timeframes ==="
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_M15; // patterns evaluated here (bar 1 = last closed)
input ENUM_TIMEFRAMES InpConfirmTF = PERIOD_H1; // must be >= InpSignalTF for stable bias (not enforced)
input group "=== Patterns (signal TF, shift 1) ==="
input bool InpUseEngulfing = true;
input bool InpUseHammerPin = true;
input double InpMinBodyPoints = 5.0; // min body size for engulfing (points)
input double InpHammerWickRatio = 2.0; // shadow >= ratio * body for hammer/pin
input group "=== HTF confirmation ==="
input bool InpRequireHtfCandleDir = true; // HTF last closed bar same direction as trade idea
input bool InpRequireHtfPattern = false; // if true, same pattern class must also print on HTF bar 1
input group "=== Behaviour ==="
input bool InpOnlyOnePosition = true;
input bool InpCloseOnReverseSignal = true; // close long if validated short setup appears (and vice versa)
input bool InpCloseOnAdversePattern = true; // close long on bearish engulf / bear pin on signal or HTF
CTrade g_trade;
string g_sym;
datetime g_lastSignalBarTime = 0;
ENUM_ORDER_TYPE_FILLING ResolveFilling(const string sym)
{
const long mask = SymbolInfoInteger(sym, SYMBOL_FILLING_MODE);
if((mask & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
return ORDER_FILLING_IOC;
if((mask & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
return ORDER_FILLING_FOK;
return ORDER_FILLING_RETURN;
}
bool SpreadOk(const string sym)
{
if(InpMaxSpreadPoints <= 0)
return true;
const double point = SymbolInfoDouble(sym, SYMBOL_POINT);
if(point <= 0.0)
return false;
const double spreadPts = (SymbolInfoDouble(sym, SYMBOL_ASK) - SymbolInfoDouble(sym, SYMBOL_BID)) / point;
return (spreadPts <= (double)InpMaxSpreadPoints);
}
bool IsNewSignalBar()
{
const datetime t = iTime(g_sym, InpSignalTF, 0);
if(t <= 0)
return false;
if(t == g_lastSignalBarTime)
return false;
g_lastSignalBarTime = t;
return true;
}
double BodyPoints(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
const double o = iOpen(s, tf, sh);
const double c = iClose(s, tf, sh);
const double point = SymbolInfoDouble(s, SYMBOL_POINT);
if(point <= 0.0)
return 0.0;
return MathAbs(c - o) / point;
}
bool BullishEngulfing(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
if(!InpUseEngulfing)
return false;
const double o1 = iOpen(s, tf, sh);
const double c1 = iClose(s, tf, sh);
const double o2 = iOpen(s, tf, sh + 1);
const double c2 = iClose(s, tf, sh + 1);
if(c2 >= o2)
return false;
if(c1 <= o1)
return false;
if(BodyPoints(s, tf, sh) < InpMinBodyPoints || BodyPoints(s, tf, sh + 1) < InpMinBodyPoints)
return false;
return (o1 <= c2 && c1 >= o2);
}
bool BearishEngulfing(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
if(!InpUseEngulfing)
return false;
const double o1 = iOpen(s, tf, sh);
const double c1 = iClose(s, tf, sh);
const double o2 = iOpen(s, tf, sh + 1);
const double c2 = iClose(s, tf, sh + 1);
if(c2 <= o2)
return false;
if(c1 >= o1)
return false;
if(BodyPoints(s, tf, sh) < InpMinBodyPoints || BodyPoints(s, tf, sh + 1) < InpMinBodyPoints)
return false;
return (o1 >= c2 && c1 <= o2);
}
bool BullishHammer(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
if(!InpUseHammerPin)
return false;
const double o = iOpen(s, tf, sh);
const double c = iClose(s, tf, sh);
const double h = iHigh(s, tf, sh);
const double l = iLow(s, tf, sh);
const double body = MathAbs(c - o);
const double lower = MathMin(o, c) - l;
const double upper = h - MathMax(o, c);
const double point = SymbolInfoDouble(s, SYMBOL_POINT);
if(point <= 0.0 || body < point * 0.1)
return false;
return (lower >= InpHammerWickRatio * body && upper <= body);
}
bool BearishPinBar(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
if(!InpUseHammerPin)
return false;
const double o = iOpen(s, tf, sh);
const double c = iClose(s, tf, sh);
const double h = iHigh(s, tf, sh);
const double l = iLow(s, tf, sh);
const double body = MathAbs(c - o);
const double lower = MathMin(o, c) - l;
const double upper = h - MathMax(o, c);
const double point = SymbolInfoDouble(s, SYMBOL_POINT);
if(point <= 0.0 || body < point * 0.1)
return false;
return (upper >= InpHammerWickRatio * body && lower <= body);
}
bool BullishPatternBar(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
return BullishEngulfing(s, tf, sh) || BullishHammer(s, tf, sh);
}
bool BearishPatternBar(const string s, const ENUM_TIMEFRAMES tf, const int sh)
{
return BearishEngulfing(s, tf, sh) || BearishPinBar(s, tf, sh);
}
bool HtfBullishClosedBar(const string s, const ENUM_TIMEFRAMES htf)
{
return (iClose(s, htf, 1) > iOpen(s, htf, 1));
}
bool HtfBearishClosedBar(const string s, const ENUM_TIMEFRAMES htf)
{
return (iClose(s, htf, 1) < iOpen(s, htf, 1));
}
bool ConfirmLong(const string s)
{
if(!InpRequireHtfCandleDir && !InpRequireHtfPattern)
return true;
if(InpRequireHtfCandleDir && !HtfBullishClosedBar(s, InpConfirmTF))
return false;
if(InpRequireHtfPattern && !BullishPatternBar(s, InpConfirmTF, 1))
return false;
return true;
}
bool ConfirmShort(const string s)
{
if(!InpRequireHtfCandleDir && !InpRequireHtfPattern)
return true;
if(InpRequireHtfCandleDir && !HtfBearishClosedBar(s, InpConfirmTF))
return false;
if(InpRequireHtfPattern && !BearishPatternBar(s, InpConfirmTF, 1))
return false;
return true;
}
bool ValidatedLongSetup(const string s)
{
if(!BullishPatternBar(s, InpSignalTF, 1))
return false;
return ConfirmLong(s);
}
bool ValidatedShortSetup(const string s)
{
if(!BearishPatternBar(s, InpSignalTF, 1))
return false;
return ConfirmShort(s);
}
bool HasOurPosition(const string s, const int magic, int &dir)
{
dir = -1;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != s)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != magic)
continue;
const long typ = PositionGetInteger(POSITION_TYPE);
dir = (typ == POSITION_TYPE_BUY) ? 0 : 1;
return true;
}
return false;
}
bool CloseOurPositions(const string s, const int magic)
{
bool ok = true;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
const ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != s)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != magic)
continue;
if(!g_trade.PositionClose(ticket))
ok = false;
}
return ok;
}
int OnInit()
{
g_sym = (StringLen(InpSymbol) == 0) ? _Symbol : InpSymbol;
if(!SymbolSelect(g_sym, true))
{
Print("SymbolSelect failed: ", g_sym);
return INIT_FAILED;
}
g_trade.SetExpertMagicNumber(InpMagic);
g_trade.SetDeviationInPoints(InpSlippagePoints);
g_trade.SetTypeFilling(ResolveFilling(g_sym));
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
}
void OnTick()
{
if(!IsNewSignalBar())
return;
if(Bars(g_sym, InpSignalTF) < 5 || Bars(g_sym, InpConfirmTF) < 5)
return;
if(!SpreadOk(g_sym))
return;
const bool longSetup = ValidatedLongSetup(g_sym);
const bool shortSetup = ValidatedShortSetup(g_sym);
int dir = -1;
bool has = HasOurPosition(g_sym, InpMagic, dir);
if(has)
{
if(dir == 0)
{
bool adverse = false;
if(InpCloseOnAdversePattern)
{
if(BearishPatternBar(g_sym, InpSignalTF, 1) || BearishPatternBar(g_sym, InpConfirmTF, 1))
adverse = true;
}
const bool reverse = (InpCloseOnReverseSignal && shortSetup);
if(adverse || reverse)
CloseOurPositions(g_sym, InpMagic);
}
else if(dir == 1)
{
bool adverse = false;
if(InpCloseOnAdversePattern)
{
if(BullishPatternBar(g_sym, InpSignalTF, 1) || BullishPatternBar(g_sym, InpConfirmTF, 1))
adverse = true;
}
const bool reverse = (InpCloseOnReverseSignal && longSetup);
if(adverse || reverse)
CloseOurPositions(g_sym, InpMagic);
}
}
has = HasOurPosition(g_sym, InpMagic, dir);
if(InpOnlyOnePosition && has)
return;
if(longSetup && !shortSetup)
{
const double ask = SymbolInfoDouble(g_sym, SYMBOL_ASK);
g_trade.Buy(InpLots, g_sym, ask, 0.0, 0.0, "CandlePattern long");
}
else if(shortSetup && !longSetup)
{
const double bid = SymbolInfoDouble(g_sym, SYMBOL_BID);
g_trade.Sell(InpLots, g_sym, bid, 0.0, 0.0, "CandlePattern short");
}
}
File diff suppressed because it is too large Load Diff
+425
View File
@@ -0,0 +1,425 @@
//+------------------------------------------------------------------+
//| DerivativePlots.mq5 |
//| Subwindow line plots for d1 / d2 / d3 — use with Derivative EA |
//| Compile into MQL5\\Indicators\\ (same name). EA can ChartIndicatorAdd.|
//+------------------------------------------------------------------+
#property copyright "Lab"
#property link ""
#property version "1.10"
#property indicator_separate_window
#property indicator_buffers 3
#property indicator_plots 3
#property description "Plots d1 d2 d3 below chart. Match inputs to Derivative EA."
#property indicator_label1 "d1 velocity"
#property indicator_type1 DRAW_LINE
#property indicator_color1 clrDodgerBlue
#property indicator_width1 1
#property indicator_label2 "d2 acceleration"
#property indicator_type2 DRAW_LINE
#property indicator_color2 clrOrange
#property indicator_width2 1
#property indicator_label3 "d3 jerk"
#property indicator_type3 DRAW_LINE
#property indicator_color3 clrMagenta
#property indicator_width3 1
enum ENUM_DERIVATIVE_VIEW
{
DERIVATIVE_ALL = 0,
DERIVATIVE_LEVEL_1 = 1,
DERIVATIVE_LEVEL_2 = 2,
DERIVATIVE_LEVEL_3 = 3
};
input group "=== Source ==="
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
input group "=== Layout ==="
input ENUM_DERIVATIVE_VIEW InpWhichDerivative = DERIVATIVE_ALL; // Single-line modes clear other buffers to EMPTY_VALUE so Y-scale matches the visible line
input bool InpUnifyPlotYScale = true; // Scale d2,d3 for comparable magnitude when normalized (shared subwindow)
input group "=== Calculus ==="
input int InpDiffStep = 1;
input bool InpNormalizePoints = true;
input group "=== Smoothing ==="
input int InpSmoothPeriod = 0;
input group "=== Status ==="
input bool InpShowValueBanner = true; // Text label; short name is DERIV_ALL / DERIV_d1 / DERIV_d2 / DERIV_d3 for ChartWindowFind
input group "=== Debug (Experts / Journal) ==="
input bool InpDebugTrace = false; // Print diagnostics to Experts tab
input bool InpDebugLogEveryCalculate = false; // Log every OnCalculate (very verbose)
double ExtD1[];
double ExtD2[];
double ExtD3[];
string g_deriv_chart_title = "DERIV_ALL";
string g_deriv_stat_obj = "DerivPV_ALL";
void SetupDerivIdentity()
{
switch(InpWhichDerivative)
{
case DERIVATIVE_ALL:
g_deriv_chart_title = "DERIV_ALL";
g_deriv_stat_obj = "DerivPV_ALL";
break;
case DERIVATIVE_LEVEL_1:
g_deriv_chart_title = "DERIV_d1";
g_deriv_stat_obj = "DerivPV_d1";
break;
case DERIVATIVE_LEVEL_2:
g_deriv_chart_title = "DERIV_d2";
g_deriv_stat_obj = "DerivPV_d2";
break;
default:
g_deriv_chart_title = "DERIV_d3";
g_deriv_stat_obj = "DerivPV_d3";
break;
}
}
// OnCalculate passes OHLC with index 0 = oldest bar (non-series). Do not ArraySetAsSeries() those arrays.
double AppliedPriceRowNs(const int pos, const double &open[], const double &high[],
const double &low[], const double &close[])
{
switch(InpAppliedPrice)
{
case PRICE_OPEN: return open[pos];
case PRICE_HIGH: return high[pos];
case PRICE_LOW: return low[pos];
case PRICE_CLOSE: return close[pos];
case PRICE_MEDIAN: return (high[pos] + low[pos]) * 0.5;
case PRICE_TYPICAL: return (high[pos] + low[pos] + close[pos]) / 3.0;
case PRICE_WEIGHTED: return (high[pos] + low[pos] + close[pos] + close[pos]) / 4.0;
default: return close[pos];
}
}
void SmoothPriceArrayNs(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);
dst[0] = src[0];
for(int pos = 1; pos < total; pos++)
dst[pos] = alpha * src[pos] + (1.0 - alpha) * dst[pos - 1];
}
double SrcNs(const int pos, const bool useSmooth, const double &smooth[], const double &raw[])
{
return useSmooth ? smooth[pos] : raw[pos];
}
double DerivativeScalePts()
{
double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
if(pt <= 0.0 || !MathIsValidNumber(pt))
pt = _Point;
if(!InpNormalizePoints)
return 1.0;
if(pt <= 0.0)
return 1.0;
return pt;
}
void DerivPlotsTrace(const int rates_total, const int prev_calculated,
const int h, const int min_bars, const double scale, const bool useSmooth,
const double &close[], const double &WorkNs[], const datetime &time[])
{
if(!InpDebugTrace)
return;
static int s_call = 0;
s_call++;
const int newest = rates_total - 1;
const datetime barOpen = time[newest];
static datetime s_prevBarOpen = 0;
const bool isNewBarTime = (barOpen != s_prevBarOpen);
if(isNewBarTime)
s_prevBarOpen = barOpen;
const bool fullRecalc = (prev_calculated == 0);
if(InpDebugLogEveryCalculate)
{
PrintFormat("DERIV_PLOTS #%d prev_calc=%d rates=%d bar=%s | d1[0]=%.8g d2[0]=%.8g d3[0]=%.8g",
s_call, prev_calculated, rates_total, TimeToString(barOpen, TIME_DATE | TIME_MINUTES),
ExtD1[0], ExtD2[0], ExtD3[0]);
return;
}
if(fullRecalc)
{
const double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
const double rawStep = (newest >= h) ? (WorkNs[newest] - WorkNs[newest - h]) : 0.0;
PrintFormat("DERIV_PLOTS FULL_CALC #%d sym=%s rates=%d prev_calc=%d h=%d min_need=%d smooth=%s which=%d",
s_call, _Symbol, rates_total, prev_calculated, h, min_bars,
useSmooth ? "on" : "off", (int)InpWhichDerivative);
PrintFormat(" scale=%.12g normalize=%s SYPOINT=%.12g _Point=%.12g SYM_DIGITS=%d",
scale, InpNormalizePoints ? "on" : "off", pt, _Point,
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
PrintFormat(" close[oldest]=%.8f close[newest]=%.8f rawStep(newest..newest-h)=%.8f",
close[0], close[newest], rawStep);
PrintFormat(" series buf [0]=current bar: d1=%.8g d2=%.8g d3=%.8g (EMPTY_VALUE=%.8g)",
ExtD1[0], ExtD2[0], ExtD3[0], EMPTY_VALUE);
}
else if(isNewBarTime)
{
PrintFormat("DERIV_PLOTS BAR %s rates=%d prev_calc=%d | d1[0]=%.8g d2[0]=%.8g d3[0]=%.8g",
TimeToString(barOpen, TIME_DATE | TIME_MINUTES), rates_total, prev_calculated,
ExtD1[0], ExtD2[0], ExtD3[0]);
}
}
string FormatPlotVal(const double v)
{
if(v == EMPTY_VALUE || !MathIsValidNumber(v))
return "—";
return DoubleToString(v, 4);
}
void UpdateValueBanner(const int rates_total)
{
if(!InpShowValueBanner || rates_total < 1)
return;
string txt = "";
switch(InpWhichDerivative)
{
case DERIVATIVE_ALL:
txt = StringFormat("d1=%s d2=%s d3=%s (h=%d sm=%d%s)",
FormatPlotVal(ExtD1[0]), FormatPlotVal(ExtD2[0]), FormatPlotVal(ExtD3[0]),
InpDiffStep, InpSmoothPeriod, InpUnifyPlotYScale ? " unifyY" : "");
break;
case DERIVATIVE_LEVEL_1:
txt = StringFormat("d1=%s", FormatPlotVal(ExtD1[0]));
break;
case DERIVATIVE_LEVEL_2:
txt = StringFormat("d2=%s", FormatPlotVal(ExtD2[0]));
break;
default:
txt = StringFormat("d3=%s", FormatPlotVal(ExtD3[0]));
break;
}
IndicatorSetString(INDICATOR_SHORTNAME, g_deriv_chart_title);
const int sub = ChartWindowFind(0, g_deriv_chart_title);
if(sub < 0)
return;
if(ObjectFind(0, g_deriv_stat_obj) < 0)
{
if(!ObjectCreate(0, g_deriv_stat_obj, OBJ_LABEL, sub, 0, 0))
return;
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_CORNER, CORNER_LEFT_UPPER);
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_XDISTANCE, 6);
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_YDISTANCE, 16);
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_COLOR, clrSilver);
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_FONTSIZE, 9);
ObjectSetString(0, g_deriv_stat_obj, OBJPROP_FONT, "Consolas");
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_HIDDEN, true);
}
ObjectSetString(0, g_deriv_stat_obj, OBJPROP_TEXT, txt);
}
// Hide unused buffers from autoscale: DRAW_NONE plots can still skew separate-window limits if buffers hold numbers.
void MaskBuffersForDerivativeView()
{
switch(InpWhichDerivative)
{
case DERIVATIVE_ALL:
break;
case DERIVATIVE_LEVEL_1:
ArrayInitialize(ExtD2, EMPTY_VALUE);
ArrayInitialize(ExtD3, EMPTY_VALUE);
break;
case DERIVATIVE_LEVEL_2:
ArrayInitialize(ExtD1, EMPTY_VALUE);
ArrayInitialize(ExtD3, EMPTY_VALUE);
break;
default:
ArrayInitialize(ExtD1, EMPTY_VALUE);
ArrayInitialize(ExtD2, EMPTY_VALUE);
break;
}
}
void ApplyDerivativeViewMode()
{
switch(InpWhichDerivative)
{
case DERIVATIVE_ALL:
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrDodgerBlue);
PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrOrange);
PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrMagenta);
PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 3);
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 3);
PlotIndexSetInteger(0, PLOT_LINE_STYLE, STYLE_SOLID);
PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_SOLID);
PlotIndexSetInteger(2, PLOT_LINE_STYLE, STYLE_SOLID);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
break;
case DERIVATIVE_LEVEL_1:
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrDodgerBlue);
PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
break;
case DERIVATIVE_LEVEL_2:
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrOrange);
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 3);
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
break;
default:
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE);
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrMagenta);
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 3);
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
break;
}
}
int OnInit()
{
SetIndexBuffer(0, ExtD1, INDICATOR_DATA);
SetIndexBuffer(1, ExtD2, INDICATOR_DATA);
SetIndexBuffer(2, ExtD3, INDICATOR_DATA);
SetupDerivIdentity();
ApplyDerivativeViewMode();
IndicatorSetString(INDICATOR_SHORTNAME, g_deriv_chart_title);
const int dig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
IndicatorSetInteger(INDICATOR_DIGITS, MathMax(6, dig));
if(InpDebugTrace)
PrintFormat("DERIV_PLOTS INIT sym=%s applied=%s h=%d sm=%d norm=%s dbg_every_calc=%s",
_Symbol, EnumToString(InpAppliedPrice), InpDiffStep, InpSmoothPeriod,
InpNormalizePoints ? "on" : "off", InpDebugLogEveryCalculate ? "on" : "off");
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
ObjectDelete(0, g_deriv_stat_obj);
}
int OnCalculate(const int rates_total,
const int prev_calculated,
const datetime &time[],
const double &open[],
const double &high[],
const double &low[],
const double &close[],
const long &tick_volume[],
const long &volume[],
const int &spread[])
{
const int h = MathMax(InpDiffStep, 1);
const int min_bars = 3 * h + 2;
ApplyDerivativeViewMode();
ArrayResize(ExtD1, rates_total);
ArrayResize(ExtD2, rates_total);
ArrayResize(ExtD3, rates_total);
ArraySetAsSeries(ExtD1, true);
ArraySetAsSeries(ExtD2, true);
ArraySetAsSeries(ExtD3, true);
ArrayInitialize(ExtD1, EMPTY_VALUE);
ArrayInitialize(ExtD2, EMPTY_VALUE);
ArrayInitialize(ExtD3, EMPTY_VALUE);
if(rates_total < min_bars)
{
if(InpDebugTrace)
PrintFormat("DERIV_PLOTS SHORT_HISTORY sym=%s rates=%d need=%d (3*h+2, h=%d) — buffers left EMPTY",
_Symbol, rates_total, min_bars, h);
return rates_total;
}
double WorkNs[];
ArrayResize(WorkNs, rates_total);
for(int pos = 0; pos < rates_total; pos++)
WorkNs[pos] = AppliedPriceRowNs(pos, open, high, low, close);
static double SmoothNs[];
SmoothPriceArrayNs(rates_total, WorkNs, SmoothNs);
const bool useSmooth = (InpSmoothPeriod > 1);
const double scale = DerivativeScalePts();
// Bar index pos: 0 = oldest, rates_total-1 = newest. Map to series buffer si = rates_total - 1 - pos (0 = current bar).
const double hs = (double)h * scale;
const bool unify = InpUnifyPlotYScale;
for(int pos = h; pos < rates_total; pos++)
{
const double d1 = (SrcNs(pos, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
const int si = rates_total - 1 - pos;
ExtD1[si] = d1;
}
for(int pos = 2 * h; pos < rates_total; pos++)
{
const double d1_pos = (SrcNs(pos, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
const double d1_pm = (SrcNs(pos - h, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - 2 * h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
double d2 = (d1_pos - d1_pm) / ((double)h * scale);
if(unify)
d2 *= hs;
const int si = rates_total - 1 - pos;
ExtD2[si] = d2;
}
for(int pos = 3 * h; pos < rates_total; pos++)
{
const double d1_pos = (SrcNs(pos, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
const double d1_pm = (SrcNs(pos - h, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - 2 * h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
const double d1_pm2 = (SrcNs(pos - 2 * h, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - 3 * h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
const double d2_pos = (d1_pos - d1_pm) / ((double)h * scale);
const double d2_pm = (d1_pm - d1_pm2) / ((double)h * scale);
double d3 = (d2_pos - d2_pm) / ((double)h * scale);
if(unify)
d3 *= hs * hs;
const int si = rates_total - 1 - pos;
ExtD3[si] = d3;
}
MaskBuffersForDerivativeView();
DerivPlotsTrace(rates_total, prev_calculated, h, min_bars, scale, useSmooth, close, WorkNs, time);
UpdateValueBanner(rates_total);
return rates_total;
}
//+------------------------------------------------------------------+
+383
View File
@@ -0,0 +1,383 @@
//+------------------------------------------------------------------+
//| TFXNZDUSD.mq5 |
//| NZDUSD: HTF directional bias + intraday bearish→bullish shift |
//| Mirrors a reactive workflow: higher TFs for bias (D1/W1), |
//| lower TFs (H4M15) for confirmation — long bias / pullback / |
//| reclaim entry. Not predictive; signals on closed bars. |
//+------------------------------------------------------------------+
#property copyright "Lab"
#property link ""
#property version "1.01"
#property description "NZDUSD long-bias EA: D1/W1 trend filter, intraday EMA cross after pullback streak, ATR risk."
#include <Trade/Trade.mqh>
input group "=== Symbol ==="
input string InpSymbol = "NZDUSD"; // Spot FX symbol (broker-specific)
input group "=== Timeframes (thesis) ==="
input ENUM_TIMEFRAMES InpBiasTF = PERIOD_D1; // Directional bias (monthly/weekly/daily idea → D1 default)
input ENUM_TIMEFRAMES InpHigherBiasTF = PERIOD_W1; // Optional second bias filter
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_H4; // Intraday environment shift (H4 or lower)
input group "=== HTF bias (long-only, reactive) ==="
input bool InpUseWeeklyBias = true; // Require W1 close > W1 EMA
input int InpBiasEmaPeriod = 50; // EMA period on bias TFs
input bool InpAllowCounterBias = false; // If false, skip longs when D1 close < D1 EMA
input group "=== Intraday shift (bearish → bullish) ==="
input int InpFastEma = 8;
input int InpSlowEma = 21;
input int InpMinBearishBars = 3; // Min consecutive bars with fast EMA < slow before cross-up
input bool InpRequireBullBody = true; // Bullish closed candle on cross bar
input group "=== Risk ==="
input double InpLots = 0.10;
input int InpMagic = 926001;
input int InpSlippagePoints = 20;
input int InpMaxSpreadPoints = 40;
input bool InpUseAtrStops = true;
input int InpAtrPeriod = 14;
input double InpSlAtrMult = 1.5;
input double InpTpAtrMult = 2.5;
input double InpMinStopPoints = 50;
input int InpMaxPositions = 1;
input group "=== Session (optional) ==="
input bool InpUseSessionFilter = false;
input int InpSessionStartHour = 7; // Server hour start
input int InpSessionEndHour = 20; // Server hour end (exclusive if cross midnight handled below)
CTrade g_trade;
int g_atrSig = INVALID_HANDLE;
int g_emaBiasD1 = INVALID_HANDLE;
int g_emaBiasW1 = INVALID_HANDLE;
int g_emaFastSig = INVALID_HANDLE;
int g_emaSlowSig = INVALID_HANDLE;
/// Effective TFs after sanity check (genetic optimizers often pass invalid ENUM integers).
ENUM_TIMEFRAMES g_effBiasTF = PERIOD_D1;
ENUM_TIMEFRAMES g_effHigherBiasTF = PERIOD_W1;
ENUM_TIMEFRAMES g_effSignalTF = PERIOD_H4;
datetime g_lastSignalBar = 0;
// Maps garbage timeframe integers from optimization to nearest supported standard period.
ENUM_TIMEFRAMES NearestStandardTf(const ENUM_TIMEFRAMES raw)
{
if(PeriodSeconds(raw) > 0)
return raw;
const ENUM_TIMEFRAMES cand[] =
{
PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1
};
const long r = (long)raw;
ENUM_TIMEFRAMES best = PERIOD_H4;
long bestDist = -1;
for(int i = 0; i < ArraySize(cand); i++)
{
if(PeriodSeconds(cand[i]) <= 0)
continue;
const long diff = r - (long)cand[i];
const long d = (diff >= 0 ? diff : -diff);
if(bestDist < 0 || d < bestDist)
{
bestDist = d;
best = cand[i];
}
}
return best;
}
string WorkSymbol()
{
string s = InpSymbol;
StringTrimLeft(s);
StringTrimRight(s);
// .set files sometimes concatenate optimization payload into string inputs (e.g. "NZDUSD||0||...")
const int bar = StringFind(s, "|");
if(bar >= 0)
s = StringSubstr(s, 0, bar);
StringTrimRight(s);
return (StringLen(s) > 0 ? s : _Symbol);
}
bool SessionOk()
{
if(!InpUseSessionFilter)
return true;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
int h = dt.hour;
if(InpSessionStartHour <= InpSessionEndHour)
return (h >= InpSessionStartHour && h < InpSessionEndHour);
return (h >= InpSessionStartHour || h < InpSessionEndHour);
}
double Buf1(const int handle, const int shift)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(handle, 0, shift, 1, b) != 1)
return 0.0;
return b[0];
}
bool CopyClose(const string sym, const ENUM_TIMEFRAMES tf, const int shift, double &out)
{
double c[];
ArraySetAsSeries(c, true);
if(CopyClose(sym, tf, shift, 1, c) != 1)
return false;
out = c[0];
return true;
}
bool HtfLongBias(const string sym)
{
double cD1 = 0.0, eD1 = 0.0;
if(!CopyClose(sym, g_effBiasTF, 1, cD1))
return false;
eD1 = Buf1(g_emaBiasD1, 1);
if(eD1 <= 0.0)
return false;
if(!InpAllowCounterBias && cD1 <= eD1)
return false;
if(InpUseWeeklyBias)
{
double cW1 = 0.0, eW1 = 0.0;
if(!CopyClose(sym, g_effHigherBiasTF, 1, cW1))
return false;
eW1 = Buf1(g_emaBiasW1, 1);
if(eW1 <= 0.0)
return false;
if(cW1 <= eW1)
return false;
}
return true;
}
int CountConsecutiveBearishEma(const string sym, const int fromShift, const int maxLookback)
{
double f[], s[];
ArraySetAsSeries(f, true);
ArraySetAsSeries(s, true);
int need = maxLookback + fromShift;
if(CopyBuffer(g_emaFastSig, 0, 0, need, f) < need)
return 0;
if(CopyBuffer(g_emaSlowSig, 0, 0, need, s) < need)
return 0;
int n = 0;
for(int i = fromShift; i < fromShift + maxLookback; i++)
{
if(f[i] <= s[i])
n++;
else
break;
}
return n;
}
bool BullishCrossOnLastClosedBar(const string sym)
{
double f1 = Buf1(g_emaFastSig, 1);
double s1 = Buf1(g_emaSlowSig, 1);
double f2 = Buf1(g_emaFastSig, 2);
double s2 = Buf1(g_emaSlowSig, 2);
if(f1 <= 0.0 || s1 <= 0.0 || f2 <= 0.0 || s2 <= 0.0)
return false;
bool crossedUp = (f1 > s1 && f2 <= s2);
if(!crossedUp)
return false;
int bearStreak = CountConsecutiveBearishEma(sym, 2, 32);
if(bearStreak < InpMinBearishBars)
return false;
if(InpRequireBullBody)
{
MqlRates r[];
ArraySetAsSeries(r, true);
if(CopyRates(sym, g_effSignalTF, 1, 1, r) != 1)
return false;
if(r[0].close <= r[0].open)
return false;
}
return true;
}
double NormalizeVolumeLots(const string sym, double lots)
{
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(step > 0.0)
lots = MathFloor(lots / step) * step;
if(lots < minLot)
lots = minLot;
if(lots > maxLot)
lots = maxLot;
return lots;
}
int CountOurPositions(const string sym)
{
int total = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0 || !PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != sym)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
continue;
total++;
}
return total;
}
bool SpreadOk(const string sym)
{
long spreadPts = SymbolInfoInteger(sym, SYMBOL_SPREAD);
return ((double)spreadPts <= (double)InpMaxSpreadPoints);
}
void ComputeStopsBuy(const string sym, const double entry, double &sl, double &tp)
{
double ptsSl = InpMinStopPoints;
double ptsTp = InpMinStopPoints * 2.0;
if(InpUseAtrStops && g_atrSig != INVALID_HANDLE)
{
double atr = Buf1(g_atrSig, 1);
if(atr > 0.0)
{
double atrPts = atr / SymbolInfoDouble(sym, SYMBOL_POINT);
ptsSl = MathMax(atrPts * InpSlAtrMult, InpMinStopPoints);
ptsTp = MathMax(atrPts * InpTpAtrMult, InpMinStopPoints);
}
}
double p = SymbolInfoDouble(sym, SYMBOL_POINT);
sl = entry - ptsSl * p;
tp = entry + ptsTp * p;
long stopsLevel = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
double minDist = (double)stopsLevel * p;
if(minDist > 0.0)
{
if(entry - sl < minDist)
sl = entry - minDist;
if(tp - entry < minDist)
tp = entry + minDist;
}
int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
sl = NormalizeDouble(sl, dg);
tp = NormalizeDouble(tp, dg);
}
int OnInit()
{
string sym = WorkSymbol();
if(!SymbolSelect(sym, true))
{
Print("TFXNZDUSD: symbol not available: ", sym);
return INIT_FAILED;
}
g_effBiasTF = NearestStandardTf(InpBiasTF);
g_effHigherBiasTF = NearestStandardTf(InpHigherBiasTF);
g_effSignalTF = NearestStandardTf(InpSignalTF);
if(g_effBiasTF != InpBiasTF || g_effHigherBiasTF != InpHigherBiasTF || g_effSignalTF != InpSignalTF)
Print("TFXNZDUSD: resolved TFs — bias ", EnumToString(g_effBiasTF), " (in ", (long)InpBiasTF, ")",
" W1 ", EnumToString(g_effHigherBiasTF), " (in ", (long)InpHigherBiasTF, ")",
" signal ", EnumToString(g_effSignalTF), " (in ", (long)InpSignalTF, ")");
if(InpBiasEmaPeriod < 1 || InpFastEma < 1 || InpSlowEma < 1 || InpAtrPeriod < 1)
{
Print("TFXNZDUSD: EMA/ATR period must be >= 1");
return INIT_PARAMETERS_INCORRECT;
}
g_trade.SetExpertMagicNumber(InpMagic);
g_trade.SetDeviationInPoints(InpSlippagePoints);
g_trade.SetTypeFillingBySymbol(sym);
g_emaBiasD1 = iMA(sym, g_effBiasTF, InpBiasEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
g_emaBiasW1 = iMA(sym, g_effHigherBiasTF, InpBiasEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
g_emaFastSig = iMA(sym, g_effSignalTF, InpFastEma, 0, MODE_EMA, PRICE_CLOSE);
g_emaSlowSig = iMA(sym, g_effSignalTF, InpSlowEma, 0, MODE_EMA, PRICE_CLOSE);
g_atrSig = iATR(sym, g_effSignalTF, InpAtrPeriod);
if(g_emaBiasD1 == INVALID_HANDLE || g_emaFastSig == INVALID_HANDLE || g_emaSlowSig == INVALID_HANDLE ||
g_atrSig == INVALID_HANDLE)
{
Print("TFXNZDUSD: indicator init failed — check InpBiasTF/InpHigherBiasTF/InpSignalTF & symbol history");
return INIT_FAILED;
}
if(InpUseWeeklyBias && g_emaBiasW1 == INVALID_HANDLE)
{
Print("TFXNZDUSD: W1 bias handle failed");
return INIT_FAILED;
}
Print("TFXNZDUSD: ", sym, " eff TFs: bias=", EnumToString(g_effBiasTF), " higher=", EnumToString(g_effHigherBiasTF),
" signal=", EnumToString(g_effSignalTF));
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_emaBiasD1 != INVALID_HANDLE) IndicatorRelease(g_emaBiasD1);
if(g_emaBiasW1 != INVALID_HANDLE) IndicatorRelease(g_emaBiasW1);
if(g_emaFastSig != INVALID_HANDLE) IndicatorRelease(g_emaFastSig);
if(g_emaSlowSig != INVALID_HANDLE) IndicatorRelease(g_emaSlowSig);
if(g_atrSig != INVALID_HANDLE) IndicatorRelease(g_atrSig);
}
void OnTick()
{
string sym = WorkSymbol();
datetime barOpen = iTime(sym, g_effSignalTF, 0);
if(barOpen == 0)
return;
if(barOpen == g_lastSignalBar)
return;
datetime prevBar = iTime(sym, g_effSignalTF, 1);
if(prevBar == 0)
return;
g_lastSignalBar = barOpen;
if(!SessionOk())
return;
if(!SpreadOk(sym))
return;
if(CountOurPositions(sym) >= InpMaxPositions)
return;
if(!HtfLongBias(sym))
return;
if(!BullishCrossOnLastClosedBar(sym))
return;
MqlTick tick;
if(!SymbolInfoTick(sym, tick))
return;
double lots = NormalizeVolumeLots(sym, InpLots);
double sl = 0.0, tp = 0.0;
ComputeStopsBuy(sym, tick.ask, sl, tp);
if(!g_trade.Buy(lots, sym, tick.ask, sl, tp, "TFX NZDUSD shift"))
Print("TFXNZDUSD Buy failed ret=", g_trade.ResultRetcode(), " ", g_trade.ResultRetcodeDescription());
}
//+------------------------------------------------------------------+
@@ -0,0 +1,42 @@
; saved for genetic optimization — TFXNZDUSD.mq5 (Strategy Tester → Inputs → Load)
; Repo format: Parameter=Value||Step||Min||Max||Optimize(Y/N)
; ENUM_TIMEFRAMES: H1=16385, H4=16388, D1=16408, W1=32769
; Do NOT optimize InpSignalTF as MinMax integers — MT5 genetic samples invalid values (e.g. 16386)
; between real enums and OnInit fails. Compare H1 vs H4 in separate runs, or rely on EA TF resolution.
; === Symbol ===
; String inputs: use bare name OR Value||Value||Value||Value||N — never use 0 as middle field (MT5 may feed the whole line into the string).
InpSymbol=NZDUSD
; === Timeframes (thesis) ===
InpBiasTF=16408||0||16408||16408||N
InpHigherBiasTF=32769||0||32769||32769||N
InpSignalTF=16388||0||16388||16388||N
; === HTF bias (long-only, reactive) ===
InpUseWeeklyBias=true||false||0||true||N
InpBiasEmaPeriod=50||2||34||120||Y
InpAllowCounterBias=false||false||0||true||N
; === Intraday shift (bearish → bullish) ===
InpFastEma=8||1||5||34||Y
InpSlowEma=21||2||15||55||Y
InpMinBearishBars=3||1||2||10||Y
InpRequireBullBody=true||false||0||true||N
; === Risk ===
InpLots=0.1||0.01||0.1||0.1||N
InpMagic=926001||0||926001||926001||N
InpSlippagePoints=20||0||20||20||N
InpMaxSpreadPoints=40||5||20||60||Y
InpUseAtrStops=true||false||0||true||N
InpAtrPeriod=14||1||7||28||Y
InpSlAtrMult=1.5||0.1||1.0||3.5||Y
InpTpAtrMult=2.5||0.2||1.5||5.0||Y
InpMinStopPoints=50.0||5.0||30.0||120.0||Y
InpMaxPositions=1||0||1||1||N
; === Session (optional) ===
InpUseSessionFilter=false||false||0||true||N
InpSessionStartHour=7||0||7||7||N
InpSessionEndHour=20||0||20||20||N
+367
View File
@@ -0,0 +1,367 @@
//+------------------------------------------------------------------+
//| TFXXAUUSDScalper.mq5 |
//| Gold (XAUUSD) Donchian breakout scalper — momentum / range |
//| breakout style suited to impulse-or-consolidate dynamics. |
//+------------------------------------------------------------------+
#property copyright "Lab"
#property link ""
#property version "1.00"
#property description "Donchian channel breakout on XAUUSD; optional consolidation filter; percent-risk or fixed lots."
#include <Trade/Trade.mqh>
input group "=== Instrument ==="
input string InpSymbol = "XAUUSD";
input group "=== Session ==="
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_M5;
input bool InpUseSessionFilter = false;
input int InpSessionStartHour = 7;
input int InpSessionEndHour = 22;
input group "=== Donchian breakout ==="
input int InpDonchianPeriod = 20; // Lookback for channel high/low (past bars exclude signal bar)
input bool InpRequireFreshBreak = true; // Close[2] inside prior upper/lower band (no churn)
input bool InpTradeLong = true;
input bool InpTradeShort = true;
input group "=== Consolidation filter (horizontal → breakout) ==="
input bool InpUseNarrowChannelFilter = false;
input double InpMaxChannelWidthAtrMult = 3.0; // Upper-Lower <= this * ATR(shift 2)
input group "=== Stops & targets (Nick-style RR) ==="
input int InpSlBufferPoints = 30; // Beyond opposite Donchian / structural low-high
input double InpTpRiskReward = 2.0; // TP distance = RR * risk distance
input bool InpUseMidStopFallback = false; // Optional tighter SL at channel mid (more aggressive)
input group "=== Risk ==="
input bool InpUsePercentRisk = true;
input double InpRiskPercent = 1.0; // % balance per trade (video example)
input double InpFixedLots = 0.10;
input int InpMagic = 928001;
input int InpSlippagePoints = 50;
input int InpMaxSpreadPoints = 60;
input int InpMaxPositions = 1;
input group "=== Indicators ==="
input int InpAtrPeriod = 14;
CTrade g_trade;
int g_atr = INVALID_HANDLE;
datetime g_lastBar = 0;
string WorkSymbol()
{
string s = InpSymbol;
StringTrimLeft(s);
StringTrimRight(s);
const int bar = StringFind(s, "|");
if(bar >= 0)
s = StringSubstr(s, 0, bar);
StringTrimRight(s);
return (StringLen(s) > 0 ? s : _Symbol);
}
bool SessionOk()
{
if(!InpUseSessionFilter)
return true;
MqlDateTime dt;
TimeToStruct(TimeCurrent(), dt);
const int h = dt.hour;
if(InpSessionStartHour <= InpSessionEndHour)
return (h >= InpSessionStartHour && h < InpSessionEndHour);
return (h >= InpSessionStartHour || h < InpSessionEndHour);
}
double DonchianUpper(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shiftAnchor)
{
if(period < 1)
return 0.0;
double mx = -DBL_MAX;
for(int i = shiftAnchor + 1; i <= shiftAnchor + period; i++)
{
const double hi = iHigh(sym, tf, i);
if(hi > mx)
mx = hi;
}
return mx;
}
double DonchianLower(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shiftAnchor)
{
if(period < 1)
return 0.0;
double mn = DBL_MAX;
for(int i = shiftAnchor + 1; i <= shiftAnchor + period; i++)
{
const double lo = iLow(sym, tf, i);
if(lo < mn)
mn = lo;
}
return mn;
}
double AtrAt(const int shift)
{
double b[];
ArraySetAsSeries(b, true);
if(g_atr == INVALID_HANDLE || CopyBuffer(g_atr, 0, shift, 1, b) != 1)
return 0.0;
return b[0];
}
double NormalizeLots(const string sym, double lots)
{
double mn = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double mx = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double st = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(st > 0.0)
lots = MathFloor(lots / st) * st;
if(lots < mn)
lots = mn;
if(lots > mx)
lots = mx;
return lots;
}
bool MoneyPerLotAtSl(const string sym, const ENUM_ORDER_TYPE type, const double openPrice, const double slPrice, double &lossPerLot)
{
lossPerLot = 0.0;
double p = 0.0;
if(!OrderCalcProfit(type, sym, 1.0, openPrice, slPrice, p))
return false;
lossPerLot = MathAbs(p);
return (lossPerLot > 0.0);
}
double LotsFromPercentRisk(const string sym, const ENUM_ORDER_TYPE type, const double openPrice, const double slPrice)
{
double perLotLoss = 0.0;
if(!MoneyPerLotAtSl(sym, type, openPrice, slPrice, perLotLoss))
return InpFixedLots;
const double balance = AccountInfoDouble(ACCOUNT_BALANCE);
const double riskMoney = balance * (InpRiskPercent / 100.0);
if(riskMoney <= 0.0 || perLotLoss <= 0.0)
return NormalizeLots(sym, InpFixedLots);
double lots = riskMoney / perLotLoss;
return NormalizeLots(sym, lots);
}
bool SpreadOk(const string sym)
{
const long sp = SymbolInfoInteger(sym, SYMBOL_SPREAD);
return ((double)sp <= (double)InpMaxSpreadPoints);
}
int CountMagicPositions(const string sym)
{
int n = 0;
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((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
continue;
n++;
}
return n;
}
void BuildStopsBuy(const string sym, const double entry, const double upperD1, const double lowerD1,
double &sl, double &tp)
{
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
const double buf = (double)InpSlBufferPoints * pt;
double riskDist = entry - (lowerD1 - buf);
sl = lowerD1 - buf;
if(InpUseMidStopFallback)
{
const double mid = (upperD1 + lowerD1) * 0.5;
const double distMid = entry - mid;
if(distMid > 0 && distMid < riskDist)
{
sl = mid - buf;
riskDist = entry - sl;
}
}
const long lvl = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
const double minD = (double)lvl * pt;
if(minD > 0.0 && entry - sl < minD)
sl = entry - minD;
riskDist = entry - sl;
tp = entry + riskDist * InpTpRiskReward;
if(minD > 0.0 && tp - entry < minD)
tp = entry + minD;
const int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
sl = NormalizeDouble(sl, dg);
tp = NormalizeDouble(tp, dg);
}
void BuildStopsSell(const string sym, const double entry, const double upperD1, const double lowerD1,
double &sl, double &tp)
{
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
const double buf = (double)InpSlBufferPoints * pt;
double riskDist = (upperD1 + buf) - entry;
sl = upperD1 + buf;
if(InpUseMidStopFallback)
{
const double mid = (upperD1 + lowerD1) * 0.5;
const double distMid = mid - entry;
if(distMid > 0 && distMid < riskDist)
{
sl = mid + buf;
riskDist = sl - entry;
}
}
const long lvl = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
const double minD = (double)lvl * pt;
if(minD > 0.0 && sl - entry < minD)
sl = entry + minD;
riskDist = sl - entry;
tp = entry - riskDist * InpTpRiskReward;
if(minD > 0.0 && entry - tp < minD)
tp = entry - minD;
const int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
sl = NormalizeDouble(sl, dg);
tp = NormalizeDouble(tp, dg);
}
bool NarrowChannelOk(const string sym, const ENUM_TIMEFRAMES tf, const int period)
{
if(!InpUseNarrowChannelFilter)
return true;
const double up = DonchianUpper(sym, tf, period, 2);
const double lo = DonchianLower(sym, tf, period, 2);
const double atr = AtrAt(2);
if(up <= 0 || lo <= 0 || atr <= 0)
return false;
const double width = up - lo;
return (width <= atr * InpMaxChannelWidthAtrMult);
}
int OnInit()
{
const string sym = WorkSymbol();
if(!SymbolSelect(sym, true))
{
Print("TFXXAUUSDScalper: symbol not available: ", sym);
return INIT_FAILED;
}
if(InpDonchianPeriod < 2)
{
Print("TFXXAUUSDScalper: InpDonchianPeriod must be >= 2");
return INIT_PARAMETERS_INCORRECT;
}
g_trade.SetExpertMagicNumber(InpMagic);
g_trade.SetDeviationInPoints(InpSlippagePoints);
g_trade.SetTypeFillingBySymbol(sym);
g_atr = iATR(sym, InpSignalTF, InpAtrPeriod);
if(g_atr == INVALID_HANDLE)
{
Print("TFXXAUUSDScalper: ATR init failed");
return INIT_FAILED;
}
Print("TFXXAUUSDScalper: ", sym, " ", EnumToString(InpSignalTF),
" Donchian=", InpDonchianPeriod, " RR=", InpTpRiskReward,
" risk%=", (InpUsePercentRisk ? DoubleToString(InpRiskPercent, 2) : "off"));
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(g_atr != INVALID_HANDLE)
IndicatorRelease(g_atr);
g_atr = INVALID_HANDLE;
}
void OnTick()
{
const string sym = WorkSymbol();
const datetime t0 = iTime(sym, InpSignalTF, 0);
if(t0 == 0 || t0 == g_lastBar)
return;
g_lastBar = t0;
if(!SessionOk() || !SpreadOk(sym))
return;
if(CountMagicPositions(sym) >= InpMaxPositions)
return;
const int p = InpDonchianPeriod;
const double c1 = iClose(sym, InpSignalTF, 1);
const double c2 = iClose(sym, InpSignalTF, 2);
if(c1 <= 0.0 || c2 <= 0.0)
return;
const double up1 = DonchianUpper(sym, InpSignalTF, p, 1);
const double lo1 = DonchianLower(sym, InpSignalTF, p, 1);
const double up2 = DonchianUpper(sym, InpSignalTF, p, 2);
const double lo2 = DonchianLower(sym, InpSignalTF, p, 2);
if(up1 <= 0 || lo1 <= 0 || up2 <= 0 || lo2 <= 0)
return;
if(!NarrowChannelOk(sym, InpSignalTF, p))
return;
bool longSig = InpTradeLong && (c1 > up1);
bool shortSig = InpTradeShort && (c1 < lo1);
if(InpRequireFreshBreak)
{
longSig = longSig && (c2 <= up2);
shortSig = shortSig && (c2 >= lo2);
}
if(!longSig && !shortSig)
return;
MqlTick tick;
if(!SymbolInfoTick(sym, tick))
return;
if(longSig && !shortSig)
{
double sl = 0.0, tp = 0.0;
BuildStopsBuy(sym, tick.ask, up1, lo1, sl, tp);
const double lots = InpUsePercentRisk ? LotsFromPercentRisk(sym, ORDER_TYPE_BUY, tick.ask, sl) : NormalizeLots(sym, InpFixedLots);
if(!g_trade.Buy(lots, sym, tick.ask, sl, tp, "TFX Gold Donchian↑"))
Print("Buy failed ", g_trade.ResultRetcode(), " ", g_trade.ResultRetcodeDescription());
return;
}
if(shortSig && !longSig)
{
double sl = 0.0, tp = 0.0;
BuildStopsSell(sym, tick.bid, up1, lo1, sl, tp);
const double lots = InpUsePercentRisk ? LotsFromPercentRisk(sym, ORDER_TYPE_SELL, tick.bid, sl) : NormalizeLots(sym, InpFixedLots);
if(!g_trade.Sell(lots, sym, tick.bid, sl, tp, "TFX Gold Donchian↓"))
Print("Sell failed ", g_trade.ResultRetcode(), " ", g_trade.ResultRetcodeDescription());
return;
}
// Bothtrue — rare; skip to avoid ambiguous execution
}
//+------------------------------------------------------------------+
@@ -0,0 +1,44 @@
; TFXXAUUSDScalper.mq5 — Strategy Tester → Inputs → Load (Genetic optimization)
; Format: Parameter=Value||Step||Min||Max||Optimize(Y/N)
;
; ENUM_TIMEFRAMES (MT5): M1=1 M5=5 M15=15 M30=30 H1=16385 H4=16388 D1=16408
; Keep InpSignalTF fixed (single integer). Do not use MinMax sweeps on enums — genetic
; often tries invalid values between named periods and OnInit fails.
;
; Baseline aligned with Desktop 123.set (2026.05.08); magic corrected to 928001 (EA default).
; === Instrument ===
InpSymbol=XAUUSD
; === Session ===
InpSignalTF=5||0||5||5||N
InpUseSessionFilter=false||false||0||true||N
InpSessionStartHour=7||0||7||7||N
InpSessionEndHour=22||0||22||22||N
; === Donchian breakout ===
InpDonchianPeriod=20||2||10||80||Y
InpRequireFreshBreak=true||false||0||true||N
InpTradeLong=true||false||0||true||N
InpTradeShort=true||false||0||true||N
; === Consolidation filter (horizontal → breakout) ===
InpUseNarrowChannelFilter=false||false||0||true||N
InpMaxChannelWidthAtrMult=3.0||0.5||1.5||6.0||Y
; === Stops & targets (Nick-style RR) ===
InpSlBufferPoints=30||5||10||120||Y
InpTpRiskReward=2.0||0.25||1.25||4.0||Y
InpUseMidStopFallback=false||false||0||true||N
; === Risk ===
InpUsePercentRisk=true||false||0||true||N
InpRiskPercent=1.0||0.15||0.25||2.5||Y
InpFixedLots=0.1||0.01||0.1||0.1||N
InpMagic=928001||0||928001||928001||N
InpSlippagePoints=50||0||50||50||N
InpMaxSpreadPoints=60||5||20||100||Y
InpMaxPositions=1||0||1||1||N
; === Indicators ===
InpAtrPeriod=14||1||7||28||Y
@@ -0,0 +1,458 @@
//+------------------------------------------------------------------+
//| ScoringTrade.mq5 |
//| Generated by ChatGPT |
//| |
//+------------------------------------------------------------------+
#property strict
#include <Trade\Trade.mqh>
// Input parameters
input int MagicNumber = 42;
input int scoreThreshold = 5200; // Score threshold for trade entry
input int slopeThreshold = 93; // EMA slope threshold
input double maxScore = 7900; // Max score value for clamping
input int cooldownMinutes = 18; // Cooldown period in minutes (37 minutes)
input int tradeCooldownMinutes = 24; // Trade debounce cooldown period (5 minutes)
input ENUM_TIMEFRAMES emaTimeFrame = PERIOD_H1; // EMA Timeframe
input double delayClampAbsolute = 1690;
input int emaPeriod = 64; // EMA period
input double crossOverStep = 950;
input double slopeThresholdStep = 635;
input double emaDistanceStep = 150;
input double emaDecayStep = 0;
input double decayMultiplier = 0.08; // Decay multiplier
input double distanceThreshold = 28.5; // Set your distance threshold (adjust as necessary)
input double atrMultiplier = 7.6; // Multiplier for dynamic SL and TP calculation
input double TrailingStop = 5;
input bool UseTrailingStop = true;
input int maxCrossoverTrades = 4; // Maximum number of trades per crossover
input double max_drawdown = 0.1; // Maximum drawdown percentage
input bool resetCrossoverTradeOnDistance = false;
input int resetCrossoverNumber = 0;
input double minimumLotSize = 0.01;
input int maxTimeInPosition = 9;
input int tradeLengthThreshold = 98;
input int reverseTP = 32;
input int reverseLotSizeMultiplier = 15;
input int secondaryPositionHoldTime = 32;
// Global variables
int emaHandle; // EMA handle
double prevScore = 0; // Previous score
double currentScore = 0; // Current score
double emaPrevValue = 0; // Previous EMA value
double emaCurrentValue = 0; // Current EMA value
double emaSlope = 0; // EMA slope value
CTrade trade; // Trading object
datetime lastCrossoverTime = 0; // Time of last crossover
datetime lastTradeTime = 0; // Time of last trade
int crossoverTradeCount = 0; // Count of trades after each crossover
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit() {
// Create EMA handle (e.g., 14-period EMA on the closing price)
emaHandle = iMA(Symbol(), emaTimeFrame, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if (emaHandle == INVALID_HANDLE) {
Print("Failed to create EMA handle");
return INIT_FAILED;
}
return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
if (emaHandle != INVALID_HANDLE) {
IndicatorRelease(emaHandle);
emaHandle = INVALID_HANDLE;
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick() {
// Buffer to hold the EMA values
double emaBuffer[];
// Get dynamic lot size based on current balance and max drawdown
double lotSize = CalculateLotSize();
if(lotSize < minimumLotSize) {
lotSize = minimumLotSize;
}
// Get the current Ask and Bid prices
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
// Copy the last 2 EMA values (current and previous)
int copied = CopyBuffer(emaHandle, 0, 0, 2, emaBuffer);
if (copied < 2) {
Print("Failed to copy EMA values. Error code: ", GetLastError());
return;
}
// Get the current and previous EMA values
emaPrevValue = emaBuffer[1]; // Previous EMA value (index 1)
emaCurrentValue = emaBuffer[0]; // Current EMA value (index 0)
// Calculate the EMA slope (change in EMA values)
emaSlope = - (emaCurrentValue - emaPrevValue) * 100;
// Check for price action crossover with EMA
double closePrev = iClose(Symbol(), Period(), 1); // Close of previous bar
double closeCurr = iClose(Symbol(), Period(), 0); // Close of current bar
// Check if enough time has passed for the cooldown (cooldownMinutes)
if (TimeCurrent() - lastCrossoverTime >= cooldownMinutes * 60) {
if (closePrev < emaPrevValue && closeCurr > emaCurrentValue) { // Bullish crossover
Print("Bullish crossover");
currentScore += crossOverStep;
crossoverTradeCount = 0; // Reset trade count after new crossover
lastCrossoverTime = TimeCurrent(); // Update the last crossover time
}
else if (closePrev > emaPrevValue && closeCurr < emaCurrentValue) { // Bearish crossover
Print("Bearish crossover");
currentScore -= crossOverStep;
crossoverTradeCount = 0; // Reset trade count after new crossover
lastCrossoverTime = TimeCurrent(); // Update the last crossover time
}
}
// Check EMA slope
if (emaSlope > slopeThreshold) { // Positive slope
currentScore += slopeThresholdStep;
}
else if (emaSlope < -slopeThreshold) { // Negative slope
currentScore -= slopeThresholdStep;
}
else {
if (MathAbs(currentScore) > delayClampAbsolute) {
currentScore *= decayMultiplier;
}
}
if(UseTrailingStop) {
ApplyTrailingStop();
}
// Calculate distance to EMA and adjust score
double priceToEmaDistance = closeCurr - emaCurrentValue; // Distance between the current price and the EMA
if (MathAbs(priceToEmaDistance) > distanceThreshold) {
if (priceToEmaDistance > 0) { // Bullish (price above EMA)
currentScore += emaDistanceStep;
Print("Bullish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue);
}
else if (priceToEmaDistance < 0) { // Bearish (price below EMA)
currentScore -= emaDistanceStep;
Print("Bearish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue);
}
}
else {
if (currentScore > 0) {
currentScore -= emaDecayStep;
}
else {
currentScore += emaDecayStep;
}
}
// Close all positions if score crosses zero
if ((prevScore > 0 && currentScore <= 0) || (prevScore < 0 && currentScore >= 0)) {
Close_Position_MN(MagicNumber);
}
// Update the previous score
prevScore = currentScore;
if (crossoverTradeCount > maxCrossoverTrades) {
return;
}
// Debounce check: Ensure enough time has passed since the last trade
if (TimeCurrent() - lastTradeTime >= tradeCooldownMinutes * 60) {
// Calculate ATR (Average True Range) for stop loss calculation
double atrArray[];
int atrPeriod = 14; // ATR period (can be adjusted)
int copied = CopyBuffer(iATR(Symbol(), Period(), atrPeriod), 0, 0, 1, atrArray);
if (copied < 1) {
Print("Failed to get ATR values. Error code: ", GetLastError());
return;
}
// Get the current price (using Bid price)
double currentPrice = Bid;
// Get ATR value
double atrValue = atrArray[0]; // Latest ATR value
// Get the minimum stop level and freeze level for the symbol
long stopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
long freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL);
// Calculate the minimum stop loss in price units (converted from pips)
double minStopLoss = stopLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
double minFreezeLevel = freezeLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
// Dynamic Stop Loss and Take Profit calculation based on ATR
double dynamicSL = atrValue * atrMultiplier;
double dynamicTP = atrValue * atrMultiplier;
// Adjust SL and TP if they are smaller than the minimum stop level
dynamicSL = MathMax(dynamicSL, minStopLoss);
dynamicTP = MathMax(dynamicTP, dynamicSL); // Ensure TP is at least the same as SL
// Trade logic based on the score
if (currentScore > scoreThreshold) { // Buy signal
if ((!PositionSelect(Symbol()) || PositionGetInteger(POSITION_MAGIC) != MagicNumber)
&& crossoverTradeCount < maxCrossoverTrades) {
Print("maxCrossover");
Print(crossoverTradeCount);
// Open buy position with dynamic SL and TP
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Buy(lotSize, Symbol(), currentPrice, Bid - dynamicSL, 0)) {
Print("Buy order executed with score: ", currentScore);
crossoverTradeCount++; // Increment trade count
lastTradeTime = TimeCurrent(); // Update the last trade time
}
}
}
else if (currentScore < -scoreThreshold) { // Sell signal
if ((!PositionSelect(Symbol()) || PositionGetInteger(POSITION_MAGIC) != MagicNumber)
&& crossoverTradeCount < maxCrossoverTrades) {
Print("maxCrossover");
Print(crossoverTradeCount);
// Open sell position with dynamic SL and TP
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Sell(lotSize, Symbol(), currentPrice, Ask + dynamicSL, 0)) {
Print("Sell order executed with score: ", currentScore);
crossoverTradeCount++; // Increment trade count
lastTradeTime = TimeCurrent(); // Update the last trade time
}
}
}
} else {
Print("Trade skipped due to debounce: ", currentScore);
}
// Check existing positions for profit and place reverse trade if needed
CheckPositions();
}
//+------------------------------------------------------------------+
//| Check existing positions for profit and place reverse trade if needed |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Check existing positions for duration and place reverse trade if needed |
//+------------------------------------------------------------------+
void CheckPositions() {
// Check if there are any open positions
if (PositionsTotal() > 0) {
// Check if there are exactly 2 open positions
if (PositionsTotal() == 2) {
for (int i = 0; i < PositionsTotal(); i++) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
long tradeLength = (long)(TimeCurrent() - openTime);
// Check if the trade has been open for more than the secondaryPositionHoldTime
if (tradeLength > secondaryPositionHoldTime * 60) { // Convert threshold to seconds
// Close all positions
CloseAllPositions();
Print("All positions closed due to exceeding secondaryPositionHoldTime");
return; // Exit the function after closing all positions
}
}
}
} else if (PositionsTotal() < 2) {
for (int i = 0; i < PositionsTotal(); i++) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
double profit = PositionGetDouble(POSITION_PROFIT);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
long tradeLength = (long)(TimeCurrent() - openTime);
// Check if the trade has been open for more than the tradeLengthThreshold
if (tradeLength > tradeLengthThreshold * 60) { // Convert threshold to seconds
double lotSize = PositionGetDouble(POSITION_VOLUME);
double newLotSize = lotSize * reverseLotSizeMultiplier; // 10 times the original lot size
crossoverTradeCount = maxCrossoverTrades + 1;
// Place a reverse trade
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Sell(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_BID))) {
Print("Reversal sell order executed with increased lot size");
} else {
Print("Failed to execute reversal sell order");
}
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
trade.SetExpertMagicNumber(MagicNumber);
if (trade.Buy(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_ASK))) {
Print("Reversal buy order executed with increased lot size");
} else {
Print("Failed to execute reversal buy order");
}
}
}
// Close the trade if profit meets the take profit level
if (profit >= reverseTP) {
Close_Position_MN(MagicNumber);
CloseAllPositions();
}
// Check if there is only one position and its volume is lotSize * reverseLotSizeMultiplier
if (PositionsTotal() == 1 && PositionGetDouble(POSITION_VOLUME) == minimumLotSize * reverseLotSizeMultiplier) {
trade.PositionClose(ticket);
Print("Single position with volume equal to lotSize * reverseLotSizeMultiplier closed");
}
// Get the current Ask and Bid prices
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
// Check if the double down trade is exited by stop loss
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetDouble(POSITION_SL) > 0 && Bid <= PositionGetDouble(POSITION_SL)) {
// Close the original trade
CloseOriginalTrade();
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetDouble(POSITION_SL) > 0 && Ask >= PositionGetDouble(POSITION_SL)) {
// Close the original trade
CloseOriginalTrade();
}
}
}
}
}
}
// Function to close the original trade
void CloseOriginalTrade() {
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
trade.PositionClose(ticket);
Print("Original buy position closed due to double down stop loss.");
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
trade.PositionClose(ticket);
Print("Original sell position closed due to double down stop loss.");
}
}
}
}
//+------------------------------------------------------------------+
//| Function to close all positions |
//+------------------------------------------------------------------+
void CloseAllPositions() {
// Loop through all positions and close them
for (int i = PositionsTotal() - 1; i >= 0; i--) {
ulong ticket = PositionGetTicket(i);
if (PositionSelectByTicket(ticket)) {
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
trade.PositionClose(ticket);
Print("Buy position closed at score crossover.");
}
else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
trade.PositionClose(ticket);
Print("Sell position closed at score crossover.");
}
}
}
}
void ApplyTrailingStop()
{
for(int i=PositionsTotal()-1; i>=0; i--)
{
string symbol = PositionGetSymbol(i);
ulong PositionTicket = PositionGetTicket(i);
long trade_type = PositionGetInteger(POSITION_TYPE);
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) {
continue;
}
double POINT = SymbolInfoDouble( symbol, SYMBOL_POINT );
int DIGIT = (int) SymbolInfoInteger( symbol, SYMBOL_DIGITS );
if(trade_type == 0)
{
double Bid = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_BID),DIGIT);
if(Bid-PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * TrailingStop,DIGIT))
{
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop,DIGIT))
{
trade.PositionModify(PositionTicket,NormalizeDouble(Bid - POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
}
}
}
if(trade_type == 1)
{
double Ask = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_ASK),DIGIT);
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble( POINT * TrailingStop,DIGIT))
{
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop,DIGIT)) || (PositionGetDouble(POSITION_SL)==0))
{
trade.PositionModify(PositionTicket,NormalizeDouble(Ask + POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
}
}
}
}
}
void Close_Position_MN(ulong magicNumber)
{
int total = PositionsTotal();
for(int i = total - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
// Use PositionSelect by symbol instead of ticket
string symbol = PositionGetSymbol(i);
if(PositionSelect(symbol))
{
if (PositionGetInteger(POSITION_MAGIC) == magicNumber && PositionGetInteger(POSITION_TICKET) == ticket)
{
if(symbol == _Symbol) // Verify the symbol
{
Print("MN ", magicNumber);
trade.PositionClose(ticket);
}
}
}
else
{
int errorCode = GetLastError();
Print("aaaa PositionSelect failed with error code: ", errorCode);
}
}
}
//+------------------------------------------------------------------+
//| Calculate the dynamic lot size based on max drawdown |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Get account balance
double allowedDrawdown = balance * max_drawdown; // Calculate allowed drawdown in account currency
double baseDrawdownPerLot = 150; // Assumed drawdown per 0.01 lots as per backtest
// Calculate lot size based on maximum drawdown
double lotSize = (allowedDrawdown / baseDrawdownPerLot) * 0.01;
return NormalizeDouble(lotSize, 2); // Normalize lot size to 2 decimal places
}
@@ -0,0 +1,55 @@
; READY_EMACrossOverXAUUSD.mq5 — Genetic optimization (sanitized ranges)
; Load: Strategy Tester → Inputs → Load
; Format: Name=Default||Min||Step||Max||Y/N
;
; Units (read before optimizing):
; slopeThreshold — |ΔEMA|×100 per chart bar; ~93 ≈ $0.93 EMA move (H1 EMA, checked each tick)
; distanceThreshold — |priceEMA| in price ($ for XAUUSD)
; cooldown* — minutes
; atrMultiplier — stop distance = ATR(14) × multiplier (price $)
; TrailingStop — trail in symbol POINTS (0.01 pt on XAU: 500≈$5). EA default 5≈$0.05 — set uses 500 for tests.
; reverseTP — close reversal basket when profit ≥ this (account currency)
; reverseLotSizeMultiplier — reversal volume = position volume × this (dangerous above ~5)
; score* — arbitrary units; keep threshold ~410× crossOverStep or ~815 ticks of slope step
;
; ENUM_TIMEFRAMES: H1=16385 — fixed; do not sweep enum range.
; === fixed ===
MagicNumber=42||42||1||42||N
minimumLotSize=0.01||0.01||0||0.01||N
emaTimeFrame=16385||16385||0||16385||N
UseTrailingStop=true||false||0||true||N
maxScore=7900||7900||0||7900||N
emaDecayStep=0||0||0||0||N
resetCrossoverTradeOnDistance=false||false||0||false||N
resetCrossoverNumber=0||0||0||0||N
maxTimeInPosition=9||9||0||9||N
max_drawdown=0.1||0.1||0||0.1||N
; === EMA / slope (price-scaled) ===
emaPeriod=64||40||4||88||Y
slopeThreshold=93||50||5||140||Y
distanceThreshold=28.5||12.0||2.0||45.0||Y
; === score increments (keep proportional to scoreThreshold) ===
scoreThreshold=5200||3500||250||7000||Y
crossOverStep=950||600||50||1400||Y
slopeThresholdStep=635||350||50||950||Y
emaDistanceStep=150||75||25||250||Y
delayClampAbsolute=1690||1000||100||2500||Y
decayMultiplier=0.08||0.03||0.01||0.15||Y
; === timing ===
cooldownMinutes=18||8||2||35||Y
tradeCooldownMinutes=24||12||3||48||Y
maxCrossoverTrades=4||2||1||6||Y
; === stops / trail ===
atrMultiplier=7.6||4.0||0.5||12.0||Y
TrailingStop=500||200||50||1000||Y
; === reversal / hold (minutes & account $) ===
tradeLengthThreshold=98||60||10||180||Y
secondaryPositionHoldTime=32||15||5||60||Y
reverseTP=32||15||5||80||Y
reverseLotSizeMultiplier=15||4||1||20||Y