UpDATE
This commit is contained in:
@@ -0,0 +1,628 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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.01"
|
||||
#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 bool UseTrailingStop = true; // Gleitenden Stop anwenden
|
||||
input double TrailingActivationPips = 0.0; // Mindestgewinn in Pips bis Trail startet (0 = Konto-Profit>0)
|
||||
input bool UseStaleStopLossExit = false; // schließen wenn SL zu lange nicht angepasst wurde
|
||||
input int StaleStopLossSeconds = 33800; // Sekunden ohne SL-Änderung -> Close (0 = aus)
|
||||
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 = 3; // Maximale Trades pro Crossover-Ereignis
|
||||
input int ProfitCheckBars = 11; // 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
|
||||
datetime g_last_sl_adjust_success_time = 0; // letzte erfolgreiche SL-Verschiebung (Stale-Exit)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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()
|
||||
{
|
||||
static datetime last_bar_time = 0;
|
||||
const datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
|
||||
const bool new_bar = (current_bar_time != last_bar_time);
|
||||
const bool has_position = PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
|
||||
// Offene Positionen: Management jeden Tick (Trailing / Stale-SL). Sonst bei Bar-Modus nur neuer Bar.
|
||||
if(UseBarData)
|
||||
{
|
||||
if(!new_bar && !has_position)
|
||||
return;
|
||||
if(new_bar)
|
||||
last_bar_time = current_bar_time;
|
||||
}
|
||||
|
||||
//--- EMA Werte berechnen (Calculate EMA values)
|
||||
BerechneEMA();
|
||||
|
||||
const bool run_signals = (!UseBarData || new_bar);
|
||||
|
||||
//--- Debug / Überwachung / Entry nur bei neuem Bar (Bar-Modus) oder jeden Tick (Tick-Modus)
|
||||
if(run_signals && 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, (ulong)MagicNumber));
|
||||
Print("Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
|
||||
Print("==================");
|
||||
}
|
||||
|
||||
if(run_signals)
|
||||
{
|
||||
//--- Überwachung prüfen (Check monitoring)
|
||||
if(überwachung_aktiv)
|
||||
{
|
||||
if(UseBarData)
|
||||
{
|
||||
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
|
||||
{
|
||||
if(TimeCurrent() - letzte_überwachung_zeit > ÜberwachungTimeout)
|
||||
{
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrüfeTrigger();
|
||||
}
|
||||
|
||||
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, (ulong)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, (ulong)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, (ulong)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);
|
||||
g_last_sl_adjust_success_time = 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;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Mindestgewinn fuer Trailing erreicht? |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TrailingActivationReached(const double position_profit, const ENUM_POSITION_TYPE position_type,
|
||||
const double pips_multiplier)
|
||||
{
|
||||
if(TrailingActivationPips <= 0.0)
|
||||
return (position_profit > 0.0);
|
||||
|
||||
const double open_px = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
return ((bid - open_px) / _Point / pips_multiplier >= TrailingActivationPips);
|
||||
}
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
return ((open_px - ask) / _Point / pips_multiplier >= TrailingActivationPips);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trades verwalten (Manage trades) |
|
||||
//+------------------------------------------------------------------+
|
||||
void VerwalteTrades()
|
||||
{
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
if(UseStaleStopLossExit && StaleStopLossSeconds > 0)
|
||||
{
|
||||
const datetime stale_ref = (g_last_sl_adjust_success_time > 0)
|
||||
? g_last_sl_adjust_success_time
|
||||
: (datetime)PositionGetInteger(POSITION_TIME);
|
||||
if(TimeCurrent() - stale_ref >= StaleStopLossSeconds)
|
||||
{
|
||||
SchließePosition("Stale stop loss - keine SL-Anpassung");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
|
||||
const double trail_dist = TrailingStop * _Point * pips_multiplier;
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * _Point;
|
||||
|
||||
//--- Gleitender Stop (Trailing Stop)
|
||||
if(UseTrailingStop && TrailingStop > 0.0 && TrailingActivationReached(position_profit, position_type, pips_multiplier))
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double new_stop_loss = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_stop_loss < min_dist)
|
||||
new_stop_loss = NormalizeDouble(bid - min_dist, digits);
|
||||
const double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
if(new_stop_loss < bid && new_stop_loss > 0.0 && new_stop_loss > current_stop_loss)
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double new_stop_loss = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_stop_loss - ask < min_dist)
|
||||
new_stop_loss = NormalizeDouble(ask + min_dist, digits);
|
||||
const double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
if(new_stop_loss > ask && new_stop_loss > 0.0 &&
|
||||
(new_stop_loss < current_stop_loss || current_stop_loss == 0.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, (ulong)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, (ulong)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, (ulong)MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
|
||||
|
||||
if(success)
|
||||
{
|
||||
g_last_sl_adjust_success_time = TimeCurrent();
|
||||
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, (ulong)MagicNumber);
|
||||
|
||||
if(success)
|
||||
{
|
||||
g_last_sl_adjust_success_time = 0;
|
||||
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,581 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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.01"
|
||||
|
||||
#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 = 90; // RSI Overbought Level
|
||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 88; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 48; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 6; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 0.1; // Lot Size
|
||||
input int MagicNumber = 123459123; // 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
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
|
||||
input double TrailingStopDistancePoints = 120.0; // SL distance from bid/ask (points)
|
||||
input double TrailingActivationPoints = 0.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
//--- 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;
|
||||
|
||||
void ResetPositionTracking();
|
||||
void SyncTrackedPosition();
|
||||
double ATRPriceOnTF(const int period);
|
||||
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr);
|
||||
void TryReversalEscape();
|
||||
void ApplyTrailingStop();
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a new bar
|
||||
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
bool is_new_bar = (current_bar_time != last_bar_time);
|
||||
bool in_position = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
|
||||
// While flat, process only on new bars. While in position, allow intrabar reversal escape checks.
|
||||
if(!in_position && !is_new_bar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update RSI values
|
||||
if(!UpdateRSI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(in_position && UseReversalEscape)
|
||||
{
|
||||
TryReversalEscape();
|
||||
}
|
||||
|
||||
if(in_position && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
if(!is_new_bar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Keep local tracking aligned with actual terminal positions for this symbol/magic.
|
||||
SyncTrackedPosition();
|
||||
|
||||
// Check for existing position
|
||||
CheckExistingPosition();
|
||||
|
||||
// Check for new entry signals - only if no position exists for THIS EA (magic number) on THIS symbol
|
||||
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 rates[];
|
||||
if(CopyRates(_Symbol, TimeFrame, 0, 4, rates) >= 4)
|
||||
{
|
||||
ArraySetAsSeries(rates, true);
|
||||
const double body = MathAbs(rates[1].close - rates[1].open);
|
||||
if(body >= ReversalBodyAtrMult * atr)
|
||||
{
|
||||
if(ptype == POSITION_TYPE_BUY && rates[1].close < rates[1].open)
|
||||
signs++;
|
||||
else if(ptype == POSITION_TYPE_SELL && rates[1].close > rates[1].open)
|
||||
signs++;
|
||||
}
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(rates[1].close < rates[2].close && rates[2].close < rates[3].close)
|
||||
signs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rates[1].close > rates[2].close && rates[2].close > rates[3].close)
|
||||
signs++;
|
||||
}
|
||||
}
|
||||
|
||||
return signs;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Cut losers fast on violent reversals (evaluated every tick) |
|
||||
//+------------------------------------------------------------------+
|
||||
void TryReversalEscape()
|
||||
{
|
||||
ulong live_ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(live_ticket == 0)
|
||||
return;
|
||||
if(!PositionSelectByTicketSymbolAndMagic(live_ticket, _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 signs = CountReversalEscapeSigns(ptype, atr);
|
||||
if(signs < ReversalSignsRequired)
|
||||
return;
|
||||
|
||||
ClosePosition();
|
||||
Print("RSIScalpingBTCUSD: reversal escape signs=", signs, " need=", ReversalSignsRequired,
|
||||
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reset local position tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetPositionTracking()
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sync local state with real position in terminal |
|
||||
//+------------------------------------------------------------------+
|
||||
void SyncTrackedPosition()
|
||||
{
|
||||
ulong live_ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(live_ticket == 0)
|
||||
{
|
||||
ResetPositionTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we were not tracking (or ticket changed), start tracking the live position.
|
||||
if(!position_open || position_ticket != (int)live_ticket)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = true;
|
||||
position_ticket = (int)live_ticket;
|
||||
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPosition()
|
||||
{
|
||||
if(!position_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if position still exists with correct magic number AND symbol for THIS EA
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
ResetPositionTracking();
|
||||
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()
|
||||
{
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
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)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
bool position_exists_before_close = PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(!position_exists_before_close)
|
||||
{
|
||||
ResetPositionTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
ResetPositionTracking();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep tracking when close fails (e.g. market closed); retry on next bar.
|
||||
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
ResetPositionTracking();
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 8.2 KiB |
@@ -0,0 +1,28 @@
|
||||
; saved on 2026.02.07
|
||||
; Genetic Algorithm Optimization Parameters for RSIScalpingNVDA
|
||||
; Recommended ranges for profitable parameter discovery
|
||||
;
|
||||
; Format: Parameter=Start||Step||Min||Max||Optimize(Y/N)
|
||||
;
|
||||
; NOTE: Current values show RSI_Overbought=19 and RSI_Oversold=50 which are unusual.
|
||||
; This config uses STANDARD RSI ranges (60-85 overbought, 15-40 oversold).
|
||||
; If your current values are intentional, use the alternative ranges in OPTIMIZATION_GUIDE.md
|
||||
;
|
||||
; === PHASE 1: CORE RSI PARAMETERS (Primary Optimization) ===
|
||||
RSI_Period=14||1||7||21||Y
|
||||
RSI_Overbought=70.0||2.0||60.0||85.0||Y
|
||||
RSI_Oversold=30.0||2.0||15.0||40.0||Y
|
||||
RSI_Target_Buy=75.0||2.0||65.0||90.0||Y
|
||||
RSI_Target_Sell=25.0||2.0||10.0||35.0||Y
|
||||
|
||||
; === PHASE 2: RISK MANAGEMENT (Secondary Optimization) ===
|
||||
BarsToWait=2||1||1||8||Y
|
||||
TimeFrame=16387||0||16385||16390||Y
|
||||
|
||||
; === PHASE 3: POSITION SIZING (Optimize with caution) ===
|
||||
LotSize=50.0||5.0||10.0||100.0||Y
|
||||
|
||||
; === FIXED PARAMETERS (Do Not Optimize) ===
|
||||
RSI_Applied_Price=1||0||1||1||N
|
||||
MagicNumber=12345||0||12345||12345||N
|
||||
Slippage=3||0||3||3||N
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
; saved on 2026.02.07
|
||||
; Alternative Genetic Algorithm Optimization - Respects Current Unusual RSI Values
|
||||
; Use this if RSI_Overbought=19 and RSI_Oversold=50 are intentional
|
||||
;
|
||||
; Format: Parameter=Start||Step||Min||Max||Optimize(Y/N)
|
||||
;
|
||||
; === PHASE 1: CORE RSI PARAMETERS ===
|
||||
RSI_Period=14||1||7||21||Y
|
||||
RSI_Overbought=19.0||1.0||15.0||30.0||Y
|
||||
RSI_Oversold=50.0||2.0||40.0||60.0||Y
|
||||
RSI_Target_Buy=71.0||2.0||65.0||80.0||Y
|
||||
RSI_Target_Sell=70.0||2.0||60.0||75.0||Y
|
||||
|
||||
; === PHASE 2: RISK MANAGEMENT ===
|
||||
BarsToWait=1||1||1||8||Y
|
||||
TimeFrame=16387||0||16385||16390||Y
|
||||
|
||||
; === PHASE 3: POSITION SIZING ===
|
||||
LotSize=50.0||5.0||10.0||100.0||Y
|
||||
|
||||
; === FIXED PARAMETERS ===
|
||||
RSI_Applied_Price=1||0||1||1||N
|
||||
MagicNumber=12345||0||12345||12345||N
|
||||
Slippage=3||0||3||3||N
|
||||
@@ -0,0 +1,134 @@
|
||||
# Genetic Algorithm Optimization Guide for RSIScalpingNVDA
|
||||
|
||||
## Recommended Optimization Strategy
|
||||
|
||||
### Phase 1: Core RSI Parameters (Primary Focus)
|
||||
These parameters directly control entry/exit signals and should be optimized first.
|
||||
|
||||
#### **RSI_Period** (Y - Optimize)
|
||||
- **Current**: 14
|
||||
- **Recommended Range**: 7-21
|
||||
- **Step**: 1
|
||||
- **Rationale**: Standard RSI periods. Shorter = more sensitive, longer = smoother signals
|
||||
|
||||
#### **RSI_Overbought** (Y - Optimize)
|
||||
- **Current**: 19.0 (unusually low - verify if this is correct)
|
||||
- **Standard Range**: 60.0-85.0
|
||||
- **Step**: 2.0
|
||||
- **Alternative Range** (if current is intentional): 15.0-30.0
|
||||
- **Rationale**: Level where RSI indicates overbought condition for sell entries
|
||||
|
||||
#### **RSI_Oversold** (Y - Optimize)
|
||||
- **Current**: 50.0 (unusually high - verify if this is correct)
|
||||
- **Standard Range**: 15.0-40.0
|
||||
- **Step**: 2.0
|
||||
- **Alternative Range** (if current is intentional): 40.0-60.0
|
||||
- **Rationale**: Level where RSI indicates oversold condition for buy entries
|
||||
|
||||
#### **RSI_Target_Buy** (Y - Optimize)
|
||||
- **Current**: 71.0
|
||||
- **Recommended Range**: 65.0-90.0
|
||||
- **Step**: 2.0
|
||||
- **Rationale**: Exit target for long positions. Must be > RSI_Oversold
|
||||
|
||||
#### **RSI_Target_Sell** (Y - Optimize)
|
||||
- **Current**: 70.0
|
||||
- **Recommended Range**: 10.0-35.0
|
||||
- **Step**: 2.0
|
||||
- **Rationale**: Exit target for short positions. Must be < RSI_Overbought
|
||||
|
||||
### Phase 2: Risk Management Parameters
|
||||
|
||||
#### **BarsToWait** (Y - Optimize)
|
||||
- **Current**: 1
|
||||
- **Recommended Range**: 1-8
|
||||
- **Step**: 1
|
||||
- **Rationale**: Bars to wait before closing when RSI goes against position. Higher = more patience
|
||||
|
||||
#### **TimeFrame** (Y - Optimize)
|
||||
- **Current**: 16387 (M5)
|
||||
- **Recommended**: Test M1, M5, M15, H1
|
||||
- **Values**:
|
||||
- M1 = 16385
|
||||
- M5 = 16387
|
||||
- M15 = 16388
|
||||
- H1 = 16390
|
||||
- **Rationale**: Different timeframes can significantly affect scalping performance
|
||||
|
||||
### Phase 3: Position Sizing (Optimize with Caution)
|
||||
|
||||
#### **LotSize** (Y - Optimize with Fixed Risk)
|
||||
- **Current**: 50.0
|
||||
- **Recommended Range**: 10.0-100.0
|
||||
- **Step**: 5.0
|
||||
- **Note**: Consider using fixed risk % instead of fixed lot size
|
||||
- **Rationale**: Position sizing affects profitability but also risk
|
||||
|
||||
### Fixed Parameters (Do NOT Optimize)
|
||||
|
||||
#### **RSI_Applied_Price** (N)
|
||||
- **Value**: 1 (PRICE_CLOSE)
|
||||
- **Rationale**: Standard choice, changing may not improve results significantly
|
||||
|
||||
#### **MagicNumber** (N)
|
||||
- **Value**: 12345
|
||||
- **Rationale**: Identifier only, no impact on performance
|
||||
|
||||
#### **Slippage** (N)
|
||||
- **Value**: 3
|
||||
- **Rationale**: Broker-specific, should match your actual slippage
|
||||
|
||||
## Genetic Algorithm Settings
|
||||
|
||||
### Recommended GA Settings:
|
||||
- **Optimization Criterion**: Balance (or Custom: Profit Factor * Total Net Profit)
|
||||
- **Population Size**: 50-100
|
||||
- **Mutation Probability**: 0.1-0.2
|
||||
- **Crossover Probability**: 0.7-0.9
|
||||
- **Optimization Passes**: 3-5
|
||||
- **Forward Testing**: Always use out-of-sample data
|
||||
|
||||
### Optimization Phases:
|
||||
|
||||
1. **Broad Search** (First Pass):
|
||||
- Optimize: RSI_Period, RSI_Overbought, RSI_Oversold, RSI_Target_Buy, RSI_Target_Sell
|
||||
- Fix: BarsToWait=1, TimeFrame=M5, LotSize=50
|
||||
|
||||
2. **Refinement** (Second Pass):
|
||||
- Use best results from Phase 1
|
||||
- Optimize: BarsToWait, TimeFrame
|
||||
- Narrow ranges around Phase 1 winners
|
||||
|
||||
3. **Fine-Tuning** (Third Pass):
|
||||
- Optimize: LotSize (if needed)
|
||||
- Very narrow ranges around Phase 2 winners
|
||||
|
||||
## Important Notes
|
||||
|
||||
⚠️ **Current Parameter Anomaly**:
|
||||
- RSI_Overbought=19 and RSI_Oversold=50 are unusual
|
||||
- Standard RSI ranges: Overbought 70-80, Oversold 20-30
|
||||
- **Verify** if these are intentional or if there's a scaling issue
|
||||
|
||||
✅ **Validation Checklist**:
|
||||
- Ensure RSI_Target_Buy > RSI_Oversold
|
||||
- Ensure RSI_Target_Sell < RSI_Overbought
|
||||
- Test on sufficient historical data (at least 6-12 months)
|
||||
- Use forward testing on unseen data
|
||||
- Check for overfitting (too many parameters optimized)
|
||||
|
||||
## Example .set File Structure
|
||||
|
||||
```
|
||||
RSI_Period=14||1||7||21||Y
|
||||
RSI_Overbought=70.0||2.0||60.0||85.0||Y
|
||||
RSI_Oversold=30.0||2.0||15.0||40.0||Y
|
||||
RSI_Target_Buy=75.0||2.0||65.0||90.0||Y
|
||||
RSI_Target_Sell=25.0||2.0||10.0||35.0||Y
|
||||
BarsToWait=2||1||1||8||Y
|
||||
TimeFrame=16387||0||16385||16390||Y
|
||||
LotSize=50.0||5.0||10.0||100.0||Y
|
||||
RSI_Applied_Price=1||0||1||1||N
|
||||
MagicNumber=12345||0||12345||12345||N
|
||||
Slippage=3||0||3||3||N
|
||||
```
|
||||
@@ -0,0 +1,408 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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.01"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M15; // Timeframe for Analysis
|
||||
input int RSI_Period = 8; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 36; // RSI Overbought Level
|
||||
input double RSI_Oversold = 38; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 90; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 70; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 5; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 50; // Lot Size
|
||||
input int MagicNumber = 12345; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
|
||||
input double TrailingStopDistancePoints = 375.0; // SL distance from bid/ask (points)
|
||||
input double TrailingActivationPoints = 75.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
//--- 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 && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sync ticket/state if a position exists after restart |
|
||||
//+------------------------------------------------------------------+
|
||||
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 AND symbol for THIS EA
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)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()
|
||||
{
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
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)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position doesn't exist or wrong magic number - reset tracking
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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.01"
|
||||
|
||||
#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 = 54; // RSI Overbought Level
|
||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 87; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 33; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 1; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 5; // Lot Size
|
||||
input int MagicNumber = 125421321; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
|
||||
input double TrailingStopDistancePoints = 900.0; // SL distance from bid/ask (points)
|
||||
input double TrailingActivationPoints = 950.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
//--- 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 && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sync ticket/state if a position exists after restart |
|
||||
//+------------------------------------------------------------------+
|
||||
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 AND symbol for THIS EA
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)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()
|
||||
{
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
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)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position doesn't exist or wrong magic number - reset tracking
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
@@ -0,0 +1,34 @@
|
||||
; RSIScalpingXAUUSD-trailing — matches main.mq5 v1.06 default inputs
|
||||
; saved on 2026.05.01 22:32:43
|
||||
; MT5 Strategy Tester: Inputs → Load
|
||||
;
|
||||
TimeFrame=16385||15||0||16385||N
|
||||
RSI_Period=14||14||1||140||N
|
||||
RSI_Applied_Price=1||1||0||7||N
|
||||
RSI_Overbought=71.0||0||2||100||N
|
||||
RSI_Oversold=57.0||0||2||100||N
|
||||
UseEntrySlopeFilter=false||false||0||true||N
|
||||
EntryMinSlopePerBar=1.0||1.0||0.100000||10.000000||N
|
||||
RSI_Target_Buy=80.0||0||2||100||N
|
||||
RSI_Target_Sell=57.0||0||2||100||N
|
||||
BarsToWait=1||0||1||50||N
|
||||
LotSize=0.1||0.1||0.010000||1.000000||N
|
||||
MagicNumber=129102315||129102315||1||1291023150||N
|
||||
Slippage=3||3||1||30||N
|
||||
; === Reversal escape (intrabar, multi-signal) ===
|
||||
UseReversalEscape=true||false||0||true||N
|
||||
ReversalEscapeTimeFrame=5||0||0||49153||N
|
||||
ReversalATRPeriod=14||14||1||140||N
|
||||
ReversalAdverseAtrMult=5.25||5.25||0.525000||52.500000||N
|
||||
ReversalSignsRequired=1||2||1||20||N
|
||||
ReversalRsiVelocity=16.0||16.0||1.600000||160.000000||N
|
||||
ReversalBodyAtrMult=5.1||5.1||0.510000||51.000000||N
|
||||
; === Trailing stop ===
|
||||
UseTrailingStop=true||false||0||true||N
|
||||
TrailingStopDistancePoints=71.0||100||100||5000||Y
|
||||
TrailingActivationPoints=41.0||100||100||5000||Y
|
||||
; === Intrabar give-back (same bar reversals) ===
|
||||
UseGiveBackExit=true||false||0||true||N
|
||||
GiveBackATRPeriod=14||14||1||140||N
|
||||
GiveBackAtrMult=0.1||1.85||0.185000||18.500000||N
|
||||
GiveBackRequireMfe=true||false||0||true||N
|
||||
@@ -0,0 +1,645 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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.06"
|
||||
|
||||
#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 = 1; // 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 (now uses ReversalEscapeTimeFrame)
|
||||
input ENUM_TIMEFRAMES ReversalEscapeTimeFrame = PERIOD_M5; // ATR / RSI velocity / bar signs on this TF (not signal TF)
|
||||
input int ReversalATRPeriod = 14; // ATR lookback on ReversalEscapeTimeFrame
|
||||
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
|
||||
input int ReversalSignsRequired = 1; // 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
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind price while in profit
|
||||
input double TrailingStopDistancePoints = 71.0; // SL distance from current bid/ask (points)
|
||||
input double TrailingActivationPoints = 41.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
input group "=== Intrabar give-back (same bar reversals) ==="
|
||||
input bool UseGiveBackExit = true; // exit if price gives back vs best tick since entry
|
||||
input int GiveBackATRPeriod = 14; // ATR period on signal timeframe (Wilder)
|
||||
input double GiveBackAtrMult = 0.1; // close when retrace from peak/trough >= this * ATR
|
||||
input bool GiveBackRequireMfe = true; // long: only after bid was above entry; short: ask below entry
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
int rsi_escape_handle = INVALID_HANDLE; // RSI on ReversalEscapeTimeFrame (may alias 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;
|
||||
|
||||
ulong g_giveback_track_ticket = 0;
|
||||
double g_peak_bid_since_entry = 0.0;
|
||||
double g_trough_ask_since_entry = 0.0;
|
||||
|
||||
void ResetIntrabarGiveBackState();
|
||||
void TryGiveBackExit();
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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);
|
||||
}
|
||||
|
||||
if(ReversalEscapeTimeFrame == TimeFrame)
|
||||
rsi_escape_handle = rsi_handle;
|
||||
else
|
||||
{
|
||||
rsi_escape_handle = iRSI(_Symbol, ReversalEscapeTimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_escape_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_escape_handle != INVALID_HANDLE && rsi_escape_handle != rsi_handle)
|
||||
IndicatorRelease(rsi_escape_handle);
|
||||
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(in_pos && UseGiveBackExit)
|
||||
TryGiveBackExit();
|
||||
|
||||
if(in_pos && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Wilder ATR on arbitrary timeframe |
|
||||
//+------------------------------------------------------------------+
|
||||
double WilderATRForTF(const ENUM_TIMEFRAMES tf, const int period)
|
||||
{
|
||||
if(period < 1)
|
||||
return 0.0;
|
||||
MqlRates rates[];
|
||||
const int need = period + 2;
|
||||
if(CopyRates(_Symbol, tf, 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;
|
||||
|
||||
double rsi_esc[];
|
||||
ArraySetAsSeries(rsi_esc, true);
|
||||
const bool ok_esc_rsi = (rsi_escape_handle != INVALID_HANDLE &&
|
||||
CopyBuffer(rsi_escape_handle, 0, 0, 2, rsi_esc) >= 2);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(entry - bid >= ReversalAdverseAtrMult * atr)
|
||||
signs++;
|
||||
if(ok_esc_rsi && rsi_esc[1] - rsi_esc[0] >= ReversalRsiVelocity)
|
||||
signs++;
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(ask - entry >= ReversalAdverseAtrMult * atr)
|
||||
signs++;
|
||||
if(ok_esc_rsi && rsi_esc[0] - rsi_esc[1] >= ReversalRsiVelocity)
|
||||
signs++;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
MqlRates r[];
|
||||
if(CopyRates(_Symbol, ReversalEscapeTimeFrame, 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 = WilderATRForTF(ReversalEscapeTimeFrame, ReversalATRPeriod);
|
||||
if(atr <= 0.0)
|
||||
return;
|
||||
|
||||
const int n = CountReversalEscapeSigns(ptype, atr);
|
||||
if(n < ReversalSignsRequired)
|
||||
return;
|
||||
|
||||
ClosePosition();
|
||||
Print("RSIScalpingXAUUSD: reversal escape TF=", EnumToString(ReversalEscapeTimeFrame),
|
||||
" signs=", n, " need=", ReversalSignsRequired,
|
||||
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reset give-back peak/trough tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetIntrabarGiveBackState()
|
||||
{
|
||||
g_giveback_track_ticket = 0;
|
||||
g_peak_bid_since_entry = 0.0;
|
||||
g_trough_ask_since_entry = 0.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Exit when intrabar price gives back sharply vs best since entry |
|
||||
//+------------------------------------------------------------------+
|
||||
void TryGiveBackExit()
|
||||
{
|
||||
if(!UseGiveBackExit || GiveBackAtrMult <= 0.0)
|
||||
return;
|
||||
|
||||
const ulong ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
{
|
||||
ResetIntrabarGiveBackState();
|
||||
return;
|
||||
}
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
|
||||
if(g_giveback_track_ticket != ticket)
|
||||
{
|
||||
g_giveback_track_ticket = ticket;
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
g_peak_bid_since_entry = bid;
|
||||
g_trough_ask_since_entry = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_trough_ask_since_entry = ask;
|
||||
g_peak_bid_since_entry = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
const double atr = ATRPriceOnTF(GiveBackATRPeriod);
|
||||
if(atr <= 0.0)
|
||||
return;
|
||||
|
||||
const double threshold = GiveBackAtrMult * atr;
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(bid > g_peak_bid_since_entry)
|
||||
g_peak_bid_since_entry = bid;
|
||||
if(GiveBackRequireMfe && g_peak_bid_since_entry <= entry)
|
||||
return;
|
||||
if(g_peak_bid_since_entry - bid >= threshold)
|
||||
{
|
||||
ClosePosition();
|
||||
Print("RSIScalpingXAUUSD: give-back exit BUY retrace=",
|
||||
DoubleToString(g_peak_bid_since_entry - bid, digits),
|
||||
" thr=", DoubleToString(threshold, digits));
|
||||
}
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(ask < g_trough_ask_since_entry)
|
||||
g_trough_ask_since_entry = ask;
|
||||
if(GiveBackRequireMfe && g_trough_ask_since_entry >= entry)
|
||||
return;
|
||||
if(ask - g_trough_ask_since_entry >= threshold)
|
||||
{
|
||||
ClosePosition();
|
||||
Print("RSIScalpingXAUUSD: give-back exit SELL retrace=",
|
||||
DoubleToString(ask - g_trough_ask_since_entry, digits),
|
||||
" thr=", DoubleToString(threshold, digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
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, (ulong)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"))
|
||||
{
|
||||
const ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(t > 0 && PositionSelectByTicketSymbolAndMagic(t, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = (int)t;
|
||||
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"))
|
||||
{
|
||||
const ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(t > 0 && PositionSelectByTicketSymbolAndMagic(t, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = (int)t;
|
||||
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;
|
||||
ResetIntrabarGiveBackState();
|
||||
return;
|
||||
}
|
||||
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
ResetIntrabarGiveBackState();
|
||||
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 |
Reference in New Issue
Block a user