Update
This commit is contained in:
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user