//+------------------------------------------------------------------+
//| TranslatedPythonBotEA.mq5 |
//| Based on Python Bot Logic Mentor |
//| Your Name Here |
//+------------------------------------------------------------------+
#property copyright "Your Name Here"
#property link "Your Link Here"
#property version "1.02" // Incremented version
#property strict
// Include necessary standard library files
#include <Trade\[Link]> // For CTrade class (trading functions)
#include <Trade\[Link]> // For CSymbolInfo class (symbol properties)
#include <Trade\[Link]> // For CAccountInfo class (account properties)
// --- Input Parameters (User-configurable EA settings) ---
input group "Symbol & Timeframe Settings"
// string InpSymbolToTrade = "Volatility 75 Index"; // EA uses
_Symbol (chart symbol) by default
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 & Trade Management"
input double InpRiskPerTradePercent = 1.0; // e.g., 1.0 for 1% of
equity
input double InpATR_SL_Multiplier = 1.5; // Stop Loss based on ATR *
Multiplier
input double InpATR_TP_Multiplier = 3.0; // Take Profit based on ATR
* Multiplier
input int InpDefaultSLPips = 100; // Fallback Stop Loss in
Points (not pips if [Link] != pip unit)
input int InpDefaultTPPips = 200; // Fallback Take Profit in
Points
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 from [Link]
CSymbolInfo symbol; // Symbol information object from [Link]
(GLOBAL)
CAccountInfo account; // Account information object from
[Link] (GLOBAL)
string G_CurrentSymbol; // Stores the symbol the EA is running on
(GLOBAL)
// Indicator Handles (to store references to initialised indicators)
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[]; // Bollinger Bands Middle line
double bb_upper_buffer[]; // Bollinger Bands Upper line
double bb_lower_buffer[]; // Bollinger Bands Lower line
double rsi_buffer[];
double ema_fast_buffer[];
double ema_slow_buffer[];
MqlRates rates[]; // For OHLC, Time, Volume (price data)
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
G_CurrentSymbol = _Symbol; // Get the symbol of the chart the EA is attached to
PrintFormat("OnInit: EA Starting on symbol %s, timeframe %s", G_CurrentSymbol,
EnumToString(InpTimeframe));
// Initialize GLOBAL SymbolInfo object
if() // Set the symbol for the GLOBAL CSymbolInfo
object
{
PrintFormat("Error setting symbol '%s' for CSymbolInfo: %d", G_CurrentSymbol,
GetLastError());
return(INIT_FAILED);
}
// Initialize GLOBAL AccountInfo object and refresh data
[Link]();
// Initialize CTrade object
[Link](InpBotMagicNumber);
[Link](InpSlippagePips);
[Link](G_CurrentSymbol);
// --- Initialize Indicator Handles ---
h_ATR = iATR(G_CurrentSymbol, InpTimeframe, InpATRPeriod);
if(h_ATR == INVALID_HANDLE) { PrintFormat("Error creating ATR indicator: %d",
GetLastError()); return(INIT_FAILED); }
h_BB = iBands(G_CurrentSymbol, InpTimeframe, InpBBandsPeriod, 0,
InpBBandsStdDev, PRICE_CLOSE);
if(h_BB == INVALID_HANDLE) { PrintFormat("Error creating Bollinger Bands
indicator: %d", GetLastError()); return(INIT_FAILED); }
h_RSI = iRSI(G_CurrentSymbol, InpTimeframe, InpRSIPeriod, PRICE_CLOSE);
if(h_RSI == INVALID_HANDLE) { PrintFormat("Error creating RSI indicator: %d",
GetLastError()); return(INIT_FAILED); }
h_EMAFast = iMA(G_CurrentSymbol, InpTimeframe, InpEMAFastPeriod, 0, MODE_EMA,
PRICE_CLOSE);
if(h_EMAFast == INVALID_HANDLE) { PrintFormat("Error creating Fast EMA
indicator: %d", GetLastError()); return(INIT_FAILED); }
h_EMASlow = iMA(G_CurrentSymbol, InpTimeframe, InpEMASlowPeriod, 0, MODE_EMA,
PRICE_CLOSE);
if(h_EMASlow == INVALID_HANDLE) { PrintFormat("Error creating Slow EMA
indicator: %d", GetLastError()); return(INIT_FAILED); }
// Set arrays as series (important for correct indexing: 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)
{
PrintFormat("OnDeinit: EA Stopping. Reason code: %d", reason);
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()
{
if(!TerminalInfoInteger(TERMINAL_CONNECTED) || !
TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) || !MQLInfoInteger(MQL_TRADE_ALLOWED)
|| IsStopped())
{
return;
}
// Refresh GLOBAL symbol object for latest prices (Ask, Bid, Point etc.)
[Link]();
// Refresh GLOBAL account object for latest equity etc. (optional here if done
before lot calc)
// [Link](); // Already called in CalculateLotSize and OnInit
if(CopyRates(G_CurrentSymbol, InpTimeframe, 0, 3, rates) < 3)
{
PrintFormat("Error copying rates for %s: %d", G_CurrentSymbol,
GetLastError());
return;
}
int bars_to_copy = 3;
if(CopyBuffer(h_ATR, 0, 0, bars_to_copy, atr_buffer) < bars_to_copy) {
/*Print("Error copying ATR buffer");*/ return; } // Reduced print frequency for
brevity
if(CopyBuffer(h_BB, 0, 0, bars_to_copy, bb_main_buffer) < bars_to_copy) {
/*Print("Error copying BB Main buffer");*/ return; }
if(CopyBuffer(h_BB, 1, 0, bars_to_copy, bb_upper_buffer) < bars_to_copy) {
/*Print("Error copying BB Upper buffer");*/ return; }
if(CopyBuffer(h_BB, 2, 0, bars_to_copy, bb_lower_buffer) < bars_to_copy) {
/*Print("Error copying BB Lower buffer");*/ return; }
if(CopyBuffer(h_RSI, 0, 0, bars_to_copy, rsi_buffer) < bars_to_copy) {
/*Print("Error copying RSI buffer");*/ return; }
if(CopyBuffer(h_EMAFast, 0, 0, bars_to_copy, ema_fast_buffer) < bars_to_copy)
{ /*Print("Error copying EMA Fast buffer");*/ return; }
if(CopyBuffer(h_EMASlow, 0, 0, bars_to_copy, ema_slow_buffer) < bars_to_copy)
{ /*Print("Error copying EMA Slow buffer");*/ return; }
if(CheckExistingPosition())
{
return;
}
double ema_fast_prev = ema_fast_buffer[1];
double ema_slow_prev = ema_slow_buffer[1];
double rsi_prev = rsi_buffer[1];
double ema_fast_bar2 = ema_fast_buffer[2];
double ema_slow_bar2 = ema_slow_buffer[2];
string signal = "HOLD";
if (ema_fast_bar2 <= ema_slow_bar2 && ema_fast_prev > ema_slow_prev && rsi_prev
< 70.0)
{
signal = "BUY";
}
else if (ema_fast_bar2 >= ema_slow_bar2 && ema_fast_prev < ema_slow_prev &&
rsi_prev > 30.0)
{
signal = "SELL";
}
if(signal != "HOLD")
{
double current_atr_value = atr_buffer[1]; // ATR from last closed bar
double sl_distance_price_units;
double tp_distance_price_units;
// Use NormalizeDouble with [Link]() for ATR checks
if(current_atr_value <= 0 || NormalizeDouble(current_atr_value,
[Link]()) <= NormalizeDouble([Link]() / 10.0, [Link]()))
{
PrintFormat("ATR value (%.*f) is zero or too small. Using default Points
for SL/TP.", [Link](), current_atr_value);
sl_distance_price_units = InpDefaultSLPips * [Link](); // Assuming
InpDefaultSLPips are "points"
tp_distance_price_units = InpDefaultTPPips * [Link]();
if(sl_distance_price_units <=0 ) sl_distance_price_units = 100 *
[Link]();
if(tp_distance_price_units <=0 ) tp_distance_price_units = 200 *
[Link]();
}
else
{
sl_distance_price_units = current_atr_value * InpATR_SL_Multiplier;
tp_distance_price_units = current_atr_value * InpATR_TP_Multiplier;
}
PlaceTrade(signal, sl_distance_price_units, tp_distance_price_units);
}
}
//+------------------------------------------------------------------+
//| Helper Function: Place Trade |
//+------------------------------------------------------------------+
void PlaceTrade(string signal_type, double sl_distance_price_units, double
tp_distance_price_units)
{
double lot_size = CalculateLotSize(sl_distance_price_units);
if(lot_size < [Link]()) // Check against actual min lot from symbol info
{
PrintFormat("Calculated lot size %.2f is less than minimum allowed %.2f.
Skipping trade.", NormalizeDouble(lot_size, 8), [Link]());
return;
}
// Ensure GLOBAL symbol object has fresh rates
// [Link](); // Already called at the start of OnTick
double entry_price_ask = [Link]();
double entry_price_bid = [Link]();
double sl_price = 0.0;
double tp_price = 0.0;
string comment = "EA_Trade_" + TimeToString(TimeCurrent(), TIME_SECONDS);
if(signal_type == "BUY")
{
sl_price = entry_price_ask - sl_distance_price_units;
tp_price = entry_price_ask + tp_distance_price_units;
sl_price = NormalizeDouble(sl_price, [Link]());
tp_price = NormalizeDouble(tp_price, [Link]());
PrintFormat("Attempting BUY: Lot=%.*f, EntryAsk=%.*f, SL=%.*f, TP=%.*f",
[Link](), lot_size, [Link](),
entry_price_ask, [Link](), sl_price, [Link](), tp_price);
if()
{
PrintFormat("BUY order failed: %d - %s", [Link](),
[Link]());
}
else
{
PrintFormat("BUY order placed successfully. Ticket: %d",
(int)[Link]());
}
}
else if(signal_type == "SELL")
{
sl_price = entry_price_bid + sl_distance_price_units;
tp_price = entry_price_bid - tp_distance_price_units;
sl_price = NormalizeDouble(sl_price, [Link]());
tp_price = NormalizeDouble(tp_price, [Link]());
PrintFormat("Attempting SELL: Lot=%.*f, EntryBid=%.*f, SL=%.*f, TP=%.*f",
[Link](), lot_size, [Link](),
entry_price_bid, [Link](), sl_price, [Link](), tp_price);
if()
{
PrintFormat("SELL order failed: %d - %s", [Link](),
[Link]());
}
else
{
PrintFormat("SELL order placed successfully. Ticket: %d",
(int)[Link]());
}
}
}
//+------------------------------------------------------------------+
//| Helper Function: Calculate Lot Size based on Risk % and SL |
//+------------------------------------------------------------------+
double CalculateLotSize(double sl_distance_price_units)
{
[Link](); // Ensure latest equity
double acc_equity = [Link]();
double risk_amount_deposit_currency = acc_equity * (InpRiskPerTradePercent /
100.0);
// Ensure SL distance is at least one point, otherwise lot calculation is
problematic
if(sl_distance_price_units < [Link]())
{
PrintFormat("SL distance (%.*f) is less than one point. Cannot accurately
calculate lot size. Using min lot.", [Link](), sl_distance_price_units);
return([Link]());
}
double tick_value = [Link]();
double tick_size = [Link]();
double point_size = [Link]();
if(tick_size <= 0 || point_size <= 0 || tick_value <=0) // Added tick_value
check
{
Print("Symbol tick_size, point_size, or tick_value is zero or negative.
Cannot calculate lot. Using min lot.");
return([Link]());
}
double value_per_point_one_lot = tick_value / (tick_size / point_size);
if(value_per_point_one_lot <=0) // Check if value_per_point is valid
{
PrintFormat("Value per point for 1 lot (%.2f) is zero or negative. Cannot
calculate lot size. Using min lot.", value_per_point_one_lot);
return([Link]());
}
double sl_value_for_one_lot = (sl_distance_price_units / point_size) *
value_per_point_one_lot;
if(sl_value_for_one_lot <= 0)
{
PrintFormat("SL value per lot (%.2f) is zero or negative. Using min lot.",
sl_value_for_one_lot);
return([Link]());
}
double desired_lot_size = risk_amount_deposit_currency / sl_value_for_one_lot;
double min_lot = [Link]();
double max_lot = [Link]();
double step_lot = [Link]();
desired_lot_size = MathMax(min_lot, desired_lot_size);
if(max_lot > 0 && max_lot > min_lot) desired_lot_size = MathMin(max_lot,
desired_lot_size);
if(step_lot > 0)
{
desired_lot_size = MathFloor(desired_lot_size / step_lot) * step_lot;
// For fractional lots, NormalizeDouble is important
desired_lot_size = NormalizeDouble(desired_lot_size,
[Link]());
}
if (desired_lot_size < min_lot) desired_lot_size = min_lot; // Final check after
stepping
PrintFormat("LotCalc: Equity=%.2f, RiskAmt=%.2f, SL_Dist_Price=%.*f,
SL_Val_Per_Lot=%.2f, CalcLot=%.*f",
acc_equity, risk_amount_deposit_currency,
[Link](), sl_distance_price_units,
sl_value_for_one_lot,
[Link](), 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--)
{
ulong pos_ticket = PositionGetTicket(i);
if(pos_ticket == 0) continue;
if(PositionSelectByTicket(pos_ticket))
{
if(PositionGetString(POSITION_SYMBOL) == G_CurrentSymbol &&
PositionGetInteger(POSITION_MAGIC) == InpBotMagicNumber)
{
return(true);
}
}
}
return(false);
}
//+------------------------------------------------------------------+