This commit is contained in:
zhutoutoutousan
2026-05-27 14:59:00 +02:00
parent b5acd37754
commit 3f75a08848
122 changed files with 5259 additions and 12459 deletions
@@ -0,0 +1,328 @@
//+------------------------------------------------------------------+
//| DarvasBoxStrategy.mqh |
//+------------------------------------------------------------------+
// MQL5: no #if — use #ifdef only (no defined() / || in one #if)
#ifdef UNITED_V2_DYNAMIC_LOTS
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#ifdef CLUSTER0_ORCHESTRATOR
extern double g_DB_LotSize;
#define DARVAS_TRADE_LOT (g_DB_LotSize)
#else
#define DARVAS_TRADE_LOT 0.01
#endif
#endif
bool InitDarvasBox(string symbol)
{
dbData.symbol = symbol;
dbData.boxHigh = 0;
dbData.boxLow = 0;
dbData.boxFormed = false;
dbData.lastBoxTime = 0;
dbData.boxName = "DarvasBox_" + IntegerToString(DB_MagicNumber) + "_";
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("DarvasBox: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
dbData.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
dbData.minStopLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * dbData.point;
dbData.maHandle = iMA(symbol, DB_TrendTimeframe, DB_MA_Period, 0, DB_MA_Method, DB_MA_Price);
// Same TF as box highs/lows (H1 loop in CalculateDarvasBox). PERIOD_CURRENT breaks when United EA
// runs on a chart timeframe other than H1 (volume/breakout no longer match the box).
dbData.volumeHandle = iVolumes(symbol, PERIOD_H1, VOLUME_TICK);
if(dbData.maHandle == INVALID_HANDLE || dbData.volumeHandle == INVALID_HANDLE)
{
Print("DarvasBox: Error creating indicators for '", symbol, "'");
return false;
}
dbData.trade.SetDeviationInPoints(10);
dbData.trade.SetTypeFilling(ORDER_FILLING_IOC);
dbData.trade.SetAsyncMode(false);
dbData.trade.SetExpertMagicNumber(DB_MagicNumber);
ObjectsDeleteAll(0, dbData.boxName);
dbData.isInitialized = true;
Print("DarvasBox: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitDarvasBox()
{
if(dbData.maHandle != INVALID_HANDLE) IndicatorRelease(dbData.maHandle);
if(dbData.volumeHandle != INVALID_HANDLE) IndicatorRelease(dbData.volumeHandle);
ObjectsDeleteAll(0, dbData.boxName);
}
void DrawDarvasBox()
{
if(!dbData.boxFormed) return;
datetime time1 = iTime(dbData.symbol, PERIOD_H1, DB_BoxPeriod);
datetime time2 = iTime(dbData.symbol, PERIOD_H1, 0);
ObjectsDeleteAll(0, dbData.boxName);
ObjectCreate(0, dbData.boxName + "Top", OBJ_TREND, 0, time1, dbData.boxHigh, time2, dbData.boxHigh);
ObjectCreate(0, dbData.boxName + "Bottom", OBJ_TREND, 0, time1, dbData.boxLow, time2, dbData.boxLow);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_COLOR, DB_BoxColor);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_COLOR, DB_BoxColor);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_WIDTH, DB_BoxWidth);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_WIDTH, DB_BoxWidth);
ObjectSetInteger(0, dbData.boxName + "Top", OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, dbData.boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
}
void CalculateDarvasBox()
{
double high = 0;
double low = DBL_MAX;
// Find highest high and lowest low in the period - EXACTLY like original
for(int i = 0; i < DB_BoxPeriod; i++)
{
high = MathMax(high, iHigh(dbData.symbol, PERIOD_H1, i));
low = MathMin(low, iLow(dbData.symbol, PERIOD_H1, i));
}
double range = high - low;
double allowedRange = DB_BoxDeviation * dbData.point; // Use dbData.point instead of _Point
if(DB_EnableLogging)
{
Print("DarvasBox: Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
}
// Check if box is formed - EXACTLY like original
if(range <= allowedRange)
{
dbData.boxHigh = high;
dbData.boxLow = low;
dbData.boxFormed = true;
dbData.lastBoxTime = iTime(dbData.symbol, PERIOD_CURRENT, 0);
// Draw the box
DrawDarvasBox();
if(DB_EnableLogging)
Print("DarvasBox: Box Formed - High: ", dbData.boxHigh, " Low: ", dbData.boxLow, " Time: ", dbData.lastBoxTime);
}
else
{
dbData.boxFormed = false;
// Delete box if it exists
ObjectsDeleteAll(0, dbData.boxName);
}
}
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
{
double minSlDistance = MathMax(dbData.minStopLevel, DB_StopLoss * dbData.point);
double minTpDistance = MathMax(dbData.minStopLevel, DB_TakeProfit * dbData.point);
if(orderType == ORDER_TYPE_BUY)
{
sl = price - minSlDistance;
tp = price + minTpDistance;
}
else
{
sl = price + minSlDistance;
tp = price - minTpDistance;
}
return true;
}
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
{
if(!DB_UseTrendFilter)
return true;
double ma[];
ArraySetAsSeries(ma, true);
if(CopyBuffer(dbData.maHandle, 0, 0, 2, ma) <= 0)
return false;
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
double trendStrength = MathAbs(currentPrice - ma[0]) / dbData.point;
if(orderType == ORDER_TYPE_BUY)
return (currentPrice > ma[0] && trendStrength > DB_TrendThreshold);
else
return (currentPrice < ma[0] && trendStrength > DB_TrendThreshold);
}
bool CheckVolumeConditions()
{
if(!DB_UseVolumeSpikeFilter)
return true;
double volumes[];
ArraySetAsSeries(volumes, true);
if(CopyBuffer(dbData.volumeHandle, 0, 0, DB_VolumeMA_Period + 1, volumes) <= 0)
return false;
double volumeMA = 0;
for(int i = 1; i <= DB_VolumeMA_Period; i++)
volumeMA += volumes[i];
volumeMA /= DB_VolumeMA_Period;
double currentVolume = volumes[0];
if(volumeMA <= 0.0)
return (currentVolume > 0.0);
double volumeRatio = currentVolume / volumeMA;
return (volumeRatio > DB_VolumeThresholdMultiplier);
}
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
{
if(!ValidateStopLevels(price, sl, tp, orderType))
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Stop levels validation failed");
return false;
}
if(!IsTrendFavorable(orderType))
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Trend not favorable for ", EnumToString(orderType));
return false;
}
if(!CheckVolumeConditions())
{
if(DB_EnableLogging)
Print("DarvasBox: Order rejected - Volume conditions not met");
return false;
}
bool result = false;
const double lot = United_NormalizeVolume(dbData.symbol, DARVAS_TRADE_LOT);
if(lot <= 0.0)
{
Print("DarvasBox: Order rejected - invalid lot after normalize (raw=", DARVAS_TRADE_LOT, ")");
return false;
}
// Use market price (0) instead of explicit price - this ensures market order execution
// In backtesting, explicit price might fail if price has moved
if(orderType == ORDER_TYPE_BUY)
result = dbData.trade.Buy(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakout");
else
result = dbData.trade.Sell(lot, dbData.symbol, 0, sl, tp, "Darvas Box Breakdown");
// Always log errors, success only if logging enabled
if(result)
{
if(DB_EnableLogging)
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
}
else
{
// Always log failures with detailed info
uint retcode_uint = dbData.trade.ResultRetcode();
int retcode = (int)retcode_uint;
string desc = dbData.trade.ResultRetcodeDescription();
ulong deal = dbData.trade.ResultDeal();
ulong order = dbData.trade.ResultOrder();
Print("DarvasBox: ", (orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"),
" Order Failed - Retcode: ", retcode,
", Description: ", desc,
", Deal: ", deal,
", Order: ", order,
", Symbol: ", dbData.symbol,
", Requested Price: ", price,
", SL: ", sl,
", TP: ", tp);
}
return result;
}
void ProcessDarvasBox(string symbol)
{
// Skip if not initialized (symbol not available)
if(!dbData.isInitialized)
return;
dbData.symbol = symbol; // Update symbol in case it changed
// Calculate new box levels - EXACTLY like original (called every tick)
CalculateDarvasBox();
// Check for trading signals - EXACTLY like original (checked every tick)
if(dbData.boxFormed)
{
double currentPrice = SymbolInfoDouble(dbData.symbol, SYMBOL_ASK);
long currentVolume_long = iVolume(dbData.symbol, PERIOD_H1, 0);
double currentVolume = (double)currentVolume_long;
if(DB_EnableLogging)
{
Print("DarvasBox: Current Price: ", currentPrice, " Box High: ", dbData.boxHigh, " Box Low: ", dbData.boxLow);
Print("DarvasBox: Current Volume: ", currentVolume, " Volume Threshold: ", DB_VolumeThreshold);
}
// Check for breakout above box - EXACTLY like original
if(currentPrice > dbData.boxHigh && currentVolume > DB_VolumeThreshold)
{
if(DB_EnableLogging)
Print("DarvasBox: Breakout Signal Detected - Price above box high");
// Buy signal
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice - DB_StopLoss * dbData.point;
double tp = currentPrice + DB_TakeProfit * dbData.point;
if(DB_EnableLogging)
Print("DarvasBox: Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
}
else if(DB_EnableLogging)
Print("DarvasBox: Skipping Buy Signal - Position already exists");
}
// Check for breakdown below box - EXACTLY like original
if(currentPrice < dbData.boxLow && currentVolume > DB_VolumeThreshold)
{
if(DB_EnableLogging)
Print("DarvasBox: Breakdown Signal Detected - Price below box low");
// Sell signal
if(!PositionExistsByMagic(dbData.symbol, (ulong)DB_MagicNumber)) // No existing positions with our magic number
{
double sl = currentPrice + DB_StopLoss * dbData.point;
double tp = currentPrice - DB_TakeProfit * dbData.point;
if(DB_EnableLogging)
Print("DarvasBox: Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
}
else if(DB_EnableLogging)
Print("DarvasBox: Skipping Sell Signal - Position already exists");
}
}
else if(DB_EnableLogging)
Print("DarvasBox: No Box Formed - Waiting for consolidation");
}
//+------------------------------------------------------------------+
@@ -0,0 +1,576 @@
//+------------------------------------------------------------------+
//| EMASlopeDistanceStrategy.mqh |
//+------------------------------------------------------------------+
bool InitEMASlopeDistance(string symbol)
{
esData.symbol = symbol;
esData.letzte_überwachung_zeit = 0;
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
esData.ticket = 0;
esData.trades_in_current_crossover = 0;
esData.crossover_detected = false;
esData.trade_open_time = 0;
esData.last_bar_time = 0;
esData.es_last_sl_adjust_success_time = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("EMASlopeDistance: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
esData.trade.SetExpertMagicNumber(ES_MagicNumber);
esData.trade.SetDeviationInPoints(10);
esData.trade.SetTypeFilling(ORDER_FILLING_IOC);
esData.ema_handle = iMA(symbol, ES_Timeframe, ES_EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
if(esData.ema_handle == INVALID_HANDLE)
{
Print("EMASlopeDistance: Error creating EMA indicator for '", symbol, "'");
return false;
}
ArraySetAsSeries(esData.ema_array, true);
esData.isInitialized = true;
Print("EMASlopeDistance: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitEMASlopeDistance()
{
if(esData.ema_handle != INVALID_HANDLE)
IndicatorRelease(esData.ema_handle);
}
bool ES_IsWeeklyADXTrendFavorable(const ENUM_ORDER_TYPE order_type)
{
if(!ES_UseWeeklyADXFilter)
return true;
int adxShift = ES_WeeklyADXBarShift;
if(adxShift < 0)
adxShift = 0;
int adx_handle = iADX(esData.symbol, PERIOD_W1, ES_WeeklyADXPeriod);
if(adx_handle == INVALID_HANDLE)
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)
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 >= ES_WeeklyADXMin);
bool direction_ok = true;
if(ES_WeeklyADXUseDirection)
{
if(order_type == ORDER_TYPE_BUY)
direction_ok = (plus_di > minus_di);
else
direction_ok = (minus_di > plus_di);
}
return strength_ok && direction_ok;
}
bool ES_TrailingActivationReached(const double position_profit, const ENUM_POSITION_TYPE position_type,
const double pips_multiplier)
{
if(ES_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(esData.symbol, SYMBOL_BID);
return ((bid - open_px) / SymbolInfoDouble(esData.symbol, SYMBOL_POINT) / pips_multiplier >= ES_TrailingActivationPips);
}
const double ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
return ((open_px - ask) / SymbolInfoDouble(esData.symbol, SYMBOL_POINT) / pips_multiplier >= ES_TrailingActivationPips);
}
//+------------------------------------------------------------------+
//| EMA Berechnung (EMA Calculation) |
//+------------------------------------------------------------------+
void BerechneEMA()
{
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
int copied = CopyBuffer(esData.ema_handle, 0, 0, 3, esData.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]: ", esData.ema_array[0], " [1]: ", esData.ema_array[1], " [2]: ", esData.ema_array[2]);
}
//+------------------------------------------------------------------+
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
//+------------------------------------------------------------------+
void PrüfeTrigger()
{
if(ArraySize(esData.ema_array) < 2)
{
Print("TRACE: Array zu klein - Größe: ", ArraySize(esData.ema_array));
return;
}
//--- Aktuelle Werte (Current values)
double aktueller_preis = SymbolInfoDouble(esData.symbol, SYMBOL_BID);
double aktueller_ask = SymbolInfoDouble(esData.symbol, SYMBOL_ASK);
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
//--- EMA Werte in Variablen (EMA values in variables)
double ema_aktuell = esData.ema_array[0];
double ema_vorher = esData.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)
{
esData.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: ", ES_PreisSchwelle, ")");
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
Print("TRACE: Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
if(preis_abstand > ES_PreisSchwelle && !esData.preis_trigger_aktiv)
{
esData.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: ", ES_SteigungSchwelle, ")");
if(MathAbs(steigung) > ES_SteigungSchwelle && !esData.steigung_trigger_aktiv)
{
esData.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(esData.preis_trigger_aktiv && esData.steigung_trigger_aktiv && !esData.überwachung_aktiv)
{
esData.überwachung_aktiv = true;
if(ES_UseBarData)
{
esData.letzte_überwachung_zeit = iTime(esData.symbol, ES_Timeframe, 0); // Aktuelle Bar-Zeit
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(esData.letzte_überwachung_zeit), ")");
}
else
{
esData.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(esData.ü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(esData.trades_in_current_crossover >= ES_MaxTradesPerCrossover)
{
Print("TRACE: Trade-Limit erreicht (", ES_MaxTradesPerCrossover, ") - Kein neuer Trade");
return;
}
if(bullish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
if(!ES_IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
{
Print("TRACE: Weekly ADX blockiert BUY-Entry");
return;
}
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_BUY))
{
esData.trades_in_current_crossover++;
}
}
else if(bearish_signal && !PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
if(!ES_IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
{
Print("TRACE: Weekly ADX blockiert SELL-Entry");
return;
}
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", esData.trades_in_current_crossover + 1, ")");
if(PlatziereTrade(ORDER_TYPE_SELL))
{
esData.trades_in_current_crossover++;
}
}
else if(PositionExistsByMagic(esData.symbol, (ulong)ES_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");
const double lot = United_NormalizeVolume(esData.symbol, g_ES_LotSize);
Print("TRACE: Lot (raw): ", g_ES_LotSize, " normalized: ", lot);
if(lot <= 0.0)
{
Print("TRACE: Abbruch — Lot nach Normalisierung ungültig");
return false;
}
bool success = false;
if(order_type == ORDER_TYPE_BUY)
{
success = esData.trade.Buy(lot, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
else
{
success = esData.trade.Sell(lot, esData.symbol, 0, 0, 0, "EMA Crossover Trade");
}
if(success)
{
esData.ticket = (int)esData.trade.ResultOrder();
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", esData.ticket);
//--- Trade-Öffnungszeit speichern (Save trade opening time)
esData.trade_open_time = iTime(esData.symbol, ES_Timeframe, 0);
esData.es_last_sl_adjust_success_time = 0;
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(esData.trade_open_time));
//--- Überwachung zurücksetzen (Reset monitoring)
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
return true;
}
else
{
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
return false;
}
}
//+------------------------------------------------------------------+
//| Trades verwalten (Manage trades) |
//+------------------------------------------------------------------+
void VerwalteTrades()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
return;
if(ES_UseStaleStopLossExit && ES_StaleStopLossSeconds > 0)
{
const datetime stale_ref = (esData.es_last_sl_adjust_success_time > 0)
? esData.es_last_sl_adjust_success_time
: (datetime)PositionGetInteger(POSITION_TIME);
if(TimeCurrent() - stale_ref >= ES_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);
int digits = (int)SymbolInfoInteger(esData.symbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double pips_multiplier = (digits == 3 || digits == 5) ? 10.0 : 1.0;
const double trail_dist = ES_TrailingStop * point * pips_multiplier;
const long stops_level = SymbolInfoInteger(esData.symbol, SYMBOL_TRADE_STOPS_LEVEL);
const double min_dist = (double)stops_level * point;
if(ES_UseTrailingStop && ES_TrailingStop > 0.0 && ES_TrailingActivationReached(position_profit, position_type, pips_multiplier))
{
if(position_type == POSITION_TYPE_BUY)
{
const double bid = SymbolInfoDouble(esData.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(esData.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(esData.ema_array) >= 1)
{
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
double ema_aktuell = esData.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 ", esData.trades_in_current_crossover);
}
}
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
if(ES_CloseUnprofitableTrades && esData.trade_open_time != 0 && PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
PrüfeProfitNachBars();
}
else if(!ES_CloseUnprofitableTrades)
{
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", ES_CloseUnprofitableTrades);
}
}
//+------------------------------------------------------------------+
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
//+------------------------------------------------------------------+
void PrüfeProfitNachBars()
{
if(!PositionSelectByMagic(esData.symbol, (ulong)ES_MagicNumber))
{
return; // Keine Position offen
}
datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
int bars_since_trade_open = iBarShift(esData.symbol, ES_Timeframe, esData.trade_open_time);
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ES_ProfitCheckBars);
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
if(bars_since_trade_open >= ES_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 ", ES_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)
esData.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)
esData.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(esData.trade, esData.symbol, (ulong)ES_MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
if(success)
{
esData.es_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: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.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(esData.trade, esData.symbol, (ulong)ES_MagicNumber);
if(success)
{
esData.es_last_sl_adjust_success_time = 0;
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
}
else
{
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", esData.trade.ResultRetcode());
Print("TRACE: Fehlerbeschreibung: ", esData.trade.ResultRetcodeDescription());
}
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void ProcessEMASlopeDistance(string symbol)
{
if(!esData.isInitialized)
return;
esData.symbol = symbol;
const datetime current_bar_time = iTime(esData.symbol, ES_Timeframe, 0);
const bool new_bar = (current_bar_time != esData.last_bar_time);
const bool has_position = PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber);
if(ES_UseBarData && !new_bar && !has_position)
return;
if(new_bar)
esData.last_bar_time = current_bar_time;
BerechneEMA();
const bool run_signals = (!ES_UseBarData || new_bar);
if(run_signals && ArraySize(esData.ema_array) > 0)
{
double aktueller_close = iClose(esData.symbol, ES_Timeframe, 0);
double ema_aktuell = esData.ema_array[0];
double ema_vorher = esData.ema_array[1];
double point = SymbolInfoDouble(esData.symbol, SYMBOL_POINT);
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / point;
double steigung = (ema_aktuell - ema_vorher) / point;
if(ES_UseBarData)
{
Print("=== DEBUG INFO (Neuer Bar) ===");
Print("Bar Zeit: ", TimeToString(iTime(esData.symbol, ES_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: ", esData.preis_trigger_aktiv, " Steigungs-Trigger: ", esData.steigung_trigger_aktiv);
Print("Überwachung aktiv: ", esData.überwachung_aktiv);
Print("Position offen: ", PositionExistsByMagic(esData.symbol, (ulong)ES_MagicNumber));
Print("Trades im aktuellen Crossover: ", esData.trades_in_current_crossover, "/", ES_MaxTradesPerCrossover);
Print("==================");
}
if(run_signals)
{
if(esData.überwachung_aktiv)
{
if(ES_UseBarData)
{
int bars_since_monitoring = iBarShift(esData.symbol, ES_Timeframe, esData.letzte_überwachung_zeit);
int timeout_bars = (int)(ES_ÜberwachungTimeout / PeriodSeconds(ES_Timeframe));
if(bars_since_monitoring > timeout_bars)
{
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
}
}
else
{
if(TimeCurrent() - esData.letzte_überwachung_zeit > ES_ÜberwachungTimeout)
{
esData.überwachung_aktiv = false;
esData.preis_trigger_aktiv = false;
esData.steigung_trigger_aktiv = false;
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
}
}
}
PrüfeTrigger();
}
VerwalteTrades();
}
//+------------------------------------------------------------------+
@@ -0,0 +1,387 @@
//+------------------------------------------------------------------+
//| RSIConsolidationStrategy.mqh |
//| Ported from cluster-0/RSIConsolidation/RSIConsolidation.mq5 |
//+------------------------------------------------------------------+
#ifndef RSI_CONSOLIDATION_STRATEGY_MQH
#define RSI_CONSOLIDATION_STRATEGY_MQH
struct RSIConsolidationData
{
string symbol;
bool isInitialized;
CTrade trade;
ENUM_TIMEFRAMES signalTF;
bool entryOnNewBarOnly;
int adxPeriod;
double adxMax;
bool useATRRatioFilter;
int atrPeriod;
int atrSmaPeriod;
double atrRatioMax;
bool useFlatEMAFilter;
int emaFast;
int emaSlow;
double emaSeparationMaxPct;
int rsiPeriod;
ENUM_APPLIED_PRICE rsiPrice;
double rsiOversold;
double rsiOverbought;
bool useRSIMeanExit;
double rsiExitLong;
double rsiExitShort;
double slAtrMult;
double tpAtrMult;
int maxBarsInTrade;
ulong magic;
int slippage;
int maxSpreadPoints;
int h_rsi;
int h_adx;
int h_atr;
int h_ema_fast;
int h_ema_slow;
datetime lastBar;
};
bool RCO_Copy1(const int handle, double &v)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(handle, 0, 0, 1, b) < 1)
return false;
v = b[0];
return true;
}
bool RCO_RsiBuffers(RSIConsolidationData &d, double &cur, double &prev, double &twoAgo)
{
double b[];
ArraySetAsSeries(b, true);
if(CopyBuffer(d.h_rsi, 0, 0, 3, b) < 3)
return false;
cur = b[0];
prev = b[1];
twoAgo = b[2];
return true;
}
double RCO_NormalizeVolume(const string sym, double vol)
{
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(step > 0.0)
vol = MathFloor(vol / step) * step;
if(vol < minLot)
vol = minLot;
if(vol > maxLot)
vol = maxLot;
return vol;
}
int RCO_CurrentSpreadPoints(const string sym)
{
long spread = 0;
if(!SymbolInfoInteger(sym, SYMBOL_SPREAD, spread))
return 999999;
return (int)spread;
}
double RCO_MinStopsDistancePrice(const string sym)
{
long lvl = 0;
if(!SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL, lvl))
return 0;
double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
if(pt <= 0)
return 0;
return (double)lvl * pt;
}
bool RCO_RegimeIsConsolidation(RSIConsolidationData &d)
{
double adx = 0;
if(!RCO_Copy1(d.h_adx, adx))
return false;
if(adx >= d.adxMax)
return false;
if(d.useATRRatioFilter)
{
double atrArr[];
ArraySetAsSeries(atrArr, true);
if(CopyBuffer(d.h_atr, 0, 0, d.atrSmaPeriod + 1, atrArr) < d.atrSmaPeriod + 1)
return false;
double sum = 0;
for(int i = 1; i <= d.atrSmaPeriod; i++)
sum += atrArr[i];
double smaAtr = sum / (double)d.atrSmaPeriod;
if(smaAtr <= 0.0)
return false;
double ratio = atrArr[0] / smaAtr;
if(ratio > d.atrRatioMax)
return false;
}
if(d.useFlatEMAFilter)
{
double ef[], es[];
ArraySetAsSeries(ef, true);
ArraySetAsSeries(es, true);
if(CopyBuffer(d.h_ema_fast, 0, 0, 1, ef) < 1)
return false;
if(CopyBuffer(d.h_ema_slow, 0, 0, 1, es) < 1)
return false;
double c = SymbolInfoDouble(d.symbol, SYMBOL_BID);
if(c <= 0)
return false;
double sep = MathAbs(ef[0] - es[0]) / c * 100.0;
if(sep > d.emaSeparationMaxPct)
return false;
}
return true;
}
bool RCO_EntryBuyCross(RSIConsolidationData &d, const double twoAgo, const double prev)
{
return (twoAgo <= d.rsiOversold && prev > d.rsiOversold);
}
bool RCO_EntrySellCross(RSIConsolidationData &d, const double twoAgo, const double prev)
{
return (twoAgo >= d.rsiOverbought && prev < d.rsiOverbought);
}
void RCO_TryCloseByRSI(RSIConsolidationData &d, const ENUM_POSITION_TYPE typ, const double rsi)
{
ulong tk = GetPositionTicketByMagic(d.symbol, d.magic);
if(tk == 0 || !PositionSelectByTicketSymbolAndMagic(tk, d.symbol, d.magic))
return;
if(!d.useRSIMeanExit)
return;
if(typ == POSITION_TYPE_BUY && rsi >= d.rsiExitLong)
d.trade.PositionClose(tk);
else if(typ == POSITION_TYPE_SELL && rsi <= d.rsiExitShort)
d.trade.PositionClose(tk);
}
void RCO_ManageOpenPosition(RSIConsolidationData &d, const double rsi)
{
ulong tk = GetPositionTicketByMagic(d.symbol, d.magic);
if(tk == 0 || !PositionSelectByTicketSymbolAndMagic(tk, d.symbol, d.magic))
return;
ENUM_POSITION_TYPE typ = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
int barsAgo = iBarShift(d.symbol, d.signalTF, openT, false);
if(barsAgo >= 0 && barsAgo >= d.maxBarsInTrade)
{
d.trade.PositionClose(tk);
return;
}
RCO_TryCloseByRSI(d, typ, rsi);
}
bool InitRSIConsolidation(RSIConsolidationData &d,
const string inpSymbol,
const ENUM_TIMEFRAMES signalTF,
const bool entryOnNewBarOnly,
const int adxPeriod,
const double adxMax,
const bool useATRRatioFilter,
const int atrPeriod,
const int atrSmaPeriod,
const double atrRatioMax,
const bool useFlatEMAFilter,
const int emaFast,
const int emaSlow,
const double emaSeparationMaxPct,
const int rsiPeriod,
const ENUM_APPLIED_PRICE rsiPrice,
const double rsiOversold,
const double rsiOverbought,
const bool useRSIMeanExit,
const double rsiExitLong,
const double rsiExitShort,
const double slAtrMult,
const double tpAtrMult,
const int maxBarsInTrade,
const ulong magic,
const int slippage,
const int maxSpreadPoints)
{
d.isInitialized = false;
d.symbol = inpSymbol;
StringTrimLeft(d.symbol);
StringTrimRight(d.symbol);
if(StringLen(d.symbol) == 0)
d.symbol = _Symbol;
d.signalTF = signalTF;
d.entryOnNewBarOnly = entryOnNewBarOnly;
d.adxPeriod = adxPeriod;
d.adxMax = adxMax;
d.useATRRatioFilter = useATRRatioFilter;
d.atrPeriod = atrPeriod;
d.atrSmaPeriod = atrSmaPeriod;
d.atrRatioMax = atrRatioMax;
d.useFlatEMAFilter = useFlatEMAFilter;
d.emaFast = emaFast;
d.emaSlow = emaSlow;
d.emaSeparationMaxPct = emaSeparationMaxPct;
d.rsiPeriod = rsiPeriod;
d.rsiPrice = rsiPrice;
d.rsiOversold = rsiOversold;
d.rsiOverbought = rsiOverbought;
d.useRSIMeanExit = useRSIMeanExit;
d.rsiExitLong = rsiExitLong;
d.rsiExitShort = rsiExitShort;
d.slAtrMult = slAtrMult;
d.tpAtrMult = tpAtrMult;
d.maxBarsInTrade = maxBarsInTrade;
d.magic = magic;
d.slippage = slippage;
d.maxSpreadPoints = maxSpreadPoints;
d.lastBar = 0;
d.h_rsi = INVALID_HANDLE;
d.h_adx = INVALID_HANDLE;
d.h_atr = INVALID_HANDLE;
d.h_ema_fast = INVALID_HANDLE;
d.h_ema_slow = INVALID_HANDLE;
d.isInitialized = false;
if(!SymbolSelect(d.symbol, true))
{
Print("RSIConsolidation: SymbolSelect failed: ", d.symbol);
return false;
}
d.trade.SetExpertMagicNumber((long)d.magic);
d.trade.SetDeviationInPoints(d.slippage);
d.trade.SetTypeFillingBySymbol(d.symbol);
d.h_rsi = iRSI(d.symbol, d.signalTF, d.rsiPeriod, d.rsiPrice);
d.h_adx = iADX(d.symbol, d.signalTF, d.adxPeriod);
d.h_atr = iATR(d.symbol, d.signalTF, d.atrPeriod);
d.h_ema_fast = iMA(d.symbol, d.signalTF, d.emaFast, 0, MODE_EMA, PRICE_CLOSE);
d.h_ema_slow = iMA(d.symbol, d.signalTF, d.emaSlow, 0, MODE_EMA, PRICE_CLOSE);
if(d.h_rsi == INVALID_HANDLE || d.h_adx == INVALID_HANDLE || d.h_atr == INVALID_HANDLE
|| d.h_ema_fast == INVALID_HANDLE || d.h_ema_slow == INVALID_HANDLE)
{
Print("RSIConsolidation: indicator init failed");
DeinitRSIConsolidation(d);
return false;
}
d.isInitialized = true;
Print("RSIConsolidation: symbol=", d.symbol, " TF=", EnumToString(d.signalTF));
return true;
}
void DeinitRSIConsolidation(RSIConsolidationData &d)
{
if(d.h_rsi != INVALID_HANDLE)
IndicatorRelease(d.h_rsi);
if(d.h_adx != INVALID_HANDLE)
IndicatorRelease(d.h_adx);
if(d.h_atr != INVALID_HANDLE)
IndicatorRelease(d.h_atr);
if(d.h_ema_fast != INVALID_HANDLE)
IndicatorRelease(d.h_ema_fast);
if(d.h_ema_slow != INVALID_HANDLE)
IndicatorRelease(d.h_ema_slow);
d.h_rsi = INVALID_HANDLE;
d.h_adx = INVALID_HANDLE;
d.h_atr = INVALID_HANDLE;
d.h_ema_fast = INVALID_HANDLE;
d.h_ema_slow = INVALID_HANDLE;
d.isInitialized = false;
}
bool RCO_EnoughHistory(RSIConsolidationData &d)
{
int need = MathMax(d.rsiPeriod + 3, MathMax(d.adxPeriod + 2, d.atrSmaPeriod + 3));
if(Bars(d.symbol, d.signalTF) < need)
return false;
return true;
}
void ProcessRSIConsolidation(RSIConsolidationData &d, const double lots)
{
if(!d.isInitialized)
return;
if(!RCO_EnoughHistory(d))
return;
if(d.maxSpreadPoints > 0 && RCO_CurrentSpreadPoints(d.symbol) > d.maxSpreadPoints)
return;
double rsi, rsiPrev, rsi2;
if(!RCO_RsiBuffers(d, rsi, rsiPrev, rsi2))
return;
datetime barTime = iTime(d.symbol, d.signalTF, 0);
bool isNew = (barTime != d.lastBar);
if(PositionExistsByMagic(d.symbol, d.magic))
{
RCO_ManageOpenPosition(d, rsi);
if(isNew)
d.lastBar = barTime;
return;
}
if(d.entryOnNewBarOnly && !isNew)
return;
d.lastBar = barTime;
if(!RCO_RegimeIsConsolidation(d))
return;
double atrArr[];
ArraySetAsSeries(atrArr, true);
if(CopyBuffer(d.h_atr, 0, 0, 1, atrArr) < 1)
return;
double atr = atrArr[0];
int dig = (int)SymbolInfoInteger(d.symbol, SYMBOL_DIGITS);
double slDist = atr * d.slAtrMult;
double tpDist = atr * d.tpAtrMult;
double minD = RCO_MinStopsDistancePrice(d.symbol);
if(slDist < minD)
slDist = minD;
if(tpDist < minD)
tpDist = minD;
double vol = RCO_NormalizeVolume(d.symbol, lots);
if(RCO_EntryBuyCross(d, rsi2, rsiPrev))
{
if(!United_MayOpenNewEntry(d.symbol, d.magic, true))
return;
double ask = SymbolInfoDouble(d.symbol, SYMBOL_ASK);
double sl = ask - slDist;
double tp = ask + tpDist;
sl = NormalizeDouble(sl, dig);
tp = NormalizeDouble(tp, dig);
if(!d.trade.Buy(vol, d.symbol, ask, sl, tp, "RSIConsolidation BUY"))
Print("RSIConsolidation BUY failed | retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
else if(RCO_EntrySellCross(d, rsi2, rsiPrev))
{
if(!United_MayOpenNewEntry(d.symbol, d.magic, false))
return;
double bid = SymbolInfoDouble(d.symbol, SYMBOL_BID);
double sl = bid + slDist;
double tp = bid - tpDist;
sl = NormalizeDouble(sl, dig);
tp = NormalizeDouble(tp, dig);
if(!d.trade.Sell(vol, d.symbol, bid, sl, tp, "RSIConsolidation SELL"))
Print("RSIConsolidation SELL failed | retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
#endif // RSI_CONSOLIDATION_STRATEGY_MQH
@@ -0,0 +1,269 @@
//+------------------------------------------------------------------+
//| RSICrossOverReversalStrategy.mqh |
//+------------------------------------------------------------------+
void WeekDays_Init()
{
rcData.WeekDays[0] = RC_Sunday;
rcData.WeekDays[1] = RC_Monday;
rcData.WeekDays[2] = RC_Tuesday;
rcData.WeekDays[3] = RC_Wednesday;
rcData.WeekDays[4] = RC_Thursday;
rcData.WeekDays[5] = RC_Friday;
rcData.WeekDays[6] = RC_Saturday;
}
bool WeekDays_Check(datetime aTime)
{
MqlDateTime stm;
TimeToStruct(aTime, stm);
return(rcData.WeekDays[stm.day_of_week]);
}
bool RC_HourInWindow(const int h, const int beginRaw, const int endRaw)
{
const int b = beginRaw % 24;
const int e = endRaw % 24;
if(b == e)
return false;
if(b < e)
return (h >= b && h < e);
return (h >= b || h < e);
}
bool RC_TradingHoursAllow(const int currentHour)
{
return RC_HourInWindow(currentHour, RC_tradingHourOneBegin, RC_tradingHourOneEnd)
|| RC_HourInWindow(currentHour, RC_tradingHourTwoBegin, RC_tradingHourTwoEnd);
}
int TimeHour(datetime when = 0)
{
if(when == 0) when = TimeCurrent();
MqlDateTime dt;
TimeToStruct(when, dt);
return dt.hour;
}
bool InitRSICrossOverReversal(string symbol)
{
WeekDays_Init();
rcData.symbol = symbol;
rcData.previousRSIDef = 0;
rcData.lastTradeTime = 0;
rcData.bartime = 0;
rcData.lastBarTime = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSICrossOverReversal: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
rcData.rsiHandle = iRSI(symbol, RC_TimeFrame1, RC_rsiPeriod, PRICE_CLOSE);
if(rcData.rsiHandle == INVALID_HANDLE)
{
Print("RSICrossOverReversal: Error creating RSI handle for '", symbol, "'");
return false;
}
rcData.emaHandle = iMA(symbol, RC_TimeFrame2, RC_emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rcData.emaHandle == INVALID_HANDLE)
{
Print("RSICrossOverReversal: Error creating EMA handle for '", symbol, "'");
return false;
}
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
rcData.trade.SetDeviationInPoints(RC_slippage);
rcData.isInitialized = true;
Print("RSICrossOverReversal: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSICrossOverReversal()
{
if(rcData.rsiHandle != INVALID_HANDLE)
IndicatorRelease(rcData.rsiHandle);
if(rcData.emaHandle != INVALID_HANDLE)
IndicatorRelease(rcData.emaHandle);
}
void Close_Position_MN(ulong magicNumber)
{
ClosePositionByMagic(rcData.trade, rcData.symbol, (int)magicNumber);
}
void ApplyTrailingStop()
{
if(!PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
return;
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
ENUM_POSITION_TYPE trade_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
string symbol = rcData.symbol;
double POINT = SymbolInfoDouble(symbol, SYMBOL_POINT);
int DIGIT = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
if(trade_type == POSITION_TYPE_BUY)
{
double Bid = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_BID), DIGIT);
if(Bid - PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
{
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT))
{
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
NormalizeDouble(Bid - POINT * RC_TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
else if(trade_type == POSITION_TYPE_SELL)
{
double Ask = NormalizeDouble(SymbolInfoDouble(symbol, SYMBOL_ASK), DIGIT);
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble(POINT * RC_TrailingStop, DIGIT))
{
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT)) ||
(PositionGetDouble(POSITION_SL) == 0))
{
ModifyPositionByMagic(rcData.trade, symbol, RC_MagicNumber,
NormalizeDouble(Ask + POINT * RC_TrailingStop, DIGIT),
PositionGetDouble(POSITION_TP));
}
}
}
}
void ProcessRSICrossOverReversal(string symbol)
{
// Skip if not initialized (symbol not available)
if(!rcData.isInitialized)
return;
rcData.symbol = symbol; // Update symbol in case it changed
if(rcData.bartime == iTime(rcData.symbol, RC_BarTimeFrame, 0))
return;
rcData.bartime = iTime(rcData.symbol, RC_BarTimeFrame, 0);
double rsi[];
if(CopyBuffer(rcData.rsiHandle, 0, 0, 2, rsi) <= 0)
return;
double ema[];
if(CopyBuffer(rcData.emaHandle, 0, 0, 2, ema) <= 0)
return;
datetime currentTime = TimeCurrent();
int currentHour = TimeHour(TimeCurrent());
if(!WeekDays_Check(TimeTradeServer()))
{
Close_Position_MN(RC_MagicNumber);
return;
}
if(!RC_TradingHoursAllow(currentHour))
{
Close_Position_MN(RC_MagicNumber);
return;
}
bool hasPosition = PositionExistsByMagic(rcData.symbol, RC_MagicNumber);
double currentRSI = rsi[0];
double previousRSI = rsi[1];
if(rcData.previousRSIDef == 0)
{
rcData.previousRSIDef = currentRSI;
return;
}
double currentEMA = ema[0];
double previousEMA = ema[1];
double emaSlope = (currentEMA - previousEMA) * 100;
const double closeCurr = iClose(rcData.symbol, RC_TimeFrame1, 0);
// Raw (close-EMA)*10 blows past threshold on XAUUSD (~2600) almost every bar — blocks all entries.
// Compare distance in pips so RC_emaDistanceThreshold matches intent across symbols.
const double point = SymbolInfoDouble(rcData.symbol, SYMBOL_POINT);
const int symDig = (int)SymbolInfoInteger(rcData.symbol, SYMBOL_DIGITS);
const double pipMult = (symDig == 3 || symDig == 5) ? 10.0 : 1.0;
const double pipSize = (point > 0.0 ? point * pipMult : point);
const double priceToEmaPips = (pipSize > 0.0 ? MathAbs(closeCurr - currentEMA) / pipSize : 0.0);
bool isBuyPosition = false;
bool isSellPosition = false;
if(hasPosition)
{
if(PositionSelectByMagic(rcData.symbol, RC_MagicNumber))
{
ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if(positionType == POSITION_TYPE_BUY)
isBuyPosition = true;
else if(positionType == POSITION_TYPE_SELL)
isSellPosition = true;
}
}
ApplyTrailingStop();
bool cooldownPassed = (currentTime - rcData.lastTradeTime) >= RC_cooldownSeconds;
const bool isTrendStrong = RC_UseTrendStrengthFilter &&
(MathAbs(emaSlope) > RC_emaSlopeThreshold || priceToEmaPips > RC_emaDistanceThreshold);
if(isBuyPosition && currentRSI > RC_exitBuyRSI)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(isSellPosition && currentRSI < RC_exitSellRSI)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(isTrendStrong)
{
Close_Position_MN(RC_MagicNumber);
rcData.lastTradeTime = currentTime;
}
if(!isTrendStrong &&
currentRSI < RC_overboughtLevel - RC_entryRSISellSpread && rcData.previousRSIDef >= RC_overboughtLevel &&
!isSellPosition && !hasPosition && cooldownPassed)
{
const double vol = United_NormalizeVolume(rcData.symbol, g_RC_LotSize);
if(vol > 0.0)
{
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Sell(vol, rcData.symbol, 0.0, 0.0, 0.0, "Sell Order"))
rcData.lastTradeTime = currentTime;
}
}
if(!isTrendStrong &&
currentRSI > RC_oversoldLevel + RC_entryRSIBuySpread && rcData.previousRSIDef <= RC_oversoldLevel &&
!isBuyPosition && !hasPosition && cooldownPassed)
{
const double vol = United_NormalizeVolume(rcData.symbol, g_RC_LotSize);
if(vol > 0.0)
{
rcData.trade.SetExpertMagicNumber(RC_MagicNumber);
if(rcData.trade.Buy(vol, rcData.symbol, 0.0, 0.0, 0.0, "Buy Order"))
rcData.lastTradeTime = currentTime;
}
}
rcData.previousRSIDef = currentRSI;
}
//+------------------------------------------------------------------+
@@ -0,0 +1,492 @@
//+------------------------------------------------------------------+
//| RSIMidPointHijackStrategy.mqh |
//+------------------------------------------------------------------+
double RM_NormalizedLot(const string sym)
{
return United_NormalizeVolume(sym, g_RM_LotSize);
}
bool IsNewBar(string symbol)
{
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
if(time[0] != rmData.lastBarTime)
{
rmData.lastBarTime = time[0];
return true;
}
}
return false;
}
bool IsWithinTradingHours(int startHour, int endHour)
{
MqlDateTime currentTime;
TimeToStruct(TimeCurrent(), currentTime);
if(startHour <= endHour)
return (currentTime.hour >= startHour && currentTime.hour < endHour);
else
return (currentTime.hour >= startHour || currentTime.hour < endHour);
}
bool HasPosition(string symbol, int magic)
{
return PositionExistsByMagic(symbol, magic);
}
bool HasProfitablePosition(int excludeMagic)
{
bool hasProfitable = false;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(rmData.positionInfo.SelectByIndex(i))
{
if(rmData.positionInfo.Magic() != excludeMagic)
{
double profit = rmData.positionInfo.Profit();
if(profit > RM_InpLockProfitThreshold * _Point)
{
hasProfitable = true;
if(RM_InpCloseOppositeTrades)
{
if((excludeMagic == RM_InpMagicNumberRSIFollow && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse) ||
(excludeMagic == RM_InpMagicNumberRSIReverse && rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow) ||
(excludeMagic == RM_InpMagicNumberEMACross && (rmData.positionInfo.Magic() == RM_InpMagicNumberRSIReverse || rmData.positionInfo.Magic() == RM_InpMagicNumberRSIFollow)) ||
((excludeMagic == RM_InpMagicNumberRSIFollow || excludeMagic == RM_InpMagicNumberRSIReverse) && rmData.positionInfo.Magic() == RM_InpMagicNumberEMACross))
{
ClosePosition(rmData.symbol, (int)rmData.positionInfo.Magic());
}
}
}
}
}
}
return hasProfitable;
}
bool IsRSIReverseInCooldown(string symbol)
{
if(RM_InpRSIReverseCooldownBars <= 0)
return false;
if(!rmData.rsiReverseInCooldown)
return false;
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
datetime currentBarTime = time[0];
datetime cooldownEndTime = rmData.rsiReverseLastCloseTime + RM_InpRSIReverseCooldownBars * PeriodSeconds(RM_InpTimeframe);
if(currentBarTime >= cooldownEndTime)
{
rmData.rsiReverseInCooldown = false;
return false;
}
}
return true;
}
void CheckRSIFollowStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpRSIFollowStartHour, RM_InpRSIFollowEndHour))
{
if(RM_InpRSIFollowCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIFollow))
return;
if(rmData.lastBarRSI > RM_InpRSIOverbought)
rmData.rsiOverbought = true;
else if(rmData.lastBarRSI < RM_InpRSIOversold)
rmData.rsiOversold = true;
if(rmData.rsiOverbought && rmData.lastBarRSI < RM_InpRSIExitLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOverbought = false;
}
else if(rmData.rsiOversold && rmData.lastBarRSI > RM_InpRSIExitLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "RSI Follow");
}
rmData.rsiOversold = false;
}
}
void CheckRSIReverseStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpRSIReverseStartHour, RM_InpRSIReverseEndHour))
{
if(RM_InpRSIReverseCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberRSIReverse))
return;
if(IsRSIReverseInCooldown(symbol))
return;
if(rmData.lastBarRSIReverse > RM_InpRSIReverseOverbought)
rmData.rsiReverseOverbought = true;
else if(rmData.lastBarRSIReverse < RM_InpRSIReverseOversold)
rmData.rsiReverseOversold = true;
if(rmData.rsiReverseOverbought && rmData.lastBarRSIReverse < RM_InpRSIReverseCrossLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOverbought = false;
}
else if(rmData.rsiReverseOversold && rmData.lastBarRSIReverse > RM_InpRSIReverseCrossLevel)
{
if(!HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIReverse);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "RSI Reverse");
}
rmData.rsiReverseOversold = false;
}
}
void CheckEMACrossStrategy(string symbol)
{
if(!IsWithinTradingHours(RM_InpEMACrossStartHour, RM_InpEMACrossEndHour))
{
if(RM_InpEMACrossCloseOutsideHours)
{
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
ClosePosition(symbol, RM_InpMagicNumberEMACross);
}
return;
}
if(RM_InpEnableStrategyLock && HasProfitablePosition(RM_InpMagicNumberEMACross))
return;
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
{
rmData.emaCrossBuySignal = true;
rmData.emaCrossSellSignal = false;
rmData.emaCrossSignalBar = 0;
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
{
rmData.emaCrossSellSignal = true;
rmData.emaCrossBuySignal = false;
rmData.emaCrossSignalBar = 0;
}
if(RM_InpUseEMADistanceEntry)
{
if(rmData.emaCrossBuySignal)
{
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
{
double distance = (closeHistory[i] - emaHistory[i]) / point;
if(distance < RM_InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossBuySignal = false;
}
}
}
else if(rmData.emaCrossSellSignal)
{
bool distanceConditionMet = true;
double emaHistory[], closeHistory[];
ArraySetAsSeries(emaHistory, true);
ArraySetAsSeries(closeHistory, true);
if(CopyBuffer(rmData.emaHandle, 0, 0, RM_InpEMADistancePeriod, emaHistory) > 0 &&
CopyClose(symbol, RM_InpTimeframe, 0, RM_InpEMADistancePeriod, closeHistory) > 0)
{
double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
for(int i = 0; i < RM_InpEMADistancePeriod; i++)
{
double distance = (emaHistory[i] - closeHistory[i]) / point;
if(distance < RM_InpEMADistancePips)
{
distanceConditionMet = false;
break;
}
}
if(distanceConditionMet && !HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "EMA Cross Distance");
rmData.emaCrossSellSignal = false;
}
}
}
}
else
{
if(rmData.lastBarEMAPrev < rmData.lastBarClosePrev && rmData.lastBarEMA > rmData.lastBarClose)
{
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Buy(vol, symbol, 0, 0, 0, "EMA Cross");
}
}
else if(rmData.lastBarEMAPrev > rmData.lastBarClosePrev && rmData.lastBarEMA < rmData.lastBarClose)
{
if(!HasPosition(symbol, RM_InpMagicNumberEMACross))
{
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberEMACross);
const double vol = RM_NormalizedLot(symbol);
if(vol > 0.0)
rmData.trade.Sell(vol, symbol, 0, 0, 0, "EMA Cross");
}
}
}
if(rmData.emaCrossBuySignal || rmData.emaCrossSellSignal)
{
rmData.emaCrossSignalBar++;
if(rmData.emaCrossSignalBar > RM_InpEMADistancePeriod * 2)
{
rmData.emaCrossBuySignal = false;
rmData.emaCrossSellSignal = false;
}
}
}
void CheckExitConditions(string symbol)
{
if(RM_InpEnableRSIFollow)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIFollow))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIFollow))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSI < RM_InpRSIExitLevel) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSI > RM_InpRSIExitLevel))
{
ClosePosition(symbol, RM_InpMagicNumberRSIFollow);
}
}
}
}
if(RM_InpEnableRSIReverse)
{
if(HasPosition(symbol, RM_InpMagicNumberRSIReverse))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberRSIReverse))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarRSIReverse < RM_InpRSIReverseExitLevel) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarRSIReverse > RM_InpRSIReverseExitLevel))
{
ClosePosition(symbol, RM_InpMagicNumberRSIReverse);
}
}
}
}
if(RM_InpEnableEMACross)
{
if(HasPosition(symbol, RM_InpMagicNumberEMACross))
{
if(PositionSelectByMagic(symbol, RM_InpMagicNumberEMACross))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
if((posType == POSITION_TYPE_BUY && rmData.lastBarEMA > rmData.lastBarClose) ||
(posType == POSITION_TYPE_SELL && rmData.lastBarEMA < rmData.lastBarClose))
{
ClosePosition(symbol, RM_InpMagicNumberEMACross);
}
}
}
}
}
void ClosePosition(string symbol, int magic)
{
if(!PositionExistsByMagic(symbol, magic))
return;
ulong ticket = GetPositionTicketByMagic(symbol, magic);
if(ticket == 0)
return;
if(magic == RM_InpMagicNumberRSIReverse)
{
if(PositionSelectByTicketSymbolAndMagic(ticket, symbol, magic))
{
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
{
rmData.rsiReverseLastCloseTime = time[0];
double profit = PositionGetDouble(POSITION_PROFIT);
if(!RM_InpRSIReverseCooldownOnLoss || profit < 0)
{
rmData.rsiReverseInCooldown = true;
}
}
}
}
ClosePositionByMagic(rmData.trade, symbol, magic);
}
bool InitRSIMidPointHijack(string symbol)
{
rmData.symbol = symbol;
rmData.rsiOverbought = false;
rmData.rsiOversold = false;
rmData.rsiReverseOverbought = false;
rmData.rsiReverseOversold = false;
rmData.emaCrossBuySignal = false;
rmData.emaCrossSellSignal = false;
rmData.emaCrossSignalBar = 0;
rmData.rsiReverseInCooldown = false;
rmData.lastBarRSI = 0;
rmData.lastBarRSIReverse = 0;
rmData.lastBarEMA = 0;
rmData.lastBarClose = 0;
rmData.lastBarEMAPrev = 0;
rmData.lastBarClosePrev = 0;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIMidPointHijack: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
Sleep(100); // Wait for symbol to be ready
rmData.rsiHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIPeriod, PRICE_CLOSE);
rmData.rsiReverseHandle = iRSI(symbol, RM_InpTimeframe, RM_InpRSIReversePeriod, PRICE_CLOSE);
rmData.emaHandle = iMA(symbol, RM_InpTimeframe, RM_InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
if(rmData.rsiHandle == INVALID_HANDLE || rmData.rsiReverseHandle == INVALID_HANDLE || rmData.emaHandle == INVALID_HANDLE)
{
Print("RSIMidPointHijack: Error creating indicators for '", symbol, "'");
return false;
}
rmData.trade.SetExpertMagicNumber(RM_InpMagicNumberRSIFollow);
rmData.trade.SetMarginMode();
rmData.trade.SetTypeFillingBySymbol(symbol);
rmData.trade.SetDeviationInPoints(10);
datetime time[];
if(CopyTime(symbol, RM_InpTimeframe, 0, 1, time) > 0)
rmData.lastBarTime = time[0];
rmData.isInitialized = true;
Print("RSIMidPointHijack: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSIMidPointHijack()
{
if(rmData.rsiHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiHandle);
if(rmData.rsiReverseHandle != INVALID_HANDLE) IndicatorRelease(rmData.rsiReverseHandle);
if(rmData.emaHandle != INVALID_HANDLE) IndicatorRelease(rmData.emaHandle);
}
void ProcessRSIMidPointHijack(string symbol)
{
// Skip if not initialized (symbol not available)
if(!rmData.isInitialized)
return;
rmData.symbol = symbol; // Update symbol in case it changed
if(!IsNewBar(rmData.symbol))
return;
double rsi[], rsiReverse[], ema[], close[];
ArraySetAsSeries(rsi, true);
ArraySetAsSeries(rsiReverse, true);
ArraySetAsSeries(ema, true);
ArraySetAsSeries(close, true);
rmData.lastBarEMAPrev = rmData.lastBarEMA;
rmData.lastBarClosePrev = rmData.lastBarClose;
if(CopyBuffer(rmData.rsiHandle, 0, 0, 1, rsi) > 0)
rmData.lastBarRSI = rsi[0];
if(CopyBuffer(rmData.rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
rmData.lastBarRSIReverse = rsiReverse[0];
if(CopyBuffer(rmData.emaHandle, 0, 0, 1, ema) > 0)
rmData.lastBarEMA = ema[0];
if(CopyClose(rmData.symbol, RM_InpTimeframe, 0, 1, close) > 0)
rmData.lastBarClose = close[0];
if(RM_InpEnableRSIFollow)
CheckRSIFollowStrategy(rmData.symbol);
if(RM_InpEnableRSIReverse)
CheckRSIReverseStrategy(rmData.symbol);
if(RM_InpEnableEMACross)
CheckEMACrossStrategy(rmData.symbol);
CheckExitConditions(rmData.symbol);
}
//+------------------------------------------------------------------+
@@ -0,0 +1,488 @@
//+------------------------------------------------------------------+
//| RSIReversalAsianStrategy.mqh |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| RSI Reversal Asian Strategy Data Structure |
//+------------------------------------------------------------------+
struct RSIReversalAsianData {
string symbol;
bool isInitialized;
int rsiHandle;
CTrade trade;
bool isPositionOpen;
double positionOpenPrice;
datetime positionOpenTime;
ENUM_POSITION_TYPE lastPositionType;
bool sessionCloseAttempted;
// RSI crossover variables
double rsiCurrent;
double rsiPrevious;
double rsiPrevious2;
bool rsiCrossedOverbought;
bool rsiCrossedOversold;
bool rsiCrossedExitLevel;
// Strategy parameters
int RSIPeriod;
double OverboughtLevel;
double OversoldLevel;
int TakeProfitPips;
int StopLossPips;
double MaxLotSize;
int MaxSpread;
int MaxDuration;
bool UseStopLoss;
bool UseTakeProfit;
bool UseRSIExit;
double RSIExitLevel;
bool CloseOutsideSession;
ENUM_TIMEFRAMES TimeFrame;
int MagicNumber;
int Slippage;
double point;
};
// Session times (UTC)
const int AsianSessionStart = 0; // 00:00 UTC
const int AsianSessionEnd = 8; // 08:00 UTC
//+------------------------------------------------------------------+
//| Check if current time is in Asian session |
//+------------------------------------------------------------------+
bool IsAsianSession()
{
datetime currentTime = TimeCurrent();
MqlDateTime timeStruct;
TimeToStruct(currentTime, timeStruct);
return (timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd);
}
//+------------------------------------------------------------------+
//| Check if trading is allowed for symbol |
//+------------------------------------------------------------------+
bool IsTradingAllowed(RSIReversalAsianData& data)
{
// Do not require SYMBOL_TRADE_MODE_FULL: many symbols allow one side only (long/short).
const long tradeMode = SymbolInfoInteger(data.symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED || tradeMode == SYMBOL_TRADE_MODE_CLOSEONLY)
return false;
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
return false;
return true;
}
//+------------------------------------------------------------------+
//| Check RSI crossover conditions |
//+------------------------------------------------------------------+
void CheckRSICrossover(RSIReversalAsianData& data)
{
// Reset crossover flags
data.rsiCrossedOverbought = false;
data.rsiCrossedOversold = false;
data.rsiCrossedExitLevel = false;
// Check for overbought crossover (RSI crosses above overbought level)
if(data.rsiPrevious < data.OverboughtLevel && data.rsiCurrent >= data.OverboughtLevel)
{
data.rsiCrossedOverbought = true;
}
// Check for oversold crossover (RSI crosses below oversold level)
if(data.rsiPrevious > data.OversoldLevel && data.rsiCurrent <= data.OversoldLevel)
{
data.rsiCrossedOversold = true;
}
// Check for exit level crossover
if(data.rsiPrevious < data.RSIExitLevel && data.rsiCurrent >= data.RSIExitLevel)
{
data.rsiCrossedExitLevel = true;
}
else if(data.rsiPrevious > data.RSIExitLevel && data.rsiCurrent <= data.RSIExitLevel)
{
data.rsiCrossedExitLevel = true;
}
}
//+------------------------------------------------------------------+
//| Close all trades for the symbol |
//+------------------------------------------------------------------+
bool CloseAllTrades(RSIReversalAsianData& data, string reason = "")
{
bool allClosed = true;
int totalPositions = PositionsTotal();
if(totalPositions == 0)
return true;
for(int i = totalPositions - 1; i >= 0; i--)
{
if(PositionGetSymbol(i) == data.symbol)
{
ulong ticket = PositionGetTicket(i);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
if(PositionGetInteger(POSITION_MAGIC) == (ulong)data.MagicNumber)
{
// Try to close position with retry logic
int retryCount = 0;
bool positionClosed = false;
while(retryCount < 3 && !positionClosed)
{
if(data.trade.PositionClose(ticket))
{
data.isPositionOpen = false;
positionClosed = true;
}
else
{
int error = GetLastError();
// If error is 4756 (Trade disabled), wait longer before retry
if(error == 4756)
{
Sleep(5000); // Wait 5 seconds before retry
retryCount++;
}
else
{
// For other errors, break the loop
break;
}
}
}
if(!positionClosed)
{
allClosed = false;
}
}
}
}
}
return allClosed;
}
//+------------------------------------------------------------------+
//| Initialize RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
bool InitRSIReversalAsian(RSIReversalAsianData& data, string symbol,
int RSIPeriod, double OverboughtLevel, double OversoldLevel,
int TakeProfitPips, int StopLossPips, double MaxLotSize,
int MaxSpread, int MaxDuration, bool UseStopLoss,
bool UseTakeProfit, bool UseRSIExit, double RSIExitLevel,
bool CloseOutsideSession, ENUM_TIMEFRAMES TimeFrame,
int MagicNumber, int Slippage)
{
data.symbol = symbol;
data.isInitialized = false;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIReversalAsian: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false;
}
// Wait a bit for symbol to be ready
Sleep(100);
// Get symbol point
data.point = SymbolInfoDouble(symbol, SYMBOL_POINT);
// Store parameters
data.RSIPeriod = RSIPeriod;
data.OverboughtLevel = OverboughtLevel;
data.OversoldLevel = OversoldLevel;
data.TakeProfitPips = TakeProfitPips;
data.StopLossPips = StopLossPips;
data.MaxLotSize = MaxLotSize;
data.MaxSpread = MaxSpread;
data.MaxDuration = MaxDuration;
data.UseStopLoss = UseStopLoss;
data.UseTakeProfit = UseTakeProfit;
data.UseRSIExit = UseRSIExit;
data.RSIExitLevel = RSIExitLevel;
data.CloseOutsideSession = CloseOutsideSession;
data.TimeFrame = TimeFrame;
data.MagicNumber = MagicNumber;
data.Slippage = Slippage;
// Initialize RSI indicator with retry logic (for insufficient history in backtesting)
data.rsiHandle = INVALID_HANDLE;
int retryCount = 0;
int maxRetries = 5;
while(retryCount < maxRetries && data.rsiHandle == INVALID_HANDLE)
{
data.rsiHandle = iRSI(symbol, TimeFrame, RSIPeriod, PRICE_CLOSE);
if(data.rsiHandle == INVALID_HANDLE)
{
int error = GetLastError();
// Error 4805 = insufficient history - wait longer and retry
if(error == 4805 && retryCount < maxRetries - 1)
{
Sleep(1000); // Wait 1 second for history to load
retryCount++;
continue;
}
Print("RSIReversalAsian: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", error == 4805 ? "Insufficient history data" : "Unknown", ")");
return false;
}
}
if(data.rsiHandle == INVALID_HANDLE)
{
Print("RSIReversalAsian: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
return false;
}
// Wait a bit for the indicator to be ready
Sleep(100);
// Initialize RSI values with retry logic
double rsi[];
ArraySetAsSeries(rsi, true);
retryCount = 0;
bool rsiInitialized = false;
while(retryCount < 10 && !rsiInitialized)
{
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
if(copied >= 3)
{
data.rsiCurrent = rsi[0];
data.rsiPrevious = rsi[1];
data.rsiPrevious2 = rsi[2];
rsiInitialized = true;
}
else
{
retryCount++;
Sleep(100);
}
}
if(!rsiInitialized)
{
// Don't fail initialization, just set default values
data.rsiCurrent = 50.0;
data.rsiPrevious = 50.0;
data.rsiPrevious2 = 50.0;
}
// Set trade parameters
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFillingBySymbol(symbol);
// Initialize state
data.isPositionOpen = false;
data.positionOpenPrice = 0;
data.positionOpenTime = 0;
data.lastPositionType = POSITION_TYPE_BUY;
data.sessionCloseAttempted = false;
data.rsiCrossedOverbought = false;
data.rsiCrossedOversold = false;
data.rsiCrossedExitLevel = false;
data.isInitialized = true;
Print("RSIReversalAsian: Successfully initialized for symbol '", symbol, "'");
return true;
}
//+------------------------------------------------------------------+
//| Deinitialize RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
void DeinitRSIReversalAsian(RSIReversalAsianData& data)
{
if(data.rsiHandle != INVALID_HANDLE)
IndicatorRelease(data.rsiHandle);
}
//+------------------------------------------------------------------+
//| Process RSI Reversal Asian Strategy |
//+------------------------------------------------------------------+
void ProcessRSIReversalAsian(RSIReversalAsianData& data, double lotSize)
{
if(!data.isInitialized)
return;
// Check if trading is allowed
if(!IsTradingAllowed(data))
{
return;
}
// Check if we're in Asian session
if(!IsAsianSession())
{
// Close all positions if outside Asian session and CloseOutsideSession is true
if(data.CloseOutsideSession && !data.sessionCloseAttempted)
{
CloseAllTrades(data, "Outside Asian session");
data.sessionCloseAttempted = true;
}
return;
}
else
{
// Reset the session close attempt flag when we enter Asian session
data.sessionCloseAttempted = false;
}
// Get current spread
double spread = SymbolInfoDouble(data.symbol, SYMBOL_ASK) - SymbolInfoDouble(data.symbol, SYMBOL_BID);
int spreadInPips = (int)(spread / data.point);
// Check if spread is too high
if(spreadInPips > data.MaxSpread)
{
return;
}
// Get RSI values from bar data
double rsi[];
ArraySetAsSeries(rsi, true);
int copied = CopyBuffer(data.rsiHandle, 0, 0, 3, rsi);
if(copied < 3)
{
return;
}
// Update RSI values
data.rsiPrevious2 = data.rsiPrevious;
data.rsiPrevious = data.rsiCurrent;
data.rsiCurrent = rsi[0];
// Validate RSI values
if(data.rsiCurrent == 0 || data.rsiPrevious == 0)
{
return;
}
// Check for RSI crossovers
CheckRSICrossover(data);
// Get current prices
double currentBid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
double currentAsk = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
// Check for open position
bool hasOpenPosition = PositionExistsByMagic(data.symbol, (ulong)data.MagicNumber);
if(hasOpenPosition)
{
// Get position details
ulong ticket = GetPositionTicketByMagic(data.symbol, (ulong)data.MagicNumber);
if(ticket > 0 && PositionSelectByTicket(ticket))
{
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
// Check for RSI exit if enabled
if(data.UseRSIExit && data.rsiCrossedExitLevel)
{
bool shouldExit = false;
// For long positions, exit when RSI crosses above exit level
if(posType == POSITION_TYPE_BUY && data.rsiCurrent >= data.RSIExitLevel && data.rsiPrevious < data.RSIExitLevel)
{
shouldExit = true;
}
// For short positions, exit when RSI crosses below exit level
else if(posType == POSITION_TYPE_SELL && data.rsiCurrent <= data.RSIExitLevel && data.rsiPrevious > data.RSIExitLevel)
{
shouldExit = true;
}
if(shouldExit)
{
CloseAllTrades(data, "RSI Exit Crossover");
return;
}
}
// Check for timeout
if(TimeCurrent() - openTime > data.MaxDuration * 3600)
{
CloseAllTrades(data, "Timeout");
return;
}
}
}
// If no position is open, look for entry signals based on RSI crossover
if(!hasOpenPosition)
{
// Place buy order if RSI crosses below oversold level (oversold crossover)
if(data.rsiCrossedOversold)
{
double sl = data.UseStopLoss ? currentBid - data.StopLossPips * data.point : 0;
double tp = data.UseTakeProfit ? currentBid + data.TakeProfitPips * data.point : 0;
if(data.UseStopLoss && sl >= currentBid)
return;
if(data.UseTakeProfit && tp <= currentBid)
return;
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFillingBySymbol(data.symbol);
data.trade.SetExpertMagicNumber(data.MagicNumber);
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
const double vol = United_NormalizeVolume(data.symbol, tradeLotSize);
if(vol <= 0.0)
return;
if(data.trade.Buy(vol, data.symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentAsk;
data.positionOpenTime = TimeCurrent();
data.lastPositionType = POSITION_TYPE_BUY;
}
}
// Place sell order if RSI crosses above overbought level (overbought crossover)
else if(data.rsiCrossedOverbought)
{
double sl = data.UseStopLoss ? currentAsk + data.StopLossPips * data.point : 0;
double tp = data.UseTakeProfit ? currentAsk - data.TakeProfitPips * data.point : 0;
if(data.UseStopLoss && sl <= currentAsk)
return;
if(data.UseTakeProfit && tp >= currentAsk)
return;
data.trade.SetDeviationInPoints(data.Slippage);
data.trade.SetTypeFillingBySymbol(data.symbol);
data.trade.SetExpertMagicNumber(data.MagicNumber);
double tradeLotSize = lotSize > 0 ? lotSize : data.MaxLotSize;
const double vol = United_NormalizeVolume(data.symbol, tradeLotSize);
if(vol <= 0.0)
return;
if(data.trade.Sell(vol, data.symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
{
data.isPositionOpen = true;
data.positionOpenPrice = currentBid;
data.positionOpenTime = TimeCurrent();
data.lastPositionType = POSITION_TYPE_SELL;
}
}
}
}
@@ -0,0 +1,615 @@
//+------------------------------------------------------------------+
//| RSIScalpingStrategy.mqh |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| RSI Scalping Strategy Data Structure |
//+------------------------------------------------------------------+
struct RSIScalpingData {
string symbol;
bool isInitialized;
CTrade trade;
int rsi_handle;
double rsi_buffer[];
double rsi_prev;
double rsi_current;
double rsi_two_bars_ago;
bool position_open;
ulong position_ticket;
ENUM_POSITION_TYPE current_position_type;
datetime last_bar_time;
bool rsi_against_position;
int bars_against_count;
};
void ClosePosition(RSIScalpingData& data, int MagicNumber);
double RS_ATRPriceOnTF(const string symbol, 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;
}
int RS_CountReversalEscapeSigns(RSIScalpingData& data, const ENUM_TIMEFRAMES tf,
const ENUM_POSITION_TYPE ptype, const double atr,
const double adverseAtrMult, const double rsiVelocity,
const double bodyAtrMult)
{
if(atr <= 0.0)
return 0;
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
const double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
const double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
int signs = 0;
if(ptype == POSITION_TYPE_BUY)
{
if(entry - bid >= adverseAtrMult * atr)
signs++;
if(data.rsi_prev - data.rsi_current >= rsiVelocity)
signs++;
}
else if(ptype == POSITION_TYPE_SELL)
{
if(ask - entry >= adverseAtrMult * atr)
signs++;
if(data.rsi_current - data.rsi_prev >= rsiVelocity)
signs++;
}
else
return 0;
MqlRates r[];
if(CopyRates(data.symbol, tf, 0, 4, r) >= 4)
{
ArraySetAsSeries(r, true);
const double body = MathAbs(r[1].close - r[1].open);
if(body >= bodyAtrMult * 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;
}
void RS_TryReversalEscape(RSIScalpingData& data, const ENUM_TIMEFRAMES tf, const int MagicNumber,
const int atrPeriod, const double adverseAtrMult, const int signsRequired,
const double rsiVelocity, const double bodyAtrMult)
{
if(!PositionSelectByMagic(data.symbol, (ulong)MagicNumber))
return;
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
const double atr = RS_ATRPriceOnTF(data.symbol, tf, atrPeriod);
if(atr <= 0.0)
return;
const int n = RS_CountReversalEscapeSigns(data, tf, ptype, atr, adverseAtrMult, rsiVelocity, bodyAtrMult);
if(n < signsRequired)
return;
ClosePosition(data, MagicNumber);
Print("RSIScalping: reversal escape symbol=", data.symbol, " signs=", n, " need=", signsRequired,
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(data.symbol, SYMBOL_DIGITS)));
}
void RS_ApplyTrailingStop(RSIScalpingData& data, const int MagicNumber,
const bool useTrailingStop,
const double trailingStopDistancePoints,
const double trailingActivationPoints)
{
if(!useTrailingStop || trailingStopDistancePoints <= 0.0)
return;
if(!PositionSelectByMagic(data.symbol, (ulong)MagicNumber))
return;
const double point = SymbolInfoDouble(data.symbol, SYMBOL_POINT);
if(point <= 0.0)
return;
const int digits = (int)SymbolInfoInteger(data.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(data.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(data.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(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
else if(ptype == POSITION_TYPE_SELL)
{
const double ask = SymbolInfoDouble(data.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(data.trade, data.symbol, (ulong)MagicNumber, new_sl, cur_tp);
}
}
string ErrorDescription(int errorCode)
{
switch(errorCode)
{
case 4801: return "Symbol not found";
case 4802: return "Symbol not selected";
case 4803: return "Symbol not visible";
case 4804: return "Symbol not available";
case 4805: return "Cannot load indicator - insufficient history data";
default: return "Unknown error " + IntegerToString(errorCode);
}
}
bool InitRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
ENUM_APPLIED_PRICE RSI_Applied_Price, int MagicNumber, int Slippage)
{
data.symbol = symbol;
data.isInitialized = false;
// Check if symbol exists
if(!SymbolSelect(symbol, true))
{
Print("RSIScalping: Symbol '", symbol, "' not available in Market Watch. Please add it to Market Watch or check symbol name.");
return false; // Return false but don't fail entire EA
}
// Wait a bit for symbol to be ready
Sleep(100);
// Try to create RSI indicator with retry logic (for insufficient history in backtesting)
data.rsi_handle = INVALID_HANDLE;
int retryCount = 0;
int maxRetries = 5;
while(retryCount < maxRetries && data.rsi_handle == INVALID_HANDLE)
{
data.rsi_handle = iRSI(symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
if(data.rsi_handle == INVALID_HANDLE)
{
int error = GetLastError();
// Error 4805 = insufficient history - wait longer and retry
if(error == 4805 && retryCount < maxRetries - 1)
{
Sleep(1000); // Wait 1 second for history to load
retryCount++;
continue;
}
Print("RSIScalping: Error creating RSI indicator for '", symbol, "' - Error: ", error, " (", ErrorDescription(error), ")");
return false; // Return false but don't fail entire EA
}
}
if(data.rsi_handle == INVALID_HANDLE)
{
Print("RSIScalping: Failed to create RSI indicator for '", symbol, "' after ", maxRetries, " retries");
return false;
}
data.trade.SetExpertMagicNumber(MagicNumber);
data.trade.SetDeviationInPoints(Slippage);
data.trade.SetTypeFilling(ORDER_FILLING_FOK);
ArraySetAsSeries(data.rsi_buffer, true);
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
data.isInitialized = true;
Print("RSIScalping: Successfully initialized for symbol '", symbol, "'");
return true;
}
void DeinitRSIScalping(RSIScalpingData& data)
{
if(data.rsi_handle != INVALID_HANDLE)
IndicatorRelease(data.rsi_handle);
}
bool UpdateRSI(RSIScalpingData& data)
{
if(CopyBuffer(data.rsi_handle, 0, 0, 3, data.rsi_buffer) < 3)
return false;
data.rsi_current = data.rsi_buffer[0];
data.rsi_prev = data.rsi_buffer[1];
data.rsi_two_bars_ago = data.rsi_buffer[2];
return true;
}
void CheckExistingPosition(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
double RSI_Oversold, double RSI_Overbought, double RSI_Target_Buy,
double RSI_Target_Sell, int BarsToWait)
{
// Always check if position exists, even if tracking says it doesn't
bool positionExists = PositionExistsByMagic(data.symbol, MagicNumber);
if(!positionExists && data.position_open)
{
// Position was closed externally, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
if(!positionExists)
return;
// Update tracking if we have a position but tracking was lost
if(!data.position_open && positionExists)
{
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
{
data.position_ticket = ticket;
data.position_open = true;
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
// Verify our tracked position still exists
if(data.position_open && data.position_ticket > 0)
{
if(!PositionSelectByTicketSymbolAndMagic(data.position_ticket, data.symbol, MagicNumber))
{
// Try to find the position again
ulong ticket = GetPositionTicketByMagic(data.symbol, MagicNumber);
if(ticket > 0 && PositionSelectByTicketSymbolAndMagic(ticket, data.symbol, MagicNumber))
{
data.position_ticket = ticket;
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
else
{
// Position doesn't exist, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
}
else
{
// Update position type in case it changed (shouldn't happen, but be safe)
data.current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
}
}
if(data.current_position_type == POSITION_TYPE_BUY)
{
if(data.rsi_current < RSI_Oversold)
{
if(!data.rsi_against_position)
{
data.rsi_against_position = true;
data.bars_against_count = 1;
}
else
{
data.bars_against_count++;
}
if(data.bars_against_count >= BarsToWait)
{
ClosePosition(data, MagicNumber);
return;
}
}
else
{
if(data.rsi_against_position)
{
data.rsi_against_position = false;
data.bars_against_count = 0;
}
if(data.rsi_current >= RSI_Target_Buy)
{
ClosePosition(data, MagicNumber);
}
}
}
else if(data.current_position_type == POSITION_TYPE_SELL)
{
if(data.rsi_current > RSI_Overbought)
{
if(!data.rsi_against_position)
{
data.rsi_against_position = true;
data.bars_against_count = 1;
}
else
{
data.bars_against_count++;
}
if(data.bars_against_count >= BarsToWait)
{
ClosePosition(data, MagicNumber);
return;
}
}
else
{
if(data.rsi_against_position)
{
data.rsi_against_position = false;
data.bars_against_count = 0;
}
if(data.rsi_current <= RSI_Target_Sell)
{
ClosePosition(data, MagicNumber);
}
}
}
}
void CheckEntrySignals(RSIScalpingData& data, ENUM_TIMEFRAMES TimeFrame, int MagicNumber,
double RSI_Oversold, double RSI_Overbought, double LotSize)
{
if(data.rsi_two_bars_ago <= RSI_Oversold && data.rsi_prev > RSI_Oversold)
{
OpenBuyPosition(data, MagicNumber, LotSize);
}
if(data.rsi_two_bars_ago >= RSI_Overbought && data.rsi_prev < RSI_Overbought)
{
OpenSellPosition(data, MagicNumber, LotSize);
}
}
//+------------------------------------------------------------------+
//| Normalize Lot Size According to Symbol Properties |
//+------------------------------------------------------------------+
double NormalizeLotSize(string symbol, double lotSize)
{
return United_NormalizeVolume(symbol, lotSize);
}
void OpenBuyPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
if(PositionExistsByMagic(data.symbol, MagicNumber))
return;
// Normalize lot size according to symbol properties
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
double ask = SymbolInfoDouble(data.symbol, SYMBOL_ASK);
if(data.trade.Buy(normalizedLot, data.symbol, ask, 0, 0, "RSI Scalping Buy"))
{
ulong new_ticket = data.trade.ResultOrder();
if(new_ticket > 0)
{
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
{
data.position_ticket = new_ticket;
data.position_open = true;
data.current_position_type = POSITION_TYPE_BUY;
}
}
}
}
void OpenSellPosition(RSIScalpingData& data, int MagicNumber, double LotSize)
{
if(PositionExistsByMagic(data.symbol, MagicNumber))
return;
// Normalize lot size according to symbol properties
double normalizedLot = NormalizeLotSize(data.symbol, LotSize);
double bid = SymbolInfoDouble(data.symbol, SYMBOL_BID);
if(data.trade.Sell(normalizedLot, data.symbol, bid, 0, 0, "RSI Scalping Sell"))
{
ulong new_ticket = data.trade.ResultOrder();
if(new_ticket > 0)
{
if(PositionSelectByTicketSymbolAndMagic(new_ticket, data.symbol, MagicNumber))
{
data.position_ticket = new_ticket;
data.position_open = true;
data.current_position_type = POSITION_TYPE_SELL;
}
}
}
}
void ClosePosition(RSIScalpingData& data, int MagicNumber)
{
// First verify position still exists
if(!PositionExistsByMagic(data.symbol, MagicNumber))
{
// Position doesn't exist, reset tracking
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
return;
}
// Try to close by ticket first (more reliable)
bool closed = false;
if(data.position_ticket > 0)
{
if(PositionSelectByTicket(data.position_ticket))
{
// Verify it's our position
if(PositionGetString(POSITION_SYMBOL) == data.symbol &&
PositionGetInteger(POSITION_MAGIC) == MagicNumber)
{
closed = data.trade.PositionClose(data.position_ticket);
if(!closed)
{
Print("RSIScalping: Failed to close position by ticket ", data.position_ticket,
" - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
}
}
}
}
// If ticket method failed, try magic number method
if(!closed)
{
closed = ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
if(!closed)
{
Print("RSIScalping: Failed to close position by magic number for '", data.symbol,
"' - Error: ", data.trade.ResultRetcode(), " (", data.trade.ResultRetcodeDescription(), ")");
}
}
// Verify position is actually closed
if(closed)
{
// Wait a moment and verify
Sleep(50);
if(!PositionExistsByMagic(data.symbol, MagicNumber))
{
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
Print("RSIScalping: Position successfully closed for '", data.symbol, "'");
}
else
{
Print("RSIScalping: Warning - Close returned success but position still exists for '", data.symbol, "'");
// Try one more time
Sleep(100);
if(PositionExistsByMagic(data.symbol, MagicNumber))
{
ClosePositionByMagic(data.trade, data.symbol, MagicNumber);
}
// Reset tracking anyway to prevent getting stuck
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
}
}
else
{
// Close failed, but reset tracking to prevent getting stuck
// The position might have been closed externally
data.position_open = false;
data.position_ticket = 0;
data.rsi_against_position = false;
data.bars_against_count = 0;
}
}
void ProcessRSIScalping(RSIScalpingData& data, string symbol, ENUM_TIMEFRAMES TimeFrame, int RSI_Period,
ENUM_APPLIED_PRICE RSI_Applied_Price, double RSI_Overbought,
double RSI_Oversold, double RSI_Target_Buy, double RSI_Target_Sell,
int BarsToWait, double LotSize, int MagicNumber,
bool UseReversalEscape, int ReversalATRPeriod, double ReversalAdverseAtrMult,
int ReversalSignsRequired, double ReversalRsiVelocity, double ReversalBodyAtrMult,
bool UseTrailingStop, double TrailingStopDistancePoints, double TrailingActivationPoints)
{
// Skip if not initialized (symbol not available)
if(!data.isInitialized)
return;
data.symbol = symbol; // Update symbol in case it changed
if(Bars(data.symbol, TimeFrame) < RSI_Period + 2)
return;
const datetime current_bar_time = iTime(data.symbol, TimeFrame, 0);
const bool new_bar = (current_bar_time != data.last_bar_time);
const bool in_pos = data.position_open || PositionExistsByMagic(data.symbol, MagicNumber);
if(!in_pos && !new_bar)
return;
if(!UpdateRSI(data))
return;
if(in_pos && UseReversalEscape)
RS_TryReversalEscape(data, TimeFrame, MagicNumber, ReversalATRPeriod, ReversalAdverseAtrMult,
ReversalSignsRequired, ReversalRsiVelocity, ReversalBodyAtrMult);
if(in_pos)
RS_ApplyTrailingStop(data, MagicNumber, UseTrailingStop,
TrailingStopDistancePoints, TrailingActivationPoints);
if(!new_bar)
return;
data.last_bar_time = current_bar_time;
CheckExistingPosition(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought,
RSI_Target_Buy, RSI_Target_Sell, BarsToWait);
if(!data.position_open && !PositionExistsByMagic(data.symbol, MagicNumber))
{
CheckEntrySignals(data, TimeFrame, MagicNumber, RSI_Oversold, RSI_Overbought, LotSize);
}
}
//+------------------------------------------------------------------+
@@ -0,0 +1,336 @@
//+------------------------------------------------------------------+
//| RSISecretSauceStrategy.mqh |
//| Cluster-0 orchestrator: RSI leave extreme then peak/bottom entry |
//+------------------------------------------------------------------+
#ifndef RSI_SECRET_SAUCE_STRATEGY_MQH
#define RSI_SECRET_SAUCE_STRATEGY_MQH
#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>
struct RSISecretSauceOrcData
{
string actualSymbol;
bool isInitialized;
CTrade trade;
CPositionInfo positionInfo;
int rsiHandle;
int atrHandle;
double rsiBuffer[];
double atrBuffer[];
double highBuffer[];
double lowBuffer[];
bool rsiWasOverbought;
bool rsiWasOversold;
bool rsiBackInRange;
datetime lastRSIExitTime;
datetime lastRSIReentryTime;
datetime lastTradeTime;
datetime lastBarTime;
};
bool RSS_UpdateIndicators(RSISecretSauceOrcData &d)
{
int rsiBarsNeeded = RSS_RSILookback + 5;
if(CopyBuffer(d.rsiHandle, 0, 0, rsiBarsNeeded, d.rsiBuffer) < rsiBarsNeeded)
return false;
if(CopyBuffer(d.atrHandle, 0, 0, 2, d.atrBuffer) < 2)
return false;
if(CopyHigh(d.actualSymbol, RSS_Timeframe, 0, RSS_SwingLookback + 5, d.highBuffer) < RSS_SwingLookback + 5)
return false;
if(CopyLow(d.actualSymbol, RSS_Timeframe, 0, RSS_SwingLookback + 5, d.lowBuffer) < RSS_SwingLookback + 5)
return false;
return true;
}
void RSS_UpdateRSIState(RSISecretSauceOrcData &d)
{
double rsiCurrent = d.rsiBuffer[0];
double rsiPrev = d.rsiBuffer[1];
if(rsiPrev >= RSS_RSIOverbought && rsiCurrent < RSS_RSIOverbought)
{
d.rsiWasOverbought = true;
d.rsiBackInRange = true;
d.lastRSIExitTime = TimeCurrent();
d.lastRSIReentryTime = TimeCurrent();
}
if(rsiPrev <= RSS_RSIOversold && rsiCurrent > RSS_RSIOversold)
{
d.rsiWasOversold = true;
d.rsiBackInRange = true;
d.lastRSIExitTime = TimeCurrent();
d.lastRSIReentryTime = TimeCurrent();
}
if(rsiCurrent >= RSS_RSIOverbought)
{
d.rsiWasOverbought = false;
d.rsiBackInRange = false;
}
if(rsiCurrent <= RSS_RSIOversold)
{
d.rsiWasOversold = false;
d.rsiBackInRange = false;
}
}
bool RSS_IsRSIPeak(RSISecretSauceOrcData &d)
{
if(ArraySize(d.rsiBuffer) < RSS_PeakBars + 2)
return false;
double currentRSI = d.rsiBuffer[0];
bool isPeak = true;
for(int i = 1; i <= RSS_PeakBars; i++)
{
if(d.rsiBuffer[i] >= currentRSI)
{
isPeak = false;
break;
}
}
if(d.rsiBuffer[1] >= currentRSI)
isPeak = false;
return isPeak;
}
bool RSS_IsRSIBottom(RSISecretSauceOrcData &d)
{
if(ArraySize(d.rsiBuffer) < RSS_PeakBars + 2)
return false;
double currentRSI = d.rsiBuffer[0];
bool isBottom = true;
for(int i = 1; i <= RSS_PeakBars; i++)
{
if(d.rsiBuffer[i] <= currentRSI)
{
isBottom = false;
break;
}
}
if(d.rsiBuffer[1] <= currentRSI)
isBottom = false;
return isBottom;
}
double RSS_GetSwingStopLoss(RSISecretSauceOrcData &d, double currentPrice, ENUM_POSITION_TYPE type)
{
if(type == POSITION_TYPE_BUY)
{
double lowestLow = d.lowBuffer[0];
for(int i = 1; i < RSS_SwingLookback && i < ArraySize(d.lowBuffer); i++)
{
if(d.lowBuffer[i] < lowestLow)
lowestLow = d.lowBuffer[i];
}
return lowestLow;
}
double highestHigh = d.highBuffer[0];
for(int i = 1; i < RSS_SwingLookback && i < ArraySize(d.highBuffer); i++)
{
if(d.highBuffer[i] > highestHigh)
highestHigh = d.highBuffer[i];
}
return highestHigh;
}
bool RSS_CalculateStops(RSISecretSauceOrcData &d, double price, ENUM_POSITION_TYPE type, double &sl, double &tp)
{
double atrValue = d.atrBuffer[0];
if(atrValue <= 0)
atrValue = price * 0.01;
double slDistance = atrValue * RSS_StopLossATR;
double tpDistance = atrValue * RSS_TakeProfitATR;
int digits = (int)SymbolInfoInteger(d.actualSymbol, SYMBOL_DIGITS);
double point = SymbolInfoDouble(d.actualSymbol, SYMBOL_POINT);
int stopsLevel = (int)SymbolInfoInteger(d.actualSymbol, SYMBOL_TRADE_STOPS_LEVEL);
double minStopDistance = MathMax(stopsLevel * point, point * 10);
if(RSS_UseSwingStopLoss)
{
double swingStop = RSS_GetSwingStopLoss(d, price, type);
if(swingStop > 0)
{
if(type == POSITION_TYPE_BUY)
{
if(swingStop < price && (price - swingStop) > minStopDistance)
slDistance = price - swingStop;
}
else
{
if(swingStop > price && (swingStop - price) > minStopDistance)
slDistance = swingStop - price;
}
}
}
if(slDistance < minStopDistance)
slDistance = minStopDistance;
if(tpDistance < minStopDistance)
tpDistance = minStopDistance;
if(type == POSITION_TYPE_BUY)
{
sl = NormalizeDouble(price - slDistance, digits);
tp = NormalizeDouble(price + tpDistance, digits);
}
else
{
sl = NormalizeDouble(price + slDistance, digits);
tp = NormalizeDouble(price - tpDistance, digits);
}
return true;
}
bool RSS_CanOpenNewPosition(RSISecretSauceOrcData &d)
{
int positionCount = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
if(d.positionInfo.SelectByIndex(i))
{
if(d.positionInfo.Symbol() == d.actualSymbol && d.positionInfo.Magic() == RSS_MagicNumber)
positionCount++;
}
}
if(positionCount >= RSS_MaxPositions)
return false;
if(d.lastTradeTime > 0)
{
int barsSince = Bars(d.actualSymbol, RSS_Timeframe, d.lastTradeTime, TimeCurrent());
if(barsSince < RSS_MinBarsBetweenTrades)
return false;
}
return true;
}
void RSS_OpenPosition(RSISecretSauceOrcData &d, ENUM_POSITION_TYPE type, const double lotSize)
{
double price = (type == POSITION_TYPE_BUY) ?
SymbolInfoDouble(d.actualSymbol, SYMBOL_ASK) :
SymbolInfoDouble(d.actualSymbol, SYMBOL_BID);
if(price <= 0)
return;
double sl = 0.0, tp = 0.0;
if(!RSS_CalculateStops(d, price, type, sl, tp))
return;
string comment = "RSI_Secret_" + (type == POSITION_TYPE_BUY ? "LONG" : "SHORT");
bool result = false;
if(type == POSITION_TYPE_BUY)
result = d.trade.Buy(lotSize, d.actualSymbol, 0, sl, tp, comment);
else
result = d.trade.Sell(lotSize, d.actualSymbol, 0, sl, tp, comment);
if(result)
{
d.lastTradeTime = TimeCurrent();
if(type == POSITION_TYPE_BUY)
d.rsiWasOverbought = false;
else
d.rsiWasOversold = false;
d.rsiBackInRange = false;
}
}
void RSS_CheckEntrySignals(RSISecretSauceOrcData &d, const double lotSize)
{
if(d.rsiWasOverbought && d.rsiBackInRange)
{
if(d.rsiBuffer[0] < RSS_RSIOverbought && RSS_IsRSIPeak(d))
RSS_OpenPosition(d, POSITION_TYPE_BUY, lotSize);
}
if(d.rsiWasOversold && d.rsiBackInRange)
{
if(d.rsiBuffer[0] > RSS_RSIOversold && RSS_IsRSIBottom(d))
RSS_OpenPosition(d, POSITION_TYPE_SELL, lotSize);
}
}
bool InitRSISecretSauce(RSISecretSauceOrcData &d, const string symbol)
{
d.isInitialized = false;
d.rsiHandle = INVALID_HANDLE;
d.atrHandle = INVALID_HANDLE;
d.rsiWasOverbought = false;
d.rsiWasOversold = false;
d.rsiBackInRange = false;
d.lastRSIExitTime = 0;
d.lastRSIReentryTime = 0;
d.lastTradeTime = 0;
d.lastBarTime = 0;
d.actualSymbol = symbol;
StringTrimLeft(d.actualSymbol);
StringTrimRight(d.actualSymbol);
if(StringLen(d.actualSymbol) == 0)
d.actualSymbol = _Symbol;
if(!SymbolSelect(d.actualSymbol, true))
{
Print("RSISecretSauce: symbol not available '", d.actualSymbol, "'");
return false;
}
d.rsiHandle = iRSI(d.actualSymbol, RSS_Timeframe, RSS_RSIPeriod, PRICE_CLOSE);
d.atrHandle = iATR(d.actualSymbol, RSS_Timeframe, RSS_ATRPeriod);
if(d.rsiHandle == INVALID_HANDLE || d.atrHandle == INVALID_HANDLE)
return false;
ArraySetAsSeries(d.rsiBuffer, true);
ArraySetAsSeries(d.atrBuffer, true);
ArraySetAsSeries(d.highBuffer, true);
ArraySetAsSeries(d.lowBuffer, true);
d.trade.SetExpertMagicNumber(RSS_MagicNumber);
d.trade.SetDeviationInPoints(RSS_Slippage);
d.trade.SetTypeFilling(ORDER_FILLING_FOK);
d.isInitialized = true;
return true;
}
void DeinitRSISecretSauce(RSISecretSauceOrcData &d)
{
if(d.rsiHandle != INVALID_HANDLE)
IndicatorRelease(d.rsiHandle);
if(d.atrHandle != INVALID_HANDLE)
IndicatorRelease(d.atrHandle);
d.rsiHandle = INVALID_HANDLE;
d.atrHandle = INVALID_HANDLE;
d.isInitialized = false;
}
void ProcessRSISecretSauce(RSISecretSauceOrcData &d, const double lotSize)
{
if(!d.isInitialized)
return;
int requiredBars = MathMax(RSS_RSILookback, RSS_SwingLookback) + 10;
if(Bars(d.actualSymbol, RSS_Timeframe) < requiredBars)
return;
datetime currentBarTime = iTime(d.actualSymbol, RSS_Timeframe, 0);
if(currentBarTime == d.lastBarTime)
return;
d.lastBarTime = currentBarTime;
if(!RSS_UpdateIndicators(d))
return;
RSS_UpdateRSIState(d);
if(RSS_CanOpenNewPosition(d))
RSS_CheckEntrySignals(d, lotSize);
}
#endif
@@ -0,0 +1,318 @@
//+------------------------------------------------------------------+
//| SimpleTrendlineStrategy.mqh |
//+------------------------------------------------------------------+
#ifndef SIMPLE_TRENDLINE_STRATEGY_MQH
#define SIMPLE_TRENDLINE_STRATEGY_MQH
struct SimpleTrendlineModel
{
datetime t1;
datetime t2;
datetime t3;
double a;
double b;
bool valid;
};
struct SimpleTrendlineData
{
string symbol;
bool isInitialized;
CTrade trade;
ENUM_TIMEFRAMES signalTF;
ENUM_TIMEFRAMES higherTF;
int maPeriod;
ENUM_MA_METHOD maMethod;
ENUM_APPLIED_PRICE appliedPrice;
int htfBarsToScan;
double touchTolerancePoints;
double breakBufferPoints;
ulong magic;
bool drawTrendline;
int maHandle;
datetime lastSignalBarTime;
string lineName;
};
double ST_NormalizeVolume(const string sym, double vol)
{
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
if(step > 0.0)
vol = MathFloor(vol / step) * step;
if(vol < minLot)
vol = minLot;
if(vol > maxLot)
vol = maxLot;
return vol;
}
bool ST_GetPosition(const string sym, const ulong magic, ENUM_POSITION_TYPE &type, double &volume)
{
if(!PositionSelectByMagic(sym, magic))
return false;
type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
volume = PositionGetDouble(POSITION_VOLUME);
return true;
}
int ST_FindRecentCrossPoints(SimpleTrendlineData &d, datetime &times[], double &prices[])
{
ArrayResize(times, 0);
ArrayResize(prices, 0);
if(d.maHandle == INVALID_HANDLE)
return 0;
int needBars = MathMax(d.htfBarsToScan, d.maPeriod + 20);
MqlRates rates[];
double maBuf[];
ArraySetAsSeries(rates, true);
ArraySetAsSeries(maBuf, true);
int copiedRates = CopyRates(d.symbol, d.higherTF, 0, needBars, rates);
int copiedMa = CopyBuffer(d.maHandle, 0, 0, needBars, maBuf);
if(copiedRates <= 5 || copiedMa <= 5)
return 0;
int bars = MathMin(copiedRates, copiedMa);
for(int i = 2; i < bars - 1; i++)
{
double d0 = rates[i].close - maBuf[i];
double d1 = rates[i + 1].close - maBuf[i + 1];
if(d0 == 0.0 || d1 == 0.0 || (d0 * d1 < 0.0))
{
int n = ArraySize(times);
ArrayResize(times, n + 1);
ArrayResize(prices, n + 1);
times[n] = rates[i].time;
prices[n] = rates[i].close;
if(ArraySize(times) >= 3)
break;
}
}
return ArraySize(times);
}
bool ST_BuildTrendline(SimpleTrendlineData &d, SimpleTrendlineModel &m)
{
m.valid = false;
datetime ts[];
double ps[];
if(ST_FindRecentCrossPoints(d, ts, ps) < 3)
return false;
datetime tOld[3];
double pOld[3];
for(int i = 0; i < 3; i++)
{
tOld[i] = ts[2 - i];
pOld[i] = ps[2 - i];
}
long t0 = (long)tOld[0];
double x1 = 0.0;
double x2 = (double)((long)tOld[1] - t0);
double x3 = (double)((long)tOld[2] - t0);
double y1 = pOld[0];
double y2 = pOld[1];
double y3 = pOld[2];
double sx = x1 + x2 + x3;
double sy = y1 + y2 + y3;
double sxx = x1 * x1 + x2 * x2 + x3 * x3;
double sxy = x1 * y1 + x2 * y2 + x3 * y3;
double den = 3.0 * sxx - sx * sx;
if(MathAbs(den) < 1e-10)
return false;
m.a = (3.0 * sxy - sx * sy) / den;
m.b = (sy - m.a * sx) / 3.0;
m.t1 = tOld[0];
m.t2 = tOld[1];
m.t3 = tOld[2];
m.valid = true;
return true;
}
double ST_LinePriceAt(const SimpleTrendlineModel &m, const datetime t)
{
if(!m.valid)
return 0.0;
double x = (double)((long)t - (long)m.t1);
return m.a * x + m.b;
}
void ST_DrawTrendline(SimpleTrendlineData &d, const SimpleTrendlineModel &m)
{
if(!d.drawTrendline || !m.valid || d.symbol != _Symbol)
return;
datetime tStart = m.t1;
datetime tEnd = iTime(d.symbol, d.signalTF, 0);
if(tEnd <= tStart)
tEnd = m.t3 + PeriodSeconds(d.signalTF) * 20;
double pStart = ST_LinePriceAt(m, tStart);
double pEnd = ST_LinePriceAt(m, tEnd);
if(ObjectFind(0, d.lineName) < 0)
ObjectCreate(0, d.lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
else
{
ObjectMove(0, d.lineName, 0, tStart, pStart);
ObjectMove(0, d.lineName, 1, tEnd, pEnd);
}
ObjectSetInteger(0, d.lineName, OBJPROP_RAY_RIGHT, true);
ObjectSetInteger(0, d.lineName, OBJPROP_COLOR, clrGold);
ObjectSetInteger(0, d.lineName, OBJPROP_WIDTH, 2);
}
void ST_TryExitOnBreak(SimpleTrendlineData &d, const SimpleTrendlineModel &m)
{
ENUM_POSITION_TYPE posType;
double vol;
if(!ST_GetPosition(d.symbol, d.magic, posType, vol))
return;
double close1 = iClose(d.symbol, d.signalTF, 1);
datetime t1 = iTime(d.symbol, d.signalTF, 1);
double line1 = ST_LinePriceAt(m, t1);
double buf = d.breakBufferPoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT);
bool closePos = false;
if(posType == POSITION_TYPE_BUY && close1 < (line1 - buf))
closePos = true;
if(posType == POSITION_TYPE_SELL && close1 > (line1 + buf))
closePos = true;
if(closePos)
ClosePositionByMagic(d.trade, d.symbol, d.magic);
}
void ST_TryPullbackEntry(SimpleTrendlineData &d, const SimpleTrendlineModel &m, const double lots)
{
if(PositionExistsByMagic(d.symbol, d.magic))
return;
MqlRates b1[], b2[];
ArraySetAsSeries(b1, true);
ArraySetAsSeries(b2, true);
if(CopyRates(d.symbol, d.signalTF, 1, 1, b1) != 1)
return;
if(CopyRates(d.symbol, d.signalTF, 2, 1, b2) != 1)
return;
if(ArraySize(b1) < 1 || ArraySize(b2) < 1)
return;
double line1 = ST_LinePriceAt(m, b1[0].time);
double tol = d.touchTolerancePoints * SymbolInfoDouble(d.symbol, SYMBOL_POINT);
bool upTrend = (m.a > 0.0);
bool downTrend = (m.a < 0.0);
double vol = ST_NormalizeVolume(d.symbol, lots);
if(upTrend)
{
bool touched = (b1[0].low <= (line1 + tol));
bool reclaim = (b1[0].close > line1);
bool bullish = (b1[0].close > b1[0].open);
bool stillHealthy = (b2[0].close >= ST_LinePriceAt(m, b2[0].time) - tol);
if(touched && reclaim && bullish && stillHealthy)
{
if(!d.trade.Buy(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline BUY"))
Print("SimpleTrendline BUY failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
else if(downTrend)
{
bool touched = (b1[0].high >= (line1 - tol));
bool reject = (b1[0].close < line1);
bool bearish = (b1[0].close < b1[0].open);
bool stillWeak = (b2[0].close <= ST_LinePriceAt(m, b2[0].time) + tol);
if(touched && reject && bearish && stillWeak)
{
if(!d.trade.Sell(vol, d.symbol, 0.0, 0.0, 0.0, "SimpleTrendline SELL"))
Print("SimpleTrendline SELL failed [", d.symbol, "] retcode=", d.trade.ResultRetcode(), " ", d.trade.ResultRetcodeDescription());
}
}
}
bool InitSimpleTrendline(SimpleTrendlineData &d,
const string symbol,
const ENUM_TIMEFRAMES signalTF,
const ENUM_TIMEFRAMES higherTF,
const int maPeriod,
const ENUM_MA_METHOD maMethod,
const ENUM_APPLIED_PRICE appliedPrice,
const int htfBarsToScan,
const double touchTolerancePoints,
const double breakBufferPoints,
const ulong magic,
const bool drawTrendline)
{
d.isInitialized = false;
d.symbol = symbol;
StringTrimLeft(d.symbol);
StringTrimRight(d.symbol);
if(StringLen(d.symbol) == 0)
d.symbol = _Symbol;
if(!SymbolSelect(d.symbol, true))
return false;
d.signalTF = signalTF;
d.higherTF = higherTF;
d.maPeriod = maPeriod;
d.maMethod = maMethod;
d.appliedPrice = appliedPrice;
d.htfBarsToScan = htfBarsToScan;
d.touchTolerancePoints = touchTolerancePoints;
d.breakBufferPoints = breakBufferPoints;
d.magic = magic;
d.drawTrendline = drawTrendline;
d.lastSignalBarTime = 0;
d.lineName = "SimpleTrendline_" + d.symbol + "_" + IntegerToString((int)d.magic);
d.trade.SetExpertMagicNumber((long)d.magic);
d.trade.SetTypeFillingBySymbol(d.symbol);
d.trade.SetDeviationInPoints(20);
d.maHandle = iMA(d.symbol, d.higherTF, d.maPeriod, 0, d.maMethod, d.appliedPrice);
if(d.maHandle == INVALID_HANDLE)
return false;
d.isInitialized = true;
return true;
}
void DeinitSimpleTrendline(SimpleTrendlineData &d)
{
if(d.maHandle != INVALID_HANDLE)
IndicatorRelease(d.maHandle);
d.maHandle = INVALID_HANDLE;
if(ObjectFind(0, d.lineName) >= 0)
ObjectDelete(0, d.lineName);
d.isInitialized = false;
}
void ProcessSimpleTrendline(SimpleTrendlineData &d, const double lots)
{
if(!d.isInitialized)
return;
datetime bar0 = iTime(d.symbol, d.signalTF, 0);
if(bar0 == 0 || bar0 == d.lastSignalBarTime)
return;
d.lastSignalBarTime = bar0;
SimpleTrendlineModel m;
if(!ST_BuildTrendline(d, m))
return;
ST_DrawTrendline(d, m);
ST_TryExitOnBreak(d, m);
ST_TryPullbackEntry(d, m, lots);
}
#endif // SIMPLE_TRENDLINE_STRATEGY_MQH
@@ -0,0 +1,509 @@
//+------------------------------------------------------------------+
//| SuperEMAStrategy.mqh — EMA + CCI + MACD (United EA module) |
//+------------------------------------------------------------------+
#ifndef SUPER_EMA_STRATEGY_MQH
#define SUPER_EMA_STRATEGY_MQH
#include <Trade/Trade.mqh>
enum ENUM_SE_ENTRY_STYLE
{
SE_ENTRY_CCIZERO_MACD = 0,
SE_ENTRY_LAMBERT = 1,
SE_ENTRY_PULLBACK = 2
};
struct SuperEMAData
{
string symbol;
ENUM_TIMEFRAMES tf;
datetime lastBarTime;
CTrade trade;
bool isInitialized;
int slippagePoints;
int magic;
int emaFast;
int emaMid;
int emaSlow;
int emaTrendBars;
int cciPeriod;
double cciOverbought;
double cciOversold;
int pullbackCciLookback;
int macdFast;
int macdSlow;
int macdSignal;
ENUM_SE_ENTRY_STYLE entryStyle;
bool oneTradeOnly;
bool useStructuralSL;
double slBufferPoints;
bool exitOnTrendFlip;
bool exitOnMacdFlip;
bool exitOnCciZeroCross;
int maxHoldingBars;
bool exitBelowMidEma;
bool debugLogs;
};
void SuperEMA_Log(SuperEMAData &d, const string s)
{
if(d.debugLogs)
Print("[SuperEMA] ", s);
}
double SuperEMA_Point(const SuperEMAData &d)
{
double pt = SymbolInfoDouble(d.symbol, SYMBOL_POINT);
return (pt > 0.0 ? pt : _Point);
}
bool SuperEMA_IsNewBar(SuperEMAData &d)
{
datetime t = iTime(d.symbol, d.tf, 0);
if(t <= 0 || t == d.lastBarTime)
return false;
d.lastBarTime = t;
return true;
}
double SuperEMA_EmaAt(SuperEMAData &d, const int period, const int shift)
{
int h = iMA(d.symbol, d.tf, period, 0, MODE_EMA, PRICE_CLOSE);
if(h == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return b[0];
}
double SuperEMA_CciAt(SuperEMAData &d, const int shift)
{
int h = iCCI(d.symbol, d.tf, d.cciPeriod, PRICE_TYPICAL);
if(h == INVALID_HANDLE)
return 0.0;
double b[1];
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
{
IndicatorRelease(h);
return 0.0;
}
IndicatorRelease(h);
return b[0];
}
bool SuperEMA_MacdHistAt(SuperEMAData &d, const int shift, double &hist)
{
int h = iMACD(d.symbol, d.tf, d.macdFast, d.macdSlow, d.macdSignal, PRICE_CLOSE);
if(h == INVALID_HANDLE)
return false;
double mainLine[1], sigLine[1];
if(CopyBuffer(h, 0, shift, 1, mainLine) <= 0 || CopyBuffer(h, 1, shift, 1, sigLine) <= 0)
{
IndicatorRelease(h);
return false;
}
IndicatorRelease(h);
hist = mainLine[0] - sigLine[0];
return true;
}
bool SuperEMA_TrendUp(SuperEMAData &d, const int sh)
{
double c = iClose(d.symbol, d.tf, sh);
double emaS = SuperEMA_EmaAt(d, d.emaSlow, sh);
return (emaS > 0.0 && c > emaS);
}
bool SuperEMA_TrendDown(SuperEMAData &d, const int sh)
{
double c = iClose(d.symbol, d.tf, sh);
double emaS = SuperEMA_EmaAt(d, d.emaSlow, sh);
return (emaS > 0.0 && c < emaS);
}
bool SuperEMA_CciCrossAboveZero(SuperEMAData &d)
{
double c1 = SuperEMA_CciAt(d, 1);
double c2 = SuperEMA_CciAt(d, 2);
return (c2 <= 0.0 && c1 > 0.0);
}
bool SuperEMA_CciCrossBelowZero(SuperEMAData &d)
{
double c1 = SuperEMA_CciAt(d, 1);
double c2 = SuperEMA_CciAt(d, 2);
return (c2 >= 0.0 && c1 < 0.0);
}
bool SuperEMA_CciCrossAbove100(SuperEMAData &d)
{
double c1 = SuperEMA_CciAt(d, 1);
double c2 = SuperEMA_CciAt(d, 2);
return (c2 < d.cciOverbought && c1 > d.cciOverbought);
}
bool SuperEMA_CciCrossBelowMinus100(SuperEMAData &d)
{
double c1 = SuperEMA_CciAt(d, 1);
double c2 = SuperEMA_CciAt(d, 2);
return (c2 > d.cciOversold && c1 < d.cciOversold);
}
bool SuperEMA_HadCciOversoldRecently(SuperEMAData &d)
{
for(int i = 2; i <= d.pullbackCciLookback + 1; i++)
{
double v = SuperEMA_CciAt(d, i);
if(v <= d.cciOversold)
return true;
}
return false;
}
bool SuperEMA_HadCciOverboughtRecently(SuperEMAData &d)
{
for(int i = 2; i <= d.pullbackCciLookback + 1; i++)
{
double v = SuperEMA_CciAt(d, i);
if(v >= d.cciOverbought)
return true;
}
return false;
}
bool SuperEMA_PullbackNearFastEmaLong(SuperEMAData &d)
{
double emaF = SuperEMA_EmaAt(d, d.emaFast, 1);
double lo = iLow(d.symbol, d.tf, 1);
if(emaF <= 0.0)
return false;
const double pt = SuperEMA_Point(d);
return (lo <= emaF + d.slBufferPoints * pt * 3.0);
}
bool SuperEMA_PullbackNearFastEmaShort(SuperEMAData &d)
{
double emaF = SuperEMA_EmaAt(d, d.emaFast, 1);
double hi = iHigh(d.symbol, d.tf, 1);
if(emaF <= 0.0)
return false;
const double pt = SuperEMA_Point(d);
return (hi >= emaF - d.slBufferPoints * pt * 3.0);
}
int SuperEMA_PositionsByMagic(SuperEMAData &d)
{
int n = 0;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong t = PositionGetTicket(i);
if(t == 0)
continue;
if(!PositionSelectByTicket(t))
continue;
if(PositionGetString(POSITION_SYMBOL) == d.symbol && (int)PositionGetInteger(POSITION_MAGIC) == d.magic)
n++;
}
return n;
}
void SuperEMA_ComputeSLTP(SuperEMAData &d, const bool isBuy, double &sl, double &tp)
{
sl = 0.0;
tp = 0.0;
if(!d.useStructuralSL)
return;
double emaM = SuperEMA_EmaAt(d, d.emaMid, d.emaTrendBars);
double buf = d.slBufferPoints * SuperEMA_Point(d);
if(isBuy)
sl = emaM - buf;
else
sl = emaM + buf;
}
int SuperEMA_BarsSinceOpen(SuperEMAData &d, const datetime openTime)
{
if(openTime <= 0)
return 0;
int sh = iBarShift(d.symbol, d.tf, openTime, false);
if(sh < 0)
return 999999;
return sh;
}
void SuperEMA_CloseTicket(SuperEMAData &d, const ulong ticket, const string reason)
{
#ifdef UNITED_MARTINGALE_NO_SELF_CLOSE
return;
#endif
d.trade.SetExpertMagicNumber(d.magic);
if(d.trade.PositionClose(ticket))
SuperEMA_Log(d, "Close: " + reason);
}
void SuperEMA_ManageExits(SuperEMAData &d)
{
#ifdef UNITED_MARTINGALE_NO_SELF_CLOSE
return;
#endif
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(ticket == 0)
continue;
if(!PositionSelectByTicket(ticket))
continue;
if(PositionGetString(POSITION_SYMBOL) != d.symbol)
continue;
if((int)PositionGetInteger(POSITION_MAGIC) != d.magic)
continue;
ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
double h1 = 0.0;
if(!SuperEMA_MacdHistAt(d, 1, h1))
continue;
bool closeLong = false;
bool closeShort = false;
string reason = "";
if(d.maxHoldingBars > 0)
{
int held = SuperEMA_BarsSinceOpen(d, openTime);
if(held >= d.maxHoldingBars)
{
if(ptype == POSITION_TYPE_BUY)
closeLong = true;
else
closeShort = true;
reason = "time stop (max bars)";
}
}
if(ptype == POSITION_TYPE_BUY)
{
if(d.exitOnTrendFlip && SuperEMA_TrendDown(d, d.emaTrendBars))
{
closeLong = true;
reason = "trend flip (below slow EMA)";
}
if(d.exitOnMacdFlip && h1 < 0.0)
{
closeLong = true;
reason = "MACD histogram < 0";
}
if(d.exitOnCciZeroCross && SuperEMA_CciCrossBelowZero(d))
{
closeLong = true;
reason = "CCI crossed below zero";
}
if(d.exitBelowMidEma)
{
double c = iClose(d.symbol, d.tf, 1);
double emaM = SuperEMA_EmaAt(d, d.emaMid, 1);
if(emaM > 0.0 && c < emaM)
{
closeLong = true;
reason = "close below mid EMA";
}
}
if(closeLong)
SuperEMA_CloseTicket(d, ticket, reason);
}
else if(ptype == POSITION_TYPE_SELL)
{
if(d.exitOnTrendFlip && SuperEMA_TrendUp(d, d.emaTrendBars))
{
closeShort = true;
reason = "trend flip (above slow EMA)";
}
if(d.exitOnMacdFlip && h1 > 0.0)
{
closeShort = true;
reason = "MACD histogram > 0";
}
if(d.exitOnCciZeroCross && SuperEMA_CciCrossAboveZero(d))
{
closeShort = true;
reason = "CCI crossed above zero";
}
if(d.exitBelowMidEma)
{
double c = iClose(d.symbol, d.tf, 1);
double emaM = SuperEMA_EmaAt(d, d.emaMid, 1);
if(emaM > 0.0 && c > emaM)
{
closeShort = true;
reason = "close above mid EMA";
}
}
if(closeShort)
SuperEMA_CloseTicket(d, ticket, reason);
}
}
}
bool InitSuperEMA(SuperEMAData &d,
const string symbol,
const ENUM_TIMEFRAMES tf,
const int slippagePoints,
const int magic,
const int emaFast,
const int emaMid,
const int emaSlow,
const int emaTrendBars,
const int cciPeriod,
const double cciOverbought,
const double cciOversold,
const int pullbackCciLookback,
const int macdFast,
const int macdSlow,
const int macdSignal,
const ENUM_SE_ENTRY_STYLE entryStyle,
const bool oneTradeOnly,
const bool useStructuralSL,
const double slBufferPoints,
const bool exitOnTrendFlip,
const bool exitOnMacdFlip,
const bool exitOnCciZeroCross,
const int maxHoldingBars,
const bool exitBelowMidEma,
const bool debugLogs)
{
d.symbol = symbol;
if(StringLen(d.symbol) == 0)
d.symbol = _Symbol;
d.tf = tf;
d.lastBarTime = 0;
d.isInitialized = false;
d.slippagePoints = slippagePoints;
d.magic = magic;
d.emaFast = emaFast;
d.emaMid = emaMid;
d.emaSlow = emaSlow;
d.emaTrendBars = emaTrendBars;
d.cciPeriod = cciPeriod;
d.cciOverbought = cciOverbought;
d.cciOversold = cciOversold;
d.pullbackCciLookback = pullbackCciLookback;
d.macdFast = macdFast;
d.macdSlow = macdSlow;
d.macdSignal = macdSignal;
d.entryStyle = entryStyle;
d.oneTradeOnly = oneTradeOnly;
d.useStructuralSL = useStructuralSL;
d.slBufferPoints = slBufferPoints;
d.exitOnTrendFlip = exitOnTrendFlip;
d.exitOnMacdFlip = exitOnMacdFlip;
d.exitOnCciZeroCross = exitOnCciZeroCross;
d.maxHoldingBars = maxHoldingBars;
d.exitBelowMidEma = exitBelowMidEma;
d.debugLogs = debugLogs;
if(!SymbolSelect(d.symbol, true))
{
Print("SuperEMA: symbol not available: ", d.symbol);
return false;
}
d.trade.SetExpertMagicNumber(d.magic);
d.trade.SetDeviationInPoints(d.slippagePoints);
d.isInitialized = true;
return true;
}
void ProcessSuperEMA(SuperEMAData &d, const double lots)
{
if(!d.isInitialized)
return;
if(!SuperEMA_IsNewBar(d))
return;
SuperEMA_ManageExits(d);
// Same order as standalone SuperEMAXAUUSD: skip entry logic when flat is not allowed.
if(d.oneTradeOnly && SuperEMA_PositionsByMagic(d) > 0)
return;
const int sh = d.emaTrendBars;
double h1 = 0.0;
if(!SuperEMA_MacdHistAt(d, 1, h1))
return;
bool up = SuperEMA_TrendUp(d, sh);
bool dn = SuperEMA_TrendDown(d, sh);
bool wantBuy = false;
bool wantSell = false;
switch(d.entryStyle)
{
case SE_ENTRY_CCIZERO_MACD:
if(up && SuperEMA_CciCrossAboveZero(d) && h1 > 0.0)
wantBuy = true;
if(dn && SuperEMA_CciCrossBelowZero(d) && h1 < 0.0)
wantSell = true;
break;
case SE_ENTRY_LAMBERT:
if(up && SuperEMA_CciCrossAbove100(d) && h1 > 0.0)
wantBuy = true;
if(dn && SuperEMA_CciCrossBelowMinus100(d) && h1 < 0.0)
wantSell = true;
break;
case SE_ENTRY_PULLBACK:
if(up && SuperEMA_HadCciOversoldRecently(d) && SuperEMA_CciCrossAboveZero(d) && h1 > 0.0 && SuperEMA_PullbackNearFastEmaLong(d))
wantBuy = true;
if(dn && SuperEMA_HadCciOverboughtRecently(d) && SuperEMA_CciCrossBelowZero(d) && h1 < 0.0 && SuperEMA_PullbackNearFastEmaShort(d))
wantSell = true;
break;
}
if(!wantBuy && !wantSell)
return;
const double vol = United_NormalizeVolume(d.symbol, lots);
if(vol <= 0.0)
{
SuperEMA_Log(d, "Skip entry: normalized volume <= 0");
return;
}
MqlTick tick;
if(!SymbolInfoTick(d.symbol, tick))
return;
double sl = 0.0, tp = 0.0;
if(wantBuy && !wantSell)
{
#ifndef UNITED_MARTINGALE_NO_SELF_CLOSE
SuperEMA_ComputeSLTP(d, true, sl, tp);
#endif
if(d.trade.Buy(vol, d.symbol, tick.ask, sl, tp, "United SuperEMA long"))
SuperEMA_Log(d, StringFormat("BUY ask=%.5f sl=%.5f", tick.ask, sl));
}
else if(wantSell && !wantBuy)
{
#ifndef UNITED_MARTINGALE_NO_SELF_CLOSE
SuperEMA_ComputeSLTP(d, false, sl, tp);
#endif
if(d.trade.Sell(vol, d.symbol, tick.bid, sl, tp, "United SuperEMA short"))
SuperEMA_Log(d, StringFormat("SELL bid=%.5f sl=%.5f", tick.bid, sl));
}
}
void DeinitSuperEMA(SuperEMAData &d)
{
d.isInitialized = false;
}
#endif // SUPER_EMA_STRATEGY_MQH