Update
This commit is contained in:
@@ -0,0 +1,559 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| EMAPriceSlope.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 description "Expert Advisor using EMA Slope for intelligent trend trading"
|
||||
#property description "Trades based on EMA momentum, slope strength, and price confirmation"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//--- Input parameters
|
||||
input group "Timeframe Settings"
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15; // Trading Timeframe
|
||||
|
||||
input group "EMA Settings"
|
||||
input int InpEMAPeriod = 20; // EMA Period
|
||||
input int InpSlopeBars = 3; // Slope Calculation Bars (lookback for slope)
|
||||
|
||||
input group "Slope Trading Logic"
|
||||
input double InpMinSlopeStrength = 0.0001; // Minimum Slope Strength (0.01% per bar)
|
||||
input bool InpUseSlopeAcceleration = true; // Require slope acceleration (increasing momentum)
|
||||
input double InpMinAcceleration = 0.00005; // Minimum Acceleration Threshold
|
||||
input bool InpUsePriceConfirmation = true; // Require price above/below EMA for confirmation
|
||||
input double InpPriceDistanceMultiplier = 0.5; // Price distance from EMA (ATR multiplier)
|
||||
|
||||
input group "Entry Filters"
|
||||
input bool InpUseVolatilityFilter = true; // Use ATR volatility filter
|
||||
input double InpMinATR = 0.0002; // Minimum ATR for trading (filter low volatility)
|
||||
input double InpMaxATR = 0.01; // Maximum ATR for trading (filter high volatility)
|
||||
input bool InpUseRSIFilter = false; // Use RSI filter
|
||||
input int InpRSIPeriod = 14; // RSI Period
|
||||
input double InpRSIOverbought = 70; // RSI Overbought (avoid longs)
|
||||
input double InpRSIOversold = 30; // RSI Oversold (avoid shorts)
|
||||
|
||||
input group "Trading Hours (Server Time)"
|
||||
input int InpStartHour = 8; // Trading Start Hour (0-23)
|
||||
input int InpEndHour = 18; // Trading End Hour (0-23)
|
||||
input bool InpUseTimeFilter = true; // Use Trading Hours Filter
|
||||
|
||||
input group "Risk Management"
|
||||
input double InpLotSize = 0.01; // Lot Size
|
||||
input int InpStopLoss = 50; // Stop Loss (pips) - 0 = no SL
|
||||
input int InpTakeProfit = 100; // Take Profit (pips) - 0 = no TP
|
||||
input bool InpUseTrailingStop = true; // Use Trailing Stop
|
||||
input int InpTrailingStop = 30; // Trailing Stop (pips)
|
||||
input int InpTrailingStep = 5; // Trailing Step (pips)
|
||||
input int InpMagicNumber = 890123; // Magic Number
|
||||
input int InpSlippage = 3; // Slippage (points)
|
||||
|
||||
input group "Exit Strategy"
|
||||
input bool InpUseSlopeReversalExit = true; // Exit on slope reversal
|
||||
input double InpSlopeReversalThreshold = -0.00005; // Slope reversal threshold (negative slope for long exit)
|
||||
input bool InpUseEMAExit = false; // Exit when price crosses EMA
|
||||
|
||||
input group "Loss Minimization"
|
||||
input bool InpUseMaxDailyLoss = true; // Use Max Daily Loss
|
||||
input double InpMaxDailyLoss = 50.0; // Max Daily Loss (USD)
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int ema_handle;
|
||||
int atr_handle;
|
||||
int rsi_handle;
|
||||
datetime last_bar_time = 0;
|
||||
double daily_profit = 0.0;
|
||||
datetime last_daily_reset = 0;
|
||||
double last_profit = 0.0;
|
||||
double last_slope = 0.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Set trade parameters
|
||||
trade.SetExpertMagicNumber(InpMagicNumber);
|
||||
trade.SetDeviationInPoints(InpSlippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Create indicators
|
||||
ema_handle = iMA(_Symbol, InpTimeframe, InpEMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(InpUseVolatilityFilter)
|
||||
{
|
||||
atr_handle = iATR(_Symbol, InpTimeframe, 14);
|
||||
if(atr_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("ERROR: Failed to create ATR indicator");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
if(InpUseRSIFilter)
|
||||
{
|
||||
rsi_handle = iRSI(_Symbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
|
||||
if(rsi_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("ERROR: Failed to create RSI indicator");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
}
|
||||
|
||||
if(ema_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("ERROR: Failed to create EMA indicator");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Initialize daily tracking
|
||||
last_daily_reset = TimeCurrent();
|
||||
daily_profit = 0.0;
|
||||
|
||||
Print("EMAPriceSlope EA initialized for ", _Symbol);
|
||||
Print("Timeframe: ", EnumToString(InpTimeframe));
|
||||
Print("EMA Period: ", InpEMAPeriod, " Slope Bars: ", InpSlopeBars);
|
||||
Print("Min Slope Strength: ", InpMinSlopeStrength);
|
||||
Print("Trading Hours: ", InpStartHour, ":00 - ", InpEndHour, ":00");
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicators
|
||||
if(ema_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(ema_handle);
|
||||
if(atr_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(atr_handle);
|
||||
if(rsi_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(rsi_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if new bar on the specified timeframe
|
||||
datetime current_bar_time = iTime(_Symbol, InpTimeframe, 0);
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
// Still same bar - only manage existing positions
|
||||
ManagePosition();
|
||||
return;
|
||||
}
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Reset daily profit at midnight
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
MqlDateTime last_dt;
|
||||
TimeToStruct(last_daily_reset, last_dt);
|
||||
bool is_new_day = (dt.day != last_dt.day || dt.month != last_dt.month || dt.year != last_dt.year);
|
||||
|
||||
if(is_new_day)
|
||||
{
|
||||
daily_profit = 0.0;
|
||||
last_daily_reset = TimeCurrent();
|
||||
Print("Daily reset: New trading day started. Daily profit reset to 0.");
|
||||
}
|
||||
|
||||
// Check daily loss limit
|
||||
if(InpUseMaxDailyLoss && daily_profit <= -InpMaxDailyLoss)
|
||||
{
|
||||
Print("Daily loss limit reached: ", daily_profit, " USD. Trading stopped for today.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check trading hours
|
||||
if(InpUseTimeFilter && !IsWithinTradingHours())
|
||||
{
|
||||
return; // Outside trading hours
|
||||
}
|
||||
|
||||
// Get EMA values for slope calculation
|
||||
double ema[];
|
||||
ArraySetAsSeries(ema, true);
|
||||
|
||||
// Need enough bars for slope calculation
|
||||
int bars_needed = InpSlopeBars + 5;
|
||||
if(CopyBuffer(ema_handle, 0, 0, bars_needed, ema) < bars_needed)
|
||||
{
|
||||
Print("ERROR: Failed to copy EMA buffer");
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate EMA slope (rate of change)
|
||||
double current_ema = ema[0];
|
||||
double previous_ema = ema[InpSlopeBars];
|
||||
double slope = (current_ema - previous_ema) / previous_ema; // Percentage change
|
||||
|
||||
// Calculate slope acceleration (change in slope)
|
||||
double previous_slope = last_slope;
|
||||
double acceleration = 0.0;
|
||||
if(previous_slope != 0.0)
|
||||
{
|
||||
acceleration = slope - previous_slope;
|
||||
}
|
||||
last_slope = slope;
|
||||
|
||||
// Get current price
|
||||
double current_price = iClose(_Symbol, InpTimeframe, 0);
|
||||
double price_distance_from_ema = MathAbs(current_price - current_ema) / current_ema;
|
||||
|
||||
// Get ATR for volatility filter
|
||||
double atr_value = 0.0;
|
||||
if(InpUseVolatilityFilter)
|
||||
{
|
||||
double atr_array[];
|
||||
ArraySetAsSeries(atr_array, true);
|
||||
if(CopyBuffer(atr_handle, 0, 0, 1, atr_array) > 0)
|
||||
{
|
||||
atr_value = atr_array[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Get RSI for filter
|
||||
double rsi_value = 50.0;
|
||||
if(InpUseRSIFilter)
|
||||
{
|
||||
double rsi_array[];
|
||||
ArraySetAsSeries(rsi_array, true);
|
||||
if(CopyBuffer(rsi_handle, 0, 0, 1, rsi_array) > 0)
|
||||
{
|
||||
rsi_value = rsi_array[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Check existing position
|
||||
if(PositionSelect(_Symbol))
|
||||
{
|
||||
ManagePosition();
|
||||
|
||||
// Check exit conditions
|
||||
long position_type = PositionGetInteger(POSITION_TYPE);
|
||||
|
||||
// Exit on slope reversal
|
||||
if(InpUseSlopeReversalExit)
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY && slope < InpSlopeReversalThreshold)
|
||||
{
|
||||
// Long position: exit on negative slope reversal
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
Print("Position closed: Slope reversal (slope=", slope, ")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL && slope > -InpSlopeReversalThreshold)
|
||||
{
|
||||
// Short position: exit on positive slope reversal
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
Print("Position closed: Slope reversal (slope=", slope, ")");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Exit when price crosses EMA (if enabled)
|
||||
if(InpUseEMAExit)
|
||||
{
|
||||
double prev_price = iClose(_Symbol, InpTimeframe, 1);
|
||||
if(position_type == POSITION_TYPE_BUY && current_price < current_ema && prev_price >= ema[1])
|
||||
{
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
Print("Position closed: Price crossed below EMA");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL && current_price > current_ema && prev_price <= ema[1])
|
||||
{
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
Print("Position closed: Price crossed above EMA");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No position - check for entry signals
|
||||
|
||||
// Volatility filter
|
||||
if(InpUseVolatilityFilter && atr_value > 0)
|
||||
{
|
||||
if(atr_value < InpMinATR || atr_value > InpMaxATR)
|
||||
{
|
||||
return; // Volatility out of range
|
||||
}
|
||||
}
|
||||
|
||||
// RSI filter
|
||||
if(InpUseRSIFilter)
|
||||
{
|
||||
if(rsi_value > InpRSIOverbought || rsi_value < InpRSIOversold)
|
||||
{
|
||||
return; // RSI in extreme zone
|
||||
}
|
||||
}
|
||||
|
||||
// BUY Signal: Positive slope with strength
|
||||
bool buy_signal = false;
|
||||
if(slope > InpMinSlopeStrength)
|
||||
{
|
||||
// Check acceleration (if enabled)
|
||||
if(InpUseSlopeAcceleration)
|
||||
{
|
||||
if(acceleration > InpMinAcceleration)
|
||||
{
|
||||
buy_signal = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buy_signal = true;
|
||||
}
|
||||
|
||||
// Price confirmation (if enabled)
|
||||
if(buy_signal && InpUsePriceConfirmation)
|
||||
{
|
||||
double min_distance = atr_value * InpPriceDistanceMultiplier / current_price;
|
||||
if(price_distance_from_ema < min_distance || current_price < current_ema)
|
||||
{
|
||||
buy_signal = false; // Price too close to EMA or below EMA
|
||||
}
|
||||
}
|
||||
|
||||
// RSI filter for buy
|
||||
if(buy_signal && InpUseRSIFilter && rsi_value > InpRSIOverbought)
|
||||
{
|
||||
buy_signal = false;
|
||||
}
|
||||
}
|
||||
|
||||
// SELL Signal: Negative slope with strength
|
||||
bool sell_signal = false;
|
||||
if(slope < -InpMinSlopeStrength)
|
||||
{
|
||||
// Check acceleration (if enabled)
|
||||
if(InpUseSlopeAcceleration)
|
||||
{
|
||||
if(acceleration < -InpMinAcceleration)
|
||||
{
|
||||
sell_signal = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sell_signal = true;
|
||||
}
|
||||
|
||||
// Price confirmation (if enabled)
|
||||
if(sell_signal && InpUsePriceConfirmation)
|
||||
{
|
||||
double min_distance = atr_value * InpPriceDistanceMultiplier / current_price;
|
||||
if(price_distance_from_ema < min_distance || current_price > current_ema)
|
||||
{
|
||||
sell_signal = false; // Price too close to EMA or above EMA
|
||||
}
|
||||
}
|
||||
|
||||
// RSI filter for sell
|
||||
if(sell_signal && InpUseRSIFilter && rsi_value < InpRSIOversold)
|
||||
{
|
||||
sell_signal = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute trades
|
||||
if(buy_signal)
|
||||
{
|
||||
Print("BUY Signal: Slope=", slope, " Acceleration=", acceleration, " Price=", current_price);
|
||||
OpenBuyPosition();
|
||||
}
|
||||
else if(sell_signal)
|
||||
{
|
||||
Print("SELL Signal: Slope=", slope, " Acceleration=", acceleration, " Price=", current_price);
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is within trading hours |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsWithinTradingHours()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
int current_hour = dt.hour;
|
||||
|
||||
// Handle case where end hour is before start hour (overnight)
|
||||
if(InpEndHour < InpStartHour)
|
||||
{
|
||||
return (current_hour >= InpStartHour || current_hour < InpEndHour);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (current_hour >= InpStartHour && current_hour < InpEndHour);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
double sl = 0.0;
|
||||
double tp = 0.0;
|
||||
|
||||
if(InpStopLoss > 0)
|
||||
{
|
||||
sl = price - InpStopLoss * _Point * 10;
|
||||
}
|
||||
if(InpTakeProfit > 0)
|
||||
{
|
||||
tp = price + InpTakeProfit * _Point * 10;
|
||||
}
|
||||
|
||||
// Validate stops
|
||||
int stop_level = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
double min_stop = stop_level * point;
|
||||
|
||||
if(sl > 0 && (price - sl) < min_stop)
|
||||
sl = price - min_stop;
|
||||
if(tp > 0 && (tp - price) < min_stop)
|
||||
tp = price + min_stop;
|
||||
|
||||
if(trade.Buy(InpLotSize, _Symbol, price, sl, tp, "EMA Slope Buy"))
|
||||
{
|
||||
Print("Buy order opened at ", price, " SL: ", sl, " TP: ", tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to open buy order: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
double sl = 0.0;
|
||||
double tp = 0.0;
|
||||
|
||||
if(InpStopLoss > 0)
|
||||
{
|
||||
sl = price + InpStopLoss * _Point * 10;
|
||||
}
|
||||
if(InpTakeProfit > 0)
|
||||
{
|
||||
tp = price - InpTakeProfit * _Point * 10;
|
||||
}
|
||||
|
||||
// Validate stops
|
||||
int stop_level = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
|
||||
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
|
||||
double min_stop = stop_level * point;
|
||||
|
||||
if(sl > 0 && (sl - price) < min_stop)
|
||||
sl = price + min_stop;
|
||||
if(tp > 0 && (price - tp) < min_stop)
|
||||
tp = price - min_stop;
|
||||
|
||||
if(trade.Sell(InpLotSize, _Symbol, price, sl, tp, "EMA Slope Sell"))
|
||||
{
|
||||
Print("Sell order opened at ", price, " SL: ", sl, " TP: ", tp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to open sell order: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage existing position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManagePosition()
|
||||
{
|
||||
if(!PositionSelect(_Symbol))
|
||||
return;
|
||||
|
||||
// Update daily profit
|
||||
double current_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
if(current_profit != last_profit)
|
||||
{
|
||||
daily_profit += (current_profit - last_profit);
|
||||
last_profit = current_profit;
|
||||
}
|
||||
|
||||
// Apply trailing stop
|
||||
if(InpUseTrailingStop && InpTrailingStop > 0)
|
||||
{
|
||||
ApplyTrailingStop();
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply trailing stop |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
if(!PositionSelect(_Symbol))
|
||||
return;
|
||||
|
||||
double position_sl = PositionGetDouble(POSITION_SL);
|
||||
double position_tp = PositionGetDouble(POSITION_TP);
|
||||
long position_type = PositionGetInteger(POSITION_TYPE);
|
||||
double current_price = (position_type == POSITION_TYPE_BUY) ?
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
|
||||
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
double trailing_distance = InpTrailingStop * _Point * 10;
|
||||
double new_sl = 0;
|
||||
|
||||
if(position_type == POSITION_TYPE_BUY)
|
||||
{
|
||||
new_sl = current_price - trailing_distance;
|
||||
if(new_sl > position_sl && new_sl < current_price)
|
||||
{
|
||||
// Check trailing step
|
||||
if(position_sl == 0 || (new_sl - position_sl) >= InpTrailingStep * _Point * 10)
|
||||
{
|
||||
if(trade.PositionModify(_Symbol, new_sl, position_tp))
|
||||
{
|
||||
Print("Trailing stop updated: New SL=", new_sl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL)
|
||||
{
|
||||
new_sl = current_price + trailing_distance;
|
||||
if((position_sl == 0 || new_sl < position_sl) && new_sl > current_price)
|
||||
{
|
||||
// Check trailing step
|
||||
if(position_sl == 0 || (position_sl - new_sl) >= InpTrailingStep * _Point * 10)
|
||||
{
|
||||
if(trade.PositionModify(_Symbol, new_sl, position_tp))
|
||||
{
|
||||
Print("Trailing stop updated: New SL=", new_sl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| TimeSelectiveEMA.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 description "EMA Crossover EA for EURUSD with Trading Hours Filter"
|
||||
#property description "Minimizes loss by trading only during optimal hours"
|
||||
|
||||
#include <Trade\Trade.mqh>
|
||||
|
||||
//--- Input parameters
|
||||
input group "Timeframe Settings"
|
||||
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M15; // Trading Timeframe
|
||||
|
||||
input group "EMA Settings"
|
||||
input int InpFastEMA = 12; // Fast EMA Period
|
||||
input int InpSlowEMA = 26; // Slow EMA Period
|
||||
|
||||
input group "Trading Hours (Server Time)"
|
||||
input int InpStartHour = 8; // Trading Start Hour (0-23)
|
||||
input int InpEndHour = 18; // Trading End Hour (0-23)
|
||||
input bool InpUseTimeFilter = true; // Use Trading Hours Filter
|
||||
|
||||
input group "Risk Management"
|
||||
input double InpLotSize = 0.01; // Lot Size
|
||||
input int InpMagicNumber = 789012; // Magic Number
|
||||
input int InpSlippage = 3; // Slippage (points)
|
||||
input bool InpUseRecrossExit = true; // Use EMA Recross as Exit (No SL/TP)
|
||||
|
||||
input group "Loss Minimization"
|
||||
input bool InpUseMaxDailyLoss = true; // Use Max Daily Loss
|
||||
input double InpMaxDailyLoss = 50.0; // Max Daily Loss (USD)
|
||||
|
||||
//--- Global variables
|
||||
CTrade trade;
|
||||
int fast_ema_handle;
|
||||
int slow_ema_handle;
|
||||
datetime last_bar_time = 0;
|
||||
double daily_profit = 0.0;
|
||||
datetime last_daily_reset = 0;
|
||||
double last_profit = 0.0;
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert initialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
int OnInit()
|
||||
{
|
||||
// Check symbol
|
||||
if(_Symbol != "EURUSD" && _Symbol != "EURUSD#")
|
||||
{
|
||||
Alert("This EA is designed for EURUSD only. Current symbol: ", _Symbol);
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Set trade parameters
|
||||
trade.SetExpertMagicNumber(InpMagicNumber);
|
||||
trade.SetDeviationInPoints(InpSlippage);
|
||||
trade.SetTypeFilling(ORDER_FILLING_FOK);
|
||||
|
||||
// Create EMA indicators on specified timeframe
|
||||
fast_ema_handle = iMA(_Symbol, InpTimeframe, InpFastEMA, 0, MODE_EMA, PRICE_CLOSE);
|
||||
slow_ema_handle = iMA(_Symbol, InpTimeframe, InpSlowEMA, 0, MODE_EMA, PRICE_CLOSE);
|
||||
|
||||
if(fast_ema_handle == INVALID_HANDLE || slow_ema_handle == INVALID_HANDLE)
|
||||
{
|
||||
Print("ERROR: Failed to create EMA indicators");
|
||||
return(INIT_FAILED);
|
||||
}
|
||||
|
||||
// Initialize daily tracking
|
||||
last_daily_reset = TimeCurrent();
|
||||
daily_profit = 0.0;
|
||||
|
||||
Print("TimeSelectiveEMA EA initialized for ", _Symbol);
|
||||
Print("Timeframe: ", EnumToString(InpTimeframe));
|
||||
Print("Trading Hours: ", InpStartHour, ":00 - ", InpEndHour, ":00 (Server Time)");
|
||||
Print("EMA Crossover: Fast=", InpFastEMA, " Slow=", InpSlowEMA);
|
||||
|
||||
return(INIT_SUCCEEDED);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert deinitialization function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
// Release indicators
|
||||
if(fast_ema_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(fast_ema_handle);
|
||||
if(slow_ema_handle != INVALID_HANDLE)
|
||||
IndicatorRelease(slow_ema_handle);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Expert tick function |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTick()
|
||||
{
|
||||
// Check if new bar on the specified timeframe
|
||||
datetime current_bar_time = iTime(_Symbol, InpTimeframe, 0);
|
||||
if(current_bar_time == last_bar_time)
|
||||
{
|
||||
// Still same bar - only manage existing positions
|
||||
ManagePosition();
|
||||
return;
|
||||
}
|
||||
last_bar_time = current_bar_time;
|
||||
|
||||
// Reset daily profit and consecutive losses at midnight
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
MqlDateTime last_dt;
|
||||
TimeToStruct(last_daily_reset, last_dt);
|
||||
|
||||
// Check if new day (day, month, or year changed)
|
||||
bool is_new_day = (dt.day != last_dt.day || dt.month != last_dt.month || dt.year != last_dt.year);
|
||||
|
||||
if(is_new_day)
|
||||
{
|
||||
// New day - reset daily profit
|
||||
daily_profit = 0.0;
|
||||
last_daily_reset = TimeCurrent();
|
||||
Print("Daily reset: New trading day started. Daily profit reset to 0.");
|
||||
}
|
||||
|
||||
// Check daily loss limit
|
||||
if(InpUseMaxDailyLoss && daily_profit <= -InpMaxDailyLoss)
|
||||
{
|
||||
Print("Daily loss limit reached: ", daily_profit, " USD. Trading stopped for today.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check trading hours
|
||||
if(InpUseTimeFilter && !IsWithinTradingHours())
|
||||
{
|
||||
return; // Outside trading hours
|
||||
}
|
||||
|
||||
// Get EMA values
|
||||
double fast_ema[], slow_ema[];
|
||||
ArraySetAsSeries(fast_ema, true);
|
||||
ArraySetAsSeries(slow_ema, true);
|
||||
|
||||
if(CopyBuffer(fast_ema_handle, 0, 0, 3, fast_ema) < 3 ||
|
||||
CopyBuffer(slow_ema_handle, 0, 0, 3, slow_ema) < 3)
|
||||
{
|
||||
Print("ERROR: Failed to copy EMA buffers");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for crossover signals
|
||||
bool bullish_cross = false;
|
||||
bool bearish_cross = false;
|
||||
|
||||
// Bullish: Fast EMA crosses above Slow EMA
|
||||
if(fast_ema[1] > slow_ema[1] && fast_ema[2] <= slow_ema[2])
|
||||
{
|
||||
bullish_cross = true;
|
||||
}
|
||||
|
||||
// Bearish: Fast EMA crosses below Slow EMA
|
||||
if(fast_ema[1] < slow_ema[1] && fast_ema[2] >= slow_ema[2])
|
||||
{
|
||||
bearish_cross = true;
|
||||
}
|
||||
|
||||
// Check existing position
|
||||
if(PositionSelect(_Symbol))
|
||||
{
|
||||
// Check for recross (opposite signal) - this is the exit signal
|
||||
long position_type = PositionGetInteger(POSITION_TYPE);
|
||||
if(InpUseRecrossExit)
|
||||
{
|
||||
if(position_type == POSITION_TYPE_BUY && bearish_cross)
|
||||
{
|
||||
// Close long position on bearish recross (Fast EMA crosses below Slow EMA)
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
Print("Position closed due to bearish recross (Fast EMA crossed below Slow EMA)");
|
||||
}
|
||||
}
|
||||
else if(position_type == POSITION_TYPE_SELL && bullish_cross)
|
||||
{
|
||||
// Close short position on bullish recross (Fast EMA crosses above Slow EMA)
|
||||
if(trade.PositionClose(_Symbol))
|
||||
{
|
||||
Print("Position closed due to bullish recross (Fast EMA crossed above Slow EMA)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Manage existing position (trailing stop, break-even if enabled)
|
||||
ManagePosition();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No position - check for new entry
|
||||
if(bullish_cross)
|
||||
{
|
||||
OpenBuyPosition();
|
||||
}
|
||||
else if(bearish_cross)
|
||||
{
|
||||
OpenSellPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if current time is within trading hours |
|
||||
//+------------------------------------------------------------------+
|
||||
bool IsWithinTradingHours()
|
||||
{
|
||||
MqlDateTime dt;
|
||||
TimeToStruct(TimeCurrent(), dt);
|
||||
int current_hour = dt.hour;
|
||||
|
||||
// Handle case where end hour is before start hour (overnight)
|
||||
if(InpEndHour < InpStartHour)
|
||||
{
|
||||
return (current_hour >= InpStartHour || current_hour < InpEndHour);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (current_hour >= InpStartHour && current_hour < InpEndHour);
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open buy position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenBuyPosition()
|
||||
{
|
||||
double price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
|
||||
|
||||
// No SL/TP - exit only on EMA recross
|
||||
if(trade.Buy(InpLotSize, _Symbol, price, 0, 0, "EMA Crossover Buy"))
|
||||
{
|
||||
Print("Buy order opened at ", price, " (Exit on bearish recross)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to open buy order: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Open sell position |
|
||||
//+------------------------------------------------------------------+
|
||||
void OpenSellPosition()
|
||||
{
|
||||
double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
|
||||
|
||||
// No SL/TP - exit only on EMA recross
|
||||
if(trade.Sell(InpLotSize, _Symbol, price, 0, 0, "EMA Crossover Sell"))
|
||||
{
|
||||
Print("Sell order opened at ", price, " (Exit on bullish recross)");
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("Failed to open sell order: ", trade.ResultRetcodeDescription());
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Manage existing position |
|
||||
//+------------------------------------------------------------------+
|
||||
void ManagePosition()
|
||||
{
|
||||
if(!PositionSelect(_Symbol))
|
||||
return;
|
||||
|
||||
// Update daily profit
|
||||
double current_profit = PositionGetDouble(POSITION_PROFIT);
|
||||
if(current_profit != last_profit)
|
||||
{
|
||||
daily_profit += (current_profit - last_profit);
|
||||
last_profit = current_profit;
|
||||
}
|
||||
|
||||
// Position management: Only track profit/loss
|
||||
// Exit is handled by EMA recross signal in OnTick()
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Apply trailing stop (disabled - using recross exit only) |
|
||||
//+------------------------------------------------------------------+
|
||||
void ApplyTrailingStop()
|
||||
{
|
||||
// Trailing stop disabled - using EMA recross as exit signal only
|
||||
// This function kept for compatibility but does nothing
|
||||
return;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Move stop loss to break-even (disabled - using recross exit only)|
|
||||
//+------------------------------------------------------------------+
|
||||
void MoveToBreakEven()
|
||||
{
|
||||
// Break-even disabled - using EMA recross as exit signal only
|
||||
// This function kept for compatibility but does nothing
|
||||
return;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Trade transaction event handler |
|
||||
//+------------------------------------------------------------------+
|
||||
void OnTradeTransaction(const MqlTradeTransaction& trans,
|
||||
const MqlTradeRequest& request,
|
||||
const MqlTradeResult& result)
|
||||
{
|
||||
// Track daily profit only (consecutive losses feature removed)
|
||||
if(trans.type == TRADE_TRANSACTION_DEAL_ADD)
|
||||
{
|
||||
if(HistoryDealSelect(trans.deal))
|
||||
{
|
||||
long deal_type = HistoryDealGetInteger(trans.deal, DEAL_TYPE);
|
||||
if(deal_type == DEAL_TYPE_BALANCE || deal_type == DEAL_TYPE_COMMISSION)
|
||||
return;
|
||||
|
||||
// Check if deal is from current day
|
||||
datetime deal_time = (datetime)HistoryDealGetInteger(trans.deal, DEAL_TIME);
|
||||
MqlDateTime deal_dt, current_dt;
|
||||
TimeToStruct(deal_time, deal_dt);
|
||||
TimeToStruct(TimeCurrent(), current_dt);
|
||||
|
||||
// Only process deals from current day
|
||||
bool is_current_day = (deal_dt.day == current_dt.day &&
|
||||
deal_dt.month == current_dt.month &&
|
||||
deal_dt.year == current_dt.year);
|
||||
|
||||
if(is_current_day)
|
||||
{
|
||||
double deal_profit = HistoryDealGetDouble(trans.deal, DEAL_PROFIT);
|
||||
// Daily profit is tracked in ManagePosition()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user