Update
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
#property description "BTCUSD mean reversion: RSI extreme + EMA distance + low ADX; escape when ADX trends up."
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input group "=== Market ==="
|
||||
input string InpSymbol = "BTCUSD";
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M20;
|
||||
input double InpLots = 0.01;
|
||||
input int InpSlippagePoints = 30;
|
||||
input int InpMagic = 930201;
|
||||
input int InpMaxPositions = 5;
|
||||
input bool InpDebugLogs = false;
|
||||
|
||||
input group "=== EMA distance (mean reversion stretch) ==="
|
||||
input int InpEmaPeriod = 250;
|
||||
input double InpMinEmaDistancePts = 3650.0; // |close-EMA| in points; raise/lower for BTC broker digits
|
||||
|
||||
input group "=== RSI ==="
|
||||
input int InpRsiPeriod = 28;
|
||||
input double InpRsiOversold = 40.0;
|
||||
input double InpRsiOverbought = 83.0;
|
||||
input bool InpUseRsiCross = false; // true: require cross into zone on last bar
|
||||
|
||||
input group "=== ADX (trend filter + escape) ==="
|
||||
input int InpAdxPeriod = 27;
|
||||
input double InpAdxMaxForEntry = 17.0; // no new trades if ADX >= this (ranging bias)
|
||||
input double InpAdxEscape = 34.0; // close all if ADX >= this (trend building)
|
||||
|
||||
input group "=== Price action (optional) ==="
|
||||
input bool InpRequireReversalBar = false; // buy: bearish bar at signal; sell: bullish bar
|
||||
|
||||
input group "=== Risk ==="
|
||||
input bool InpUseHardSLTP = false;
|
||||
input double InpSLPoints = 1300;
|
||||
input double InpTPPoints = 13400;
|
||||
|
||||
CTrade trade;
|
||||
datetime g_lastBarTime = 0;
|
||||
|
||||
int g_hRsi = INVALID_HANDLE;
|
||||
int g_hEma = INVALID_HANDLE;
|
||||
int g_hAdx = INVALID_HANDLE;
|
||||
|
||||
void DebugLog(const string msg)
|
||||
{
|
||||
if(InpDebugLogs)
|
||||
Print("[MeanRevEMA_RSI_ADX] ", msg);
|
||||
}
|
||||
|
||||
bool IsNewBar(const string symbol, const ENUM_TIMEFRAMES tf)
|
||||
{
|
||||
datetime t = iTime(symbol, tf, 0);
|
||||
if(t <= 0 || t == g_lastBarTime)
|
||||
return false;
|
||||
g_lastBarTime = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CopyOne(const int handle, const int buffer, const int shift, double &out)
|
||||
{
|
||||
if(handle == INVALID_HANDLE)
|
||||
return false;
|
||||
double v[1];
|
||||
if(CopyBuffer(handle, buffer, shift, 1, v) != 1)
|
||||
return false;
|
||||
out = v[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
double GetAdx(const int shift)
|
||||
{
|
||||
double v = 0.0;
|
||||
if(!CopyOne(g_hAdx, 0, shift, v))
|
||||
return 0.0;
|
||||
return v;
|
||||
}
|
||||
|
||||
double GetRsi(const int shift)
|
||||
{
|
||||
double v = 0.0;
|
||||
if(!CopyOne(g_hRsi, 0, shift, v))
|
||||
return 0.0;
|
||||
return v;
|
||||
}
|
||||
|
||||
double GetEma(const int shift)
|
||||
{
|
||||
double v = 0.0;
|
||||
if(!CopyOne(g_hEma, 0, shift, v))
|
||||
return 0.0;
|
||||
return v;
|
||||
}
|
||||
|
||||
int CountPositionsByMagic(const string symbol, const int magic)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; --i)
|
||||
{
|
||||
ulong t = PositionGetTicket(i);
|
||||
if(t == 0)
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
(int)PositionGetInteger(POSITION_MAGIC) == magic)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void CloseAllByMagic(const string symbol, const int magic)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; --i)
|
||||
{
|
||||
ulong t = PositionGetTicket(i);
|
||||
if(t == 0)
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
(int)PositionGetInteger(POSITION_MAGIC) == magic)
|
||||
trade.PositionClose(t);
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeSLTP(const bool isBuy, const double entry, double &sl, double &tp)
|
||||
{
|
||||
if(!InpUseHardSLTP)
|
||||
{
|
||||
sl = 0.0;
|
||||
tp = 0.0;
|
||||
return;
|
||||
}
|
||||
if(isBuy)
|
||||
{
|
||||
sl = entry - InpSLPoints * _Point;
|
||||
tp = entry + InpTPPoints * _Point;
|
||||
}
|
||||
else
|
||||
{
|
||||
sl = entry + InpSLPoints * _Point;
|
||||
tp = entry - InpTPPoints * _Point;
|
||||
}
|
||||
}
|
||||
|
||||
bool RsiOversoldSignal()
|
||||
{
|
||||
double r1 = 0.0, r2 = 0.0;
|
||||
if(!CopyOne(g_hRsi, 0, 1, r1) || !CopyOne(g_hRsi, 0, 2, r2))
|
||||
return false;
|
||||
if(InpUseRsiCross)
|
||||
return (r2 > InpRsiOversold && r1 <= InpRsiOversold);
|
||||
return (r1 <= InpRsiOversold);
|
||||
}
|
||||
|
||||
bool RsiOverboughtSignal()
|
||||
{
|
||||
double r1 = 0.0, r2 = 0.0;
|
||||
if(!CopyOne(g_hRsi, 0, 1, r1) || !CopyOne(g_hRsi, 0, 2, r2))
|
||||
return false;
|
||||
if(InpUseRsiCross)
|
||||
return (r2 < InpRsiOverbought && r1 >= InpRsiOverbought);
|
||||
return (r1 >= InpRsiOverbought);
|
||||
}
|
||||
|
||||
bool BarBearish(const int shift)
|
||||
{
|
||||
double o = iOpen(InpSymbol, InpTimeframe, shift);
|
||||
double c = iClose(InpSymbol, InpTimeframe, shift);
|
||||
return (c < o);
|
||||
}
|
||||
|
||||
bool BarBullish(const int shift)
|
||||
{
|
||||
double o = iOpen(InpSymbol, InpTimeframe, shift);
|
||||
double c = iClose(InpSymbol, InpTimeframe, shift);
|
||||
return (c > o);
|
||||
}
|
||||
|
||||
bool BuySetup()
|
||||
{
|
||||
if(!RsiOversoldSignal())
|
||||
return false;
|
||||
|
||||
double ema = GetEma(1);
|
||||
double cls = iClose(InpSymbol, InpTimeframe, 1);
|
||||
if(ema <= 0.0 || cls <= 0.0)
|
||||
return false;
|
||||
|
||||
double distPts = (ema - cls) / _Point;
|
||||
if(distPts < InpMinEmaDistancePts)
|
||||
return false;
|
||||
|
||||
if(InpRequireReversalBar && !BarBearish(1))
|
||||
return false;
|
||||
|
||||
double adx = GetAdx(1);
|
||||
if(adx <= 0.0)
|
||||
return false;
|
||||
if(adx >= InpAdxMaxForEntry)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SellSetup()
|
||||
{
|
||||
if(!RsiOverboughtSignal())
|
||||
return false;
|
||||
|
||||
double ema = GetEma(1);
|
||||
double cls = iClose(InpSymbol, InpTimeframe, 1);
|
||||
if(ema <= 0.0 || cls <= 0.0)
|
||||
return false;
|
||||
|
||||
double distPts = (cls - ema) / _Point;
|
||||
if(distPts < InpMinEmaDistancePts)
|
||||
return false;
|
||||
|
||||
if(InpRequireReversalBar && !BarBullish(1))
|
||||
return false;
|
||||
|
||||
double adx = GetAdx(1);
|
||||
if(adx <= 0.0)
|
||||
return false;
|
||||
if(adx >= InpAdxMaxForEntry)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
if(StringLen(InpSymbol) == 0)
|
||||
{
|
||||
Print("[MeanRevEMA_RSI_ADX] OnInit: InpSymbol is empty.");
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
}
|
||||
if(InpRsiPeriod < 2 || InpEmaPeriod < 1 || InpAdxPeriod < 1)
|
||||
{
|
||||
Print("[MeanRevEMA_RSI_ADX] OnInit: invalid periods rsi=", InpRsiPeriod,
|
||||
" ema=", InpEmaPeriod, " adx=", InpAdxPeriod);
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
}
|
||||
if(!SymbolSelect(InpSymbol, true))
|
||||
{
|
||||
Print("[MeanRevEMA_RSI_ADX] OnInit: SymbolSelect failed (symbol missing on this agent?): ", InpSymbol,
|
||||
" err=", GetLastError(),
|
||||
" — use Local agents only for broker-specific names, or set InpSymbol to a symbol the agent has.");
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
}
|
||||
|
||||
g_hRsi = iRSI(InpSymbol, InpTimeframe, InpRsiPeriod, PRICE_CLOSE);
|
||||
if(g_hRsi == INVALID_HANDLE)
|
||||
{
|
||||
Print("[MeanRevEMA_RSI_ADX] OnInit: iRSI failed sym=", InpSymbol, " tf=", (int)InpTimeframe,
|
||||
" period=", InpRsiPeriod, " err=", GetLastError());
|
||||
return INIT_FAILED;
|
||||
}
|
||||
g_hEma = iMA(InpSymbol, InpTimeframe, InpEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(g_hEma == INVALID_HANDLE)
|
||||
{
|
||||
Print("[MeanRevEMA_RSI_ADX] OnInit: iMA failed sym=", InpSymbol, " tf=", (int)InpTimeframe,
|
||||
" period=", InpEmaPeriod, " err=", GetLastError());
|
||||
IndicatorRelease(g_hRsi);
|
||||
g_hRsi = INVALID_HANDLE;
|
||||
return INIT_FAILED;
|
||||
}
|
||||
g_hAdx = iADX(InpSymbol, InpTimeframe, InpAdxPeriod);
|
||||
if(g_hAdx == INVALID_HANDLE)
|
||||
{
|
||||
Print("[MeanRevEMA_RSI_ADX] OnInit: iADX failed sym=", InpSymbol, " tf=", (int)InpTimeframe,
|
||||
" period=", InpAdxPeriod, " err=", GetLastError());
|
||||
IndicatorRelease(g_hRsi);
|
||||
IndicatorRelease(g_hEma);
|
||||
g_hRsi = g_hEma = INVALID_HANDLE;
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
trade.SetDeviationInPoints(InpSlippagePoints);
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_hRsi != INVALID_HANDLE) IndicatorRelease(g_hRsi);
|
||||
if(g_hEma != INVALID_HANDLE) IndicatorRelease(g_hEma);
|
||||
if(g_hAdx != INVALID_HANDLE) IndicatorRelease(g_hAdx);
|
||||
g_hRsi = g_hEma = g_hAdx = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(_Symbol != InpSymbol)
|
||||
{
|
||||
static datetime lastMismatchLog = 0;
|
||||
datetime nowBar = iTime(_Symbol, PERIOD_M1, 0);
|
||||
if(nowBar != lastMismatchLog)
|
||||
{
|
||||
lastMismatchLog = nowBar;
|
||||
DebugLog(StringFormat("Skipped: chart symbol=%s but InpSymbol=%s.", _Symbol, InpSymbol));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const int posCount = CountPositionsByMagic(InpSymbol, InpMagic);
|
||||
const double adxLive = GetAdx(0);
|
||||
|
||||
if(posCount > 0 && adxLive > 0.0 && adxLive >= InpAdxEscape)
|
||||
{
|
||||
DebugLog(StringFormat("ADX escape: adx0=%.2f >= %.2f -> closing %d position(s).", adxLive, InpAdxEscape, posCount));
|
||||
CloseAllByMagic(InpSymbol, InpMagic);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!IsNewBar(InpSymbol, InpTimeframe))
|
||||
return;
|
||||
|
||||
const double rsi1 = GetRsi(1);
|
||||
const double adx1 = GetAdx(1);
|
||||
const double ema1 = GetEma(1);
|
||||
const double c1 = iClose(InpSymbol, InpTimeframe, 1);
|
||||
DebugLog(StringFormat("Bar=%s rsi1=%.2f adx1=%.2f ema1=%.5f close1=%.5f positions=%d",
|
||||
TimeToString(iTime(InpSymbol, InpTimeframe, 1), TIME_DATE | TIME_MINUTES),
|
||||
rsi1, adx1, ema1, c1, posCount));
|
||||
|
||||
if(posCount >= InpMaxPositions)
|
||||
{
|
||||
DebugLog(StringFormat("Skipped: max positions (%d).", InpMaxPositions));
|
||||
return;
|
||||
}
|
||||
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(InpSymbol, tick))
|
||||
{
|
||||
DebugLog("Skipped: SymbolInfoTick failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
double sl = 0.0, tp = 0.0;
|
||||
|
||||
if(BuySetup())
|
||||
{
|
||||
ComputeSLTP(true, tick.ask, sl, tp);
|
||||
if(trade.Buy(InpLots, InpSymbol, tick.ask, sl, tp, "MeanRev_RSI_OS_EMA"))
|
||||
DebugLog(StringFormat("BUY lots=%.2f ask=%.2f sl=%.2f tp=%.2f", InpLots, tick.ask, sl, tp));
|
||||
else
|
||||
DebugLog(StringFormat("BUY failed retcode=%d", trade.ResultRetcode()));
|
||||
return;
|
||||
}
|
||||
|
||||
if(SellSetup())
|
||||
{
|
||||
ComputeSLTP(false, tick.bid, sl, tp);
|
||||
if(trade.Sell(InpLots, InpSymbol, tick.bid, sl, tp, "MeanRev_RSI_OB_EMA"))
|
||||
DebugLog(StringFormat("SELL lots=%.2f bid=%.2f sl=%.2f tp=%.2f", InpLots, tick.bid, sl, tp));
|
||||
else
|
||||
DebugLog(StringFormat("SELL failed retcode=%d", trade.ResultRetcode()));
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLog("No entry: RSI/EMA distance/ADX/bar filters not aligned.");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
; Synced with daughter.set (ranges + current defaults). Strategy Tester → Inputs → Load.
|
||||
; Format: value||start||step||stop||Y|N
|
||||
;
|
||||
; === Market ===
|
||||
InpSymbol=BTCUSD
|
||||
InpTimeframe=20||15||0||16385||Y
|
||||
InpLots=0.01||0.01||0.001000||0.050000||N
|
||||
InpSlippagePoints=30||30||1||300||N
|
||||
InpMagic=930201||930201||1||930201||N
|
||||
InpMaxPositions=5||1||1||8||Y
|
||||
InpDebugLogs=false||false||0||true||N
|
||||
; === EMA distance (mean reversion stretch) ===
|
||||
InpEmaPeriod=250||50||10||400||Y
|
||||
InpMinEmaDistancePts=3650||200.0||50.0||5000.0||Y
|
||||
; === RSI ===
|
||||
InpRsiPeriod=28||7||1||28||Y
|
||||
InpRsiOversold=40||18.0||1.0||42.0||Y
|
||||
InpRsiOverbought=83||58.0||1.0||88.0||Y
|
||||
InpUseRsiCross=false||false||0||true||Y
|
||||
; === ADX (trend filter + escape) ===
|
||||
InpAdxPeriod=27||7||1||28||Y
|
||||
InpAdxMaxForEntry=17||15.0||1.0||40.0||Y
|
||||
InpAdxEscape=34||22.0||1.0||55.0||Y
|
||||
; === Price action (optional) ===
|
||||
InpRequireReversalBar=false||false||0||true||Y
|
||||
; === Risk ===
|
||||
InpUseHardSLTP=false||false||0||true||Y
|
||||
InpSLPoints=1300||800.0||100.0||8000.0||Y
|
||||
InpTPPoints=13400||1000.0||200.0||15000.0||Y
|
||||
@@ -0,0 +1,30 @@
|
||||
; saved on 2026.04.23 22:29:36
|
||||
; this file contains input parameters for testing/optimizing MeanReversion expert advisor
|
||||
; to use it in the strategy tester, click Load in the context menu of the Inputs tab
|
||||
;
|
||||
; === Market ===
|
||||
InpSymbol=BTCUSD
|
||||
InpTimeframe=20||15||0||16385||Y
|
||||
InpLots=0.01||0.01||0.001000||0.050000||N
|
||||
InpSlippagePoints=30||30||1||300||N
|
||||
InpMagic=930201||930201||1||930201||N
|
||||
InpMaxPositions=5||1||1||8||Y
|
||||
InpDebugLogs=false||false||0||true||N
|
||||
; === EMA distance (mean reversion stretch) ===
|
||||
InpEmaPeriod=250||50||10||400||Y
|
||||
InpMinEmaDistancePts=3650||200.0||50.0||5000.0||Y
|
||||
; === RSI ===
|
||||
InpRsiPeriod=28||7||1||28||Y
|
||||
InpRsiOversold=40||18.0||1.0||42.0||Y
|
||||
InpRsiOverbought=83||58.0||1.0||88.0||Y
|
||||
InpUseRsiCross=false||false||0||true||Y
|
||||
; === ADX (trend filter + escape) ===
|
||||
InpAdxPeriod=27||7||1||28||Y
|
||||
InpAdxMaxForEntry=17||15.0||1.0||40.0||Y
|
||||
InpAdxEscape=34||22.0||1.0||55.0||Y
|
||||
; === Price action (optional) ===
|
||||
InpRequireReversalBar=false||false||0||true||Y
|
||||
; === Risk ===
|
||||
InpUseHardSLTP=false||false||0||true||Y
|
||||
InpSLPoints=1300||800.0||100.0||8000.0||Y
|
||||
InpTPPoints=13400||1000.0||200.0||15000.0||Y
|
||||
Reference in New Issue
Block a user