//+------------------------------------------------------------------+
//| TranslatedPythonBotEA.mq5 |
//| Based on Python Bot Logic Mentor |
//| kareem junior |
//+------------------------------------------------------------------+
#property copyright "Your Name Here"
#property link "Your Link Here"
#property version "1.00"
#property strict
// Include for CTrade class, makes trading simpler
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh> // For SymbolInfo class
#include <Trade\AccountInfo.mqh> // For AccountInfo class
// --- Input Parameters (Mirrors Python Configuration) ---
input group "Symbol & Timeframe"
input string InpSymbolToTrade = "Volatility 75 Index"; //
IMPORTANT: EA should usually run on the chart of this symbol
input ENUM_TIMEFRAMES InpTimeframe = PERIOD_M1; //
Timeframe for indicator calculations
input group "Indicator Parameters"
input int InpATRPeriod = 14;
input int InpBBandsPeriod = 20;
input double InpBBandsStdDev = 2.0;
input int InpRSIPeriod = 14;
input int InpEMAFastPeriod = 12;
input int InpEMASlowPeriod = 50;
input group "Risk Management"
input double InpRiskPerTradePercent = 1.0; // e.g., 1.0 for 1%
input double InpATR_SL_Multiplier = 1.5;
input double InpATR_TP_Multiplier = 3.0;
input int InpDefaultSLPips = 100; // Fallback SL if ATR is
zero
input int InpDefaultTPPips = 200; // Fallback TP if ATR is
zero
input ulong InpBotMagicNumber = 23052025; // Unique Magic Number
for this EA's trades
input int InpSlippagePips = 20; // Max allowed slippage in
points
// --- Global Variables ---
CTrade trade; // Trading object
CSymbolInfo symbol; // Symbol information object
CAccountInfo account; // Account information object
string G_CurrentSymbol; // Stores the symbol the EA is running on
// Indicator Handles
int h_ATR;
int h_BB;
int h_RSI;
int h_EMAFast;
int h_EMASlow;
// Arrays to store indicator and price data
double atr_buffer[];
double bb_main_buffer[]; // Middle band
double bb_upper_buffer[]; // Upper band
double bb_lower_buffer[]; // Lower band
double rsi_buffer[];
double ema_fast_buffer[];
double ema_slow_buffer[];
MqlRates rates[]; // For OHLC, Time, Volume
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
G_CurrentSymbol = _Symbol; // Get the symbol of the chart the EA is attached to
Print("OnInit: EA Starting on symbol ", G_CurrentSymbol, " timeframe ",
EnumToString(InpTimeframe));
// Initialize SymbolInfo and AccountInfo objects
if(!symbol.Name(G_CurrentSymbol))
{
Print("Error setting symbol for CSymbolInfo: ", GetLastError());
return(INIT_FAILED);
}
if(!account.Refresh()) // Refresh account data
{
Print("Error refreshing account info: ", GetLastError());
// Can still proceed, but lot size calculation might use stale equity if it
fails repeatedly
}
// Initialize CTrade object
trade.SetExpertMagicNumber(InpBotMagicNumber);
trade.SetDeviationInPoints(InpSlippagePips); // Max allowed price deviation
(slippage)
trade.SetTypeFillingBySymbol(G_CurrentSymbol); // Auto-detect filling mode, or
set specific like ORDER_FILLING_IOC
// --- Initialize Indicator Handles ---
// ATR
h_ATR = iATR(G_CurrentSymbol, InpTimeframe, InpATRPeriod);
if(h_ATR == INVALID_HANDLE)
{
Print("Error creating ATR indicator handle: ", GetLastError());
return(INIT_FAILED);
}
// Bollinger Bands
h_BB = iBands(G_CurrentSymbol, InpTimeframe, InpBBandsPeriod, 0,
InpBBandsStdDev, PRICE_CLOSE);
if(h_BB == INVALID_HANDLE)
{
Print("Error creating Bollinger Bands indicator handle: ", GetLastError());
return(INIT_FAILED);
}
// RSI
h_RSI = iRSI(G_CurrentSymbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
if(h_RSI == INVALID_HANDLE)
{
Print("Error creating RSI indicator handle: ", GetLastError());
return(INIT_FAILED);
}
// EMA Fast
h_EMAFast = iMA(G_CurrentSymbol, InpTimeframe, InpEMAFastPeriod, 0, MODE_EMA,
PRICE_CLOSE);
if(h_EMAFast == INVALID_HANDLE)
{
Print("Error creating Fast EMA indicator handle: ", GetLastError());
return(INIT_FAILED);
}
// EMA Slow
h_EMASlow = iMA(G_CurrentSymbol, InpTimeframe, InpEMASlowPeriod, 0, MODE_EMA,
PRICE_CLOSE);
if(h_EMASlow == INVALID_HANDLE)
{
Print("Error creating Slow EMA indicator handle: ", GetLastError());
return(INIT_FAILED);
}
// Set arrays as series (latest data at index 0)
ArraySetAsSeries(rates, true);
ArraySetAsSeries(atr_buffer, true);
ArraySetAsSeries(bb_main_buffer, true);
ArraySetAsSeries(bb_upper_buffer, true);
ArraySetAsSeries(bb_lower_buffer, true);
ArraySetAsSeries(rsi_buffer, true);
ArraySetAsSeries(ema_fast_buffer, true);
ArraySetAsSeries(ema_slow_buffer, true);
Print("OnInit: Initialization successful.");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Print("OnDeinit: EA Stopping. Reason code: ", reason);
// Release indicator handles
IndicatorRelease(h_ATR);
IndicatorRelease(h_BB);
IndicatorRelease(h_RSI);
IndicatorRelease(h_EMAFast);
IndicatorRelease(h_EMASlow);
}
//+------------------------------------------------------------------+
//| Expert tick function (called on every new price tick) |
//+------------------------------------------------------------------+
void OnTick()
{
// --- 0. Allow trading? Check terminal & EA settings ---
if(!MQLInfoInteger(MQL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_SERVER))
{
// Comment("Trading is not allowed for this account or server.");
return;
}
if(IsStopped()) // Check if EA was stopped from Experts tab
{
return;
}
// --- 1. Get Latest Data (OHLC and Indicators) ---
if(CopyRates(G_CurrentSymbol, InpTimeframe, 0, 3, rates) < 3) // Need at least
2-3 bars for prev/current
{
Print("Error copying rates: ", GetLastError());
return;
}
// Copy indicator buffers (typically need values for current [0] and previous
[1] bar)
// For signals, it's often safer to use data from the most recently COMPLETED
bar (index 1)
// Index 0 is the current, developing bar.
int bars_to_copy = 3; // Get a few bars for safety
if(CopyBuffer(h_ATR, 0, 0, bars_to_copy, atr_buffer) < bars_to_copy) return;
if(CopyBuffer(h_BB, 0, 0, bars_to_copy, bb_main_buffer) < bars_to_copy)
return; // Main Line
if(CopyBuffer(h_BB, 1, 0, bars_to_copy, bb_upper_buffer) < bars_to_copy) return;
// Upper Band
if(CopyBuffer(h_BB, 2, 0, bars_to_copy, bb_lower_buffer) < bars_to_copy) return;
// Lower Band
if(CopyBuffer(h_RSI, 0, 0, bars_to_copy, rsi_buffer) < bars_to_copy) return;
if(CopyBuffer(h_EMAFast, 0, 0, bars_to_copy, ema_fast_buffer) < bars_to_copy)
return;
if(CopyBuffer(h_EMASlow, 0, 0, bars_to_copy, ema_slow_buffer) < bars_to_copy)
return;
// --- 2. Check for Existing Position by this Bot ---
if(CheckExistingPosition())
{
// Print("An active trade with magic number ", InpBotMagicNumber, " already
exists for ", G_CurrentSymbol);
return; // Don't open new trades if one is already open by this EA
}
// --- 3. Get Trading Signal (Translate Python's get_trading_signal logic here)
---
// Example: Use values from index 1 (most recent fully closed bar) for signals
// Use values from index 0 (current bar) for entry price points
double current_close = rates[0].close; // Or rates[1].close for confirmed bar
close
double prev_close = rates[1].close;
double ema_fast_current = ema_fast_buffer[0]; // Current bar, might repaint
double ema_fast_prev = ema_fast_buffer[1]; // Previous completed bar
double ema_slow_current = ema_slow_buffer[0];
double ema_slow_prev = ema_slow_buffer[1];
double rsi_current = rsi_buffer[0]; // Or rsi_buffer[1]
string signal = "HOLD"; // "BUY", "SELL", "HOLD"
// Implement EMA Crossover with RSI filter from Python script:
// Buy signal: prev Fast EMA <= prev Slow EMA AND current Fast EMA > current
Slow EMA AND current RSI < 70
if (ema_fast_prev <= ema_slow_prev && ema_fast_current > ema_slow_current &&
rsi_current < 70.0)
{
signal = "BUY";
}
// Sell signal: prev Fast EMA >= prev Slow EMA AND current Fast EMA < current
Slow EMA AND current RSI > 30
else if (ema_fast_prev >= ema_slow_prev && ema_fast_current < ema_slow_current
&& rsi_current > 30.0)
{
signal = "SELL";
}
// Print("Signal: ", signal, " FastEMA_prev: ", ema_fast_prev, " SlowEMA_prev:
", ema_slow_prev, " RSI: ", rsi_current);
// --- 4. If No Existing Position and Valid Signal, then Trade ---
if(signal != "HOLD")
{
double current_atr = atr_buffer[0]; // Or atr_buffer[1] for stability
if(current_atr == 0 || NormalizedDouble(current_atr, _Digits) == 0) // Check
for valid ATR
{
Print("ATR is zero or invalid. Cannot calculate SL/TP dynamically.
Skipping trade.");
// Optionally use default SL/TP pips here, but be cautious
return;
}
// a. Calculate SL and TP distances in points (not pips, MQL5 points)
double point_value = symbol.Point(); // _Point can also be used
double sl_distance_points = (current_atr * InpATR_SL_Multiplier);
double tp_distance_points = (current_atr * InpATR_TP_Multiplier);
// If ATR is too small, use default pips converted to points
if(sl_distance_points / point_value < 1) sl_distance_points =
InpDefaultSLPips * point_value * pow(10, _Digits - symbol.Digits()); // Adjust for
fractional pips
if(tp_distance_points / point_value < 1) tp_distance_points =
InpDefaultTPPips * point_value * pow(10, _Digits - symbol.Digits());
// b. Calculate Lot Size
double lot_size = CalculateLotSize(sl_distance_points);
if(lot_size < symbol.LotsMin())
{
Print("Calculated lot size ", lot_size, " is less than minimum allowed ",
symbol.LotsMin(), ". Adjusting to min or skipping.");
// lot_size = symbol.LotsMin(); // Option: force min lot
// return; // Option: skip trade
if(symbol.LotsMin() > 0) lot_size = symbol.LotsMin(); // Only if min lot
is positive
else { Print("Min lot size is zero or negative, cannot trade."); return; }
}
// c. Define SL and TP price levels
double entry_price_ask = symbol.Ask(); // For BUY
double entry_price_bid = symbol.Bid(); // For SELL
double sl_price = 0.0;
double tp_price = 0.0;
if(signal == "BUY")
{
sl_price = entry_price_ask - sl_distance_points;
tp_price = entry_price_ask + tp_distance_points;
Print("Attempting BUY: Lot=", lot_size, " SL=", sl_price, " TP=",
tp_price, " EntryAsk=", entry_price_ask);
if(!trade.Buy(lot_size, G_CurrentSymbol, entry_price_ask, sl_price,
tp_price, "PythonLogicEA BUY"))
{
Print("BUY order failed: ", trade.ResultRetcode(), " - ",
trade.ResultComment());
}
else
{
Print("BUY order placed successfully. Ticket: ", trade.ResultOrder());
}
}
else if(signal == "SELL")
{
sl_price = entry_price_bid + sl_distance_points;
tp_price = entry_price_bid - tp_distance_points;
Print("Attempting SELL: Lot=", lot_size, " SL=", sl_price, " TP=",
tp_price, " EntryBid=", entry_price_bid);
if(!trade.Sell(lot_size, G_CurrentSymbol, entry_price_bid, sl_price,
tp_price, "PythonLogicEA SELL"))
{
Print("SELL order failed: ", trade.ResultRetcode(), " - ",
trade.ResultComment());
}
else
{
Print("SELL order placed successfully. Ticket: ", trade.ResultOrder());
}
}
}
}
//+------------------------------------------------------------------+
//| Helper Function: Calculate Lot Size based on Risk % and SL |
//+------------------------------------------------------------------+
double CalculateLotSize(double sl_distance_points) // sl_distance_points is the
distance from entry to SL in PRICE units
{
account.Refresh(); // Get latest account info
double acc_equity = account.Equity();
double risk_amount = acc_equity * (InpRiskPerTradePercent / 100.0);
if(sl_distance_points <= 0)
{
Print("SL distance is zero or negative (", sl_distance_points, "). Cannot
calculate lot size. Using min lot.");
return(symbol.LotsMin());
}
// Value of 1 lot SL
// double tick_value = symbol.TickValue(); // Value of a 1-point move for 1 lot
in deposit currency
// double sl_value_per_lot = sl_distance_points / symbol.Point() *
tick_value; // This is more complex with varying contract specs
// Simplified: Value of 1 point move for 1 lot in deposit currency
// For Forex, TickValue is often per tick, and Point is the size of the tick.
// For Indices, it can be different. We need value per point for 1 lot.
// SymbolInfoDouble(G_CurrentSymbol, SYMBOL_TRADE_TICK_VALUE_LOSS) or
SYMBOL_TRADE_TICK_VALUE_PROFIT
// A more robust way:
double one_lot_value_per_point = 0;
MqlTick latest_tick;
SymbolInfoTick(G_CurrentSymbol, latest_tick);
// Calculate profit/loss for a 1 point move with 1 lot
// This is tricky because SYMBOL_TRADE_TICK_VALUE is value of
SYMBOL_TRADE_TICK_SIZE move.
// If SYMBOL_TRADE_TICK_SIZE is not equal to SYMBOL_POINT, adjustment is needed.
// Let's assume SYMBOL_TRADE_TICK_VALUE is for a move of SYMBOL_TRADE_TICK_SIZE.
// Value per point = SYMBOL_TRADE_TICK_VALUE / (SYMBOL_TRADE_TICK_SIZE /
SYMBOL_POINT)
double tick_value = symbol.TickValue(); // Value of a SYMBOL_TRADE_TICK_SIZE
move for 1.0 lot
double tick_size = symbol.TickSize();
double point = symbol.Point();
if (tick_size == 0) return symbol.LotsMin(); // Avoid division by zero
double value_per_point_one_lot = tick_value / (tick_size / point) ;
double sl_value_per_lot = (sl_distance_points / point) *
value_per_point_one_lot;
if(sl_value_per_lot <= 0)
{
Print("SL value per lot is zero or negative (", sl_value_per_lot, "). Cannot
calculate lot size. Using min lot.");
return(symbol.LotsMin());
}
double desired_lot_size = risk_amount / sl_value_per_lot;
// Normalize lot size according to symbol's contract specification
double min_lot = symbol.LotsMin();
double max_lot = symbol.LotsMax();
double step_lot = symbol.LotsStep();
desired_lot_size = MathMax(min_lot, desired_lot_size); // Not less than
min_lot
desired_lot_size = MathMin(max_lot, desired_lot_size); // Not more than
max_lot
desired_lot_size = MathFloor(desired_lot_size / step_lot) * step_lot; // Adhere
to volume step
// Ensure it's rounded to the correct number of decimal places for lot size
int lot_digits = 0;
string step_str = DoubleToString(step_lot, 8); // Convert step to string to find
decimal places
int dot_pos = StringFind(step_str, ".");
if(dot_pos >= 0) lot_digits = StringLen(step_str) - dot_pos - 1;
desired_lot_size = NormalizeDouble(desired_lot_size, lot_digits);
Print("LotCalc: Equity=", acc_equity, " RiskAmt=", risk_amount, " SL_Dist_Pts=",
sl_distance_points, " SL_Val_Per_Lot=", sl_value_per_lot, " CalcLot=",
desired_lot_size);
return(desired_lot_size);
}
//+------------------------------------------------------------------+
//| Helper Function: Check if a position by this EA already exists |
//+------------------------------------------------------------------+
bool CheckExistingPosition()
{
for(int i = PositionsTotal() - 1; i >= 0; i--) // Loop through all open
positions
{
ulong pos_ticket = PositionGetTicket(i); // Get ticket of position at index i
if(PositionSelectByTicket(pos_ticket)) // Select the position to access its
properties
{
// Check if the position is for the current symbol and has the EA's magic
number
if(PositionGetString(POSITION_SYMBOL) == G_CurrentSymbol &&
PositionGetInteger(POSITION_MAGIC) == InpBotMagicNumber)
{
return(true); // Position found
}
}
}
return(false); // No matching position found
}
//+------------------------------------------------------------------+