Update
This commit is contained in:
@@ -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
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -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 (H4–M15) 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 Min–Max 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
|
||||
@@ -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 Min–Max 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 — |price−EMA| 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 ~4–10× crossOverStep or ~8–15 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
|
||||
Reference in New Issue
Block a user