|
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")
|
print("3. Try logging into MT5 manually first")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
print("✓ MT5 initialized successfully")
|
print("[OK] MT5 initialized successfully")
|
||||||
|
|
||||||
# Get account info
|
# Get account info
|
||||||
account_info = mt5.account_info()
|
account_info = mt5.account_info()
|
||||||
if account_info is None:
|
if account_info is None:
|
||||||
print("WARNING: Could not get account info")
|
print("WARNING: Could not get account info")
|
||||||
else:
|
else:
|
||||||
print(f"✓ Account: {account_info.login}")
|
print(f"[OK] Account: {account_info.login}")
|
||||||
print(f" Server: {account_info.server}")
|
print(f" Server: {account_info.server}")
|
||||||
print(f" Balance: ${account_info.balance:.2f}")
|
print(f" Balance: ${account_info.balance:.2f}")
|
||||||
|
|
||||||
@@ -41,9 +41,9 @@ def test_mt5_connection():
|
|||||||
for symbol in test_symbols:
|
for symbol in test_symbols:
|
||||||
symbol_info = mt5.symbol_info(symbol)
|
symbol_info = mt5.symbol_info(symbol)
|
||||||
if symbol_info is None:
|
if symbol_info is None:
|
||||||
print(f"✗ {symbol}: Not available")
|
print(f"[FAIL] {symbol}: Not available")
|
||||||
else:
|
else:
|
||||||
print(f"✓ {symbol}: Available")
|
print(f"[OK] {symbol}: Available")
|
||||||
print(f" Bid: {symbol_info.bid:.5f}, Ask: {symbol_info.ask:.5f}")
|
print(f" Bid: {symbol_info.bid:.5f}, Ask: {symbol_info.ask:.5f}")
|
||||||
print(f" Spread: {symbol_info.spread} points")
|
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)
|
rates = mt5.copy_rates_range(symbol, timeframe, start_date, end_date)
|
||||||
if rates is None or len(rates) == 0:
|
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")
|
print(" Make sure you have historical data in MT5")
|
||||||
else:
|
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'])}")
|
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...")
|
print("\nTesting indicator creation...")
|
||||||
rsi_handle = mt5.iRSI(symbol, timeframe, 14, mt5.PRICE_CLOSE)
|
if not hasattr(mt5, "iRSI") or not hasattr(mt5, "iMA"):
|
||||||
if rsi_handle == mt5.INVALID_HANDLE:
|
print("[SKIP] mt5.iRSI / mt5.iMA not available in this MetaTrader5 build; skipping handle tests.")
|
||||||
print("✗ Failed to create RSI indicator")
|
print(" (Historical data + account checks above are enough for the Python backtest harness.)")
|
||||||
else:
|
else:
|
||||||
print("✓ RSI indicator created successfully")
|
rsi_handle = mt5.iRSI(symbol, timeframe, 14, mt5.PRICE_CLOSE)
|
||||||
# Get RSI values
|
if rsi_handle == mt5.INVALID_HANDLE:
|
||||||
rsi_values = mt5.copy_buffer(rsi_handle, 0, 0, 10)
|
print("[FAIL] Failed to create RSI indicator")
|
||||||
if rsi_values is not None:
|
else:
|
||||||
print(f" Latest RSI values: {rsi_values[-3:]}")
|
print("[OK] RSI indicator created successfully")
|
||||||
mt5.indicator_release(rsi_handle)
|
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)
|
ema_handle = mt5.iMA(symbol, timeframe, 50, 0, mt5.MODE_EMA, mt5.PRICE_CLOSE)
|
||||||
if ema_handle == mt5.INVALID_HANDLE:
|
if ema_handle == mt5.INVALID_HANDLE:
|
||||||
print("✗ Failed to create EMA indicator")
|
print("[FAIL] Failed to create EMA indicator")
|
||||||
else:
|
else:
|
||||||
print("✓ EMA indicator created successfully")
|
print("[OK] EMA indicator created successfully")
|
||||||
mt5.indicator_release(ema_handle)
|
mt5.indicator_release(ema_handle)
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
mt5.shutdown()
|
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 copyright "Copyright 2025, MetaQuotes Ltd."
|
||||||
#property link "https://www.mql5.com"
|
#property link "https://www.mql5.com"
|
||||||
#property version "1.01"
|
#property version "1.00"
|
||||||
|
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
#include "../_united/MagicNumberHelpers.mqh"
|
#include "../_united/MagicNumberHelpers.mqh"
|
||||||
|
|
||||||
//--- Input parameters
|
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
|
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||||
input int RSI_Period = 14; // RSI Period
|
input int RSI_Period = 14; // RSI Period
|
||||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||||
input double RSI_Overbought = 54; // RSI Overbought Level
|
input double RSI_Overbought = 32; // RSI Overbought Level
|
||||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
input double RSI_Oversold = 86; // RSI Oversold Level
|
||||||
input double RSI_Target_Buy = 87; // RSI Target for Buy Exit
|
input double RSI_Target_Buy = 100; // RSI Target for Buy Exit
|
||||||
input double RSI_Target_Sell = 33; // RSI Target for Sell Exit
|
input double RSI_Target_Sell = 24; // RSI Target for Sell Exit
|
||||||
input int BarsToWait = 1; // Bars to wait when RSI goes against position
|
input int BarsToWait = 34; // Bars to wait when RSI goes against position
|
||||||
input double LotSize = 5; // Lot Size
|
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 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
|
//--- Global variables
|
||||||
CTrade trade;
|
CTrade trade;
|
||||||
int rsi_handle;
|
int rsi_handle;
|
||||||
@@ -77,32 +72,35 @@ void OnDeinit(const int reason)
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
void OnTick()
|
void OnTick()
|
||||||
{
|
{
|
||||||
|
// Check if we have enough bars
|
||||||
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
if(Bars(_Symbol, TimeFrame) < RSI_Period + 2)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
// Check if this is a new bar
|
||||||
const bool new_bar = (current_bar_time != last_bar_time);
|
datetime current_bar_time = iTime(_Symbol, TimeFrame, 0);
|
||||||
const bool in_pos = position_open || PositionExistsByMagic(_Symbol, (ulong)MagicNumber);
|
if(current_bar_time == last_bar_time)
|
||||||
|
{
|
||||||
if(!in_pos && !new_bar)
|
return; // Still the same bar, don't process
|
||||||
return;
|
}
|
||||||
|
|
||||||
if(!UpdateRSI())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(in_pos && UseTrailingStop)
|
|
||||||
ApplyTrailingStop();
|
|
||||||
|
|
||||||
if(!new_bar)
|
|
||||||
return;
|
|
||||||
|
|
||||||
last_bar_time = current_bar_time;
|
last_bar_time = current_bar_time;
|
||||||
|
|
||||||
ResyncPositionFromMarket();
|
// Update RSI values
|
||||||
|
if(!UpdateRSI())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for existing position
|
||||||
CheckExistingPosition();
|
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();
|
CheckEntrySignals();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
@@ -122,85 +120,6 @@ bool UpdateRSI()
|
|||||||
return true;
|
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 |
|
//| 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
|
// 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_open = false;
|
||||||
position_ticket = 0;
|
position_ticket = 0;
|
||||||
@@ -324,7 +243,7 @@ void CheckEntrySignals()
|
|||||||
void OpenBuyPosition()
|
void OpenBuyPosition()
|
||||||
{
|
{
|
||||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
// 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
|
return; // Position already exists for this EA
|
||||||
}
|
}
|
||||||
@@ -337,7 +256,7 @@ void OpenBuyPosition()
|
|||||||
if(new_ticket > 0)
|
if(new_ticket > 0)
|
||||||
{
|
{
|
||||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
// 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_ticket = new_ticket;
|
||||||
position_open = true;
|
position_open = true;
|
||||||
@@ -357,7 +276,7 @@ void OpenBuyPosition()
|
|||||||
void OpenSellPosition()
|
void OpenSellPosition()
|
||||||
{
|
{
|
||||||
// Verify no position exists for THIS EA (magic number) on THIS symbol before opening
|
// 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
|
return; // Position already exists for this EA
|
||||||
}
|
}
|
||||||
@@ -370,7 +289,7 @@ void OpenSellPosition()
|
|||||||
if(new_ticket > 0)
|
if(new_ticket > 0)
|
||||||
{
|
{
|
||||||
// Verify position was opened for THIS EA (magic number) on THIS symbol
|
// 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_ticket = new_ticket;
|
||||||
position_open = true;
|
position_open = true;
|
||||||
@@ -390,7 +309,7 @@ void OpenSellPosition()
|
|||||||
void ClosePosition()
|
void ClosePosition()
|
||||||
{
|
{
|
||||||
// Close position using helper that verifies symbol AND magic number for THIS EA
|
// 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_open = false;
|
||||||
position_ticket = 0;
|
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_NVDA;
|
||||||
double g_Pos_RS_TSLA;
|
double g_Pos_RS_TSLA;
|
||||||
double g_Pos_RS_XAUUSD;
|
double g_Pos_RS_XAUUSD;
|
||||||
|
double g_Pos_RS_MU;
|
||||||
double g_Pos_RRA_EURUSD;
|
double g_Pos_RRA_EURUSD;
|
||||||
double g_Pos_RRA_AUDUSD;
|
double g_Pos_RRA_AUDUSD;
|
||||||
double g_Pos_SE;
|
double g_Pos_SE;
|
||||||
@@ -65,44 +66,47 @@ input bool EnableEMASlopeDistance = true;
|
|||||||
input bool EnableRSICrossOverReversal = true;
|
input bool EnableRSICrossOverReversal = true;
|
||||||
input bool EnableRSIMidPointHijack = true;
|
input bool EnableRSIMidPointHijack = true;
|
||||||
input bool EnableRSIScalpingAPPL = true;
|
input bool EnableRSIScalpingAPPL = true;
|
||||||
input bool EnableRSIScalpingBTCUSD = true;
|
input bool EnableRSIScalpingBTCUSD = false;
|
||||||
input bool EnableRSIScalpingNVDA = true;
|
input bool EnableRSIScalpingNVDA = true;
|
||||||
input bool EnableRSIScalpingTSLA = true;
|
input bool EnableRSIScalpingTSLA = true;
|
||||||
input bool EnableRSIScalpingXAUUSD = true;
|
input bool EnableRSIScalpingXAUUSD = false;
|
||||||
input bool EnableSuperEMA = true;
|
input bool EnableRSIScalpingMU = true;
|
||||||
input bool EnableRSIConsolidation = true;
|
input bool EnableSuperEMA = false;
|
||||||
|
input bool EnableRSIConsolidation = false;
|
||||||
input bool EnableRSIReversalAsianEURUSD = true;
|
input bool EnableRSIReversalAsianEURUSD = true;
|
||||||
input bool EnableRSIReversalAsianAUDUSD = true;
|
input bool EnableRSIReversalAsianAUDUSD = true;
|
||||||
input bool EnableSimpleTrendlineBTCUSD = true;
|
input bool EnableSimpleTrendlineBTCUSD = false;
|
||||||
input bool EnableSimpleTrendlineXAUUSD = true;
|
input bool EnableSimpleTrendlineXAUUSD = false;
|
||||||
input bool EnableSimpleTrendlineGER40 = true;
|
input bool EnableSimpleTrendlineGER40 = false;
|
||||||
input bool EnableRSISecretSauce = true;
|
input bool EnableRSISecretSauce = false;
|
||||||
|
input bool OPT_GuardOptimizationMode = true; // legacy compatibility with 123.set
|
||||||
|
|
||||||
input group "=== Centralized Lot Size (Granular Per Robot) ==="
|
input group "=== Centralized Lot Size (Granular Per Robot) ==="
|
||||||
input double LOT_DB_DarvasBox = 0.05;
|
input double LOT_DB_DarvasBox = 0.01;
|
||||||
input double LOT_ES_EMASlopeDistance = 0.05;
|
input double LOT_ES_EMASlopeDistance = 0.02;
|
||||||
input double LOT_RC_RSICrossOver = 0.06;
|
input double LOT_RC_RSICrossOver = 0.01;
|
||||||
input double LOT_RM_RSIMidPointHijack = 0.03;
|
input double LOT_RM_RSIMidPointHijack = 0.01;
|
||||||
input double LOT_RS_APPL = 5.0;
|
input double LOT_RS_APPL = 25.0;
|
||||||
input double LOT_RS_BTCUSD = 0.1;
|
input double LOT_RS_BTCUSD = 0.1;
|
||||||
input double LOT_RS_NVDA = 10.0;
|
input double LOT_RS_NVDA = 25.0;
|
||||||
input double LOT_RS_TSLA = 15.0;
|
input double LOT_RS_TSLA = 5.0;
|
||||||
input double LOT_RS_XAUUSD = 0.1;
|
input double LOT_RS_XAUUSD = 0.27;
|
||||||
input double LOT_RRA_EURUSD = 0.05;
|
input double LOT_RS_MU = 5.0;
|
||||||
input double LOT_RRA_AUDUSD = 0.08;
|
input double LOT_RRA_EURUSD = 0.1;
|
||||||
input double LOT_SE_SuperEMA = 0.02;
|
input double LOT_RRA_AUDUSD = 0.1;
|
||||||
input double LOT_RCO_RSIConsolidation = 0.02;
|
input double LOT_SE_SuperEMA = 0.01;
|
||||||
input double LOT_ST_BTCUSD = 0.07;
|
input double LOT_RCO_RSIConsolidation = 0.1;
|
||||||
input double LOT_ST_XAUUSD = 0.01;
|
input double LOT_ST_BTCUSD = 0.1;
|
||||||
|
input double LOT_ST_XAUUSD = 0.1;
|
||||||
input double LOT_ST_GER40 = 0.10;
|
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 group "=== Balance-based position sizing ==="
|
||||||
input bool ORCH_ScaleLotsByBalance = true;
|
input bool ORCH_ScaleLotsByBalance = true;
|
||||||
input bool ORCH_UseEquityInsteadOfBalance = false;
|
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_MinBalanceScale = 0.1;
|
||||||
input double ORCH_MaxBalanceScale = 10.0;
|
input double ORCH_MaxBalanceScale = 100000.0;
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
//| Strategy 1: DarvasBoxXAUUSD |
|
//| Strategy 1: DarvasBoxXAUUSD |
|
||||||
@@ -143,7 +147,7 @@ input bool ES_UseTrailingStop = true;
|
|||||||
input double ES_TrailingActivationPips = 0.0;
|
input double ES_TrailingActivationPips = 0.0;
|
||||||
input bool ES_UseStaleStopLossExit = false;
|
input bool ES_UseStaleStopLossExit = false;
|
||||||
input int ES_StaleStopLossSeconds = 33800;
|
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 int ES_MagicNumber = 12350;
|
||||||
input bool ES_UseSpreadAdjustment = true;
|
input bool ES_UseSpreadAdjustment = true;
|
||||||
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
input ENUM_TIMEFRAMES ES_Timeframe = PERIOD_H1;
|
||||||
@@ -169,7 +173,7 @@ input int RC_overboughtLevel = 93;
|
|||||||
input int RC_oversoldLevel = 22;
|
input int RC_oversoldLevel = 22;
|
||||||
input double RC_entryRSIBuySpread = 0;
|
input double RC_entryRSIBuySpread = 0;
|
||||||
input double RC_entryRSISellSpread = 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_slippage = 3;
|
||||||
input int RC_cooldownSeconds = 209;
|
input int RC_cooldownSeconds = 209;
|
||||||
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
input ENUM_TIMEFRAMES RC_TimeFrame1 = PERIOD_M1;
|
||||||
@@ -201,7 +205,7 @@ input bool RC_Saturday = false;
|
|||||||
input group "=== RSI MidPoint Hijack Strategy ==="
|
input group "=== RSI MidPoint Hijack Strategy ==="
|
||||||
input string RM_Symbol = "XAUUSD";
|
input string RM_Symbol = "XAUUSD";
|
||||||
input ENUM_TIMEFRAMES RM_InpTimeframe = PERIOD_H1;
|
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_InpMagicNumberRSIFollow = 1001;
|
||||||
input int RM_InpMagicNumberRSIReverse = 1002;
|
input int RM_InpMagicNumberRSIReverse = 1002;
|
||||||
input int RM_InpMagicNumberEMACross = 1003;
|
input int RM_InpMagicNumberEMACross = 1003;
|
||||||
@@ -254,7 +258,7 @@ input int RM_InpEMADistancePeriod = 26;
|
|||||||
//| 4. Use the exact symbol name shown |
|
//| 4. Use the exact symbol name shown |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
input group "=== RSI Scalping APPL (AAPL) - Pepperstone US ==="
|
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 ENUM_TIMEFRAMES RS_APPL_TimeFrame = PERIOD_M10;
|
||||||
input int RS_APPL_RSI_Period = 14;
|
input int RS_APPL_RSI_Period = 14;
|
||||||
input ENUM_APPLIED_PRICE RS_APPL_RSI_Applied_Price = PRICE_CLOSE;
|
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 int RS_BTCUSD_Slippage = 3;
|
||||||
|
|
||||||
input group "=== RSI Scalping NVDA - Pepperstone US ==="
|
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 ENUM_TIMEFRAMES RS_NVDA_TimeFrame = PERIOD_M15;
|
||||||
input int RS_NVDA_RSI_Period = 8;
|
input int RS_NVDA_RSI_Period = 8;
|
||||||
input ENUM_APPLIED_PRICE RS_NVDA_RSI_Applied_Price = PRICE_CLOSE;
|
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_Buy = 90;
|
||||||
input double RS_NVDA_RSI_Target_Sell = 70;
|
input double RS_NVDA_RSI_Target_Sell = 70;
|
||||||
input int RS_NVDA_BarsToWait = 5;
|
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_MagicNumber = 20003;
|
||||||
input int RS_NVDA_Slippage = 3;
|
input int RS_NVDA_Slippage = 3;
|
||||||
|
|
||||||
input group "=== RSI Scalping TSLA - Pepperstone US ==="
|
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 ENUM_TIMEFRAMES RS_TSLA_TimeFrame = PERIOD_H1;
|
||||||
input int RS_TSLA_RSI_Period = 14;
|
input int RS_TSLA_RSI_Period = 14;
|
||||||
input ENUM_APPLIED_PRICE RS_TSLA_RSI_Applied_Price = PRICE_CLOSE;
|
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_Buy = 87;
|
||||||
input double RS_TSLA_RSI_Target_Sell = 33;
|
input double RS_TSLA_RSI_Target_Sell = 33;
|
||||||
input int RS_TSLA_BarsToWait = 1;
|
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_MagicNumber = 125421321;
|
||||||
input int RS_TSLA_Slippage = 3;
|
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_MagicNumber = 129102315;
|
||||||
input int RS_XAUUSD_Slippage = 3;
|
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 group "=== RSI Scalping Reversal Escape (XAUUSD only) ==="
|
||||||
input bool RS_UseReversalEscape = true;
|
input bool RS_UseReversalEscape = true;
|
||||||
input int RS_ReversalATRPeriod = 14;
|
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 int ST_XAU_HTFBarsToScan = 500;
|
||||||
input double ST_XAU_LineTouchTolerance = 220.0;
|
input double ST_XAU_LineTouchTolerance = 220.0;
|
||||||
input double ST_XAU_BreakBuffer = 110.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 bool ST_XAU_DrawTrendline = true;
|
||||||
|
|
||||||
input group "=== SimpleTrendline GER40 ==="
|
input group "=== SimpleTrendline GER40 ==="
|
||||||
@@ -549,6 +567,7 @@ void United_RefreshScaledLots()
|
|||||||
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
|
g_Pos_RS_NVDA = United_ScaledLot(LOT_RS_NVDA);
|
||||||
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
|
g_Pos_RS_TSLA = United_ScaledLot(LOT_RS_TSLA);
|
||||||
g_Pos_RS_XAUUSD = United_ScaledLot(LOT_RS_XAUUSD);
|
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_EURUSD = United_ScaledLot(LOT_RRA_EURUSD);
|
||||||
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
|
g_Pos_RRA_AUDUSD = United_ScaledLot(LOT_RRA_AUDUSD);
|
||||||
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
|
g_Pos_SE = United_ScaledLot(LOT_SE_SuperEMA);
|
||||||
@@ -656,6 +675,7 @@ RSIScalpingData rsBTCUSDData;
|
|||||||
RSIScalpingData rsNVDAData;
|
RSIScalpingData rsNVDAData;
|
||||||
RSIScalpingData rsTSLAData;
|
RSIScalpingData rsTSLAData;
|
||||||
RSIScalpingData rsXAUUSDData;
|
RSIScalpingData rsXAUUSDData;
|
||||||
|
RSIScalpingData rsMUData;
|
||||||
SuperEMAData seData;
|
SuperEMAData seData;
|
||||||
RSIConsolidationData rcoData;
|
RSIConsolidationData rcoData;
|
||||||
SimpleTrendlineData stBTCData;
|
SimpleTrendlineData stBTCData;
|
||||||
@@ -710,6 +730,8 @@ int OnInit()
|
|||||||
|
|
||||||
if(EnableRSIScalpingXAUUSD)
|
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);
|
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(EnableRSISecretSauce)
|
||||||
if(!InitRSISecretSauce(rssData, RSS_Symbol))
|
if(!InitRSISecretSauce(rssData, RSS_Symbol))
|
||||||
@@ -779,6 +801,7 @@ int OnInit()
|
|||||||
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
(EnableRSIScalpingNVDA ? "RSIScalpingNVDA " : ""),
|
||||||
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
(EnableRSIScalpingTSLA ? "RSIScalpingTSLA " : ""),
|
||||||
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
|
(EnableRSIScalpingXAUUSD ? "RSIScalpingXAUUSD " : ""),
|
||||||
|
(EnableRSIScalpingMU ? "RSIScalpingMU " : ""),
|
||||||
(EnableRSISecretSauce ? "RSISecretSauce " : ""),
|
(EnableRSISecretSauce ? "RSISecretSauce " : ""),
|
||||||
(EnableSuperEMA ? "SuperEMA " : ""),
|
(EnableSuperEMA ? "SuperEMA " : ""),
|
||||||
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
|
(EnableRSIConsolidation ? "RSIConsolidation " : ""),
|
||||||
@@ -822,6 +845,8 @@ void OnDeinit(const int reason)
|
|||||||
|
|
||||||
if(EnableRSIScalpingXAUUSD)
|
if(EnableRSIScalpingXAUUSD)
|
||||||
DeinitRSIScalping(rsXAUUSDData);
|
DeinitRSIScalping(rsXAUUSDData);
|
||||||
|
if(EnableRSIScalpingMU)
|
||||||
|
DeinitRSIScalping(rsMUData);
|
||||||
|
|
||||||
if(EnableRSISecretSauce)
|
if(EnableRSISecretSauce)
|
||||||
DeinitRSISecretSauce(rssData);
|
DeinitRSISecretSauce(rssData);
|
||||||
@@ -906,6 +931,13 @@ void OnTick()
|
|||||||
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
RS_UseReversalEscape, RS_ReversalATRPeriod, RS_ReversalAdverseAtrMult, RS_ReversalSignsRequired,
|
||||||
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
RS_ReversalRsiVelocity, RS_ReversalBodyAtrMult,
|
||||||
RS_XAUUSD_UseTrailingStop, RS_XAUUSD_TrailDistancePoints, RS_XAUUSD_TrailActivationPoints);
|
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)
|
if(EnableRSISecretSauce)
|
||||||
ProcessRSISecretSauce(rssData, g_RSS_LotSize);
|
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) ===
|
; === PHASE 3: POSITION SIZING (Optimize with caution) ===
|
||||||
LotSize=50.0||5.0||10.0||100.0||Y
|
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) ===
|
; === FIXED PARAMETERS (Do Not Optimize) ===
|
||||||
RSI_Applied_Price=1||0||1||1||N
|
RSI_Applied_Price=1||0||1||1||N
|
||||||
MagicNumber=12345||0||12345||12345||N
|
MagicNumber=12345||0||12345||12345||N
|
||||||
@@ -18,6 +18,16 @@ TimeFrame=16387||0||16385||16390||Y
|
|||||||
; === PHASE 3: POSITION SIZING ===
|
; === PHASE 3: POSITION SIZING ===
|
||||||
LotSize=50.0||5.0||10.0||100.0||Y
|
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 ===
|
; === FIXED PARAMETERS ===
|
||||||
RSI_Applied_Price=1||0||1||1||N
|
RSI_Applied_Price=1||0||1||1||N
|
||||||
MagicNumber=12345||0||12345||12345||N
|
MagicNumber=12345||0||12345||12345||N
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
#property copyright "Copyright 2025, MetaQuotes Ltd."
|
||||||
#property link "https://www.mql5.com"
|
#property link "https://www.mql5.com"
|
||||||
#property version "1.01"
|
#property version "1.02"
|
||||||
|
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
#include "../_united/MagicNumberHelpers.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 TrailingStopDistancePoints = 375.0; // SL distance from bid/ask (points)
|
||||||
input double TrailingActivationPoints = 75.0; // min profit before trailing (0 = same as distance)
|
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
|
//--- Global variables
|
||||||
CTrade trade;
|
CTrade trade;
|
||||||
int rsi_handle;
|
int rsi_handle;
|
||||||
@@ -105,6 +111,34 @@ void OnTick()
|
|||||||
CheckEntrySignals();
|
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 |
|
//| Update RSI values |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
@@ -305,6 +339,9 @@ void CheckExistingPosition()
|
|||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
void CheckEntrySignals()
|
void CheckEntrySignals()
|
||||||
{
|
{
|
||||||
|
if(!IsWithinNewEntryWindowUTC())
|
||||||
|
return;
|
||||||
|
|
||||||
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
// Buy signal: RSI crosses from oversold to above oversold (checking the actual crossover)
|
||||||
if(rsi_two_bars_ago <= RSI_Oversold && rsi_prev > RSI_Oversold)
|
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"
|
#property version "1.00"
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
#include "../_united/MagicNumberHelpers.mqh"
|
#include "../_united/MagicNumberHelpers.mqh"
|
||||||
//--- Eingabeparameter (Input Parameters) - Optimized Profitable Parameters
|
//--- Eingabeparameter (Input Parameters) — synced with Desktop 123.set (2026.05.13)
|
||||||
input int EMA_Periode = 50; // EMA Periode
|
input int EMA_Periode = 85; // EMA Periode
|
||||||
input double PreisSchwelle = 700.0; // Preisbewegung Schwelle in Pips
|
input double PreisSchwelle = 350.0; // Preisbewegung Schwelle in Pips
|
||||||
input double SteigungSchwelle = 25.0; // EMA Steigung Schwelle in Pips
|
input double SteigungSchwelle = 22.5; // EMA Steigung Schwelle in Pips
|
||||||
input int ÜberwachungTimeout = 340; // Überwachungszeit in Sekunden
|
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 double LotGröße = 0.07; // Handelsvolumen
|
||||||
input int MagicNumber = 135790; // Magic Number für Trades
|
input int MagicNumber = 135790; // Magic Number für Trades
|
||||||
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
|
input bool UseSpreadAdjustment = true; // Spread-Anpassung verwenden
|
||||||
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
|
input ENUM_TIMEFRAMES Timeframe = PERIOD_H1; // Zeitraum für Analyse
|
||||||
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
|
input bool UseBarData = true; // Bar-Daten statt Tick-Daten verwenden
|
||||||
input int MaxTradesPerCrossover = 10; // Maximale Trades pro Crossover-Ereignis
|
input int MaxTradesPerCrossover = 48; // Maximale Trades pro Crossover-Ereignis
|
||||||
input int ProfitCheckBars = 15; // Bars bis zur Profit-Prüfung
|
input int ProfitCheckBars = 78; // Bars bis zur Profit-Prüfung
|
||||||
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
|
input bool CloseUnprofitableTrades = true; // Unprofitable Trades nach X Bars schließen
|
||||||
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
|
input bool UseWeeklyADXFilter = true; // W1 ADX Trendfilter aktivieren
|
||||||
input int WeeklyADXPeriod = 15; // ADX-Periode auf W1
|
input int WeeklyADXPeriod = 28; // ADX-Periode auf W1
|
||||||
input double WeeklyADXMin = 40.0; // Minimaler ADX fuer Trendfreigabe
|
input double WeeklyADXMin = 25.0; // Minimaler ADX fuer Trendfreigabe
|
||||||
input int WeeklyADXBarShift = 2; // 1=letzte geschlossene W1-Kerze
|
input int WeeklyADXBarShift = 8; // 1=letzte geschlossene W1-Kerze
|
||||||
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
|
input bool WeeklyADXUseDirection = true; // +DI/-DI Richtung mitpruefen
|
||||||
|
|
||||||
//--- Globale Variablen (Global Variables)
|
//--- 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. |
|
//| Copyright 2024, MetaQuotes Ltd. |
|
||||||
//| https://www.mql5.com |
|
//| https://www.mql5.com |
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
@@ -11,19 +11,19 @@
|
|||||||
// Include trade class
|
// Include trade class
|
||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
|
|
||||||
// Input parameters
|
// Input parameters (defaults synced from working 123.set 2026.05.13)
|
||||||
input int RSIPeriod = 28; // RSI period
|
input int RSIPeriod = 32; // RSI period
|
||||||
input double OverboughtLevel = 68; // Overbought level
|
input double OverboughtLevel = 80; // Overbought level
|
||||||
input double OversoldLevel = 30; // Oversold level
|
input double OversoldLevel = 37; // Oversold level
|
||||||
input int TakeProfitPips = 175; // Take profit in pips
|
input int TakeProfitPips = 225; // Take profit in pips
|
||||||
input int StopLossPips = 5; // Stop loss in pips
|
input int StopLossPips = 45; // Stop loss in pips
|
||||||
input double MaxLotSize = 0.2; // Maximum lot size
|
input double MaxLotSize = 0.2; // Maximum lot size
|
||||||
input int MaxSpread = 1000; // Maximum allowed spread in pips
|
input int MaxSpread = 1800; // Maximum allowed spread in pips
|
||||||
input int MaxDuration = 340; // Maximum trade duration in hours
|
input int MaxDuration = 480; // Maximum trade duration in hours
|
||||||
input bool UseStopLoss = false; // Use stop loss
|
input bool UseStopLoss = false; // Use stop loss
|
||||||
input bool UseTakeProfit = false; // Use take profit
|
input bool UseTakeProfit = false; // Use take profit
|
||||||
input bool UseRSIExit = true; // Use RSI for exit
|
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 bool CloseOutsideSession = true; // Close trades outside Asian session
|
||||||
input color PanelBackground = clrBlack; // Panel background color
|
input color PanelBackground = clrBlack; // Panel background color
|
||||||
input color PanelText = clrWhite; // Panel text 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 <Trade\Trade.mqh>
|
||||||
#include "../_united/MagicNumberHelpers.mqh"
|
#include "../_united/MagicNumberHelpers.mqh"
|
||||||
|
|
||||||
//--- Input parameters
|
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M15; // Timeframe for Analysis
|
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||||
input int RSI_Period = 8; // RSI Period
|
input int RSI_Period = 14; // RSI Period
|
||||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||||
input double RSI_Overbought = 36; // RSI Overbought Level
|
input double RSI_Overbought = 6; // RSI Overbought Level
|
||||||
input double RSI_Oversold = 38; // RSI Oversold Level
|
input double RSI_Oversold = 66; // RSI Oversold Level
|
||||||
input double RSI_Target_Buy = 90; // RSI Target for Buy Exit
|
input double RSI_Target_Buy = 98; // RSI Target for Buy Exit
|
||||||
input double RSI_Target_Sell = 70; // RSI Target for Sell Exit
|
input double RSI_Target_Sell = 52; // RSI Target for Sell Exit
|
||||||
input int BarsToWait = 5; // Bars to wait when RSI goes against position
|
input int BarsToWait = 12; // Bars to wait when RSI goes against position
|
||||||
input double LotSize = 50; // Lot Size
|
input double LotSize = 5; // Lot Size
|
||||||
input int MagicNumber = 12345; // Magic Number
|
input int MagicNumber = 129102315; // Magic Number
|
||||||
input int Slippage = 3; // Slippage in points
|
input int Slippage = 3; // Slippage in points
|
||||||
|
|
||||||
//--- Global variables
|
//--- Global variables
|
||||||
|
|||||||
@@ -10,17 +10,17 @@
|
|||||||
#include <Trade\Trade.mqh>
|
#include <Trade\Trade.mqh>
|
||||||
#include "../_united/MagicNumberHelpers.mqh"
|
#include "../_united/MagicNumberHelpers.mqh"
|
||||||
|
|
||||||
//--- Input parameters
|
//--- Input parameters — synced with Desktop 123.set (2026.05.13)
|
||||||
input ENUM_TIMEFRAMES TimeFrame = PERIOD_H1; // Timeframe for Analysis
|
input ENUM_TIMEFRAMES TimeFrame = PERIOD_M20; // Timeframe for Analysis
|
||||||
input int RSI_Period = 14; // RSI Period
|
input int RSI_Period = 14; // RSI Period
|
||||||
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
input ENUM_APPLIED_PRICE RSI_Applied_Price = PRICE_CLOSE; // RSI Applied Price
|
||||||
input double RSI_Overbought = 54; // RSI Overbought Level
|
input double RSI_Overbought = 32; // RSI Overbought Level
|
||||||
input double RSI_Oversold = 73; // RSI Oversold Level
|
input double RSI_Oversold = 86; // RSI Oversold Level
|
||||||
input double RSI_Target_Buy = 87; // RSI Target for Buy Exit
|
input double RSI_Target_Buy = 100; // RSI Target for Buy Exit
|
||||||
input double RSI_Target_Sell = 33; // RSI Target for Sell Exit
|
input double RSI_Target_Sell = 24; // RSI Target for Sell Exit
|
||||||
input int BarsToWait = 1; // Bars to wait when RSI goes against position
|
input int BarsToWait = 34; // Bars to wait when RSI goes against position
|
||||||
input double LotSize = 50; // Lot Size
|
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 int Slippage = 3; // Slippage in points
|
||||||
|
|
||||||
//--- Global variables
|
//--- 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
|
||||||
|
}
|
||||||