Update
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
; EMASlopeDistanceCocktailBTCUSD\main.mq5 — fixed inputs (same as desktop ultimate.set)
|
||||
; Attach EA to BTCUSD chart. Timeframe 16385 = H1.
|
||||
;
|
||||
EMA_Periode=50||50||1||500||N
|
||||
PreisSchwelle=700.0||700.0||70.000000||7000.000000||N
|
||||
SteigungSchwelle=25.0||25.0||2.500000||250.000000||N
|
||||
ÜberwachungTimeout=340||340||1||3400||N
|
||||
TrailingStop=370.0||370.0||37.000000||3700.000000||N
|
||||
LotGröße=0.07||0.07||0.007000||0.700000||N
|
||||
MagicNumber=135790||135790||1||1357900||N
|
||||
UseSpreadAdjustment=true||false||0||true||N
|
||||
Timeframe=16385||0||0||49153||N
|
||||
UseBarData=true||false||0||true||N
|
||||
MaxTradesPerCrossover=10||10||1||100||N
|
||||
ProfitCheckBars=15||15||1||150||N
|
||||
CloseUnprofitableTrades=true||false||0||true||N
|
||||
UseWeeklyADXFilter=true||false||0||true||N
|
||||
WeeklyADXPeriod=15||15||1||150||N
|
||||
WeeklyADXMin=40.0||40.0||4.000000||400.000000||N
|
||||
WeeklyADXBarShift=2||2||1||20||N
|
||||
WeeklyADXUseDirection=true||false||0||true||N
|
||||
@@ -0,0 +1,24 @@
|
||||
; EMASlopeDistanceCocktailBTCUSD\main.mq5 — BTCUSD Strategy Tester preset
|
||||
; Load: Tester → Inputs → context menu → Load. Attach EA to BTCUSD chart (EA uses _Symbol).
|
||||
;
|
||||
; value||start||step||stop||Y|N (MT5 convention). Timeframe 16385 = H1.
|
||||
; Tune PreisSchwelle / TrailingStop / SteigungSchwelle to your broker's _Point for BTC.
|
||||
;
|
||||
EMA_Periode=50||30||5||200||Y
|
||||
PreisSchwelle=700.0||200.0||50.0||5000.0||Y
|
||||
SteigungSchwelle=25.0||5.0||1.0||80.0||Y
|
||||
ÜberwachungTimeout=340||60||20||900||Y
|
||||
TrailingStop=370.0||150.0||20.0||5000.0||Y
|
||||
LotGröße=0.07||0.01||0.01||0.50||N
|
||||
MagicNumber=135790||135790||1||1357900||N
|
||||
UseSpreadAdjustment=true||false||0||true||Y
|
||||
Timeframe=16385||0||0||49153||N
|
||||
UseBarData=true||false||0||true||N
|
||||
MaxTradesPerCrossover=10||1||1||25||Y
|
||||
ProfitCheckBars=15||5||1||60||Y
|
||||
CloseUnprofitableTrades=true||false||0||true||Y
|
||||
UseWeeklyADXFilter=true||false||0||true||Y
|
||||
WeeklyADXPeriod=15||7||1||28||Y
|
||||
WeeklyADXMin=40.0||15.0||2.0||55.0||Y
|
||||
WeeklyADXBarShift=2||1||1||5||Y
|
||||
WeeklyADXUseDirection=true||false||0||true||Y
|
||||
@@ -0,0 +1,589 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMACrossOver.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
//--- Eingabeparameter (Input Parameters) - Optimized Profitable Parameters
|
||||
input int EMA_Periode = 50; // EMA Periode
|
||||
input double PreisSchwelle = 700.0; // Preisbewegung Schwelle in Pips
|
||||
input double SteigungSchwelle = 25.0; // EMA Steigung Schwelle in Pips
|
||||
input int ÜberwachungTimeout = 340; // Überwachungszeit in Sekunden
|
||||
input double TrailingStop = 370.0; // Gleitender Stop in Pips
|
||||
input double LotGröße = 0.07; // Handelsvolumen
|
||||
input int MagicNumber = 135790; // Magic Number für Trades
|
||||
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
|
||||
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
|
||||
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
|
||||
input int MaxTradesPerCrossover = 10; // Maximale Trades pro Crossover-Ereignis
|
||||
input int ProfitCheckBars = 15; // Bars bis zur Profit-Prüfung
|
||||
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
|
||||
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
|
||||
input int WeeklyADXPeriod = 15; // ADX-Periode auf W1
|
||||
input double WeeklyADXMin = 40.0; // Minimaler ADX fuer Trendfreigabe
|
||||
input int WeeklyADXBarShift = 2; // 1=letzte geschlossene W1-Kerze
|
||||
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
|
||||
|
||||
//--- Globale Variablen (Global Variables)
|
||||
int ema_handle; // EMA Indicator Handle
|
||||
double ema_array[]; // Array für EMA
|
||||
datetime letzte_überwachung_zeit; // Zeit der letzten Überwachung
|
||||
bool überwachung_aktiv = false; // Überwachungsstatus
|
||||
bool preis_trigger_aktiv = false; // Preis-Trigger Status
|
||||
bool steigung_trigger_aktiv = false; // Steigungs-Trigger Status
|
||||
int ticket = 0; // Trade Ticket
|
||||
CTrade trade; // CTrade Objekt
|
||||
int trades_in_current_crossover = 0; // Anzahl Trades im aktuellen Crossover
|
||||
bool crossover_detected = false; // Crossover erkannt
|
||||
datetime trade_open_time = 0; // Zeitpunkt des Trade-Öffnens
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Weekly ADX trend filter |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsWeeklyADXTrendFavorable(ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
if(!UseWeeklyADXFilter)
|
||||
return true;
|
||||
|
||||
int adxShift = WeeklyADXBarShift;
|
||||
if(adxShift < 0)
|
||||
adxShift = 0;
|
||||
|
||||
int adx_handle = iADX(_Symbol, PERIOD_W1, WeeklyADXPeriod);
|
||||
if(adx_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("TRACE: Weekly ADX Handle ungültig - Filter blockiert Entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
double adx_buf[], plus_di_buf[], minus_di_buf[];
|
||||
ArraySetAsSeries(adx_buf, true);
|
||||
ArraySetAsSeries(plus_di_buf, true);
|
||||
ArraySetAsSeries(minus_di_buf, true);
|
||||
|
||||
bool ok_adx = (CopyBuffer(adx_handle, 0, adxShift, 1, adx_buf) > 0);
|
||||
bool ok_plus = (CopyBuffer(adx_handle, 1, adxShift, 1, plus_di_buf) > 0);
|
||||
bool ok_minus = (CopyBuffer(adx_handle, 2, adxShift, 1, minus_di_buf) > 0);
|
||||
IndicatorRelease(adx_handle);
|
||||
|
||||
if(!ok_adx || !ok_plus || !ok_minus)
|
||||
{
|
||||
Print("TRACE: Weekly ADX Daten nicht verfügbar - Filter blockiert Entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
double adx_value = adx_buf[0];
|
||||
double plus_di = plus_di_buf[0];
|
||||
double minus_di = minus_di_buf[0];
|
||||
|
||||
bool strength_ok = (adx_value >= WeeklyADXMin);
|
||||
bool direction_ok = true;
|
||||
if(WeeklyADXUseDirection)
|
||||
{
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
direction_ok = (plus_di > minus_di);
|
||||
else
|
||||
direction_ok = (minus_di > plus_di);
|
||||
}
|
||||
|
||||
Print("TRACE: Weekly ADX Filter | ADX=", DoubleToString(adx_value, 2),
|
||||
" +DI=", DoubleToString(plus_di, 2),
|
||||
" -DI=", DoubleToString(minus_di, 2),
|
||||
" strength_ok=", strength_ok,
|
||||
" direction_ok=", direction_ok);
|
||||
|
||||
return (strength_ok && direction_ok);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//--- CTrade konfigurieren (Configure CTrade)
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(10);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
//--- EMA Indicator Handle erstellen (Create EMA indicator handle)
|
||||
ema_handle = iMA(_Symbol, Timeframe, EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(ema_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Fehler beim Erstellen des EMA Indicators");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
//--- Arrays initialisieren (Initialize arrays)
|
||||
ArraySetAsSeries(ema_array, true);
|
||||
|
||||
//--- Arrays mit aktuellen Werten füllen (Fill arrays with current values)
|
||||
BerechneEMA();
|
||||
|
||||
Print("EMA EA initialisiert - Periode: ", EMA_Periode, " Timeframe: ", EnumToString(Timeframe), " Handle: ", ema_handle);
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//--- Indicator Handle freigeben (Release indicator handle)
|
||||
if(ema_handle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(ema_handle);
|
||||
}
|
||||
|
||||
Print("EA beendet - Grund: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
//--- Bar-Daten oder Tick-Daten verwenden (Use bar data or tick data)
|
||||
if(UseBarData)
|
||||
{
|
||||
//--- Nur bei neuen Bars ausführen (Only execute on new bars)
|
||||
static datetime last_bar_time = 0;
|
||||
datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
|
||||
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
return; // Kein neuer Bar, nichts tun
|
||||
}
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
}
|
||||
|
||||
//--- EMA Werte berechnen (Calculate EMA values)
|
||||
BerechneEMA();
|
||||
|
||||
//--- Debug: Aktuelle Werte ausgeben (Debug: Output current values)
|
||||
if(ArraySize(ema_array) > 0)
|
||||
{
|
||||
double aktueller_close = iClose(_Symbol, Timeframe, 0);
|
||||
double ema_aktuell = ema_array[0];
|
||||
double ema_vorher = ema_array[1];
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / _Point;
|
||||
double steigung = (ema_aktuell - ema_vorher) / _Point;
|
||||
|
||||
if(UseBarData)
|
||||
{
|
||||
Print("=== DEBUG INFO (Neuer Bar) ===");
|
||||
Print("Bar Zeit: ", TimeToString(iTime(_Symbol, Timeframe, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("=== DEBUG INFO (Tick) ===");
|
||||
}
|
||||
|
||||
Print("Aktueller Close: ", aktueller_close);
|
||||
Print("EMA: ", ema_aktuell);
|
||||
Print("Preis-Abstand: ", preis_abstand, " Pips");
|
||||
Print("EMA Steigung: ", steigung, " Pips");
|
||||
Print("Differenz Close-EMA: ", aktueller_close - ema_aktuell);
|
||||
Print("Preis-Trigger: ", preis_trigger_aktiv, " Steigungs-Trigger: ", steigung_trigger_aktiv);
|
||||
Print("Überwachung aktiv: ", überwachung_aktiv);
|
||||
Print("Position offen: ", PositionExistsByMagic(_Symbol, MagicNumber));
|
||||
Print("Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
|
||||
Print("==================");
|
||||
}
|
||||
|
||||
//--- Überwachung prüfen (Check monitoring)
|
||||
if(überwachung_aktiv)
|
||||
{
|
||||
if(UseBarData)
|
||||
{
|
||||
// Bar-basierte Überwachungszeit
|
||||
int bars_since_monitoring = iBarShift(_Symbol, Timeframe, letzte_überwachung_zeit);
|
||||
int timeout_bars = (int)(ÜberwachungTimeout / PeriodSeconds(Timeframe));
|
||||
|
||||
if(bars_since_monitoring > timeout_bars)
|
||||
{
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Tick-basierte Überwachungszeit
|
||||
if(TimeCurrent() - letzte_überwachung_zeit > ÜberwachungTimeout)
|
||||
{
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trigger-Bedingungen prüfen (Check trigger conditions)
|
||||
PrüfeTrigger();
|
||||
|
||||
//--- Trade Management (Trade management)
|
||||
VerwalteTrades();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMA Berechnung (EMA Calculation) |
|
||||
//+------------------------------------------------------------------+
|
||||
void BerechneEMA()
|
||||
{
|
||||
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
|
||||
int copied = CopyBuffer(ema_handle, 0, 0, 3, ema_array);
|
||||
|
||||
if(copied <= 0)
|
||||
{
|
||||
Print("TRACE: Fehler beim Kopieren der EMA Werte - Copied: ", copied);
|
||||
return;
|
||||
}
|
||||
|
||||
Print("TRACE: EMA Werte kopiert: ", copied, " Bars");
|
||||
Print("TRACE: EMA [0]: ", ema_array[0], " [1]: ", ema_array[1], " [2]: ", ema_array[2]);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeTrigger()
|
||||
{
|
||||
if(ArraySize(ema_array) < 2)
|
||||
{
|
||||
Print("TRACE: Array zu klein - Größe: ", ArraySize(ema_array));
|
||||
return;
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte (Current values)
|
||||
double aktueller_preis = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double aktueller_ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double aktueller_close = iClose(_Symbol, Timeframe, 0);
|
||||
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
|
||||
|
||||
//--- EMA Werte in Variablen (EMA values in variables)
|
||||
double ema_aktuell = ema_array[0];
|
||||
double ema_vorher = ema_array[1];
|
||||
|
||||
//--- EMA Crossover Erkennung (EMA Crossover Detection)
|
||||
// Prüfe ob Preis die EMA kreuzt (Check if price crosses EMA)
|
||||
static double last_close = 0;
|
||||
static double last_ema = 0;
|
||||
|
||||
if(last_close != 0 && last_ema != 0)
|
||||
{
|
||||
bool crossover_bullish = (last_close <= last_ema) && (aktueller_close > ema_aktuell);
|
||||
bool crossover_bearish = (last_close >= last_ema) && (aktueller_close < ema_aktuell);
|
||||
|
||||
//--- Neues Crossover-Ereignis erkannt (New crossover event detected)
|
||||
if(crossover_bullish || crossover_bearish)
|
||||
{
|
||||
trades_in_current_crossover = 0; // Reset trade counter
|
||||
Print("TRACE: EMA Crossover erkannt - ", (crossover_bullish ? "BULLISH" : "BEARISH"), " - Trade-Counter zurückgesetzt");
|
||||
Print("TRACE: Vorher: Close=", last_close, " EMA=", last_ema, " Jetzt: Close=", aktueller_close, " EMA=", ema_aktuell);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte für nächsten Vergleich speichern (Save current values for next comparison)
|
||||
last_close = aktueller_close;
|
||||
last_ema = ema_aktuell;
|
||||
|
||||
//--- Preisbewegung zur EMA prüfen (Check price action to EMA)
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / _Point / pips_multiplier;
|
||||
|
||||
Print("TRACE: Preis-Abstand: ", preis_abstand, " Pips (Schwelle: ", PreisSchwelle, ")");
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
|
||||
|
||||
if(preis_abstand > PreisSchwelle && !preis_trigger_aktiv)
|
||||
{
|
||||
preis_trigger_aktiv = true;
|
||||
Print("TRACE: Preis-Trigger aktiviert: ", preis_abstand, " Pips");
|
||||
}
|
||||
|
||||
//--- EMA Steigung prüfen (Check EMA slope)
|
||||
double steigung = (ema_aktuell - ema_vorher) / _Point / pips_multiplier;
|
||||
|
||||
Print("TRACE: EMA Steigung: ", steigung, " Pips (Schwelle: ", SteigungSchwelle, ")");
|
||||
|
||||
if(MathAbs(steigung) > SteigungSchwelle && !steigung_trigger_aktiv)
|
||||
{
|
||||
steigung_trigger_aktiv = true;
|
||||
Print("TRACE: Steigungs-Trigger aktiviert: ", steigung, " Pips");
|
||||
}
|
||||
|
||||
//--- Überwachung starten wenn beide Trigger aktiv sind (Start monitoring when both triggers are active)
|
||||
if(preis_trigger_aktiv && steigung_trigger_aktiv && !überwachung_aktiv)
|
||||
{
|
||||
überwachung_aktiv = true;
|
||||
|
||||
if(UseBarData)
|
||||
{
|
||||
letzte_überwachung_zeit = iTime(_Symbol, Timeframe, 0); // Aktuelle Bar-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(letzte_überwachung_zeit), ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
letzte_überwachung_zeit = TimeCurrent(); // Aktuelle Tick-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Tick)");
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trade platzieren wenn Überwachung aktiv und Preis über/unter EMA (Place trade when monitoring active and price above/below EMA)
|
||||
if(überwachung_aktiv)
|
||||
{
|
||||
bool bullish_signal = aktueller_close > ema_aktuell;
|
||||
bool bearish_signal = aktueller_close < ema_aktuell;
|
||||
|
||||
Print("TRACE: Signal Check - Bullish: ", bullish_signal, " Bearish: ", bearish_signal);
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Differenz: ", aktueller_close - ema_aktuell);
|
||||
|
||||
//--- Trade-Limit prüfen (Check trade limit)
|
||||
if(trades_in_current_crossover >= MaxTradesPerCrossover)
|
||||
{
|
||||
Print("TRACE: Trade-Limit erreicht (", MaxTradesPerCrossover, ") - Kein neuer Trade");
|
||||
return;
|
||||
}
|
||||
|
||||
if(bullish_signal && !PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
|
||||
{
|
||||
Print("TRACE: Weekly ADX blockiert BUY-Entry");
|
||||
return;
|
||||
}
|
||||
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_BUY))
|
||||
{
|
||||
trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(bearish_signal && !PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
|
||||
{
|
||||
Print("TRACE: Weekly ADX blockiert SELL-Entry");
|
||||
return;
|
||||
}
|
||||
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_SELL))
|
||||
{
|
||||
trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
Print("TRACE: Position bereits offen - kein neuer Trade");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade platzieren (Place trade) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
Print("TRACE: Versuche Trade zu platzieren - Typ: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF");
|
||||
Print("TRACE: Lot: ", LotGröße);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
{
|
||||
success = trade.Buy(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
else
|
||||
{
|
||||
success = trade.Sell(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
ticket = (int)trade.ResultOrder();
|
||||
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", ticket);
|
||||
|
||||
//--- Trade-Öffnungszeit speichern (Save trade opening time)
|
||||
trade_open_time = iTime(_Symbol, Timeframe, 0);
|
||||
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(trade_open_time));
|
||||
|
||||
//--- Überwachung zurücksetzen (Reset monitoring)
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trades verwalten (Manage trades) |
|
||||
//+------------------------------------------------------------------+
|
||||
void VerwalteTrades()
|
||||
{
|
||||
if(!PositionSelectByMagic(_Symbol, MagicNumber))
|
||||
return;
|
||||
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_open_price = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
double current_price = PositionGetDouble(POSITION_PRICE_CURRENT);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
|
||||
double trailing_stop_pips = TrailingStop;
|
||||
|
||||
//--- Gleitender Stop (Trailing Stop) - nur wenn Position im Profit ist
|
||||
if(position_profit > 0) // Only apply trailing stop when in profit
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
double new_stop_loss = current_price - (trailing_stop_pips * _Point * pips_multiplier);
|
||||
double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
|
||||
// Only move stop loss if new stop is higher than current stop
|
||||
if(new_stop_loss > current_stop_loss)
|
||||
{
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
double new_stop_loss = current_price + (trailing_stop_pips * _Point * pips_multiplier);
|
||||
double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
|
||||
// Only move stop loss if new stop is lower than current stop
|
||||
if(new_stop_loss < current_stop_loss || current_stop_loss == 0)
|
||||
{
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
|
||||
if(ArraySize(ema_array) >= 1)
|
||||
{
|
||||
double aktueller_close = iClose(_Symbol, Timeframe, 0);
|
||||
double ema_aktuell = ema_array[0];
|
||||
bool exit_bullish = (position_type == POSITION_TYPE_SELL && aktueller_close > ema_aktuell);
|
||||
bool exit_bearish = (position_type == POSITION_TYPE_BUY && aktueller_close < ema_aktuell);
|
||||
|
||||
if(exit_bullish || exit_bearish)
|
||||
{
|
||||
Print("TRACE: Ausstiegssignal - Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
SchließePosition("EMA Crossover Exit");
|
||||
|
||||
Print("TRACE: Position geschlossen - Trade-Counter bleibt bei ", trades_in_current_crossover);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
|
||||
if(CloseUnprofitableTrades && trade_open_time != 0 && PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
|
||||
PrüfeProfitNachBars();
|
||||
}
|
||||
else if(!CloseUnprofitableTrades)
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeProfitNachBars()
|
||||
{
|
||||
if(!PositionSelectByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
return; // Keine Position offen
|
||||
}
|
||||
|
||||
datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
|
||||
int bars_since_trade_open = iBarShift(_Symbol, Timeframe, trade_open_time);
|
||||
|
||||
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ProfitCheckBars);
|
||||
|
||||
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
|
||||
if(bars_since_trade_open >= ProfitCheckBars)
|
||||
{
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
Print("TRACE: Profit-Prüfung nach ", ProfitCheckBars, " Bars");
|
||||
Print("TRACE: Position Profit: ", position_profit, " USD");
|
||||
|
||||
//--- Schließe Position wenn nicht im Profit (Close position if not in profit)
|
||||
if(position_profit <= 0)
|
||||
{
|
||||
Print("TRACE: Position nicht im Profit - Schließe Position");
|
||||
SchließePosition("Profit Check - Unprofitable");
|
||||
|
||||
//--- Trade-Öffnungszeit zurücksetzen (Reset trade opening time)
|
||||
trade_open_time = 0;
|
||||
Print("TRACE: Trade-Öffnungszeit zurückgesetzt");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Position im Profit - Behalte Position");
|
||||
//--- Trade-Öffnungszeit zurücksetzen um weitere Prüfungen zu vermeiden (Reset to avoid further checks)
|
||||
trade_open_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Stop Loss ändern (Modify Stop Loss) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ÄndereStopLoss(double new_stop_loss)
|
||||
{
|
||||
Print("TRACE: Versuche Stop Loss zu ändern auf: ", new_stop_loss);
|
||||
|
||||
bool success = ModifyPositionByMagic(trade, _Symbol, MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
|
||||
|
||||
if(success)
|
||||
{
|
||||
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Position schließen (Close position) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SchließePosition(string reason = "Unbekannt")
|
||||
{
|
||||
Print("TRACE: Versuche Position zu schließen - Grund: ", reason);
|
||||
|
||||
bool success = ClosePositionByMagic(trade, _Symbol, MagicNumber);
|
||||
|
||||
if(success)
|
||||
{
|
||||
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
@@ -0,0 +1,246 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MartingaleBTCUSD_Safe.mq5 |
|
||||
//| Classic martingale: double lot after loss, reset after win (BTC) |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Lab"
|
||||
#property version "1.01"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
input group "=== Market ==="
|
||||
input string InpSymbol = "BTCUSD";
|
||||
input ENUM_TIMEFRAMES InpTf = PERIOD_M15;
|
||||
input ulong InpMagic = 202604241;
|
||||
input int InpSlippagePts = 50;
|
||||
|
||||
input group "=== Martingale (classic) ==="
|
||||
input double InpBaseLots = 0.01;
|
||||
input double InpLotMultiplier = 2.0; // traditional = 2.0
|
||||
input int InpMaxDoublings = 16; // cap exponent (0..MaxDoublings); then lot stops growing
|
||||
|
||||
input group "=== Entry (RSI) ==="
|
||||
input int InpRsiPeriod = 14;
|
||||
input double InpRsiBuyBelow = 32.0;
|
||||
input double InpRsiSellAbove = 68.0;
|
||||
|
||||
input group "=== SL / TP (optional) ==="
|
||||
input bool InpUseSLTP = false;
|
||||
input double InpSLPts = 4000.0;
|
||||
input double InpTPPts = 3500.0;
|
||||
|
||||
CTrade g_trade;
|
||||
int g_hRsi = INVALID_HANDLE;
|
||||
int g_lossStreak = 0;
|
||||
ulong g_lastPosId = 0;
|
||||
|
||||
string WorkSym() { return InpSymbol; }
|
||||
double SymPoint() { return SymbolInfoDouble(WorkSym(), SYMBOL_POINT); }
|
||||
|
||||
void SetFilling()
|
||||
{
|
||||
const long fill = SymbolInfoInteger(WorkSym(), SYMBOL_FILLING_MODE);
|
||||
if((fill & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
|
||||
g_trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
else if((fill & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
|
||||
g_trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
}
|
||||
|
||||
double NetProfitForPositionId(const ulong posId)
|
||||
{
|
||||
if(posId == 0)
|
||||
return 0.0;
|
||||
const datetime to = TimeCurrent();
|
||||
if(!HistorySelect(0, to))
|
||||
return 0.0;
|
||||
double sum = 0.0;
|
||||
const int n = HistoryDealsTotal();
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
const ulong deal = HistoryDealGetTicket(i);
|
||||
if(deal == 0)
|
||||
continue;
|
||||
if((ulong)HistoryDealGetInteger(deal, DEAL_POSITION_ID) != posId)
|
||||
continue;
|
||||
sum += HistoryDealGetDouble(deal, DEAL_PROFIT);
|
||||
sum += HistoryDealGetDouble(deal, DEAL_SWAP);
|
||||
sum += HistoryDealGetDouble(deal, DEAL_COMMISSION);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
int OurPositionCount()
|
||||
{
|
||||
int c = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong t = PositionGetTicket(i);
|
||||
if(t == 0 || !PositionSelectByTicket(t))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != WorkSym())
|
||||
continue;
|
||||
if((ulong)PositionGetInteger(POSITION_MAGIC) == InpMagic)
|
||||
c++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
double LotsNow()
|
||||
{
|
||||
const int exp = MathMax(0, MathMin(g_lossStreak, InpMaxDoublings));
|
||||
double lot = InpBaseLots * MathPow(InpLotMultiplier, (double)exp);
|
||||
const double minLot = SymbolInfoDouble(WorkSym(), SYMBOL_VOLUME_MIN);
|
||||
const double maxLot = SymbolInfoDouble(WorkSym(), SYMBOL_VOLUME_MAX);
|
||||
const double stepLot = SymbolInfoDouble(WorkSym(), SYMBOL_VOLUME_STEP);
|
||||
if(stepLot > 0.0)
|
||||
lot = MathFloor(lot / stepLot) * stepLot;
|
||||
if(lot < minLot)
|
||||
lot = minLot;
|
||||
if(lot > maxLot)
|
||||
lot = maxLot;
|
||||
return NormalizeDouble(lot, 8);
|
||||
}
|
||||
|
||||
bool CopyRsi1(double &rsi1)
|
||||
{
|
||||
double buf[1];
|
||||
if(CopyBuffer(g_hRsi, 0, 1, 1, buf) != 1)
|
||||
return false;
|
||||
rsi1 = buf[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
void BuildSLTP(const bool isBuy, const double price, double &sl, double &tp)
|
||||
{
|
||||
sl = tp = 0.0;
|
||||
if(!InpUseSLTP)
|
||||
return;
|
||||
const double pt = SymPoint();
|
||||
if(pt <= 0.0)
|
||||
return;
|
||||
if(isBuy)
|
||||
{
|
||||
sl = price - InpSLPts * pt;
|
||||
tp = price + InpTPPts * pt;
|
||||
}
|
||||
else
|
||||
{
|
||||
sl = price + InpSLPts * pt;
|
||||
tp = price - InpTPPts * pt;
|
||||
}
|
||||
}
|
||||
|
||||
void OnClosedPosition()
|
||||
{
|
||||
const double net = NetProfitForPositionId(g_lastPosId);
|
||||
if(net < 0.0)
|
||||
g_lossStreak++;
|
||||
else
|
||||
g_lossStreak = 0;
|
||||
Print("Martingale: closed net=", net, " lossStreak=", g_lossStreak, " next lot=", LotsNow());
|
||||
g_lastPosId = 0;
|
||||
}
|
||||
|
||||
bool OurPositionOpenById(const ulong posId)
|
||||
{
|
||||
if(posId == 0)
|
||||
return false;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong t = PositionGetTicket(i);
|
||||
if(t == 0 || !PositionSelectByTicket(t))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != WorkSym())
|
||||
continue;
|
||||
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
if((ulong)PositionGetInteger(POSITION_IDENTIFIER) == posId)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CaptureLastPositionId()
|
||||
{
|
||||
Sleep(20);
|
||||
for(int k = 0; k < PositionsTotal(); k++)
|
||||
{
|
||||
const ulong t = PositionGetTicket(k);
|
||||
if(t == 0 || !PositionSelectByTicket(t))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != WorkSym())
|
||||
continue;
|
||||
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
g_lastPosId = (ulong)PositionGetInteger(POSITION_IDENTIFIER);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
if(InpBaseLots <= 0.0 || InpLotMultiplier < 1.0 || InpMaxDoublings < 0)
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
if(!SymbolSelect(WorkSym(), true))
|
||||
Print("Martingale: SymbolSelect note ", WorkSym());
|
||||
g_hRsi = iRSI(WorkSym(), InpTf, InpRsiPeriod, PRICE_CLOSE);
|
||||
if(g_hRsi == INVALID_HANDLE)
|
||||
return INIT_FAILED;
|
||||
g_trade.SetExpertMagicNumber(InpMagic);
|
||||
g_trade.SetDeviationInPoints(InpSlippagePts);
|
||||
SetFilling();
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_hRsi != INVALID_HANDLE)
|
||||
IndicatorRelease(g_hRsi);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(_Symbol != WorkSym())
|
||||
return;
|
||||
|
||||
if(g_lastPosId != 0 && !OurPositionOpenById(g_lastPosId))
|
||||
OnClosedPosition();
|
||||
|
||||
static datetime lastBar = 0;
|
||||
const datetime tb = iTime(WorkSym(), InpTf, 0);
|
||||
if(tb == 0 || tb == lastBar)
|
||||
return;
|
||||
lastBar = tb;
|
||||
|
||||
if(OurPositionCount() > 0)
|
||||
return;
|
||||
|
||||
double rsi1 = 0.0;
|
||||
if(!CopyRsi1(rsi1))
|
||||
return;
|
||||
|
||||
const double lot = LotsNow();
|
||||
if(lot <= 0.0)
|
||||
return;
|
||||
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(WorkSym(), tick))
|
||||
return;
|
||||
|
||||
double sl = 0.0, tp = 0.0;
|
||||
const bool wantBuy = (rsi1 <= InpRsiBuyBelow);
|
||||
const bool wantSell = (rsi1 >= InpRsiSellAbove);
|
||||
|
||||
if(wantBuy && !wantSell)
|
||||
{
|
||||
BuildSLTP(true, tick.ask, sl, tp);
|
||||
if(g_trade.Buy(lot, WorkSym(), tick.ask, sl, tp, "Martingale buy"))
|
||||
CaptureLastPositionId();
|
||||
}
|
||||
else if(wantSell && !wantBuy)
|
||||
{
|
||||
BuildSLTP(false, tick.bid, sl, tp);
|
||||
if(g_trade.Sell(lot, WorkSym(), tick.bid, sl, tp, "Martingale sell"))
|
||||
CaptureLastPositionId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,367 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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.01"
|
||||
|
||||
#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 = 38.0; // allow more bars (was 29 — very few on BTC)
|
||||
input bool UseATRRatioFilter = true;
|
||||
input int ATR_Period = 8;
|
||||
input int ATR_SMA_Period = 35;
|
||||
input double ATR_Ratio_Max = 1.55; // slightly looser vs 1.36
|
||||
input bool UseFlatEMAFilter = true;
|
||||
input int EMA_Fast = 13;
|
||||
input int EMA_Slow = 17;
|
||||
input double EMA_Separation_MaxPct = 0.42; // %; was 0.26 — very strict on crypto
|
||||
|
||||
//--- RSI entries (fade extremes toward mean)
|
||||
input group "=== RSI entries ==="
|
||||
input int RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RSI_Price = PRICE_CLOSE; // OPEN made crosses rarer; CLOSE is standard
|
||||
input double RSI_Oversold = 28.0;
|
||||
input double RSI_Overbought = 68.0;
|
||||
input bool UseStrictRsiCross = false; // true = exact cross; false = looser bounce (more trades)
|
||||
input double RsiCrossSlack = 4.0; // only if !UseStrictRsiCross: widen cross band
|
||||
|
||||
//--- 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 = 0; // 0 = off (BTC tester/live often blocked at 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 CopyAtShift(int handle, const int shift, double &v)
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
if(CopyBuffer(handle, 0, shift, 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()
|
||||
{
|
||||
const int sh = 1;
|
||||
double adx = 0;
|
||||
if(!CopyAtShift(h_adx, sh, adx))
|
||||
return false;
|
||||
if(adx >= ADX_Max)
|
||||
return false;
|
||||
|
||||
if(UseATRRatioFilter)
|
||||
{
|
||||
double atrArr[];
|
||||
ArraySetAsSeries(atrArr, true);
|
||||
if(CopyBuffer(h_atr, 0, sh, 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, sh, 1, ef) < 1) return false;
|
||||
if(CopyBuffer(h_ema_slow, 0, sh, 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)
|
||||
{
|
||||
if(UseStrictRsiCross)
|
||||
return (twoAgo <= RSI_Oversold && prev > RSI_Oversold);
|
||||
const double lo = RSI_Oversold - RsiCrossSlack;
|
||||
const double hi = RSI_Oversold + RsiCrossSlack;
|
||||
return (twoAgo <= hi && prev > lo && prev > twoAgo);
|
||||
}
|
||||
|
||||
bool Entry_SellCross(double twoAgo, double prev)
|
||||
{
|
||||
if(UseStrictRsiCross)
|
||||
return (twoAgo >= RSI_Overbought && prev < RSI_Overbought);
|
||||
const double lo = RSI_Overbought - RsiCrossSlack;
|
||||
const double hi = RSI_Overbought + RsiCrossSlack;
|
||||
return (twoAgo >= lo && prev < hi && prev < twoAgo);
|
||||
}
|
||||
|
||||
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,38 @@
|
||||
; RSIConsolidation.mq5 v1.01 — BTCUSD preset (matches relaxed defaults)
|
||||
; Strategy Tester → Inputs → Load
|
||||
;
|
||||
; === Symbol & session ===
|
||||
InpSymbol=BTCUSD
|
||||
; === Timeframe & bar logic ===
|
||||
SignalTF=15||15||0||15||N
|
||||
EntryOnNewBarOnly=true||false||0||true||N
|
||||
; === Regime: consolidation (anti-trend) ===
|
||||
ADX_Period=23||10||1||40||Y
|
||||
ADX_Max=38.0||22.0||1.0||50.0||Y
|
||||
UseATRRatioFilter=true||false||0||true||N
|
||||
ATR_Period=8||5||1||21||Y
|
||||
ATR_SMA_Period=35||14||2||80||Y
|
||||
ATR_Ratio_Max=1.55||1.0||0.02||2.0||Y
|
||||
UseFlatEMAFilter=true||false||0||true||N
|
||||
EMA_Fast=13||5||1||21||Y
|
||||
EMA_Slow=17||10||1||34||Y
|
||||
EMA_Separation_MaxPct=0.42||0.10||0.02||0.70||Y
|
||||
; === RSI entries ===
|
||||
RSI_Period=8||5||1||21||Y
|
||||
RSI_Price=0||0||0||7||Y
|
||||
RSI_Oversold=28.0||18.0||1.0||42.0||Y
|
||||
RSI_Overbought=68.0||55.0||1.0||82.0||Y
|
||||
UseStrictRsiCross=false||false||0||true||Y
|
||||
RsiCrossSlack=4.0||0.0||0.5||12.0||Y
|
||||
; === Exits ===
|
||||
UseRSI_MeanExit=true||false||0||true||N
|
||||
RSI_Exit_Long=48.0||40.0||1.0||55.0||Y
|
||||
RSI_Exit_Short=52.0||45.0||1.0||60.0||Y
|
||||
SL_ATR_Mult=2.15||1.0||0.05||3.5||Y
|
||||
TP_ATR_Mult=2.40||1.2||0.05||4.0||Y
|
||||
MaxBarsInTrade=54||20||2||120||Y
|
||||
; === Risk & execution ===
|
||||
Lots=0.1||0.01||0.01||0.50||N
|
||||
MagicNumber=20250420||20250420||1||20250420||N
|
||||
Slippage=30||20||5||200||N
|
||||
MaxSpreadPoints=0||0||1||400||Y
|
||||
Binary file not shown.
@@ -0,0 +1,384 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| rsiDivergence.mq5 |
|
||||
//| Lab EA: RSI divergence + EMA distance filter |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Lab"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
input group "=== Market ==="
|
||||
input string InpSymbol = "";
|
||||
input ENUM_TIMEFRAMES InpTf = PERIOD_CURRENT;
|
||||
input double InpLots = 0.01;
|
||||
input ulong InpMagic = 202604231;
|
||||
input int InpSlippagePts = 30;
|
||||
input int InpMaxPositions = 1;
|
||||
input bool InpRequireFlat = true; // no new entry while any position with this magic exists
|
||||
|
||||
input group "=== Indicators ==="
|
||||
input int InpRsiPeriod = 14;
|
||||
input int InpEmaPeriod = 200;
|
||||
|
||||
input group "=== Swing / divergence ==="
|
||||
input int InpPivotRadius = 2; // bars each side; pivot confirms after radius closes
|
||||
input int InpSwingLookback = 80; // search swings within [radius+1 .. lookback]
|
||||
input int InpMinPivotGap = 3; // min bars between the two swings used for a pair
|
||||
|
||||
input group "=== EMA distance (entry filter) ==="
|
||||
input double InpMinEmaDistPtsBuy = 80.0; // buy: (EMA - close) / _Point >= this on signal bar
|
||||
input double InpMinEmaDistPtsSell = 80.0; // sell: (close - EMA) / _Point >= this
|
||||
|
||||
input group "=== Risk ==="
|
||||
input bool InpUseSLTP = true; // off = naked positions (can run years until margin stop)
|
||||
input double InpSLPts = 500.0; // points; tune per symbol (_Point)
|
||||
input double InpTPPts = 1000.0;
|
||||
input int InpMaxHoldBars = 0; // 0=off; else close position after this many bars open (signal TF)
|
||||
|
||||
CTrade g_trade;
|
||||
int g_hRsi = INVALID_HANDLE;
|
||||
int g_hEma = INVALID_HANDLE;
|
||||
datetime g_lastBar = 0;
|
||||
datetime g_lastBuyPivotNew = 0;
|
||||
datetime g_lastBuyPivotOld = 0;
|
||||
datetime g_lastSellPivotNew = 0;
|
||||
datetime g_lastSellPivotOld = 0;
|
||||
|
||||
string WorkSymbol()
|
||||
{
|
||||
if(StringLen(InpSymbol) > 0)
|
||||
return InpSymbol;
|
||||
return _Symbol;
|
||||
}
|
||||
|
||||
ENUM_TIMEFRAMES WorkTf()
|
||||
{
|
||||
if(InpTf == PERIOD_CURRENT)
|
||||
return (ENUM_TIMEFRAMES)_Period;
|
||||
return InpTf;
|
||||
}
|
||||
|
||||
void SetFilling()
|
||||
{
|
||||
const long fill = SymbolInfoInteger(WorkSymbol(), SYMBOL_FILLING_MODE);
|
||||
if((fill & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
|
||||
g_trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
else if((fill & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
|
||||
g_trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
}
|
||||
|
||||
bool CopyRsiEma(const int bars, double &rsi[], double &ema[])
|
||||
{
|
||||
ArrayResize(rsi, bars);
|
||||
ArrayResize(ema, bars);
|
||||
ArraySetAsSeries(rsi, true);
|
||||
ArraySetAsSeries(ema, true);
|
||||
if(CopyBuffer(g_hRsi, 0, 0, bars, rsi) < bars)
|
||||
return false;
|
||||
if(CopyBuffer(g_hEma, 0, 0, bars, ema) < bars)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsSwingLow(const int s, const int r)
|
||||
{
|
||||
if(s < r + 1)
|
||||
return false;
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
double lv = iLow(sym, tf, s);
|
||||
for(int k = -r; k <= r; k++)
|
||||
{
|
||||
if(k == 0)
|
||||
continue;
|
||||
if(iLow(sym, tf, s + k) <= lv)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsSwingHigh(const int s, const int r)
|
||||
{
|
||||
if(s < r + 1)
|
||||
return false;
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
double hv = iHigh(sym, tf, s);
|
||||
for(int k = -r; k <= r; k++)
|
||||
{
|
||||
if(k == 0)
|
||||
continue;
|
||||
if(iHigh(sym, tf, s + k) >= hv)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CollectSwingLows(int &outSwings[], const int r, const int lookback)
|
||||
{
|
||||
ArrayResize(outSwings, 0);
|
||||
const int from = r + 1;
|
||||
if(lookback <= from)
|
||||
return false;
|
||||
for(int s = from; s <= lookback; s++)
|
||||
{
|
||||
if(!IsSwingLow(s, r))
|
||||
continue;
|
||||
int n = ArraySize(outSwings);
|
||||
ArrayResize(outSwings, n + 1);
|
||||
outSwings[n] = s;
|
||||
}
|
||||
return ArraySize(outSwings) >= 2;
|
||||
}
|
||||
|
||||
bool CollectSwingHighs(int &outSwings[], const int r, const int lookback)
|
||||
{
|
||||
ArrayResize(outSwings, 0);
|
||||
const int from = r + 1;
|
||||
if(lookback <= from)
|
||||
return false;
|
||||
for(int s = from; s <= lookback; s++)
|
||||
{
|
||||
if(!IsSwingHigh(s, r))
|
||||
continue;
|
||||
int n = ArraySize(outSwings);
|
||||
ArrayResize(outSwings, n + 1);
|
||||
outSwings[n] = s;
|
||||
}
|
||||
return ArraySize(outSwings) >= 2;
|
||||
}
|
||||
|
||||
void SortSwingsAscending(int &sw[])
|
||||
{
|
||||
int n = ArraySize(sw);
|
||||
for(int i = 0; i < n - 1; i++)
|
||||
for(int j = i + 1; j < n; j++)
|
||||
if(sw[i] > sw[j])
|
||||
{
|
||||
int t = sw[i];
|
||||
sw[i] = sw[j];
|
||||
sw[j] = t;
|
||||
}
|
||||
}
|
||||
|
||||
bool BullishDivergence(const double &rsi[], const int r, const int lookback, int &sNew, int &sOld)
|
||||
{
|
||||
int swings[];
|
||||
if(!CollectSwingLows(swings, r, lookback))
|
||||
return false;
|
||||
SortSwingsAscending(swings);
|
||||
const int n = ArraySize(swings);
|
||||
sNew = swings[0];
|
||||
sOld = swings[1];
|
||||
if(sOld - sNew < InpMinPivotGap)
|
||||
return false;
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
const double lowNew = iLow(sym, tf, sNew);
|
||||
const double lowOld = iLow(sym, tf, sOld);
|
||||
if(lowNew >= lowOld)
|
||||
return false;
|
||||
if(rsi[sNew] <= rsi[sOld])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BearishDivergence(const double &rsi[], const int r, const int lookback, int &sNew, int &sOld)
|
||||
{
|
||||
int swings[];
|
||||
if(!CollectSwingHighs(swings, r, lookback))
|
||||
return false;
|
||||
SortSwingsAscending(swings);
|
||||
sNew = swings[0];
|
||||
sOld = swings[1];
|
||||
if(sOld - sNew < InpMinPivotGap)
|
||||
return false;
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
const double hiNew = iHigh(sym, tf, sNew);
|
||||
const double hiOld = iHigh(sym, tf, sOld);
|
||||
if(hiNew <= hiOld)
|
||||
return false;
|
||||
if(rsi[sNew] >= rsi[sOld])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EmaDistanceBuyOk(const double &ema[], const int barShift)
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
const double c = iClose(sym, tf, barShift);
|
||||
if(c <= 0.0 || ema[barShift] <= 0.0)
|
||||
return false;
|
||||
const double pts = (ema[barShift] - c) / _Point;
|
||||
return (pts >= InpMinEmaDistPtsBuy);
|
||||
}
|
||||
|
||||
bool EmaDistanceSellOk(const double &ema[], const int barShift)
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
const double c = iClose(sym, tf, barShift);
|
||||
if(c <= 0.0 || ema[barShift] <= 0.0)
|
||||
return false;
|
||||
const double pts = (c - ema[barShift]) / _Point;
|
||||
return (pts >= InpMinEmaDistPtsSell);
|
||||
}
|
||||
|
||||
int CountOurPositions()
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
int c = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != sym)
|
||||
continue;
|
||||
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
c++;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
void ManageMaxHoldBars()
|
||||
{
|
||||
if(InpMaxHoldBars <= 0)
|
||||
return;
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != sym)
|
||||
continue;
|
||||
if((ulong)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
|
||||
const datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
const int sh = iBarShift(sym, tf, openTime);
|
||||
if(sh < 0)
|
||||
continue;
|
||||
if(sh >= InpMaxHoldBars)
|
||||
g_trade.PositionClose(ticket);
|
||||
}
|
||||
}
|
||||
|
||||
void BuildSLTP(const bool isBuy, const double price, double &sl, double &tp)
|
||||
{
|
||||
sl = tp = 0.0;
|
||||
if(!InpUseSLTP)
|
||||
return;
|
||||
if(isBuy)
|
||||
{
|
||||
sl = price - InpSLPts * _Point;
|
||||
tp = price + InpTPPts * _Point;
|
||||
}
|
||||
else
|
||||
{
|
||||
sl = price + InpSLPts * _Point;
|
||||
tp = price - InpTPPts * _Point;
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
if(!SymbolSelect(sym, true))
|
||||
Print("rsiDivergence: SymbolSelect note for ", sym);
|
||||
|
||||
g_hRsi = iRSI(sym, tf, InpRsiPeriod, PRICE_CLOSE);
|
||||
g_hEma = iMA(sym, tf, InpEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(g_hRsi == INVALID_HANDLE || g_hEma == INVALID_HANDLE)
|
||||
return INIT_FAILED;
|
||||
|
||||
g_trade.SetExpertMagicNumber(InpMagic);
|
||||
g_trade.SetDeviationInPoints(InpSlippagePts);
|
||||
SetFilling();
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_hRsi != INVALID_HANDLE) IndicatorRelease(g_hRsi);
|
||||
if(g_hEma != INVALID_HANDLE) IndicatorRelease(g_hEma);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
const ENUM_TIMEFRAMES tf = WorkTf();
|
||||
|
||||
ManageMaxHoldBars();
|
||||
|
||||
datetime t = iTime(sym, tf, 0);
|
||||
if(t == 0 || t == g_lastBar)
|
||||
return;
|
||||
g_lastBar = t;
|
||||
|
||||
const int need = InpSwingLookback + InpPivotRadius + 5;
|
||||
double rsi[], ema[];
|
||||
if(!CopyRsiEma(need, rsi, ema))
|
||||
return;
|
||||
|
||||
const int openN = CountOurPositions();
|
||||
if(openN >= InpMaxPositions)
|
||||
return;
|
||||
if(InpRequireFlat && openN > 0)
|
||||
return;
|
||||
|
||||
const int r = MathMax(1, InpPivotRadius);
|
||||
const int lb = MathMax(r + 3, InpSwingLookback);
|
||||
|
||||
int sNew = 0, sOld = 0;
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(sym, tick))
|
||||
return;
|
||||
|
||||
if(BullishDivergence(rsi, r, lb, sNew, sOld) && EmaDistanceBuyOk(ema, 1))
|
||||
{
|
||||
const datetime tPivotNew = iTime(sym, tf, sNew);
|
||||
const datetime tPivotOld = iTime(sym, tf, sOld);
|
||||
if(tPivotNew == 0 || tPivotOld == 0)
|
||||
return;
|
||||
if(tPivotNew == g_lastBuyPivotNew && tPivotOld == g_lastBuyPivotOld)
|
||||
return;
|
||||
|
||||
double sl, tp;
|
||||
BuildSLTP(true, tick.ask, sl, tp);
|
||||
if(g_trade.Buy(InpLots, sym, tick.ask, sl, tp, "RSI div+EMA buy"))
|
||||
{
|
||||
g_lastBuyPivotNew = tPivotNew;
|
||||
g_lastBuyPivotOld = tPivotOld;
|
||||
Print("Buy RSI div: swings ", sOld, "->", sNew, " pivots ", TimeToString(tPivotOld), " -> ", TimeToString(tPivotNew));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(BearishDivergence(rsi, r, lb, sNew, sOld) && EmaDistanceSellOk(ema, 1))
|
||||
{
|
||||
const datetime tPivotNew = iTime(sym, tf, sNew);
|
||||
const datetime tPivotOld = iTime(sym, tf, sOld);
|
||||
if(tPivotNew == 0 || tPivotOld == 0)
|
||||
return;
|
||||
if(tPivotNew == g_lastSellPivotNew && tPivotOld == g_lastSellPivotOld)
|
||||
return;
|
||||
|
||||
double sl, tp;
|
||||
BuildSLTP(false, tick.bid, sl, tp);
|
||||
if(g_trade.Sell(InpLots, sym, tick.bid, sl, tp, "RSI div+EMA sell"))
|
||||
{
|
||||
g_lastSellPivotNew = tPivotNew;
|
||||
g_lastSellPivotOld = tPivotOld;
|
||||
Print("Sell RSI div: swings ", sOld, "->", sNew, " pivots ", TimeToString(tPivotOld), " -> ", TimeToString(tPivotNew));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
; rsiDivergence.mq5 — XAUUSD test / optimization preset
|
||||
; Strategy Tester → Inputs → Load. If your server lists "XAU" not "XAUUSD", change InpSymbol only.
|
||||
;
|
||||
; value||start||step||stop||Y|N (MT5 convention)
|
||||
; InpTf: 0 = chart timeframe (PERIOD_CURRENT). Attach EA to XAUUSD chart at desired TF (e.g. H1).
|
||||
;
|
||||
; === Market ===
|
||||
InpSymbol=XAUUSD
|
||||
InpTf=0||0||0||49153||N
|
||||
InpLots=0.01||0.01||0.01||0.20||N
|
||||
InpMagic=930204||930204||1||9302040||N
|
||||
InpSlippagePts=80||30||10||400||N
|
||||
InpMaxPositions=1||1||1||4||N
|
||||
InpRequireFlat=true||false||0||true||N
|
||||
; === Indicators ===
|
||||
InpRsiPeriod=14||7||1||28||Y
|
||||
InpEmaPeriod=200||50||10||400||Y
|
||||
; === Swing / divergence ===
|
||||
InpPivotRadius=2||2||1||8||N
|
||||
InpSwingLookback=120||60||10||300||Y
|
||||
InpMinPivotGap=3||2||1||15||Y
|
||||
; === EMA distance (entry filter) — gold: wider point ranges than FX ===
|
||||
InpMinEmaDistPtsBuy=200.0||50.0||25.0||2500.0||Y
|
||||
InpMinEmaDistPtsSell=200.0||50.0||25.0||2500.0||Y
|
||||
; === Risk ===
|
||||
InpUseSLTP=true||false||0||true||N
|
||||
InpSLPts=800.0||300.0||50.0||5000.0||Y
|
||||
InpTPPts=1600.0||500.0||100.0||10000.0||Y
|
||||
InpMaxHoldBars=48||0||8||240||Y
|
||||
Reference in New Issue
Block a user