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,347 @@
//+------------------------------------------------------------------+
//| RSIConsolidation.mq5 |
//| Mean-reversion RSI for ranging markets; trend filters block runs |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025"
#property link "https://www.mql5.com"
#property version "1.00"
#include <Trade\Trade.mqh>
//--- Symbol (empty = chart symbol)
input group "=== Symbol & session ==="
input string InpSymbol = "";
input group "=== Timeframe & bar logic ==="
input ENUM_TIMEFRAMES SignalTF = PERIOD_M15;
input bool EntryOnNewBarOnly = true;
//--- Core: no trend / consolidation regime
input group "=== Regime: consolidation (anti-trend) ==="
input int ADX_Period = 23;
input double ADX_Max = 29.0;
input bool UseATRRatioFilter = true;
input int ATR_Period = 8;
input int ATR_SMA_Period = 35;
input double ATR_Ratio_Max = 1.36;
input bool UseFlatEMAFilter = true;
input int EMA_Fast = 13;
input int EMA_Slow = 17;
input double EMA_Separation_MaxPct = 0.26;
//--- RSI entries (fade extremes toward mean)
input group "=== RSI entries ==="
input int RSI_Period = 8;
input ENUM_APPLIED_PRICE RSI_Price = PRICE_OPEN;
input double RSI_Oversold = 22.0;
input double RSI_Overbought = 63.0;
//--- Exits: mean target + hard ATR bracket
input group "=== Exits ==="
input bool UseRSI_MeanExit = true;
input double RSI_Exit_Long = 48.0;
input double RSI_Exit_Short = 52.0;
input double SL_ATR_Mult = 2.15;
input double TP_ATR_Mult = 2.40;
input int MaxBarsInTrade = 54;
input group "=== Risk & execution ==="
input double Lots = 0.10;
input ulong MagicNumber = 20250420;
input int Slippage = 10;
input int MaxSpreadPoints = 28;
CTrade trade;
string g_sym;
int h_rsi = INVALID_HANDLE;
int h_adx = INVALID_HANDLE;
int h_atr = INVALID_HANDLE;
int h_ema_fast = INVALID_HANDLE;
int h_ema_slow = INVALID_HANDLE;
datetime g_last_bar = 0;
bool PositionExistsByMagicSym(string sym, ulong magic)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic)
return true;
}
return false;
}
ulong GetPositionTicketByMagicSym(string sym, ulong magic)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0) continue;
if(PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic)
return t;
}
return 0;
}
bool SelectPositionTicketSymMagic(ulong ticket, string sym, ulong magic)
{
if(!PositionSelectByTicket(ticket)) return false;
return PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic;
}
double NormalizeVolume(string sym, double vol)
{
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)
vol = MathFloor(vol / step) * step;
if(vol < minLot) vol = minLot;
if(vol > maxLot) vol = maxLot;
return vol;
}
int CurrentSpreadPoints(string sym)
{
long spread = 0;
if(!SymbolInfoInteger(sym, SYMBOL_SPREAD, spread))
return 999999;
return (int)spread;
}
double MinStopsDistancePrice(string sym)
{
long lvl = 0;
if(!SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL, lvl))
return 0;
double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
if(pt <= 0)
return 0;
return (double)lvl * pt;
}
bool Copy1(int handle, double &v)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(handle, 0, 0, 1, b) < 1) return false;
v = b[0];
return true;
}
bool RSI_Buffers(double &cur, double &prev, double &twoAgo)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(h_rsi, 0, 0, 3, b) < 3) return false;
cur = b[0];
prev = b[1];
twoAgo = b[2];
return true;
}
bool Regime_IsConsolidation()
{
double adx = 0;
if(!Copy1(h_adx, adx))
return false;
if(adx >= ADX_Max)
return false;
if(UseATRRatioFilter)
{
double atrArr[], atrSma[];
ArraySetAsSeries(atrArr, true);
if(CopyBuffer(h_atr, 0, 0, ATR_SMA_Period + 1, atrArr) < ATR_SMA_Period + 1)
return false;
double sum = 0;
for(int i = 1; i <= ATR_SMA_Period; i++)
sum += atrArr[i];
double smaAtr = sum / (double)ATR_SMA_Period;
if(smaAtr <= 0.0)
return false;
double ratio = atrArr[0] / smaAtr;
if(ratio > ATR_Ratio_Max)
return false;
}
if(UseFlatEMAFilter)
{
double ef[], es[];
ArraySetAsSeries(ef, true);
ArraySetAsSeries(es, true);
if(CopyBuffer(h_ema_fast, 0, 0, 1, ef) < 1) return false;
if(CopyBuffer(h_ema_slow, 0, 0, 1, es) < 1) return false;
double c = SymbolInfoDouble(g_sym, SYMBOL_BID);
if(c <= 0) return false;
double sep = MathAbs(ef[0] - es[0]) / c * 100.0;
if(sep > EMA_Separation_MaxPct)
return false;
}
return true;
}
bool Entry_BuyCross(double twoAgo, double prev)
{
return (twoAgo <= RSI_Oversold && prev > RSI_Oversold);
}
bool Entry_SellCross(double twoAgo, double prev)
{
return (twoAgo >= RSI_Overbought && prev < RSI_Overbought);
}
void TryCloseByRSI(ENUM_POSITION_TYPE typ, double rsi)
{
ulong tk = GetPositionTicketByMagicSym(g_sym, MagicNumber);
if(tk == 0 || !SelectPositionTicketSymMagic(tk, g_sym, MagicNumber))
return;
if(!UseRSI_MeanExit)
return;
if(typ == POSITION_TYPE_BUY && rsi >= RSI_Exit_Long)
trade.PositionClose(tk);
else if(typ == POSITION_TYPE_SELL && rsi <= RSI_Exit_Short)
trade.PositionClose(tk);
}
void ManageOpenPosition(double rsi)
{
ulong tk = GetPositionTicketByMagicSym(g_sym, MagicNumber);
if(tk == 0 || !SelectPositionTicketSymMagic(tk, g_sym, MagicNumber))
return;
ENUM_POSITION_TYPE typ = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
int barsAgo = iBarShift(g_sym, SignalTF, openT, false);
if(barsAgo >= 0 && barsAgo >= MaxBarsInTrade)
{
trade.PositionClose(tk);
return;
}
TryCloseByRSI(typ, rsi);
}
int OnInit()
{
g_sym = InpSymbol;
StringTrimLeft(g_sym);
StringTrimRight(g_sym);
if(StringLen(g_sym) == 0)
g_sym = _Symbol;
if(!SymbolSelect(g_sym, true))
{
Print("RSIConsolidation: SymbolSelect failed: ", g_sym);
return INIT_FAILED;
}
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_RETURN);
h_rsi = iRSI(g_sym, SignalTF, RSI_Period, RSI_Price);
h_adx = iADX(g_sym, SignalTF, ADX_Period);
h_atr = iATR(g_sym, SignalTF, ATR_Period);
h_ema_fast = iMA(g_sym, SignalTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE);
h_ema_slow = iMA(g_sym, SignalTF, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE);
if(h_rsi == INVALID_HANDLE || h_adx == INVALID_HANDLE || h_atr == INVALID_HANDLE
|| h_ema_fast == INVALID_HANDLE || h_ema_slow == INVALID_HANDLE)
{
Print("RSIConsolidation: indicator init failed");
return INIT_FAILED;
}
Print("RSIConsolidation: symbol=", g_sym, " TF=", EnumToString(SignalTF));
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
if(h_rsi != INVALID_HANDLE) IndicatorRelease(h_rsi);
if(h_adx != INVALID_HANDLE) IndicatorRelease(h_adx);
if(h_atr != INVALID_HANDLE) IndicatorRelease(h_atr);
if(h_ema_fast != INVALID_HANDLE) IndicatorRelease(h_ema_fast);
if(h_ema_slow != INVALID_HANDLE) IndicatorRelease(h_ema_slow);
}
bool EnoughHistory()
{
int need = MathMax(RSI_Period + 3, MathMax(ADX_Period + 2, ATR_SMA_Period + 3));
if(Bars(g_sym, SignalTF) < need)
return false;
return true;
}
void OnTick()
{
if(!EnoughHistory())
return;
if(MaxSpreadPoints > 0 && CurrentSpreadPoints(g_sym) > MaxSpreadPoints)
return;
double rsi, rsiPrev, rsi2;
if(!RSI_Buffers(rsi, rsiPrev, rsi2))
return;
datetime barTime = iTime(g_sym, SignalTF, 0);
bool isNew = (barTime != g_last_bar);
if(PositionExistsByMagicSym(g_sym, MagicNumber))
{
ManageOpenPosition(rsi);
if(isNew)
g_last_bar = barTime;
return;
}
if(EntryOnNewBarOnly && !isNew)
return;
g_last_bar = barTime;
if(!Regime_IsConsolidation())
return;
double atrArr[];
ArraySetAsSeries(atrArr, true);
if(CopyBuffer(h_atr, 0, 0, 1, atrArr) < 1)
return;
double atr = atrArr[0];
int dig = (int)SymbolInfoInteger(g_sym, SYMBOL_DIGITS);
double slDist = atr * SL_ATR_Mult;
double tpDist = atr * TP_ATR_Mult;
double minD = MinStopsDistancePrice(g_sym);
if(slDist < minD)
slDist = minD;
if(tpDist < minD)
tpDist = minD;
double vol = NormalizeVolume(g_sym, Lots);
if(Entry_BuyCross(rsi2, rsiPrev))
{
double ask = SymbolInfoDouble(g_sym, SYMBOL_ASK);
double sl = ask - slDist;
double tp = ask + tpDist;
sl = NormalizeDouble(sl, dig);
tp = NormalizeDouble(tp, dig);
trade.Buy(vol, g_sym, ask, sl, tp, "RSIConsolidation BUY");
}
else if(Entry_SellCross(rsi2, rsiPrev))
{
double bid = SymbolInfoDouble(g_sym, SYMBOL_BID);
double sl = bid + slDist;
double tp = bid - tpDist;
sl = NormalizeDouble(sl, dig);
tp = NormalizeDouble(tp, dig);
trade.Sell(vol, g_sym, bid, sl, tp, "RSIConsolidation SELL");
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,37 @@
; RSIConsolidation.mq5 — optimization preset (Strategy Tester → Inputs → Load)
; Format: Name=Current||Start||Step||Stop||Y|N (Y = include in optimization)
;
; === Symbol & session ===
InpSymbol=
; === Timeframe & bar logic ===
; SignalTF: optimize per run (ENUM is non-sequential); M15=15, H1=16385, H4=16388
SignalTF=15||15||0||15||N
EntryOnNewBarOnly=true||false||0||true||N
; === Regime: consolidation (anti-trend) ===
ADX_Period=14||7||1||28||Y
ADX_Max=22.0||16.0||1.0||32.0||Y
UseATRRatioFilter=true||false||0||true||N
ATR_Period=14||7||1||21||Y
ATR_SMA_Period=50||20||5||100||Y
ATR_Ratio_Max=1.18||1.0||0.02||1.35||Y
UseFlatEMAFilter=true||false||0||true||N
EMA_Fast=8||5||1||13||Y
EMA_Slow=21||13||2||34||Y
EMA_Separation_MaxPct=0.22||0.08||0.02||0.45||Y
; === RSI entries ===
RSI_Period=14||7||1||21||Y
RSI_Price=1||1||1||7||Y
RSI_Oversold=32.0||22.0||1.0||42.0||Y
RSI_Overbought=68.0||58.0||1.0||78.0||Y
; === Exits ===
UseRSI_MeanExit=true||false||0||true||N
RSI_Exit_Long=52.0||48.0||1.0||62.0||Y
RSI_Exit_Short=48.0||38.0||1.0||52.0||Y
SL_ATR_Mult=1.35||0.9||0.05||2.2||Y
TP_ATR_Mult=1.85||1.0||0.05||3.0||Y
MaxBarsInTrade=36||12||2||80||Y
; === Risk & execution ===
Lots=0.1||0.1||0.01||1.0||N
MagicNumber=20250420||20250420||1||20250420||N
Slippage=10||10||1||100||N
MaxSpreadPoints=0||0||1||30||Y
@@ -0,0 +1,421 @@
//+------------------------------------------------------------------+
//| RSIScalping.mq5 |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.02"
#include <Trade\Trade.mqh>
#include "../_united/MagicNumberHelpers.mqh"
//--- Input parameters
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
input int RSI_Period = 14; // RSI Period
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
input double RSI_Overbought = 71; // RSI Overbought Level
input double RSI_Oversold = 57; // RSI Oversold Level
input bool UseEntrySlopeFilter = false; // require RSI momentum on entry bars
input double EntryMinSlopePerBar = 1.0; // minimum RSI delta per bar for entry
input double RSI_Target_Buy = 80; // RSI Target for Buy Exit
input double RSI_Target_Sell = 57; // RSI Target for Sell Exit
input int BarsToWait = 4; // Bars to wait when RSI goes against position
input double LotSize = 0.1; // Lot Size
input int MagicNumber = 129102315; // Magic Number
input int Slippage = 3; // Slippage in points
input group "=== Reversal escape (intrabar, multi-signal) ==="
input bool UseReversalEscape = true; // run while in position every tick
input int ReversalATRPeriod = 14; // ATR lookback on signal timeframe
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
input int ReversalSignsRequired = 2; // how many independent signs must align
input double ReversalRsiVelocity = 16.0; // RSI points drop (long) / rise (short) vs prior buffer
input double ReversalBodyAtrMult = 5.1; // last closed bar body >= this * ATR counts as one sign
//--- Global variables
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev, rsi_current, rsi_two_bars_ago;
bool position_open = false;
int position_ticket = 0;
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
datetime last_bar_time = 0;
bool rsi_against_position = false;
int bars_against_count = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize RSI indicator
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(rsi_handle == INVALID_HANDLE)
{
return(INIT_FAILED);
}
// Initialize trade object
trade.SetExpertMagicNumber(MagicNumber);
trade.SetDeviationInPoints(Slippage);
trade.SetTypeFilling(ORDER_FILLING_FOK);
// Allocate arrays
ArraySetAsSeries(rsi_buffer, true);
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
if(rsi_handle != INVALID_HANDLE)
IndicatorRelease(rsi_handle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
return;
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
const bool new_bar = (current_bar_time != last_bar_time);
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
if(!in_pos && !new_bar)
return;
if(!UpdateRSI())
return;
if(in_pos && UseReversalEscape)
TryReversalEscape();
if(!new_bar)
return;
last_bar_time = current_bar_time;
ResyncPositionFromMarket();
CheckExistingPosition();
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
CheckEntrySignals();
}
//+------------------------------------------------------------------+
//| Update RSI values |
//+------------------------------------------------------------------+
bool UpdateRSI()
{
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
{
return false;
}
rsi_current = rsi_buffer[0]; // Current bar
rsi_prev = rsi_buffer[1]; // Previous bar
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
return true;
}
//+------------------------------------------------------------------+
//| Wilder ATR in price units (signal timeframe) |
//+------------------------------------------------------------------+
double ATRPriceOnTF(const int period)
{
if(period < 1)
return 0.0;
MqlRates rates[];
const int need = period + 2;
if(CopyRates(_Symbol, TimeFrame, 0, need, rates) < need)
return 0.0;
ArraySetAsSeries(rates, true);
double sum = 0.0;
for(int i = 1; i <= period; i++)
{
const double hl = rates[i].high - rates[i].low;
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
sum += MathMax(hl, MathMax(hc, lc));
}
return sum / (double)period;
}
//+------------------------------------------------------------------+
//| Independent adverse signs (need ReversalSignsRequired to exit) |
//+------------------------------------------------------------------+
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr)
{
if(atr <= 0.0)
return 0;
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
int signs = 0;
if(ptype == POSITION_TYPE_BUY)
{
if(entry - bid >= ReversalAdverseAtrMult * atr)
signs++;
if(rsi_prev - rsi_current >= ReversalRsiVelocity)
signs++;
}
else if(ptype == POSITION_TYPE_SELL)
{
if(ask - entry >= ReversalAdverseAtrMult * atr)
signs++;
if(rsi_current - rsi_prev >= ReversalRsiVelocity)
signs++;
}
else
return 0;
MqlRates r[];
if(CopyRates(_Symbol, TimeFrame, 0, 4, r) >= 4)
{
ArraySetAsSeries(r, true);
const double body = MathAbs(r[1].close - r[1].open);
if(body >= ReversalBodyAtrMult * atr)
{
if(ptype == POSITION_TYPE_BUY && r[1].close < r[1].open)
signs++;
else if(ptype == POSITION_TYPE_SELL && r[1].close > r[1].open)
signs++;
}
if(ptype == POSITION_TYPE_BUY)
{
if(r[1].close < r[2].close && r[2].close < r[3].close)
signs++;
}
else
{
if(r[1].close > r[2].close && r[2].close > r[3].close)
signs++;
}
}
return signs;
}
//+------------------------------------------------------------------+
//| Cut losers fast on violent reversals (evaluated every tick) |
//+------------------------------------------------------------------+
void TryReversalEscape()
{
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
return;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double atr = ATRPriceOnTF(ReversalATRPeriod);
if(atr <= 0.0)
return;
const int n = CountReversalEscapeSigns(ptype, atr);
if(n < ReversalSignsRequired)
return;
ClosePosition();
Print("RSIScalpingXAUUSD: reversal escape signs=", n, " need=", ReversalSignsRequired,
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
}
void ResyncPositionFromMarket()
{
if(position_open)
return;
ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
if(t == 0 || !PositionSelectByTicket(t))
return;
position_ticket = (int)t;
position_open = true;
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
//+------------------------------------------------------------------+
//| Check existing position for exit conditions |
//+------------------------------------------------------------------+
void CheckExistingPosition()
{
if(!position_open)
{
return;
}
// Check if position still exists with correct magic number
if(!PositionSelectByTicketAndMagic(position_ticket, MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
return;
}
// Exit conditions based on RSI target
if(current_position_type == POSITION_TYPE_BUY)
{
// Check if RSI is against the position (below oversold)
if(rsi_current < RSI_Oversold)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit long position when RSI reaches buy target
if(rsi_current >= RSI_Target_Buy)
{
ClosePosition();
}
}
}
else if(current_position_type == POSITION_TYPE_SELL)
{
// Check if RSI is against the position (above overbought)
if(rsi_current > RSI_Overbought)
{
if(!rsi_against_position)
{
rsi_against_position = true;
bars_against_count = 1;
}
else
{
bars_against_count++;
}
// Close position if RSI has been against for Y bars
if(bars_against_count >= BarsToWait)
{
ClosePosition();
return;
}
}
else
{
// RSI is no longer against the position, reset counter
if(rsi_against_position)
{
rsi_against_position = false;
bars_against_count = 0;
}
// Exit short position when RSI reaches sell target
if(rsi_current <= RSI_Target_Sell)
{
ClosePosition();
}
}
}
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckEntrySignals()
{
const double upSlope1 = rsi_prev - rsi_two_bars_ago; // older->prev
const double upSlope2 = rsi_current - rsi_prev; // prev->current
const double dnSlope1 = rsi_two_bars_ago - rsi_prev; // older->prev
const double dnSlope2 = rsi_prev - rsi_current; // prev->current
const bool buySlopeOk = (!UseEntrySlopeFilter) || (upSlope1 >= EntryMinSlopePerBar && upSlope2 >= EntryMinSlopePerBar);
const bool sellSlopeOk = (!UseEntrySlopeFilter) || (dnSlope1 >= EntryMinSlopePerBar && dnSlope2 >= EntryMinSlopePerBar);
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold && buySlopeOk)
{
OpenBuyPosition();
}
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought && sellSlopeOk)
{
OpenSellPosition();
}
}
//+------------------------------------------------------------------+
//| Open buy position |
//+------------------------------------------------------------------+
void OpenBuyPosition()
{
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
{
position_ticket = trade.ResultOrder();
position_open = true;
current_position_type = POSITION_TYPE_BUY;
}
}
//+------------------------------------------------------------------+
//| Open sell position |
//+------------------------------------------------------------------+
void OpenSellPosition()
{
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
{
position_ticket = trade.ResultOrder();
position_open = true;
current_position_type = POSITION_TYPE_SELL;
}
}
//+------------------------------------------------------------------+
//| Close current position |
//+------------------------------------------------------------------+
void ClosePosition()
{
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
return;
}
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
{
position_open = false;
position_ticket = 0;
rsi_against_position = false;
bars_against_count = 0;
return;
}
Print("RSIScalpingXAUUSD: close failed (will retry on next bar). retcode=",
trade.ResultRetcode(), " lastError=", GetLastError());
}
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 KiB

@@ -0,0 +1,36 @@
; SuperEMA — defaults aligned with lab/EAs/SuperEMA.mq5 (v1.01)
; Load from Strategy Tester → Inputs → context menu → Load
;
; === Market ===
InpSymbol=
InpTimeframe=15||15||0||49153||N
InpLots=0.01||0.01||0.01||0.10||N
InpSlippagePoints=55||20||5||120||Y
InpMagic=940001||940001||1||9400010||N
; === EMA (trend & structure) ===
InpEmaFast=40||20||10||120||Y
InpEmaMid=180||60||15||200||Y
InpEmaSlow=125||100||25||400||Y
InpEmaTrendBars=3||1||1||3||Y
; === CCI ===
InpCciPeriod=17||7||1||28||Y
InpCciOverbought=80.0||80.0||10.0||140.0||Y
InpCciOversold=-140.0||-140.0||10.0||-80.0||Y
InpPullbackCciLookback=20||4||2||24||Y
; === MACD (histogram = main - signal) ===
InpMacdFast=14||8||2||20||Y
InpMacdSlow=38||20||2||40||Y
InpMacdSignal=9||5||1||15||Y
; === Strategy ===
InpEntryStyle=1||0||1||2||Y
InpOneTradeOnly=true||false||0||true||N
InpUseStructuralSL=false||false||0||true||Y
InpSlBufferPoints=110.0||20.0||10.0||200.0||Y
; === Exits (so trades do not run forever) ===
InpExitOnTrendFlip=false||false||0||true||Y
InpExitOnMacdFlip=false||false||0||true||Y
InpExitOnCciZeroCross=true||false||0||true||Y
InpMaxHoldingBars=168||48||24||480||Y
InpExitBelowMidEma=false||false||0||true||Y
; === Debug ===
InpDebugLogs=false||false||0||true||N
+448
View File
@@ -0,0 +1,448 @@
//+------------------------------------------------------------------+
//| SuperEMA.mq5 |
//| EMA + CCI + MACD histogram — trend filter, momentum confirmation |
//+------------------------------------------------------------------+
#property strict
#property version "1.01"
#include <Trade/Trade.mqh>
enum ENUM_ENTRY_STYLE
{
ENTRY_CCIZERO_MACD = 0, // EMA trend + CCI crosses zero + MACD histogram agrees
ENTRY_LAMBERT = 1, // EMA trend + CCI crosses ±100 + MACD histogram agrees
ENTRY_PULLBACK = 2 // Uptrend: pullback to fast EMA + CCI was oversold + CCI crosses up through 0 + MACD > 0 (mirror for sells)
};
input group "=== Market ==="
input string InpSymbol = "";
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15;
input double InpLots = 0.01;
input int InpSlippagePoints = 55;
input int InpMagic = 940001;
input group "=== EMA (trend & structure) ==="
input int InpEmaFast = 40;
input int InpEmaMid = 180;
input int InpEmaSlow = 125;
input int InpEmaTrendBars = 3; // closed bar shift for EMA reads
input group "=== CCI ==="
input int InpCciPeriod = 17;
input double InpCciOverbought = 80.0;
input double InpCciOversold = -140.0;
input int InpPullbackCciLookback = 20; // bars to check prior CCI oversold/overbought
input group "=== MACD (histogram = main - signal) ==="
input int InpMacdFast = 14;
input int InpMacdSlow = 38;
input int InpMacdSignal = 9;
input group "=== Strategy ==="
input ENUM_ENTRY_STYLE InpEntryStyle = ENTRY_LAMBERT;
input bool InpOneTradeOnly = true;
input bool InpUseStructuralSL = false;
input double InpSlBufferPoints = 110;
input group "=== Exits (so trades do not run forever) ==="
input bool InpExitOnTrendFlip = false; // close when price vs slow EMA flips against position
input bool InpExitOnMacdFlip = false; // close when MACD histogram flips against position
input bool InpExitOnCciZeroCross = true; // long: CCI crosses below 0; short: CCI crosses above 0
input int InpMaxHoldingBars = 168; // 0 = disabled (e.g. ~8 days M15)
input bool InpExitBelowMidEma = false; // long: close if close < mid EMA (invalidation)
input group "=== Debug ==="
input bool InpDebugLogs = false;
CTrade trade;
datetime g_lastBarTime = 0;
string WorkSymbol()
{
return (InpSymbol == "" || InpSymbol == NULL) ? _Symbol : InpSymbol;
}
void Log(const string s)
{
if(InpDebugLogs)
Print("[SuperEMA] ", s);
}
bool IsNewBar(const string sym, const ENUM_TIMEFRAMES tf)
{
datetime t = iTime(sym, tf, 0);
if(t <= 0 || t == g_lastBarTime)
return false;
g_lastBarTime = t;
return true;
}
double EmaAt(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shift)
{
int h = iMA(sym, tf, period, 0, MODE_EMA, PRICE_CLOSE);
if(h == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return b[0];
}
double CciAt(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shift)
{
int h = iCCI(sym, tf, period, PRICE_TYPICAL);
if(h == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return b[0];
}
bool MacdHistAt(const string sym, const ENUM_TIMEFRAMES tf, const int fast, const int slow, const int signal, const int shift, double &hist)
{
int h = iMACD(sym, tf, fast, slow, signal, PRICE_CLOSE);
if(h == INVALID_HANDLE)
return false;
double mainLine[1], sigLine[1];
if(CopyBuffer(h, 0, shift, 1, mainLine) <= 0 || CopyBuffer(h, 1, shift, 1, sigLine) <= 0)
{
IndicatorRelease(h);
return false;
}
IndicatorRelease(h);
hist = mainLine[0] - sigLine[0];
return true;
}
bool TrendUp(const string sym, const int sh)
{
double c = iClose(sym, InpTimeframe, sh);
double emaS = EmaAt(sym, InpTimeframe, InpEmaSlow, sh);
return (emaS > 0.0 && c > emaS);
}
bool TrendDown(const string sym, const int sh)
{
double c = iClose(sym, InpTimeframe, sh);
double emaS = EmaAt(sym, InpTimeframe, InpEmaSlow, sh);
return (emaS > 0.0 && c < emaS);
}
bool CciCrossAboveZero(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 <= 0.0 && c1 > 0.0);
}
bool CciCrossBelowZero(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 >= 0.0 && c1 < 0.0);
}
bool CciCrossAbove100(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 < InpCciOverbought && c1 > InpCciOverbought);
}
bool CciCrossBelowMinus100(const string sym)
{
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
return (c2 > InpCciOversold && c1 < InpCciOversold);
}
bool HadCciOversoldRecently(const string sym)
{
for(int i = 2; i <= InpPullbackCciLookback + 1; i++)
{
double v = CciAt(sym, InpTimeframe, InpCciPeriod, i);
if(v <= InpCciOversold)
return true;
}
return false;
}
bool HadCciOverboughtRecently(const string sym)
{
for(int i = 2; i <= InpPullbackCciLookback + 1; i++)
{
double v = CciAt(sym, InpTimeframe, InpCciPeriod, i);
if(v >= InpCciOverbought)
return true;
}
return false;
}
bool PullbackNearFastEmaLong(const string sym)
{
double emaF = EmaAt(sym, InpTimeframe, InpEmaFast, 1);
double lo = iLow(sym, InpTimeframe, 1);
if(emaF <= 0.0)
return false;
return (lo <= emaF + InpSlBufferPoints * _Point * 3.0);
}
bool PullbackNearFastEmaShort(const string sym)
{
double emaF = EmaAt(sym, InpTimeframe, InpEmaFast, 1);
double hi = iHigh(sym, InpTimeframe, 1);
if(emaF <= 0.0)
return false;
return (hi >= emaF - InpSlBufferPoints * _Point * 3.0);
}
int PositionsByMagic(const string sym, const int magic)
{
int n = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(PositionGetString(POSITION_SYMBOL) == sym && (int)PositionGetInteger(POSITION_MAGIC) == magic)
n++;
}
return n;
}
void ComputeSLTP(const bool isBuy, const double entry, double &sl, double &tp)
{
const string sym = WorkSymbol();
sl = 0.0;
tp = 0.0;
if(!InpUseStructuralSL)
return;
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, InpEmaTrendBars);
double buf = InpSlBufferPoints * _Point;
if(isBuy)
sl = emaM - buf;
else
sl = emaM + buf;
}
int BarsSinceOpen(const string sym, const datetime openTime)
{
if(openTime <= 0)
return 0;
int sh = iBarShift(sym, InpTimeframe, openTime, false);
if(sh < 0)
return 999999;
return sh;
}
void ClosePositionTicket(const ulong ticket, const string reason)
{
trade.SetExpertMagicNumber(InpMagic);
if(trade.PositionClose(ticket))
Log("Close: " + reason);
}
void ManageSuperEMAExits(const string sym)
{
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != sym)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
continue;
ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
double h1 = 0.0;
if(!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 1, h1))
continue;
bool closeLong = false;
bool closeShort = false;
string reason = "";
if(InpMaxHoldingBars > 0)
{
int held = BarsSinceOpen(sym, openTime);
if(held >= InpMaxHoldingBars)
{
if(ptype == POSITION_TYPE_BUY)
closeLong = true;
else
closeShort = true;
reason = "time stop (max bars)";
}
}
if(ptype == POSITION_TYPE_BUY)
{
if(InpExitOnTrendFlip && TrendDown(sym, InpEmaTrendBars))
{
closeLong = true;
reason = "trend flip (below slow EMA)";
}
if(InpExitOnMacdFlip && h1 < 0.0)
{
closeLong = true;
reason = "MACD histogram < 0";
}
if(InpExitOnCciZeroCross && CciCrossBelowZero(sym))
{
closeLong = true;
reason = "CCI crossed below zero";
}
if(InpExitBelowMidEma)
{
double c = iClose(sym, InpTimeframe, 1);
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, 1);
if(emaM > 0.0 && c < emaM)
{
closeLong = true;
reason = "close below mid EMA";
}
}
if(closeLong)
ClosePositionTicket(ticket, reason);
}
else if(ptype == POSITION_TYPE_SELL)
{
if(InpExitOnTrendFlip && TrendUp(sym, InpEmaTrendBars))
{
closeShort = true;
reason = "trend flip (above slow EMA)";
}
if(InpExitOnMacdFlip && h1 > 0.0)
{
closeShort = true;
reason = "MACD histogram > 0";
}
if(InpExitOnCciZeroCross && CciCrossAboveZero(sym))
{
closeShort = true;
reason = "CCI crossed above zero";
}
if(InpExitBelowMidEma)
{
double c = iClose(sym, InpTimeframe, 1);
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, 1);
if(emaM > 0.0 && c > emaM)
{
closeShort = true;
reason = "close above mid EMA";
}
}
if(closeShort)
ClosePositionTicket(ticket, reason);
}
}
}
int OnInit()
{
string sym = WorkSymbol();
if(!SymbolSelect(sym, true))
{
Print("SuperEMA: cannot select symbol ", sym);
return INIT_FAILED;
}
trade.SetExpertMagicNumber(InpMagic);
trade.SetDeviationInPoints(InpSlippagePoints);
return INIT_SUCCEEDED;
}
void OnTick()
{
string sym = WorkSymbol();
if(_Symbol != sym)
{
static datetime lastLog = 0;
datetime tb = iTime(_Symbol, PERIOD_M1, 0);
if(tb != lastLog && InpDebugLogs)
{
lastLog = tb;
Log("Chart symbol differs from WorkSymbol; attach to " + sym + " or set InpSymbol empty.");
}
return;
}
if(!IsNewBar(sym, InpTimeframe))
return;
// Exits must run every bar; do not skip when a position exists (otherwise trades never close with SL=0/TP=0).
ManageSuperEMAExits(sym);
if(InpOneTradeOnly && PositionsByMagic(sym, InpMagic) > 0)
return;
const int sh = InpEmaTrendBars;
double h1 = 0.0, h2 = 0.0;
if(!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 1, h1) ||
!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 2, h2))
return;
bool up = TrendUp(sym, sh);
bool dn = TrendDown(sym, sh);
bool wantBuy = false;
bool wantSell = false;
switch(InpEntryStyle)
{
case ENTRY_CCIZERO_MACD:
if(up && CciCrossAboveZero(sym) && h1 > 0.0)
wantBuy = true;
if(dn && CciCrossBelowZero(sym) && h1 < 0.0)
wantSell = true;
break;
case ENTRY_LAMBERT:
if(up && CciCrossAbove100(sym) && h1 > 0.0)
wantBuy = true;
if(dn && CciCrossBelowMinus100(sym) && h1 < 0.0)
wantSell = true;
break;
case ENTRY_PULLBACK:
if(up && HadCciOversoldRecently(sym) && CciCrossAboveZero(sym) && h1 > 0.0 && PullbackNearFastEmaLong(sym))
wantBuy = true;
if(dn && HadCciOverboughtRecently(sym) && CciCrossBelowZero(sym) && h1 < 0.0 && PullbackNearFastEmaShort(sym))
wantSell = true;
break;
}
MqlTick tick;
if(!SymbolInfoTick(sym, tick))
return;
double sl = 0.0, tp = 0.0;
if(wantBuy && !wantSell)
{
ComputeSLTP(true, tick.ask, sl, tp);
if(trade.Buy(InpLots, sym, tick.ask, sl, tp, "SuperEMA long"))
Log(StringFormat("BUY ask=%.5f sl=%.5f cci=%.2f macdHist=%.5f", tick.ask, sl,
CciAt(sym, InpTimeframe, InpCciPeriod, 1), h1));
}
else if(wantSell && !wantBuy)
{
ComputeSLTP(false, tick.bid, sl, tp);
if(trade.Sell(InpLots, sym, tick.bid, sl, tp, "SuperEMA short"))
Log(StringFormat("SELL bid=%.5f sl=%.5f cci=%.2f macdHist=%.5f", tick.bid, sl,
CciAt(sym, InpTimeframe, InpCciPeriod, 1), h1));
}
}