|
Before Width: | Height: | Size: 8.4 KiB After Width: | Height: | Size: 8.4 KiB |
@@ -23,14 +23,14 @@ def test_mt5_connection():
|
||||
print("3. Try logging into MT5 manually first")
|
||||
return False
|
||||
|
||||
print("✓ MT5 initialized successfully")
|
||||
print("[OK] MT5 initialized successfully")
|
||||
|
||||
# Get account info
|
||||
account_info = mt5.account_info()
|
||||
if account_info is None:
|
||||
print("WARNING: Could not get account info")
|
||||
else:
|
||||
print(f"✓ Account: {account_info.login}")
|
||||
print(f"[OK] Account: {account_info.login}")
|
||||
print(f" Server: {account_info.server}")
|
||||
print(f" Balance: ${account_info.balance:.2f}")
|
||||
|
||||
@@ -41,9 +41,9 @@ def test_mt5_connection():
|
||||
for symbol in test_symbols:
|
||||
symbol_info = mt5.symbol_info(symbol)
|
||||
if symbol_info is None:
|
||||
print(f"✗ {symbol}: Not available")
|
||||
print(f"[FAIL] {symbol}: Not available")
|
||||
else:
|
||||
print(f"✓ {symbol}: Available")
|
||||
print(f"[OK] {symbol}: Available")
|
||||
print(f" Bid: {symbol_info.bid:.5f}, Ask: {symbol_info.ask:.5f}")
|
||||
print(f" Spread: {symbol_info.spread} points")
|
||||
|
||||
@@ -56,31 +56,34 @@ def test_mt5_connection():
|
||||
|
||||
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
|
||||
if rates is None or len(rates) == 0:
|
||||
print(f"✗ Could not retrieve historical data for {symbol}")
|
||||
print(f"[FAIL] Could not retrieve historical data for {symbol}")
|
||||
print(" Make sure you have historical data in MT5")
|
||||
else:
|
||||
print(f"✓ Retrieved {len(rates)} bars for {symbol}")
|
||||
print(f"[OK] Retrieved {len(rates)} bars for {symbol}")
|
||||
print(f" Date range: {datetime.fromtimestamp(rates[0]['time'])} to {datetime.fromtimestamp(rates[-1]['time'])}")
|
||||
|
||||
# Test indicator creation
|
||||
# Test indicator creation (optional: some MetaTrader5 Python wheels omit terminal indicator APIs)
|
||||
print("\nTesting indicator creation...")
|
||||
rsi_handle = mt5.iRSI(symbol, timeframe, 14, mt5.PRICE_CLOSE)
|
||||
if rsi_handle == mt5.INVALID_HANDLE:
|
||||
print("✗ Failed to create RSI indicator")
|
||||
if not hasattr(mt5, "iRSI") or not hasattr(mt5, "iMA"):
|
||||
print("[SKIP] mt5.iRSI / mt5.iMA not available in this MetaTrader5 build; skipping handle tests.")
|
||||
print(" (Historical data + account checks above are enough for the Python backtest harness.)")
|
||||
else:
|
||||
print("✓ RSI indicator created successfully")
|
||||
# Get RSI values
|
||||
rsi_values = mt5.copy_buffer(rsi_handle, 0, 0, 10)
|
||||
if rsi_values is not None:
|
||||
print(f" Latest RSI values: {rsi_values[-3:]}")
|
||||
mt5.indicator_release(rsi_handle)
|
||||
|
||||
ema_handle = mt5.iMA(symbol, timeframe, 50, 0, mt5.MODE_EMA, mt5.PRICE_CLOSE)
|
||||
if ema_handle == mt5.INVALID_HANDLE:
|
||||
print("✗ Failed to create EMA indicator")
|
||||
else:
|
||||
print("✓ EMA indicator created successfully")
|
||||
mt5.indicator_release(ema_handle)
|
||||
rsi_handle = mt5.iRSI(symbol, timeframe, 14, mt5.PRICE_CLOSE)
|
||||
if rsi_handle == mt5.INVALID_HANDLE:
|
||||
print("[FAIL] Failed to create RSI indicator")
|
||||
else:
|
||||
print("[OK] RSI indicator created successfully")
|
||||
rsi_values = mt5.copy_buffer(rsi_handle, 0, 0, 10)
|
||||
if rsi_values is not None:
|
||||
print(f" Latest RSI values: {rsi_values[-3:]}")
|
||||
mt5.indicator_release(rsi_handle)
|
||||
|
||||
ema_handle = mt5.iMA(symbol, timeframe, 50, 0, mt5.MODE_EMA, mt5.PRICE_CLOSE)
|
||||
if ema_handle == mt5.INVALID_HANDLE:
|
||||
print("[FAIL] Failed to create EMA indicator")
|
||||
else:
|
||||
print("[OK] EMA indicator created successfully")
|
||||
mt5.indicator_release(ema_handle)
|
||||
|
||||
# Cleanup
|
||||
mt5.shutdown()
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
; saved on 2026.04.22
|
||||
; genetic optimization set for DarvasBoxXAUUSD/main.mq5
|
||||
; load in MT5 Strategy Tester -> Inputs -> Load
|
||||
;
|
||||
; === Core Darvas Box Parameters ===
|
||||
BoxPeriod=165||80||5||280||Y
|
||||
BoxDeviation=25140||8000||500||50000||Y
|
||||
VolumeThreshold=938||200||25||2500||Y
|
||||
StopLoss=1665||600||50||4000||Y
|
||||
TakeProfit=3685||1200||75||8000||Y
|
||||
EnableLogging=false||false||0||true||N
|
||||
BoxColor=255||0||1||16777215||N
|
||||
BoxWidth=1||1||1||3||N
|
||||
;
|
||||
; === Trend Confirmation Parameters ===
|
||||
TrendTimeframe=16386||16385||1||16388||Y
|
||||
MA_Period=125||20||5||260||Y
|
||||
MA_Method=1||0||1||3||Y
|
||||
MA_Price=6||0||1||6||Y
|
||||
TrendThreshold=4.94||1.0||0.2||20.0||Y
|
||||
;
|
||||
; === Volume Analysis Parameters ===
|
||||
VolumeMA_Period=110||20||5||220||Y
|
||||
VolumeThresholdMultiplier=1.5||1.0||0.1||3.5||Y
|
||||
;
|
||||
; === Execution / ID ===
|
||||
MagicNumber=135790||135790||1||1357900||N
|
||||
@@ -1,443 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DarvasBox.mq5 |
|
||||
//| Copyright 2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
// Input parameters
|
||||
input int BoxPeriod = 165; // Period for Darvas Box calculation
|
||||
input double BoxDeviation = 25140; // Box deviation in points
|
||||
input int VolumeThreshold = 938; // Minimum volume for confirmation
|
||||
input double StopLoss = 1665; // Stop loss in points (increased for BTCUSD)
|
||||
input double TakeProfit = 3685; // Take profit in points (increased for BTCUSD)
|
||||
input bool EnableLogging = false; // Enable detailed logging
|
||||
input color BoxColor = clrBlue; // Color for Darvas Box
|
||||
input int BoxWidth = 1; // Width of box lines
|
||||
|
||||
// Trend confirmation parameters
|
||||
input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H2; // Timeframe for trend analysis
|
||||
input int MA_Period = 125; // Moving Average period for trend
|
||||
input ENUM_MA_METHOD MA_Method = MODE_EMA; // Moving Average method
|
||||
input ENUM_APPLIED_PRICE MA_Price = PRICE_WEIGHTED; // Price type for MA
|
||||
input double TrendThreshold = 4.94; // Trend strength threshold
|
||||
|
||||
// Volume analysis parameters
|
||||
input int VolumeMA_Period = 110; // Period for Volume MA
|
||||
input double VolumeThresholdMultiplier = 1.5; // Volume spike threshold
|
||||
|
||||
// Magic Number
|
||||
input int MagicNumber = 135790; // Magic Number for Trades
|
||||
|
||||
// Global variables
|
||||
double boxHigh = 0;
|
||||
double boxLow = 0;
|
||||
bool boxFormed = false;
|
||||
datetime lastBoxTime = 0;
|
||||
string boxName = "DarvasBox_";
|
||||
double minStopLevel = 0;
|
||||
double point = 0;
|
||||
CTrade trade;
|
||||
|
||||
// Indicator handles
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize indicators and variables
|
||||
boxHigh = 0;
|
||||
boxLow = 0;
|
||||
boxFormed = false;
|
||||
lastBoxTime = 0;
|
||||
|
||||
// Get symbol properties
|
||||
point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point;
|
||||
|
||||
// Initialize indicators
|
||||
maHandle = iMA(_Symbol, TrendTimeframe, MA_Period, 0, MA_Method, MA_Price);
|
||||
volumeHandle = iVolumes(_Symbol, PERIOD_CURRENT, VOLUME_TICK);
|
||||
|
||||
if(maHandle == INVALID_HANDLE || volumeHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating indicators");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Configure trade object
|
||||
trade.SetDeviationInPoints(10);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
trade.SetAsyncMode(false);
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Darvas Box Expert Advisor initialized");
|
||||
Print("Symbol: ", _Symbol);
|
||||
Print("Point: ", point);
|
||||
Print("Minimum Stop Level: ", minStopLevel);
|
||||
}
|
||||
|
||||
// Delete any existing box objects
|
||||
ObjectsDeleteAll(0, boxName);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw Darvas Box on chart |
|
||||
//+------------------------------------------------------------------+
|
||||
void DrawDarvasBox()
|
||||
{
|
||||
if(!boxFormed) return;
|
||||
|
||||
datetime time1 = iTime(_Symbol, PERIOD_H1, BoxPeriod);
|
||||
datetime time2 = iTime(_Symbol, PERIOD_H1, 0);
|
||||
|
||||
// Delete old box
|
||||
ObjectsDeleteAll(0, boxName);
|
||||
|
||||
// Draw box
|
||||
ObjectCreate(0, boxName + "Top", OBJ_TREND, 0, time1, boxHigh, time2, boxHigh);
|
||||
ObjectCreate(0, boxName + "Bottom", OBJ_TREND, 0, time1, boxLow, time2, boxLow);
|
||||
|
||||
// Set box properties
|
||||
ObjectSetInteger(0, boxName + "Top", OBJPROP_COLOR, BoxColor);
|
||||
ObjectSetInteger(0, boxName + "Bottom", OBJPROP_COLOR, BoxColor);
|
||||
ObjectSetInteger(0, boxName + "Top", OBJPROP_WIDTH, BoxWidth);
|
||||
ObjectSetInteger(0, boxName + "Bottom", OBJPROP_WIDTH, BoxWidth);
|
||||
ObjectSetInteger(0, boxName + "Top", OBJPROP_RAY_RIGHT, true);
|
||||
ObjectSetInteger(0, boxName + "Bottom", OBJPROP_RAY_RIGHT, true);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate Darvas Box levels |
|
||||
//+------------------------------------------------------------------+
|
||||
void CalculateDarvasBox()
|
||||
{
|
||||
double high = 0;
|
||||
double low = DBL_MAX;
|
||||
|
||||
// Find highest high and lowest low in the period
|
||||
for(int i = 0; i < BoxPeriod; i++)
|
||||
{
|
||||
high = MathMax(high, iHigh(_Symbol, PERIOD_H1, i));
|
||||
low = MathMin(low, iLow(_Symbol, PERIOD_H1, i));
|
||||
}
|
||||
|
||||
double range = high - low;
|
||||
double allowedRange = BoxDeviation * _Point;
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Box Calculation - High: ", high, " Low: ", low, " Range: ", range, " Allowed Range: ", allowedRange);
|
||||
}
|
||||
|
||||
// Check if box is formed
|
||||
if(range <= allowedRange)
|
||||
{
|
||||
boxHigh = high;
|
||||
boxLow = low;
|
||||
boxFormed = true;
|
||||
lastBoxTime = iTime(_Symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
// Draw the box
|
||||
DrawDarvasBox();
|
||||
|
||||
if(EnableLogging)
|
||||
Print("Box Formed - High: ", boxHigh, " Low: ", boxLow, " Time: ", lastBoxTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
boxFormed = false;
|
||||
// Delete box if it exists
|
||||
ObjectsDeleteAll(0, boxName);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Validate and adjust stop levels |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ValidateStopLevels(double price, double &sl, double &tp, ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double minSlDistance = MathMax(minStopLevel, StopLoss * point);
|
||||
double minTpDistance = MathMax(minStopLevel, TakeProfit * point);
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Minimum SL Distance: ", minSlDistance);
|
||||
Print("Minimum TP Distance: ", minTpDistance);
|
||||
}
|
||||
|
||||
// Adjust stop loss
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
{
|
||||
sl = price - minSlDistance;
|
||||
tp = price + minTpDistance;
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Buy Order Levels:");
|
||||
Print("Entry: ", price);
|
||||
Print("Stop Loss: ", sl);
|
||||
Print("Take Profit: ", tp);
|
||||
}
|
||||
}
|
||||
else // ORDER_TYPE_SELL
|
||||
{
|
||||
sl = price + minSlDistance;
|
||||
tp = price - minTpDistance;
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Sell Order Levels:");
|
||||
Print("Entry: ", price);
|
||||
Print("Stop Loss: ", sl);
|
||||
Print("Take Profit: ", tp);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check trend direction and strength |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTrendFavorable(ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
double ma[];
|
||||
ArraySetAsSeries(ma, true);
|
||||
|
||||
if(CopyBuffer(maHandle, 0, 0, 2, ma) <= 0)
|
||||
return false;
|
||||
|
||||
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double trendStrength = MathAbs(currentPrice - ma[0]) / point;
|
||||
|
||||
if(EnableLogging)
|
||||
Print("Trend Strength: ", trendStrength);
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
return (currentPrice > ma[0] && trendStrength > TrendThreshold);
|
||||
else
|
||||
return (currentPrice < ma[0] && trendStrength > TrendThreshold);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check volume conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CheckVolumeConditions()
|
||||
{
|
||||
double volumes[];
|
||||
ArraySetAsSeries(volumes, true);
|
||||
|
||||
if(CopyBuffer(volumeHandle, 0, 0, VolumeMA_Period + 1, volumes) <= 0)
|
||||
return false;
|
||||
|
||||
double volumeMA = 0;
|
||||
for(int i = 1; i <= VolumeMA_Period; i++)
|
||||
volumeMA += volumes[i];
|
||||
volumeMA /= VolumeMA_Period;
|
||||
|
||||
double currentVolume = volumes[0];
|
||||
double volumeRatio = currentVolume / volumeMA;
|
||||
|
||||
if(EnableLogging)
|
||||
Print("Volume Ratio: ", volumeRatio);
|
||||
|
||||
return (volumeRatio > VolumeThresholdMultiplier);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Place trade order |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PlaceOrder(ENUM_ORDER_TYPE orderType, double price, double sl, double tp)
|
||||
{
|
||||
// Validate and adjust stop levels
|
||||
if(!ValidateStopLevels(price, sl, tp, orderType))
|
||||
{
|
||||
if(EnableLogging)
|
||||
Print("Invalid stop levels after adjustment");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check trend and volume conditions
|
||||
if(!IsTrendFavorable(orderType))
|
||||
{
|
||||
if(EnableLogging)
|
||||
Print("Trend not favorable for trade");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!CheckVolumeConditions())
|
||||
{
|
||||
if(EnableLogging)
|
||||
Print("Volume conditions not met");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Order Details:");
|
||||
Print("Type: ", EnumToString(orderType));
|
||||
Print("Price: ", price);
|
||||
Print("Stop Loss: ", sl);
|
||||
Print("Take Profit: ", tp);
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
|
||||
if(orderType == ORDER_TYPE_BUY)
|
||||
{
|
||||
result = trade.Buy(0.01, _Symbol, price, sl, tp, "Darvas Box Breakout");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = trade.Sell(0.01, _Symbol, price, sl, tp, "Darvas Box Breakdown");
|
||||
}
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
if(result)
|
||||
Print((orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Placed Successfully");
|
||||
else
|
||||
Print((orderType == ORDER_TYPE_BUY ? "Buy" : "Sell"), " Order Failed - Error: ", trade.ResultRetcode(), " Description: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Calculate new box levels
|
||||
CalculateDarvasBox();
|
||||
|
||||
// Check for trading signals
|
||||
if(boxFormed)
|
||||
{
|
||||
double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double currentVolume = iVolume(_Symbol, PERIOD_CURRENT, 0);
|
||||
|
||||
if(EnableLogging)
|
||||
{
|
||||
Print("Current Price: ", currentPrice, " Box High: ", boxHigh, " Box Low: ", boxLow);
|
||||
Print("Current Volume: ", currentVolume, " Volume Threshold: ", VolumeThreshold);
|
||||
}
|
||||
|
||||
// Check for breakout above box
|
||||
if(currentPrice > boxHigh && currentVolume > VolumeThreshold)
|
||||
{
|
||||
if(EnableLogging)
|
||||
Print("Breakout Signal Detected - Price above box high");
|
||||
|
||||
// Buy signal
|
||||
if(!PositionExistsByMagic(_Symbol, MagicNumber)) // No existing positions with our magic number
|
||||
{
|
||||
double sl = currentPrice - StopLoss * _Point;
|
||||
double tp = currentPrice + TakeProfit * _Point;
|
||||
|
||||
if(EnableLogging)
|
||||
Print("Preparing Buy Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
PlaceOrder(ORDER_TYPE_BUY, currentPrice, sl, tp);
|
||||
}
|
||||
else if(EnableLogging)
|
||||
Print("Skipping Buy Signal - Position already exists");
|
||||
}
|
||||
|
||||
// Check for breakdown below box
|
||||
if(currentPrice < boxLow && currentVolume > VolumeThreshold)
|
||||
{
|
||||
if(EnableLogging)
|
||||
Print("Breakdown Signal Detected - Price below box low");
|
||||
|
||||
// Sell signal
|
||||
if(!PositionExistsByMagic(_Symbol, MagicNumber)) // No existing positions with our magic number
|
||||
{
|
||||
double sl = currentPrice + StopLoss * _Point;
|
||||
double tp = currentPrice - TakeProfit * _Point;
|
||||
|
||||
if(EnableLogging)
|
||||
Print("Preparing Sell Order - Price: ", currentPrice, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
PlaceOrder(ORDER_TYPE_SELL, currentPrice, sl, tp);
|
||||
}
|
||||
else if(EnableLogging)
|
||||
Print("Skipping Sell Signal - Position already exists");
|
||||
}
|
||||
}
|
||||
else if(EnableLogging)
|
||||
Print("No Box Formed - Waiting for consolidation");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get last error description |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetLastErrorDescription()
|
||||
{
|
||||
string errorDescription;
|
||||
switch(GetLastError())
|
||||
{
|
||||
case 0: errorDescription = "No error"; break;
|
||||
case 1: errorDescription = "No error, but result unknown"; break;
|
||||
case 2: errorDescription = "Common error"; break;
|
||||
case 3: errorDescription = "Invalid trade parameters"; break;
|
||||
case 4: errorDescription = "Trade server is busy"; break;
|
||||
case 5: errorDescription = "Old version of the client terminal"; break;
|
||||
case 6: errorDescription = "No connection with trade server"; break;
|
||||
case 7: errorDescription = "Not enough rights"; break;
|
||||
case 8: errorDescription = "Too frequent requests"; break;
|
||||
case 9: errorDescription = "Malfunctional trade operation"; break;
|
||||
case 64: errorDescription = "Account disabled"; break;
|
||||
case 65: errorDescription = "Invalid account"; break;
|
||||
case 128: errorDescription = "Trade timeout"; break;
|
||||
case 129: errorDescription = "Invalid price"; break;
|
||||
case 130: errorDescription = "Invalid stops"; break;
|
||||
case 131: errorDescription = "Invalid trade volume"; break;
|
||||
case 132: errorDescription = "Market is closed"; break;
|
||||
case 133: errorDescription = "Trade is disabled"; break;
|
||||
case 134: errorDescription = "Not enough money"; break;
|
||||
case 135: errorDescription = "Price changed"; break;
|
||||
case 136: errorDescription = "Off quotes"; break;
|
||||
case 137: errorDescription = "Broker is busy"; break;
|
||||
case 138: errorDescription = "Requote"; break;
|
||||
case 139: errorDescription = "Order is locked"; break;
|
||||
case 140: errorDescription = "Long positions only allowed"; break;
|
||||
case 141: errorDescription = "Too many requests"; break;
|
||||
case 145: errorDescription = "Modification denied because order is too close to market"; break;
|
||||
case 146: errorDescription = "Trade context is busy"; break;
|
||||
case 147: errorDescription = "Expirations are denied by broker"; break;
|
||||
case 148: errorDescription = "Amount of open and pending orders has reached the limit"; break;
|
||||
case 149: errorDescription = "Hedging is prohibited"; break;
|
||||
case 150: errorDescription = "Prohibited by FIFO rules"; break;
|
||||
default: errorDescription = "Unknown error"; break;
|
||||
}
|
||||
return errorDescription;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Delete all box objects
|
||||
ObjectsDeleteAll(0, boxName);
|
||||
|
||||
if(EnableLogging)
|
||||
Print("Expert Advisor deinitialized - Reason: ", reason);
|
||||
}
|
||||
@@ -1,628 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMACrossOver.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.01"
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
//--- Eingabeparameter (Input Parameters) - Optimized Profitable Parameters
|
||||
input int EMA_Periode = 50; // EMA Periode
|
||||
input double PreisSchwelle = 700.0; // Preisbewegung Schwelle in Pips
|
||||
input double SteigungSchwelle = 25.0; // EMA Steigung Schwelle in Pips
|
||||
input int ÜberwachungTimeout = 340; // Überwachungszeit in Sekunden
|
||||
input double TrailingStop = 370.0; // Gleitender Stop in Pips
|
||||
input bool UseTrailingStop = true; // Gleitenden Stop anwenden
|
||||
input double TrailingActivationPips = 0.0; // Mindestgewinn in Pips bis Trail startet (0 = Konto-Profit>0)
|
||||
input bool UseStaleStopLossExit = false; // schließen wenn SL zu lange nicht angepasst wurde
|
||||
input int StaleStopLossSeconds = 33800; // Sekunden ohne SL-Änderung -> Close (0 = aus)
|
||||
input double LotGröße = 0.07; // Handelsvolumen
|
||||
input int MagicNumber = 135790; // Magic Number für Trades
|
||||
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
|
||||
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
|
||||
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
|
||||
input int MaxTradesPerCrossover = 3; // Maximale Trades pro Crossover-Ereignis
|
||||
input int ProfitCheckBars = 11; // Bars bis zur Profit-Prüfung
|
||||
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
|
||||
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
|
||||
input int WeeklyADXPeriod = 15; // ADX-Periode auf W1
|
||||
input double WeeklyADXMin = 40.0; // Minimaler ADX fuer Trendfreigabe
|
||||
input int WeeklyADXBarShift = 2; // 1=letzte geschlossene W1-Kerze
|
||||
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
|
||||
|
||||
//--- Globale Variablen (Global Variables)
|
||||
int ema_handle; // EMA Indicator Handle
|
||||
double ema_array[]; // Array für EMA
|
||||
datetime letzte_überwachung_zeit; // Zeit der letzten Überwachung
|
||||
bool überwachung_aktiv = false; // Überwachungsstatus
|
||||
bool preis_trigger_aktiv = false; // Preis-Trigger Status
|
||||
bool steigung_trigger_aktiv = false; // Steigungs-Trigger Status
|
||||
int ticket = 0; // Trade Ticket
|
||||
CTrade trade; // CTrade Objekt
|
||||
int trades_in_current_crossover = 0; // Anzahl Trades im aktuellen Crossover
|
||||
bool crossover_detected = false; // Crossover erkannt
|
||||
datetime trade_open_time = 0; // Zeitpunkt des Trade-Öffnens
|
||||
datetime g_last_sl_adjust_success_time = 0; // letzte erfolgreiche SL-Verschiebung (Stale-Exit)
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Weekly ADX trend filter |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsWeeklyADXTrendFavorable(ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
if(!UseWeeklyADXFilter)
|
||||
return true;
|
||||
|
||||
int adxShift = WeeklyADXBarShift;
|
||||
if(adxShift < 0)
|
||||
adxShift = 0;
|
||||
|
||||
int adx_handle = iADX(_Symbol, PERIOD_W1, WeeklyADXPeriod);
|
||||
if(adx_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("TRACE: Weekly ADX Handle ungültig - Filter blockiert Entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
double adx_buf[], plus_di_buf[], minus_di_buf[];
|
||||
ArraySetAsSeries(adx_buf, true);
|
||||
ArraySetAsSeries(plus_di_buf, true);
|
||||
ArraySetAsSeries(minus_di_buf, true);
|
||||
|
||||
bool ok_adx = (CopyBuffer(adx_handle, 0, adxShift, 1, adx_buf) > 0);
|
||||
bool ok_plus = (CopyBuffer(adx_handle, 1, adxShift, 1, plus_di_buf) > 0);
|
||||
bool ok_minus = (CopyBuffer(adx_handle, 2, adxShift, 1, minus_di_buf) > 0);
|
||||
IndicatorRelease(adx_handle);
|
||||
|
||||
if(!ok_adx || !ok_plus || !ok_minus)
|
||||
{
|
||||
Print("TRACE: Weekly ADX Daten nicht verfügbar - Filter blockiert Entry");
|
||||
return false;
|
||||
}
|
||||
|
||||
double adx_value = adx_buf[0];
|
||||
double plus_di = plus_di_buf[0];
|
||||
double minus_di = minus_di_buf[0];
|
||||
|
||||
bool strength_ok = (adx_value >= WeeklyADXMin);
|
||||
bool direction_ok = true;
|
||||
if(WeeklyADXUseDirection)
|
||||
{
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
direction_ok = (plus_di > minus_di);
|
||||
else
|
||||
direction_ok = (minus_di > plus_di);
|
||||
}
|
||||
|
||||
Print("TRACE: Weekly ADX Filter | ADX=", DoubleToString(adx_value, 2),
|
||||
" +DI=", DoubleToString(plus_di, 2),
|
||||
" -DI=", DoubleToString(minus_di, 2),
|
||||
" strength_ok=", strength_ok,
|
||||
" direction_ok=", direction_ok);
|
||||
|
||||
return (strength_ok && direction_ok);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
//--- CTrade konfigurieren (Configure CTrade)
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(10);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
|
||||
//--- EMA Indicator Handle erstellen (Create EMA indicator handle)
|
||||
ema_handle = iMA(_Symbol, Timeframe, EMA_Periode, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(ema_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Fehler beim Erstellen des EMA Indicators");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
//--- Arrays initialisieren (Initialize arrays)
|
||||
ArraySetAsSeries(ema_array, true);
|
||||
|
||||
//--- Arrays mit aktuellen Werten füllen (Fill arrays with current values)
|
||||
BerechneEMA();
|
||||
|
||||
Print("EMA EA initialisiert - Periode: ", EMA_Periode, " Timeframe: ", EnumToString(Timeframe), " Handle: ", ema_handle);
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//--- Indicator Handle freigeben (Release indicator handle)
|
||||
if(ema_handle != INVALID_HANDLE)
|
||||
{
|
||||
IndicatorRelease(ema_handle);
|
||||
}
|
||||
|
||||
Print("EA beendet - Grund: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
static datetime last_bar_time = 0;
|
||||
const datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
|
||||
const bool new_bar = (current_bar_time != last_bar_time);
|
||||
const bool has_position = PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
|
||||
// Offene Positionen: Management jeden Tick (Trailing / Stale-SL). Sonst bei Bar-Modus nur neuer Bar.
|
||||
if(UseBarData)
|
||||
{
|
||||
if(!new_bar && !has_position)
|
||||
return;
|
||||
if(new_bar)
|
||||
last_bar_time = current_bar_time;
|
||||
}
|
||||
|
||||
//--- EMA Werte berechnen (Calculate EMA values)
|
||||
BerechneEMA();
|
||||
|
||||
const bool run_signals = (!UseBarData || new_bar);
|
||||
|
||||
//--- Debug / Überwachung / Entry nur bei neuem Bar (Bar-Modus) oder jeden Tick (Tick-Modus)
|
||||
if(run_signals && ArraySize(ema_array) > 0)
|
||||
{
|
||||
double aktueller_close = iClose(_Symbol, Timeframe, 0);
|
||||
double ema_aktuell = ema_array[0];
|
||||
double ema_vorher = ema_array[1];
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / _Point;
|
||||
double steigung = (ema_aktuell - ema_vorher) / _Point;
|
||||
|
||||
if(UseBarData)
|
||||
{
|
||||
Print("=== DEBUG INFO (Neuer Bar) ===");
|
||||
Print("Bar Zeit: ", TimeToString(iTime(_Symbol, Timeframe, 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("=== DEBUG INFO (Tick) ===");
|
||||
}
|
||||
|
||||
Print("Aktueller Close: ", aktueller_close);
|
||||
Print("EMA: ", ema_aktuell);
|
||||
Print("Preis-Abstand: ", preis_abstand, " Pips");
|
||||
Print("EMA Steigung: ", steigung, " Pips");
|
||||
Print("Differenz Close-EMA: ", aktueller_close - ema_aktuell);
|
||||
Print("Preis-Trigger: ", preis_trigger_aktiv, " Steigungs-Trigger: ", steigung_trigger_aktiv);
|
||||
Print("Überwachung aktiv: ", überwachung_aktiv);
|
||||
Print("Position offen: ", PositionExistsByMagic(_Symbol, (ulong)MagicNumber));
|
||||
Print("Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
|
||||
Print("==================");
|
||||
}
|
||||
|
||||
if(run_signals)
|
||||
{
|
||||
//--- Überwachung prüfen (Check monitoring)
|
||||
if(überwachung_aktiv)
|
||||
{
|
||||
if(UseBarData)
|
||||
{
|
||||
int bars_since_monitoring = iBarShift(_Symbol, Timeframe, letzte_überwachung_zeit);
|
||||
int timeout_bars = (int)(ÜberwachungTimeout / PeriodSeconds(Timeframe));
|
||||
|
||||
if(bars_since_monitoring > timeout_bars)
|
||||
{
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Bar-basierte Zeitüberschreitung (", bars_since_monitoring, " Bars)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(TimeCurrent() - letzte_überwachung_zeit > ÜberwachungTimeout)
|
||||
{
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
Print("Überwachung beendet - Tick-basierte Zeitüberschreitung");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PrüfeTrigger();
|
||||
}
|
||||
|
||||
VerwalteTrades();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMA Berechnung (EMA Calculation) |
|
||||
//+------------------------------------------------------------------+
|
||||
void BerechneEMA()
|
||||
{
|
||||
//--- EMA Werte vom Indicator kopieren (Copy EMA values from indicator)
|
||||
int copied = CopyBuffer(ema_handle, 0, 0, 3, ema_array);
|
||||
|
||||
if(copied <= 0)
|
||||
{
|
||||
Print("TRACE: Fehler beim Kopieren der EMA Werte - Copied: ", copied);
|
||||
return;
|
||||
}
|
||||
|
||||
Print("TRACE: EMA Werte kopiert: ", copied, " Bars");
|
||||
Print("TRACE: EMA [0]: ", ema_array[0], " [1]: ", ema_array[1], " [2]: ", ema_array[2]);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trigger-Bedingungen prüfen (Check trigger conditions) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeTrigger()
|
||||
{
|
||||
if(ArraySize(ema_array) < 2)
|
||||
{
|
||||
Print("TRACE: Array zu klein - Größe: ", ArraySize(ema_array));
|
||||
return;
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte (Current values)
|
||||
double aktueller_preis = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double aktueller_ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double aktueller_close = iClose(_Symbol, Timeframe, 0);
|
||||
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
|
||||
|
||||
//--- EMA Werte in Variablen (EMA values in variables)
|
||||
double ema_aktuell = ema_array[0];
|
||||
double ema_vorher = ema_array[1];
|
||||
|
||||
//--- EMA Crossover Erkennung (EMA Crossover Detection)
|
||||
// Prüfe ob Preis die EMA kreuzt (Check if price crosses EMA)
|
||||
static double last_close = 0;
|
||||
static double last_ema = 0;
|
||||
|
||||
if(last_close != 0 && last_ema != 0)
|
||||
{
|
||||
bool crossover_bullish = (last_close <= last_ema) && (aktueller_close > ema_aktuell);
|
||||
bool crossover_bearish = (last_close >= last_ema) && (aktueller_close < ema_aktuell);
|
||||
|
||||
//--- Neues Crossover-Ereignis erkannt (New crossover event detected)
|
||||
if(crossover_bullish || crossover_bearish)
|
||||
{
|
||||
trades_in_current_crossover = 0; // Reset trade counter
|
||||
Print("TRACE: EMA Crossover erkannt - ", (crossover_bullish ? "BULLISH" : "BEARISH"), " - Trade-Counter zurückgesetzt");
|
||||
Print("TRACE: Vorher: Close=", last_close, " EMA=", last_ema, " Jetzt: Close=", aktueller_close, " EMA=", ema_aktuell);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Aktuelle Werte für nächsten Vergleich speichern (Save current values for next comparison)
|
||||
last_close = aktueller_close;
|
||||
last_ema = ema_aktuell;
|
||||
|
||||
//--- Preisbewegung zur EMA prüfen (Check price action to EMA)
|
||||
double preis_abstand = MathAbs(aktueller_close - ema_aktuell) / _Point / pips_multiplier;
|
||||
|
||||
Print("TRACE: Preis-Abstand: ", preis_abstand, " Pips (Schwelle: ", PreisSchwelle, ")");
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Trades im aktuellen Crossover: ", trades_in_current_crossover, "/", MaxTradesPerCrossover);
|
||||
|
||||
if(preis_abstand > PreisSchwelle && !preis_trigger_aktiv)
|
||||
{
|
||||
preis_trigger_aktiv = true;
|
||||
Print("TRACE: Preis-Trigger aktiviert: ", preis_abstand, " Pips");
|
||||
}
|
||||
|
||||
//--- EMA Steigung prüfen (Check EMA slope)
|
||||
double steigung = (ema_aktuell - ema_vorher) / _Point / pips_multiplier;
|
||||
|
||||
Print("TRACE: EMA Steigung: ", steigung, " Pips (Schwelle: ", SteigungSchwelle, ")");
|
||||
|
||||
if(MathAbs(steigung) > SteigungSchwelle && !steigung_trigger_aktiv)
|
||||
{
|
||||
steigung_trigger_aktiv = true;
|
||||
Print("TRACE: Steigungs-Trigger aktiviert: ", steigung, " Pips");
|
||||
}
|
||||
|
||||
//--- Überwachung starten wenn beide Trigger aktiv sind (Start monitoring when both triggers are active)
|
||||
if(preis_trigger_aktiv && steigung_trigger_aktiv && !überwachung_aktiv)
|
||||
{
|
||||
überwachung_aktiv = true;
|
||||
|
||||
if(UseBarData)
|
||||
{
|
||||
letzte_überwachung_zeit = iTime(_Symbol, Timeframe, 0); // Aktuelle Bar-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Bar: ", TimeToString(letzte_überwachung_zeit), ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
letzte_überwachung_zeit = TimeCurrent(); // Aktuelle Tick-Zeit
|
||||
Print("TRACE: Überwachung gestartet - Beide Trigger aktiv (Tick)");
|
||||
}
|
||||
}
|
||||
|
||||
//--- Trade platzieren wenn Überwachung aktiv und Preis über/unter EMA (Place trade when monitoring active and price above/below EMA)
|
||||
if(überwachung_aktiv)
|
||||
{
|
||||
bool bullish_signal = aktueller_close > ema_aktuell;
|
||||
bool bearish_signal = aktueller_close < ema_aktuell;
|
||||
|
||||
Print("TRACE: Signal Check - Bullish: ", bullish_signal, " Bearish: ", bearish_signal);
|
||||
Print("TRACE: Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
Print("TRACE: Differenz: ", aktueller_close - ema_aktuell);
|
||||
|
||||
//--- Trade-Limit prüfen (Check trade limit)
|
||||
if(trades_in_current_crossover >= MaxTradesPerCrossover)
|
||||
{
|
||||
Print("TRACE: Trade-Limit erreicht (", MaxTradesPerCrossover, ") - Kein neuer Trade");
|
||||
return;
|
||||
}
|
||||
|
||||
if(bullish_signal && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_BUY))
|
||||
{
|
||||
Print("TRACE: Weekly ADX blockiert BUY-Entry");
|
||||
return;
|
||||
}
|
||||
Print("TRACE: Versuche KAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_BUY))
|
||||
{
|
||||
trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(bearish_signal && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
if(!IsWeeklyADXTrendFavorable(ORDER_TYPE_SELL))
|
||||
{
|
||||
Print("TRACE: Weekly ADX blockiert SELL-Entry");
|
||||
return;
|
||||
}
|
||||
Print("TRACE: Versuche VERKAUF-Trade zu platzieren (Trade #", trades_in_current_crossover + 1, ")");
|
||||
if(PlatziereTrade(ORDER_TYPE_SELL))
|
||||
{
|
||||
trades_in_current_crossover++;
|
||||
}
|
||||
}
|
||||
else if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
Print("TRACE: Position bereits offen - kein neuer Trade");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade platzieren (Place trade) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PlatziereTrade(ENUM_ORDER_TYPE order_type)
|
||||
{
|
||||
Print("TRACE: Versuche Trade zu platzieren - Typ: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF");
|
||||
Print("TRACE: Lot: ", LotGröße);
|
||||
|
||||
bool success = false;
|
||||
|
||||
if(order_type == ORDER_TYPE_BUY)
|
||||
{
|
||||
success = trade.Buy(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
else
|
||||
{
|
||||
success = trade.Sell(LotGröße, _Symbol, 0, 0, 0, "EMA Crossover Trade");
|
||||
}
|
||||
|
||||
if(success)
|
||||
{
|
||||
ticket = (int)trade.ResultOrder();
|
||||
Print("TRACE: Trade erfolgreich platziert: ", (order_type == ORDER_TYPE_BUY) ? "KAUF" : "VERKAUF", " Ticket: ", ticket);
|
||||
|
||||
//--- Trade-Öffnungszeit speichern (Save trade opening time)
|
||||
trade_open_time = iTime(_Symbol, Timeframe, 0);
|
||||
g_last_sl_adjust_success_time = 0;
|
||||
Print("TRACE: Trade-Öffnungszeit: ", TimeToString(trade_open_time));
|
||||
|
||||
//--- Überwachung zurücksetzen (Reset monitoring)
|
||||
überwachung_aktiv = false;
|
||||
preis_trigger_aktiv = false;
|
||||
steigung_trigger_aktiv = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Platzieren des Trades - Retcode: ", trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Mindestgewinn fuer Trailing erreicht? |
|
||||
//+------------------------------------------------------------------+
|
||||
bool TrailingActivationReached(const double position_profit, const ENUM_POSITION_TYPE position_type,
|
||||
const double pips_multiplier)
|
||||
{
|
||||
if(TrailingActivationPips <= 0.0)
|
||||
return (position_profit > 0.0);
|
||||
|
||||
const double open_px = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
return ((bid - open_px) / _Point / pips_multiplier >= TrailingActivationPips);
|
||||
}
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
return ((open_px - ask) / _Point / pips_multiplier >= TrailingActivationPips);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trades verwalten (Manage trades) |
|
||||
//+------------------------------------------------------------------+
|
||||
void VerwalteTrades()
|
||||
{
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
if(UseStaleStopLossExit && StaleStopLossSeconds > 0)
|
||||
{
|
||||
const datetime stale_ref = (g_last_sl_adjust_success_time > 0)
|
||||
? g_last_sl_adjust_success_time
|
||||
: (datetime)PositionGetInteger(POSITION_TIME);
|
||||
if(TimeCurrent() - stale_ref >= StaleStopLossSeconds)
|
||||
{
|
||||
SchließePosition("Stale stop loss - keine SL-Anpassung");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
double pips_multiplier = (_Digits == 3 || _Digits == 5) ? 10.0 : 1.0;
|
||||
const double trail_dist = TrailingStop * _Point * pips_multiplier;
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * _Point;
|
||||
|
||||
//--- Gleitender Stop (Trailing Stop)
|
||||
if(UseTrailingStop && TrailingStop > 0.0 && TrailingActivationReached(position_profit, position_type, pips_multiplier))
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double new_stop_loss = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_stop_loss < min_dist)
|
||||
new_stop_loss = NormalizeDouble(bid - min_dist, digits);
|
||||
const double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
if(new_stop_loss < bid && new_stop_loss > 0.0 && new_stop_loss > current_stop_loss)
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double new_stop_loss = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_stop_loss - ask < min_dist)
|
||||
new_stop_loss = NormalizeDouble(ask + min_dist, digits);
|
||||
const double current_stop_loss = PositionGetDouble(POSITION_SL);
|
||||
if(new_stop_loss > ask && new_stop_loss > 0.0 &&
|
||||
(new_stop_loss < current_stop_loss || current_stop_loss == 0.0))
|
||||
ÄndereStopLoss(new_stop_loss);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Ausstieg bei Preis unter/über EMA (Exit when price below/above EMA)
|
||||
if(ArraySize(ema_array) >= 1)
|
||||
{
|
||||
double aktueller_close = iClose(_Symbol, Timeframe, 0);
|
||||
double ema_aktuell = ema_array[0];
|
||||
bool exit_bullish = (position_type == POSITION_TYPE_SELL && aktueller_close > ema_aktuell);
|
||||
bool exit_bearish = (position_type == POSITION_TYPE_BUY && aktueller_close < ema_aktuell);
|
||||
|
||||
if(exit_bullish || exit_bearish)
|
||||
{
|
||||
Print("TRACE: Ausstiegssignal - Close: ", aktueller_close, " EMA: ", ema_aktuell);
|
||||
SchließePosition("EMA Crossover Exit");
|
||||
|
||||
Print("TRACE: Position geschlossen - Trade-Counter bleibt bei ", trades_in_current_crossover);
|
||||
}
|
||||
}
|
||||
|
||||
//--- Profit-Prüfung nach X Bars (Profit check after X bars)
|
||||
if(CloseUnprofitableTrades && trade_open_time != 0 && PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung aktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
|
||||
PrüfeProfitNachBars();
|
||||
}
|
||||
else if(!CloseUnprofitableTrades)
|
||||
{
|
||||
Print("TRACE: Profit-Prüfung deaktiviert - CloseUnprofitableTrades: ", CloseUnprofitableTrades);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Profit-Prüfung nach X Bars (Profit check after X bars) |
|
||||
//+------------------------------------------------------------------+
|
||||
void PrüfeProfitNachBars()
|
||||
{
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Keine Position offen
|
||||
}
|
||||
|
||||
datetime current_bar_time = iTime(_Symbol, Timeframe, 0);
|
||||
int bars_since_trade_open = iBarShift(_Symbol, Timeframe, trade_open_time);
|
||||
|
||||
Print("TRACE: Bars seit Trade-Öffnung: ", bars_since_trade_open, "/", ProfitCheckBars);
|
||||
|
||||
//--- Prüfe ob genügend Bars vergangen sind (Check if enough bars have passed)
|
||||
if(bars_since_trade_open >= ProfitCheckBars)
|
||||
{
|
||||
double position_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
double position_volume = PositionGetDouble(POSITION_VOLUME);
|
||||
ENUM_POSITION_TYPE position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
Print("TRACE: Profit-Prüfung nach ", ProfitCheckBars, " Bars");
|
||||
Print("TRACE: Position Profit: ", position_profit, " USD");
|
||||
|
||||
//--- Schließe Position wenn nicht im Profit (Close position if not in profit)
|
||||
if(position_profit <= 0)
|
||||
{
|
||||
Print("TRACE: Position nicht im Profit - Schließe Position");
|
||||
SchließePosition("Profit Check - Unprofitable");
|
||||
|
||||
//--- Trade-Öffnungszeit zurücksetzen (Reset trade opening time)
|
||||
trade_open_time = 0;
|
||||
Print("TRACE: Trade-Öffnungszeit zurückgesetzt");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Position im Profit - Behalte Position");
|
||||
//--- Trade-Öffnungszeit zurücksetzen um weitere Prüfungen zu vermeiden (Reset to avoid further checks)
|
||||
trade_open_time = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Stop Loss ändern (Modify Stop Loss) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ÄndereStopLoss(double new_stop_loss)
|
||||
{
|
||||
Print("TRACE: Versuche Stop Loss zu ändern auf: ", new_stop_loss);
|
||||
|
||||
bool success = ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_stop_loss, PositionGetDouble(POSITION_TP));
|
||||
|
||||
if(success)
|
||||
{
|
||||
g_last_sl_adjust_success_time = TimeCurrent();
|
||||
Print("TRACE: Stop Loss erfolgreich geändert auf: ", new_stop_loss);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Ändern des Stop Loss - Retcode: ", trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Position schließen (Close position) |
|
||||
//+------------------------------------------------------------------+
|
||||
void SchließePosition(string reason = "Unbekannt")
|
||||
{
|
||||
Print("TRACE: Versuche Position zu schließen - Grund: ", reason);
|
||||
|
||||
bool success = ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber);
|
||||
|
||||
if(success)
|
||||
{
|
||||
g_last_sl_adjust_success_time = 0;
|
||||
Print("TRACE: Position erfolgreich geschlossen - Grund: ", reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("TRACE: Fehler beim Schließen der Position - Retcode: ", trade.ResultRetcode());
|
||||
Print("TRACE: Fehlerbeschreibung: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
Before Width: | Height: | Size: 30 KiB |
@@ -1,295 +0,0 @@
|
||||
// Input Parameters
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
input group "Trade Management"
|
||||
input int MagicNumber = 7;
|
||||
input int rsiPeriod = 19; // RSI period
|
||||
input int overboughtLevel = 93; // Overbought level (RSI > 70 for sell)
|
||||
input int oversoldLevel = 22; // Oversold level (RSI < 30 for buy)
|
||||
input double entryRSIBuySpread = 0;
|
||||
input double entryRSISellSpread = 0;
|
||||
input double lotSize = 0.1; // Trade lot size
|
||||
input int slippage = 3; // Slippage for orders
|
||||
input int cooldownSeconds = 209; // Cooldown period in seconds
|
||||
input ENUM_TIMEFRAMES TimeFrame1 = PERIOD_M1; // RSI Timeframe
|
||||
input ENUM_TIMEFRAMES TimeFrame2 = PERIOD_M1; // EMA Timeframe
|
||||
input ENUM_TIMEFRAMES BarTimeFrame = PERIOD_M12; // EMA Timeframe
|
||||
input int emaPeriod = 140; // EMA period
|
||||
input double emaSlopeThreshold = 105; // EMA slope threshold for trend strength
|
||||
input double exitBuyRSI = 86;
|
||||
input double exitSellRSI = 10;
|
||||
input double TrailingStop = 295;
|
||||
input double emaDistanceThreshold = 165;
|
||||
input int tradingHourOneBegin = 24;
|
||||
input int tradingHourOneEnd = 22;
|
||||
input int tradingHourTwoBegin = 6;
|
||||
input int tradingHourTwoEnd = 19;
|
||||
datetime bartime;
|
||||
// RSI Handle
|
||||
int rsiHandle;
|
||||
|
||||
input bool Sunday =false; // Sunday
|
||||
input bool Monday =false; // Monday
|
||||
input bool Tuesday =true; // Tuesday
|
||||
input bool Wednesday=true; // Wednesday
|
||||
input bool Thursday =true; // Thursday
|
||||
input bool Friday =false; // Friday
|
||||
input bool Saturday =false; // Saturday
|
||||
|
||||
bool WeekDays[7];
|
||||
|
||||
void WeekDays_Init()
|
||||
{
|
||||
WeekDays[0]=Sunday;
|
||||
WeekDays[1]=Monday;
|
||||
WeekDays[2]=Tuesday;
|
||||
WeekDays[3]=Wednesday;
|
||||
WeekDays[4]=Thursday;
|
||||
WeekDays[5]=Friday;
|
||||
WeekDays[6]=Saturday;
|
||||
}
|
||||
|
||||
bool WeekDays_Check(datetime aTime)
|
||||
{
|
||||
MqlDateTime stm;
|
||||
TimeToStruct(aTime,stm);
|
||||
return(WeekDays[stm.day_of_week]);
|
||||
}
|
||||
|
||||
|
||||
// EMA Handle
|
||||
int emaHandle;
|
||||
double previousRSIDef = 0;
|
||||
// Create CTrade object for executing trades
|
||||
CTrade trade;
|
||||
|
||||
// Track the last trade time
|
||||
datetime lastTradeTime = 0;
|
||||
|
||||
void OnInit() {
|
||||
WeekDays_Init();
|
||||
|
||||
// Create RSI handle
|
||||
rsiHandle = iRSI(_Symbol, TimeFrame1, rsiPeriod, PRICE_CLOSE);
|
||||
if (rsiHandle == INVALID_HANDLE) {
|
||||
Print("Error creating RSI handle: ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Create EMA handle
|
||||
emaHandle = iMA(_Symbol, TimeFrame2, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if (emaHandle == INVALID_HANDLE) {
|
||||
Print("Error creating EMA handle: ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialization successful
|
||||
Print("RSI and EMA Reversal Strategy Initialized.");
|
||||
}
|
||||
|
||||
void OnTick() {
|
||||
if(bartime==iTime(_Symbol,BarTimeFrame,0))return;
|
||||
bartime=iTime(_Symbol,BarTimeFrame,0);
|
||||
|
||||
// Check if RSI data is available
|
||||
double rsi[];
|
||||
if (CopyBuffer(rsiHandle, 0, 0, 2, rsi) <= 0) {
|
||||
Print("Error copying RSI data: ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if EMA data is available
|
||||
double ema[];
|
||||
if (CopyBuffer(emaHandle, 0, 0, 2, ema) <= 0) {
|
||||
Print("Error copying EMA data: ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current time
|
||||
datetime currentTime = TimeCurrent();
|
||||
|
||||
|
||||
int currentHour = TimeHour(TimeCurrent());
|
||||
|
||||
if(!WeekDays_Check(TimeTradeServer())) {
|
||||
Close_Position_MN(MagicNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(currentHour < tradingHourOneEnd && currentHour > tradingHourOneBegin || currentHour < tradingHourTwoEnd && currentHour > tradingHourTwoBegin))
|
||||
{
|
||||
|
||||
Close_Position_MN(MagicNumber);
|
||||
return; // Prevent further trading during this time
|
||||
}
|
||||
|
||||
|
||||
// Ensure there is at least one position
|
||||
bool hasPosition = PositionExistsByMagic(_Symbol, MagicNumber);
|
||||
|
||||
|
||||
|
||||
// Get the current and previous RSI values
|
||||
double currentRSI = rsi[0];
|
||||
double previousRSI = rsi[1];
|
||||
|
||||
if(previousRSIDef == 0) {
|
||||
previousRSIDef = currentRSI;
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current and previous EMA values
|
||||
double currentEMA = ema[0];
|
||||
double previousEMA = ema[1];
|
||||
|
||||
// Calculate the EMA slope (difference between current and previous EMA values)
|
||||
double emaSlope = (currentEMA - previousEMA) * 100;
|
||||
Print(emaSlope);
|
||||
|
||||
double closeCurr = iClose(Symbol(), Period(), 0); // Close of current bar
|
||||
// ** NEW CODE: Calculate distance to EMA and adjust score **
|
||||
double priceToEmaDistance = (closeCurr - currentEMA) * 10; // Distance between the current price and the EMA
|
||||
Print("priceToEmaDistance");
|
||||
Print(priceToEmaDistance);
|
||||
|
||||
|
||||
// Determine if there are existing buy or sell positions
|
||||
bool isBuyPosition = false;
|
||||
bool isSellPosition = false;
|
||||
if (hasPosition) {
|
||||
if (PositionSelectByMagic(_Symbol, MagicNumber)) {
|
||||
int positionType = PositionGetInteger(POSITION_TYPE);
|
||||
if (positionType == POSITION_TYPE_BUY) {
|
||||
isBuyPosition = true;
|
||||
} else if (positionType == POSITION_TYPE_SELL) {
|
||||
isSellPosition = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ApplyTrailingStop();
|
||||
|
||||
// Check if the cooldown period has elapsed since the last trade
|
||||
bool cooldownPassed = (currentTime - lastTradeTime) >= cooldownSeconds;
|
||||
|
||||
// Check if EMA slope is above the threshold (indicating strong trend)
|
||||
bool isTrendStrong = MathAbs(emaSlope) > emaSlopeThreshold || MathAbs(priceToEmaDistance) > emaDistanceThreshold;
|
||||
|
||||
// Close trade logic when RSI crosses 50
|
||||
if (isBuyPosition && currentRSI > exitBuyRSI) {
|
||||
// Close buy position
|
||||
Close_Position_MN(MagicNumber);
|
||||
lastTradeTime = currentTime; // Update last trade time
|
||||
}
|
||||
|
||||
if (isSellPosition && currentRSI < exitSellRSI) {
|
||||
Close_Position_MN(MagicNumber);
|
||||
lastTradeTime = currentTime; // Update last trade time
|
||||
|
||||
}
|
||||
|
||||
|
||||
// If the EMA slope is strong, do not place new trades
|
||||
if (isTrendStrong) {
|
||||
Close_Position_MN(MagicNumber);
|
||||
lastTradeTime = currentTime; // Update last trade time
|
||||
Print("Strong trend detected (EMA slope), skipping new trade.");
|
||||
return;
|
||||
}
|
||||
|
||||
// SELL logic (RSI crosses over the overbought level)
|
||||
if (currentRSI < overboughtLevel - entryRSISellSpread && previousRSIDef >= overboughtLevel && !isSellPosition && !hasPosition && cooldownPassed) {
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
if (trade.Sell(lotSize, _Symbol, 0, 0, "Sell Order")) {
|
||||
Print("Sell order placed.");
|
||||
lastTradeTime = currentTime; // Update last trade time
|
||||
} else {
|
||||
Print("Error placing sell order: ", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
// BUY logic (RSI crosses below the oversold level)
|
||||
if (currentRSI > oversoldLevel + entryRSIBuySpread && previousRSIDef <= oversoldLevel && !isBuyPosition && !hasPosition && cooldownPassed) {
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
if (trade.Buy(lotSize, _Symbol, 0, 0, "Buy Order")) {
|
||||
Print("Buy order placed.");
|
||||
lastTradeTime = currentTime; // Update last trade time
|
||||
} else {
|
||||
Print("Error placing buy order: ", GetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
previousRSIDef = currentRSI;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason) {
|
||||
// Release RSI and EMA handles on deinitialization
|
||||
if (rsiHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(rsiHandle);
|
||||
Print("RSI handle released.");
|
||||
}
|
||||
if (emaHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(emaHandle);
|
||||
Print("EMA handle released.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Close_Position_MN(ulong magicNumber)
|
||||
{
|
||||
// Use helper function to close position by magic number
|
||||
ClosePositionByMagic(trade, _Symbol, (int)magicNumber);
|
||||
}
|
||||
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
Print("Scanning for trailing stop");
|
||||
|
||||
// Check if position exists with our magic number
|
||||
if(!PositionSelectByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
return; // No position with our magic number
|
||||
}
|
||||
|
||||
ulong PositionTicket = PositionGetInteger(POSITION_TICKET);
|
||||
long trade_type = PositionGetInteger(POSITION_TYPE);
|
||||
string symbol = _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 * TrailingStop, DIGIT))
|
||||
{
|
||||
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop, DIGIT))
|
||||
{
|
||||
ModifyPositionByMagic(trade, symbol, MagicNumber,
|
||||
NormalizeDouble(Bid - POINT * 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 * TrailingStop, DIGIT))
|
||||
{
|
||||
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop, DIGIT)) ||
|
||||
(PositionGetDouble(POSITION_SL) == 0))
|
||||
{
|
||||
ModifyPositionByMagic(trade, symbol, MagicNumber,
|
||||
NormalizeDouble(Ask + POINT * TrailingStop, DIGIT),
|
||||
PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int TimeHour(datetime when=0){ if(when == 0) when = TimeCurrent();
|
||||
return when / 3600 % 24;
|
||||
}
|
||||
@@ -1,604 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIFollowReverseEMACrossOver.mq5 |
|
||||
//| Copyright 2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
// Input Parameters
|
||||
input group "General Settings"
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_H1; // Trading Timeframe
|
||||
input double InpLotSize = 0.1; // Lot Size
|
||||
input int InpMagicNumberRSIFollow = 1001; // Magic Number RSI Follow
|
||||
input int InpMagicNumberRSIReverse = 1002;// Magic Number RSI Reverse
|
||||
input int InpMagicNumberEMACross = 1003; // Magic Number EMA Cross
|
||||
|
||||
input group "Strategy Switches"
|
||||
input bool InpEnableRSIFollow = true; // Enable RSI Follow Strategy
|
||||
input bool InpEnableRSIReverse = true; // Enable RSI Reverse Strategy
|
||||
input bool InpEnableEMACross = true; // Enable EMA Cross Strategy
|
||||
input bool InpEnableStrategyLock = true; // Enable Strategy Lock
|
||||
input double InpLockProfitThreshold = 6.0; // Lock Profit Threshold (pips)
|
||||
input bool InpCloseOppositeTrades = true; // Close Opposite Trades When Profiting
|
||||
|
||||
input group "RSI Follow Strategy"
|
||||
input int InpRSIPeriod = 32; // RSI Period
|
||||
input int InpRSIOverbought = 78; // RSI Overbought Level
|
||||
input int InpRSIOversold = 46; // RSI Oversold Level
|
||||
input int InpRSIExitLevel = 44; // RSI Exit Level
|
||||
input int InpRSIFollowStartHour = 23; // RSI Follow Start Hour (0-23)
|
||||
input int InpRSIFollowEndHour = 8; // RSI Follow End Hour (0-23)
|
||||
input bool InpRSIFollowCloseOutsideHours = false; // Close trades outside trading hours
|
||||
|
||||
input group "RSI Reverse Strategy"
|
||||
input int InpRSIReversePeriod = 59; // RSI Period
|
||||
input int InpRSIReverseOverbought = 51; // RSI Overbought Level
|
||||
input int InpRSIReverseOversold = 49; // RSI Oversold Level
|
||||
input int InpRSIReverseCrossLevel = 53; // RSI Cross Level
|
||||
input int InpRSIReverseExitLevel = 48; // RSI Exit Level
|
||||
input int InpRSIReverseStartHour = 7; // RSI Reverse Start Hour (0-23)
|
||||
input int InpRSIReverseEndHour = 13; // RSI Reverse End Hour (0-23)
|
||||
input bool InpRSIReverseCloseOutsideHours = false; // Close trades outside trading hours
|
||||
input int InpRSIReverseCooldownBars = 15; // RSI Reverse Cooldown (bars)
|
||||
input bool InpRSIReverseCooldownOnLoss = true; // Apply cooldown only on loss
|
||||
|
||||
input group "EMA Cross Strategy"
|
||||
input int InpEMAPeriod = 120; // EMA Period
|
||||
input int InpEMACrossStartHour = 8; // EMA Cross Start Hour (0-23)
|
||||
input int InpEMACrossEndHour = 14; // EMA Cross End Hour (0-23)
|
||||
input bool InpEMACrossCloseOutsideHours = true; // Close trades outside trading hours
|
||||
input bool InpUseEMADistanceEntry = true; // Use EMA Distance Entry
|
||||
input double InpEMADistancePips = 160.0; // EMA Distance Threshold (pips)
|
||||
input int InpEMADistancePeriod = 26; // EMA Distance Period (bars)
|
||||
|
||||
// Global Variables
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought = false;
|
||||
bool rsiOversold = false;
|
||||
bool rsiReverseOverbought = false;
|
||||
bool rsiReverseOversold = false;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal = false;
|
||||
bool emaCrossSellSignal = false;
|
||||
int emaCrossSignalBar = 0;
|
||||
datetime lastBarTime = 0;
|
||||
datetime rsiReverseLastCloseTime = 0;
|
||||
bool rsiReverseInCooldown = false;
|
||||
double lastBarRSI = 0; // Store last bar's RSI value
|
||||
double lastBarRSIReverse = 0; // Store last bar's RSI Reverse value
|
||||
double lastBarEMA = 0; // Store last bar's EMA value
|
||||
double lastBarClose = 0; // Store last bar's close value
|
||||
double lastBarEMAPrev = 0; // Store previous bar's EMA value
|
||||
double lastBarClosePrev = 0; // Store previous bar's close value
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize indicators
|
||||
rsiHandle = iRSI(_Symbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
|
||||
rsiReverseHandle = iRSI(_Symbol, InpTimeframe, InpRSIReversePeriod, PRICE_CLOSE);
|
||||
emaHandle = iMA(_Symbol, InpTimeframe, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(rsiHandle == INVALID_HANDLE || rsiReverseHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating indicators");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
// Initialize trade settings
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
|
||||
trade.SetMarginMode();
|
||||
trade.SetTypeFillingBySymbol(_Symbol);
|
||||
trade.SetDeviationInPoints(10);
|
||||
|
||||
// Initialize last bar time
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
lastBarTime = time[0];
|
||||
}
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if new bar has formed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
if(time[0] != lastBarTime)
|
||||
{
|
||||
lastBarTime = time[0];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicator handles
|
||||
IndicatorRelease(rsiHandle);
|
||||
IndicatorRelease(rsiReverseHandle);
|
||||
IndicatorRelease(emaHandle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is within trading hours |
|
||||
//+------------------------------------------------------------------+
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if position exists for given magic number AND symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool HasPosition(int magic)
|
||||
{
|
||||
// Use helper function that verifies BOTH symbol AND magic number for THIS EA
|
||||
return PositionExistsByMagic(_Symbol, magic);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if any strategy has profitable position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool HasProfitablePosition(int excludeMagic)
|
||||
{
|
||||
bool hasProfitable = false;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(positionInfo.SelectByIndex(i))
|
||||
{
|
||||
if(positionInfo.Magic() != excludeMagic)
|
||||
{
|
||||
double profit = positionInfo.Profit();
|
||||
if(profit > InpLockProfitThreshold * _Point)
|
||||
{
|
||||
hasProfitable = true;
|
||||
// If enabled, close opposite trades
|
||||
if(InpCloseOppositeTrades)
|
||||
{
|
||||
// Check if this is an opposite trade to the excluded magic number
|
||||
if((excludeMagic == InpMagicNumberRSIFollow && positionInfo.Magic() == InpMagicNumberRSIReverse) ||
|
||||
(excludeMagic == InpMagicNumberRSIReverse && positionInfo.Magic() == InpMagicNumberRSIFollow) ||
|
||||
(excludeMagic == InpMagicNumberEMACross && (positionInfo.Magic() == InpMagicNumberRSIReverse || positionInfo.Magic() == InpMagicNumberRSIFollow)) ||
|
||||
((excludeMagic == InpMagicNumberRSIFollow || excludeMagic == InpMagicNumberRSIReverse) && positionInfo.Magic() == InpMagicNumberEMACross))
|
||||
{
|
||||
ClosePosition(positionInfo.Magic());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasProfitable;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for RSI Follow Strategy signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSIFollowStrategy()
|
||||
{
|
||||
// Check if within trading hours
|
||||
if(!IsWithinTradingHours(InpRSIFollowStartHour, InpRSIFollowEndHour))
|
||||
{
|
||||
if(InpRSIFollowCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIFollow);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check strategy lock
|
||||
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIFollow))
|
||||
return;
|
||||
|
||||
// Use lastBarRSI instead of copying buffer
|
||||
if(lastBarRSI > InpRSIOverbought)
|
||||
rsiOverbought = true;
|
||||
else if(lastBarRSI < InpRSIOversold)
|
||||
rsiOversold = true;
|
||||
|
||||
// Check for entry signals
|
||||
if(rsiOverbought && lastBarRSI < InpRSIExitLevel)
|
||||
{
|
||||
// Sell signal
|
||||
if(!HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rsiOverbought = false;
|
||||
}
|
||||
else if(rsiOversold && lastBarRSI > InpRSIExitLevel)
|
||||
{
|
||||
// Buy signal
|
||||
if(!HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIFollow);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Follow");
|
||||
}
|
||||
rsiOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if RSI Reverse is in cooldown |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsRSIReverseInCooldown()
|
||||
{
|
||||
if(InpRSIReverseCooldownBars <= 0)
|
||||
return false;
|
||||
|
||||
if(!rsiReverseInCooldown)
|
||||
return false;
|
||||
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
datetime currentBarTime = time[0];
|
||||
datetime cooldownEndTime = rsiReverseLastCloseTime + InpRSIReverseCooldownBars * PeriodSeconds(InpTimeframe);
|
||||
|
||||
if(currentBarTime >= cooldownEndTime)
|
||||
{
|
||||
rsiReverseInCooldown = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for RSI Reverse Strategy signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSIReverseStrategy()
|
||||
{
|
||||
// Check if within trading hours
|
||||
if(!IsWithinTradingHours(InpRSIReverseStartHour, InpRSIReverseEndHour))
|
||||
{
|
||||
if(InpRSIReverseCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIReverse);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check strategy lock
|
||||
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberRSIReverse))
|
||||
return;
|
||||
|
||||
// Check cooldown
|
||||
if(IsRSIReverseInCooldown())
|
||||
return;
|
||||
|
||||
// Use lastBarRSIReverse instead of copying buffer
|
||||
if(lastBarRSIReverse > InpRSIReverseOverbought)
|
||||
rsiReverseOverbought = true;
|
||||
else if(lastBarRSIReverse < InpRSIReverseOversold)
|
||||
rsiReverseOversold = true;
|
||||
|
||||
// Check for entry signals
|
||||
if(rsiReverseOverbought && lastBarRSIReverse < InpRSIReverseCrossLevel)
|
||||
{
|
||||
// Sell signal
|
||||
if(!HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIReverse);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rsiReverseOverbought = false;
|
||||
}
|
||||
else if(rsiReverseOversold && lastBarRSIReverse > InpRSIReverseCrossLevel)
|
||||
{
|
||||
// Buy signal
|
||||
if(!HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberRSIReverse);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "RSI Reverse");
|
||||
}
|
||||
rsiReverseOversold = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for EMA Cross Strategy signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEMACrossStrategy()
|
||||
{
|
||||
// Check if within trading hours
|
||||
if(!IsWithinTradingHours(InpEMACrossStartHour, InpEMACrossEndHour))
|
||||
{
|
||||
if(InpEMACrossCloseOutsideHours)
|
||||
{
|
||||
if(HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
ClosePosition(InpMagicNumberEMACross);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check strategy lock
|
||||
if(InpEnableStrategyLock && HasProfitablePosition(InpMagicNumberEMACross))
|
||||
return;
|
||||
|
||||
// Check for cross signals using stored values
|
||||
if(lastBarEMAPrev < lastBarClosePrev && lastBarEMA > lastBarClose)
|
||||
{
|
||||
// Buy cross signal
|
||||
emaCrossBuySignal = true;
|
||||
emaCrossSellSignal = false;
|
||||
emaCrossSignalBar = 0;
|
||||
}
|
||||
else if(lastBarEMAPrev > lastBarClosePrev && lastBarEMA < lastBarClose)
|
||||
{
|
||||
// Sell cross signal
|
||||
emaCrossSellSignal = true;
|
||||
emaCrossBuySignal = false;
|
||||
emaCrossSignalBar = 0;
|
||||
}
|
||||
|
||||
// Check for distance entry conditions
|
||||
if(InpUseEMADistanceEntry)
|
||||
{
|
||||
if(emaCrossBuySignal)
|
||||
{
|
||||
// Check if price has moved above EMA by the required distance for the required period
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
for(int i = 0; i < InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (closeHistory[i] - emaHistory[i]) / _Point;
|
||||
if(distance < InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
emaCrossBuySignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(emaCrossSellSignal)
|
||||
{
|
||||
// Check if price has moved below EMA by the required distance for the required period
|
||||
bool distanceConditionMet = true;
|
||||
double emaHistory[], closeHistory[];
|
||||
ArraySetAsSeries(emaHistory, true);
|
||||
ArraySetAsSeries(closeHistory, true);
|
||||
|
||||
if(CopyBuffer(emaHandle, 0, 0, InpEMADistancePeriod, emaHistory) > 0 &&
|
||||
CopyClose(_Symbol, InpTimeframe, 0, InpEMADistancePeriod, closeHistory) > 0)
|
||||
{
|
||||
for(int i = 0; i < InpEMADistancePeriod; i++)
|
||||
{
|
||||
double distance = (emaHistory[i] - closeHistory[i]) / _Point;
|
||||
if(distance < InpEMADistancePips)
|
||||
{
|
||||
distanceConditionMet = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(distanceConditionMet && !HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross Distance");
|
||||
emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Original cross entry logic using stored values
|
||||
if(lastBarEMAPrev < lastBarClosePrev && lastBarEMA > lastBarClose)
|
||||
{
|
||||
// Buy signal
|
||||
if(!HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Buy(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
else if(lastBarEMAPrev > lastBarClosePrev && lastBarEMA < lastBarClose)
|
||||
{
|
||||
// Sell signal
|
||||
if(!HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagicNumberEMACross);
|
||||
trade.Sell(InpLotSize, _Symbol, 0, 0, 0, "EMA Cross");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Increment signal bar counter
|
||||
if(emaCrossBuySignal || emaCrossSellSignal)
|
||||
{
|
||||
emaCrossSignalBar++;
|
||||
// Reset signals if they're too old (optional, can be removed if not needed)
|
||||
if(emaCrossSignalBar > InpEMADistancePeriod * 2)
|
||||
{
|
||||
emaCrossBuySignal = false;
|
||||
emaCrossSellSignal = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Only process on new bar
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
// Get indicator values for the new bar
|
||||
double rsi[], rsiReverse[], ema[], close[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
ArraySetAsSeries(rsiReverse, true);
|
||||
ArraySetAsSeries(ema, true);
|
||||
ArraySetAsSeries(close, true);
|
||||
|
||||
// Store previous values
|
||||
lastBarEMAPrev = lastBarEMA;
|
||||
lastBarClosePrev = lastBarClose;
|
||||
|
||||
// Get new values
|
||||
if(CopyBuffer(rsiHandle, 0, 0, 1, rsi) > 0)
|
||||
lastBarRSI = rsi[0];
|
||||
|
||||
if(CopyBuffer(rsiReverseHandle, 0, 0, 1, rsiReverse) > 0)
|
||||
lastBarRSIReverse = rsiReverse[0];
|
||||
|
||||
if(CopyBuffer(emaHandle, 0, 0, 1, ema) > 0)
|
||||
lastBarEMA = ema[0];
|
||||
|
||||
if(CopyClose(_Symbol, InpTimeframe, 0, 1, close) > 0)
|
||||
lastBarClose = close[0];
|
||||
|
||||
// Check for new signals
|
||||
if(InpEnableRSIFollow)
|
||||
CheckRSIFollowStrategy();
|
||||
if(InpEnableRSIReverse)
|
||||
CheckRSIReverseStrategy();
|
||||
if(InpEnableEMACross)
|
||||
CheckEMACrossStrategy();
|
||||
|
||||
// Check for exit conditions
|
||||
CheckExitConditions();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check exit conditions for all strategies |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExitConditions()
|
||||
{
|
||||
if(InpEnableRSIFollow)
|
||||
{
|
||||
// Check RSI Follow exit conditions
|
||||
if(HasPosition(InpMagicNumberRSIFollow))
|
||||
{
|
||||
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarRSI < InpRSIExitLevel) ||
|
||||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarRSI > InpRSIExitLevel))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIFollow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(InpEnableRSIReverse)
|
||||
{
|
||||
// Check RSI Reverse exit conditions
|
||||
if(HasPosition(InpMagicNumberRSIReverse))
|
||||
{
|
||||
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarRSIReverse < InpRSIReverseExitLevel) ||
|
||||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarRSIReverse > InpRSIReverseExitLevel))
|
||||
{
|
||||
ClosePosition(InpMagicNumberRSIReverse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(InpEnableEMACross)
|
||||
{
|
||||
// Check EMA Cross exit conditions using stored values
|
||||
if(HasPosition(InpMagicNumberEMACross))
|
||||
{
|
||||
if((positionInfo.PositionType() == POSITION_TYPE_BUY && lastBarEMA > lastBarClose) ||
|
||||
(positionInfo.PositionType() == POSITION_TYPE_SELL && lastBarEMA < lastBarClose))
|
||||
{
|
||||
ClosePosition(InpMagicNumberEMACross);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close position by magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition(int magic)
|
||||
{
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
// First check if position exists for this EA on this symbol
|
||||
if(!PositionExistsByMagic(_Symbol, magic))
|
||||
{
|
||||
return; // No position for this EA on this symbol
|
||||
}
|
||||
|
||||
// Get the position ticket for this EA on this symbol
|
||||
ulong ticket = GetPositionTicketByMagic(_Symbol, magic);
|
||||
if(ticket == 0)
|
||||
{
|
||||
return; // No valid ticket found
|
||||
}
|
||||
|
||||
// Check if this is RSI Reverse position and update cooldown
|
||||
if(magic == InpMagicNumberRSIReverse)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(ticket, _Symbol, magic))
|
||||
{
|
||||
datetime time[];
|
||||
if(CopyTime(_Symbol, InpTimeframe, 0, 1, time) > 0)
|
||||
{
|
||||
rsiReverseLastCloseTime = time[0];
|
||||
// Only enter cooldown if it's a loss or if cooldown on loss is disabled
|
||||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||||
if(!InpRSIReverseCooldownOnLoss || profit < 0)
|
||||
{
|
||||
rsiReverseInCooldown = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the position using helper function
|
||||
ClosePositionByMagic(trade, _Symbol, magic);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 29 KiB |
@@ -1,539 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SimpleRSIReversalAUDUSD.mq5 |
|
||||
//| Copyright 2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
// Include trade class
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
// Input parameters
|
||||
input int RSIPeriod = 28; // RSI period
|
||||
input double OverboughtLevel = 60; // Overbought level
|
||||
input double OversoldLevel = 8; // Oversold level
|
||||
input int TakeProfitPips = 175; // Take profit in pips
|
||||
input int StopLossPips = 5; // Stop loss in pips
|
||||
input double MaxLotSize = 0.1; // Maximum lot size
|
||||
input int MaxSpread = 1000; // Maximum allowed spread in pips
|
||||
input int MaxDuration = 270; // Maximum trade duration in hours
|
||||
input bool UseStopLoss = false; // Use stop loss
|
||||
input bool UseTakeProfit = false; // Use take profit
|
||||
input bool UseRSIExit = true; // Use RSI for exit
|
||||
input double RSIExitLevel = 55; // RSI level to exit (50 = neutral)
|
||||
input bool CloseOutsideSession = false; // Close trades outside Asian session
|
||||
input color PanelBackground = clrBlack; // Panel background color
|
||||
input color PanelText = clrWhite; // Panel text color
|
||||
input int PanelX = 10; // Panel X position
|
||||
input int PanelY = 20; // Panel Y position
|
||||
|
||||
// Global variables
|
||||
CTrade trade;
|
||||
int rsiHandle;
|
||||
bool isPositionOpen = false;
|
||||
double positionOpenPrice = 0;
|
||||
datetime positionOpenTime = 0;
|
||||
ENUM_POSITION_TYPE lastPositionType = POSITION_TYPE_BUY;
|
||||
bool sessionCloseAttempted = false; // Track if we've attempted to close positions for current session
|
||||
|
||||
// RSI crossover variables
|
||||
double rsiCurrent = 0;
|
||||
double rsiPrevious = 0;
|
||||
double rsiPrevious2 = 0;
|
||||
bool rsiCrossedOverbought = false;
|
||||
bool rsiCrossedOversold = false;
|
||||
bool rsiCrossedExitLevel = false;
|
||||
|
||||
// Panel objects
|
||||
string panelName = "RSIPanel";
|
||||
int panelWidth = 200;
|
||||
int panelHeight = 200;
|
||||
int labelHeight = 20;
|
||||
int labelSpacing = 5;
|
||||
|
||||
// Session times (UTC)
|
||||
const int AsianSessionStart = 0; // 00:00 UTC
|
||||
const int AsianSessionEnd = 8; // 08:00 UTC
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create panel |
|
||||
//+------------------------------------------------------------------+
|
||||
void CreatePanel()
|
||||
{
|
||||
// Create panel background
|
||||
ObjectCreate(0, panelName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, PanelX);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, PanelY);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_XSIZE, panelWidth);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_YSIZE, panelHeight);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_BGCOLOR, PanelBackground);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_COLOR, PanelText);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_STYLE, STYLE_SOLID);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_WIDTH, 1);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_BACK, false);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_SELECTED, false);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_HIDDEN, true);
|
||||
ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 0);
|
||||
|
||||
// Create title label
|
||||
ObjectCreate(0, panelName + "Title", OBJ_LABEL, 0, 0, 0);
|
||||
ObjectSetInteger(0, panelName + "Title", OBJPROP_XDISTANCE, PanelX + 5);
|
||||
ObjectSetInteger(0, panelName + "Title", OBJPROP_YDISTANCE, PanelY + 5);
|
||||
ObjectSetInteger(0, panelName + "Title", OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||||
ObjectSetString(0, panelName + "Title", OBJPROP_TEXT, "RSI Reversal");
|
||||
ObjectSetInteger(0, panelName + "Title", OBJPROP_COLOR, PanelText);
|
||||
ObjectSetInteger(0, panelName + "Title", OBJPROP_FONTSIZE, 10);
|
||||
|
||||
// Create score labels
|
||||
CreateScoreLabel("RSI", "RSI: ", 0);
|
||||
CreateScoreLabel("Position", "Position: ", 1);
|
||||
CreateScoreLabel("Spread", "Spread: ", 2);
|
||||
CreateScoreLabel("Session", "Session: ", 3);
|
||||
CreateScoreLabel("SL", "Stop Loss: ", 4);
|
||||
CreateScoreLabel("TP", "Take Profit: ", 5);
|
||||
CreateScoreLabel("Cross", "Cross: ", 6);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Create score label |
|
||||
//+------------------------------------------------------------------+
|
||||
void CreateScoreLabel(string name, string text, int index)
|
||||
{
|
||||
ObjectCreate(0, panelName + name, OBJ_LABEL, 0, 0, 0);
|
||||
ObjectSetInteger(0, panelName + name, OBJPROP_XDISTANCE, PanelX + 5);
|
||||
ObjectSetInteger(0, panelName + name, OBJPROP_YDISTANCE, PanelY + 30 + index * (labelHeight + labelSpacing));
|
||||
ObjectSetInteger(0, panelName + name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||||
ObjectSetString(0, panelName + name, OBJPROP_TEXT, text);
|
||||
ObjectSetInteger(0, panelName + name, OBJPROP_COLOR, PanelText);
|
||||
ObjectSetInteger(0, panelName + name, OBJPROP_FONTSIZE, 8);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update panel values |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdatePanel(double rsi, string position, int spread, string session, double sl, double tp, string crossInfo)
|
||||
{
|
||||
ObjectSetString(0, panelName + "RSI", OBJPROP_TEXT, "RSI: " + DoubleToString(rsi, 2));
|
||||
ObjectSetString(0, panelName + "Position", OBJPROP_TEXT, "Position: " + position);
|
||||
ObjectSetString(0, panelName + "Spread", OBJPROP_TEXT, "Spread: " + IntegerToString(spread) + " pips");
|
||||
ObjectSetString(0, panelName + "Session", OBJPROP_TEXT, "Session: " + session);
|
||||
ObjectSetString(0, panelName + "SL", OBJPROP_TEXT, "Stop Loss: " + IntegerToString(StopLossPips) + " pips");
|
||||
ObjectSetString(0, panelName + "TP", OBJPROP_TEXT, "Take Profit: " + IntegerToString(TakeProfitPips) + " pips");
|
||||
ObjectSetString(0, panelName + "Cross", OBJPROP_TEXT, "Cross: " + crossInfo);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get current session name |
|
||||
//+------------------------------------------------------------------+
|
||||
string GetCurrentSession()
|
||||
{
|
||||
datetime currentTime = TimeCurrent();
|
||||
MqlDateTime timeStruct;
|
||||
TimeToStruct(currentTime, timeStruct);
|
||||
|
||||
if(timeStruct.hour >= AsianSessionStart && timeStruct.hour < AsianSessionEnd)
|
||||
return "Asian";
|
||||
else if(timeStruct.hour >= 8 && timeStruct.hour < 16)
|
||||
return "London";
|
||||
else if(timeStruct.hour >= 13 && timeStruct.hour < 21)
|
||||
return "New York";
|
||||
else
|
||||
return "Other";
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if trading is allowed |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsTradingAllowed()
|
||||
{
|
||||
// Check if market is open
|
||||
if(!SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if we have enough money
|
||||
if(AccountInfoDouble(ACCOUNT_MARGIN_FREE) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check RSI crossover conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckRSICrossover()
|
||||
{
|
||||
// Reset crossover flags
|
||||
rsiCrossedOverbought = false;
|
||||
rsiCrossedOversold = false;
|
||||
rsiCrossedExitLevel = false;
|
||||
|
||||
// Check for overbought crossover (RSI crosses above overbought level)
|
||||
if(rsiPrevious < OverboughtLevel && rsiCurrent >= OverboughtLevel)
|
||||
{
|
||||
rsiCrossedOverbought = true;
|
||||
}
|
||||
|
||||
// Check for oversold crossover (RSI crosses below oversold level)
|
||||
if(rsiPrevious > OversoldLevel && rsiCurrent <= OversoldLevel)
|
||||
{
|
||||
rsiCrossedOversold = true;
|
||||
}
|
||||
|
||||
// Check for exit level crossover
|
||||
if(rsiPrevious < RSIExitLevel && rsiCurrent >= RSIExitLevel)
|
||||
{
|
||||
rsiCrossedExitLevel = true;
|
||||
}
|
||||
else if(rsiPrevious > RSIExitLevel && rsiCurrent <= RSIExitLevel)
|
||||
{
|
||||
rsiCrossedExitLevel = true;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize RSI indicator
|
||||
rsiHandle = iRSI(_Symbol, PERIOD_M15, RSIPeriod, PRICE_CLOSE);
|
||||
|
||||
if(rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Wait a bit for the indicator to be ready
|
||||
Sleep(100);
|
||||
|
||||
// Initialize RSI values with retry logic
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
int retryCount = 0;
|
||||
bool rsiInitialized = false;
|
||||
|
||||
while(retryCount < 10 && !rsiInitialized)
|
||||
{
|
||||
int copied = CopyBuffer(rsiHandle, 0, 0, 3, rsi);
|
||||
if(copied >= 3)
|
||||
{
|
||||
rsiCurrent = rsi[0];
|
||||
rsiPrevious = rsi[1];
|
||||
rsiPrevious2 = rsi[2];
|
||||
rsiInitialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
retryCount++;
|
||||
Sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
if(!rsiInitialized)
|
||||
{
|
||||
// Don't fail initialization, just set default values
|
||||
rsiCurrent = 50.0;
|
||||
rsiPrevious = 50.0;
|
||||
rsiPrevious2 = 50.0;
|
||||
}
|
||||
|
||||
// Create panel
|
||||
CreatePanel();
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicator handles
|
||||
IndicatorRelease(rsiHandle);
|
||||
|
||||
// Remove panel objects
|
||||
ObjectsDeleteAll(0, panelName);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close all trades for the current symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CloseAllTrades(string reason = "")
|
||||
{
|
||||
bool allClosed = true;
|
||||
int totalPositions = PositionsTotal();
|
||||
|
||||
if(totalPositions == 0)
|
||||
return true;
|
||||
|
||||
// Check if there are any positions with our magic number
|
||||
bool hasOurPositions = false;
|
||||
for(int i = 0; i < totalPositions; i++)
|
||||
{
|
||||
if(PositionGetSymbol(i) == _Symbol && PositionGetInteger(POSITION_MAGIC) == 123456)
|
||||
{
|
||||
hasOurPositions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = totalPositions - 1; i >= 0; i--)
|
||||
{
|
||||
if(PositionGetSymbol(i) == _Symbol)
|
||||
{
|
||||
// Try to close position with retry logic
|
||||
int retryCount = 0;
|
||||
bool positionClosed = false;
|
||||
|
||||
while(retryCount < 3 && !positionClosed)
|
||||
{
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if trading is allowed
|
||||
if(!IsTradingAllowed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're in Asian session
|
||||
if(!IsAsianSession())
|
||||
{
|
||||
// Close all positions if outside Asian session and CloseOutsideSession is true
|
||||
if(CloseOutsideSession && !sessionCloseAttempted)
|
||||
{
|
||||
CloseAllTrades("Outside Asian session");
|
||||
sessionCloseAttempted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Reset the session close attempt flag when we enter Asian session
|
||||
sessionCloseAttempted = false;
|
||||
}
|
||||
|
||||
// Get current spread
|
||||
double spread = SymbolInfoDouble(_Symbol, SYMBOL_ASK) - SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
int spreadInPips = (int)(spread / _Point);
|
||||
|
||||
// Check if spread is too high
|
||||
if(spreadInPips > MaxSpread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Get RSI values from bar data
|
||||
double rsi[];
|
||||
ArraySetAsSeries(rsi, true);
|
||||
|
||||
int copied = CopyBuffer(rsiHandle, 0, 0, 3, rsi);
|
||||
if(copied < 3)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update RSI values
|
||||
rsiPrevious2 = rsiPrevious;
|
||||
rsiPrevious = rsiCurrent;
|
||||
rsiCurrent = rsi[0];
|
||||
|
||||
// Validate RSI values
|
||||
if(rsiCurrent == 0 || rsiPrevious == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for RSI crossovers
|
||||
CheckRSICrossover();
|
||||
|
||||
// Get current prices
|
||||
double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
// Get position status
|
||||
string positionStatus = "None";
|
||||
for(int i = 0; i < PositionsTotal(); i++)
|
||||
{
|
||||
if(PositionGetSymbol(i) == _Symbol)
|
||||
{
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
positionStatus = (posType == POSITION_TYPE_BUY) ? "Long" : "Short";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate stop loss and take profit levels
|
||||
double sl = 0;
|
||||
double tp = 0;
|
||||
|
||||
// Prepare crossover info for panel
|
||||
string crossInfo = "None";
|
||||
if(rsiCrossedOverbought) crossInfo = "Overbought";
|
||||
else if(rsiCrossedOversold) crossInfo = "Oversold";
|
||||
else if(rsiCrossedExitLevel) crossInfo = "Exit";
|
||||
|
||||
// Update panel
|
||||
UpdatePanel(rsiCurrent, positionStatus, spreadInPips, GetCurrentSession(), sl, tp, crossInfo);
|
||||
|
||||
// Check for open position
|
||||
bool hasOpenPosition = false;
|
||||
for(int i = 0; i < PositionsTotal(); i++)
|
||||
{
|
||||
if(PositionGetSymbol(i) == _Symbol)
|
||||
{
|
||||
hasOpenPosition = true;
|
||||
|
||||
// Get position details
|
||||
double positionProfit = PositionGetDouble(POSITION_PROFIT);
|
||||
double positionVolume = PositionGetDouble(POSITION_VOLUME);
|
||||
double openPrice = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
// Check for RSI exit if enabled
|
||||
if(UseRSIExit && rsiCrossedExitLevel)
|
||||
{
|
||||
bool shouldExit = false;
|
||||
|
||||
// For long positions, exit when RSI crosses above exit level
|
||||
if(posType == POSITION_TYPE_BUY && rsiCurrent >= RSIExitLevel && rsiPrevious < RSIExitLevel)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
// For short positions, exit when RSI crosses below exit level
|
||||
else if(posType == POSITION_TYPE_SELL && rsiCurrent <= RSIExitLevel && rsiPrevious > RSIExitLevel)
|
||||
{
|
||||
shouldExit = true;
|
||||
}
|
||||
|
||||
if(shouldExit)
|
||||
{
|
||||
CloseAllTrades("RSI Exit Crossover");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for timeout
|
||||
if(TimeCurrent() - positionOpenTime > MaxDuration * 3600)
|
||||
{
|
||||
CloseAllTrades("Timeout");
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 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(rsiCrossedOversold)
|
||||
{
|
||||
double sl = UseStopLoss ? currentBid - StopLossPips * _Point : 0;
|
||||
double tp = UseTakeProfit ? currentBid + TakeProfitPips * _Point : 0;
|
||||
|
||||
if(UseStopLoss && sl >= currentBid)
|
||||
return;
|
||||
if(UseTakeProfit && tp <= currentBid)
|
||||
return;
|
||||
|
||||
// Set trade parameters
|
||||
trade.SetDeviationInPoints(3);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
trade.SetExpertMagicNumber(123456);
|
||||
|
||||
// Place buy order using CTrade
|
||||
if(trade.Buy(MaxLotSize, _Symbol, currentAsk, sl, tp, "RSI Oversold Crossover Buy"))
|
||||
{
|
||||
isPositionOpen = true;
|
||||
positionOpenPrice = currentAsk;
|
||||
positionOpenTime = TimeCurrent();
|
||||
lastPositionType = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
// Place sell order if RSI crosses above overbought level (overbought crossover)
|
||||
else if(rsiCrossedOverbought)
|
||||
{
|
||||
double sl = UseStopLoss ? currentAsk + StopLossPips * _Point : 0;
|
||||
double tp = UseTakeProfit ? currentAsk - TakeProfitPips * _Point : 0;
|
||||
|
||||
if(UseStopLoss && sl <= currentAsk)
|
||||
return;
|
||||
if(UseTakeProfit && tp >= currentAsk)
|
||||
return;
|
||||
|
||||
// Set trade parameters
|
||||
trade.SetDeviationInPoints(3);
|
||||
trade.SetTypeFilling(ORDER_FILLING_IOC);
|
||||
trade.SetExpertMagicNumber(123456);
|
||||
|
||||
// Place sell order using CTrade
|
||||
if(trade.Sell(MaxLotSize, _Symbol, currentBid, sl, tp, "RSI Overbought Crossover Sell"))
|
||||
{
|
||||
isPositionOpen = true;
|
||||
positionOpenPrice = currentBid;
|
||||
positionOpenTime = TimeCurrent();
|
||||
lastPositionType = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 250 KiB |
@@ -1,581 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalping.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.01"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 90; // RSI Overbought Level
|
||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 88; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 48; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 6; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 0.1; // Lot Size
|
||||
input int MagicNumber = 123459123; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
input group "=== Reversal escape (intrabar, multi-signal) ==="
|
||||
input bool UseReversalEscape = true; // run while in position every tick
|
||||
input int ReversalATRPeriod = 14; // ATR lookback on signal timeframe
|
||||
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
|
||||
input int ReversalSignsRequired = 2; // how many independent signs must align
|
||||
input double ReversalRsiVelocity = 16.0; // RSI points drop (long) / rise (short) vs prior buffer
|
||||
input double ReversalBodyAtrMult = 5.1; // last closed bar body >= this * ATR counts as one sign
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
|
||||
input double TrailingStopDistancePoints = 120.0; // SL distance from bid/ask (points)
|
||||
input double TrailingActivationPoints = 0.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev, rsi_current, rsi_two_bars_ago;
|
||||
bool position_open = false;
|
||||
int position_ticket = 0;
|
||||
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
|
||||
datetime last_bar_time = 0;
|
||||
bool rsi_against_position = false;
|
||||
int bars_against_count = 0;
|
||||
|
||||
void ResetPositionTracking();
|
||||
void SyncTrackedPosition();
|
||||
double ATRPriceOnTF(const int period);
|
||||
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr);
|
||||
void TryReversalEscape();
|
||||
void ApplyTrailingStop();
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize RSI indicator
|
||||
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Initialize trade object
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Allocate arrays
|
||||
ArraySetAsSeries(rsi_buffer, true);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsi_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a new bar
|
||||
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
bool is_new_bar = (current_bar_time != last_bar_time);
|
||||
bool in_position = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
|
||||
// While flat, process only on new bars. While in position, allow intrabar reversal escape checks.
|
||||
if(!in_position && !is_new_bar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Update RSI values
|
||||
if(!UpdateRSI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(in_position && UseReversalEscape)
|
||||
{
|
||||
TryReversalEscape();
|
||||
}
|
||||
|
||||
if(in_position && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
if(!is_new_bar)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Keep local tracking aligned with actual terminal positions for this symbol/magic.
|
||||
SyncTrackedPosition();
|
||||
|
||||
// Check for existing position
|
||||
CheckExistingPosition();
|
||||
|
||||
// Check for new entry signals - only if no position exists for THIS EA (magic number) on THIS symbol
|
||||
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateRSI()
|
||||
{
|
||||
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rsi_current = rsi_buffer[0]; // Current bar
|
||||
rsi_prev = rsi_buffer[1]; // Previous bar
|
||||
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Wilder ATR in price units (signal timeframe) |
|
||||
//+------------------------------------------------------------------+
|
||||
double ATRPriceOnTF(const int period)
|
||||
{
|
||||
if(period < 1)
|
||||
return 0.0;
|
||||
|
||||
MqlRates rates[];
|
||||
const int need = period + 2;
|
||||
if(CopyRates(_Symbol, TimeFrame, 0, need, rates) < need)
|
||||
return 0.0;
|
||||
|
||||
ArraySetAsSeries(rates, true);
|
||||
double sum = 0.0;
|
||||
for(int i = 1; i <= period; i++)
|
||||
{
|
||||
const double hl = rates[i].high - rates[i].low;
|
||||
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
|
||||
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
|
||||
sum += MathMax(hl, MathMax(hc, lc));
|
||||
}
|
||||
|
||||
return sum / (double)period;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Independent adverse signs (need ReversalSignsRequired to exit) |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr)
|
||||
{
|
||||
if(atr <= 0.0)
|
||||
return 0;
|
||||
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
int signs = 0;
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(entry - bid >= ReversalAdverseAtrMult * atr)
|
||||
signs++;
|
||||
if(rsi_prev - rsi_current >= ReversalRsiVelocity)
|
||||
signs++;
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(ask - entry >= ReversalAdverseAtrMult * atr)
|
||||
signs++;
|
||||
if(rsi_current - rsi_prev >= ReversalRsiVelocity)
|
||||
signs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MqlRates rates[];
|
||||
if(CopyRates(_Symbol, TimeFrame, 0, 4, rates) >= 4)
|
||||
{
|
||||
ArraySetAsSeries(rates, true);
|
||||
const double body = MathAbs(rates[1].close - rates[1].open);
|
||||
if(body >= ReversalBodyAtrMult * atr)
|
||||
{
|
||||
if(ptype == POSITION_TYPE_BUY && rates[1].close < rates[1].open)
|
||||
signs++;
|
||||
else if(ptype == POSITION_TYPE_SELL && rates[1].close > rates[1].open)
|
||||
signs++;
|
||||
}
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(rates[1].close < rates[2].close && rates[2].close < rates[3].close)
|
||||
signs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(rates[1].close > rates[2].close && rates[2].close > rates[3].close)
|
||||
signs++;
|
||||
}
|
||||
}
|
||||
|
||||
return signs;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Cut losers fast on violent reversals (evaluated every tick) |
|
||||
//+------------------------------------------------------------------+
|
||||
void TryReversalEscape()
|
||||
{
|
||||
ulong live_ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(live_ticket == 0)
|
||||
return;
|
||||
if(!PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double atr = ATRPriceOnTF(ReversalATRPeriod);
|
||||
if(atr <= 0.0)
|
||||
return;
|
||||
|
||||
const int signs = CountReversalEscapeSigns(ptype, atr);
|
||||
if(signs < ReversalSignsRequired)
|
||||
return;
|
||||
|
||||
ClosePosition();
|
||||
Print("RSIScalpingBTCUSD: reversal escape signs=", signs, " need=", ReversalSignsRequired,
|
||||
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reset local position tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetPositionTracking()
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sync local state with real position in terminal |
|
||||
//+------------------------------------------------------------------+
|
||||
void SyncTrackedPosition()
|
||||
{
|
||||
ulong live_ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(live_ticket == 0)
|
||||
{
|
||||
ResetPositionTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we were not tracking (or ticket changed), start tracking the live position.
|
||||
if(!position_open || position_ticket != (int)live_ticket)
|
||||
{
|
||||
if(PositionSelectByTicketSymbolAndMagic(live_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = true;
|
||||
position_ticket = (int)live_ticket;
|
||||
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPosition()
|
||||
{
|
||||
if(!position_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if position still exists with correct magic number AND symbol for THIS EA
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
ResetPositionTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit conditions based on RSI target
|
||||
if(current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Check if RSI is against the position (below oversold)
|
||||
if(rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit long position when RSI reaches buy target
|
||||
if(rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
// Check if RSI is against the position (above overbought)
|
||||
if(rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit short position when RSI reaches sell target
|
||||
if(rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
OpenBuyPosition();
|
||||
}
|
||||
|
||||
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
|
||||
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
bool position_exists_before_close = PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(!position_exists_before_close)
|
||||
{
|
||||
ResetPositionTracking();
|
||||
return;
|
||||
}
|
||||
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
ResetPositionTracking();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep tracking when close fails (e.g. market closed); retry on next bar.
|
||||
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
ResetPositionTracking();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 8.2 KiB |
@@ -1,34 +0,0 @@
|
||||
; RSIScalpingXAUUSD-trailing — matches main.mq5 v1.06 default inputs
|
||||
; saved on 2026.05.01 22:32:43
|
||||
; MT5 Strategy Tester: Inputs → Load
|
||||
;
|
||||
TimeFrame=16385||15||0||16385||N
|
||||
RSI_Period=14||14||1||140||N
|
||||
RSI_Applied_Price=1||1||0||7||N
|
||||
RSI_Overbought=71.0||0||2||100||N
|
||||
RSI_Oversold=57.0||0||2||100||N
|
||||
UseEntrySlopeFilter=false||false||0||true||N
|
||||
EntryMinSlopePerBar=1.0||1.0||0.100000||10.000000||N
|
||||
RSI_Target_Buy=80.0||0||2||100||N
|
||||
RSI_Target_Sell=57.0||0||2||100||N
|
||||
BarsToWait=1||0||1||50||N
|
||||
LotSize=0.1||0.1||0.010000||1.000000||N
|
||||
MagicNumber=129102315||129102315||1||1291023150||N
|
||||
Slippage=3||3||1||30||N
|
||||
; === Reversal escape (intrabar, multi-signal) ===
|
||||
UseReversalEscape=true||false||0||true||N
|
||||
ReversalEscapeTimeFrame=5||0||0||49153||N
|
||||
ReversalATRPeriod=14||14||1||140||N
|
||||
ReversalAdverseAtrMult=5.25||5.25||0.525000||52.500000||N
|
||||
ReversalSignsRequired=1||2||1||20||N
|
||||
ReversalRsiVelocity=16.0||16.0||1.600000||160.000000||N
|
||||
ReversalBodyAtrMult=5.1||5.1||0.510000||51.000000||N
|
||||
; === Trailing stop ===
|
||||
UseTrailingStop=true||false||0||true||N
|
||||
TrailingStopDistancePoints=71.0||100||100||5000||Y
|
||||
TrailingActivationPoints=41.0||100||100||5000||Y
|
||||
; === Intrabar give-back (same bar reversals) ===
|
||||
UseGiveBackExit=true||false||0||true||N
|
||||
GiveBackATRPeriod=14||14||1||140||N
|
||||
GiveBackAtrMult=0.1||1.85||0.185000||18.500000||N
|
||||
GiveBackRequireMfe=true||false||0||true||N
|
||||
@@ -1,645 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalping.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.06"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 71; // RSI Overbought Level
|
||||
input double RSI_Oversold = 57; // RSI Oversold Level
|
||||
input bool UseEntrySlopeFilter = false; // require RSI momentum on entry bars
|
||||
input double EntryMinSlopePerBar = 1.0; // minimum RSI delta per bar for entry
|
||||
input double RSI_Target_Buy = 80; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 57; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 1; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 0.1; // Lot Size
|
||||
input int MagicNumber = 129102315; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
input group "=== Reversal escape (intrabar, multi-signal) ==="
|
||||
input bool UseReversalEscape = true; // run while in position every tick (now uses ReversalEscapeTimeFrame)
|
||||
input ENUM_TIMEFRAMES ReversalEscapeTimeFrame = PERIOD_M5; // ATR / RSI velocity / bar signs on this TF (not signal TF)
|
||||
input int ReversalATRPeriod = 14; // ATR lookback on ReversalEscapeTimeFrame
|
||||
input double ReversalAdverseAtrMult = 5.25; // close if price vs entry >= this * ATR
|
||||
input int ReversalSignsRequired = 1; // how many independent signs must align
|
||||
input double ReversalRsiVelocity = 16.0; // RSI points drop (long) / rise (short) vs prior buffer
|
||||
input double ReversalBodyAtrMult = 5.1; // last closed bar body >= this * ATR counts as one sign
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind price while in profit
|
||||
input double TrailingStopDistancePoints = 71.0; // SL distance from current bid/ask (points)
|
||||
input double TrailingActivationPoints = 41.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
input group "=== Intrabar give-back (same bar reversals) ==="
|
||||
input bool UseGiveBackExit = true; // exit if price gives back vs best tick since entry
|
||||
input int GiveBackATRPeriod = 14; // ATR period on signal timeframe (Wilder)
|
||||
input double GiveBackAtrMult = 0.1; // close when retrace from peak/trough >= this * ATR
|
||||
input bool GiveBackRequireMfe = true; // long: only after bid was above entry; short: ask below entry
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
int rsi_escape_handle = INVALID_HANDLE; // RSI on ReversalEscapeTimeFrame (may alias rsi_handle)
|
||||
double rsi_buffer[];
|
||||
double rsi_prev, rsi_current, rsi_two_bars_ago;
|
||||
bool position_open = false;
|
||||
int position_ticket = 0;
|
||||
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
|
||||
datetime last_bar_time = 0;
|
||||
bool rsi_against_position = false;
|
||||
int bars_against_count = 0;
|
||||
|
||||
ulong g_giveback_track_ticket = 0;
|
||||
double g_peak_bid_since_entry = 0.0;
|
||||
double g_trough_ask_since_entry = 0.0;
|
||||
|
||||
void ResetIntrabarGiveBackState();
|
||||
void TryGiveBackExit();
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize RSI indicator
|
||||
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
if(ReversalEscapeTimeFrame == TimeFrame)
|
||||
rsi_escape_handle = rsi_handle;
|
||||
else
|
||||
{
|
||||
rsi_escape_handle = iRSI(_Symbol, ReversalEscapeTimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_escape_handle == INVALID_HANDLE)
|
||||
{
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize trade object
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Allocate arrays
|
||||
ArraySetAsSeries(rsi_buffer, true);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsi_escape_handle != INVALID_HANDLE && rsi_escape_handle != rsi_handle)
|
||||
IndicatorRelease(rsi_escape_handle);
|
||||
if(rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsi_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
return;
|
||||
|
||||
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
const bool new_bar = (current_bar_time != last_bar_time);
|
||||
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
|
||||
if(!in_pos && !new_bar)
|
||||
return;
|
||||
|
||||
if(!UpdateRSI())
|
||||
return;
|
||||
|
||||
if(in_pos && UseReversalEscape)
|
||||
TryReversalEscape();
|
||||
|
||||
if(in_pos && UseGiveBackExit)
|
||||
TryGiveBackExit();
|
||||
|
||||
if(in_pos && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
if(!new_bar)
|
||||
return;
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
ResyncPositionFromMarket();
|
||||
CheckExistingPosition();
|
||||
|
||||
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
CheckEntrySignals();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateRSI()
|
||||
{
|
||||
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rsi_current = rsi_buffer[0]; // Current bar
|
||||
rsi_prev = rsi_buffer[1]; // Previous bar
|
||||
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Wilder ATR in price units (signal timeframe) |
|
||||
//+------------------------------------------------------------------+
|
||||
double ATRPriceOnTF(const int period)
|
||||
{
|
||||
if(period < 1)
|
||||
return 0.0;
|
||||
MqlRates rates[];
|
||||
const int need = period + 2;
|
||||
if(CopyRates(_Symbol, TimeFrame, 0, need, rates) < need)
|
||||
return 0.0;
|
||||
ArraySetAsSeries(rates, true);
|
||||
double sum = 0.0;
|
||||
for(int i = 1; i <= period; i++)
|
||||
{
|
||||
const double hl = rates[i].high - rates[i].low;
|
||||
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
|
||||
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
|
||||
sum += MathMax(hl, MathMax(hc, lc));
|
||||
}
|
||||
return sum / (double)period;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Wilder ATR on arbitrary timeframe |
|
||||
//+------------------------------------------------------------------+
|
||||
double WilderATRForTF(const ENUM_TIMEFRAMES tf, const int period)
|
||||
{
|
||||
if(period < 1)
|
||||
return 0.0;
|
||||
MqlRates rates[];
|
||||
const int need = period + 2;
|
||||
if(CopyRates(_Symbol, tf, 0, need, rates) < need)
|
||||
return 0.0;
|
||||
ArraySetAsSeries(rates, true);
|
||||
double sum = 0.0;
|
||||
for(int i = 1; i <= period; i++)
|
||||
{
|
||||
const double hl = rates[i].high - rates[i].low;
|
||||
const double hc = MathAbs(rates[i].high - rates[i + 1].close);
|
||||
const double lc = MathAbs(rates[i].low - rates[i + 1].close);
|
||||
sum += MathMax(hl, MathMax(hc, lc));
|
||||
}
|
||||
return sum / (double)period;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Independent adverse signs (need ReversalSignsRequired to exit) |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountReversalEscapeSigns(const ENUM_POSITION_TYPE ptype, const double atr)
|
||||
{
|
||||
if(atr <= 0.0)
|
||||
return 0;
|
||||
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
int signs = 0;
|
||||
|
||||
double rsi_esc[];
|
||||
ArraySetAsSeries(rsi_esc, true);
|
||||
const bool ok_esc_rsi = (rsi_escape_handle != INVALID_HANDLE &&
|
||||
CopyBuffer(rsi_escape_handle, 0, 0, 2, rsi_esc) >= 2);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(entry - bid >= ReversalAdverseAtrMult * atr)
|
||||
signs++;
|
||||
if(ok_esc_rsi && rsi_esc[1] - rsi_esc[0] >= ReversalRsiVelocity)
|
||||
signs++;
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(ask - entry >= ReversalAdverseAtrMult * atr)
|
||||
signs++;
|
||||
if(ok_esc_rsi && rsi_esc[0] - rsi_esc[1] >= ReversalRsiVelocity)
|
||||
signs++;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
MqlRates r[];
|
||||
if(CopyRates(_Symbol, ReversalEscapeTimeFrame, 0, 4, r) >= 4)
|
||||
{
|
||||
ArraySetAsSeries(r, true);
|
||||
const double body = MathAbs(r[1].close - r[1].open);
|
||||
if(body >= ReversalBodyAtrMult * atr)
|
||||
{
|
||||
if(ptype == POSITION_TYPE_BUY && r[1].close < r[1].open)
|
||||
signs++;
|
||||
else if(ptype == POSITION_TYPE_SELL && r[1].close > r[1].open)
|
||||
signs++;
|
||||
}
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(r[1].close < r[2].close && r[2].close < r[3].close)
|
||||
signs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(r[1].close > r[2].close && r[2].close > r[3].close)
|
||||
signs++;
|
||||
}
|
||||
}
|
||||
|
||||
return signs;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Cut losers fast on violent reversals (evaluated every tick) |
|
||||
//+------------------------------------------------------------------+
|
||||
void TryReversalEscape()
|
||||
{
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double atr = WilderATRForTF(ReversalEscapeTimeFrame, ReversalATRPeriod);
|
||||
if(atr <= 0.0)
|
||||
return;
|
||||
|
||||
const int n = CountReversalEscapeSigns(ptype, atr);
|
||||
if(n < ReversalSignsRequired)
|
||||
return;
|
||||
|
||||
ClosePosition();
|
||||
Print("RSIScalpingXAUUSD: reversal escape TF=", EnumToString(ReversalEscapeTimeFrame),
|
||||
" signs=", n, " need=", ReversalSignsRequired,
|
||||
" ATR=", DoubleToString(atr, (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS)));
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Reset give-back peak/trough tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResetIntrabarGiveBackState()
|
||||
{
|
||||
g_giveback_track_ticket = 0;
|
||||
g_peak_bid_since_entry = 0.0;
|
||||
g_trough_ask_since_entry = 0.0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Exit when intrabar price gives back sharply vs best since entry |
|
||||
//+------------------------------------------------------------------+
|
||||
void TryGiveBackExit()
|
||||
{
|
||||
if(!UseGiveBackExit || GiveBackAtrMult <= 0.0)
|
||||
return;
|
||||
|
||||
const ulong ticket = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
{
|
||||
ResetIntrabarGiveBackState();
|
||||
return;
|
||||
}
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
|
||||
if(g_giveback_track_ticket != ticket)
|
||||
{
|
||||
g_giveback_track_ticket = ticket;
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
g_peak_bid_since_entry = bid;
|
||||
g_trough_ask_since_entry = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_trough_ask_since_entry = ask;
|
||||
g_peak_bid_since_entry = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
const double atr = ATRPriceOnTF(GiveBackATRPeriod);
|
||||
if(atr <= 0.0)
|
||||
return;
|
||||
|
||||
const double threshold = GiveBackAtrMult * atr;
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(bid > g_peak_bid_since_entry)
|
||||
g_peak_bid_since_entry = bid;
|
||||
if(GiveBackRequireMfe && g_peak_bid_since_entry <= entry)
|
||||
return;
|
||||
if(g_peak_bid_since_entry - bid >= threshold)
|
||||
{
|
||||
ClosePosition();
|
||||
Print("RSIScalpingXAUUSD: give-back exit BUY retrace=",
|
||||
DoubleToString(g_peak_bid_since_entry - bid, digits),
|
||||
" thr=", DoubleToString(threshold, digits));
|
||||
}
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(ask < g_trough_ask_since_entry)
|
||||
g_trough_ask_since_entry = ask;
|
||||
if(GiveBackRequireMfe && g_trough_ask_since_entry >= entry)
|
||||
return;
|
||||
if(ask - g_trough_ask_since_entry >= threshold)
|
||||
{
|
||||
ClosePosition();
|
||||
Print("RSIScalpingXAUUSD: give-back exit SELL retrace=",
|
||||
DoubleToString(ask - g_trough_ask_since_entry, digits),
|
||||
" thr=", DoubleToString(threshold, digits));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
void ResyncPositionFromMarket()
|
||||
{
|
||||
if(position_open)
|
||||
return;
|
||||
ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(t == 0 || !PositionSelectByTicket(t))
|
||||
return;
|
||||
position_ticket = (int)t;
|
||||
position_open = true;
|
||||
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPosition()
|
||||
{
|
||||
if(!position_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if position still exists with correct magic number
|
||||
if(!PositionSelectByTicketAndMagic(position_ticket, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit conditions based on RSI target
|
||||
if(current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Check if RSI is against the position (below oversold)
|
||||
if(rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit long position when RSI reaches buy target
|
||||
if(rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
// Check if RSI is against the position (above overbought)
|
||||
if(rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit short position when RSI reaches sell target
|
||||
if(rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
const double upSlope1 = rsi_prev - rsi_two_bars_ago; // older->prev
|
||||
const double upSlope2 = rsi_current - rsi_prev; // prev->current
|
||||
const double dnSlope1 = rsi_two_bars_ago - rsi_prev; // older->prev
|
||||
const double dnSlope2 = rsi_prev - rsi_current; // prev->current
|
||||
const bool buySlopeOk = (!UseEntrySlopeFilter) || (upSlope1 >= EntryMinSlopePerBar && upSlope2 >= EntryMinSlopePerBar);
|
||||
const bool sellSlopeOk = (!UseEntrySlopeFilter) || (dnSlope1 >= EntryMinSlopePerBar && dnSlope2 >= EntryMinSlopePerBar);
|
||||
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold && buySlopeOk)
|
||||
{
|
||||
OpenBuyPosition();
|
||||
}
|
||||
|
||||
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
|
||||
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought && sellSlopeOk)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
const ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(t > 0 && PositionSelectByTicketSymbolAndMagic(t, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = (int)t;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
const ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(t > 0 && PositionSelectByTicketSymbolAndMagic(t, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_ticket = (int)t;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
ResetIntrabarGiveBackState();
|
||||
return;
|
||||
}
|
||||
if(!PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
ResetIntrabarGiveBackState();
|
||||
return;
|
||||
}
|
||||
Print("RSIScalpingXAUUSD: close failed (will retry on next bar). retcode=",
|
||||
trade.ResultRetcode(), " lastError=", GetLastError());
|
||||
}
|
||||
@@ -1,508 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSI_SecretSauce_XAUUSD.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.01"
|
||||
#property description "RSI Secret Sauce Strategy: Wait for RSI to leave 70/30 zone, then enter when it comes back in"
|
||||
#property description "Based on momentum flip concept - not traditional overbought/oversold"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
|
||||
//--- Input Parameters
|
||||
input group "=== Trading Settings ==="
|
||||
input string InpSymbol = "XAUUSD"; // Default gold; same numbers as secret_sauce.set (that file uses BTCUSD as symbol)
|
||||
input double InpLotSize = 0.1; // Lot Size (Profiles/Tester/secret_sauce.set)
|
||||
input int InpMagicNumber = 789012; // Magic Number
|
||||
input int InpSlippage = 10; // Slippage in points
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M30; // Trading Timeframe (set value 30 = M30)
|
||||
|
||||
input group "=== RSI Settings ==="
|
||||
input int InpRSIPeriod = 16; // RSI Period
|
||||
input double InpRSIOverbought = 72.5; // RSI Overbought Level
|
||||
input double InpRSIOversold = 32.5; // RSI Oversold Level
|
||||
input int InpRSILookback = 60; // RSI Lookback for Peak/Bottom Detection
|
||||
|
||||
input group "=== Entry Logic ==="
|
||||
input int InpPeakBars = 2; // Bars to confirm peak/bottom
|
||||
input bool InpRequireDivergence = false; // Require divergence confirmation (optional)
|
||||
|
||||
input group "=== Risk Management ==="
|
||||
input double InpStopLossATR = 2.75; // Stop Loss (ATR multiples)
|
||||
input double InpTakeProfitATR = 5.0; // Take Profit (ATR multiples)
|
||||
input int InpATRPeriod = 14; // ATR Period
|
||||
input bool InpUseSwingStopLoss = false; // Use previous swing high/low for stop loss
|
||||
input int InpSwingLookback = 30; // Bars to look back for swing points
|
||||
|
||||
input group "=== Position Management ==="
|
||||
input int InpMaxPositions = 1; // Max Simultaneous Positions
|
||||
input int InpMinBarsBetweenTrades = 7; // Min Bars Between Trades
|
||||
|
||||
//--- Global Variables
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
|
||||
string actualSymbol;
|
||||
int rsiHandle = INVALID_HANDLE;
|
||||
int atrHandle = INVALID_HANDLE;
|
||||
|
||||
double rsiBuffer[];
|
||||
double atrBuffer[];
|
||||
double highBuffer[];
|
||||
double lowBuffer[];
|
||||
|
||||
// RSI state tracking
|
||||
bool rsiWasOverbought = false; // RSI was above 70
|
||||
bool rsiWasOversold = false; // RSI was below 30
|
||||
bool rsiBackInRange = false; // RSI came back into range
|
||||
datetime lastRSIExitTime = 0; // When RSI left the range
|
||||
datetime lastRSIReentryTime = 0; // When RSI came back in
|
||||
|
||||
// Trade tracking
|
||||
datetime lastTradeTime = 0;
|
||||
int barsSinceLastTrade = 0;
|
||||
|
||||
datetime lastBarTime = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Determine actual symbol
|
||||
if(InpSymbol == "" || InpSymbol == NULL)
|
||||
actualSymbol = _Symbol;
|
||||
else
|
||||
actualSymbol = InpSymbol;
|
||||
|
||||
// Check if symbol exists
|
||||
if(!SymbolInfoInteger(actualSymbol, SYMBOL_SELECT))
|
||||
{
|
||||
Print("Error: Symbol ", actualSymbol, " not found. Using chart symbol.");
|
||||
actualSymbol = _Symbol;
|
||||
}
|
||||
|
||||
// Initialize RSI indicator
|
||||
rsiHandle = iRSI(actualSymbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
|
||||
if(rsiHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating RSI indicator");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ArraySetAsSeries(rsiBuffer, true);
|
||||
|
||||
// Initialize ATR indicator
|
||||
atrHandle = iATR(actualSymbol, InpTimeframe, InpATRPeriod);
|
||||
if(atrHandle == INVALID_HANDLE)
|
||||
{
|
||||
Print("Error creating ATR indicator");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
ArraySetAsSeries(atrBuffer, true);
|
||||
|
||||
// Initialize price buffers
|
||||
ArraySetAsSeries(highBuffer, true);
|
||||
ArraySetAsSeries(lowBuffer, true);
|
||||
|
||||
// Set trade parameters
|
||||
trade.SetExpertMagicNumber(InpMagicNumber);
|
||||
trade.SetDeviationInPoints(InpSlippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
Print("=== RSI Secret Sauce Strategy Initialized ===");
|
||||
Print("Symbol: ", actualSymbol);
|
||||
Print("Timeframe: ", EnumToString(InpTimeframe));
|
||||
Print("RSI Period: ", InpRSIPeriod, " | Overbought: ", InpRSIOverbought, " | Oversold: ", InpRSIOversold);
|
||||
Print("Stop Loss: ", InpStopLossATR, "x ATR | Take Profit: ", InpTakeProfitATR, "x ATR");
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsiHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsiHandle);
|
||||
if(atrHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(atrHandle);
|
||||
|
||||
Print("Expert Advisor deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
int requiredBars = MathMax(InpRSILookback, InpSwingLookback) + 10;
|
||||
if(Bars(actualSymbol, InpTimeframe) < requiredBars)
|
||||
return;
|
||||
|
||||
// Check if this is a new bar (wait for candle close)
|
||||
datetime currentBarTime = iTime(actualSymbol, InpTimeframe, 0);
|
||||
if(currentBarTime == lastBarTime)
|
||||
return; // Still the same bar, don't process
|
||||
|
||||
lastBarTime = currentBarTime;
|
||||
|
||||
// Update indicators
|
||||
if(!UpdateIndicators())
|
||||
return;
|
||||
|
||||
// Update RSI state tracking
|
||||
UpdateRSIState();
|
||||
|
||||
// Check existing positions
|
||||
CheckExistingPositions();
|
||||
|
||||
// Check for entry signals
|
||||
if(CanOpenNewPosition())
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update indicator values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateIndicators()
|
||||
{
|
||||
// Update RSI (need enough bars for lookback)
|
||||
int rsiBarsNeeded = InpRSILookback + 5;
|
||||
if(CopyBuffer(rsiHandle, 0, 0, rsiBarsNeeded, rsiBuffer) < rsiBarsNeeded)
|
||||
return false;
|
||||
|
||||
// Update ATR
|
||||
if(CopyBuffer(atrHandle, 0, 0, 2, atrBuffer) < 2)
|
||||
return false;
|
||||
|
||||
// Update price buffers for swing detection
|
||||
if(CopyHigh(actualSymbol, InpTimeframe, 0, InpSwingLookback + 5, highBuffer) < InpSwingLookback + 5)
|
||||
return false;
|
||||
if(CopyLow(actualSymbol, InpTimeframe, 0, InpSwingLookback + 5, lowBuffer) < InpSwingLookback + 5)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI state tracking |
|
||||
//+------------------------------------------------------------------+
|
||||
void UpdateRSIState()
|
||||
{
|
||||
double rsiCurrent = rsiBuffer[0];
|
||||
double rsiPrev = rsiBuffer[1];
|
||||
|
||||
// Check if RSI left overbought zone (was above 70, now below 70)
|
||||
if(rsiPrev >= InpRSIOverbought && rsiCurrent < InpRSIOverbought)
|
||||
{
|
||||
rsiWasOverbought = true;
|
||||
rsiBackInRange = true;
|
||||
lastRSIExitTime = TimeCurrent();
|
||||
lastRSIReentryTime = TimeCurrent();
|
||||
Print(TimeToString(TimeCurrent()), " - RSI left overbought zone (", rsiPrev, " -> ", rsiCurrent, ")");
|
||||
}
|
||||
|
||||
// Check if RSI left oversold zone (was below 30, now above 30)
|
||||
if(rsiPrev <= InpRSIOversold && rsiCurrent > InpRSIOversold)
|
||||
{
|
||||
rsiWasOversold = true;
|
||||
rsiBackInRange = true;
|
||||
lastRSIExitTime = TimeCurrent();
|
||||
lastRSIReentryTime = TimeCurrent();
|
||||
Print(TimeToString(TimeCurrent()), " - RSI left oversold zone (", rsiPrev, " -> ", rsiCurrent, ")");
|
||||
}
|
||||
|
||||
// Reset flags if RSI goes back to extreme
|
||||
if(rsiCurrent >= InpRSIOverbought)
|
||||
{
|
||||
rsiWasOverbought = false;
|
||||
rsiBackInRange = false;
|
||||
}
|
||||
|
||||
if(rsiCurrent <= InpRSIOversold)
|
||||
{
|
||||
rsiWasOversold = false;
|
||||
rsiBackInRange = false;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if we can open a new position |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CanOpenNewPosition()
|
||||
{
|
||||
// Check max positions
|
||||
int positionCount = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
if(positionInfo.SelectByIndex(i))
|
||||
{
|
||||
if(positionInfo.Symbol() == actualSymbol && positionInfo.Magic() == InpMagicNumber)
|
||||
positionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if(positionCount >= InpMaxPositions)
|
||||
return false;
|
||||
|
||||
// Check minimum bars between trades
|
||||
if(lastTradeTime > 0)
|
||||
{
|
||||
int barsSince = Bars(actualSymbol, InpTimeframe, lastTradeTime, TimeCurrent());
|
||||
if(barsSince < InpMinBarsBetweenTrades)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
// LONG Entry: RSI was overbought (>70), came back in range, now look for peak
|
||||
if(rsiWasOverbought && rsiBackInRange)
|
||||
{
|
||||
// Check if RSI is back in normal range (below 70)
|
||||
if(rsiBuffer[0] < InpRSIOverbought)
|
||||
{
|
||||
// Look for a peak in RSI after re-entry
|
||||
if(IsRSIPeak())
|
||||
{
|
||||
Print(TimeToString(TimeCurrent()), " - LONG Signal: RSI peak detected after leaving overbought zone");
|
||||
OpenPosition(POSITION_TYPE_BUY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SHORT Entry: RSI was oversold (<30), came back in range, now look for bottom
|
||||
if(rsiWasOversold && rsiBackInRange)
|
||||
{
|
||||
// Check if RSI is back in normal range (above 30)
|
||||
if(rsiBuffer[0] > InpRSIOversold)
|
||||
{
|
||||
// Look for a bottom in RSI after re-entry
|
||||
if(IsRSIBottom())
|
||||
{
|
||||
Print(TimeToString(TimeCurrent()), " - SHORT Signal: RSI bottom detected after leaving oversold zone");
|
||||
OpenPosition(POSITION_TYPE_SELL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if RSI is forming a peak (for LONG entry) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsRSIPeak()
|
||||
{
|
||||
// We need at least InpPeakBars + 1 bars to confirm a peak
|
||||
if(ArraySize(rsiBuffer) < InpPeakBars + 2)
|
||||
return false;
|
||||
|
||||
// Check if current RSI is higher than previous bars (forming a peak)
|
||||
double currentRSI = rsiBuffer[0];
|
||||
bool isPeak = true;
|
||||
|
||||
// Check if current is higher than the next few bars
|
||||
for(int i = 1; i <= InpPeakBars; i++)
|
||||
{
|
||||
if(rsiBuffer[i] >= currentRSI)
|
||||
{
|
||||
isPeak = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check if previous bar was lower (confirming upward movement before peak)
|
||||
if(rsiBuffer[1] >= currentRSI)
|
||||
isPeak = false;
|
||||
|
||||
return isPeak;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if RSI is forming a bottom (for SHORT entry) |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsRSIBottom()
|
||||
{
|
||||
// We need at least InpPeakBars + 1 bars to confirm a bottom
|
||||
if(ArraySize(rsiBuffer) < InpPeakBars + 2)
|
||||
return false;
|
||||
|
||||
// Check if current RSI is lower than previous bars (forming a bottom)
|
||||
double currentRSI = rsiBuffer[0];
|
||||
bool isBottom = true;
|
||||
|
||||
// Check if current is lower than the next few bars
|
||||
for(int i = 1; i <= InpPeakBars; i++)
|
||||
{
|
||||
if(rsiBuffer[i] <= currentRSI)
|
||||
{
|
||||
isBottom = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Also check if previous bar was higher (confirming downward movement before bottom)
|
||||
if(rsiBuffer[1] <= currentRSI)
|
||||
isBottom = false;
|
||||
|
||||
return isBottom;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenPosition(ENUM_POSITION_TYPE type)
|
||||
{
|
||||
double price = (type == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(actualSymbol, SYMBOL_ASK) :
|
||||
SymbolInfoDouble(actualSymbol, SYMBOL_BID);
|
||||
|
||||
if(price <= 0)
|
||||
return;
|
||||
|
||||
// Calculate stop loss and take profit
|
||||
double sl = 0.0, tp = 0.0;
|
||||
if(!CalculateStops(price, type, sl, tp))
|
||||
{
|
||||
Print("Error: Failed to calculate stops");
|
||||
return;
|
||||
}
|
||||
|
||||
string comment = "RSI_Secret_" + (type == POSITION_TYPE_BUY ? "LONG" : "SHORT");
|
||||
|
||||
bool result = false;
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
result = trade.Buy(InpLotSize, actualSymbol, 0, sl, tp, comment);
|
||||
else
|
||||
result = trade.Sell(InpLotSize, actualSymbol, 0, sl, tp, comment);
|
||||
|
||||
if(result)
|
||||
{
|
||||
lastTradeTime = TimeCurrent();
|
||||
ulong ticket = trade.ResultOrder();
|
||||
Print(TimeToString(TimeCurrent()), " - Position opened: ", comment, " Ticket: ", ticket,
|
||||
" Price: ", price, " SL: ", sl, " TP: ", tp);
|
||||
|
||||
// Reset RSI state after opening position
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
rsiWasOverbought = false;
|
||||
else
|
||||
rsiWasOversold = false;
|
||||
rsiBackInRange = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to open position: ", comment, " Error: ",
|
||||
trade.ResultRetcode(), " - ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate stop loss and take profit |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CalculateStops(double price, ENUM_POSITION_TYPE type, double &sl, double &tp)
|
||||
{
|
||||
double atrValue = atrBuffer[0];
|
||||
if(atrValue <= 0)
|
||||
atrValue = price * 0.01; // Fallback: 1% of price
|
||||
|
||||
double slDistance = atrValue * InpStopLossATR;
|
||||
double tpDistance = atrValue * InpTakeProfitATR;
|
||||
|
||||
int digits = (int)SymbolInfoInteger(actualSymbol, SYMBOL_DIGITS);
|
||||
double point = SymbolInfoDouble(actualSymbol, SYMBOL_POINT);
|
||||
int stopsLevel = (int)SymbolInfoInteger(actualSymbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double minStopDistance = MathMax(stopsLevel * point, point * 10);
|
||||
|
||||
// Use swing-based stop loss if enabled
|
||||
if(InpUseSwingStopLoss)
|
||||
{
|
||||
double swingStop = GetSwingStopLoss(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure minimum distance
|
||||
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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get swing-based stop loss (previous swing high/low) |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetSwingStopLoss(double currentPrice, ENUM_POSITION_TYPE type)
|
||||
{
|
||||
// For LONG: find previous swing low
|
||||
// For SHORT: find previous swing high
|
||||
|
||||
if(type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Find the lowest low in the lookback period
|
||||
double lowestLow = lowBuffer[0];
|
||||
for(int i = 1; i < InpSwingLookback && i < ArraySize(lowBuffer); i++)
|
||||
{
|
||||
if(lowBuffer[i] < lowestLow)
|
||||
lowestLow = lowBuffer[i];
|
||||
}
|
||||
return lowestLow;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Find the highest high in the lookback period
|
||||
double highestHigh = highBuffer[0];
|
||||
for(int i = 1; i < InpSwingLookback && i < ArraySize(highBuffer); i++)
|
||||
{
|
||||
if(highBuffer[i] > highestHigh)
|
||||
highestHigh = highBuffer[i];
|
||||
}
|
||||
return highestHigh;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPositions()
|
||||
{
|
||||
// Position management can be added here if needed
|
||||
// For now, positions are managed by TP/SL
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,284 +0,0 @@
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input ENUM_TIMEFRAMES InpHigherTF = PERIOD_H4; // Higher timeframe for MA/cross points
|
||||
input int InpMAPeriod = 150; // MA period
|
||||
input ENUM_MA_METHOD InpMAMethod = MODE_SMMA; // MA method
|
||||
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price
|
||||
input int InpHTFBarsToScan = 1200; // HTF bars to scan for crossings
|
||||
input double InpLineTouchTolerance = 170; // Pullback touch tolerance (points)
|
||||
input double InpBreakBuffer = 90; // Break confirmation buffer (points)
|
||||
input double InpLots = 0.10; // Position size
|
||||
input long InpMagic = 26042501; // Magic number
|
||||
input bool InpDrawTrendline = true; // Draw detected trendline
|
||||
|
||||
CTrade trade;
|
||||
|
||||
int g_maHandle = INVALID_HANDLE;
|
||||
datetime g_lastBarTime = 0;
|
||||
string g_lineName = "SimpleTrendline_Basis";
|
||||
|
||||
struct TrendlineModel
|
||||
{
|
||||
datetime t1;
|
||||
datetime t2;
|
||||
datetime t3;
|
||||
double p1;
|
||||
double p2;
|
||||
double p3;
|
||||
double a;
|
||||
double b;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime t = iTime(_Symbol, _Period, 0);
|
||||
if(t == 0)
|
||||
return false;
|
||||
if(t != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = t;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int FindRecentCrossPoints(datetime ×[], double &prices[])
|
||||
{
|
||||
ArrayResize(times, 0);
|
||||
ArrayResize(prices, 0);
|
||||
|
||||
if(g_maHandle == INVALID_HANDLE)
|
||||
return 0;
|
||||
|
||||
int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20);
|
||||
MqlRates rates[];
|
||||
double maBuf[];
|
||||
|
||||
int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates);
|
||||
int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf);
|
||||
if(copiedRates <= 5 || copiedMa <= 5)
|
||||
return 0;
|
||||
|
||||
int bars = MathMin(copiedRates, copiedMa);
|
||||
ArraySetAsSeries(rates, true);
|
||||
ArraySetAsSeries(maBuf, true);
|
||||
|
||||
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 BuildTrendlineFrom3Points(TrendlineModel &m)
|
||||
{
|
||||
m.valid = false;
|
||||
datetime ts[];
|
||||
double ps[];
|
||||
int n = FindRecentCrossPoints(ts, ps);
|
||||
if(n < 3)
|
||||
return false;
|
||||
|
||||
// We collected from recent to older in series order.
|
||||
// Re-map as oldest -> newest to stabilize slope direction.
|
||||
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.p1 = pOld[0];
|
||||
m.p2 = pOld[1];
|
||||
m.p3 = pOld[2];
|
||||
m.valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
double TrendlinePriceAtTime(const TrendlineModel &m, datetime t)
|
||||
{
|
||||
if(!m.valid)
|
||||
return 0.0;
|
||||
double x = (double)((long)t - (long)m.t1);
|
||||
return m.a * x + m.b;
|
||||
}
|
||||
|
||||
void DrawTrendline(const TrendlineModel &m)
|
||||
{
|
||||
if(!InpDrawTrendline || !m.valid)
|
||||
return;
|
||||
|
||||
datetime tStart = m.t1;
|
||||
datetime tEnd = iTime(_Symbol, _Period, 0);
|
||||
if(tEnd <= tStart)
|
||||
tEnd = m.t3 + PeriodSeconds(_Period) * 20;
|
||||
|
||||
double pStart = TrendlinePriceAtTime(m, tStart);
|
||||
double pEnd = TrendlinePriceAtTime(m, tEnd);
|
||||
|
||||
if(ObjectFind(0, g_lineName) < 0)
|
||||
ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
|
||||
else
|
||||
{
|
||||
ObjectMove(0, g_lineName, 0, tStart, pStart);
|
||||
ObjectMove(0, g_lineName, 1, tEnd, pEnd);
|
||||
}
|
||||
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true);
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold);
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2);
|
||||
}
|
||||
|
||||
bool GetCurrentPosition(long &type, double &volume)
|
||||
{
|
||||
if(!PositionSelect(_Symbol))
|
||||
return false;
|
||||
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
return false;
|
||||
type = PositionGetInteger(POSITION_TYPE);
|
||||
volume = PositionGetDouble(POSITION_VOLUME);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TryExitOnBreak(const TrendlineModel &m)
|
||||
{
|
||||
long posType;
|
||||
double vol;
|
||||
if(!GetCurrentPosition(posType, vol))
|
||||
return;
|
||||
|
||||
double close1 = iClose(_Symbol, _Period, 1);
|
||||
datetime t1 = iTime(_Symbol, _Period, 1);
|
||||
double line1 = TrendlinePriceAtTime(m, t1);
|
||||
double buf = InpBreakBuffer * _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)
|
||||
trade.PositionClose(_Symbol);
|
||||
}
|
||||
|
||||
void TryPullbackEntry(const TrendlineModel &m)
|
||||
{
|
||||
long posType;
|
||||
double vol;
|
||||
if(GetCurrentPosition(posType, vol))
|
||||
return;
|
||||
|
||||
MqlRates bars1[], bars2[];
|
||||
if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1)
|
||||
return;
|
||||
if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1)
|
||||
return;
|
||||
if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1)
|
||||
return;
|
||||
|
||||
MqlRates b1 = bars1[0];
|
||||
MqlRates b2 = bars2[0];
|
||||
|
||||
double line1 = TrendlinePriceAtTime(m, b1.time);
|
||||
double tol = InpLineTouchTolerance * _Point;
|
||||
|
||||
bool upTrend = (m.a > 0.0);
|
||||
bool downTrend = (m.a < 0.0);
|
||||
|
||||
if(upTrend)
|
||||
{
|
||||
bool touched = (b1.low <= (line1 + tol));
|
||||
bool reclaim = (b1.close > line1);
|
||||
bool bullish = (b1.close > b1.open);
|
||||
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
|
||||
if(touched && reclaim && bullish && stillHealthy)
|
||||
{
|
||||
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
|
||||
}
|
||||
}
|
||||
else if(downTrend)
|
||||
{
|
||||
bool touched = (b1.high >= (line1 - tol));
|
||||
bool reject = (b1.close < line1);
|
||||
bool bearish = (b1.close < b1.open);
|
||||
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
|
||||
if(touched && reject && bearish && stillWeak)
|
||||
{
|
||||
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice);
|
||||
if(g_maHandle == INVALID_HANDLE)
|
||||
return INIT_FAILED;
|
||||
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
g_lastBarTime = 0;
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_maHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(g_maHandle);
|
||||
if(ObjectFind(0, g_lineName) >= 0)
|
||||
ObjectDelete(0, g_lineName);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
TrendlineModel m;
|
||||
if(!BuildTrendlineFrom3Points(m))
|
||||
return;
|
||||
|
||||
DrawTrendline(m);
|
||||
TryExitOnBreak(m);
|
||||
TryPullbackEntry(m);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
; SimpleTrendline.mq5 optimization preset
|
||||
; Strategy Tester -> Inputs -> Load
|
||||
; Focus: trendline pullback entries + break exits (no broker SL/TP)
|
||||
;
|
||||
InpHigherTF=16385||16385||0||16388||Y
|
||||
InpMAPeriod=50||20||5||200||Y
|
||||
InpMAMethod=1||0||1||3||Y
|
||||
InpAppliedPrice=0||0||1||6||Y
|
||||
InpHTFBarsToScan=400||200||100||1200||Y
|
||||
InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y
|
||||
InpBreakBuffer=30.0||5.0||5.0||120.0||Y
|
||||
InpLots=0.10||0.10||0.01||0.10||N
|
||||
InpMagic=26042501||26042501||1||26042501||N
|
||||
InpDrawTrendline=false||false||0||true||N
|
||||
@@ -1,284 +0,0 @@
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input ENUM_TIMEFRAMES InpHigherTF = PERIOD_M15; // Higher timeframe for MA/cross points
|
||||
input int InpMAPeriod = 65; // MA period
|
||||
input ENUM_MA_METHOD InpMAMethod = MODE_LWMA; // MA method
|
||||
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price
|
||||
input int InpHTFBarsToScan = 1200; // HTF bars to scan for crossings
|
||||
input double InpLineTouchTolerance = 100; // Pullback touch tolerance (points)
|
||||
input double InpBreakBuffer = 80; // Break confirmation buffer (points)
|
||||
input double InpLots = 0.10; // Position size
|
||||
input long InpMagic = 26042501; // Magic number
|
||||
input bool InpDrawTrendline = true; // Draw detected trendline
|
||||
|
||||
CTrade trade;
|
||||
|
||||
int g_maHandle = INVALID_HANDLE;
|
||||
datetime g_lastBarTime = 0;
|
||||
string g_lineName = "SimpleTrendline_Basis";
|
||||
|
||||
struct TrendlineModel
|
||||
{
|
||||
datetime t1;
|
||||
datetime t2;
|
||||
datetime t3;
|
||||
double p1;
|
||||
double p2;
|
||||
double p3;
|
||||
double a;
|
||||
double b;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime t = iTime(_Symbol, _Period, 0);
|
||||
if(t == 0)
|
||||
return false;
|
||||
if(t != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = t;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int FindRecentCrossPoints(datetime ×[], double &prices[])
|
||||
{
|
||||
ArrayResize(times, 0);
|
||||
ArrayResize(prices, 0);
|
||||
|
||||
if(g_maHandle == INVALID_HANDLE)
|
||||
return 0;
|
||||
|
||||
int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20);
|
||||
MqlRates rates[];
|
||||
double maBuf[];
|
||||
|
||||
int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates);
|
||||
int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf);
|
||||
if(copiedRates <= 5 || copiedMa <= 5)
|
||||
return 0;
|
||||
|
||||
int bars = MathMin(copiedRates, copiedMa);
|
||||
ArraySetAsSeries(rates, true);
|
||||
ArraySetAsSeries(maBuf, true);
|
||||
|
||||
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 BuildTrendlineFrom3Points(TrendlineModel &m)
|
||||
{
|
||||
m.valid = false;
|
||||
datetime ts[];
|
||||
double ps[];
|
||||
int n = FindRecentCrossPoints(ts, ps);
|
||||
if(n < 3)
|
||||
return false;
|
||||
|
||||
// We collected from recent to older in series order.
|
||||
// Re-map as oldest -> newest to stabilize slope direction.
|
||||
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.p1 = pOld[0];
|
||||
m.p2 = pOld[1];
|
||||
m.p3 = pOld[2];
|
||||
m.valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
double TrendlinePriceAtTime(const TrendlineModel &m, datetime t)
|
||||
{
|
||||
if(!m.valid)
|
||||
return 0.0;
|
||||
double x = (double)((long)t - (long)m.t1);
|
||||
return m.a * x + m.b;
|
||||
}
|
||||
|
||||
void DrawTrendline(const TrendlineModel &m)
|
||||
{
|
||||
if(!InpDrawTrendline || !m.valid)
|
||||
return;
|
||||
|
||||
datetime tStart = m.t1;
|
||||
datetime tEnd = iTime(_Symbol, _Period, 0);
|
||||
if(tEnd <= tStart)
|
||||
tEnd = m.t3 + PeriodSeconds(_Period) * 20;
|
||||
|
||||
double pStart = TrendlinePriceAtTime(m, tStart);
|
||||
double pEnd = TrendlinePriceAtTime(m, tEnd);
|
||||
|
||||
if(ObjectFind(0, g_lineName) < 0)
|
||||
ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
|
||||
else
|
||||
{
|
||||
ObjectMove(0, g_lineName, 0, tStart, pStart);
|
||||
ObjectMove(0, g_lineName, 1, tEnd, pEnd);
|
||||
}
|
||||
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true);
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold);
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2);
|
||||
}
|
||||
|
||||
bool GetCurrentPosition(long &type, double &volume)
|
||||
{
|
||||
if(!PositionSelect(_Symbol))
|
||||
return false;
|
||||
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
return false;
|
||||
type = PositionGetInteger(POSITION_TYPE);
|
||||
volume = PositionGetDouble(POSITION_VOLUME);
|
||||
return true;
|
||||
}
|
||||
|
||||
void TryExitOnBreak(const TrendlineModel &m)
|
||||
{
|
||||
long posType;
|
||||
double vol;
|
||||
if(!GetCurrentPosition(posType, vol))
|
||||
return;
|
||||
|
||||
double close1 = iClose(_Symbol, _Period, 1);
|
||||
datetime t1 = iTime(_Symbol, _Period, 1);
|
||||
double line1 = TrendlinePriceAtTime(m, t1);
|
||||
double buf = InpBreakBuffer * _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)
|
||||
trade.PositionClose(_Symbol);
|
||||
}
|
||||
|
||||
void TryPullbackEntry(const TrendlineModel &m)
|
||||
{
|
||||
long posType;
|
||||
double vol;
|
||||
if(GetCurrentPosition(posType, vol))
|
||||
return;
|
||||
|
||||
MqlRates bars1[], bars2[];
|
||||
if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1)
|
||||
return;
|
||||
if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1)
|
||||
return;
|
||||
if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1)
|
||||
return;
|
||||
|
||||
MqlRates b1 = bars1[0];
|
||||
MqlRates b2 = bars2[0];
|
||||
|
||||
double line1 = TrendlinePriceAtTime(m, b1.time);
|
||||
double tol = InpLineTouchTolerance * _Point;
|
||||
|
||||
bool upTrend = (m.a > 0.0);
|
||||
bool downTrend = (m.a < 0.0);
|
||||
|
||||
if(upTrend)
|
||||
{
|
||||
bool touched = (b1.low <= (line1 + tol));
|
||||
bool reclaim = (b1.close > line1);
|
||||
bool bullish = (b1.close > b1.open);
|
||||
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
|
||||
if(touched && reclaim && bullish && stillHealthy)
|
||||
{
|
||||
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
|
||||
}
|
||||
}
|
||||
else if(downTrend)
|
||||
{
|
||||
bool touched = (b1.high >= (line1 - tol));
|
||||
bool reject = (b1.close < line1);
|
||||
bool bearish = (b1.close < b1.open);
|
||||
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
|
||||
if(touched && reject && bearish && stillWeak)
|
||||
{
|
||||
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice);
|
||||
if(g_maHandle == INVALID_HANDLE)
|
||||
return INIT_FAILED;
|
||||
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
g_lastBarTime = 0;
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_maHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(g_maHandle);
|
||||
if(ObjectFind(0, g_lineName) >= 0)
|
||||
ObjectDelete(0, g_lineName);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
TrendlineModel m;
|
||||
if(!BuildTrendlineFrom3Points(m))
|
||||
return;
|
||||
|
||||
DrawTrendline(m);
|
||||
TryExitOnBreak(m);
|
||||
TryPullbackEntry(m);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
; SimpleTrendline.mq5 optimization preset
|
||||
; Strategy Tester -> Inputs -> Load
|
||||
; Focus: trendline pullback entries + break exits (no broker SL/TP)
|
||||
;
|
||||
InpHigherTF=16385||16385||0||16388||Y
|
||||
InpMAPeriod=50||20||5||200||Y
|
||||
InpMAMethod=1||0||1||3||Y
|
||||
InpAppliedPrice=0||0||1||6||Y
|
||||
InpHTFBarsToScan=400||200||100||1200||Y
|
||||
InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y
|
||||
InpBreakBuffer=30.0||5.0||5.0||120.0||Y
|
||||
InpLots=0.10||0.10||0.01||0.10||N
|
||||
InpMagic=26042501||26042501||1||26042501||N
|
||||
InpDrawTrendline=false||false||0||true||N
|
||||
@@ -1,376 +0,0 @@
|
||||
#property strict
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input ENUM_TIMEFRAMES InpHigherTF = PERIOD_M10; // Higher timeframe for MA/cross points
|
||||
input int InpMAPeriod = 65; // MA period
|
||||
input ENUM_MA_METHOD InpMAMethod = MODE_EMA; // MA method
|
||||
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_OPEN; // MA applied price
|
||||
input int InpHTFBarsToScan = 500; // HTF bars to scan for crossings
|
||||
input double InpLineTouchTolerance = 220; // Pullback touch tolerance (points)
|
||||
input double InpBreakBuffer = 110; // Break confirmation buffer (points)
|
||||
input double InpLots = 0.10; // Position size
|
||||
input long InpMagic = 26042501; // Magic number
|
||||
input bool InpDrawTrendline = true; // Draw detected trendline
|
||||
input bool InpUseSessionModeGate = true; // Block entries when symbol/session disallow opens
|
||||
input bool InpBypassGateInTester = true; // Ignore gate in Strategy Tester for optimization
|
||||
|
||||
CTrade trade;
|
||||
|
||||
int g_maHandle = INVALID_HANDLE;
|
||||
datetime g_lastBarTime = 0;
|
||||
string g_lineName = "SimpleTrendline_Basis";
|
||||
datetime g_lastEntryBlockLog = 0;
|
||||
|
||||
struct TrendlineModel
|
||||
{
|
||||
datetime t1;
|
||||
datetime t2;
|
||||
datetime t3;
|
||||
double p1;
|
||||
double p2;
|
||||
double p3;
|
||||
double a;
|
||||
double b;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
bool IsNewBar()
|
||||
{
|
||||
datetime t = iTime(_Symbol, _Period, 0);
|
||||
if(t == 0)
|
||||
return false;
|
||||
if(t != g_lastBarTime)
|
||||
{
|
||||
g_lastBarTime = t;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int FindRecentCrossPoints(datetime ×[], double &prices[])
|
||||
{
|
||||
ArrayResize(times, 0);
|
||||
ArrayResize(prices, 0);
|
||||
|
||||
if(g_maHandle == INVALID_HANDLE)
|
||||
return 0;
|
||||
|
||||
int needBars = MathMax(InpHTFBarsToScan, InpMAPeriod + 20);
|
||||
MqlRates rates[];
|
||||
double maBuf[];
|
||||
|
||||
int copiedRates = CopyRates(_Symbol, InpHigherTF, 0, needBars, rates);
|
||||
int copiedMa = CopyBuffer(g_maHandle, 0, 0, needBars, maBuf);
|
||||
if(copiedRates <= 5 || copiedMa <= 5)
|
||||
return 0;
|
||||
|
||||
int bars = MathMin(copiedRates, copiedMa);
|
||||
ArraySetAsSeries(rates, true);
|
||||
ArraySetAsSeries(maBuf, true);
|
||||
|
||||
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 BuildTrendlineFrom3Points(TrendlineModel &m)
|
||||
{
|
||||
m.valid = false;
|
||||
datetime ts[];
|
||||
double ps[];
|
||||
int n = FindRecentCrossPoints(ts, ps);
|
||||
if(n < 3)
|
||||
return false;
|
||||
|
||||
// We collected from recent to older in series order.
|
||||
// Re-map as oldest -> newest to stabilize slope direction.
|
||||
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.p1 = pOld[0];
|
||||
m.p2 = pOld[1];
|
||||
m.p3 = pOld[2];
|
||||
m.valid = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
double TrendlinePriceAtTime(const TrendlineModel &m, datetime t)
|
||||
{
|
||||
if(!m.valid)
|
||||
return 0.0;
|
||||
double x = (double)((long)t - (long)m.t1);
|
||||
return m.a * x + m.b;
|
||||
}
|
||||
|
||||
void DrawTrendline(const TrendlineModel &m)
|
||||
{
|
||||
if(!InpDrawTrendline || !m.valid)
|
||||
return;
|
||||
|
||||
datetime tStart = m.t1;
|
||||
datetime tEnd = iTime(_Symbol, _Period, 0);
|
||||
if(tEnd <= tStart)
|
||||
tEnd = m.t3 + PeriodSeconds(_Period) * 20;
|
||||
|
||||
double pStart = TrendlinePriceAtTime(m, tStart);
|
||||
double pEnd = TrendlinePriceAtTime(m, tEnd);
|
||||
|
||||
if(ObjectFind(0, g_lineName) < 0)
|
||||
ObjectCreate(0, g_lineName, OBJ_TREND, 0, tStart, pStart, tEnd, pEnd);
|
||||
else
|
||||
{
|
||||
ObjectMove(0, g_lineName, 0, tStart, pStart);
|
||||
ObjectMove(0, g_lineName, 1, tEnd, pEnd);
|
||||
}
|
||||
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_RAY_RIGHT, true);
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_COLOR, clrGold);
|
||||
ObjectSetInteger(0, g_lineName, OBJPROP_WIDTH, 2);
|
||||
}
|
||||
|
||||
bool GetCurrentPosition(long &type, double &volume)
|
||||
{
|
||||
if(!PositionSelect(_Symbol))
|
||||
return false;
|
||||
if((long)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
return false;
|
||||
type = PositionGetInteger(POSITION_TYPE);
|
||||
volume = PositionGetDouble(POSITION_VOLUME);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsWithinAnyTradeSession(const datetime nowServer)
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(nowServer, dt);
|
||||
ENUM_DAY_OF_WEEK day = (ENUM_DAY_OF_WEEK)dt.day_of_week;
|
||||
int nowSec = dt.hour * 3600 + dt.min * 60 + dt.sec;
|
||||
|
||||
datetime from = 0;
|
||||
datetime to = 0;
|
||||
bool hasAny = false;
|
||||
for(uint idx = 0; idx < 16; idx++)
|
||||
{
|
||||
if(!SymbolInfoSessionTrade(_Symbol, day, idx, from, to))
|
||||
break;
|
||||
hasAny = true;
|
||||
// SymbolInfoSessionTrade returns session boundaries as time-of-day values.
|
||||
MqlDateTime fdt, tdt;
|
||||
TimeToStruct(from, fdt);
|
||||
TimeToStruct(to, tdt);
|
||||
int fromSec = fdt.hour * 3600 + fdt.min * 60 + fdt.sec;
|
||||
int toSec = tdt.hour * 3600 + tdt.min * 60 + tdt.sec;
|
||||
|
||||
// from==to on some brokers means full-day session.
|
||||
if(fromSec == toSec)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if(fromSec < toSec)
|
||||
{
|
||||
if(nowSec >= fromSec && nowSec <= toSec)
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Session passes midnight.
|
||||
if(nowSec >= fromSec || nowSec <= toSec)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// If broker does not expose sessions for this symbol, do not block by session.
|
||||
if(!hasAny)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CanOpenNewPositionNow(const ENUM_ORDER_TYPE orderType)
|
||||
{
|
||||
if(!InpUseSessionModeGate)
|
||||
return true;
|
||||
if(InpBypassGateInTester && (bool)MQLInfoInteger(MQL_TESTER))
|
||||
return true;
|
||||
|
||||
long tradeMode = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
|
||||
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED ||
|
||||
tradeMode == SYMBOL_TRADE_MODE_CLOSEONLY)
|
||||
return false;
|
||||
if(orderType == ORDER_TYPE_BUY &&
|
||||
tradeMode == SYMBOL_TRADE_MODE_SHORTONLY)
|
||||
return false;
|
||||
if(orderType == ORDER_TYPE_SELL &&
|
||||
tradeMode == SYMBOL_TRADE_MODE_LONGONLY)
|
||||
return false;
|
||||
|
||||
if(!IsWithinAnyTradeSession(TimeCurrent()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TryExitOnBreak(const TrendlineModel &m)
|
||||
{
|
||||
long posType;
|
||||
double vol;
|
||||
if(!GetCurrentPosition(posType, vol))
|
||||
return;
|
||||
|
||||
double close1 = iClose(_Symbol, _Period, 1);
|
||||
datetime t1 = iTime(_Symbol, _Period, 1);
|
||||
double line1 = TrendlinePriceAtTime(m, t1);
|
||||
double buf = InpBreakBuffer * _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)
|
||||
trade.PositionClose(_Symbol);
|
||||
}
|
||||
|
||||
void TryPullbackEntry(const TrendlineModel &m)
|
||||
{
|
||||
long posType;
|
||||
double vol;
|
||||
if(GetCurrentPosition(posType, vol))
|
||||
return;
|
||||
|
||||
MqlRates bars1[], bars2[];
|
||||
if(CopyRates(_Symbol, _Period, 1, 1, bars1) != 1)
|
||||
return;
|
||||
if(CopyRates(_Symbol, _Period, 2, 1, bars2) != 1)
|
||||
return;
|
||||
if(ArraySize(bars1) < 1 || ArraySize(bars2) < 1)
|
||||
return;
|
||||
|
||||
MqlRates b1 = bars1[0];
|
||||
MqlRates b2 = bars2[0];
|
||||
|
||||
double line1 = TrendlinePriceAtTime(m, b1.time);
|
||||
double tol = InpLineTouchTolerance * _Point;
|
||||
|
||||
bool upTrend = (m.a > 0.0);
|
||||
bool downTrend = (m.a < 0.0);
|
||||
|
||||
if(upTrend)
|
||||
{
|
||||
bool touched = (b1.low <= (line1 + tol));
|
||||
bool reclaim = (b1.close > line1);
|
||||
bool bullish = (b1.close > b1.open);
|
||||
bool stillHealthy = (b2.close >= TrendlinePriceAtTime(m, b2.time) - tol);
|
||||
if(touched && reclaim && bullish && stillHealthy)
|
||||
{
|
||||
if(!CanOpenNewPositionNow(ORDER_TYPE_BUY))
|
||||
{
|
||||
datetime nowBar = iTime(_Symbol, _Period, 0);
|
||||
if(nowBar != g_lastEntryBlockLog)
|
||||
{
|
||||
g_lastEntryBlockLog = nowBar;
|
||||
Print("Buy entry skipped: symbol mode/session does not allow opening now");
|
||||
}
|
||||
return;
|
||||
}
|
||||
trade.Buy(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback buy");
|
||||
}
|
||||
}
|
||||
else if(downTrend)
|
||||
{
|
||||
bool touched = (b1.high >= (line1 - tol));
|
||||
bool reject = (b1.close < line1);
|
||||
bool bearish = (b1.close < b1.open);
|
||||
bool stillWeak = (b2.close <= TrendlinePriceAtTime(m, b2.time) + tol);
|
||||
if(touched && reject && bearish && stillWeak)
|
||||
{
|
||||
if(!CanOpenNewPositionNow(ORDER_TYPE_SELL))
|
||||
{
|
||||
datetime nowBar = iTime(_Symbol, _Period, 0);
|
||||
if(nowBar != g_lastEntryBlockLog)
|
||||
{
|
||||
g_lastEntryBlockLog = nowBar;
|
||||
Print("Sell entry skipped: symbol mode/session does not allow opening now");
|
||||
}
|
||||
return;
|
||||
}
|
||||
trade.Sell(InpLots, _Symbol, 0.0, 0.0, 0.0, "Pullback sell");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
g_maHandle = iMA(_Symbol, InpHigherTF, InpMAPeriod, 0, InpMAMethod, InpAppliedPrice);
|
||||
if(g_maHandle == INVALID_HANDLE)
|
||||
return INIT_FAILED;
|
||||
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
g_lastBarTime = 0;
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_maHandle != INVALID_HANDLE)
|
||||
IndicatorRelease(g_maHandle);
|
||||
if(ObjectFind(0, g_lineName) >= 0)
|
||||
ObjectDelete(0, g_lineName);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewBar())
|
||||
return;
|
||||
|
||||
TrendlineModel m;
|
||||
if(!BuildTrendlineFrom3Points(m))
|
||||
return;
|
||||
|
||||
DrawTrendline(m);
|
||||
TryExitOnBreak(m);
|
||||
TryPullbackEntry(m);
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
; SimpleTrendline.mq5 optimization preset
|
||||
; Strategy Tester -> Inputs -> Load
|
||||
; Focus: trendline pullback entries + break exits (no broker SL/TP)
|
||||
;
|
||||
InpHigherTF=16385||16385||0||16388||Y
|
||||
InpMAPeriod=50||20||5||200||Y
|
||||
InpMAMethod=1||0||1||3||Y
|
||||
InpAppliedPrice=0||0||1||6||Y
|
||||
InpHTFBarsToScan=400||200||100||1200||Y
|
||||
InpLineTouchTolerance=100.0||30.0||10.0||300.0||Y
|
||||
InpBreakBuffer=30.0||5.0||5.0||120.0||Y
|
||||
InpLots=0.10||0.10||0.01||0.10||N
|
||||
InpMagic=26042501||26042501||1||26042501||N
|
||||
InpDrawTrendline=false||false||0||true||N
|
||||
@@ -1,173 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| MagicNumberHelpers.mqh |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by ticket and verify magic number and symbol |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByTicketAndMagic(ulong ticket, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
return (PositionGetInteger(POSITION_MAGIC) == magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Select position by ticket and verify symbol, magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionSelectByTicketSymbolAndMagic(ulong ticket, string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
return false;
|
||||
|
||||
return (PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
PositionGetInteger(POSITION_MAGIC) == magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if position exists with correct magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool PositionExistsByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
return PositionSelectByMagic(symbol, magic_number);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position ticket by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
ulong GetPositionTicketByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
return ticket;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ClosePositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
return trade_obj.PositionClose(ticket);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Modify position by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
bool ModifyPositionByMagic(CTrade &trade_obj, string symbol, ulong magic_number,
|
||||
double sl, double tp)
|
||||
{
|
||||
ulong ticket = GetPositionTicketByMagic(symbol, magic_number);
|
||||
if(ticket == 0)
|
||||
return false;
|
||||
|
||||
return trade_obj.PositionModify(ticket, sl, tp);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position profit by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
double GetPositionProfitByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByMagic(symbol, magic_number))
|
||||
return 0.0;
|
||||
|
||||
return PositionGetDouble(POSITION_PROFIT);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get position type by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
ENUM_POSITION_TYPE GetPositionTypeByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
if(!PositionSelectByMagic(symbol, magic_number))
|
||||
return WRONG_VALUE;
|
||||
|
||||
return (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Count positions by symbol and magic number |
|
||||
//+------------------------------------------------------------------+
|
||||
int CountPositionsByMagic(string symbol, ulong magic_number)
|
||||
{
|
||||
int count = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == symbol &&
|
||||
(ulong)PositionGetInteger(POSITION_MAGIC) == magic_number)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Align volume to SYMBOL_VOLUME_STEP / min / max (avoids Invalid volume) |
|
||||
//+------------------------------------------------------------------+
|
||||
double United_NormalizeVolume(const string symbol, double volume)
|
||||
{
|
||||
double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
|
||||
double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
|
||||
if(lotStep <= 0.0)
|
||||
lotStep = 0.01;
|
||||
|
||||
double v = MathFloor(volume / lotStep) * lotStep;
|
||||
|
||||
if(v < minLot)
|
||||
v = minLot;
|
||||
if(v > maxLot)
|
||||
v = maxLot;
|
||||
|
||||
int digits = (int)MathCeil(-MathLog10(lotStep));
|
||||
if(digits < 0)
|
||||
digits = 0;
|
||||
if(digits > 8)
|
||||
digits = 8;
|
||||
|
||||
return NormalizeDouble(v, digits);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,76 +0,0 @@
|
||||
# United EA Strategy Configuration Summary
|
||||
|
||||
## Strategy Symbols and Magic Numbers
|
||||
|
||||
### Strategy 1: DarvasBox
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 135790
|
||||
|
||||
### Strategy 2: EMASlopeDistance
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 12350
|
||||
|
||||
### Strategy 3: RSICrossOverReversal
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 7
|
||||
|
||||
### Strategy 4: RSIMidPointHijack
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Numbers**:
|
||||
- RSIFollow: 1001
|
||||
- RSIReverse: 1002
|
||||
- EMACross: 1003
|
||||
|
||||
### Strategy 5: RSI Scalping APPL (Apple)
|
||||
- **Symbol**: AAPL (Apple stock)
|
||||
- **Magic Number**: 20001
|
||||
- **Note**: Changed from "APPL" to "AAPL" (correct ticker symbol)
|
||||
|
||||
### Strategy 6: RSI Scalping BTCUSD
|
||||
- **Symbol**: BTCUSD (Bitcoin/USD)
|
||||
- **Magic Number**: 123459123
|
||||
|
||||
### Strategy 7: RSI Scalping MSFT
|
||||
- **Symbol**: MSFT (Microsoft stock)
|
||||
- **Magic Number**: 20002
|
||||
|
||||
### Strategy 8: RSI Scalping NVDA
|
||||
- **Symbol**: NVDA (NVIDIA stock)
|
||||
- **Magic Number**: 20003
|
||||
|
||||
### Strategy 9: RSI Scalping TSLA
|
||||
- **Symbol**: TSLA (Tesla stock)
|
||||
- **Magic Number**: 125421321
|
||||
|
||||
### Strategy 10: RSI Scalping XAUUSD
|
||||
- **Symbol**: XAUUSD (Gold/USD)
|
||||
- **Magic Number**: 129102315
|
||||
|
||||
## Important Notes
|
||||
|
||||
1. **Stock Symbols**: Stock symbols (AAPL, MSFT, NVDA, TSLA) must be:
|
||||
- Added to Market Watch in MetaTrader 5
|
||||
- Available from your broker
|
||||
- Use the correct ticker symbol (e.g., "AAPL" not "APPL")
|
||||
|
||||
2. **Magic Numbers**: All strategies have unique magic numbers to prevent interference:
|
||||
- Each strategy can be identified by its magic number
|
||||
- RSIMidPointHijack uses 3 magic numbers (one for each sub-strategy)
|
||||
|
||||
3. **Symbol Configuration**: Each strategy trades on its own symbol:
|
||||
- You can change symbols in the input parameters
|
||||
- The EA will log warnings if a symbol is not available
|
||||
- Strategies with unavailable symbols will be skipped (EA continues running)
|
||||
|
||||
4. **RSI Scalping Strategies**:
|
||||
- Each RSI Scalping variant trades on a different symbol
|
||||
- They all use the same strategy logic but with different parameters
|
||||
- Buy and sell signals are generated based on RSI levels for each symbol
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If stock symbols are not working:
|
||||
1. Check if the symbol exists in your broker's symbol list
|
||||
2. Add the symbol to Market Watch in MetaTrader 5
|
||||
3. Verify the symbol name matches your broker's naming convention
|
||||
4. Some brokers use prefixes/suffixes (e.g., "NASDAQ:AAPL" or "AAPL.US")
|
||||
@@ -1,328 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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");
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,576 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,387 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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
|
||||
@@ -1,269 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,492 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,488 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,615 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,336 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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
|
||||
@@ -1,318 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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 ×[], 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
|
||||
@@ -1,509 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| 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
|
||||
@@ -1,904 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UnitedEA.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.20"
|
||||
#property strict
|
||||
#property description "LOT_* nominal at ORCH_ReferenceBalance; scale = balance/equity ÷ reference (clamped). No performance-evaluator ranking."
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "MagicNumberHelpers.mqh"
|
||||
#define UNITED_V2_DYNAMIC_LOTS
|
||||
double g_DB_LotSize;
|
||||
// Include strategy implementations early so structs are available
|
||||
#include "Strategies/DarvasBoxStrategy.mqh"
|
||||
#include "Strategies/EMASlopeDistanceStrategy.mqh"
|
||||
#include "Strategies/RSICrossOverReversalStrategy.mqh"
|
||||
#include "Strategies/RSIMidPointHijackStrategy.mqh"
|
||||
#include "Strategies/RSIScalpingStrategy.mqh"
|
||||
#include "Strategies/SuperEMAStrategy.mqh"
|
||||
#include "Strategies/RSIReversalAsianStrategy.mqh"
|
||||
#include "Strategies/RSIConsolidationStrategy.mqh"
|
||||
#include "Strategies/SimpleTrendlineStrategy.mqh"
|
||||
#include "Strategies/RSISecretSauceStrategy.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Lot Size Variables (for dynamic lot sizing) |
|
||||
//+------------------------------------------------------------------+
|
||||
double g_ES_LotSize; // EMA Slope Distance lot size
|
||||
double g_RC_LotSize; // RSI CrossOver Reversal lot size
|
||||
double g_RM_LotSize; // RSI MidPoint Hijack lot size
|
||||
|
||||
double g_Pos_RS_APPL;
|
||||
double g_Pos_RS_BTCUSD;
|
||||
double g_Pos_RS_NVDA;
|
||||
double g_Pos_RS_TSLA;
|
||||
double g_Pos_RS_XAUUSD;
|
||||
double g_Pos_RRA_EURUSD;
|
||||
double g_Pos_RRA_AUDUSD;
|
||||
double g_Pos_SE;
|
||||
double g_Pos_RCO;
|
||||
double g_Pos_ST_BTCUSD;
|
||||
double g_Pos_ST_XAUUSD;
|
||||
double g_RSS_LotSize;
|
||||
|
||||
bool United_MayOpenNewEntry(const string symbol, const ulong magic, const bool isBuy)
|
||||
{
|
||||
if(PositionExistsByMagic(symbol, magic))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Enable/Disable Switches |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Strategy Enable/Disable ==="
|
||||
input bool EnableDarvasBox = true;
|
||||
input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = false;
|
||||
input bool EnableRSIScalpingAPPL = false;
|
||||
input bool EnableRSIScalpingBTCUSD = false;
|
||||
input bool EnableRSIScalpingNVDA = false;
|
||||
input bool EnableRSIScalpingTSLA = false;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
input bool EnableSuperEMA = true;
|
||||
input bool EnableRSIConsolidation = true;
|
||||
input bool EnableRSIReversalAsianEURUSD = false;
|
||||
input bool EnableRSIReversalAsianAUDUSD = false;
|
||||
input bool EnableSimpleTrendlineBTCUSD = false;
|
||||
input bool EnableSimpleTrendlineXAUUSD = true;
|
||||
input bool EnableRSISecretSauce = false;
|
||||
|
||||
input group "=== Centralized Lot Size (Granular Per Robot) ==="
|
||||
input double LOT_DB_DarvasBox = 0.04;
|
||||
input double LOT_ES_EMASlopeDistance = 0.09;
|
||||
input double LOT_RC_RSICrossOver = 0.1;
|
||||
input double LOT_RM_RSIMidPointHijack = 0.01;
|
||||
input double LOT_RS_APPL = 5.0;
|
||||
input double LOT_RS_BTCUSD = 0.1;
|
||||
input double LOT_RS_NVDA = 10.0;
|
||||
input double LOT_RS_TSLA = 15.0;
|
||||
input double LOT_RS_XAUUSD = 0.05;
|
||||
input double LOT_RRA_EURUSD = 0.05;
|
||||
input double LOT_RRA_AUDUSD = 0.08;
|
||||
input double LOT_SE_SuperEMA = 0.02;
|
||||
input double LOT_RCO_RSIConsolidation = 0.02;
|
||||
input double LOT_ST_BTCUSD = 0.07;
|
||||
input double LOT_ST_XAUUSD = 0.02;
|
||||
input double LOT_RSS_SecretSauce = 0.01;
|
||||
|
||||
input group "=== Balance-based position sizing ==="
|
||||
input bool ORCH_ScaleLotsByBalance = true;
|
||||
input bool ORCH_UseEquityInsteadOfBalance = false;
|
||||
input double ORCH_ReferenceBalance = 10000.0;
|
||||
input double ORCH_MinBalanceScale = 0.1;
|
||||
input double ORCH_MaxBalanceScale = 10.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== DarvasBox Strategy ==="
|
||||
input string DB_Symbol = "XAU";
|
||||
input int DB_BoxPeriod = 165;
|
||||
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
|
||||
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
|
||||
input double DB_StopLoss = 1665;
|
||||
input double DB_TakeProfit = 3685;
|
||||
input bool DB_EnableLogging = false;
|
||||
input color DB_BoxColor = (color)16711680;
|
||||
input int DB_BoxWidth = 1;
|
||||
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
|
||||
input int DB_MA_Period = 125;
|
||||
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
|
||||
input double DB_TrendThreshold = 4.94;
|
||||
input int DB_VolumeMA_Period = 110;
|
||||
input double DB_VolumeThresholdMultiplier = 1.5;
|
||||
input bool DB_UseVolumeSpikeFilter = true;
|
||||
input bool DB_UseTrendFilter = true;
|
||||
input int DB_MagicNumber = 135790;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== EMA Slope Distance Strategy ==="
|
||||
input string ES_Symbol = "XAU";
|
||||
input int ES_EMA_Periode = 46;
|
||||
input double ES_PreisSchwelle = 600.0;
|
||||
input double ES_SteigungSchwelle = 80.0;
|
||||
input int ES_ÜberwachungTimeout = 800;
|
||||
input double ES_TrailingStop = 370.0;
|
||||
input bool ES_UseTrailingStop = true;
|
||||
input double ES_TrailingActivationPips = 0.0;
|
||||
input bool ES_UseStaleStopLossExit = false;
|
||||
input int ES_StaleStopLossSeconds = 33800;
|
||||
input double ES_LotGröße = 0.03;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
input bool ES_UseBarData = true;
|
||||
input int ES_MaxTradesPerCrossover = 9;
|
||||
input int ES_ProfitCheckBars = 18;
|
||||
input bool ES_CloseUnprofitableTrades = true;
|
||||
input bool ES_UseWeeklyADXFilter = true;
|
||||
input int ES_WeeklyADXPeriod = 15;
|
||||
input double ES_WeeklyADXMin = 40.0;
|
||||
input int ES_WeeklyADXBarShift = 2;
|
||||
input bool ES_WeeklyADXUseDirection = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 3: RSICrossOverReversalXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI CrossOver Reversal Strategy ==="
|
||||
input string RC_Symbol = "XAU";
|
||||
input int RC_MagicNumber = 7;
|
||||
input int RC_rsiPeriod = 19;
|
||||
input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.01;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
|
||||
input int RC_emaPeriod = 140;
|
||||
input double RC_emaSlopeThreshold = 105;
|
||||
input double RC_exitBuyRSI = 86;
|
||||
input double RC_exitSellRSI = 10;
|
||||
input double RC_TrailingStop = 295;
|
||||
input double RC_emaDistanceThreshold = 165;
|
||||
input bool RC_UseTrendStrengthFilter = true;
|
||||
input int RC_tradingHourOneBegin = 24;
|
||||
input int RC_tradingHourOneEnd = 22;
|
||||
input int RC_tradingHourTwoBegin = 6;
|
||||
input int RC_tradingHourTwoEnd = 19;
|
||||
input bool RC_Sunday = false;
|
||||
input bool RC_Monday = false;
|
||||
input bool RC_Tuesday = true;
|
||||
input bool RC_Wednesday = true;
|
||||
input bool RC_Thursday = true;
|
||||
input bool RC_Friday = false;
|
||||
input bool RC_Saturday = false;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 4: RSIMidPointHijackXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAU";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.02;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
input bool RM_InpEnableRSIFollow = true;
|
||||
input bool RM_InpEnableRSIReverse = true;
|
||||
input bool RM_InpEnableEMACross = true;
|
||||
input bool RM_InpEnableStrategyLock = false;
|
||||
input double RM_InpLockProfitThreshold = 0.0;
|
||||
input bool RM_InpCloseOppositeTrades = false;
|
||||
input int RM_InpRSIPeriod = 32;
|
||||
input int RM_InpRSIOverbought = 78;
|
||||
input int RM_InpRSIOversold = 46;
|
||||
input int RM_InpRSIExitLevel = 44;
|
||||
input int RM_InpRSIFollowStartHour = 23;
|
||||
input int RM_InpRSIFollowEndHour = 8;
|
||||
input bool RM_InpRSIFollowCloseOutsideHours = false;
|
||||
input int RM_InpRSIReversePeriod = 59;
|
||||
input int RM_InpRSIReverseOverbought = 51;
|
||||
input int RM_InpRSIReverseOversold = 49;
|
||||
input int RM_InpRSIReverseCrossLevel = 53;
|
||||
input int RM_InpRSIReverseExitLevel = 48;
|
||||
input int RM_InpRSIReverseStartHour = 7;
|
||||
input int RM_InpRSIReverseEndHour = 13;
|
||||
input bool RM_InpRSIReverseCloseOutsideHours = false;
|
||||
input int RM_InpRSIReverseCooldownBars = 15;
|
||||
input bool RM_InpRSIReverseCooldownOnLoss = true;
|
||||
input int RM_InpEMAPeriod = 120;
|
||||
input int RM_InpEMACrossStartHour = 8;
|
||||
input int RM_InpEMACrossEndHour = 14;
|
||||
input bool RM_InpEMACrossCloseOutsideHours = true;
|
||||
input bool RM_InpUseEMADistanceEntry = true;
|
||||
input double RM_InpEMADistancePips = 160.0;
|
||||
input int RM_InpEMADistancePeriod = 26;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 5-10: RSI Scalping Strategies |
|
||||
//| Each RSI Scalping strategy trades on its own symbol: |
|
||||
//| - APPL: Apple stock (AAPL) |
|
||||
//| - BTCUSD: Bitcoin/USD |
|
||||
//| - NVDA: NVIDIA stock |
|
||||
//| - TSLA: Tesla stock |
|
||||
//| - XAUUSD: Gold/USD |
|
||||
//| |
|
||||
//| PEPPERSTONE US SYMBOL FORMATS: |
|
||||
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
|
||||
//| - To find correct symbols: |
|
||||
//| 1. Open Market Watch (Ctrl+M) |
|
||||
//| 2. Right-click > Show All |
|
||||
//| 3. Search for the stock name |
|
||||
//| 4. Use the exact symbol name shown |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
||||
input string RS_APPL_Symbol = "AAPL.NAS"; // Pepperstone / match tester set (also try AAPL.US)
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_APPL_RSI_Overbought = 80;
|
||||
input double RS_APPL_RSI_Oversold = 78;
|
||||
input double RS_APPL_RSI_Target_Buy = 94;
|
||||
input double RS_APPL_RSI_Target_Sell = 44;
|
||||
input int RS_APPL_BarsToWait = 7;
|
||||
input double RS_APPL_LotSize = 25;
|
||||
input int RS_APPL_MagicNumber = 20001;
|
||||
input int RS_APPL_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD ==="
|
||||
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
|
||||
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_BTCUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_BTCUSD_RSI_Overbought = 90;
|
||||
input double RS_BTCUSD_RSI_Oversold = 73;
|
||||
input double RS_BTCUSD_RSI_Target_Buy = 88;
|
||||
input double RS_BTCUSD_RSI_Target_Sell = 48;
|
||||
input int RS_BTCUSD_BarsToWait = 6;
|
||||
input double RS_BTCUSD_LotSize = 0.1;
|
||||
input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.NAS"; // Pepperstone / match tester set (also try NVDA.US)
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_NVDA_RSI_Overbought = 36;
|
||||
input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 50;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.NAS"; // Pepperstone / match tester set (also try TSLA.US)
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_TSLA_RSI_Overbought = 54;
|
||||
input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 50;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD ==="
|
||||
input string RS_XAUUSD_Symbol = "XAU";
|
||||
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_XAUUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_XAUUSD_RSI_Overbought = 71;
|
||||
input double RS_XAUUSD_RSI_Oversold = 57;
|
||||
input double RS_XAUUSD_RSI_Target_Buy = 80;
|
||||
input double RS_XAUUSD_RSI_Target_Sell = 57;
|
||||
input int RS_XAUUSD_BarsToWait = 4;
|
||||
input double RS_XAUUSD_LotSize = 0.1;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping Reversal Escape (XAUUSD only) ==="
|
||||
input bool RS_UseReversalEscape = true;
|
||||
input int RS_ReversalATRPeriod = 14;
|
||||
input double RS_ReversalAdverseAtrMult = 5.25;
|
||||
input int RS_ReversalSignsRequired = 2;
|
||||
input double RS_ReversalRsiVelocity = 16.0;
|
||||
input double RS_ReversalBodyAtrMult = 5.1;
|
||||
|
||||
input group "=== RSI Scalping APPL — Trailing (cluster-fuck BTC-style defaults) ==="
|
||||
input bool RS_APPL_UseTrailingStop = true;
|
||||
input double RS_APPL_TrailDistancePoints = 120.0;
|
||||
input double RS_APPL_TrailActivationPoints = 0.0;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD — Trailing ==="
|
||||
input bool RS_BTCUSD_UseTrailingStop = true;
|
||||
input double RS_BTCUSD_TrailDistancePoints = 120.0;
|
||||
input double RS_BTCUSD_TrailActivationPoints = 0.0;
|
||||
|
||||
input group "=== RSI Scalping NVDA — Trailing ==="
|
||||
input bool RS_NVDA_UseTrailingStop = true;
|
||||
input double RS_NVDA_TrailDistancePoints = 375.0;
|
||||
input double RS_NVDA_TrailActivationPoints = 75.0;
|
||||
|
||||
input group "=== RSI Scalping TSLA — Trailing ==="
|
||||
input bool RS_TSLA_UseTrailingStop = true;
|
||||
input double RS_TSLA_TrailDistancePoints = 900.0;
|
||||
input double RS_TSLA_TrailActivationPoints = 950.0;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD — Trailing ==="
|
||||
input bool RS_XAUUSD_UseTrailingStop = true;
|
||||
input double RS_XAUUSD_TrailDistancePoints = 1000.0;
|
||||
input double RS_XAUUSD_TrailActivationPoints = 550.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 11-12: RSI Reversal Asian Strategies |
|
||||
//| Each RSI Reversal Asian strategy trades on its own symbol: |
|
||||
//| - EURUSD: Euro/USD |
|
||||
//| - AUDUSD: Australian Dollar/USD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Reversal Asian EURUSD ==="
|
||||
input string RRA_EURUSD_Symbol = "EURUSD";
|
||||
input int RRA_EURUSD_RSIPeriod = 28;
|
||||
input double RRA_EURUSD_OverboughtLevel = 60;
|
||||
input double RRA_EURUSD_OversoldLevel = 8;
|
||||
input int RRA_EURUSD_TakeProfitPips = 175;
|
||||
input int RRA_EURUSD_StopLossPips = 5;
|
||||
input double RRA_EURUSD_MaxLotSize = 0.1;
|
||||
input int RRA_EURUSD_MaxSpread = 1000;
|
||||
input int RRA_EURUSD_MaxDuration = 270;
|
||||
input bool RRA_EURUSD_UseStopLoss = false;
|
||||
input bool RRA_EURUSD_UseTakeProfit = false;
|
||||
input bool RRA_EURUSD_UseRSIExit = true;
|
||||
input double RRA_EURUSD_RSIExitLevel = 55;
|
||||
input bool RRA_EURUSD_CloseOutsideSession = false;
|
||||
input ENUM_TIMEFRAMES RRA_EURUSD_TimeFrame = PERIOD_M15;
|
||||
input int RRA_EURUSD_MagicNumber = 30001;
|
||||
input int RRA_EURUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Reversal Asian AUDUSD ==="
|
||||
input string RRA_AUDUSD_Symbol = "AUDUSD";
|
||||
input int RRA_AUDUSD_RSIPeriod = 28;
|
||||
input double RRA_AUDUSD_OverboughtLevel = 68;
|
||||
input double RRA_AUDUSD_OversoldLevel = 30;
|
||||
input int RRA_AUDUSD_TakeProfitPips = 175;
|
||||
input int RRA_AUDUSD_StopLossPips = 5;
|
||||
input double RRA_AUDUSD_MaxLotSize = 0.2;
|
||||
input int RRA_AUDUSD_MaxSpread = 1000;
|
||||
input int RRA_AUDUSD_MaxDuration = 340;
|
||||
input bool RRA_AUDUSD_UseStopLoss = false;
|
||||
input bool RRA_AUDUSD_UseTakeProfit = false;
|
||||
input bool RRA_AUDUSD_UseRSIExit = true;
|
||||
input double RRA_AUDUSD_RSIExitLevel = 48;
|
||||
input bool RRA_AUDUSD_CloseOutsideSession = true;
|
||||
input ENUM_TIMEFRAMES RRA_AUDUSD_TimeFrame = PERIOD_M15;
|
||||
input int RRA_AUDUSD_MagicNumber = 30002;
|
||||
input int RRA_AUDUSD_Slippage = 3;
|
||||
|
||||
input group "=== SuperEMA (EMA + CCI + MACD) ==="
|
||||
input string SE_Symbol = "XAU";
|
||||
input ENUM_TIMEFRAMES SE_Timeframe = PERIOD_M15;
|
||||
input double SE_LotSize = 0.01;
|
||||
input int SE_SlippagePoints = 55;
|
||||
input int SE_MagicNumber = 940001;
|
||||
input int SE_EmaFast = 40;
|
||||
input int SE_EmaMid = 180;
|
||||
input int SE_EmaSlow = 125;
|
||||
input int SE_EmaTrendBars = 3;
|
||||
input int SE_CciPeriod = 17;
|
||||
input double SE_CciOverbought = 80.0;
|
||||
input double SE_CciOversold = -140.0;
|
||||
input int SE_PullbackCciLookback = 20;
|
||||
input int SE_MacdFast = 14;
|
||||
input int SE_MacdSlow = 38;
|
||||
input int SE_MacdSignal = 9;
|
||||
input ENUM_SE_ENTRY_STYLE SE_EntryStyle = SE_ENTRY_LAMBERT;
|
||||
input bool SE_OneTradeOnly = true;
|
||||
input bool SE_UseStructuralSL = false;
|
||||
input double SE_SlBufferPoints = 110;
|
||||
input bool SE_ExitOnTrendFlip = false;
|
||||
input bool SE_ExitOnMacdFlip = false;
|
||||
input bool SE_ExitOnCciZeroCross = true;
|
||||
input int SE_MaxHoldingBars = 168;
|
||||
input bool SE_ExitBelowMidEma = false;
|
||||
input bool SE_DebugLogs = false;
|
||||
|
||||
input group "=== RSI Consolidation (ranging / mean-reversion) ==="
|
||||
input string RCO_Symbol = "XAU";
|
||||
input ENUM_TIMEFRAMES RCO_SignalTF = PERIOD_M15;
|
||||
input bool RCO_EntryOnNewBarOnly = true;
|
||||
input int RCO_ADX_Period = 23;
|
||||
input double RCO_ADX_Max = 29.0;
|
||||
input bool RCO_UseATRRatioFilter = true;
|
||||
input int RCO_ATR_Period = 8;
|
||||
input int RCO_ATR_SMA_Period = 35;
|
||||
input double RCO_ATR_Ratio_Max = 1.36;
|
||||
input bool RCO_UseFlatEMAFilter = true;
|
||||
input int RCO_EMA_Fast = 13;
|
||||
input int RCO_EMA_Slow = 17;
|
||||
input double RCO_EMA_Separation_MaxPct = 0.26;
|
||||
input int RCO_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RCO_RSI_Price = PRICE_OPEN;
|
||||
input double RCO_RSI_Oversold = 22.0;
|
||||
input double RCO_RSI_Overbought = 63.0;
|
||||
input bool RCO_UseRSI_MeanExit = true;
|
||||
input double RCO_RSI_Exit_Long = 48.0;
|
||||
input double RCO_RSI_Exit_Short = 52.0;
|
||||
input double RCO_SL_ATR_Mult = 2.15;
|
||||
input double RCO_TP_ATR_Mult = 2.40;
|
||||
input int RCO_MaxBarsInTrade = 54;
|
||||
input double RCO_Lots = 0.10;
|
||||
input ulong RCO_MagicNumber = 20250420;
|
||||
input int RCO_Slippage = 10;
|
||||
input int RCO_MaxSpreadPoints = 28;
|
||||
|
||||
input group "=== SimpleTrendline BTCUSD ==="
|
||||
input string ST_BTC_Symbol = "BTCUSD";
|
||||
input ENUM_TIMEFRAMES ST_BTC_SignalTF = PERIOD_H1;
|
||||
input ENUM_TIMEFRAMES ST_BTC_HigherTF = PERIOD_H4;
|
||||
input int ST_BTC_MAPeriod = 150;
|
||||
input ENUM_MA_METHOD ST_BTC_MAMethod = MODE_SMMA;
|
||||
input ENUM_APPLIED_PRICE ST_BTC_AppliedPrice = PRICE_OPEN;
|
||||
input int ST_BTC_HTFBarsToScan = 1200;
|
||||
input double ST_BTC_LineTouchTolerance = 170.0;
|
||||
input double ST_BTC_BreakBuffer = 90.0;
|
||||
input ulong ST_BTC_MagicNumber = 26042501;
|
||||
input bool ST_BTC_DrawTrendline = true;
|
||||
|
||||
input group "=== SimpleTrendline XAUUSD ==="
|
||||
input string ST_XAU_Symbol = "XAU";
|
||||
input ENUM_TIMEFRAMES ST_XAU_SignalTF = PERIOD_H1;
|
||||
input ENUM_TIMEFRAMES ST_XAU_HigherTF = PERIOD_M10;
|
||||
input int ST_XAU_MAPeriod = 65;
|
||||
input ENUM_MA_METHOD ST_XAU_MAMethod = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE ST_XAU_AppliedPrice = PRICE_OPEN;
|
||||
input int ST_XAU_HTFBarsToScan = 500;
|
||||
input double ST_XAU_LineTouchTolerance = 220.0;
|
||||
input double ST_XAU_BreakBuffer = 110.0;
|
||||
input ulong ST_XAU_MagicNumber = 26042503;
|
||||
input bool ST_XAU_DrawTrendline = true;
|
||||
|
||||
input group "=== RSI Secret Sauce XAUUSD ==="
|
||||
input string RSS_Symbol = "XAU";
|
||||
input int RSS_MagicNumber = 789012;
|
||||
input int RSS_Slippage = 10;
|
||||
input ENUM_TIMEFRAMES RSS_Timeframe = PERIOD_M30;
|
||||
input int RSS_RSIPeriod = 16;
|
||||
input double RSS_RSIOverbought = 72.5;
|
||||
input double RSS_RSIOversold = 32.5;
|
||||
input int RSS_RSILookback = 60;
|
||||
input int RSS_PeakBars = 2;
|
||||
input double RSS_StopLossATR = 2.75;
|
||||
input double RSS_TakeProfitATR = 5.0;
|
||||
input int RSS_ATRPeriod = 14;
|
||||
input bool RSS_UseSwingStopLoss = false;
|
||||
input int RSS_SwingLookback = 30;
|
||||
input int RSS_MaxPositions = 1;
|
||||
input int RSS_MinBarsBetweenTrades = 7;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Balance scaling: LOT_* = nominal size at ORCH_ReferenceBalance |
|
||||
//+------------------------------------------------------------------+
|
||||
double United_BalanceScaleFactor()
|
||||
{
|
||||
if(!ORCH_ScaleLotsByBalance || ORCH_ReferenceBalance <= 0.0)
|
||||
return 1.0;
|
||||
const double money = ORCH_UseEquityInsteadOfBalance
|
||||
? AccountInfoDouble(ACCOUNT_EQUITY)
|
||||
: AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
double raw = money / ORCH_ReferenceBalance;
|
||||
if(raw < ORCH_MinBalanceScale)
|
||||
raw = ORCH_MinBalanceScale;
|
||||
if(raw > ORCH_MaxBalanceScale)
|
||||
raw = ORCH_MaxBalanceScale;
|
||||
return raw;
|
||||
}
|
||||
|
||||
double United_ScaledLot(const double baseLot)
|
||||
{
|
||||
const double lot = baseLot * United_BalanceScaleFactor();
|
||||
return (lot > 0.0 ? lot : 0.0);
|
||||
}
|
||||
|
||||
void United_RefreshScaledLots()
|
||||
{
|
||||
g_DB_LotSize = United_ScaledLot(LOT_DB_DarvasBox);
|
||||
g_ES_LotSize = United_ScaledLot(LOT_ES_EMASlopeDistance);
|
||||
g_RC_LotSize = United_ScaledLot(LOT_RC_RSICrossOver);
|
||||
g_RM_LotSize = United_ScaledLot(LOT_RM_RSIMidPointHijack);
|
||||
g_Pos_RS_APPL = United_ScaledLot(LOT_RS_APPL);
|
||||
g_Pos_RS_BTCUSD = United_ScaledLot(LOT_RS_BTCUSD);
|
||||
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
|
||||
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
|
||||
g_Pos_RS_XAUUSD = United_ScaledLot(LOT_RS_XAUUSD);
|
||||
g_Pos_RRA_EURUSD = United_ScaledLot(LOT_RRA_EURUSD);
|
||||
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
|
||||
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
|
||||
g_Pos_RCO = United_ScaledLot(LOT_RCO_RSIConsolidation);
|
||||
g_Pos_ST_BTCUSD = United_ScaledLot(LOT_ST_BTCUSD);
|
||||
g_Pos_ST_XAUUSD = United_ScaledLot(LOT_ST_XAUUSD);
|
||||
g_RSS_LotSize = United_ScaledLot(LOT_RSS_SecretSauce);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - DarvasBox |
|
||||
//+------------------------------------------------------------------+
|
||||
struct DarvasBoxData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
double boxHigh;
|
||||
double boxLow;
|
||||
bool boxFormed;
|
||||
datetime lastBoxTime;
|
||||
string boxName;
|
||||
double minStopLevel;
|
||||
double point;
|
||||
CTrade trade;
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - EMA Slope Distance |
|
||||
//+------------------------------------------------------------------+
|
||||
struct EMASlopeData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int ema_handle;
|
||||
double ema_array[];
|
||||
datetime letzte_überwachung_zeit;
|
||||
bool überwachung_aktiv;
|
||||
bool preis_trigger_aktiv;
|
||||
bool steigung_trigger_aktiv;
|
||||
int ticket;
|
||||
CTrade trade;
|
||||
int trades_in_current_crossover;
|
||||
bool crossover_detected;
|
||||
datetime trade_open_time;
|
||||
datetime last_bar_time;
|
||||
datetime es_last_sl_adjust_success_time;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI CrossOver Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSICrossOverData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int emaHandle;
|
||||
double previousRSIDef;
|
||||
CTrade trade;
|
||||
datetime lastTradeTime;
|
||||
datetime bartime;
|
||||
bool WeekDays[7];
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI MidPoint Hijack |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIMidPointData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought;
|
||||
bool rsiOversold;
|
||||
bool rsiReverseOverbought;
|
||||
bool rsiReverseOversold;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal;
|
||||
bool emaCrossSellSignal;
|
||||
int emaCrossSignalBar;
|
||||
datetime lastBarTime;
|
||||
datetime rsiReverseLastCloseTime;
|
||||
bool rsiReverseInCooldown;
|
||||
double lastBarRSI;
|
||||
double lastBarRSIReverse;
|
||||
double lastBarEMA;
|
||||
double lastBarClose;
|
||||
double lastBarEMAPrev;
|
||||
double lastBarClosePrev;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Strategy Instances |
|
||||
//+------------------------------------------------------------------+
|
||||
DarvasBoxData dbData;
|
||||
EMASlopeData esData;
|
||||
RSICrossOverData rcData;
|
||||
RSIMidPointData rmData;
|
||||
RSIScalpingData rsAPPLData;
|
||||
RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
SuperEMAData seData;
|
||||
RSIConsolidationData rcoData;
|
||||
SimpleTrendlineData stBTCData;
|
||||
SimpleTrendlineData stXAUData;
|
||||
RSISecretSauceOrcData rssData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI Reversal Asian |
|
||||
//+------------------------------------------------------------------+
|
||||
RSIReversalAsianData rraEURUSDData;
|
||||
RSIReversalAsianData rraAUDUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
int initResult = INIT_SUCCEEDED;
|
||||
|
||||
United_RefreshScaledLots();
|
||||
|
||||
// Initialize strategies - log warnings but don't fail entire EA if symbol unavailable
|
||||
if(EnableDarvasBox)
|
||||
if(!InitDarvasBox(DB_Symbol))
|
||||
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
if(!InitEMASlopeDistance(ES_Symbol))
|
||||
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
if(!InitRSICrossOverReversal(RC_Symbol))
|
||||
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
if(!InitRSIMidPointHijack(RM_Symbol))
|
||||
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
|
||||
|
||||
// Initialize RSI Scalping strategies - don't fail entire EA if symbol unavailable
|
||||
if(EnableRSIScalpingAPPL)
|
||||
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
|
||||
if(EnableRSISecretSauce)
|
||||
if(!InitRSISecretSauce(rssData, RSS_Symbol))
|
||||
Print("Warning: RSI Secret Sauce failed to initialize for symbol '", RSS_Symbol, "'");
|
||||
|
||||
if(EnableSuperEMA)
|
||||
if(!InitSuperEMA(seData, SE_Symbol, SE_Timeframe, SE_SlippagePoints, SE_MagicNumber,
|
||||
SE_EmaFast, SE_EmaMid, SE_EmaSlow, SE_EmaTrendBars,
|
||||
SE_CciPeriod, SE_CciOverbought, SE_CciOversold, SE_PullbackCciLookback,
|
||||
SE_MacdFast, SE_MacdSlow, SE_MacdSignal,
|
||||
SE_EntryStyle, SE_OneTradeOnly, SE_UseStructuralSL, SE_SlBufferPoints,
|
||||
SE_ExitOnTrendFlip, SE_ExitOnMacdFlip, SE_ExitOnCciZeroCross,
|
||||
SE_MaxHoldingBars, SE_ExitBelowMidEma, SE_DebugLogs))
|
||||
Print("Warning: SuperEMA failed to initialize for symbol '", SE_Symbol, "'");
|
||||
|
||||
if(EnableRSIConsolidation)
|
||||
if(!InitRSIConsolidation(rcoData, RCO_Symbol, RCO_SignalTF, RCO_EntryOnNewBarOnly,
|
||||
RCO_ADX_Period, RCO_ADX_Max, RCO_UseATRRatioFilter, RCO_ATR_Period, RCO_ATR_SMA_Period, RCO_ATR_Ratio_Max,
|
||||
RCO_UseFlatEMAFilter, RCO_EMA_Fast, RCO_EMA_Slow, RCO_EMA_Separation_MaxPct,
|
||||
RCO_RSI_Period, RCO_RSI_Price, RCO_RSI_Oversold, RCO_RSI_Overbought,
|
||||
RCO_UseRSI_MeanExit, RCO_RSI_Exit_Long, RCO_RSI_Exit_Short, RCO_SL_ATR_Mult, RCO_TP_ATR_Mult,
|
||||
RCO_MaxBarsInTrade, RCO_MagicNumber, RCO_Slippage, RCO_MaxSpreadPoints))
|
||||
Print("Warning: RSIConsolidation failed to initialize for symbol '", RCO_Symbol, "'");
|
||||
|
||||
// Initialize RSI Reversal Asian strategies
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
if(!InitRSIReversalAsian(rraEURUSDData, RRA_EURUSD_Symbol, RRA_EURUSD_RSIPeriod, RRA_EURUSD_OverboughtLevel, RRA_EURUSD_OversoldLevel,
|
||||
RRA_EURUSD_TakeProfitPips, RRA_EURUSD_StopLossPips, LOT_RRA_EURUSD,
|
||||
RRA_EURUSD_MaxSpread, RRA_EURUSD_MaxDuration, RRA_EURUSD_UseStopLoss,
|
||||
RRA_EURUSD_UseTakeProfit, RRA_EURUSD_UseRSIExit, RRA_EURUSD_RSIExitLevel,
|
||||
RRA_EURUSD_CloseOutsideSession, RRA_EURUSD_TimeFrame, RRA_EURUSD_MagicNumber, RRA_EURUSD_Slippage))
|
||||
Print("Warning: RSIReversalAsianEURUSD strategy failed to initialize for symbol '", RRA_EURUSD_Symbol, "'");
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
if(!InitRSIReversalAsian(rraAUDUSDData, RRA_AUDUSD_Symbol, RRA_AUDUSD_RSIPeriod, RRA_AUDUSD_OverboughtLevel, RRA_AUDUSD_OversoldLevel,
|
||||
RRA_AUDUSD_TakeProfitPips, RRA_AUDUSD_StopLossPips, LOT_RRA_AUDUSD,
|
||||
RRA_AUDUSD_MaxSpread, RRA_AUDUSD_MaxDuration, RRA_AUDUSD_UseStopLoss,
|
||||
RRA_AUDUSD_UseTakeProfit, RRA_AUDUSD_UseRSIExit, RRA_AUDUSD_RSIExitLevel,
|
||||
RRA_AUDUSD_CloseOutsideSession, RRA_AUDUSD_TimeFrame, RRA_AUDUSD_MagicNumber, RRA_AUDUSD_Slippage))
|
||||
Print("Warning: RSIReversalAsianAUDUSD strategy failed to initialize for symbol '", RRA_AUDUSD_Symbol, "'");
|
||||
|
||||
if(EnableSimpleTrendlineBTCUSD)
|
||||
if(!InitSimpleTrendline(stBTCData, ST_BTC_Symbol, ST_BTC_SignalTF, ST_BTC_HigherTF, ST_BTC_MAPeriod,
|
||||
ST_BTC_MAMethod, ST_BTC_AppliedPrice, ST_BTC_HTFBarsToScan,
|
||||
ST_BTC_LineTouchTolerance, ST_BTC_BreakBuffer, ST_BTC_MagicNumber, ST_BTC_DrawTrendline))
|
||||
Print("Warning: SimpleTrendlineBTCUSD failed to initialize for symbol '", ST_BTC_Symbol, "'");
|
||||
|
||||
if(EnableSimpleTrendlineXAUUSD)
|
||||
if(!InitSimpleTrendline(stXAUData, ST_XAU_Symbol, ST_XAU_SignalTF, ST_XAU_HigherTF, ST_XAU_MAPeriod,
|
||||
ST_XAU_MAMethod, ST_XAU_AppliedPrice, ST_XAU_HTFBarsToScan,
|
||||
ST_XAU_LineTouchTolerance, ST_XAU_BreakBuffer, ST_XAU_MagicNumber, ST_XAU_DrawTrendline))
|
||||
Print("Warning: SimpleTrendlineXAUUSD failed to initialize for symbol '", ST_XAU_Symbol, "'");
|
||||
|
||||
Print("United EA initialized. Active strategies: ",
|
||||
(EnableDarvasBox ? "DarvasBox " : ""),
|
||||
(EnableEMASlopeDistance ? "EMASlope " : ""),
|
||||
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
|
||||
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
|
||||
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
|
||||
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
|
||||
(EnableRSISecretSauce ? "RSISecretSauce " : ""),
|
||||
(EnableSuperEMA ? "SuperEMA " : ""),
|
||||
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
|
||||
(EnableRSIReversalAsianEURUSD ? "RSIReversalAsianEURUSD " : ""),
|
||||
(EnableRSIReversalAsianAUDUSD ? "RSIReversalAsianAUDUSD " : ""),
|
||||
(EnableSimpleTrendlineBTCUSD ? "SimpleTrendlineBTCUSD " : ""),
|
||||
(EnableSimpleTrendlineXAUUSD ? "SimpleTrendlineXAUUSD " : ""));
|
||||
|
||||
return initResult;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
DeinitDarvasBox();
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
DeinitEMASlopeDistance();
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
DeinitRSICrossOverReversal();
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
DeinitRSIMidPointHijack();
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
DeinitRSIScalping(rsAPPLData);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
DeinitRSIScalping(rsBTCUSDData);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
DeinitRSIScalping(rsNVDAData);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
DeinitRSIScalping(rsTSLAData);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
|
||||
if(EnableRSISecretSauce)
|
||||
DeinitRSISecretSauce(rssData);
|
||||
|
||||
if(EnableSuperEMA)
|
||||
DeinitSuperEMA(seData);
|
||||
|
||||
if(EnableRSIConsolidation)
|
||||
DeinitRSIConsolidation(rcoData);
|
||||
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
DeinitRSIReversalAsian(rraEURUSDData);
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
DeinitRSIReversalAsian(rraAUDUSDData);
|
||||
|
||||
if(EnableSimpleTrendlineBTCUSD)
|
||||
DeinitSimpleTrendline(stBTCData);
|
||||
if(EnableSimpleTrendlineXAUUSD)
|
||||
DeinitSimpleTrendline(stXAUData);
|
||||
|
||||
Print("United EA deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
United_RefreshScaledLots();
|
||||
|
||||
if(EnableDarvasBox)
|
||||
ProcessDarvasBox(DB_Symbol);
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
ProcessEMASlopeDistance(ES_Symbol);
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
ProcessRSICrossOverReversal(RC_Symbol);
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
ProcessRSIMidPointHijack(RM_Symbol);
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
|
||||
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
|
||||
RS_APPL_BarsToWait, g_Pos_RS_APPL, RS_APPL_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_APPL_UseTrailingStop, RS_APPL_TrailDistancePoints, RS_APPL_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
|
||||
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
|
||||
RS_BTCUSD_BarsToWait, g_Pos_RS_BTCUSD, RS_BTCUSD_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_BTCUSD_UseTrailingStop, RS_BTCUSD_TrailDistancePoints, RS_BTCUSD_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
|
||||
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
|
||||
RS_NVDA_BarsToWait, g_Pos_RS_NVDA, RS_NVDA_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_NVDA_UseTrailingStop, RS_NVDA_TrailDistancePoints, RS_NVDA_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
|
||||
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
|
||||
RS_TSLA_BarsToWait, g_Pos_RS_TSLA, RS_TSLA_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_TSLA_UseTrailingStop, RS_TSLA_TrailDistancePoints, RS_TSLA_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
|
||||
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
|
||||
RS_XAUUSD_BarsToWait, g_Pos_RS_XAUUSD, RS_XAUUSD_MagicNumber,
|
||||
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
|
||||
|
||||
if(EnableRSISecretSauce)
|
||||
ProcessRSISecretSauce(rssData, g_RSS_LotSize);
|
||||
|
||||
if(EnableRSIReversalAsianEURUSD)
|
||||
ProcessRSIReversalAsian(rraEURUSDData, g_Pos_RRA_EURUSD);
|
||||
|
||||
if(EnableRSIReversalAsianAUDUSD)
|
||||
ProcessRSIReversalAsian(rraAUDUSDData, g_Pos_RRA_AUDUSD);
|
||||
|
||||
if(EnableSuperEMA)
|
||||
ProcessSuperEMA(seData, g_Pos_SE);
|
||||
|
||||
if(EnableRSIConsolidation)
|
||||
ProcessRSIConsolidation(rcoData, g_Pos_RCO);
|
||||
|
||||
if(EnableSimpleTrendlineBTCUSD)
|
||||
ProcessSimpleTrendline(stBTCData, g_Pos_ST_BTCUSD);
|
||||
if(EnableSimpleTrendlineXAUUSD)
|
||||
ProcessSimpleTrendline(stXAUData, g_Pos_ST_XAUUSD);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
|
Before Width: | Height: | Size: 29 KiB |
@@ -1,613 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| UnitedEA.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
#property strict
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include <Trade\PositionInfo.mqh>
|
||||
#include <Indicators\Trend.mqh>
|
||||
#include <Indicators\Volumes.mqh>
|
||||
#include "MagicNumberHelpers.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy Enable/Disable Switches |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== Strategy Enable/Disable ==="
|
||||
input bool EnableDarvasBox = true;
|
||||
input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = true;
|
||||
input bool EnableRSIScalpingAPPL = true;
|
||||
input bool EnableRSIScalpingBTCUSD = true;
|
||||
input bool EnableRSIScalpingMSFT = true;
|
||||
input bool EnableRSIScalpingNVDA = true;
|
||||
input bool EnableRSIScalpingTSLA = true;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== DarvasBox Strategy ==="
|
||||
input string DB_Symbol = "XAUUSD";
|
||||
input int DB_BoxPeriod = 165;
|
||||
input double DB_BoxDeviation = 30000; // Increased to allow larger ranges (was 25140)
|
||||
input int DB_VolumeThreshold = 0; // Set to 0 to disable volume threshold check. Volume data from indicator used instead.
|
||||
input double DB_StopLoss = 1665;
|
||||
input double DB_TakeProfit = 3685;
|
||||
input bool DB_EnableLogging = false;
|
||||
input color DB_BoxColor = clrBlue;
|
||||
input int DB_BoxWidth = 1;
|
||||
input ENUM_TIMEFRAMES DB_TrendTimeframe = PERIOD_H2;
|
||||
input int DB_MA_Period = 125;
|
||||
input ENUM_MA_METHOD DB_MA_Method = MODE_EMA;
|
||||
input ENUM_APPLIED_PRICE DB_MA_Price = PRICE_WEIGHTED;
|
||||
input double DB_TrendThreshold = 4.94;
|
||||
input int DB_VolumeMA_Period = 110;
|
||||
input double DB_VolumeThresholdMultiplier = 1.5;
|
||||
input int DB_MagicNumber = 135790;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 2: EMASlopeDistanceCocktailXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== EMA Slope Distance Strategy ==="
|
||||
input string ES_Symbol = "XAUUSD";
|
||||
input int ES_EMA_Periode = 46;
|
||||
input double ES_PreisSchwelle = 600.0;
|
||||
input double ES_SteigungSchwelle = 80.0;
|
||||
input int ES_ÜberwachungTimeout = 800;
|
||||
input double ES_TrailingStop = 370.0;
|
||||
input bool ES_UseTrailingStop = true;
|
||||
input double ES_TrailingActivationPips = 0.0;
|
||||
input bool ES_UseStaleStopLossExit = false;
|
||||
input int ES_StaleStopLossSeconds = 33800;
|
||||
input double ES_LotGröße = 0.03;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
input bool ES_UseBarData = true;
|
||||
input int ES_MaxTradesPerCrossover = 9;
|
||||
input int ES_ProfitCheckBars = 18;
|
||||
input bool ES_CloseUnprofitableTrades = true;
|
||||
input bool ES_UseWeeklyADXFilter = true;
|
||||
input int ES_WeeklyADXPeriod = 15;
|
||||
input double ES_WeeklyADXMin = 40.0;
|
||||
input int ES_WeeklyADXBarShift = 2;
|
||||
input bool ES_WeeklyADXUseDirection = true;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 3: RSICrossOverReversalXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI CrossOver Reversal Strategy ==="
|
||||
input string RC_Symbol = "XAUUSD";
|
||||
input int RC_MagicNumber = 7;
|
||||
input int RC_rsiPeriod = 19;
|
||||
input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.01;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame2 = PERIOD_M1;
|
||||
input ENUM_TIMEFRAMES RC_BarTimeFrame = PERIOD_M12;
|
||||
input int RC_emaPeriod = 140;
|
||||
input double RC_emaSlopeThreshold = 105;
|
||||
input double RC_exitBuyRSI = 86;
|
||||
input double RC_exitSellRSI = 10;
|
||||
input double RC_TrailingStop = 295;
|
||||
input double RC_emaDistanceThreshold = 165;
|
||||
input int RC_tradingHourOneBegin = 24;
|
||||
input int RC_tradingHourOneEnd = 22;
|
||||
input int RC_tradingHourTwoBegin = 6;
|
||||
input int RC_tradingHourTwoEnd = 19;
|
||||
input bool RC_Sunday = false;
|
||||
input bool RC_Monday = false;
|
||||
input bool RC_Tuesday = true;
|
||||
input bool RC_Wednesday = true;
|
||||
input bool RC_Thursday = true;
|
||||
input bool RC_Friday = false;
|
||||
input bool RC_Saturday = false;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 4: RSIMidPointHijackXAUUSD |
|
||||
//| PEPPERSTONE US: Gold symbol is typically "XAUUSD" or "GOLD" |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.02;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
input bool RM_InpEnableRSIFollow = true;
|
||||
input bool RM_InpEnableRSIReverse = true;
|
||||
input bool RM_InpEnableEMACross = true;
|
||||
input bool RM_InpEnableStrategyLock = false;
|
||||
input double RM_InpLockProfitThreshold = 0.0;
|
||||
input bool RM_InpCloseOppositeTrades = false;
|
||||
input int RM_InpRSIPeriod = 32;
|
||||
input int RM_InpRSIOverbought = 78;
|
||||
input int RM_InpRSIOversold = 46;
|
||||
input int RM_InpRSIExitLevel = 44;
|
||||
input int RM_InpRSIFollowStartHour = 23;
|
||||
input int RM_InpRSIFollowEndHour = 8;
|
||||
input bool RM_InpRSIFollowCloseOutsideHours = false;
|
||||
input int RM_InpRSIReversePeriod = 59;
|
||||
input int RM_InpRSIReverseOverbought = 51;
|
||||
input int RM_InpRSIReverseOversold = 49;
|
||||
input int RM_InpRSIReverseCrossLevel = 53;
|
||||
input int RM_InpRSIReverseExitLevel = 48;
|
||||
input int RM_InpRSIReverseStartHour = 7;
|
||||
input int RM_InpRSIReverseEndHour = 13;
|
||||
input bool RM_InpRSIReverseCloseOutsideHours = false;
|
||||
input int RM_InpRSIReverseCooldownBars = 15;
|
||||
input bool RM_InpRSIReverseCooldownOnLoss = true;
|
||||
input int RM_InpEMAPeriod = 120;
|
||||
input int RM_InpEMACrossStartHour = 8;
|
||||
input int RM_InpEMACrossEndHour = 14;
|
||||
input bool RM_InpEMACrossCloseOutsideHours = true;
|
||||
input bool RM_InpUseEMADistanceEntry = true;
|
||||
input double RM_InpEMADistancePips = 160.0;
|
||||
input int RM_InpEMADistancePeriod = 26;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 5-10: RSI Scalping Strategies |
|
||||
//| Each RSI Scalping strategy trades on its own symbol: |
|
||||
//| - APPL: Apple stock (AAPL) |
|
||||
//| - BTCUSD: Bitcoin/USD |
|
||||
//| - MSFT: Microsoft stock |
|
||||
//| - NVDA: NVIDIA stock |
|
||||
//| - TSLA: Tesla stock |
|
||||
//| - XAUUSD: Gold/USD |
|
||||
//| |
|
||||
//| PEPPERSTONE US SYMBOL FORMATS: |
|
||||
//| - Stocks may use: "AAPL.US", "NASDAQ:AAPL", or just "AAPL" |
|
||||
//| - To find correct symbols: |
|
||||
//| 1. Open Market Watch (Ctrl+M) |
|
||||
//| 2. Right-click > Show All |
|
||||
//| 3. Search for the stock name |
|
||||
//| 4. Use the exact symbol name shown |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
||||
input string RS_APPL_Symbol = "AAPL.US"; // Try: "AAPL.US", "NASDAQ:AAPL", or "AAPL"
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_APPL_RSI_Overbought = 80;
|
||||
input double RS_APPL_RSI_Oversold = 78;
|
||||
input double RS_APPL_RSI_Target_Buy = 94;
|
||||
input double RS_APPL_RSI_Target_Sell = 44;
|
||||
input int RS_APPL_BarsToWait = 7;
|
||||
input double RS_APPL_LotSize = 25;
|
||||
input int RS_APPL_MagicNumber = 20001;
|
||||
input int RS_APPL_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD ==="
|
||||
input string RS_BTCUSD_Symbol = "BTCUSD"; // Pepperstone may use: "BTCUSD", "BTC/USD", or "BTCUSD.c"
|
||||
input ENUM_TIMEFRAMES RS_BTCUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_BTCUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_BTCUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_BTCUSD_RSI_Overbought = 90;
|
||||
input double RS_BTCUSD_RSI_Oversold = 73;
|
||||
input double RS_BTCUSD_RSI_Target_Buy = 88;
|
||||
input double RS_BTCUSD_RSI_Target_Sell = 48;
|
||||
input int RS_BTCUSD_BarsToWait = 6;
|
||||
input double RS_BTCUSD_LotSize = 0.1;
|
||||
input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping MSFT - Pepperstone US ==="
|
||||
input string RS_MSFT_Symbol = "MSFT.US"; // Try: "MSFT.US", "NASDAQ:MSFT", or "MSFT"
|
||||
input ENUM_TIMEFRAMES RS_MSFT_TimeFrame = PERIOD_H3;
|
||||
input int RS_MSFT_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_MSFT_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_MSFT_RSI_Overbought = 19;
|
||||
input double RS_MSFT_RSI_Oversold = 50;
|
||||
input double RS_MSFT_RSI_Target_Buy = 71;
|
||||
input double RS_MSFT_RSI_Target_Sell = 70;
|
||||
input int RS_MSFT_BarsToWait = 1;
|
||||
input double RS_MSFT_LotSize = 50;
|
||||
input int RS_MSFT_MagicNumber = 20002;
|
||||
input int RS_MSFT_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.US"; // Try: "NVDA.US", "NASDAQ:NVDA", or "NVDA"
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_NVDA_RSI_Overbought = 36;
|
||||
input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 50;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.US"; // Try: "TSLA.US", "NASDAQ:TSLA", or "TSLA"
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_TSLA_RSI_Overbought = 54;
|
||||
input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 50;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD ==="
|
||||
input string RS_XAUUSD_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RS_XAUUSD_TimeFrame = PERIOD_H1;
|
||||
input int RS_XAUUSD_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_XAUUSD_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_XAUUSD_RSI_Overbought = 71;
|
||||
input double RS_XAUUSD_RSI_Oversold = 57;
|
||||
input double RS_XAUUSD_RSI_Target_Buy = 80;
|
||||
input double RS_XAUUSD_RSI_Target_Sell = 57;
|
||||
input int RS_XAUUSD_BarsToWait = 4;
|
||||
input double RS_XAUUSD_LotSize = 0.1;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping Reversal escape (XAUUSD) ==="
|
||||
input bool RS_UseReversalEscape = false;
|
||||
input int RS_ReversalATRPeriod = 14;
|
||||
input double RS_ReversalAdverseAtrMult = 5.25;
|
||||
input int RS_ReversalSignsRequired = 2;
|
||||
input double RS_ReversalRsiVelocity = 16.0;
|
||||
input double RS_ReversalBodyAtrMult = 5.1;
|
||||
|
||||
input group "=== RSI Scalping APPL — Trailing ==="
|
||||
input bool RS_APPL_UseTrailingStop = true;
|
||||
input double RS_APPL_TrailDistancePoints = 120.0;
|
||||
input double RS_APPL_TrailActivationPoints = 0.0;
|
||||
|
||||
input group "=== RSI Scalping BTCUSD — Trailing ==="
|
||||
input bool RS_BTCUSD_UseTrailingStop = true;
|
||||
input double RS_BTCUSD_TrailDistancePoints = 120.0;
|
||||
input double RS_BTCUSD_TrailActivationPoints = 0.0;
|
||||
|
||||
input group "=== RSI Scalping MSFT — Trailing ==="
|
||||
input bool RS_MSFT_UseTrailingStop = true;
|
||||
input double RS_MSFT_TrailDistancePoints = 375.0;
|
||||
input double RS_MSFT_TrailActivationPoints = 75.0;
|
||||
|
||||
input group "=== RSI Scalping NVDA — Trailing ==="
|
||||
input bool RS_NVDA_UseTrailingStop = true;
|
||||
input double RS_NVDA_TrailDistancePoints = 375.0;
|
||||
input double RS_NVDA_TrailActivationPoints = 75.0;
|
||||
|
||||
input group "=== RSI Scalping TSLA — Trailing ==="
|
||||
input bool RS_TSLA_UseTrailingStop = true;
|
||||
input double RS_TSLA_TrailDistancePoints = 900.0;
|
||||
input double RS_TSLA_TrailActivationPoints = 950.0;
|
||||
|
||||
input group "=== RSI Scalping XAUUSD — Trailing ==="
|
||||
input bool RS_XAUUSD_UseTrailingStop = true;
|
||||
input double RS_XAUUSD_TrailDistancePoints = 71.0;
|
||||
input double RS_XAUUSD_TrailActivationPoints = 41.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - DarvasBox |
|
||||
//+------------------------------------------------------------------+
|
||||
struct DarvasBoxData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
double boxHigh;
|
||||
double boxLow;
|
||||
bool boxFormed;
|
||||
datetime lastBoxTime;
|
||||
string boxName;
|
||||
double minStopLevel;
|
||||
double point;
|
||||
CTrade trade;
|
||||
int maHandle;
|
||||
int volumeHandle;
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - EMA Slope Distance |
|
||||
//+------------------------------------------------------------------+
|
||||
struct EMASlopeData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int ema_handle;
|
||||
double ema_array[];
|
||||
datetime letzte_überwachung_zeit;
|
||||
bool überwachung_aktiv;
|
||||
bool preis_trigger_aktiv;
|
||||
bool steigung_trigger_aktiv;
|
||||
int ticket;
|
||||
CTrade trade;
|
||||
int trades_in_current_crossover;
|
||||
bool crossover_detected;
|
||||
datetime trade_open_time;
|
||||
datetime last_bar_time;
|
||||
datetime es_last_sl_adjust_success_time;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI CrossOver Reversal |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSICrossOverData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int emaHandle;
|
||||
double previousRSIDef;
|
||||
CTrade trade;
|
||||
datetime lastTradeTime;
|
||||
datetime bartime;
|
||||
bool WeekDays[7];
|
||||
datetime lastBarTime;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI MidPoint Hijack |
|
||||
//+------------------------------------------------------------------+
|
||||
struct RSIMidPointData {
|
||||
string symbol;
|
||||
bool isInitialized;
|
||||
int rsiHandle;
|
||||
int rsiReverseHandle;
|
||||
int emaHandle;
|
||||
bool rsiOverbought;
|
||||
bool rsiOversold;
|
||||
bool rsiReverseOverbought;
|
||||
bool rsiReverseOversold;
|
||||
CTrade trade;
|
||||
CPositionInfo positionInfo;
|
||||
bool emaCrossBuySignal;
|
||||
bool emaCrossSellSignal;
|
||||
int emaCrossSignalBar;
|
||||
datetime lastBarTime;
|
||||
datetime rsiReverseLastCloseTime;
|
||||
bool rsiReverseInCooldown;
|
||||
double lastBarRSI;
|
||||
double lastBarRSIReverse;
|
||||
double lastBarEMA;
|
||||
double lastBarClose;
|
||||
double lastBarEMAPrev;
|
||||
double lastBarClosePrev;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables - RSI Scalping |
|
||||
//+------------------------------------------------------------------+
|
||||
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;
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Strategy Instances |
|
||||
//+------------------------------------------------------------------+
|
||||
DarvasBoxData dbData;
|
||||
EMASlopeData esData;
|
||||
RSICrossOverData rcData;
|
||||
RSIMidPointData rmData;
|
||||
RSIScalpingData rsAPPLData;
|
||||
RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsMSFTData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Global Variables for lot sizes (from inputs below) |
|
||||
//+------------------------------------------------------------------+
|
||||
double g_DB_LotSize = 0.01;
|
||||
double g_ES_LotSize;
|
||||
double g_RC_LotSize;
|
||||
double g_RM_LotSize;
|
||||
double g_RS_APPL_LotSize;
|
||||
double g_RS_BTCUSD_LotSize;
|
||||
double g_RS_MSFT_LotSize;
|
||||
double g_RS_NVDA_LotSize;
|
||||
double g_RS_TSLA_LotSize;
|
||||
double g_RS_XAUUSD_LotSize;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
int initResult = INIT_SUCCEEDED;
|
||||
|
||||
g_ES_LotSize = ES_LotGröße;
|
||||
g_RC_LotSize = RC_lotSize;
|
||||
g_RM_LotSize = RM_InpLotSize;
|
||||
g_RS_APPL_LotSize = RS_APPL_LotSize;
|
||||
g_RS_BTCUSD_LotSize = RS_BTCUSD_LotSize;
|
||||
g_RS_MSFT_LotSize = RS_MSFT_LotSize;
|
||||
g_RS_NVDA_LotSize = RS_NVDA_LotSize;
|
||||
g_RS_TSLA_LotSize = RS_TSLA_LotSize;
|
||||
g_RS_XAUUSD_LotSize = RS_XAUUSD_LotSize;
|
||||
|
||||
if(EnableDarvasBox)
|
||||
if(!InitDarvasBox(DB_Symbol))
|
||||
Print("Warning: DarvasBox strategy failed to initialize for symbol '", DB_Symbol, "'");
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
if(!InitEMASlopeDistance(ES_Symbol))
|
||||
Print("Warning: EMASlopeDistance strategy failed to initialize for symbol '", ES_Symbol, "'");
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
if(!InitRSICrossOverReversal(RC_Symbol))
|
||||
Print("Warning: RSICrossOverReversal strategy failed to initialize for symbol '", RC_Symbol, "'");
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
if(!InitRSIMidPointHijack(RM_Symbol))
|
||||
Print("Warning: RSIMidPointHijack strategy failed to initialize for symbol '", RM_Symbol, "'");
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
InitRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price, RS_APPL_MagicNumber, RS_APPL_Slippage);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
InitRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price, RS_BTCUSD_MagicNumber, RS_BTCUSD_Slippage);
|
||||
|
||||
if(EnableRSIScalpingMSFT)
|
||||
InitRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price, RS_MSFT_MagicNumber, RS_MSFT_Slippage);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
InitRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price, RS_NVDA_MagicNumber, RS_NVDA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
InitRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price, RS_TSLA_MagicNumber, RS_TSLA_Slippage);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
|
||||
Print("United EA (self-evaluate build) initialized. Active strategies: ",
|
||||
(EnableDarvasBox ? "DarvasBox " : ""),
|
||||
(EnableEMASlopeDistance ? "EMASlope " : ""),
|
||||
(EnableRSICrossOverReversal ? "RSICrossOver " : ""),
|
||||
(EnableRSIMidPointHijack ? "RSIMidPoint " : ""),
|
||||
(EnableRSIScalpingAPPL ? "RSIScalpingAPPL " : ""),
|
||||
(EnableRSIScalpingBTCUSD ? "RSIScalpingBTCUSD " : ""),
|
||||
(EnableRSIScalpingMSFT ? "RSIScalpingMSFT " : ""),
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""));
|
||||
|
||||
return initResult;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
DeinitDarvasBox();
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
DeinitEMASlopeDistance();
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
DeinitRSICrossOverReversal();
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
DeinitRSIMidPointHijack();
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
DeinitRSIScalping(rsAPPLData);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
DeinitRSIScalping(rsBTCUSDData);
|
||||
|
||||
if(EnableRSIScalpingMSFT)
|
||||
DeinitRSIScalping(rsMSFTData);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
DeinitRSIScalping(rsNVDAData);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
DeinitRSIScalping(rsTSLAData);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
|
||||
Print("United EA deinitialized. Reason: ", reason);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
if(EnableDarvasBox)
|
||||
ProcessDarvasBox(DB_Symbol);
|
||||
|
||||
if(EnableEMASlopeDistance)
|
||||
ProcessEMASlopeDistance(ES_Symbol);
|
||||
|
||||
if(EnableRSICrossOverReversal)
|
||||
ProcessRSICrossOverReversal(RC_Symbol);
|
||||
|
||||
if(EnableRSIMidPointHijack)
|
||||
ProcessRSIMidPointHijack(RM_Symbol);
|
||||
|
||||
if(EnableRSIScalpingAPPL)
|
||||
ProcessRSIScalping(rsAPPLData, RS_APPL_Symbol, RS_APPL_TimeFrame, RS_APPL_RSI_Period, RS_APPL_RSI_Applied_Price,
|
||||
RS_APPL_RSI_Overbought, RS_APPL_RSI_Oversold, RS_APPL_RSI_Target_Buy, RS_APPL_RSI_Target_Sell,
|
||||
RS_APPL_BarsToWait, g_RS_APPL_LotSize, RS_APPL_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_APPL_UseTrailingStop, RS_APPL_TrailDistancePoints, RS_APPL_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingBTCUSD)
|
||||
ProcessRSIScalping(rsBTCUSDData, RS_BTCUSD_Symbol, RS_BTCUSD_TimeFrame, RS_BTCUSD_RSI_Period, RS_BTCUSD_RSI_Applied_Price,
|
||||
RS_BTCUSD_RSI_Overbought, RS_BTCUSD_RSI_Oversold, RS_BTCUSD_RSI_Target_Buy, RS_BTCUSD_RSI_Target_Sell,
|
||||
RS_BTCUSD_BarsToWait, g_RS_BTCUSD_LotSize, RS_BTCUSD_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_BTCUSD_UseTrailingStop, RS_BTCUSD_TrailDistancePoints, RS_BTCUSD_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingMSFT)
|
||||
ProcessRSIScalping(rsMSFTData, RS_MSFT_Symbol, RS_MSFT_TimeFrame, RS_MSFT_RSI_Period, RS_MSFT_RSI_Applied_Price,
|
||||
RS_MSFT_RSI_Overbought, RS_MSFT_RSI_Oversold, RS_MSFT_RSI_Target_Buy, RS_MSFT_RSI_Target_Sell,
|
||||
RS_MSFT_BarsToWait, g_RS_MSFT_LotSize, RS_MSFT_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_MSFT_UseTrailingStop, RS_MSFT_TrailDistancePoints, RS_MSFT_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingNVDA)
|
||||
ProcessRSIScalping(rsNVDAData, RS_NVDA_Symbol, RS_NVDA_TimeFrame, RS_NVDA_RSI_Period, RS_NVDA_RSI_Applied_Price,
|
||||
RS_NVDA_RSI_Overbought, RS_NVDA_RSI_Oversold, RS_NVDA_RSI_Target_Buy, RS_NVDA_RSI_Target_Sell,
|
||||
RS_NVDA_BarsToWait, g_RS_NVDA_LotSize, RS_NVDA_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_NVDA_UseTrailingStop, RS_NVDA_TrailDistancePoints, RS_NVDA_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingTSLA)
|
||||
ProcessRSIScalping(rsTSLAData, RS_TSLA_Symbol, RS_TSLA_TimeFrame, RS_TSLA_RSI_Period, RS_TSLA_RSI_Applied_Price,
|
||||
RS_TSLA_RSI_Overbought, RS_TSLA_RSI_Oversold, RS_TSLA_RSI_Target_Buy, RS_TSLA_RSI_Target_Sell,
|
||||
RS_TSLA_BarsToWait, g_RS_TSLA_LotSize, RS_TSLA_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_TSLA_UseTrailingStop, RS_TSLA_TrailDistancePoints, RS_TSLA_TrailActivationPoints);
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
ProcessRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price,
|
||||
RS_XAUUSD_RSI_Overbought, RS_XAUUSD_RSI_Oversold, RS_XAUUSD_RSI_Target_Buy, RS_XAUUSD_RSI_Target_Sell,
|
||||
RS_XAUUSD_BarsToWait, g_RS_XAUUSD_LotSize, RS_XAUUSD_MagicNumber,
|
||||
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Include strategy implementations |
|
||||
//+------------------------------------------------------------------+
|
||||
#include "Strategies/DarvasBoxStrategy.mqh"
|
||||
#include "Strategies/EMASlopeDistanceStrategy.mqh"
|
||||
#include "Strategies/RSICrossOverReversalStrategy.mqh"
|
||||
#include "Strategies/RSIMidPointHijackStrategy.mqh"
|
||||
#include "Strategies/RSIScalpingStrategy.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -5,29 +5,24 @@
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.01"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
|
||||
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 54; // RSI Overbought Level
|
||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 87; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 33; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 1; // Bars to wait when RSI goes against position
|
||||
input double RSI_Overbought = 32; // RSI Overbought Level
|
||||
input double RSI_Oversold = 86; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 100; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 24; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 34; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 5; // Lot Size
|
||||
input int MagicNumber = 125421321; // Magic Number
|
||||
input int MagicNumber = 129102315; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
input group "=== Trailing stop ==="
|
||||
input bool UseTrailingStop = true; // move SL behind bid/ask while in profit
|
||||
input double TrailingStopDistancePoints = 900.0; // SL distance from bid/ask (points)
|
||||
input double TrailingActivationPoints = 950.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
@@ -77,32 +72,35 @@ void OnDeinit(const int reason)
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
{
|
||||
return;
|
||||
|
||||
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
const bool new_bar = (current_bar_time != last_bar_time);
|
||||
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
||||
|
||||
if(!in_pos && !new_bar)
|
||||
return;
|
||||
|
||||
if(!UpdateRSI())
|
||||
return;
|
||||
|
||||
if(in_pos && UseTrailingStop)
|
||||
ApplyTrailingStop();
|
||||
|
||||
if(!new_bar)
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Check if this is a new bar
|
||||
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
return; // Still the same bar, don't process
|
||||
}
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
ResyncPositionFromMarket();
|
||||
|
||||
// Update RSI values
|
||||
if(!UpdateRSI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for existing position
|
||||
CheckExistingPosition();
|
||||
|
||||
if(!position_open && !PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
|
||||
// Check for new entry signals - only if no position exists for THIS EA (magic number) on THIS symbol
|
||||
if(!position_open && !PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -122,85 +120,6 @@ bool UpdateRSI()
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trail SL behind favorable price (every tick when enabled) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(TrailingStopDistancePoints <= 0.0)
|
||||
return;
|
||||
if(!PositionSelectByMagic(_Symbol, (ulong)MagicNumber))
|
||||
return;
|
||||
|
||||
const double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return;
|
||||
|
||||
const int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
const double trail_dist = TrailingStopDistancePoints * point;
|
||||
const double activation_pts = (TrailingActivationPoints > 0.0)
|
||||
? TrailingActivationPoints
|
||||
: TrailingStopDistancePoints;
|
||||
const double activation = activation_pts * point;
|
||||
const long stops_level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double min_dist = (double)stops_level * point;
|
||||
|
||||
const ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
const double entry = PositionGetDouble(POSITION_PRICE_OPEN);
|
||||
const double cur_sl = PositionGetDouble(POSITION_SL);
|
||||
const double cur_tp = PositionGetDouble(POSITION_TP);
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
if(bid - entry <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(bid - trail_dist, digits);
|
||||
if(min_dist > 0.0 && bid - new_sl < min_dist)
|
||||
new_sl = NormalizeDouble(bid - min_dist, digits);
|
||||
|
||||
if(new_sl >= bid || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl <= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
if(entry - ask <= activation)
|
||||
return;
|
||||
|
||||
double new_sl = NormalizeDouble(ask + trail_dist, digits);
|
||||
if(min_dist > 0.0 && new_sl - ask < min_dist)
|
||||
new_sl = NormalizeDouble(ask + min_dist, digits);
|
||||
|
||||
if(new_sl <= ask || new_sl <= 0.0)
|
||||
return;
|
||||
if(cur_sl > 0.0 && new_sl >= cur_sl)
|
||||
return;
|
||||
|
||||
ModifyPositionByMagic(trade, _Symbol, (ulong)MagicNumber, new_sl, cur_tp);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Sync ticket/state if a position exists after restart |
|
||||
//+------------------------------------------------------------------+
|
||||
void ResyncPositionFromMarket()
|
||||
{
|
||||
if(position_open)
|
||||
return;
|
||||
ulong t = GetPositionTicketByMagic(_Symbol, (ulong)MagicNumber);
|
||||
if(t == 0 || !PositionSelectByTicket(t))
|
||||
return;
|
||||
position_ticket = (int)t;
|
||||
position_open = true;
|
||||
current_position_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -212,7 +131,7 @@ void CheckExistingPosition()
|
||||
}
|
||||
|
||||
// Check if position still exists with correct magic number AND symbol for THIS EA
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, (ulong)MagicNumber))
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
@@ -324,7 +243,7 @@ void CheckEntrySignals()
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
if(PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
@@ -337,7 +256,7 @@ void OpenBuyPosition()
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
@@ -357,7 +276,7 @@ void OpenBuyPosition()
|
||||
void OpenSellPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, (ulong)MagicNumber))
|
||||
if(PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
@@ -370,7 +289,7 @@ void OpenSellPosition()
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, (ulong)MagicNumber))
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
@@ -390,7 +309,7 @@ void OpenSellPosition()
|
||||
void ClosePosition()
|
||||
{
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
if(ClosePositionByMagic(trade, _Symbol, (ulong)MagicNumber))
|
||||
if(ClosePositionByMagic(trade, _Symbol, MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
|
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 8.6 KiB |
@@ -40,6 +40,7 @@ double g_Pos_RS_BTCUSD;
|
||||
double g_Pos_RS_NVDA;
|
||||
double g_Pos_RS_TSLA;
|
||||
double g_Pos_RS_XAUUSD;
|
||||
double g_Pos_RS_MU;
|
||||
double g_Pos_RRA_EURUSD;
|
||||
double g_Pos_RRA_AUDUSD;
|
||||
double g_Pos_SE;
|
||||
@@ -65,44 +66,47 @@ input bool EnableEMASlopeDistance = true;
|
||||
input bool EnableRSICrossOverReversal = true;
|
||||
input bool EnableRSIMidPointHijack = true;
|
||||
input bool EnableRSIScalpingAPPL = true;
|
||||
input bool EnableRSIScalpingBTCUSD = true;
|
||||
input bool EnableRSIScalpingBTCUSD = false;
|
||||
input bool EnableRSIScalpingNVDA = true;
|
||||
input bool EnableRSIScalpingTSLA = true;
|
||||
input bool EnableRSIScalpingXAUUSD = true;
|
||||
input bool EnableSuperEMA = true;
|
||||
input bool EnableRSIConsolidation = true;
|
||||
input bool EnableRSIScalpingXAUUSD = false;
|
||||
input bool EnableRSIScalpingMU = true;
|
||||
input bool EnableSuperEMA = false;
|
||||
input bool EnableRSIConsolidation = false;
|
||||
input bool EnableRSIReversalAsianEURUSD = true;
|
||||
input bool EnableRSIReversalAsianAUDUSD = true;
|
||||
input bool EnableSimpleTrendlineBTCUSD = true;
|
||||
input bool EnableSimpleTrendlineXAUUSD = true;
|
||||
input bool EnableSimpleTrendlineGER40 = true;
|
||||
input bool EnableRSISecretSauce = true;
|
||||
input bool EnableSimpleTrendlineBTCUSD = false;
|
||||
input bool EnableSimpleTrendlineXAUUSD = false;
|
||||
input bool EnableSimpleTrendlineGER40 = false;
|
||||
input bool EnableRSISecretSauce = false;
|
||||
input bool OPT_GuardOptimizationMode = true; // legacy compatibility with 123.set
|
||||
|
||||
input group "=== Centralized Lot Size (Granular Per Robot) ==="
|
||||
input double LOT_DB_DarvasBox = 0.05;
|
||||
input double LOT_ES_EMASlopeDistance = 0.05;
|
||||
input double LOT_RC_RSICrossOver = 0.06;
|
||||
input double LOT_RM_RSIMidPointHijack = 0.03;
|
||||
input double LOT_RS_APPL = 5.0;
|
||||
input double LOT_DB_DarvasBox = 0.01;
|
||||
input double LOT_ES_EMASlopeDistance = 0.02;
|
||||
input double LOT_RC_RSICrossOver = 0.01;
|
||||
input double LOT_RM_RSIMidPointHijack = 0.01;
|
||||
input double LOT_RS_APPL = 25.0;
|
||||
input double LOT_RS_BTCUSD = 0.1;
|
||||
input double LOT_RS_NVDA = 10.0;
|
||||
input double LOT_RS_TSLA = 15.0;
|
||||
input double LOT_RS_XAUUSD = 0.1;
|
||||
input double LOT_RRA_EURUSD = 0.05;
|
||||
input double LOT_RRA_AUDUSD = 0.08;
|
||||
input double LOT_SE_SuperEMA = 0.02;
|
||||
input double LOT_RCO_RSIConsolidation = 0.02;
|
||||
input double LOT_ST_BTCUSD = 0.07;
|
||||
input double LOT_ST_XAUUSD = 0.01;
|
||||
input double LOT_RS_NVDA = 25.0;
|
||||
input double LOT_RS_TSLA = 5.0;
|
||||
input double LOT_RS_XAUUSD = 0.27;
|
||||
input double LOT_RS_MU = 5.0;
|
||||
input double LOT_RRA_EURUSD = 0.1;
|
||||
input double LOT_RRA_AUDUSD = 0.1;
|
||||
input double LOT_SE_SuperEMA = 0.01;
|
||||
input double LOT_RCO_RSIConsolidation = 0.1;
|
||||
input double LOT_ST_BTCUSD = 0.1;
|
||||
input double LOT_ST_XAUUSD = 0.1;
|
||||
input double LOT_ST_GER40 = 0.10;
|
||||
input double LOT_RSS_SecretSauce = 0.01;
|
||||
input double LOT_RSS_SecretSauce = 0.1;
|
||||
|
||||
input group "=== Balance-based position sizing ==="
|
||||
input bool ORCH_ScaleLotsByBalance = true;
|
||||
input bool ORCH_UseEquityInsteadOfBalance = false;
|
||||
input double ORCH_ReferenceBalance = 10000.0;
|
||||
input double ORCH_ReferenceBalance = 1000.0;
|
||||
input double ORCH_MinBalanceScale = 0.1;
|
||||
input double ORCH_MaxBalanceScale = 10.0;
|
||||
input double ORCH_MaxBalanceScale = 100000.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Strategy 1: DarvasBoxXAUUSD |
|
||||
@@ -143,7 +147,7 @@ input bool ES_UseTrailingStop = true;
|
||||
input double ES_TrailingActivationPips = 0.0;
|
||||
input bool ES_UseStaleStopLossExit = false;
|
||||
input int ES_StaleStopLossSeconds = 33800;
|
||||
input double ES_LotGröße = 0.03;
|
||||
input double ES_LotGröße = 0.07;
|
||||
input int ES_MagicNumber = 12350;
|
||||
input bool ES_UseSpreadAdjustment = true;
|
||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||
@@ -169,7 +173,7 @@ input int RC_overboughtLevel = 93;
|
||||
input int RC_oversoldLevel = 22;
|
||||
input double RC_entryRSIBuySpread = 0;
|
||||
input double RC_entryRSISellSpread = 0;
|
||||
input double RC_lotSize = 0.01;
|
||||
input double RC_lotSize = 0.1;
|
||||
input int RC_slippage = 3;
|
||||
input int RC_cooldownSeconds = 209;
|
||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||
@@ -201,7 +205,7 @@ input bool RC_Saturday = false;
|
||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||
input string RM_Symbol = "XAUUSD";
|
||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
||||
input double RM_InpLotSize = 0.02;
|
||||
input double RM_InpLotSize = 0.1;
|
||||
input int RM_InpMagicNumberRSIFollow = 1001;
|
||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||
input int RM_InpMagicNumberEMACross = 1003;
|
||||
@@ -254,7 +258,7 @@ input int RM_InpEMADistancePeriod = 26;
|
||||
//| 4. Use the exact symbol name shown |
|
||||
//+------------------------------------------------------------------+
|
||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
||||
input string RS_APPL_Symbol = "AAPL.NAS"; // Pepperstone / match tester set (also try AAPL.US)
|
||||
input string RS_APPL_Symbol = "AAPL";
|
||||
input ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||
input int RS_APPL_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
||||
@@ -282,7 +286,7 @@ input int RS_BTCUSD_MagicNumber = 123459123;
|
||||
input int RS_BTCUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
||||
input string RS_NVDA_Symbol = "NVDA.NAS"; // Pepperstone / match tester set (also try NVDA.US)
|
||||
input string RS_NVDA_Symbol = "NVDA";
|
||||
input ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||
input int RS_NVDA_RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
@@ -291,12 +295,12 @@ input double RS_NVDA_RSI_Oversold = 38;
|
||||
input double RS_NVDA_RSI_Target_Buy = 90;
|
||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||
input int RS_NVDA_BarsToWait = 5;
|
||||
input double RS_NVDA_LotSize = 50;
|
||||
input double RS_NVDA_LotSize = 5;
|
||||
input int RS_NVDA_MagicNumber = 20003;
|
||||
input int RS_NVDA_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
||||
input string RS_TSLA_Symbol = "TSLA.NAS"; // Pepperstone / match tester set (also try TSLA.US)
|
||||
input string RS_TSLA_Symbol = "TSLA";
|
||||
input ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||
input int RS_TSLA_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
||||
@@ -305,7 +309,7 @@ input double RS_TSLA_RSI_Oversold = 73;
|
||||
input double RS_TSLA_RSI_Target_Buy = 87;
|
||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||
input int RS_TSLA_BarsToWait = 1;
|
||||
input double RS_TSLA_LotSize = 50;
|
||||
input double RS_TSLA_LotSize = 5;
|
||||
input int RS_TSLA_MagicNumber = 125421321;
|
||||
input int RS_TSLA_Slippage = 3;
|
||||
|
||||
@@ -323,6 +327,20 @@ input double RS_XAUUSD_LotSize = 0.1;
|
||||
input int RS_XAUUSD_MagicNumber = 129102315;
|
||||
input int RS_XAUUSD_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping MU ==="
|
||||
input string RS_MU_Symbol = "MU";
|
||||
input ENUM_TIMEFRAMES RS_MU_TimeFrame = PERIOD_M20;
|
||||
input int RS_MU_RSI_Period = 14;
|
||||
input ENUM_APPLIED_PRICE RS_MU_RSI_Applied_Price = PRICE_CLOSE;
|
||||
input double RS_MU_RSI_Overbought = 32;
|
||||
input double RS_MU_RSI_Oversold = 86;
|
||||
input double RS_MU_RSI_Target_Buy = 100;
|
||||
input double RS_MU_RSI_Target_Sell = 24;
|
||||
input int RS_MU_BarsToWait = 34;
|
||||
input double RS_MU_LotSize = 5.0;
|
||||
input int RS_MU_MagicNumber = 129102316;
|
||||
input int RS_MU_Slippage = 3;
|
||||
|
||||
input group "=== RSI Scalping Reversal Escape (XAUUSD only) ==="
|
||||
input bool RS_UseReversalEscape = true;
|
||||
input int RS_ReversalATRPeriod = 14;
|
||||
@@ -480,7 +498,7 @@ input ENUM_APPLIED_PRICE ST_XAU_AppliedPrice = PRICE_OPEN;
|
||||
input int ST_XAU_HTFBarsToScan = 500;
|
||||
input double ST_XAU_LineTouchTolerance = 220.0;
|
||||
input double ST_XAU_BreakBuffer = 110.0;
|
||||
input ulong ST_XAU_MagicNumber = 26042503;
|
||||
input ulong ST_XAU_MagicNumber = 26042501;
|
||||
input bool ST_XAU_DrawTrendline = true;
|
||||
|
||||
input group "=== SimpleTrendline GER40 ==="
|
||||
@@ -549,6 +567,7 @@ void United_RefreshScaledLots()
|
||||
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
|
||||
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
|
||||
g_Pos_RS_XAUUSD = United_ScaledLot(LOT_RS_XAUUSD);
|
||||
g_Pos_RS_MU = United_ScaledLot(LOT_RS_MU);
|
||||
g_Pos_RRA_EURUSD = United_ScaledLot(LOT_RRA_EURUSD);
|
||||
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
|
||||
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
|
||||
@@ -656,6 +675,7 @@ RSIScalpingData rsBTCUSDData;
|
||||
RSIScalpingData rsNVDAData;
|
||||
RSIScalpingData rsTSLAData;
|
||||
RSIScalpingData rsXAUUSDData;
|
||||
RSIScalpingData rsMUData;
|
||||
SuperEMAData seData;
|
||||
RSIConsolidationData rcoData;
|
||||
SimpleTrendlineData stBTCData;
|
||||
@@ -710,6 +730,8 @@ int OnInit()
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
InitRSIScalping(rsXAUUSDData, RS_XAUUSD_Symbol, RS_XAUUSD_TimeFrame, RS_XAUUSD_RSI_Period, RS_XAUUSD_RSI_Applied_Price, RS_XAUUSD_MagicNumber, RS_XAUUSD_Slippage);
|
||||
if(EnableRSIScalpingMU)
|
||||
InitRSIScalping(rsMUData, RS_MU_Symbol, RS_MU_TimeFrame, RS_MU_RSI_Period, RS_MU_RSI_Applied_Price, RS_MU_MagicNumber, RS_MU_Slippage);
|
||||
|
||||
if(EnableRSISecretSauce)
|
||||
if(!InitRSISecretSauce(rssData, RSS_Symbol))
|
||||
@@ -779,6 +801,7 @@ int OnInit()
|
||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
|
||||
(EnableRSIScalpingMU ? "RSIScalpingMU " : ""),
|
||||
(EnableRSISecretSauce ? "RSISecretSauce " : ""),
|
||||
(EnableSuperEMA ? "SuperEMA " : ""),
|
||||
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
|
||||
@@ -822,6 +845,8 @@ void OnDeinit(const int reason)
|
||||
|
||||
if(EnableRSIScalpingXAUUSD)
|
||||
DeinitRSIScalping(rsXAUUSDData);
|
||||
if(EnableRSIScalpingMU)
|
||||
DeinitRSIScalping(rsMUData);
|
||||
|
||||
if(EnableRSISecretSauce)
|
||||
DeinitRSISecretSauce(rssData);
|
||||
@@ -906,6 +931,13 @@ void OnTick()
|
||||
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
|
||||
if(EnableRSIScalpingMU)
|
||||
ProcessRSIScalping(rsMUData, RS_MU_Symbol, RS_MU_TimeFrame, RS_MU_RSI_Period, RS_MU_RSI_Applied_Price,
|
||||
RS_MU_RSI_Overbought, RS_MU_RSI_Oversold, RS_MU_RSI_Target_Buy, RS_MU_RSI_Target_Sell,
|
||||
RS_MU_BarsToWait, g_Pos_RS_MU, RS_MU_MagicNumber,
|
||||
false, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||
false, 0.0, 0.0);
|
||||
|
||||
if(EnableRSISecretSauce)
|
||||
ProcessRSISecretSauce(rssData, g_RSS_LotSize);
|
||||
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@@ -22,6 +22,16 @@ TimeFrame=16387||0||16385||16390||Y
|
||||
; === PHASE 3: POSITION SIZING (Optimize with caution) ===
|
||||
LotSize=50.0||5.0||10.0||100.0||Y
|
||||
|
||||
; === Trailing stop (main.mq5 1.01+) ===
|
||||
UseTrailingStop=true||false||0||true||N
|
||||
TrailingStopDistancePoints=375.0||25.0||100.0||800.0||Y
|
||||
TrailingActivationPoints=75.0||5.0||0.0||300.0||Y
|
||||
|
||||
; === Session filter new entries UTC (main.mq5 1.02+) ===
|
||||
UseSessionFilterUTC=false||false||0||true||Y
|
||||
TradeStartHourUTC=9||1||6||14||Y
|
||||
TradeEndHourUTC=22||1||18||24||N
|
||||
|
||||
; === FIXED PARAMETERS (Do Not Optimize) ===
|
||||
RSI_Applied_Price=1||0||1||1||N
|
||||
MagicNumber=12345||0||12345||12345||N
|
||||
@@ -18,6 +18,16 @@ TimeFrame=16387||0||16385||16390||Y
|
||||
; === PHASE 3: POSITION SIZING ===
|
||||
LotSize=50.0||5.0||10.0||100.0||Y
|
||||
|
||||
; === Trailing stop (main.mq5 1.01+) ===
|
||||
UseTrailingStop=true||false||0||true||N
|
||||
TrailingStopDistancePoints=375.0||25.0||100.0||800.0||Y
|
||||
TrailingActivationPoints=75.0||5.0||0.0||300.0||Y
|
||||
|
||||
; === Session filter new entries UTC (main.mq5 1.02+) ===
|
||||
UseSessionFilterUTC=false||false||0||true||Y
|
||||
TradeStartHourUTC=9||1||6||14||Y
|
||||
TradeEndHourUTC=22||1||18||24||N
|
||||
|
||||
; === FIXED PARAMETERS ===
|
||||
RSI_Applied_Price=1||0||1||1||N
|
||||
MagicNumber=12345||0||12345||12345||N
|
||||
@@ -5,7 +5,7 @@
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.01"
|
||||
#property version "1.02"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
@@ -28,6 +28,12 @@ input bool UseTrailingStop = true; // move SL behind bid/ask whi
|
||||
input double TrailingStopDistancePoints = 375.0; // SL distance from bid/ask (points)
|
||||
input double TrailingActivationPoints = 75.0; // min profit before trailing (0 = same as distance)
|
||||
|
||||
input group "=== Session filter new entries (UTC) ==="
|
||||
// Stocks/CFDs often shift behaviour around major cash opens; some NVDA 1h charts show a sharp volume + direction change near 09:00 UTC. Filter affects OPEN only (exits/trailing unchanged).
|
||||
input bool UseSessionFilterUTC = false; // if true, block new entries outside [TradeStartHourUTC, TradeEndHourUTC)
|
||||
input int TradeStartHourUTC = 9; // allow new trades when TimeGMT hour >= this (0..23)
|
||||
input int TradeEndHourUTC = 22; // allow new trades when TimeGMT hour < this (exclusive). If Start>End, window wraps midnight (e.g. 22..6)
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
@@ -105,6 +111,34 @@ void OnTick()
|
||||
CheckEntrySignals();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| New entries allowed in [TradeStartHourUTC, TradeEndHourUTC) GMT |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsWithinNewEntryWindowUTC()
|
||||
{
|
||||
if(!UseSessionFilterUTC)
|
||||
return true;
|
||||
|
||||
int s = TradeStartHourUTC;
|
||||
int e = TradeEndHourUTC;
|
||||
if(s < 0) s = 0;
|
||||
if(s > 23) s = 23;
|
||||
if(e < 0) e = 0;
|
||||
if(e > 24) e = 24;
|
||||
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeGMT(), dt);
|
||||
const int h = dt.hour;
|
||||
|
||||
if(s == e)
|
||||
return true;
|
||||
|
||||
if(s < e)
|
||||
return (h >= s && h < e);
|
||||
|
||||
return (h >= s || h < e);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI values |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -305,6 +339,9 @@ void CheckExistingPosition()
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
if(!IsWithinNewEntryWindowUTC())
|
||||
return;
|
||||
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
@@ -0,0 +1,35 @@
|
||||
; saved on 2026.05.13
|
||||
; genetic optimization set for EMASlopeDistanceCocktailXAUUSD/main.mq5
|
||||
; load in MT5 Strategy Tester -> Inputs -> Load
|
||||
; format: Parameter=Current||Start||Step||Stop||Optimize(Y/N)
|
||||
;
|
||||
; ENUM_TIMEFRAMES: M1=1 M5=5 M15=15 M30=30 H1=16385 H4=16388 D1=16408
|
||||
; Timeframe fixed at H1 (16385); sweeping 0..49153 can pick invalid enum values.
|
||||
;
|
||||
; Ranges/steps match Desktop 123.set (2026.05.13); Y marks parameters included in genetic optimization.
|
||||
|
||||
; === EMA / trigger thresholds ===
|
||||
EMA_Periode=50||50||1||500||Y
|
||||
PreisSchwelle=700.0||70.0||70.0||7000.0||Y
|
||||
SteigungSchwelle=25.0||2.5||2.5||250.0||Y
|
||||
ÜberwachungTimeout=340||1||1||3400||Y
|
||||
TrailingStop=370.0||37.0||37.0||3700.0||Y
|
||||
LotGröße=0.07||0.007||0.007||0.7||Y
|
||||
|
||||
; === execution / data ===
|
||||
MagicNumber=135790||135790||1||1357900||N
|
||||
UseSpreadAdjustment=true||false||0||true||N
|
||||
Timeframe=16385||16385||0||16385||N
|
||||
UseBarData=true||false||0||true||N
|
||||
|
||||
; === crossover / profit management ===
|
||||
MaxTradesPerCrossover=10||1||1||100||Y
|
||||
ProfitCheckBars=15||1||1||150||Y
|
||||
CloseUnprofitableTrades=true||false||0||true||N
|
||||
|
||||
; === weekly ADX filter ===
|
||||
UseWeeklyADXFilter=true||false||0||true||N
|
||||
WeeklyADXPeriod=15||1||1||150||Y
|
||||
WeeklyADXMin=40.0||4.0||4.0||400.0||Y
|
||||
WeeklyADXBarShift=2||1||1||20||Y
|
||||
WeeklyADXUseDirection=true||false||0||true||N
|
||||
@@ -8,24 +8,24 @@
|
||||
#property version "1.00"
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
//--- Eingabeparameter (Input Parameters) - Optimized Profitable Parameters
|
||||
input int EMA_Periode = 50; // EMA Periode
|
||||
input double PreisSchwelle = 700.0; // Preisbewegung Schwelle in Pips
|
||||
input double SteigungSchwelle = 25.0; // EMA Steigung Schwelle in Pips
|
||||
//--- Eingabeparameter (Input Parameters) — synced with Desktop 123.set (2026.05.13)
|
||||
input int EMA_Periode = 85; // EMA Periode
|
||||
input double PreisSchwelle = 350.0; // Preisbewegung Schwelle in Pips
|
||||
input double SteigungSchwelle = 22.5; // EMA Steigung Schwelle in Pips
|
||||
input int ÜberwachungTimeout = 340; // Überwachungszeit in Sekunden
|
||||
input double TrailingStop = 370.0; // Gleitender Stop in Pips
|
||||
input double TrailingStop = 74.0; // Gleitender Stop in Pips
|
||||
input double LotGröße = 0.07; // Handelsvolumen
|
||||
input int MagicNumber = 135790; // Magic Number für Trades
|
||||
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
|
||||
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
|
||||
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
|
||||
input int MaxTradesPerCrossover = 10; // Maximale Trades pro Crossover-Ereignis
|
||||
input int ProfitCheckBars = 15; // Bars bis zur Profit-Prüfung
|
||||
input int MaxTradesPerCrossover = 48; // Maximale Trades pro Crossover-Ereignis
|
||||
input int ProfitCheckBars = 78; // Bars bis zur Profit-Prüfung
|
||||
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
|
||||
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
|
||||
input int WeeklyADXPeriod = 15; // ADX-Periode auf W1
|
||||
input double WeeklyADXMin = 40.0; // Minimaler ADX fuer Trendfreigabe
|
||||
input int WeeklyADXBarShift = 2; // 1=letzte geschlossene W1-Kerze
|
||||
input int WeeklyADXPeriod = 28; // ADX-Periode auf W1
|
||||
input double WeeklyADXMin = 25.0; // Minimaler ADX fuer Trendfreigabe
|
||||
input int WeeklyADXBarShift = 8; // 1=letzte geschlossene W1-Kerze
|
||||
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
|
||||
|
||||
//--- Globale Variablen (Global Variables)
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIConsolidation.mq5 |
|
||||
//| Mean-reversion RSI for ranging markets; trend filters block runs |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025"
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//--- Symbol (empty = chart symbol)
|
||||
input group "=== Symbol & session ==="
|
||||
input string InpSymbol = "";
|
||||
|
||||
input group "=== Timeframe & bar logic ==="
|
||||
input ENUM_TIMEFRAMES SignalTF = PERIOD_M15;
|
||||
input bool EntryOnNewBarOnly = true;
|
||||
|
||||
//--- Core: no trend / consolidation regime
|
||||
input group "=== Regime: consolidation (anti-trend) ==="
|
||||
input int ADX_Period = 23;
|
||||
input double ADX_Max = 29.0;
|
||||
input bool UseATRRatioFilter = true;
|
||||
input int ATR_Period = 8;
|
||||
input int ATR_SMA_Period = 35;
|
||||
input double ATR_Ratio_Max = 1.36;
|
||||
input bool UseFlatEMAFilter = true;
|
||||
input int EMA_Fast = 13;
|
||||
input int EMA_Slow = 17;
|
||||
input double EMA_Separation_MaxPct = 0.26;
|
||||
|
||||
//--- RSI entries (fade extremes toward mean)
|
||||
input group "=== RSI entries ==="
|
||||
input int RSI_Period = 8;
|
||||
input ENUM_APPLIED_PRICE RSI_Price = PRICE_OPEN;
|
||||
input double RSI_Oversold = 22.0;
|
||||
input double RSI_Overbought = 63.0;
|
||||
|
||||
//--- Exits: mean target + hard ATR bracket
|
||||
input group "=== Exits ==="
|
||||
input bool UseRSI_MeanExit = true;
|
||||
input double RSI_Exit_Long = 48.0;
|
||||
input double RSI_Exit_Short = 52.0;
|
||||
input double SL_ATR_Mult = 2.15;
|
||||
input double TP_ATR_Mult = 2.40;
|
||||
input int MaxBarsInTrade = 54;
|
||||
|
||||
input group "=== Risk & execution ==="
|
||||
input double Lots = 0.10;
|
||||
input ulong MagicNumber = 20250420;
|
||||
input int Slippage = 10;
|
||||
input int MaxSpreadPoints = 28;
|
||||
|
||||
CTrade trade;
|
||||
string g_sym;
|
||||
|
||||
int h_rsi = INVALID_HANDLE;
|
||||
int h_adx = INVALID_HANDLE;
|
||||
int h_atr = INVALID_HANDLE;
|
||||
int h_ema_fast = INVALID_HANDLE;
|
||||
int h_ema_slow = INVALID_HANDLE;
|
||||
|
||||
datetime g_last_bar = 0;
|
||||
|
||||
bool PositionExistsByMagicSym(string sym, ulong magic)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong t = PositionGetTicket(i);
|
||||
if(t == 0) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ulong GetPositionTicketByMagicSym(string sym, ulong magic)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong t = PositionGetTicket(i);
|
||||
if(t == 0) continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic)
|
||||
return t;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool SelectPositionTicketSymMagic(ulong ticket, string sym, ulong magic)
|
||||
{
|
||||
if(!PositionSelectByTicket(ticket)) return false;
|
||||
return PositionGetString(POSITION_SYMBOL) == sym && PositionGetInteger(POSITION_MAGIC) == (long)magic;
|
||||
}
|
||||
|
||||
double NormalizeVolume(string sym, double vol)
|
||||
{
|
||||
double minLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
|
||||
double maxLot = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
|
||||
double step = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
|
||||
if(step > 0.0)
|
||||
vol = MathFloor(vol / step) * step;
|
||||
if(vol < minLot) vol = minLot;
|
||||
if(vol > maxLot) vol = maxLot;
|
||||
return vol;
|
||||
}
|
||||
|
||||
int CurrentSpreadPoints(string sym)
|
||||
{
|
||||
long spread = 0;
|
||||
if(!SymbolInfoInteger(sym, SYMBOL_SPREAD, spread))
|
||||
return 999999;
|
||||
return (int)spread;
|
||||
}
|
||||
|
||||
double MinStopsDistancePrice(string sym)
|
||||
{
|
||||
long lvl = 0;
|
||||
if(!SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL, lvl))
|
||||
return 0;
|
||||
double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||||
if(pt <= 0)
|
||||
return 0;
|
||||
return (double)lvl * pt;
|
||||
}
|
||||
|
||||
bool Copy1(int handle, double &v)
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
if(CopyBuffer(handle, 0, 0, 1, b) < 1) return false;
|
||||
v = b[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RSI_Buffers(double &cur, double &prev, double &twoAgo)
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
if(CopyBuffer(h_rsi, 0, 0, 3, b) < 3) return false;
|
||||
cur = b[0];
|
||||
prev = b[1];
|
||||
twoAgo = b[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Regime_IsConsolidation()
|
||||
{
|
||||
double adx = 0;
|
||||
if(!Copy1(h_adx, adx))
|
||||
return false;
|
||||
if(adx >= ADX_Max)
|
||||
return false;
|
||||
|
||||
if(UseATRRatioFilter)
|
||||
{
|
||||
double atrArr[], atrSma[];
|
||||
ArraySetAsSeries(atrArr, true);
|
||||
if(CopyBuffer(h_atr, 0, 0, ATR_SMA_Period + 1, atrArr) < ATR_SMA_Period + 1)
|
||||
return false;
|
||||
double sum = 0;
|
||||
for(int i = 1; i <= ATR_SMA_Period; i++)
|
||||
sum += atrArr[i];
|
||||
double smaAtr = sum / (double)ATR_SMA_Period;
|
||||
if(smaAtr <= 0.0)
|
||||
return false;
|
||||
double ratio = atrArr[0] / smaAtr;
|
||||
if(ratio > ATR_Ratio_Max)
|
||||
return false;
|
||||
}
|
||||
|
||||
if(UseFlatEMAFilter)
|
||||
{
|
||||
double ef[], es[];
|
||||
ArraySetAsSeries(ef, true);
|
||||
ArraySetAsSeries(es, true);
|
||||
if(CopyBuffer(h_ema_fast, 0, 0, 1, ef) < 1) return false;
|
||||
if(CopyBuffer(h_ema_slow, 0, 0, 1, es) < 1) return false;
|
||||
double c = SymbolInfoDouble(g_sym, SYMBOL_BID);
|
||||
if(c <= 0) return false;
|
||||
double sep = MathAbs(ef[0] - es[0]) / c * 100.0;
|
||||
if(sep > EMA_Separation_MaxPct)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Entry_BuyCross(double twoAgo, double prev)
|
||||
{
|
||||
return (twoAgo <= RSI_Oversold && prev > RSI_Oversold);
|
||||
}
|
||||
|
||||
bool Entry_SellCross(double twoAgo, double prev)
|
||||
{
|
||||
return (twoAgo >= RSI_Overbought && prev < RSI_Overbought);
|
||||
}
|
||||
|
||||
void TryCloseByRSI(ENUM_POSITION_TYPE typ, double rsi)
|
||||
{
|
||||
ulong tk = GetPositionTicketByMagicSym(g_sym, MagicNumber);
|
||||
if(tk == 0 || !SelectPositionTicketSymMagic(tk, g_sym, MagicNumber))
|
||||
return;
|
||||
if(!UseRSI_MeanExit)
|
||||
return;
|
||||
if(typ == POSITION_TYPE_BUY && rsi >= RSI_Exit_Long)
|
||||
trade.PositionClose(tk);
|
||||
else if(typ == POSITION_TYPE_SELL && rsi <= RSI_Exit_Short)
|
||||
trade.PositionClose(tk);
|
||||
}
|
||||
|
||||
void ManageOpenPosition(double rsi)
|
||||
{
|
||||
ulong tk = GetPositionTicketByMagicSym(g_sym, MagicNumber);
|
||||
if(tk == 0 || !SelectPositionTicketSymMagic(tk, g_sym, MagicNumber))
|
||||
return;
|
||||
ENUM_POSITION_TYPE typ = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
datetime openT = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
int barsAgo = iBarShift(g_sym, SignalTF, openT, false);
|
||||
if(barsAgo >= 0 && barsAgo >= MaxBarsInTrade)
|
||||
{
|
||||
trade.PositionClose(tk);
|
||||
return;
|
||||
}
|
||||
TryCloseByRSI(typ, rsi);
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
g_sym = InpSymbol;
|
||||
StringTrimLeft(g_sym);
|
||||
StringTrimRight(g_sym);
|
||||
if(StringLen(g_sym) == 0)
|
||||
g_sym = _Symbol;
|
||||
|
||||
if(!SymbolSelect(g_sym, true))
|
||||
{
|
||||
Print("RSIConsolidation: SymbolSelect failed: ", g_sym);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_RETURN);
|
||||
|
||||
h_rsi = iRSI(g_sym, SignalTF, RSI_Period, RSI_Price);
|
||||
h_adx = iADX(g_sym, SignalTF, ADX_Period);
|
||||
h_atr = iATR(g_sym, SignalTF, ATR_Period);
|
||||
h_ema_fast = iMA(g_sym, SignalTF, EMA_Fast, 0, MODE_EMA, PRICE_CLOSE);
|
||||
h_ema_slow = iMA(g_sym, SignalTF, EMA_Slow, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(h_rsi == INVALID_HANDLE || h_adx == INVALID_HANDLE || h_atr == INVALID_HANDLE
|
||||
|| h_ema_fast == INVALID_HANDLE || h_ema_slow == INVALID_HANDLE)
|
||||
{
|
||||
Print("RSIConsolidation: indicator init failed");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
Print("RSIConsolidation: symbol=", g_sym, " TF=", EnumToString(SignalTF));
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(h_rsi != INVALID_HANDLE) IndicatorRelease(h_rsi);
|
||||
if(h_adx != INVALID_HANDLE) IndicatorRelease(h_adx);
|
||||
if(h_atr != INVALID_HANDLE) IndicatorRelease(h_atr);
|
||||
if(h_ema_fast != INVALID_HANDLE) IndicatorRelease(h_ema_fast);
|
||||
if(h_ema_slow != INVALID_HANDLE) IndicatorRelease(h_ema_slow);
|
||||
}
|
||||
|
||||
bool EnoughHistory()
|
||||
{
|
||||
int need = MathMax(RSI_Period + 3, MathMax(ADX_Period + 2, ATR_SMA_Period + 3));
|
||||
if(Bars(g_sym, SignalTF) < need)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(!EnoughHistory())
|
||||
return;
|
||||
|
||||
if(MaxSpreadPoints > 0 && CurrentSpreadPoints(g_sym) > MaxSpreadPoints)
|
||||
return;
|
||||
|
||||
double rsi, rsiPrev, rsi2;
|
||||
if(!RSI_Buffers(rsi, rsiPrev, rsi2))
|
||||
return;
|
||||
|
||||
datetime barTime = iTime(g_sym, SignalTF, 0);
|
||||
bool isNew = (barTime != g_last_bar);
|
||||
|
||||
if(PositionExistsByMagicSym(g_sym, MagicNumber))
|
||||
{
|
||||
ManageOpenPosition(rsi);
|
||||
if(isNew)
|
||||
g_last_bar = barTime;
|
||||
return;
|
||||
}
|
||||
|
||||
if(EntryOnNewBarOnly && !isNew)
|
||||
return;
|
||||
|
||||
g_last_bar = barTime;
|
||||
|
||||
if(!Regime_IsConsolidation())
|
||||
return;
|
||||
|
||||
double atrArr[];
|
||||
ArraySetAsSeries(atrArr, true);
|
||||
if(CopyBuffer(h_atr, 0, 0, 1, atrArr) < 1)
|
||||
return;
|
||||
double atr = atrArr[0];
|
||||
int dig = (int)SymbolInfoInteger(g_sym, SYMBOL_DIGITS);
|
||||
|
||||
double slDist = atr * SL_ATR_Mult;
|
||||
double tpDist = atr * TP_ATR_Mult;
|
||||
double minD = MinStopsDistancePrice(g_sym);
|
||||
if(slDist < minD)
|
||||
slDist = minD;
|
||||
if(tpDist < minD)
|
||||
tpDist = minD;
|
||||
|
||||
double vol = NormalizeVolume(g_sym, Lots);
|
||||
|
||||
if(Entry_BuyCross(rsi2, rsiPrev))
|
||||
{
|
||||
double ask = SymbolInfoDouble(g_sym, SYMBOL_ASK);
|
||||
double sl = ask - slDist;
|
||||
double tp = ask + tpDist;
|
||||
sl = NormalizeDouble(sl, dig);
|
||||
tp = NormalizeDouble(tp, dig);
|
||||
trade.Buy(vol, g_sym, ask, sl, tp, "RSIConsolidation BUY");
|
||||
}
|
||||
else if(Entry_SellCross(rsi2, rsiPrev))
|
||||
{
|
||||
double bid = SymbolInfoDouble(g_sym, SYMBOL_BID);
|
||||
double sl = bid + slDist;
|
||||
double tp = bid - tpDist;
|
||||
sl = NormalizeDouble(sl, dig);
|
||||
tp = NormalizeDouble(tp, dig);
|
||||
trade.Sell(vol, g_sym, bid, sl, tp, "RSIConsolidation SELL");
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -1,37 +0,0 @@
|
||||
; RSIConsolidation.mq5 — optimization preset (Strategy Tester → Inputs → Load)
|
||||
; Format: Name=Current||Start||Step||Stop||Y|N (Y = include in optimization)
|
||||
;
|
||||
; === Symbol & session ===
|
||||
InpSymbol=
|
||||
; === Timeframe & bar logic ===
|
||||
; SignalTF: optimize per run (ENUM is non-sequential); M15=15, H1=16385, H4=16388
|
||||
SignalTF=15||15||0||15||N
|
||||
EntryOnNewBarOnly=true||false||0||true||N
|
||||
; === Regime: consolidation (anti-trend) ===
|
||||
ADX_Period=14||7||1||28||Y
|
||||
ADX_Max=22.0||16.0||1.0||32.0||Y
|
||||
UseATRRatioFilter=true||false||0||true||N
|
||||
ATR_Period=14||7||1||21||Y
|
||||
ATR_SMA_Period=50||20||5||100||Y
|
||||
ATR_Ratio_Max=1.18||1.0||0.02||1.35||Y
|
||||
UseFlatEMAFilter=true||false||0||true||N
|
||||
EMA_Fast=8||5||1||13||Y
|
||||
EMA_Slow=21||13||2||34||Y
|
||||
EMA_Separation_MaxPct=0.22||0.08||0.02||0.45||Y
|
||||
; === RSI entries ===
|
||||
RSI_Period=14||7||1||21||Y
|
||||
RSI_Price=1||1||1||7||Y
|
||||
RSI_Oversold=32.0||22.0||1.0||42.0||Y
|
||||
RSI_Overbought=68.0||58.0||1.0||78.0||Y
|
||||
; === Exits ===
|
||||
UseRSI_MeanExit=true||false||0||true||N
|
||||
RSI_Exit_Long=52.0||48.0||1.0||62.0||Y
|
||||
RSI_Exit_Short=48.0||38.0||1.0||52.0||Y
|
||||
SL_ATR_Mult=1.35||0.9||0.05||2.2||Y
|
||||
TP_ATR_Mult=1.85||1.0||0.05||3.0||Y
|
||||
MaxBarsInTrade=36||12||2||80||Y
|
||||
; === Risk & execution ===
|
||||
Lots=0.1||0.1||0.01||1.0||N
|
||||
MagicNumber=20250420||20250420||1||20250420||N
|
||||
Slippage=10||10||1||100||N
|
||||
MaxSpreadPoints=0||0||1||30||Y
|
||||
@@ -0,0 +1,38 @@
|
||||
; RSICrossOverReversalXAUUSD/main.mq5 — Strategy Tester → Inputs → Load
|
||||
;
|
||||
; MT5 line format: Name=Value||From||Step||To||Optimize
|
||||
; Value = input default when you Load this file
|
||||
; From, Step, To = optimization range when Optimize=Y (Genetic / slow complete algorithm)
|
||||
; Optimize=N = keep Value; From/Step/To are ignored
|
||||
;
|
||||
; First column matches main.mq5 source defaults (not the old Desktop 123 snapshot).
|
||||
|
||||
MagicNumber=7||7||1||7||N
|
||||
rsiPeriod=19||10||1||35||Y
|
||||
overboughtLevel=93||65||2||95||Y
|
||||
oversoldLevel=22||10||1||45||Y
|
||||
entryRSIBuySpread=0.0||0.0||0.5||8.0||Y
|
||||
entryRSISellSpread=0.0||0.0||0.5||8.0||Y
|
||||
lotSize=0.1||0.1||0.01||0.5||N
|
||||
slippage=3||3||1||3||N
|
||||
cooldownSeconds=209||60||30||600||Y
|
||||
TimeFrame1=1||1||0||1||N
|
||||
TimeFrame2=1||1||0||1||N
|
||||
BarTimeFrame=12||12||0||12||N
|
||||
emaPeriod=140||50||10||300||Y
|
||||
emaSlopeThreshold=105.0||20.0||5.0||200.0||Y
|
||||
exitBuyRSI=86.0||65.0||1.0||92.0||Y
|
||||
exitSellRSI=10.0||5.0||1.0||40.0||Y
|
||||
TrailingStop=295.0||80.0||15.0||450.0||Y
|
||||
emaDistanceThreshold=165.0||40.0||10.0||350.0||Y
|
||||
tradingHourOneBegin=24||0||1||23||N
|
||||
tradingHourOneEnd=22||0||1||23||N
|
||||
tradingHourTwoBegin=6||0||1||14||Y
|
||||
tradingHourTwoEnd=19||14||1||23||Y
|
||||
Sunday=false||false||0||true||Y
|
||||
Monday=false||false||0||true||Y
|
||||
Tuesday=true||false||0||true||Y
|
||||
Wednesday=true||false||0||true||Y
|
||||
Thursday=true||false||0||true||Y
|
||||
Friday=false||false||0||true||Y
|
||||
Saturday=false||false||0||true||Y
|
||||
@@ -0,0 +1,50 @@
|
||||
; RSIMidPointHijackXAUUSD/main.mq5 — Strategy Tester → Inputs → Load (Genetic optimization)
|
||||
; Format: Parameter=Current||Start||Step||Stop||Optimize(Y/N)
|
||||
;
|
||||
; Baseline from Desktop 123.set (2026.05.13). InpTimeframe fixed at H1 (16385) to avoid invalid ENUM_TIMEFRAMES.
|
||||
; RSI level sweeps capped at 100; hour sweeps capped at 23 (123.set used wider columns that are invalid for this EA).
|
||||
|
||||
; === General Settings ===
|
||||
InpTimeframe=16385||16385||1||16385||N
|
||||
InpLotSize=0.1||0.01||0.01||1.0||Y
|
||||
InpMagicNumberRSIFollow=1001||1001||1||10010||N
|
||||
InpMagicNumberRSIReverse=1002||1002||1||10020||N
|
||||
InpMagicNumberEMACross=1003||1003||1||10030||N
|
||||
|
||||
; === Strategy Switches ===
|
||||
InpEnableRSIFollow=true||false||0||true||N
|
||||
InpEnableRSIReverse=true||false||0||true||N
|
||||
InpEnableEMACross=true||false||0||true||N
|
||||
InpEnableStrategyLock=true||false||0||true||N
|
||||
InpLockProfitThreshold=6.0||0.6||0.6||60.0||Y
|
||||
InpCloseOppositeTrades=true||false||0||true||N
|
||||
|
||||
; === RSI Follow Strategy ===
|
||||
InpRSIPeriod=32||8||2||96||Y
|
||||
InpRSIOverbought=78||60||1||95||Y
|
||||
InpRSIOversold=46||5||1||45||Y
|
||||
InpRSIExitLevel=44||10||1||90||Y
|
||||
InpRSIFollowStartHour=23||0||1||23||Y
|
||||
InpRSIFollowEndHour=8||0||1||23||Y
|
||||
InpRSIFollowCloseOutsideHours=false||false||0||true||N
|
||||
|
||||
; === RSI Reverse Strategy ===
|
||||
InpRSIReversePeriod=59||14||2||96||Y
|
||||
InpRSIReverseOverbought=51||55||1||95||Y
|
||||
InpRSIReverseOversold=49||5||1||50||Y
|
||||
InpRSIReverseCrossLevel=53||45||1||70||Y
|
||||
InpRSIReverseExitLevel=48||10||1||90||Y
|
||||
InpRSIReverseStartHour=7||0||1||23||Y
|
||||
InpRSIReverseEndHour=13||0||1||23||Y
|
||||
InpRSIReverseCloseOutsideHours=false||false||0||true||N
|
||||
InpRSIReverseCooldownBars=15||1||1||150||Y
|
||||
InpRSIReverseCooldownOnLoss=true||false||0||true||N
|
||||
|
||||
; === EMA Cross Strategy ===
|
||||
InpEMAPeriod=120||20||5||200||Y
|
||||
InpEMACrossStartHour=8||0||1||23||Y
|
||||
InpEMACrossEndHour=14||0||1||23||Y
|
||||
InpEMACrossCloseOutsideHours=true||false||0||true||N
|
||||
InpUseEMADistanceEntry=true||false||0||true||N
|
||||
InpEMADistancePips=160.0||16.0||16.0||1600.0||Y
|
||||
InpEMADistancePeriod=26||5||1||60||Y
|
||||
@@ -0,0 +1,23 @@
|
||||
; RSIReversalAsianAUDUSD/main.mq5 — Strategy Tester → Inputs → Load (Genetic optimization)
|
||||
; Format: Parameter=Current||Start||Step||Stop||Optimize(Y/N)
|
||||
; Baseline aligned with current input defaults and Desktop 123.set-style ranges (2026.05.13).
|
||||
;
|
||||
; Core RSI / exits
|
||||
RSIPeriod=28||14||2||40||Y
|
||||
OverboughtLevel=68.0||55.0||1.0||80.0||Y
|
||||
OversoldLevel=30.0||18.0||1.0||42.0||Y
|
||||
TakeProfitPips=175||50||25||350||Y
|
||||
StopLossPips=5||5||5||80||Y
|
||||
MaxLotSize=0.2||0.01||0.01||0.2||N
|
||||
MaxSpread=1000||200||100||2000||Y
|
||||
MaxDuration=340||48||24||720||Y
|
||||
UseStopLoss=false||false||0||true||N
|
||||
UseTakeProfit=false||false||0||true||N
|
||||
UseRSIExit=true||false||0||true||N
|
||||
RSIExitLevel=48.0||40.0||1.0||55.0||Y
|
||||
CloseOutsideSession=true||false||0||true||Y
|
||||
; Panel (usually fixed; colors as in MT5 saved sets)
|
||||
PanelBackground=0
|
||||
PanelText=16777215
|
||||
PanelX=10||10||1||100||N
|
||||
PanelY=20||20||1||200||N
|
||||
@@ -0,0 +1,23 @@
|
||||
; RSIReversalAsianAUDUSD/main.mq5 — Strategy Tester → Inputs → Load (Genetic optimization)
|
||||
; Format: Parameter=Current||Start||Step||Stop||Optimize(Y/N)
|
||||
; Baseline aligned with current input defaults and Desktop 123.set-style ranges (2026.05.13).
|
||||
;
|
||||
; Core RSI / exits
|
||||
RSIPeriod=28||14||2||40||Y
|
||||
OverboughtLevel=68.0||55.0||1.0||80.0||Y
|
||||
OversoldLevel=30.0||18.0||1.0||42.0||Y
|
||||
TakeProfitPips=175||50||25||350||Y
|
||||
StopLossPips=5||5||5||80||Y
|
||||
MaxLotSize=0.2||0.01||0.01||0.2||N
|
||||
MaxSpread=1000||200||100||2000||Y
|
||||
MaxDuration=340||48||24||720||Y
|
||||
UseStopLoss=false||false||0||true||N
|
||||
UseTakeProfit=false||false||0||true||N
|
||||
UseRSIExit=true||false||0||true||N
|
||||
RSIExitLevel=48.0||40.0||1.0||55.0||Y
|
||||
CloseOutsideSession=true||false||0||true||Y
|
||||
; Panel (usually fixed; colors as in MT5 saved sets)
|
||||
PanelBackground=0
|
||||
PanelText=16777215
|
||||
PanelX=10||10||1||100||N
|
||||
PanelY=20||20||1||200||N
|
||||
@@ -1,5 +1,5 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SimpleRSIReversalAUDUSD.mq5 |
|
||||
//| RSIReversalAsianGBPUSD.mq5 |
|
||||
//| Copyright 2024, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -11,19 +11,19 @@
|
||||
// Include trade class
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
// Input parameters
|
||||
input int RSIPeriod = 28; // RSI period
|
||||
input double OverboughtLevel = 68; // Overbought level
|
||||
input double OversoldLevel = 30; // Oversold level
|
||||
input int TakeProfitPips = 175; // Take profit in pips
|
||||
input int StopLossPips = 5; // Stop loss in pips
|
||||
// Input parameters (defaults synced from working 123.set 2026.05.13)
|
||||
input int RSIPeriod = 32; // RSI period
|
||||
input double OverboughtLevel = 80; // Overbought level
|
||||
input double OversoldLevel = 37; // Oversold level
|
||||
input int TakeProfitPips = 225; // Take profit in pips
|
||||
input int StopLossPips = 45; // Stop loss in pips
|
||||
input double MaxLotSize = 0.2; // Maximum lot size
|
||||
input int MaxSpread = 1000; // Maximum allowed spread in pips
|
||||
input int MaxDuration = 340; // Maximum trade duration in hours
|
||||
input int MaxSpread = 1800; // Maximum allowed spread in pips
|
||||
input int MaxDuration = 480; // Maximum trade duration in hours
|
||||
input bool UseStopLoss = false; // Use stop loss
|
||||
input bool UseTakeProfit = false; // Use take profit
|
||||
input bool UseRSIExit = true; // Use RSI for exit
|
||||
input double RSIExitLevel = 48; // RSI level to exit (50 = neutral)
|
||||
input double RSIExitLevel = 43; // RSI level to exit (50 = neutral)
|
||||
input bool CloseOutsideSession = true; // Close trades outside Asian session
|
||||
input color PanelBackground = clrBlack; // Panel background color
|
||||
input color PanelText = clrWhite; // Panel text color
|
||||
|
Before Width: | Height: | Size: 241 KiB After Width: | Height: | Size: 241 KiB |
@@ -0,0 +1,327 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| RSIScalping.mq5 |
|
||||
//| Copyright 2025, MetaQuotes Ltd. |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||
#property link "https://www.mql5.com"
|
||||
#property version "1.00"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 32; // RSI Overbought Level
|
||||
input double RSI_Oversold = 86; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 100; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 24; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 34; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 5; // Lot Size
|
||||
input int MagicNumber = 129102315; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int rsi_handle;
|
||||
double rsi_buffer[];
|
||||
double rsi_prev, rsi_current, rsi_two_bars_ago;
|
||||
bool position_open = false;
|
||||
int position_ticket = 0;
|
||||
ENUM_POSITION_TYPE current_position_type = POSITION_TYPE_BUY;
|
||||
datetime last_bar_time = 0;
|
||||
bool rsi_against_position = false;
|
||||
int bars_against_count = 0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Initialize RSI indicator
|
||||
rsi_handle = iRSI(_Symbol, TimeFrame, RSI_Period, RSI_Applied_Price);
|
||||
if(rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Initialize trade object
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
trade.SetDeviationInPoints(Slippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Allocate arrays
|
||||
ArraySetAsSeries(rsi_buffer, true);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsi_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if we have enough bars
|
||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a new bar
|
||||
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
return; // Still the same bar, don't process
|
||||
}
|
||||
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Update RSI values
|
||||
if(!UpdateRSI())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for existing position
|
||||
CheckExistingPosition();
|
||||
|
||||
// Check for new entry signals - only if no position exists for THIS EA (magic number) on THIS symbol
|
||||
if(!position_open && !PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
CheckEntrySignals();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Update RSI values |
|
||||
//+------------------------------------------------------------------+
|
||||
bool UpdateRSI()
|
||||
{
|
||||
if(CopyBuffer(rsi_handle, 0, 0, 3, rsi_buffer) < 3)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rsi_current = rsi_buffer[0]; // Current bar
|
||||
rsi_prev = rsi_buffer[1]; // Previous bar
|
||||
rsi_two_bars_ago = rsi_buffer[2]; // Two bars ago
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing position for exit conditions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckExistingPosition()
|
||||
{
|
||||
if(!position_open)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if position still exists with correct magic number AND symbol for THIS EA
|
||||
if(!PositionSelectByTicketSymbolAndMagic(position_ticket, _Symbol, MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Exit conditions based on RSI target
|
||||
if(current_position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
// Check if RSI is against the position (below oversold)
|
||||
if(rsi_current < RSI_Oversold)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit long position when RSI reaches buy target
|
||||
if(rsi_current >= RSI_Target_Buy)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(current_position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
// Check if RSI is against the position (above overbought)
|
||||
if(rsi_current > RSI_Overbought)
|
||||
{
|
||||
if(!rsi_against_position)
|
||||
{
|
||||
rsi_against_position = true;
|
||||
bars_against_count = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
bars_against_count++;
|
||||
}
|
||||
|
||||
// Close position if RSI has been against for Y bars
|
||||
if(bars_against_count >= BarsToWait)
|
||||
{
|
||||
ClosePosition();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// RSI is no longer against the position, reset counter
|
||||
if(rsi_against_position)
|
||||
{
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
|
||||
// Exit short position when RSI reaches sell target
|
||||
if(rsi_current <= RSI_Target_Sell)
|
||||
{
|
||||
ClosePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for entry signals |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckEntrySignals()
|
||||
{
|
||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
||||
{
|
||||
OpenBuyPosition();
|
||||
}
|
||||
|
||||
// Sell signal: RSI crosses from overbought to below overbought (checking the actual crossover)
|
||||
if(rsi_two_bars_ago >= RSI_Overbought && rsi_prev < RSI_Overbought)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
if(trade.Buy(LotSize, _Symbol, ask, 0, 0, "RSI Scalping Buy"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_BUY;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
||||
if(PositionExistsByMagic(_Symbol, MagicNumber))
|
||||
{
|
||||
return; // Position already exists for this EA
|
||||
}
|
||||
|
||||
double bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
if(trade.Sell(LotSize, _Symbol, bid, 0, 0, "RSI Scalping Sell"))
|
||||
{
|
||||
ulong new_ticket = trade.ResultOrder();
|
||||
if(new_ticket > 0)
|
||||
{
|
||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
||||
if(PositionSelectByTicketSymbolAndMagic(new_ticket, _Symbol, MagicNumber))
|
||||
{
|
||||
position_ticket = new_ticket;
|
||||
position_open = true;
|
||||
current_position_type = POSITION_TYPE_SELL;
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Error: Position opened but doesn't match EA magic number or symbol");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Close current position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ClosePosition()
|
||||
{
|
||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
||||
if(ClosePositionByMagic(trade, _Symbol, MagicNumber))
|
||||
{
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Position doesn't exist or wrong magic number - reset tracking
|
||||
position_open = false;
|
||||
position_ticket = 0;
|
||||
rsi_against_position = false;
|
||||
bars_against_count = 0;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
@@ -10,17 +10,17 @@
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M15; // Timeframe for Analysis
|
||||
input int RSI_Period = 8; // RSI Period
|
||||
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 36; // RSI Overbought Level
|
||||
input double RSI_Oversold = 38; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 90; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 70; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 5; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 50; // Lot Size
|
||||
input int MagicNumber = 12345; // Magic Number
|
||||
input double RSI_Overbought = 6; // RSI Overbought Level
|
||||
input double RSI_Oversold = 66; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 98; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 52; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 12; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 5; // Lot Size
|
||||
input int MagicNumber = 129102315; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
//--- Global variables
|
||||
|
||||
@@ -10,17 +10,17 @@
|
||||
#include <Trade\Trade.mqh>
|
||||
#include "../_united/MagicNumberHelpers.mqh"
|
||||
|
||||
//--- Input parameters
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
|
||||
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||
input int RSI_Period = 14; // RSI Period
|
||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||
input double RSI_Overbought = 54; // RSI Overbought Level
|
||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 87; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 33; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 1; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 50; // Lot Size
|
||||
input int MagicNumber = 125421321; // Magic Number
|
||||
input double RSI_Overbought = 32; // RSI Overbought Level
|
||||
input double RSI_Oversold = 86; // RSI Oversold Level
|
||||
input double RSI_Target_Buy = 100; // RSI Target for Buy Exit
|
||||
input double RSI_Target_Sell = 24; // RSI Target for Sell Exit
|
||||
input int BarsToWait = 34; // Bars to wait when RSI goes against position
|
||||
input double LotSize = 5; // Lot Size
|
||||
input int MagicNumber = 129102315; // Magic Number
|
||||
input int Slippage = 3; // Slippage in points
|
||||
|
||||
//--- Global variables
|
||||
|
||||
|
Before Width: | Height: | Size: 8.4 KiB |
@@ -1,36 +0,0 @@
|
||||
; SuperEMA — defaults aligned with lab/EAs/SuperEMA.mq5 (v1.01)
|
||||
; Load from Strategy Tester → Inputs → context menu → Load
|
||||
;
|
||||
; === Market ===
|
||||
InpSymbol=
|
||||
InpTimeframe=15||15||0||49153||N
|
||||
InpLots=0.01||0.01||0.01||0.10||N
|
||||
InpSlippagePoints=55||20||5||120||Y
|
||||
InpMagic=940001||940001||1||9400010||N
|
||||
; === EMA (trend & structure) ===
|
||||
InpEmaFast=40||20||10||120||Y
|
||||
InpEmaMid=180||60||15||200||Y
|
||||
InpEmaSlow=125||100||25||400||Y
|
||||
InpEmaTrendBars=3||1||1||3||Y
|
||||
; === CCI ===
|
||||
InpCciPeriod=17||7||1||28||Y
|
||||
InpCciOverbought=80.0||80.0||10.0||140.0||Y
|
||||
InpCciOversold=-140.0||-140.0||10.0||-80.0||Y
|
||||
InpPullbackCciLookback=20||4||2||24||Y
|
||||
; === MACD (histogram = main - signal) ===
|
||||
InpMacdFast=14||8||2||20||Y
|
||||
InpMacdSlow=38||20||2||40||Y
|
||||
InpMacdSignal=9||5||1||15||Y
|
||||
; === Strategy ===
|
||||
InpEntryStyle=1||0||1||2||Y
|
||||
InpOneTradeOnly=true||false||0||true||N
|
||||
InpUseStructuralSL=false||false||0||true||Y
|
||||
InpSlBufferPoints=110.0||20.0||10.0||200.0||Y
|
||||
; === Exits (so trades do not run forever) ===
|
||||
InpExitOnTrendFlip=false||false||0||true||Y
|
||||
InpExitOnMacdFlip=false||false||0||true||Y
|
||||
InpExitOnCciZeroCross=true||false||0||true||Y
|
||||
InpMaxHoldingBars=168||48||24||480||Y
|
||||
InpExitBelowMidEma=false||false||0||true||Y
|
||||
; === Debug ===
|
||||
InpDebugLogs=false||false||0||true||N
|
||||
@@ -1,448 +0,0 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| SuperEMA.mq5 |
|
||||
//| EMA + CCI + MACD histogram — trend filter, momentum confirmation |
|
||||
//+------------------------------------------------------------------+
|
||||
#property strict
|
||||
#property version "1.01"
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
enum ENUM_ENTRY_STYLE
|
||||
{
|
||||
ENTRY_CCIZERO_MACD = 0, // EMA trend + CCI crosses zero + MACD histogram agrees
|
||||
ENTRY_LAMBERT = 1, // EMA trend + CCI crosses ±100 + MACD histogram agrees
|
||||
ENTRY_PULLBACK = 2 // Uptrend: pullback to fast EMA + CCI was oversold + CCI crosses up through 0 + MACD > 0 (mirror for sells)
|
||||
};
|
||||
|
||||
input group "=== Market ==="
|
||||
input string InpSymbol = "";
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15;
|
||||
input double InpLots = 0.01;
|
||||
input int InpSlippagePoints = 55;
|
||||
input int InpMagic = 940001;
|
||||
|
||||
input group "=== EMA (trend & structure) ==="
|
||||
input int InpEmaFast = 40;
|
||||
input int InpEmaMid = 180;
|
||||
input int InpEmaSlow = 125;
|
||||
input int InpEmaTrendBars = 3; // closed bar shift for EMA reads
|
||||
|
||||
input group "=== CCI ==="
|
||||
input int InpCciPeriod = 17;
|
||||
input double InpCciOverbought = 80.0;
|
||||
input double InpCciOversold = -140.0;
|
||||
input int InpPullbackCciLookback = 20; // bars to check prior CCI oversold/overbought
|
||||
|
||||
input group "=== MACD (histogram = main - signal) ==="
|
||||
input int InpMacdFast = 14;
|
||||
input int InpMacdSlow = 38;
|
||||
input int InpMacdSignal = 9;
|
||||
|
||||
input group "=== Strategy ==="
|
||||
input ENUM_ENTRY_STYLE InpEntryStyle = ENTRY_LAMBERT;
|
||||
input bool InpOneTradeOnly = true;
|
||||
input bool InpUseStructuralSL = false;
|
||||
input double InpSlBufferPoints = 110;
|
||||
|
||||
input group "=== Exits (so trades do not run forever) ==="
|
||||
input bool InpExitOnTrendFlip = false; // close when price vs slow EMA flips against position
|
||||
input bool InpExitOnMacdFlip = false; // close when MACD histogram flips against position
|
||||
input bool InpExitOnCciZeroCross = true; // long: CCI crosses below 0; short: CCI crosses above 0
|
||||
input int InpMaxHoldingBars = 168; // 0 = disabled (e.g. ~8 days M15)
|
||||
input bool InpExitBelowMidEma = false; // long: close if close < mid EMA (invalidation)
|
||||
|
||||
input group "=== Debug ==="
|
||||
input bool InpDebugLogs = false;
|
||||
|
||||
CTrade trade;
|
||||
datetime g_lastBarTime = 0;
|
||||
|
||||
string WorkSymbol()
|
||||
{
|
||||
return (InpSymbol == "" || InpSymbol == NULL) ? _Symbol : InpSymbol;
|
||||
}
|
||||
|
||||
void Log(const string s)
|
||||
{
|
||||
if(InpDebugLogs)
|
||||
Print("[SuperEMA] ", s);
|
||||
}
|
||||
|
||||
bool IsNewBar(const string sym, const ENUM_TIMEFRAMES tf)
|
||||
{
|
||||
datetime t = iTime(sym, tf, 0);
|
||||
if(t <= 0 || t == g_lastBarTime)
|
||||
return false;
|
||||
g_lastBarTime = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
double EmaAt(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shift)
|
||||
{
|
||||
int h = iMA(sym, tf, period, 0, MODE_EMA, PRICE_CLOSE);
|
||||
if(h == INVALID_HANDLE)
|
||||
return 0.0;
|
||||
double b[1];
|
||||
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
|
||||
{
|
||||
IndicatorRelease(h);
|
||||
return 0.0;
|
||||
}
|
||||
IndicatorRelease(h);
|
||||
return b[0];
|
||||
}
|
||||
|
||||
double CciAt(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shift)
|
||||
{
|
||||
int h = iCCI(sym, tf, period, PRICE_TYPICAL);
|
||||
if(h == INVALID_HANDLE)
|
||||
return 0.0;
|
||||
double b[1];
|
||||
if(CopyBuffer(h, 0, shift, 1, b) <= 0)
|
||||
{
|
||||
IndicatorRelease(h);
|
||||
return 0.0;
|
||||
}
|
||||
IndicatorRelease(h);
|
||||
return b[0];
|
||||
}
|
||||
|
||||
bool MacdHistAt(const string sym, const ENUM_TIMEFRAMES tf, const int fast, const int slow, const int signal, const int shift, double &hist)
|
||||
{
|
||||
int h = iMACD(sym, tf, fast, slow, signal, PRICE_CLOSE);
|
||||
if(h == INVALID_HANDLE)
|
||||
return false;
|
||||
double mainLine[1], sigLine[1];
|
||||
if(CopyBuffer(h, 0, shift, 1, mainLine) <= 0 || CopyBuffer(h, 1, shift, 1, sigLine) <= 0)
|
||||
{
|
||||
IndicatorRelease(h);
|
||||
return false;
|
||||
}
|
||||
IndicatorRelease(h);
|
||||
hist = mainLine[0] - sigLine[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TrendUp(const string sym, const int sh)
|
||||
{
|
||||
double c = iClose(sym, InpTimeframe, sh);
|
||||
double emaS = EmaAt(sym, InpTimeframe, InpEmaSlow, sh);
|
||||
return (emaS > 0.0 && c > emaS);
|
||||
}
|
||||
|
||||
bool TrendDown(const string sym, const int sh)
|
||||
{
|
||||
double c = iClose(sym, InpTimeframe, sh);
|
||||
double emaS = EmaAt(sym, InpTimeframe, InpEmaSlow, sh);
|
||||
return (emaS > 0.0 && c < emaS);
|
||||
}
|
||||
|
||||
bool CciCrossAboveZero(const string sym)
|
||||
{
|
||||
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
|
||||
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
|
||||
return (c2 <= 0.0 && c1 > 0.0);
|
||||
}
|
||||
|
||||
bool CciCrossBelowZero(const string sym)
|
||||
{
|
||||
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
|
||||
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
|
||||
return (c2 >= 0.0 && c1 < 0.0);
|
||||
}
|
||||
|
||||
bool CciCrossAbove100(const string sym)
|
||||
{
|
||||
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
|
||||
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
|
||||
return (c2 < InpCciOverbought && c1 > InpCciOverbought);
|
||||
}
|
||||
|
||||
bool CciCrossBelowMinus100(const string sym)
|
||||
{
|
||||
double c1 = CciAt(sym, InpTimeframe, InpCciPeriod, 1);
|
||||
double c2 = CciAt(sym, InpTimeframe, InpCciPeriod, 2);
|
||||
return (c2 > InpCciOversold && c1 < InpCciOversold);
|
||||
}
|
||||
|
||||
bool HadCciOversoldRecently(const string sym)
|
||||
{
|
||||
for(int i = 2; i <= InpPullbackCciLookback + 1; i++)
|
||||
{
|
||||
double v = CciAt(sym, InpTimeframe, InpCciPeriod, i);
|
||||
if(v <= InpCciOversold)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HadCciOverboughtRecently(const string sym)
|
||||
{
|
||||
for(int i = 2; i <= InpPullbackCciLookback + 1; i++)
|
||||
{
|
||||
double v = CciAt(sym, InpTimeframe, InpCciPeriod, i);
|
||||
if(v >= InpCciOverbought)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PullbackNearFastEmaLong(const string sym)
|
||||
{
|
||||
double emaF = EmaAt(sym, InpTimeframe, InpEmaFast, 1);
|
||||
double lo = iLow(sym, InpTimeframe, 1);
|
||||
if(emaF <= 0.0)
|
||||
return false;
|
||||
return (lo <= emaF + InpSlBufferPoints * _Point * 3.0);
|
||||
}
|
||||
|
||||
bool PullbackNearFastEmaShort(const string sym)
|
||||
{
|
||||
double emaF = EmaAt(sym, InpTimeframe, InpEmaFast, 1);
|
||||
double hi = iHigh(sym, InpTimeframe, 1);
|
||||
if(emaF <= 0.0)
|
||||
return false;
|
||||
return (hi >= emaF - InpSlBufferPoints * _Point * 3.0);
|
||||
}
|
||||
|
||||
int PositionsByMagic(const string sym, const int magic)
|
||||
{
|
||||
int n = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong t = PositionGetTicket(i);
|
||||
if(t == 0)
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) == sym && (int)PositionGetInteger(POSITION_MAGIC) == magic)
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void ComputeSLTP(const bool isBuy, const double entry, double &sl, double &tp)
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
sl = 0.0;
|
||||
tp = 0.0;
|
||||
if(!InpUseStructuralSL)
|
||||
return;
|
||||
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, InpEmaTrendBars);
|
||||
double buf = InpSlBufferPoints * _Point;
|
||||
if(isBuy)
|
||||
sl = emaM - buf;
|
||||
else
|
||||
sl = emaM + buf;
|
||||
}
|
||||
|
||||
int BarsSinceOpen(const string sym, const datetime openTime)
|
||||
{
|
||||
if(openTime <= 0)
|
||||
return 0;
|
||||
int sh = iBarShift(sym, InpTimeframe, openTime, false);
|
||||
if(sh < 0)
|
||||
return 999999;
|
||||
return sh;
|
||||
}
|
||||
|
||||
void ClosePositionTicket(const ulong ticket, const string reason)
|
||||
{
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
if(trade.PositionClose(ticket))
|
||||
Log("Close: " + reason);
|
||||
}
|
||||
|
||||
void ManageSuperEMAExits(const string sym)
|
||||
{
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0)
|
||||
continue;
|
||||
if(!PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != sym)
|
||||
continue;
|
||||
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
|
||||
ENUM_POSITION_TYPE ptype = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
|
||||
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
|
||||
double h1 = 0.0;
|
||||
if(!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 1, h1))
|
||||
continue;
|
||||
|
||||
bool closeLong = false;
|
||||
bool closeShort = false;
|
||||
string reason = "";
|
||||
|
||||
if(InpMaxHoldingBars > 0)
|
||||
{
|
||||
int held = BarsSinceOpen(sym, openTime);
|
||||
if(held >= InpMaxHoldingBars)
|
||||
{
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
closeLong = true;
|
||||
else
|
||||
closeShort = true;
|
||||
reason = "time stop (max bars)";
|
||||
}
|
||||
}
|
||||
|
||||
if(ptype == POSITION_TYPE_BUY)
|
||||
{
|
||||
if(InpExitOnTrendFlip && TrendDown(sym, InpEmaTrendBars))
|
||||
{
|
||||
closeLong = true;
|
||||
reason = "trend flip (below slow EMA)";
|
||||
}
|
||||
if(InpExitOnMacdFlip && h1 < 0.0)
|
||||
{
|
||||
closeLong = true;
|
||||
reason = "MACD histogram < 0";
|
||||
}
|
||||
if(InpExitOnCciZeroCross && CciCrossBelowZero(sym))
|
||||
{
|
||||
closeLong = true;
|
||||
reason = "CCI crossed below zero";
|
||||
}
|
||||
if(InpExitBelowMidEma)
|
||||
{
|
||||
double c = iClose(sym, InpTimeframe, 1);
|
||||
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, 1);
|
||||
if(emaM > 0.0 && c < emaM)
|
||||
{
|
||||
closeLong = true;
|
||||
reason = "close below mid EMA";
|
||||
}
|
||||
}
|
||||
if(closeLong)
|
||||
ClosePositionTicket(ticket, reason);
|
||||
}
|
||||
else if(ptype == POSITION_TYPE_SELL)
|
||||
{
|
||||
if(InpExitOnTrendFlip && TrendUp(sym, InpEmaTrendBars))
|
||||
{
|
||||
closeShort = true;
|
||||
reason = "trend flip (above slow EMA)";
|
||||
}
|
||||
if(InpExitOnMacdFlip && h1 > 0.0)
|
||||
{
|
||||
closeShort = true;
|
||||
reason = "MACD histogram > 0";
|
||||
}
|
||||
if(InpExitOnCciZeroCross && CciCrossAboveZero(sym))
|
||||
{
|
||||
closeShort = true;
|
||||
reason = "CCI crossed above zero";
|
||||
}
|
||||
if(InpExitBelowMidEma)
|
||||
{
|
||||
double c = iClose(sym, InpTimeframe, 1);
|
||||
double emaM = EmaAt(sym, InpTimeframe, InpEmaMid, 1);
|
||||
if(emaM > 0.0 && c > emaM)
|
||||
{
|
||||
closeShort = true;
|
||||
reason = "close above mid EMA";
|
||||
}
|
||||
}
|
||||
if(closeShort)
|
||||
ClosePositionTicket(ticket, reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
string sym = WorkSymbol();
|
||||
if(!SymbolSelect(sym, true))
|
||||
{
|
||||
Print("SuperEMA: cannot select symbol ", sym);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
trade.SetExpertMagicNumber(InpMagic);
|
||||
trade.SetDeviationInPoints(InpSlippagePoints);
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
string sym = WorkSymbol();
|
||||
if(_Symbol != sym)
|
||||
{
|
||||
static datetime lastLog = 0;
|
||||
datetime tb = iTime(_Symbol, PERIOD_M1, 0);
|
||||
if(tb != lastLog && InpDebugLogs)
|
||||
{
|
||||
lastLog = tb;
|
||||
Log("Chart symbol differs from WorkSymbol; attach to " + sym + " or set InpSymbol empty.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(!IsNewBar(sym, InpTimeframe))
|
||||
return;
|
||||
|
||||
// Exits must run every bar; do not skip when a position exists (otherwise trades never close with SL=0/TP=0).
|
||||
ManageSuperEMAExits(sym);
|
||||
|
||||
if(InpOneTradeOnly && PositionsByMagic(sym, InpMagic) > 0)
|
||||
return;
|
||||
|
||||
const int sh = InpEmaTrendBars;
|
||||
double h1 = 0.0, h2 = 0.0;
|
||||
if(!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 1, h1) ||
|
||||
!MacdHistAt(sym, InpTimeframe, InpMacdFast, InpMacdSlow, InpMacdSignal, 2, h2))
|
||||
return;
|
||||
|
||||
bool up = TrendUp(sym, sh);
|
||||
bool dn = TrendDown(sym, sh);
|
||||
|
||||
bool wantBuy = false;
|
||||
bool wantSell = false;
|
||||
|
||||
switch(InpEntryStyle)
|
||||
{
|
||||
case ENTRY_CCIZERO_MACD:
|
||||
if(up && CciCrossAboveZero(sym) && h1 > 0.0)
|
||||
wantBuy = true;
|
||||
if(dn && CciCrossBelowZero(sym) && h1 < 0.0)
|
||||
wantSell = true;
|
||||
break;
|
||||
|
||||
case ENTRY_LAMBERT:
|
||||
if(up && CciCrossAbove100(sym) && h1 > 0.0)
|
||||
wantBuy = true;
|
||||
if(dn && CciCrossBelowMinus100(sym) && h1 < 0.0)
|
||||
wantSell = true;
|
||||
break;
|
||||
|
||||
case ENTRY_PULLBACK:
|
||||
if(up && HadCciOversoldRecently(sym) && CciCrossAboveZero(sym) && h1 > 0.0 && PullbackNearFastEmaLong(sym))
|
||||
wantBuy = true;
|
||||
if(dn && HadCciOverboughtRecently(sym) && CciCrossBelowZero(sym) && h1 < 0.0 && PullbackNearFastEmaShort(sym))
|
||||
wantSell = true;
|
||||
break;
|
||||
}
|
||||
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(sym, tick))
|
||||
return;
|
||||
|
||||
double sl = 0.0, tp = 0.0;
|
||||
|
||||
if(wantBuy && !wantSell)
|
||||
{
|
||||
ComputeSLTP(true, tick.ask, sl, tp);
|
||||
if(trade.Buy(InpLots, sym, tick.ask, sl, tp, "SuperEMA long"))
|
||||
Log(StringFormat("BUY ask=%.5f sl=%.5f cci=%.2f macdHist=%.5f", tick.ask, sl,
|
||||
CciAt(sym, InpTimeframe, InpCciPeriod, 1), h1));
|
||||
}
|
||||
else if(wantSell && !wantBuy)
|
||||
{
|
||||
ComputeSLTP(false, tick.bid, sl, tp);
|
||||
if(trade.Sell(InpLots, sym, tick.bid, sl, tp, "SuperEMA short"))
|
||||
Log(StringFormat("SELL bid=%.5f sl=%.5f cci=%.2f macdHist=%.5f", tick.bid, sl,
|
||||
CciAt(sym, InpTimeframe, InpCciPeriod, 1), h1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
; CandleChartPattern/main.mq5 — Strategy Tester → Inputs → Load
|
||||
; Format: Name=Value||From||Step||To||Optimize(Y/N)
|
||||
; Value = load default (aligned with EA + Desktop 123.set 2026.05.14). From/Step/To used when Y.
|
||||
;
|
||||
; === Market ===
|
||||
InpSymbol=
|
||||
InpLots=0.01||0.01||0.01||0.2||N
|
||||
InpMagic=771001||771001||1||771001||N
|
||||
InpSlippagePoints=30||30||1||300||N
|
||||
InpMaxSpreadPoints=50||10||5||200||Y
|
||||
; === Timeframes ===
|
||||
; Enum timeframes: keep fixed during optimization (change manually if needed).
|
||||
InpSignalTF=15||0||0||49153||N
|
||||
InpConfirmTF=16385||0||0||49153||N
|
||||
; === Patterns (signal TF, shift 1) ===
|
||||
InpUseEngulfing=true||false||0||true||Y
|
||||
InpUseHammerPin=true||false||0||true||Y
|
||||
InpMinBodyPoints=5.0||2.0||0.5||25.0||Y
|
||||
InpHammerWickRatio=2.0||1.2||0.1||4.0||Y
|
||||
; === HTF confirmation ===
|
||||
InpRequireHtfCandleDir=true||false||0||true||Y
|
||||
InpRequireHtfPattern=false||false||0||true||Y
|
||||
; === Behaviour ===
|
||||
InpOnlyOnePosition=true||false||0||true||N
|
||||
InpCloseOnReverseSignal=true||false||0||true||Y
|
||||
InpCloseOnAdversePattern=true||false||0||true||Y
|
||||
@@ -0,0 +1,332 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| CandleChartPattern.mq5 |
|
||||
//| Lab EA: candle patterns on signal TF + HTF confirmation. |
|
||||
//| No SL/TP. Exit on opposite signal or adverse pattern. |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Lab"
|
||||
#property link ""
|
||||
#property version "1.01"
|
||||
#property strict
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input group "=== Market ==="
|
||||
input string InpSymbol = ""; // empty = chart symbol
|
||||
input double InpLots = 0.01;
|
||||
input int InpMagic = 771001;
|
||||
input int InpSlippagePoints = 30;
|
||||
input int InpMaxSpreadPoints = 50; // 0 = ignore
|
||||
|
||||
input group "=== Timeframes ==="
|
||||
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_M15; // patterns evaluated here (bar 1 = last closed)
|
||||
input ENUM_TIMEFRAMES InpConfirmTF = PERIOD_H1; // must be >= InpSignalTF for stable bias (not enforced)
|
||||
|
||||
input group "=== Patterns (signal TF, shift 1) ==="
|
||||
input bool InpUseEngulfing = true;
|
||||
input bool InpUseHammerPin = true;
|
||||
input double InpMinBodyPoints = 5.0; // min body size for engulfing (points)
|
||||
input double InpHammerWickRatio = 2.0; // shadow >= ratio * body for hammer/pin
|
||||
|
||||
input group "=== HTF confirmation ==="
|
||||
input bool InpRequireHtfCandleDir = true; // HTF last closed bar same direction as trade idea
|
||||
input bool InpRequireHtfPattern = false; // if true, same pattern class must also print on HTF bar 1
|
||||
|
||||
input group "=== Behaviour ==="
|
||||
input bool InpOnlyOnePosition = true;
|
||||
input bool InpCloseOnReverseSignal = true; // close long if validated short setup appears (and vice versa)
|
||||
input bool InpCloseOnAdversePattern = true; // close long on bearish engulf / bear pin on signal or HTF
|
||||
|
||||
CTrade g_trade;
|
||||
string g_sym;
|
||||
datetime g_lastSignalBarTime = 0;
|
||||
|
||||
ENUM_ORDER_TYPE_FILLING ResolveFilling(const string sym)
|
||||
{
|
||||
const long mask = SymbolInfoInteger(sym, SYMBOL_FILLING_MODE);
|
||||
if((mask & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
|
||||
return ORDER_FILLING_IOC;
|
||||
if((mask & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
|
||||
return ORDER_FILLING_FOK;
|
||||
return ORDER_FILLING_RETURN;
|
||||
}
|
||||
|
||||
bool SpreadOk(const string sym)
|
||||
{
|
||||
if(InpMaxSpreadPoints <= 0)
|
||||
return true;
|
||||
const double point = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return false;
|
||||
const double spreadPts = (SymbolInfoDouble(sym, SYMBOL_ASK) - SymbolInfoDouble(sym, SYMBOL_BID)) / point;
|
||||
return (spreadPts <= (double)InpMaxSpreadPoints);
|
||||
}
|
||||
|
||||
bool IsNewSignalBar()
|
||||
{
|
||||
const datetime t = iTime(g_sym, InpSignalTF, 0);
|
||||
if(t <= 0)
|
||||
return false;
|
||||
if(t == g_lastSignalBarTime)
|
||||
return false;
|
||||
g_lastSignalBarTime = t;
|
||||
return true;
|
||||
}
|
||||
|
||||
double BodyPoints(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
const double o = iOpen(s, tf, sh);
|
||||
const double c = iClose(s, tf, sh);
|
||||
const double point = SymbolInfoDouble(s, SYMBOL_POINT);
|
||||
if(point <= 0.0)
|
||||
return 0.0;
|
||||
return MathAbs(c - o) / point;
|
||||
}
|
||||
|
||||
bool BullishEngulfing(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
if(!InpUseEngulfing)
|
||||
return false;
|
||||
const double o1 = iOpen(s, tf, sh);
|
||||
const double c1 = iClose(s, tf, sh);
|
||||
const double o2 = iOpen(s, tf, sh + 1);
|
||||
const double c2 = iClose(s, tf, sh + 1);
|
||||
if(c2 >= o2)
|
||||
return false;
|
||||
if(c1 <= o1)
|
||||
return false;
|
||||
if(BodyPoints(s, tf, sh) < InpMinBodyPoints || BodyPoints(s, tf, sh + 1) < InpMinBodyPoints)
|
||||
return false;
|
||||
return (o1 <= c2 && c1 >= o2);
|
||||
}
|
||||
|
||||
bool BearishEngulfing(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
if(!InpUseEngulfing)
|
||||
return false;
|
||||
const double o1 = iOpen(s, tf, sh);
|
||||
const double c1 = iClose(s, tf, sh);
|
||||
const double o2 = iOpen(s, tf, sh + 1);
|
||||
const double c2 = iClose(s, tf, sh + 1);
|
||||
if(c2 <= o2)
|
||||
return false;
|
||||
if(c1 >= o1)
|
||||
return false;
|
||||
if(BodyPoints(s, tf, sh) < InpMinBodyPoints || BodyPoints(s, tf, sh + 1) < InpMinBodyPoints)
|
||||
return false;
|
||||
return (o1 >= c2 && c1 <= o2);
|
||||
}
|
||||
|
||||
bool BullishHammer(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
if(!InpUseHammerPin)
|
||||
return false;
|
||||
const double o = iOpen(s, tf, sh);
|
||||
const double c = iClose(s, tf, sh);
|
||||
const double h = iHigh(s, tf, sh);
|
||||
const double l = iLow(s, tf, sh);
|
||||
const double body = MathAbs(c - o);
|
||||
const double lower = MathMin(o, c) - l;
|
||||
const double upper = h - MathMax(o, c);
|
||||
const double point = SymbolInfoDouble(s, SYMBOL_POINT);
|
||||
if(point <= 0.0 || body < point * 0.1)
|
||||
return false;
|
||||
return (lower >= InpHammerWickRatio * body && upper <= body);
|
||||
}
|
||||
|
||||
bool BearishPinBar(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
if(!InpUseHammerPin)
|
||||
return false;
|
||||
const double o = iOpen(s, tf, sh);
|
||||
const double c = iClose(s, tf, sh);
|
||||
const double h = iHigh(s, tf, sh);
|
||||
const double l = iLow(s, tf, sh);
|
||||
const double body = MathAbs(c - o);
|
||||
const double lower = MathMin(o, c) - l;
|
||||
const double upper = h - MathMax(o, c);
|
||||
const double point = SymbolInfoDouble(s, SYMBOL_POINT);
|
||||
if(point <= 0.0 || body < point * 0.1)
|
||||
return false;
|
||||
return (upper >= InpHammerWickRatio * body && lower <= body);
|
||||
}
|
||||
|
||||
bool BullishPatternBar(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
return BullishEngulfing(s, tf, sh) || BullishHammer(s, tf, sh);
|
||||
}
|
||||
|
||||
bool BearishPatternBar(const string s, const ENUM_TIMEFRAMES tf, const int sh)
|
||||
{
|
||||
return BearishEngulfing(s, tf, sh) || BearishPinBar(s, tf, sh);
|
||||
}
|
||||
|
||||
bool HtfBullishClosedBar(const string s, const ENUM_TIMEFRAMES htf)
|
||||
{
|
||||
return (iClose(s, htf, 1) > iOpen(s, htf, 1));
|
||||
}
|
||||
|
||||
bool HtfBearishClosedBar(const string s, const ENUM_TIMEFRAMES htf)
|
||||
{
|
||||
return (iClose(s, htf, 1) < iOpen(s, htf, 1));
|
||||
}
|
||||
|
||||
bool ConfirmLong(const string s)
|
||||
{
|
||||
if(!InpRequireHtfCandleDir && !InpRequireHtfPattern)
|
||||
return true;
|
||||
|
||||
if(InpRequireHtfCandleDir && !HtfBullishClosedBar(s, InpConfirmTF))
|
||||
return false;
|
||||
|
||||
if(InpRequireHtfPattern && !BullishPatternBar(s, InpConfirmTF, 1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConfirmShort(const string s)
|
||||
{
|
||||
if(!InpRequireHtfCandleDir && !InpRequireHtfPattern)
|
||||
return true;
|
||||
|
||||
if(InpRequireHtfCandleDir && !HtfBearishClosedBar(s, InpConfirmTF))
|
||||
return false;
|
||||
|
||||
if(InpRequireHtfPattern && !BearishPatternBar(s, InpConfirmTF, 1))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ValidatedLongSetup(const string s)
|
||||
{
|
||||
if(!BullishPatternBar(s, InpSignalTF, 1))
|
||||
return false;
|
||||
return ConfirmLong(s);
|
||||
}
|
||||
|
||||
bool ValidatedShortSetup(const string s)
|
||||
{
|
||||
if(!BearishPatternBar(s, InpSignalTF, 1))
|
||||
return false;
|
||||
return ConfirmShort(s);
|
||||
}
|
||||
|
||||
bool HasOurPosition(const string s, const int magic, int &dir)
|
||||
{
|
||||
dir = -1;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != s)
|
||||
continue;
|
||||
if((int)PositionGetInteger(POSITION_MAGIC) != magic)
|
||||
continue;
|
||||
const long typ = PositionGetInteger(POSITION_TYPE);
|
||||
dir = (typ == POSITION_TYPE_BUY) ? 0 : 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CloseOurPositions(const string s, const int magic)
|
||||
{
|
||||
bool ok = true;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != s)
|
||||
continue;
|
||||
if((int)PositionGetInteger(POSITION_MAGIC) != magic)
|
||||
continue;
|
||||
if(!g_trade.PositionClose(ticket))
|
||||
ok = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
g_sym = (StringLen(InpSymbol) == 0) ? _Symbol : InpSymbol;
|
||||
if(!SymbolSelect(g_sym, true))
|
||||
{
|
||||
Print("SymbolSelect failed: ", g_sym);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
g_trade.SetExpertMagicNumber(InpMagic);
|
||||
g_trade.SetDeviationInPoints(InpSlippagePoints);
|
||||
g_trade.SetTypeFilling(ResolveFilling(g_sym));
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
if(!IsNewSignalBar())
|
||||
return;
|
||||
|
||||
if(Bars(g_sym, InpSignalTF) < 5 || Bars(g_sym, InpConfirmTF) < 5)
|
||||
return;
|
||||
|
||||
if(!SpreadOk(g_sym))
|
||||
return;
|
||||
|
||||
const bool longSetup = ValidatedLongSetup(g_sym);
|
||||
const bool shortSetup = ValidatedShortSetup(g_sym);
|
||||
|
||||
int dir = -1;
|
||||
bool has = HasOurPosition(g_sym, InpMagic, dir);
|
||||
|
||||
if(has)
|
||||
{
|
||||
if(dir == 0)
|
||||
{
|
||||
bool adverse = false;
|
||||
if(InpCloseOnAdversePattern)
|
||||
{
|
||||
if(BearishPatternBar(g_sym, InpSignalTF, 1) || BearishPatternBar(g_sym, InpConfirmTF, 1))
|
||||
adverse = true;
|
||||
}
|
||||
const bool reverse = (InpCloseOnReverseSignal && shortSetup);
|
||||
if(adverse || reverse)
|
||||
CloseOurPositions(g_sym, InpMagic);
|
||||
}
|
||||
else if(dir == 1)
|
||||
{
|
||||
bool adverse = false;
|
||||
if(InpCloseOnAdversePattern)
|
||||
{
|
||||
if(BullishPatternBar(g_sym, InpSignalTF, 1) || BullishPatternBar(g_sym, InpConfirmTF, 1))
|
||||
adverse = true;
|
||||
}
|
||||
const bool reverse = (InpCloseOnReverseSignal && longSetup);
|
||||
if(adverse || reverse)
|
||||
CloseOurPositions(g_sym, InpMagic);
|
||||
}
|
||||
}
|
||||
|
||||
has = HasOurPosition(g_sym, InpMagic, dir);
|
||||
|
||||
if(InpOnlyOnePosition && has)
|
||||
return;
|
||||
|
||||
if(longSetup && !shortSetup)
|
||||
{
|
||||
const double ask = SymbolInfoDouble(g_sym, SYMBOL_ASK);
|
||||
g_trade.Buy(InpLots, g_sym, ask, 0.0, 0.0, "CandlePattern long");
|
||||
}
|
||||
else if(shortSetup && !longSetup)
|
||||
{
|
||||
const double bid = SymbolInfoDouble(g_sym, SYMBOL_BID);
|
||||
g_trade.Sell(InpLots, g_sym, bid, 0.0, 0.0, "CandlePattern short");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,425 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| DerivativePlots.mq5 |
|
||||
//| Subwindow line plots for d1 / d2 / d3 — use with Derivative EA |
|
||||
//| Compile into MQL5\\Indicators\\ (same name). EA can ChartIndicatorAdd.|
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Lab"
|
||||
#property link ""
|
||||
#property version "1.10"
|
||||
#property indicator_separate_window
|
||||
#property indicator_buffers 3
|
||||
#property indicator_plots 3
|
||||
#property description "Plots d1 d2 d3 below chart. Match inputs to Derivative EA."
|
||||
|
||||
#property indicator_label1 "d1 velocity"
|
||||
#property indicator_type1 DRAW_LINE
|
||||
#property indicator_color1 clrDodgerBlue
|
||||
#property indicator_width1 1
|
||||
|
||||
#property indicator_label2 "d2 acceleration"
|
||||
#property indicator_type2 DRAW_LINE
|
||||
#property indicator_color2 clrOrange
|
||||
#property indicator_width2 1
|
||||
|
||||
#property indicator_label3 "d3 jerk"
|
||||
#property indicator_type3 DRAW_LINE
|
||||
#property indicator_color3 clrMagenta
|
||||
#property indicator_width3 1
|
||||
|
||||
enum ENUM_DERIVATIVE_VIEW
|
||||
{
|
||||
DERIVATIVE_ALL = 0,
|
||||
DERIVATIVE_LEVEL_1 = 1,
|
||||
DERIVATIVE_LEVEL_2 = 2,
|
||||
DERIVATIVE_LEVEL_3 = 3
|
||||
};
|
||||
|
||||
input group "=== Source ==="
|
||||
input ENUM_APPLIED_PRICE InpAppliedPrice = PRICE_CLOSE;
|
||||
|
||||
input group "=== Layout ==="
|
||||
input ENUM_DERIVATIVE_VIEW InpWhichDerivative = DERIVATIVE_ALL; // Single-line modes clear other buffers to EMPTY_VALUE so Y-scale matches the visible line
|
||||
input bool InpUnifyPlotYScale = true; // Scale d2,d3 for comparable magnitude when normalized (shared subwindow)
|
||||
|
||||
input group "=== Calculus ==="
|
||||
input int InpDiffStep = 1;
|
||||
input bool InpNormalizePoints = true;
|
||||
|
||||
input group "=== Smoothing ==="
|
||||
input int InpSmoothPeriod = 0;
|
||||
|
||||
input group "=== Status ==="
|
||||
input bool InpShowValueBanner = true; // Text label; short name is DERIV_ALL / DERIV_d1 / DERIV_d2 / DERIV_d3 for ChartWindowFind
|
||||
|
||||
input group "=== Debug (Experts / Journal) ==="
|
||||
input bool InpDebugTrace = false; // Print diagnostics to Experts tab
|
||||
input bool InpDebugLogEveryCalculate = false; // Log every OnCalculate (very verbose)
|
||||
|
||||
double ExtD1[];
|
||||
double ExtD2[];
|
||||
double ExtD3[];
|
||||
|
||||
string g_deriv_chart_title = "DERIV_ALL";
|
||||
string g_deriv_stat_obj = "DerivPV_ALL";
|
||||
|
||||
void SetupDerivIdentity()
|
||||
{
|
||||
switch(InpWhichDerivative)
|
||||
{
|
||||
case DERIVATIVE_ALL:
|
||||
g_deriv_chart_title = "DERIV_ALL";
|
||||
g_deriv_stat_obj = "DerivPV_ALL";
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_1:
|
||||
g_deriv_chart_title = "DERIV_d1";
|
||||
g_deriv_stat_obj = "DerivPV_d1";
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_2:
|
||||
g_deriv_chart_title = "DERIV_d2";
|
||||
g_deriv_stat_obj = "DerivPV_d2";
|
||||
break;
|
||||
default:
|
||||
g_deriv_chart_title = "DERIV_d3";
|
||||
g_deriv_stat_obj = "DerivPV_d3";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// OnCalculate passes OHLC with index 0 = oldest bar (non-series). Do not ArraySetAsSeries() those arrays.
|
||||
|
||||
double AppliedPriceRowNs(const int pos, const double &open[], const double &high[],
|
||||
const double &low[], const double &close[])
|
||||
{
|
||||
switch(InpAppliedPrice)
|
||||
{
|
||||
case PRICE_OPEN: return open[pos];
|
||||
case PRICE_HIGH: return high[pos];
|
||||
case PRICE_LOW: return low[pos];
|
||||
case PRICE_CLOSE: return close[pos];
|
||||
case PRICE_MEDIAN: return (high[pos] + low[pos]) * 0.5;
|
||||
case PRICE_TYPICAL: return (high[pos] + low[pos] + close[pos]) / 3.0;
|
||||
case PRICE_WEIGHTED: return (high[pos] + low[pos] + close[pos] + close[pos]) / 4.0;
|
||||
default: return close[pos];
|
||||
}
|
||||
}
|
||||
|
||||
void SmoothPriceArrayNs(const int total, const double &src[], double &dst[])
|
||||
{
|
||||
ArrayResize(dst, total);
|
||||
const int p = InpSmoothPeriod;
|
||||
if(p <= 1)
|
||||
{
|
||||
ArrayCopy(dst, src);
|
||||
return;
|
||||
}
|
||||
const double alpha = 2.0 / (p + 1.0);
|
||||
dst[0] = src[0];
|
||||
for(int pos = 1; pos < total; pos++)
|
||||
dst[pos] = alpha * src[pos] + (1.0 - alpha) * dst[pos - 1];
|
||||
}
|
||||
|
||||
double SrcNs(const int pos, const bool useSmooth, const double &smooth[], const double &raw[])
|
||||
{
|
||||
return useSmooth ? smooth[pos] : raw[pos];
|
||||
}
|
||||
|
||||
double DerivativeScalePts()
|
||||
{
|
||||
double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
if(pt <= 0.0 || !MathIsValidNumber(pt))
|
||||
pt = _Point;
|
||||
if(!InpNormalizePoints)
|
||||
return 1.0;
|
||||
if(pt <= 0.0)
|
||||
return 1.0;
|
||||
return pt;
|
||||
}
|
||||
|
||||
void DerivPlotsTrace(const int rates_total, const int prev_calculated,
|
||||
const int h, const int min_bars, const double scale, const bool useSmooth,
|
||||
const double &close[], const double &WorkNs[], const datetime &time[])
|
||||
{
|
||||
if(!InpDebugTrace)
|
||||
return;
|
||||
|
||||
static int s_call = 0;
|
||||
s_call++;
|
||||
|
||||
const int newest = rates_total - 1;
|
||||
const datetime barOpen = time[newest];
|
||||
|
||||
static datetime s_prevBarOpen = 0;
|
||||
const bool isNewBarTime = (barOpen != s_prevBarOpen);
|
||||
if(isNewBarTime)
|
||||
s_prevBarOpen = barOpen;
|
||||
|
||||
const bool fullRecalc = (prev_calculated == 0);
|
||||
|
||||
if(InpDebugLogEveryCalculate)
|
||||
{
|
||||
PrintFormat("DERIV_PLOTS #%d prev_calc=%d rates=%d bar=%s | d1[0]=%.8g d2[0]=%.8g d3[0]=%.8g",
|
||||
s_call, prev_calculated, rates_total, TimeToString(barOpen, TIME_DATE | TIME_MINUTES),
|
||||
ExtD1[0], ExtD2[0], ExtD3[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if(fullRecalc)
|
||||
{
|
||||
const double pt = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
const double rawStep = (newest >= h) ? (WorkNs[newest] - WorkNs[newest - h]) : 0.0;
|
||||
PrintFormat("DERIV_PLOTS FULL_CALC #%d sym=%s rates=%d prev_calc=%d h=%d min_need=%d smooth=%s which=%d",
|
||||
s_call, _Symbol, rates_total, prev_calculated, h, min_bars,
|
||||
useSmooth ? "on" : "off", (int)InpWhichDerivative);
|
||||
PrintFormat(" scale=%.12g normalize=%s SYPOINT=%.12g _Point=%.12g SYM_DIGITS=%d",
|
||||
scale, InpNormalizePoints ? "on" : "off", pt, _Point,
|
||||
(int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS));
|
||||
PrintFormat(" close[oldest]=%.8f close[newest]=%.8f rawStep(newest..newest-h)=%.8f",
|
||||
close[0], close[newest], rawStep);
|
||||
PrintFormat(" series buf [0]=current bar: d1=%.8g d2=%.8g d3=%.8g (EMPTY_VALUE=%.8g)",
|
||||
ExtD1[0], ExtD2[0], ExtD3[0], EMPTY_VALUE);
|
||||
}
|
||||
else if(isNewBarTime)
|
||||
{
|
||||
PrintFormat("DERIV_PLOTS BAR %s rates=%d prev_calc=%d | d1[0]=%.8g d2[0]=%.8g d3[0]=%.8g",
|
||||
TimeToString(barOpen, TIME_DATE | TIME_MINUTES), rates_total, prev_calculated,
|
||||
ExtD1[0], ExtD2[0], ExtD3[0]);
|
||||
}
|
||||
}
|
||||
|
||||
string FormatPlotVal(const double v)
|
||||
{
|
||||
if(v == EMPTY_VALUE || !MathIsValidNumber(v))
|
||||
return "—";
|
||||
return DoubleToString(v, 4);
|
||||
}
|
||||
|
||||
void UpdateValueBanner(const int rates_total)
|
||||
{
|
||||
if(!InpShowValueBanner || rates_total < 1)
|
||||
return;
|
||||
|
||||
string txt = "";
|
||||
switch(InpWhichDerivative)
|
||||
{
|
||||
case DERIVATIVE_ALL:
|
||||
txt = StringFormat("d1=%s d2=%s d3=%s (h=%d sm=%d%s)",
|
||||
FormatPlotVal(ExtD1[0]), FormatPlotVal(ExtD2[0]), FormatPlotVal(ExtD3[0]),
|
||||
InpDiffStep, InpSmoothPeriod, InpUnifyPlotYScale ? " unifyY" : "");
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_1:
|
||||
txt = StringFormat("d1=%s", FormatPlotVal(ExtD1[0]));
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_2:
|
||||
txt = StringFormat("d2=%s", FormatPlotVal(ExtD2[0]));
|
||||
break;
|
||||
default:
|
||||
txt = StringFormat("d3=%s", FormatPlotVal(ExtD3[0]));
|
||||
break;
|
||||
}
|
||||
|
||||
IndicatorSetString(INDICATOR_SHORTNAME, g_deriv_chart_title);
|
||||
|
||||
const int sub = ChartWindowFind(0, g_deriv_chart_title);
|
||||
if(sub < 0)
|
||||
return;
|
||||
|
||||
if(ObjectFind(0, g_deriv_stat_obj) < 0)
|
||||
{
|
||||
if(!ObjectCreate(0, g_deriv_stat_obj, OBJ_LABEL, sub, 0, 0))
|
||||
return;
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_CORNER, CORNER_LEFT_UPPER);
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_XDISTANCE, 6);
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_YDISTANCE, 16);
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_COLOR, clrSilver);
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_FONTSIZE, 9);
|
||||
ObjectSetString(0, g_deriv_stat_obj, OBJPROP_FONT, "Consolas");
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_SELECTABLE, false);
|
||||
ObjectSetInteger(0, g_deriv_stat_obj, OBJPROP_HIDDEN, true);
|
||||
}
|
||||
ObjectSetString(0, g_deriv_stat_obj, OBJPROP_TEXT, txt);
|
||||
}
|
||||
|
||||
// Hide unused buffers from autoscale: DRAW_NONE plots can still skew separate-window limits if buffers hold numbers.
|
||||
void MaskBuffersForDerivativeView()
|
||||
{
|
||||
switch(InpWhichDerivative)
|
||||
{
|
||||
case DERIVATIVE_ALL:
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_1:
|
||||
ArrayInitialize(ExtD2, EMPTY_VALUE);
|
||||
ArrayInitialize(ExtD3, EMPTY_VALUE);
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_2:
|
||||
ArrayInitialize(ExtD1, EMPTY_VALUE);
|
||||
ArrayInitialize(ExtD3, EMPTY_VALUE);
|
||||
break;
|
||||
default:
|
||||
ArrayInitialize(ExtD1, EMPTY_VALUE);
|
||||
ArrayInitialize(ExtD2, EMPTY_VALUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyDerivativeViewMode()
|
||||
{
|
||||
switch(InpWhichDerivative)
|
||||
{
|
||||
case DERIVATIVE_ALL:
|
||||
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
|
||||
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);
|
||||
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
|
||||
PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrDodgerBlue);
|
||||
PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrOrange);
|
||||
PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrMagenta);
|
||||
PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
|
||||
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 3);
|
||||
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 3);
|
||||
PlotIndexSetInteger(0, PLOT_LINE_STYLE, STYLE_SOLID);
|
||||
PlotIndexSetInteger(1, PLOT_LINE_STYLE, STYLE_SOLID);
|
||||
PlotIndexSetInteger(2, PLOT_LINE_STYLE, STYLE_SOLID);
|
||||
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||||
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||||
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_1:
|
||||
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
|
||||
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE);
|
||||
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
|
||||
PlotIndexSetInteger(0, PLOT_LINE_COLOR, clrDodgerBlue);
|
||||
PlotIndexSetInteger(0, PLOT_LINE_WIDTH, 2);
|
||||
PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||||
break;
|
||||
case DERIVATIVE_LEVEL_2:
|
||||
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
|
||||
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_LINE);
|
||||
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_NONE);
|
||||
PlotIndexSetInteger(1, PLOT_LINE_COLOR, clrOrange);
|
||||
PlotIndexSetInteger(1, PLOT_LINE_WIDTH, 3);
|
||||
PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||||
break;
|
||||
default:
|
||||
PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_NONE);
|
||||
PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_NONE);
|
||||
PlotIndexSetInteger(2, PLOT_DRAW_TYPE, DRAW_LINE);
|
||||
PlotIndexSetInteger(2, PLOT_LINE_COLOR, clrMagenta);
|
||||
PlotIndexSetInteger(2, PLOT_LINE_WIDTH, 3);
|
||||
PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
SetIndexBuffer(0, ExtD1, INDICATOR_DATA);
|
||||
SetIndexBuffer(1, ExtD2, INDICATOR_DATA);
|
||||
SetIndexBuffer(2, ExtD3, INDICATOR_DATA);
|
||||
SetupDerivIdentity();
|
||||
ApplyDerivativeViewMode();
|
||||
IndicatorSetString(INDICATOR_SHORTNAME, g_deriv_chart_title);
|
||||
const int dig = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
|
||||
IndicatorSetInteger(INDICATOR_DIGITS, MathMax(6, dig));
|
||||
if(InpDebugTrace)
|
||||
PrintFormat("DERIV_PLOTS INIT sym=%s applied=%s h=%d sm=%d norm=%s dbg_every_calc=%s",
|
||||
_Symbol, EnumToString(InpAppliedPrice), InpDiffStep, InpSmoothPeriod,
|
||||
InpNormalizePoints ? "on" : "off", InpDebugLogEveryCalculate ? "on" : "off");
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
ObjectDelete(0, g_deriv_stat_obj);
|
||||
}
|
||||
|
||||
int OnCalculate(const int rates_total,
|
||||
const int prev_calculated,
|
||||
const datetime &time[],
|
||||
const double &open[],
|
||||
const double &high[],
|
||||
const double &low[],
|
||||
const double &close[],
|
||||
const long &tick_volume[],
|
||||
const long &volume[],
|
||||
const int &spread[])
|
||||
{
|
||||
const int h = MathMax(InpDiffStep, 1);
|
||||
const int min_bars = 3 * h + 2;
|
||||
|
||||
ApplyDerivativeViewMode();
|
||||
|
||||
ArrayResize(ExtD1, rates_total);
|
||||
ArrayResize(ExtD2, rates_total);
|
||||
ArrayResize(ExtD3, rates_total);
|
||||
ArraySetAsSeries(ExtD1, true);
|
||||
ArraySetAsSeries(ExtD2, true);
|
||||
ArraySetAsSeries(ExtD3, true);
|
||||
ArrayInitialize(ExtD1, EMPTY_VALUE);
|
||||
ArrayInitialize(ExtD2, EMPTY_VALUE);
|
||||
ArrayInitialize(ExtD3, EMPTY_VALUE);
|
||||
|
||||
if(rates_total < min_bars)
|
||||
{
|
||||
if(InpDebugTrace)
|
||||
PrintFormat("DERIV_PLOTS SHORT_HISTORY sym=%s rates=%d need=%d (3*h+2, h=%d) — buffers left EMPTY",
|
||||
_Symbol, rates_total, min_bars, h);
|
||||
return rates_total;
|
||||
}
|
||||
|
||||
double WorkNs[];
|
||||
ArrayResize(WorkNs, rates_total);
|
||||
for(int pos = 0; pos < rates_total; pos++)
|
||||
WorkNs[pos] = AppliedPriceRowNs(pos, open, high, low, close);
|
||||
|
||||
static double SmoothNs[];
|
||||
SmoothPriceArrayNs(rates_total, WorkNs, SmoothNs);
|
||||
|
||||
const bool useSmooth = (InpSmoothPeriod > 1);
|
||||
const double scale = DerivativeScalePts();
|
||||
|
||||
// Bar index pos: 0 = oldest, rates_total-1 = newest. Map to series buffer si = rates_total - 1 - pos (0 = current bar).
|
||||
const double hs = (double)h * scale;
|
||||
const bool unify = InpUnifyPlotYScale;
|
||||
|
||||
for(int pos = h; pos < rates_total; pos++)
|
||||
{
|
||||
const double d1 = (SrcNs(pos, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
|
||||
const int si = rates_total - 1 - pos;
|
||||
ExtD1[si] = d1;
|
||||
}
|
||||
|
||||
for(int pos = 2 * h; pos < rates_total; pos++)
|
||||
{
|
||||
const double d1_pos = (SrcNs(pos, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
|
||||
const double d1_pm = (SrcNs(pos - h, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - 2 * h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
|
||||
double d2 = (d1_pos - d1_pm) / ((double)h * scale);
|
||||
if(unify)
|
||||
d2 *= hs;
|
||||
const int si = rates_total - 1 - pos;
|
||||
ExtD2[si] = d2;
|
||||
}
|
||||
|
||||
for(int pos = 3 * h; pos < rates_total; pos++)
|
||||
{
|
||||
const double d1_pos = (SrcNs(pos, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
|
||||
const double d1_pm = (SrcNs(pos - h, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - 2 * h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
|
||||
const double d1_pm2 = (SrcNs(pos - 2 * h, useSmooth, SmoothNs, WorkNs) - SrcNs(pos - 3 * h, useSmooth, SmoothNs, WorkNs)) / ((double)h * scale);
|
||||
const double d2_pos = (d1_pos - d1_pm) / ((double)h * scale);
|
||||
const double d2_pm = (d1_pm - d1_pm2) / ((double)h * scale);
|
||||
double d3 = (d2_pos - d2_pm) / ((double)h * scale);
|
||||
if(unify)
|
||||
d3 *= hs * hs;
|
||||
const int si = rates_total - 1 - pos;
|
||||
ExtD3[si] = d3;
|
||||
}
|
||||
|
||||
MaskBuffersForDerivativeView();
|
||||
|
||||
DerivPlotsTrace(rates_total, prev_calculated, h, min_bars, scale, useSmooth, close, WorkNs, time);
|
||||
|
||||
UpdateValueBanner(rates_total);
|
||||
|
||||
return rates_total;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,383 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TFXNZDUSD.mq5 |
|
||||
//| NZDUSD: HTF directional bias + intraday bearish→bullish shift |
|
||||
//| Mirrors a reactive workflow: higher TFs for bias (D1/W1), |
|
||||
//| lower TFs (H4–M15) for confirmation — long bias / pullback / |
|
||||
//| reclaim entry. Not predictive; signals on closed bars. |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Lab"
|
||||
#property link ""
|
||||
#property version "1.01"
|
||||
#property description "NZDUSD long-bias EA: D1/W1 trend filter, intraday EMA cross after pullback streak, ATR risk."
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input group "=== Symbol ==="
|
||||
input string InpSymbol = "NZDUSD"; // Spot FX symbol (broker-specific)
|
||||
|
||||
input group "=== Timeframes (thesis) ==="
|
||||
input ENUM_TIMEFRAMES InpBiasTF = PERIOD_D1; // Directional bias (monthly/weekly/daily idea → D1 default)
|
||||
input ENUM_TIMEFRAMES InpHigherBiasTF = PERIOD_W1; // Optional second bias filter
|
||||
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_H4; // Intraday environment shift (H4 or lower)
|
||||
|
||||
input group "=== HTF bias (long-only, reactive) ==="
|
||||
input bool InpUseWeeklyBias = true; // Require W1 close > W1 EMA
|
||||
input int InpBiasEmaPeriod = 50; // EMA period on bias TFs
|
||||
input bool InpAllowCounterBias = false; // If false, skip longs when D1 close < D1 EMA
|
||||
|
||||
input group "=== Intraday shift (bearish → bullish) ==="
|
||||
input int InpFastEma = 8;
|
||||
input int InpSlowEma = 21;
|
||||
input int InpMinBearishBars = 3; // Min consecutive bars with fast EMA < slow before cross-up
|
||||
input bool InpRequireBullBody = true; // Bullish closed candle on cross bar
|
||||
|
||||
input group "=== Risk ==="
|
||||
input double InpLots = 0.10;
|
||||
input int InpMagic = 926001;
|
||||
input int InpSlippagePoints = 20;
|
||||
input int InpMaxSpreadPoints = 40;
|
||||
input bool InpUseAtrStops = true;
|
||||
input int InpAtrPeriod = 14;
|
||||
input double InpSlAtrMult = 1.5;
|
||||
input double InpTpAtrMult = 2.5;
|
||||
input double InpMinStopPoints = 50;
|
||||
input int InpMaxPositions = 1;
|
||||
|
||||
input group "=== Session (optional) ==="
|
||||
input bool InpUseSessionFilter = false;
|
||||
input int InpSessionStartHour = 7; // Server hour start
|
||||
input int InpSessionEndHour = 20; // Server hour end (exclusive if cross midnight handled below)
|
||||
|
||||
CTrade g_trade;
|
||||
|
||||
int g_atrSig = INVALID_HANDLE;
|
||||
int g_emaBiasD1 = INVALID_HANDLE;
|
||||
int g_emaBiasW1 = INVALID_HANDLE;
|
||||
int g_emaFastSig = INVALID_HANDLE;
|
||||
int g_emaSlowSig = INVALID_HANDLE;
|
||||
|
||||
/// Effective TFs after sanity check (genetic optimizers often pass invalid ENUM integers).
|
||||
ENUM_TIMEFRAMES g_effBiasTF = PERIOD_D1;
|
||||
ENUM_TIMEFRAMES g_effHigherBiasTF = PERIOD_W1;
|
||||
ENUM_TIMEFRAMES g_effSignalTF = PERIOD_H4;
|
||||
|
||||
datetime g_lastSignalBar = 0;
|
||||
|
||||
// Maps garbage timeframe integers from optimization to nearest supported standard period.
|
||||
ENUM_TIMEFRAMES NearestStandardTf(const ENUM_TIMEFRAMES raw)
|
||||
{
|
||||
if(PeriodSeconds(raw) > 0)
|
||||
return raw;
|
||||
|
||||
const ENUM_TIMEFRAMES cand[] =
|
||||
{
|
||||
PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1
|
||||
};
|
||||
const long r = (long)raw;
|
||||
ENUM_TIMEFRAMES best = PERIOD_H4;
|
||||
long bestDist = -1;
|
||||
for(int i = 0; i < ArraySize(cand); i++)
|
||||
{
|
||||
if(PeriodSeconds(cand[i]) <= 0)
|
||||
continue;
|
||||
const long diff = r - (long)cand[i];
|
||||
const long d = (diff >= 0 ? diff : -diff);
|
||||
if(bestDist < 0 || d < bestDist)
|
||||
{
|
||||
bestDist = d;
|
||||
best = cand[i];
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
string WorkSymbol()
|
||||
{
|
||||
string s = InpSymbol;
|
||||
StringTrimLeft(s);
|
||||
StringTrimRight(s);
|
||||
// .set files sometimes concatenate optimization payload into string inputs (e.g. "NZDUSD||0||...")
|
||||
const int bar = StringFind(s, "|");
|
||||
if(bar >= 0)
|
||||
s = StringSubstr(s, 0, bar);
|
||||
StringTrimRight(s);
|
||||
return (StringLen(s) > 0 ? s : _Symbol);
|
||||
}
|
||||
|
||||
bool SessionOk()
|
||||
{
|
||||
if(!InpUseSessionFilter)
|
||||
return true;
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
int h = dt.hour;
|
||||
if(InpSessionStartHour <= InpSessionEndHour)
|
||||
return (h >= InpSessionStartHour && h < InpSessionEndHour);
|
||||
return (h >= InpSessionStartHour || h < InpSessionEndHour);
|
||||
}
|
||||
|
||||
double Buf1(const int handle, const int shift)
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
if(CopyBuffer(handle, 0, shift, 1, b) != 1)
|
||||
return 0.0;
|
||||
return b[0];
|
||||
}
|
||||
|
||||
bool CopyClose(const string sym, const ENUM_TIMEFRAMES tf, const int shift, double &out)
|
||||
{
|
||||
double c[];
|
||||
ArraySetAsSeries(c, true);
|
||||
if(CopyClose(sym, tf, shift, 1, c) != 1)
|
||||
return false;
|
||||
out = c[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HtfLongBias(const string sym)
|
||||
{
|
||||
double cD1 = 0.0, eD1 = 0.0;
|
||||
if(!CopyClose(sym, g_effBiasTF, 1, cD1))
|
||||
return false;
|
||||
eD1 = Buf1(g_emaBiasD1, 1);
|
||||
if(eD1 <= 0.0)
|
||||
return false;
|
||||
if(!InpAllowCounterBias && cD1 <= eD1)
|
||||
return false;
|
||||
|
||||
if(InpUseWeeklyBias)
|
||||
{
|
||||
double cW1 = 0.0, eW1 = 0.0;
|
||||
if(!CopyClose(sym, g_effHigherBiasTF, 1, cW1))
|
||||
return false;
|
||||
eW1 = Buf1(g_emaBiasW1, 1);
|
||||
if(eW1 <= 0.0)
|
||||
return false;
|
||||
if(cW1 <= eW1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int CountConsecutiveBearishEma(const string sym, const int fromShift, const int maxLookback)
|
||||
{
|
||||
double f[], s[];
|
||||
ArraySetAsSeries(f, true);
|
||||
ArraySetAsSeries(s, true);
|
||||
int need = maxLookback + fromShift;
|
||||
if(CopyBuffer(g_emaFastSig, 0, 0, need, f) < need)
|
||||
return 0;
|
||||
if(CopyBuffer(g_emaSlowSig, 0, 0, need, s) < need)
|
||||
return 0;
|
||||
|
||||
int n = 0;
|
||||
for(int i = fromShift; i < fromShift + maxLookback; i++)
|
||||
{
|
||||
if(f[i] <= s[i])
|
||||
n++;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
bool BullishCrossOnLastClosedBar(const string sym)
|
||||
{
|
||||
double f1 = Buf1(g_emaFastSig, 1);
|
||||
double s1 = Buf1(g_emaSlowSig, 1);
|
||||
double f2 = Buf1(g_emaFastSig, 2);
|
||||
double s2 = Buf1(g_emaSlowSig, 2);
|
||||
if(f1 <= 0.0 || s1 <= 0.0 || f2 <= 0.0 || s2 <= 0.0)
|
||||
return false;
|
||||
|
||||
bool crossedUp = (f1 > s1 && f2 <= s2);
|
||||
if(!crossedUp)
|
||||
return false;
|
||||
|
||||
int bearStreak = CountConsecutiveBearishEma(sym, 2, 32);
|
||||
if(bearStreak < InpMinBearishBars)
|
||||
return false;
|
||||
|
||||
if(InpRequireBullBody)
|
||||
{
|
||||
MqlRates r[];
|
||||
ArraySetAsSeries(r, true);
|
||||
if(CopyRates(sym, g_effSignalTF, 1, 1, r) != 1)
|
||||
return false;
|
||||
if(r[0].close <= r[0].open)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
double NormalizeVolumeLots(const string sym, double lots)
|
||||
{
|
||||
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)
|
||||
lots = MathFloor(lots / step) * step;
|
||||
if(lots < minLot)
|
||||
lots = minLot;
|
||||
if(lots > maxLot)
|
||||
lots = maxLot;
|
||||
return lots;
|
||||
}
|
||||
|
||||
int CountOurPositions(const string sym)
|
||||
{
|
||||
int total = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if(ticket == 0 || !PositionSelectByTicket(ticket))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != sym)
|
||||
continue;
|
||||
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
bool SpreadOk(const string sym)
|
||||
{
|
||||
long spreadPts = SymbolInfoInteger(sym, SYMBOL_SPREAD);
|
||||
return ((double)spreadPts <= (double)InpMaxSpreadPoints);
|
||||
}
|
||||
|
||||
void ComputeStopsBuy(const string sym, const double entry, double &sl, double &tp)
|
||||
{
|
||||
double ptsSl = InpMinStopPoints;
|
||||
double ptsTp = InpMinStopPoints * 2.0;
|
||||
if(InpUseAtrStops && g_atrSig != INVALID_HANDLE)
|
||||
{
|
||||
double atr = Buf1(g_atrSig, 1);
|
||||
if(atr > 0.0)
|
||||
{
|
||||
double atrPts = atr / SymbolInfoDouble(sym, SYMBOL_POINT);
|
||||
ptsSl = MathMax(atrPts * InpSlAtrMult, InpMinStopPoints);
|
||||
ptsTp = MathMax(atrPts * InpTpAtrMult, InpMinStopPoints);
|
||||
}
|
||||
}
|
||||
double p = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||||
sl = entry - ptsSl * p;
|
||||
tp = entry + ptsTp * p;
|
||||
|
||||
long stopsLevel = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double minDist = (double)stopsLevel * p;
|
||||
if(minDist > 0.0)
|
||||
{
|
||||
if(entry - sl < minDist)
|
||||
sl = entry - minDist;
|
||||
if(tp - entry < minDist)
|
||||
tp = entry + minDist;
|
||||
}
|
||||
int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
|
||||
sl = NormalizeDouble(sl, dg);
|
||||
tp = NormalizeDouble(tp, dg);
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
string sym = WorkSymbol();
|
||||
if(!SymbolSelect(sym, true))
|
||||
{
|
||||
Print("TFXNZDUSD: symbol not available: ", sym);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
g_effBiasTF = NearestStandardTf(InpBiasTF);
|
||||
g_effHigherBiasTF = NearestStandardTf(InpHigherBiasTF);
|
||||
g_effSignalTF = NearestStandardTf(InpSignalTF);
|
||||
if(g_effBiasTF != InpBiasTF || g_effHigherBiasTF != InpHigherBiasTF || g_effSignalTF != InpSignalTF)
|
||||
Print("TFXNZDUSD: resolved TFs — bias ", EnumToString(g_effBiasTF), " (in ", (long)InpBiasTF, ")",
|
||||
" W1 ", EnumToString(g_effHigherBiasTF), " (in ", (long)InpHigherBiasTF, ")",
|
||||
" signal ", EnumToString(g_effSignalTF), " (in ", (long)InpSignalTF, ")");
|
||||
|
||||
if(InpBiasEmaPeriod < 1 || InpFastEma < 1 || InpSlowEma < 1 || InpAtrPeriod < 1)
|
||||
{
|
||||
Print("TFXNZDUSD: EMA/ATR period must be >= 1");
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
}
|
||||
|
||||
g_trade.SetExpertMagicNumber(InpMagic);
|
||||
g_trade.SetDeviationInPoints(InpSlippagePoints);
|
||||
g_trade.SetTypeFillingBySymbol(sym);
|
||||
|
||||
g_emaBiasD1 = iMA(sym, g_effBiasTF, InpBiasEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
g_emaBiasW1 = iMA(sym, g_effHigherBiasTF, InpBiasEmaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
g_emaFastSig = iMA(sym, g_effSignalTF, InpFastEma, 0, MODE_EMA, PRICE_CLOSE);
|
||||
g_emaSlowSig = iMA(sym, g_effSignalTF, InpSlowEma, 0, MODE_EMA, PRICE_CLOSE);
|
||||
g_atrSig = iATR(sym, g_effSignalTF, InpAtrPeriod);
|
||||
|
||||
if(g_emaBiasD1 == INVALID_HANDLE || g_emaFastSig == INVALID_HANDLE || g_emaSlowSig == INVALID_HANDLE ||
|
||||
g_atrSig == INVALID_HANDLE)
|
||||
{
|
||||
Print("TFXNZDUSD: indicator init failed — check InpBiasTF/InpHigherBiasTF/InpSignalTF & symbol history");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
if(InpUseWeeklyBias && g_emaBiasW1 == INVALID_HANDLE)
|
||||
{
|
||||
Print("TFXNZDUSD: W1 bias handle failed");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
Print("TFXNZDUSD: ", sym, " eff TFs: bias=", EnumToString(g_effBiasTF), " higher=", EnumToString(g_effHigherBiasTF),
|
||||
" signal=", EnumToString(g_effSignalTF));
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_emaBiasD1 != INVALID_HANDLE) IndicatorRelease(g_emaBiasD1);
|
||||
if(g_emaBiasW1 != INVALID_HANDLE) IndicatorRelease(g_emaBiasW1);
|
||||
if(g_emaFastSig != INVALID_HANDLE) IndicatorRelease(g_emaFastSig);
|
||||
if(g_emaSlowSig != INVALID_HANDLE) IndicatorRelease(g_emaSlowSig);
|
||||
if(g_atrSig != INVALID_HANDLE) IndicatorRelease(g_atrSig);
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
string sym = WorkSymbol();
|
||||
datetime barOpen = iTime(sym, g_effSignalTF, 0);
|
||||
if(barOpen == 0)
|
||||
return;
|
||||
if(barOpen == g_lastSignalBar)
|
||||
return;
|
||||
|
||||
datetime prevBar = iTime(sym, g_effSignalTF, 1);
|
||||
if(prevBar == 0)
|
||||
return;
|
||||
|
||||
g_lastSignalBar = barOpen;
|
||||
|
||||
if(!SessionOk())
|
||||
return;
|
||||
if(!SpreadOk(sym))
|
||||
return;
|
||||
|
||||
if(CountOurPositions(sym) >= InpMaxPositions)
|
||||
return;
|
||||
|
||||
if(!HtfLongBias(sym))
|
||||
return;
|
||||
|
||||
if(!BullishCrossOnLastClosedBar(sym))
|
||||
return;
|
||||
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(sym, tick))
|
||||
return;
|
||||
|
||||
double lots = NormalizeVolumeLots(sym, InpLots);
|
||||
double sl = 0.0, tp = 0.0;
|
||||
ComputeStopsBuy(sym, tick.ask, sl, tp);
|
||||
|
||||
if(!g_trade.Buy(lots, sym, tick.ask, sl, tp, "TFX NZDUSD shift"))
|
||||
Print("TFXNZDUSD Buy failed ret=", g_trade.ResultRetcode(), " ", g_trade.ResultRetcodeDescription());
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,42 @@
|
||||
; saved for genetic optimization — TFXNZDUSD.mq5 (Strategy Tester → Inputs → Load)
|
||||
; Repo format: Parameter=Value||Step||Min||Max||Optimize(Y/N)
|
||||
; ENUM_TIMEFRAMES: H1=16385, H4=16388, D1=16408, W1=32769
|
||||
; Do NOT optimize InpSignalTF as Min–Max integers — MT5 genetic samples invalid values (e.g. 16386)
|
||||
; between real enums and OnInit fails. Compare H1 vs H4 in separate runs, or rely on EA TF resolution.
|
||||
|
||||
; === Symbol ===
|
||||
; String inputs: use bare name OR Value||Value||Value||Value||N — never use 0 as middle field (MT5 may feed the whole line into the string).
|
||||
InpSymbol=NZDUSD
|
||||
|
||||
; === Timeframes (thesis) ===
|
||||
InpBiasTF=16408||0||16408||16408||N
|
||||
InpHigherBiasTF=32769||0||32769||32769||N
|
||||
InpSignalTF=16388||0||16388||16388||N
|
||||
|
||||
; === HTF bias (long-only, reactive) ===
|
||||
InpUseWeeklyBias=true||false||0||true||N
|
||||
InpBiasEmaPeriod=50||2||34||120||Y
|
||||
InpAllowCounterBias=false||false||0||true||N
|
||||
|
||||
; === Intraday shift (bearish → bullish) ===
|
||||
InpFastEma=8||1||5||34||Y
|
||||
InpSlowEma=21||2||15||55||Y
|
||||
InpMinBearishBars=3||1||2||10||Y
|
||||
InpRequireBullBody=true||false||0||true||N
|
||||
|
||||
; === Risk ===
|
||||
InpLots=0.1||0.01||0.1||0.1||N
|
||||
InpMagic=926001||0||926001||926001||N
|
||||
InpSlippagePoints=20||0||20||20||N
|
||||
InpMaxSpreadPoints=40||5||20||60||Y
|
||||
InpUseAtrStops=true||false||0||true||N
|
||||
InpAtrPeriod=14||1||7||28||Y
|
||||
InpSlAtrMult=1.5||0.1||1.0||3.5||Y
|
||||
InpTpAtrMult=2.5||0.2||1.5||5.0||Y
|
||||
InpMinStopPoints=50.0||5.0||30.0||120.0||Y
|
||||
InpMaxPositions=1||0||1||1||N
|
||||
|
||||
; === Session (optional) ===
|
||||
InpUseSessionFilter=false||false||0||true||N
|
||||
InpSessionStartHour=7||0||7||7||N
|
||||
InpSessionEndHour=20||0||20||20||N
|
||||
@@ -0,0 +1,367 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TFXXAUUSDScalper.mq5 |
|
||||
//| Gold (XAUUSD) Donchian breakout scalper — momentum / range |
|
||||
//| breakout style suited to impulse-or-consolidate dynamics. |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Lab"
|
||||
#property link ""
|
||||
#property version "1.00"
|
||||
#property description "Donchian channel breakout on XAUUSD; optional consolidation filter; percent-risk or fixed lots."
|
||||
|
||||
#include <Trade/Trade.mqh>
|
||||
|
||||
input group "=== Instrument ==="
|
||||
input string InpSymbol = "XAUUSD";
|
||||
|
||||
input group "=== Session ==="
|
||||
input ENUM_TIMEFRAMES InpSignalTF = PERIOD_M5;
|
||||
input bool InpUseSessionFilter = false;
|
||||
input int InpSessionStartHour = 7;
|
||||
input int InpSessionEndHour = 22;
|
||||
|
||||
input group "=== Donchian breakout ==="
|
||||
input int InpDonchianPeriod = 20; // Lookback for channel high/low (past bars exclude signal bar)
|
||||
input bool InpRequireFreshBreak = true; // Close[2] inside prior upper/lower band (no churn)
|
||||
input bool InpTradeLong = true;
|
||||
input bool InpTradeShort = true;
|
||||
|
||||
input group "=== Consolidation filter (horizontal → breakout) ==="
|
||||
input bool InpUseNarrowChannelFilter = false;
|
||||
input double InpMaxChannelWidthAtrMult = 3.0; // Upper-Lower <= this * ATR(shift 2)
|
||||
|
||||
input group "=== Stops & targets (Nick-style RR) ==="
|
||||
input int InpSlBufferPoints = 30; // Beyond opposite Donchian / structural low-high
|
||||
input double InpTpRiskReward = 2.0; // TP distance = RR * risk distance
|
||||
input bool InpUseMidStopFallback = false; // Optional tighter SL at channel mid (more aggressive)
|
||||
|
||||
input group "=== Risk ==="
|
||||
input bool InpUsePercentRisk = true;
|
||||
input double InpRiskPercent = 1.0; // % balance per trade (video example)
|
||||
input double InpFixedLots = 0.10;
|
||||
input int InpMagic = 928001;
|
||||
input int InpSlippagePoints = 50;
|
||||
input int InpMaxSpreadPoints = 60;
|
||||
input int InpMaxPositions = 1;
|
||||
|
||||
input group "=== Indicators ==="
|
||||
input int InpAtrPeriod = 14;
|
||||
|
||||
CTrade g_trade;
|
||||
|
||||
int g_atr = INVALID_HANDLE;
|
||||
datetime g_lastBar = 0;
|
||||
|
||||
string WorkSymbol()
|
||||
{
|
||||
string s = InpSymbol;
|
||||
StringTrimLeft(s);
|
||||
StringTrimRight(s);
|
||||
const int bar = StringFind(s, "|");
|
||||
if(bar >= 0)
|
||||
s = StringSubstr(s, 0, bar);
|
||||
StringTrimRight(s);
|
||||
return (StringLen(s) > 0 ? s : _Symbol);
|
||||
}
|
||||
|
||||
bool SessionOk()
|
||||
{
|
||||
if(!InpUseSessionFilter)
|
||||
return true;
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
const int h = dt.hour;
|
||||
if(InpSessionStartHour <= InpSessionEndHour)
|
||||
return (h >= InpSessionStartHour && h < InpSessionEndHour);
|
||||
return (h >= InpSessionStartHour || h < InpSessionEndHour);
|
||||
}
|
||||
|
||||
double DonchianUpper(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shiftAnchor)
|
||||
{
|
||||
if(period < 1)
|
||||
return 0.0;
|
||||
double mx = -DBL_MAX;
|
||||
for(int i = shiftAnchor + 1; i <= shiftAnchor + period; i++)
|
||||
{
|
||||
const double hi = iHigh(sym, tf, i);
|
||||
if(hi > mx)
|
||||
mx = hi;
|
||||
}
|
||||
return mx;
|
||||
}
|
||||
|
||||
double DonchianLower(const string sym, const ENUM_TIMEFRAMES tf, const int period, const int shiftAnchor)
|
||||
{
|
||||
if(period < 1)
|
||||
return 0.0;
|
||||
double mn = DBL_MAX;
|
||||
for(int i = shiftAnchor + 1; i <= shiftAnchor + period; i++)
|
||||
{
|
||||
const double lo = iLow(sym, tf, i);
|
||||
if(lo < mn)
|
||||
mn = lo;
|
||||
}
|
||||
return mn;
|
||||
}
|
||||
|
||||
double AtrAt(const int shift)
|
||||
{
|
||||
double b[];
|
||||
ArraySetAsSeries(b, true);
|
||||
if(g_atr == INVALID_HANDLE || CopyBuffer(g_atr, 0, shift, 1, b) != 1)
|
||||
return 0.0;
|
||||
return b[0];
|
||||
}
|
||||
|
||||
double NormalizeLots(const string sym, double lots)
|
||||
{
|
||||
double mn = SymbolInfoDouble(sym, SYMBOL_VOLUME_MIN);
|
||||
double mx = SymbolInfoDouble(sym, SYMBOL_VOLUME_MAX);
|
||||
double st = SymbolInfoDouble(sym, SYMBOL_VOLUME_STEP);
|
||||
if(st > 0.0)
|
||||
lots = MathFloor(lots / st) * st;
|
||||
if(lots < mn)
|
||||
lots = mn;
|
||||
if(lots > mx)
|
||||
lots = mx;
|
||||
return lots;
|
||||
}
|
||||
|
||||
bool MoneyPerLotAtSl(const string sym, const ENUM_ORDER_TYPE type, const double openPrice, const double slPrice, double &lossPerLot)
|
||||
{
|
||||
lossPerLot = 0.0;
|
||||
double p = 0.0;
|
||||
if(!OrderCalcProfit(type, sym, 1.0, openPrice, slPrice, p))
|
||||
return false;
|
||||
lossPerLot = MathAbs(p);
|
||||
return (lossPerLot > 0.0);
|
||||
}
|
||||
|
||||
double LotsFromPercentRisk(const string sym, const ENUM_ORDER_TYPE type, const double openPrice, const double slPrice)
|
||||
{
|
||||
double perLotLoss = 0.0;
|
||||
if(!MoneyPerLotAtSl(sym, type, openPrice, slPrice, perLotLoss))
|
||||
return InpFixedLots;
|
||||
|
||||
const double balance = AccountInfoDouble(ACCOUNT_BALANCE);
|
||||
const double riskMoney = balance * (InpRiskPercent / 100.0);
|
||||
if(riskMoney <= 0.0 || perLotLoss <= 0.0)
|
||||
return NormalizeLots(sym, InpFixedLots);
|
||||
|
||||
double lots = riskMoney / perLotLoss;
|
||||
return NormalizeLots(sym, lots);
|
||||
}
|
||||
|
||||
bool SpreadOk(const string sym)
|
||||
{
|
||||
const long sp = SymbolInfoInteger(sym, SYMBOL_SPREAD);
|
||||
return ((double)sp <= (double)InpMaxSpreadPoints);
|
||||
}
|
||||
|
||||
int CountMagicPositions(const string sym)
|
||||
{
|
||||
int n = 0;
|
||||
for(int i = PositionsTotal() - 1; i >= 0; i--)
|
||||
{
|
||||
const ulong t = PositionGetTicket(i);
|
||||
if(t == 0 || !PositionSelectByTicket(t))
|
||||
continue;
|
||||
if(PositionGetString(POSITION_SYMBOL) != sym)
|
||||
continue;
|
||||
if((int)PositionGetInteger(POSITION_MAGIC) != InpMagic)
|
||||
continue;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void BuildStopsBuy(const string sym, const double entry, const double upperD1, const double lowerD1,
|
||||
double &sl, double &tp)
|
||||
{
|
||||
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||||
const double buf = (double)InpSlBufferPoints * pt;
|
||||
double riskDist = entry - (lowerD1 - buf);
|
||||
sl = lowerD1 - buf;
|
||||
|
||||
if(InpUseMidStopFallback)
|
||||
{
|
||||
const double mid = (upperD1 + lowerD1) * 0.5;
|
||||
const double distMid = entry - mid;
|
||||
if(distMid > 0 && distMid < riskDist)
|
||||
{
|
||||
sl = mid - buf;
|
||||
riskDist = entry - sl;
|
||||
}
|
||||
}
|
||||
|
||||
const long lvl = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double minD = (double)lvl * pt;
|
||||
if(minD > 0.0 && entry - sl < minD)
|
||||
sl = entry - minD;
|
||||
|
||||
riskDist = entry - sl;
|
||||
tp = entry + riskDist * InpTpRiskReward;
|
||||
|
||||
if(minD > 0.0 && tp - entry < minD)
|
||||
tp = entry + minD;
|
||||
|
||||
const int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
|
||||
sl = NormalizeDouble(sl, dg);
|
||||
tp = NormalizeDouble(tp, dg);
|
||||
}
|
||||
|
||||
void BuildStopsSell(const string sym, const double entry, const double upperD1, const double lowerD1,
|
||||
double &sl, double &tp)
|
||||
{
|
||||
const double pt = SymbolInfoDouble(sym, SYMBOL_POINT);
|
||||
const double buf = (double)InpSlBufferPoints * pt;
|
||||
double riskDist = (upperD1 + buf) - entry;
|
||||
sl = upperD1 + buf;
|
||||
|
||||
if(InpUseMidStopFallback)
|
||||
{
|
||||
const double mid = (upperD1 + lowerD1) * 0.5;
|
||||
const double distMid = mid - entry;
|
||||
if(distMid > 0 && distMid < riskDist)
|
||||
{
|
||||
sl = mid + buf;
|
||||
riskDist = sl - entry;
|
||||
}
|
||||
}
|
||||
|
||||
const long lvl = SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
const double minD = (double)lvl * pt;
|
||||
if(minD > 0.0 && sl - entry < minD)
|
||||
sl = entry + minD;
|
||||
|
||||
riskDist = sl - entry;
|
||||
tp = entry - riskDist * InpTpRiskReward;
|
||||
|
||||
if(minD > 0.0 && entry - tp < minD)
|
||||
tp = entry - minD;
|
||||
|
||||
const int dg = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
|
||||
sl = NormalizeDouble(sl, dg);
|
||||
tp = NormalizeDouble(tp, dg);
|
||||
}
|
||||
|
||||
bool NarrowChannelOk(const string sym, const ENUM_TIMEFRAMES tf, const int period)
|
||||
{
|
||||
if(!InpUseNarrowChannelFilter)
|
||||
return true;
|
||||
const double up = DonchianUpper(sym, tf, period, 2);
|
||||
const double lo = DonchianLower(sym, tf, period, 2);
|
||||
const double atr = AtrAt(2);
|
||||
if(up <= 0 || lo <= 0 || atr <= 0)
|
||||
return false;
|
||||
const double width = up - lo;
|
||||
return (width <= atr * InpMaxChannelWidthAtrMult);
|
||||
}
|
||||
|
||||
int OnInit()
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
if(!SymbolSelect(sym, true))
|
||||
{
|
||||
Print("TFXXAUUSDScalper: symbol not available: ", sym);
|
||||
return INIT_FAILED;
|
||||
}
|
||||
if(InpDonchianPeriod < 2)
|
||||
{
|
||||
Print("TFXXAUUSDScalper: InpDonchianPeriod must be >= 2");
|
||||
return INIT_PARAMETERS_INCORRECT;
|
||||
}
|
||||
|
||||
g_trade.SetExpertMagicNumber(InpMagic);
|
||||
g_trade.SetDeviationInPoints(InpSlippagePoints);
|
||||
g_trade.SetTypeFillingBySymbol(sym);
|
||||
|
||||
g_atr = iATR(sym, InpSignalTF, InpAtrPeriod);
|
||||
if(g_atr == INVALID_HANDLE)
|
||||
{
|
||||
Print("TFXXAUUSDScalper: ATR init failed");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
Print("TFXXAUUSDScalper: ", sym, " ", EnumToString(InpSignalTF),
|
||||
" Donchian=", InpDonchianPeriod, " RR=", InpTpRiskReward,
|
||||
" risk%=", (InpUsePercentRisk ? DoubleToString(InpRiskPercent, 2) : "off"));
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
if(g_atr != INVALID_HANDLE)
|
||||
IndicatorRelease(g_atr);
|
||||
g_atr = INVALID_HANDLE;
|
||||
}
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
const string sym = WorkSymbol();
|
||||
const datetime t0 = iTime(sym, InpSignalTF, 0);
|
||||
if(t0 == 0 || t0 == g_lastBar)
|
||||
return;
|
||||
g_lastBar = t0;
|
||||
|
||||
if(!SessionOk() || !SpreadOk(sym))
|
||||
return;
|
||||
if(CountMagicPositions(sym) >= InpMaxPositions)
|
||||
return;
|
||||
|
||||
const int p = InpDonchianPeriod;
|
||||
const double c1 = iClose(sym, InpSignalTF, 1);
|
||||
const double c2 = iClose(sym, InpSignalTF, 2);
|
||||
if(c1 <= 0.0 || c2 <= 0.0)
|
||||
return;
|
||||
|
||||
const double up1 = DonchianUpper(sym, InpSignalTF, p, 1);
|
||||
const double lo1 = DonchianLower(sym, InpSignalTF, p, 1);
|
||||
const double up2 = DonchianUpper(sym, InpSignalTF, p, 2);
|
||||
const double lo2 = DonchianLower(sym, InpSignalTF, p, 2);
|
||||
|
||||
if(up1 <= 0 || lo1 <= 0 || up2 <= 0 || lo2 <= 0)
|
||||
return;
|
||||
|
||||
if(!NarrowChannelOk(sym, InpSignalTF, p))
|
||||
return;
|
||||
|
||||
bool longSig = InpTradeLong && (c1 > up1);
|
||||
bool shortSig = InpTradeShort && (c1 < lo1);
|
||||
|
||||
if(InpRequireFreshBreak)
|
||||
{
|
||||
longSig = longSig && (c2 <= up2);
|
||||
shortSig = shortSig && (c2 >= lo2);
|
||||
}
|
||||
|
||||
if(!longSig && !shortSig)
|
||||
return;
|
||||
|
||||
MqlTick tick;
|
||||
if(!SymbolInfoTick(sym, tick))
|
||||
return;
|
||||
|
||||
if(longSig && !shortSig)
|
||||
{
|
||||
double sl = 0.0, tp = 0.0;
|
||||
BuildStopsBuy(sym, tick.ask, up1, lo1, sl, tp);
|
||||
const double lots = InpUsePercentRisk ? LotsFromPercentRisk(sym, ORDER_TYPE_BUY, tick.ask, sl) : NormalizeLots(sym, InpFixedLots);
|
||||
if(!g_trade.Buy(lots, sym, tick.ask, sl, tp, "TFX Gold Donchian↑"))
|
||||
Print("Buy failed ", g_trade.ResultRetcode(), " ", g_trade.ResultRetcodeDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
if(shortSig && !longSig)
|
||||
{
|
||||
double sl = 0.0, tp = 0.0;
|
||||
BuildStopsSell(sym, tick.bid, up1, lo1, sl, tp);
|
||||
const double lots = InpUsePercentRisk ? LotsFromPercentRisk(sym, ORDER_TYPE_SELL, tick.bid, sl) : NormalizeLots(sym, InpFixedLots);
|
||||
if(!g_trade.Sell(lots, sym, tick.bid, sl, tp, "TFX Gold Donchian↓"))
|
||||
Print("Sell failed ", g_trade.ResultRetcode(), " ", g_trade.ResultRetcodeDescription());
|
||||
return;
|
||||
}
|
||||
|
||||
// Bothtrue — rare; skip to avoid ambiguous execution
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -0,0 +1,44 @@
|
||||
; TFXXAUUSDScalper.mq5 — Strategy Tester → Inputs → Load (Genetic optimization)
|
||||
; Format: Parameter=Value||Step||Min||Max||Optimize(Y/N)
|
||||
;
|
||||
; ENUM_TIMEFRAMES (MT5): M1=1 M5=5 M15=15 M30=30 H1=16385 H4=16388 D1=16408
|
||||
; Keep InpSignalTF fixed (single integer). Do not use Min–Max sweeps on enums — genetic
|
||||
; often tries invalid values between named periods and OnInit fails.
|
||||
;
|
||||
; Baseline aligned with Desktop 123.set (2026.05.08); magic corrected to 928001 (EA default).
|
||||
|
||||
; === Instrument ===
|
||||
InpSymbol=XAUUSD
|
||||
|
||||
; === Session ===
|
||||
InpSignalTF=5||0||5||5||N
|
||||
InpUseSessionFilter=false||false||0||true||N
|
||||
InpSessionStartHour=7||0||7||7||N
|
||||
InpSessionEndHour=22||0||22||22||N
|
||||
|
||||
; === Donchian breakout ===
|
||||
InpDonchianPeriod=20||2||10||80||Y
|
||||
InpRequireFreshBreak=true||false||0||true||N
|
||||
InpTradeLong=true||false||0||true||N
|
||||
InpTradeShort=true||false||0||true||N
|
||||
|
||||
; === Consolidation filter (horizontal → breakout) ===
|
||||
InpUseNarrowChannelFilter=false||false||0||true||N
|
||||
InpMaxChannelWidthAtrMult=3.0||0.5||1.5||6.0||Y
|
||||
|
||||
; === Stops & targets (Nick-style RR) ===
|
||||
InpSlBufferPoints=30||5||10||120||Y
|
||||
InpTpRiskReward=2.0||0.25||1.25||4.0||Y
|
||||
InpUseMidStopFallback=false||false||0||true||N
|
||||
|
||||
; === Risk ===
|
||||
InpUsePercentRisk=true||false||0||true||N
|
||||
InpRiskPercent=1.0||0.15||0.25||2.5||Y
|
||||
InpFixedLots=0.1||0.01||0.1||0.1||N
|
||||
InpMagic=928001||0||928001||928001||N
|
||||
InpSlippagePoints=50||0||50||50||N
|
||||
InpMaxSpreadPoints=60||5||20||100||Y
|
||||
InpMaxPositions=1||0||1||1||N
|
||||
|
||||
; === Indicators ===
|
||||
InpAtrPeriod=14||1||7||28||Y
|
||||
@@ -0,0 +1,458 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| ScoringTrade.mq5 |
|
||||
//| Generated by ChatGPT |
|
||||
//| |
|
||||
//+------------------------------------------------------------------+
|
||||
#property strict
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
// Input parameters
|
||||
input int MagicNumber = 42;
|
||||
input int scoreThreshold = 5200; // Score threshold for trade entry
|
||||
input int slopeThreshold = 93; // EMA slope threshold
|
||||
input double maxScore = 7900; // Max score value for clamping
|
||||
input int cooldownMinutes = 18; // Cooldown period in minutes (37 minutes)
|
||||
input int tradeCooldownMinutes = 24; // Trade debounce cooldown period (5 minutes)
|
||||
input ENUM_TIMEFRAMES emaTimeFrame = PERIOD_H1; // EMA Timeframe
|
||||
input double delayClampAbsolute = 1690;
|
||||
input int emaPeriod = 64; // EMA period
|
||||
input double crossOverStep = 950;
|
||||
input double slopeThresholdStep = 635;
|
||||
input double emaDistanceStep = 150;
|
||||
input double emaDecayStep = 0;
|
||||
input double decayMultiplier = 0.08; // Decay multiplier
|
||||
input double distanceThreshold = 28.5; // Set your distance threshold (adjust as necessary)
|
||||
input double atrMultiplier = 7.6; // Multiplier for dynamic SL and TP calculation
|
||||
input double TrailingStop = 5;
|
||||
input bool UseTrailingStop = true;
|
||||
input int maxCrossoverTrades = 4; // Maximum number of trades per crossover
|
||||
input double max_drawdown = 0.1; // Maximum drawdown percentage
|
||||
input bool resetCrossoverTradeOnDistance = false;
|
||||
input int resetCrossoverNumber = 0;
|
||||
input double minimumLotSize = 0.01;
|
||||
input int maxTimeInPosition = 9;
|
||||
input int tradeLengthThreshold = 98;
|
||||
input int reverseTP = 32;
|
||||
input int reverseLotSizeMultiplier = 15;
|
||||
input int secondaryPositionHoldTime = 32;
|
||||
// Global variables
|
||||
int emaHandle; // EMA handle
|
||||
double prevScore = 0; // Previous score
|
||||
double currentScore = 0; // Current score
|
||||
double emaPrevValue = 0; // Previous EMA value
|
||||
double emaCurrentValue = 0; // Current EMA value
|
||||
double emaSlope = 0; // EMA slope value
|
||||
CTrade trade; // Trading object
|
||||
|
||||
datetime lastCrossoverTime = 0; // Time of last crossover
|
||||
datetime lastTradeTime = 0; // Time of last trade
|
||||
int crossoverTradeCount = 0; // Count of trades after each crossover
|
||||
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit() {
|
||||
// Create EMA handle (e.g., 14-period EMA on the closing price)
|
||||
emaHandle = iMA(Symbol(), emaTimeFrame, emaPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if (emaHandle == INVALID_HANDLE) {
|
||||
Print("Failed to create EMA handle");
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason) {
|
||||
if (emaHandle != INVALID_HANDLE) {
|
||||
IndicatorRelease(emaHandle);
|
||||
emaHandle = INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick() {
|
||||
// Buffer to hold the EMA values
|
||||
double emaBuffer[];
|
||||
|
||||
// Get dynamic lot size based on current balance and max drawdown
|
||||
double lotSize = CalculateLotSize();
|
||||
|
||||
if(lotSize < minimumLotSize) {
|
||||
lotSize = minimumLotSize;
|
||||
}
|
||||
|
||||
// Get the current Ask and Bid prices
|
||||
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||||
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
||||
|
||||
// Copy the last 2 EMA values (current and previous)
|
||||
int copied = CopyBuffer(emaHandle, 0, 0, 2, emaBuffer);
|
||||
if (copied < 2) {
|
||||
Print("Failed to copy EMA values. Error code: ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current and previous EMA values
|
||||
emaPrevValue = emaBuffer[1]; // Previous EMA value (index 1)
|
||||
emaCurrentValue = emaBuffer[0]; // Current EMA value (index 0)
|
||||
|
||||
// Calculate the EMA slope (change in EMA values)
|
||||
emaSlope = - (emaCurrentValue - emaPrevValue) * 100;
|
||||
|
||||
// Check for price action crossover with EMA
|
||||
double closePrev = iClose(Symbol(), Period(), 1); // Close of previous bar
|
||||
double closeCurr = iClose(Symbol(), Period(), 0); // Close of current bar
|
||||
|
||||
// Check if enough time has passed for the cooldown (cooldownMinutes)
|
||||
if (TimeCurrent() - lastCrossoverTime >= cooldownMinutes * 60) {
|
||||
if (closePrev < emaPrevValue && closeCurr > emaCurrentValue) { // Bullish crossover
|
||||
Print("Bullish crossover");
|
||||
currentScore += crossOverStep;
|
||||
crossoverTradeCount = 0; // Reset trade count after new crossover
|
||||
lastCrossoverTime = TimeCurrent(); // Update the last crossover time
|
||||
}
|
||||
else if (closePrev > emaPrevValue && closeCurr < emaCurrentValue) { // Bearish crossover
|
||||
Print("Bearish crossover");
|
||||
currentScore -= crossOverStep;
|
||||
crossoverTradeCount = 0; // Reset trade count after new crossover
|
||||
lastCrossoverTime = TimeCurrent(); // Update the last crossover time
|
||||
}
|
||||
}
|
||||
|
||||
// Check EMA slope
|
||||
if (emaSlope > slopeThreshold) { // Positive slope
|
||||
currentScore += slopeThresholdStep;
|
||||
}
|
||||
else if (emaSlope < -slopeThreshold) { // Negative slope
|
||||
currentScore -= slopeThresholdStep;
|
||||
}
|
||||
else {
|
||||
if (MathAbs(currentScore) > delayClampAbsolute) {
|
||||
currentScore *= decayMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
if(UseTrailingStop) {
|
||||
ApplyTrailingStop();
|
||||
}
|
||||
|
||||
// Calculate distance to EMA and adjust score
|
||||
double priceToEmaDistance = closeCurr - emaCurrentValue; // Distance between the current price and the EMA
|
||||
|
||||
if (MathAbs(priceToEmaDistance) > distanceThreshold) {
|
||||
if (priceToEmaDistance > 0) { // Bullish (price above EMA)
|
||||
currentScore += emaDistanceStep;
|
||||
Print("Bullish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue);
|
||||
}
|
||||
else if (priceToEmaDistance < 0) { // Bearish (price below EMA)
|
||||
currentScore -= emaDistanceStep;
|
||||
Print("Bearish distance score added. Price: ", closeCurr, " EMA: ", emaCurrentValue);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (currentScore > 0) {
|
||||
currentScore -= emaDecayStep;
|
||||
}
|
||||
else {
|
||||
currentScore += emaDecayStep;
|
||||
}
|
||||
}
|
||||
|
||||
// Close all positions if score crosses zero
|
||||
if ((prevScore > 0 && currentScore <= 0) || (prevScore < 0 && currentScore >= 0)) {
|
||||
Close_Position_MN(MagicNumber);
|
||||
}
|
||||
|
||||
// Update the previous score
|
||||
prevScore = currentScore;
|
||||
|
||||
if (crossoverTradeCount > maxCrossoverTrades) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Debounce check: Ensure enough time has passed since the last trade
|
||||
if (TimeCurrent() - lastTradeTime >= tradeCooldownMinutes * 60) {
|
||||
// Calculate ATR (Average True Range) for stop loss calculation
|
||||
double atrArray[];
|
||||
int atrPeriod = 14; // ATR period (can be adjusted)
|
||||
int copied = CopyBuffer(iATR(Symbol(), Period(), atrPeriod), 0, 0, 1, atrArray);
|
||||
if (copied < 1) {
|
||||
Print("Failed to get ATR values. Error code: ", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current price (using Bid price)
|
||||
double currentPrice = Bid;
|
||||
// Get ATR value
|
||||
double atrValue = atrArray[0]; // Latest ATR value
|
||||
|
||||
// Get the minimum stop level and freeze level for the symbol
|
||||
long stopLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);
|
||||
long freezeLevel = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_FREEZE_LEVEL);
|
||||
|
||||
// Calculate the minimum stop loss in price units (converted from pips)
|
||||
double minStopLoss = stopLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
double minFreezeLevel = freezeLevel * SymbolInfoDouble(Symbol(), SYMBOL_POINT);
|
||||
|
||||
// Dynamic Stop Loss and Take Profit calculation based on ATR
|
||||
double dynamicSL = atrValue * atrMultiplier;
|
||||
double dynamicTP = atrValue * atrMultiplier;
|
||||
|
||||
// Adjust SL and TP if they are smaller than the minimum stop level
|
||||
dynamicSL = MathMax(dynamicSL, minStopLoss);
|
||||
dynamicTP = MathMax(dynamicTP, dynamicSL); // Ensure TP is at least the same as SL
|
||||
|
||||
// Trade logic based on the score
|
||||
if (currentScore > scoreThreshold) { // Buy signal
|
||||
if ((!PositionSelect(Symbol()) || PositionGetInteger(POSITION_MAGIC) != MagicNumber)
|
||||
&& crossoverTradeCount < maxCrossoverTrades) {
|
||||
Print("maxCrossover");
|
||||
Print(crossoverTradeCount);
|
||||
// Open buy position with dynamic SL and TP
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
if (trade.Buy(lotSize, Symbol(), currentPrice, Bid - dynamicSL, 0)) {
|
||||
Print("Buy order executed with score: ", currentScore);
|
||||
crossoverTradeCount++; // Increment trade count
|
||||
lastTradeTime = TimeCurrent(); // Update the last trade time
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentScore < -scoreThreshold) { // Sell signal
|
||||
if ((!PositionSelect(Symbol()) || PositionGetInteger(POSITION_MAGIC) != MagicNumber)
|
||||
&& crossoverTradeCount < maxCrossoverTrades) {
|
||||
Print("maxCrossover");
|
||||
Print(crossoverTradeCount);
|
||||
// Open sell position with dynamic SL and TP
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
if (trade.Sell(lotSize, Symbol(), currentPrice, Ask + dynamicSL, 0)) {
|
||||
Print("Sell order executed with score: ", currentScore);
|
||||
crossoverTradeCount++; // Increment trade count
|
||||
lastTradeTime = TimeCurrent(); // Update the last trade time
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Print("Trade skipped due to debounce: ", currentScore);
|
||||
}
|
||||
|
||||
// Check existing positions for profit and place reverse trade if needed
|
||||
CheckPositions();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing positions for profit and place reverse trade if needed |
|
||||
//+------------------------------------------------------------------+
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check existing positions for duration and place reverse trade if needed |
|
||||
//+------------------------------------------------------------------+
|
||||
void CheckPositions() {
|
||||
// Check if there are any open positions
|
||||
if (PositionsTotal() > 0) {
|
||||
// Check if there are exactly 2 open positions
|
||||
if (PositionsTotal() == 2) {
|
||||
for (int i = 0; i < PositionsTotal(); i++) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (PositionSelectByTicket(ticket)) {
|
||||
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
long tradeLength = (long)(TimeCurrent() - openTime);
|
||||
|
||||
// Check if the trade has been open for more than the secondaryPositionHoldTime
|
||||
if (tradeLength > secondaryPositionHoldTime * 60) { // Convert threshold to seconds
|
||||
// Close all positions
|
||||
CloseAllPositions();
|
||||
Print("All positions closed due to exceeding secondaryPositionHoldTime");
|
||||
return; // Exit the function after closing all positions
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (PositionsTotal() < 2) {
|
||||
for (int i = 0; i < PositionsTotal(); i++) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (PositionSelectByTicket(ticket)) {
|
||||
double profit = PositionGetDouble(POSITION_PROFIT);
|
||||
datetime openTime = (datetime)PositionGetInteger(POSITION_TIME);
|
||||
long tradeLength = (long)(TimeCurrent() - openTime);
|
||||
|
||||
// Check if the trade has been open for more than the tradeLengthThreshold
|
||||
if (tradeLength > tradeLengthThreshold * 60) { // Convert threshold to seconds
|
||||
double lotSize = PositionGetDouble(POSITION_VOLUME);
|
||||
double newLotSize = lotSize * reverseLotSizeMultiplier; // 10 times the original lot size
|
||||
|
||||
crossoverTradeCount = maxCrossoverTrades + 1;
|
||||
|
||||
// Place a reverse trade
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
if (trade.Sell(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_BID))) {
|
||||
Print("Reversal sell order executed with increased lot size");
|
||||
} else {
|
||||
Print("Failed to execute reversal sell order");
|
||||
}
|
||||
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
||||
trade.SetExpertMagicNumber(MagicNumber);
|
||||
if (trade.Buy(newLotSize, Symbol(), SymbolInfoDouble(Symbol(), SYMBOL_ASK))) {
|
||||
Print("Reversal buy order executed with increased lot size");
|
||||
} else {
|
||||
Print("Failed to execute reversal buy order");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close the trade if profit meets the take profit level
|
||||
if (profit >= reverseTP) {
|
||||
Close_Position_MN(MagicNumber);
|
||||
CloseAllPositions();
|
||||
}
|
||||
|
||||
// Check if there is only one position and its volume is lotSize * reverseLotSizeMultiplier
|
||||
if (PositionsTotal() == 1 && PositionGetDouble(POSITION_VOLUME) == minimumLotSize * reverseLotSizeMultiplier) {
|
||||
trade.PositionClose(ticket);
|
||||
Print("Single position with volume equal to lotSize * reverseLotSizeMultiplier closed");
|
||||
}
|
||||
|
||||
// Get the current Ask and Bid prices
|
||||
double Ask = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
|
||||
double Bid = SymbolInfoDouble(Symbol(), SYMBOL_BID);
|
||||
|
||||
|
||||
// Check if the double down trade is exited by stop loss
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetDouble(POSITION_SL) > 0 && Bid <= PositionGetDouble(POSITION_SL)) {
|
||||
// Close the original trade
|
||||
CloseOriginalTrade();
|
||||
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetDouble(POSITION_SL) > 0 && Ask >= PositionGetDouble(POSITION_SL)) {
|
||||
// Close the original trade
|
||||
CloseOriginalTrade();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Function to close the original trade
|
||||
void CloseOriginalTrade() {
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (PositionSelectByTicket(ticket)) {
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
||||
trade.PositionClose(ticket);
|
||||
Print("Original buy position closed due to double down stop loss.");
|
||||
} else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
||||
trade.PositionClose(ticket);
|
||||
Print("Original sell position closed due to double down stop loss.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
//| Function to close all positions |
|
||||
//+------------------------------------------------------------------+
|
||||
void CloseAllPositions() {
|
||||
// Loop through all positions and close them
|
||||
for (int i = PositionsTotal() - 1; i >= 0; i--) {
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
if (PositionSelectByTicket(ticket)) {
|
||||
if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
|
||||
trade.PositionClose(ticket);
|
||||
Print("Buy position closed at score crossover.");
|
||||
}
|
||||
else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
|
||||
trade.PositionClose(ticket);
|
||||
Print("Sell position closed at score crossover.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
for(int i=PositionsTotal()-1; i>=0; i--)
|
||||
{
|
||||
string symbol = PositionGetSymbol(i);
|
||||
ulong PositionTicket = PositionGetTicket(i);
|
||||
long trade_type = PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
if(PositionGetInteger(POSITION_MAGIC) != MagicNumber) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double POINT = SymbolInfoDouble( symbol, SYMBOL_POINT );
|
||||
int DIGIT = (int) SymbolInfoInteger( symbol, SYMBOL_DIGITS );
|
||||
|
||||
|
||||
if(trade_type == 0)
|
||||
{
|
||||
double Bid = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_BID),DIGIT);
|
||||
|
||||
if(Bid-PositionGetDouble(POSITION_PRICE_OPEN) > NormalizeDouble(POINT * TrailingStop,DIGIT))
|
||||
{
|
||||
if(PositionGetDouble(POSITION_SL) < NormalizeDouble(Bid - POINT * TrailingStop,DIGIT))
|
||||
{
|
||||
trade.PositionModify(PositionTicket,NormalizeDouble(Bid - POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(trade_type == 1)
|
||||
{
|
||||
double Ask = NormalizeDouble(SymbolInfoDouble(symbol,SYMBOL_ASK),DIGIT);
|
||||
|
||||
if((PositionGetDouble(POSITION_PRICE_OPEN) - Ask) > NormalizeDouble( POINT * TrailingStop,DIGIT))
|
||||
{
|
||||
if((PositionGetDouble(POSITION_SL) > NormalizeDouble(Ask + POINT * TrailingStop,DIGIT)) || (PositionGetDouble(POSITION_SL)==0))
|
||||
{
|
||||
trade.PositionModify(PositionTicket,NormalizeDouble(Ask + POINT * TrailingStop,DIGIT),PositionGetDouble(POSITION_TP));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Close_Position_MN(ulong magicNumber)
|
||||
{
|
||||
int total = PositionsTotal();
|
||||
for(int i = total - 1; i >= 0; i--)
|
||||
{
|
||||
ulong ticket = PositionGetTicket(i);
|
||||
|
||||
// Use PositionSelect by symbol instead of ticket
|
||||
string symbol = PositionGetSymbol(i);
|
||||
if(PositionSelect(symbol))
|
||||
{
|
||||
if (PositionGetInteger(POSITION_MAGIC) == magicNumber && PositionGetInteger(POSITION_TICKET) == ticket)
|
||||
{
|
||||
if(symbol == _Symbol) // Verify the symbol
|
||||
{
|
||||
Print("MN ", magicNumber);
|
||||
trade.PositionClose(ticket);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int errorCode = GetLastError();
|
||||
Print("aaaa PositionSelect failed with error code: ", errorCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate the dynamic lot size based on max drawdown |
|
||||
//+------------------------------------------------------------------+
|
||||
double CalculateLotSize()
|
||||
{
|
||||
double balance = AccountInfoDouble(ACCOUNT_BALANCE); // Get account balance
|
||||
double allowedDrawdown = balance * max_drawdown; // Calculate allowed drawdown in account currency
|
||||
double baseDrawdownPerLot = 150; // Assumed drawdown per 0.01 lots as per backtest
|
||||
|
||||
// Calculate lot size based on maximum drawdown
|
||||
double lotSize = (allowedDrawdown / baseDrawdownPerLot) * 0.01;
|
||||
return NormalizeDouble(lotSize, 2); // Normalize lot size to 2 decimal places
|
||||
}
|
||||