KEMBAR78
Working Code | PDF | Parameter (Computer Programming) | Databases
0% found this document useful (0 votes)
25 views93 pages

Working Code

This document outlines the code for an Expert Advisor (EA) in MQL5, which includes functionalities for trade execution, logging, and database management. It defines various input parameters for risk management, indicators, and trade settings, as well as structures for managing open positions and trade data. The EA also incorporates features for handling volatility, trailing stops, and logging trade activities to a SQLite database.

Uploaded by

ibraheim811
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views93 pages

Working Code

This document outlines the code for an Expert Advisor (EA) in MQL5, which includes functionalities for trade execution, logging, and database management. It defines various input parameters for risk management, indicators, and trade settings, as well as structures for managing open positions and trade data. The EA also incorporates features for handling volatility, trailing stops, and logging trade activities to a SQLite database.

Uploaded by

ibraheim811
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 93

//+------------------------------------------------------------------+

//| Your EA Name |

//| Your Name or Info |

//+------------------------------------------------------------------+

#property copyright "Your Name or Info"

#property version "1.00"

#property strict

// Include statements

#include <Trade\Trade.mqh>

#include <Trade\PositionInfo.mqh>

// Import the functions from the SQLiteWrapper.dll

#import "SQLiteWrapper.dll"

long OpenDatabase(const char &filename[], char &errmsg[], int


errmsgSize);

int CloseDatabase(long dbHandle);

int ExecuteSQL(long dbHandle, const char &query[], char &errmsg[], int


errmsgSize);

int ExportTradeLogsToCSV(long dbHandle, const char &csvFilePath[], char


&errmsg[], int errmsgSize);

#import

// Import ShellExecute function from shell32.dll

#import "shell32.dll"

int ShellExecuteW(int hwnd, string Operation, string File, string


Parameters, string Directory, int ShowCmd);

#import

// Global variables
CTrade trade;

long dbHandle; // Changed from int to long to match the return type of
OpenDatabase

int nextTradeID = 1;

//+------------------------------------------------------------------+

//| Struct definitions |

//+------------------------------------------------------------------+

struct OpenPositionData

ulong positionID;

ulong entryDealTicket;

string symbol;

string tradeType; // "Buy" or "Sell"

double entryPrice;

datetime entryTime;

double atr;

double wprValue; // Williams %R value

double lotSize;

string reasonEntry;

string date;

string time;

double currentStopLoss; // Field to track current Stop Loss

double takeProfitPrice;

int profitLevelReached; // Field to track profit levels reached

bool trailingStopActivated; // Indicates if trailing stop was activated

double profitLevelAtTrailingStop; // Stores profit level at trailing stop


activation

bool isLogged; // New field to indicate if the position has


been logged
};

// Array to store open positions

OpenPositionData openPositions[];

//+------------------------------------------------------------------+

//| Struct for trade data logging |

//+------------------------------------------------------------------+

struct TradeData

ulong positionID; // Position ID

string symbol;

string tradeType;

double entryPrice;

double exitPrice;

double lotSize;

string entryDate;

string entryTime;

string exitDate;

string exitTime;

long duration;

string reasonEntry;

string reasonExit;

double profitLoss;

double swap; // Added field for swap

double commission; // Added field for commission

double atr;

double wprValue; // Williams %R value

string remarks;
};

//--- Input parameters

input double HighRisk = 5.0; // High risk percentage

input double LowRisk = 0.7; // Low risk percentage

input int MagicNumber = 987654321; // Magic number for trades

input int Slippage = 10; // Slippage for trades

// --- Input parameters for each indicator's timeframe

input ENUM_TIMEFRAMES ATRTimeframe = PERIOD_M1; // Timeframe


for ATR

input ENUM_TIMEFRAMES WPRTimeframe = PERIOD_M1; // Timeframe


for Williams %R

input ENUM_TIMEFRAMES PivotTimeframe = PERIOD_H6; // Timeframe for


Pivot Points

input ENUM_TIMEFRAMES TickVolumeTimeframe = PERIOD_M1; //


Timeframe for tick volume

input ENUM_TIMEFRAMES VolumeProfileTimeframe = PERIOD_M1; //


Timeframe for Volume Profile

input int VolumeProfilePeriod = 20; // Look-back period for


Volume Profile

//--- Input parameters for Trailing Stop ---

input bool EnableTrailingStop = true; // Toggle to enable or


disable Trailing Stop

// input double TrailingStopATRMultiplier = 2.0; // **Increased


Multiplier** for ATR to set trailing stop distance

// input int TrailingStopATRPeriod = 14; // ATR period for Trailing


Stop calculation
// input double TrailingStopMinDistance = 10.0; // Minimum trailing
stop distance in points

//--- ATR variables

input int atrPeriod = 14; // ATR period (adjustable in input)

input double atrLogThreshold = 0.0001; // Set a threshold for significant


ATR change

input double atrMinThreshold = 0.0001; // Minimum ATR value for trading


(adjustable in input)

//--- Williams %R Input parameters

input int WPR_Period = 14; // Period for Williams %R

input int WPR_Overbought = -20; // Overbought level

input int WPR_Oversold = -80; // Oversold level

int wprHandle; // Handle for Williams %R

double wprValueGlobal = 0.0; // Global variable to store Williams %R


value

bool tradeExecuted = false; // Initialize trade execution flag

//--- Global variables

double prevATRValue = 0.0;

double lastLoggedATRValue = 0.0;

bool isHedging = (AccountInfoInteger(ACCOUNT_MARGIN_MODE) ==


ACCOUNT_MARGIN_MODE_RETAIL_HEDGING);

//--- Adjust stop-loss multiplier for low volatility

input double atrStopLossMultiplierHighVolatility = 3.0; // Higher SL


multiplier for high volatility
input double atrStopLossMultiplierLowVolatility = 2.0; // SL multiplier for
low volatility

//--- Lot size adjustment based on volatility

input double volatilityLotMultiplier = 1.0;

//--- Consecutive loss protection

input int maxConsecutiveLosses = 3; // Max allowed consecutive losses


before pausing trading

int consecutiveLosses = 0;

bool isPaused = false;

datetime pauseEndTime;

//--- Volatility parameters

input double atrVolatilityThreshold = 0.0008; // Threshold to determine


volatility

bool isHighVolatility = false;

bool wasHighVolatility = false;

datetime nextVolatilityCheckTime = 0;

//--- Cooldown parameters

datetime lastTradeTime = 0;

input int cooldownTime = 30; // Cooldown time in seconds

//--- Input parameters for risk management

input double stopLossMultiplier = 10; // Multiplier for the stop loss points

double minStopLossPoints; // Minimum stop loss points variable


(initialized later)

//--- Input parameters for Trailing Stop Profit Levels ---


input double ProfitLevel1 = 15.0;

input double ProfitLevel2 = 30.0;

input double ProfitLevel3 = 45.0;

input double ProfitLevel4 = 60.0;

input double ProfitLevel5 = 75.0;

input uint TimerIntervalMilliseconds = 500; // Timer interval in


milliseconds

input double RiskToRewardRatio = 1.5; // Default risk-to-reward ratio

input bool EnableDatabaseLogging = true; // Toggle for database logging

// Define log levels

enum LogLevelEnum

LOG_LEVEL_NONE = 0,

LOG_LEVEL_ERROR = 1,

LOG_LEVEL_WARNING= 2,

LOG_LEVEL_INFO = 3,

LOG_LEVEL_DEBUG = 4

};

// Define log categories as bit flags

enum LogCategoryEnum {

LOG_CAT_NONE = 0x0000,

LOG_CAT_ATR = 0x0001,

LOG_CAT_TRADE_EXECUTION = 0x0002,

LOG_CAT_SLTP_VALUES = 0x0004,
LOG_CAT_RISK_MANAGEMENT = 0x0008,

LOG_CAT_ERRORS = 0x0010,

LOG_CAT_OTHER = 0x0020,

LOG_CAT_ALL = 0xFFFF

};

// Input parameter for log level

input LogLevelEnum LogLevel = LOG_LEVEL_INFO;

// Input parameter for log categories (bit flags)

input uint LogCategories = LOG_CAT_ALL; // Default to all categories

// Global array to store log entries

struct LogEntry {

string date;

string time;

string logLevel;

string category;

string message;

};

LogEntry logEntries[];

// Global variable to count ticks

int tickCount = 0;

ulong loggedPositionIDs[];

//--- Input parameters for closing trades before end of day


input bool CloseTradesBeforeEndOfDay = true; // Enable closing trades
before end of day

input int CloseTradesHour = 23; // Hour to close all trades (24-hour


format)

input int CloseTradesMinute = 59; // Minute to close all trades

//--- Input parameters for tick volume filtering

input int TickVolumePeriods = 5; // Number of periods to consider for


average tick volume

input double TickVolumeMultiplier = 1.0; // Multiplier for average tick


volume threshold

input bool EnableTickVolumeFilter = true; // Enable or disable tick volume


filtering

//--- Input parameters for trend detection ---

input ENUM_TIMEFRAMES TrendTimeframe = PERIOD_H6; // Trend


Detection

input int TrendMAPeriod = 50; // Period for moving average


used in trend detection

input ENUM_MA_METHOD TrendMAMethod = MODE_SMA; // Moving


average method (SMA, EMA, etc.)

int volumeProfileHandle; // Handle for Volume Profile custom indicator

// New global variables for trade counting

input int maxTradesPerDay = 10; // Maximum allowed trades per day

int dailyTradeCount = 0; // Current trade count for the day

datetime lastTradeDay = 0; // Stores the day of the last trade to reset


count daily

datetime lastBarTime = 0; // To track the last bar time for resetting


tradeExecuted
//+------------------------------------------------------------------+

//| Expert initialization function |

//+------------------------------------------------------------------+

int OnInit()

// Set the minimum stop loss points based on the multiplier and the
symbol's _Point value

minStopLossPoints = stopLossMultiplier * _Point;

// Check if automated trading is allowed

if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED))

LogMessage("Automated trading is disabled. Please enable


automated trading.", LOG_LEVEL_ERROR);

return INIT_FAILED;

// Ensure the symbol is selected and data is available

if (!SymbolSelect(_Symbol, true))

LogMessage("Failed to select symbol " + _Symbol,


LOG_LEVEL_ERROR);

return INIT_FAILED;

// Initialize timer for regular updates

EventSetMillisecondTimer(TimerIntervalMilliseconds);

// Initialize logging and database


LogMessage("Initializing EA and preparing log file...", LOG_LEVEL_INFO);

// --- Database Initialization ---

// Get the path to the Files directory

string filesDir = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\


Files";

string dbPath = filesDir + "\\TradeLog.db";

// Convert dbPath to a char array

char filePath[512];

StringToCharArray(dbPath, filePath);

// Log the paths for debugging

LogMessage("Files directory: " + filesDir, LOG_LEVEL_DEBUG);

LogMessage("Database path: " + dbPath, LOG_LEVEL_DEBUG);

// Database handle and error message buffer

char errorMsg[256];

// Open the database

dbHandle = OpenDatabase(filePath, errorMsg, sizeof(errorMsg));

if (dbHandle == 0)

LogMessage("Failed to open database. Error: " +


CharArrayToString(errorMsg), LOG_LEVEL_ERROR);

return INIT_FAILED;

else

{
LogMessage("Database opened successfully.", LOG_LEVEL_INFO);

// Create the trades table if it doesn't exist

string createTableQuery = "CREATE TABLE IF NOT EXISTS trades ("

"TradeID INTEGER PRIMARY KEY AUTOINCREMENT, "

"PositionID INTEGER, "

"EntryDate TEXT, "

"EntryTime TEXT, "

"ExitDate TEXT, "

"ExitTime TEXT, "

"Symbol TEXT, "

"TradeType TEXT, "

"EntryPrice REAL, "

"ExitPrice REAL, "

"ReasonEntry TEXT, "

"ReasonExit TEXT, "

"ProfitLoss REAL, "

"Swap REAL, " // Added Swap field

"Commission REAL, " // Added Commission field

"ATR REAL, "

"WPRValue REAL, "

"Duration INTEGER, "

"LotSize REAL, "

"Remarks TEXT"

");";

char query[4096];

StringToCharArray(createTableQuery, query);
int execResult = ExecuteSQL(dbHandle, query, errorMsg,
sizeof(errorMsg));

if (execResult != 0)

LogMessage("Error creating trades table. Error: " +


CharArrayToString(errorMsg), LOG_LEVEL_ERROR);

CloseDatabase(dbHandle);

return INIT_FAILED;

LogMessage("Trades table created or already exists.",


LOG_LEVEL_INFO);

// Create the TradeLog table for failed trades

string createFailedTradesTableQuery = "CREATE TABLE IF NOT EXISTS


TradeLog ("

"Date TEXT, "

"Time TEXT, "

"Symbol TEXT, "

"Remarks TEXT, "

"ATR REAL"

");";

StringToCharArray(createFailedTradesTableQuery, query);

execResult = ExecuteSQL(dbHandle, query, errorMsg, sizeof(errorMsg));

if (execResult != 0)

LogMessage("Error creating TradeLog table. Error: " +


CharArrayToString(errorMsg), LOG_LEVEL_ERROR);

CloseDatabase(dbHandle);

return INIT_FAILED;
}

LogMessage("TradeLog table created or already exists.",


LOG_LEVEL_INFO);

// Create the CustomLog table for logs while testing

string createLogTableQuery = "CREATE TABLE IF NOT EXISTS LogEntries


("

"Date TEXT, "

"Time TEXT, "

"LogLevel TEXT, "

"Category TEXT, "

"Message TEXT"

");";

char logTableQuery[1024];

StringToCharArray(createLogTableQuery, logTableQuery);

ExecuteSQL(dbHandle, logTableQuery, errorMsg, sizeof(errorMsg));

// --- End of Database Initialization ---

// Initialize Williams %R indicator handle

wprHandle = iWPR(_Symbol, WPRTimeframe, WPR_Period);

if (wprHandle == INVALID_HANDLE)

LogMessage("Failed to create Williams %R handle",


LOG_LEVEL_ERROR);

return INIT_FAILED;

LogMessage("Williams %R initialized", LOG_LEVEL_INFO);


// Initialize Volume Profile Indicator with correct file name ("Volume
Indicator")

// Pass VolumeProfilePeriod and VolumeProfileTimeframe as parameters


to the indicator

volumeProfileHandle = iCustom(_Symbol, VolumeProfileTimeframe,


"Volume Indicator", VolumeProfilePeriod);

if (volumeProfileHandle == INVALID_HANDLE)

LogMessage("Failed to initialize Volume Profile Indicator.",


LOG_LEVEL_ERROR);

return INIT_FAILED;

LogMessage("Volume Profile Indicator initialized with


VolumeProfilePeriod=" + IntegerToString(VolumeProfilePeriod) + " and
Timeframe=" + EnumToString(VolumeProfileTimeframe),
LOG_LEVEL_INFO);

LogMessage("Expert initialized.", LOG_LEVEL_INFO);

return INIT_SUCCEEDED;

//+------------------------------------------------------------------+

//| Expert deinitialization function |

//+------------------------------------------------------------------+

void OnDeinit(const int reason)

// Flush remaining logs to ensure all data is written to the database

FlushLogsToDatabase();

Print("Expert deinitialized with reason: ", reason);

// Check for any open positions and log them


int totalPositions = ArraySize(openPositions);

for (int i = 0; i < totalPositions; i++)

ulong positionID = openPositions[i].positionID;

// Attempt to find the closing deal

ulong closingDealTicket = FindClosingDealForPosition(positionID);

if (closingDealTicket != 0)

LogClosedTrade(positionID, closingDealTicket);

else

// Manually construct TradeData and log the trade

LogMessage("No closing deal found for position ID: " +


IntegerToString(positionID) + ". Logging as closed due to deinitialization.",
LOG_LEVEL_WARNING);

// Manually construct TradeData

TradeData tradeData;

OpenPositionData entryData = openPositions[i];

tradeData.positionID = entryData.positionID;

tradeData.entryDate = entryData.date;

tradeData.entryTime = entryData.time;

tradeData.entryPrice = entryData.entryPrice;

tradeData.reasonEntry = entryData.reasonEntry;

tradeData.lotSize = entryData.lotSize;

tradeData.duration = (long)(TimeCurrent() - entryData.entryTime);


tradeData.symbol = entryData.symbol;

// Correctly set the exit price based on the trade type

if (entryData.tradeType == "Buy")

tradeData.exitPrice = SymbolInfoDouble(_Symbol,
SYMBOL_BID);

else

tradeData.exitPrice = SymbolInfoDouble(_Symbol,
SYMBOL_ASK);

tradeData.exitPrice = NormalizeDouble(tradeData.exitPrice,
_Digits);

tradeData.tradeType = entryData.tradeType;

tradeData.exitDate = TimeToString(TimeCurrent(), TIME_DATE);

tradeData.exitTime = TimeToString(TimeCurrent(), TIME_SECONDS


| TIME_MINUTES);

tradeData.atr = entryData.atr;

tradeData.wprValue = entryData.wprValue;

tradeData.reasonExit = "Closed due to EA deinitialization";

tradeData.remarks = "Logged on deinitialization";

// Retrieve swap and commission

double swap = 0.0;

double commission = 0.0;

// Attempt to find the last deal associated with this position


ulong lastDealTicket = FindLastDealForPosition(positionID);

if (lastDealTicket != 0 && HistoryDealSelect(lastDealTicket))

swap = HistoryDealGetDouble(lastDealTicket, DEAL_SWAP);

commission = HistoryDealGetDouble(lastDealTicket,
DEAL_COMMISSION);

// Calculate profit/loss using OrderCalcProfit

ENUM_ORDER_TYPE orderType = (tradeData.tradeType ==


"Buy") ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;

double calculatedProfitLoss = 0.0;

// Adjust the lot size to standard lots if necessary

double adjustedLotSize = tradeData.lotSize;

if (adjustedLotSize > 50.0)

adjustedLotSize = adjustedLotSize / 100.0;

if (OrderCalcProfit(

orderType,

_Symbol,

adjustedLotSize,

tradeData.entryPrice,

tradeData.exitPrice,

calculatedProfitLoss))

// Adjust profit/loss
tradeData.profitLoss = calculatedProfitLoss + swap +
commission;

tradeData.swap = swap;

tradeData.commission = commission;

else

int errorCode = GetLastError();

PrintFormat("Failed to calculate profit/loss using OrderCalcProfit.


Error code: %d, Description: %s", errorCode, ErrorDescription(errorCode));

tradeData.profitLoss = 0.0; // Default to zero if calculation fails

tradeData.swap = swap;

tradeData.commission = commission;

// Log the trade

LogTrade(tradeData);

// Clear openPositions array

ArrayResize(openPositions, 0);

// Export trade log to Excel before closing the database

ExportTradeLogToExcel();

// Close the database if it's open

if (dbHandle != 0)

int closeResult = CloseDatabase(dbHandle);


if (closeResult != 0)

LogMessage("Failed to close the database.", LOG_LEVEL_ERROR);

else

LogMessage("Database closed successfully.", LOG_LEVEL_INFO);

dbHandle = 0; // Reset the handle

// Remove the timer

EventKillTimer();

//+------------------------------------------------------------------+

//| Expert tick function |

//+------------------------------------------------------------------+

void OnTick()

// Close trades before end of day to avoid swaps

if (CloseTradesBeforeEndOfDay && IsTimeToCloseTrades())

LogMessage("End of day detected. Closing all positions to avoid


swaps.", LOG_LEVEL_INFO);

CloseAllPositions();

return; // Skip further processing on this tick

}
// Check and reset daily trade count if a new day starts

MqlDateTime currentTimeStruct;

TimeToStruct(TimeCurrent(), currentTimeStruct);

int currentDay = currentTimeStruct.day;

if (currentDay != lastTradeDay)

dailyTradeCount = 0; // Reset trade count

lastTradeDay = currentDay;

LogMessage("New day detected. Daily trade count reset.",


LOG_LEVEL_INFO);

// Check if daily trade limit has been reached

if (dailyTradeCount >= maxTradesPerDay)

LogMessage("Daily trade limit reached. No further trades will be


executed today.", LOG_LEVEL_WARNING);

return;

// Check for new bar

datetime currentBarTime = iTime(_Symbol, PERIOD_CURRENT, 0); // Get


the time of the current bar

if (currentBarTime != lastBarTime)

// A new bar has started

tradeExecuted = false;

lastBarTime = currentBarTime;
}

// Check if cooldown period has passed

if (TimeCurrent() - lastTradeTime < cooldownTime)

LogMessage("Cooldown time is active. Waiting before placing new


trades.", LOG_LEVEL_WARNING);

return;

// Prevent repeated trade execution if tradeExecuted is set

if (tradeExecuted)

LogMessage("Trade already executed on this bar, skipping further


trade execution.", LOG_LEVEL_WARNING);

return;

if (EnableTrailingStop)

// Manage trailing stops on every tick

int totalPositions = ArraySize(openPositions);

for (int i = 0; i < totalPositions; i++)

OpenPositionData position = openPositions[i];

ManageTrailingStop(

position.positionID,

position.entryPrice,

position.takeProfitPrice,

isHighVolatility ? HighRisk : LowRisk


);

totalPositions = ArraySize(openPositions);

if (i >= totalPositions) break;

// Always check for closed positions

CheckForClosedPositions();

// Skip trading if ATR is below minimum threshold

double atrValue = GetATR(atrPeriod);

if (atrValue < atrMinThreshold)

LogMessage("ATR below minimum threshold. No trade will be placed.


ATR: " + DoubleToString(atrValue, _Digits), LOG_LEVEL_WARNING,
LOG_CAT_ATR);

return;

// Log and verify volatility status

CheckVolatility(atrValue);

LogMessage("ATR=" + DoubleToString(atrValue, _Digits) + ",


Volatility=" + (isHighVolatility ? "High" : "Low"), LOG_LEVEL_DEBUG,
LOG_CAT_ATR);

// Ensure trades are processed within trading hours

if (!IsWithinTradingHours())

LogMessage("Outside trading hours.", LOG_LEVEL_WARNING);

return;
}

// Fetch Williams %R value for current bar

double wprValue[1];

if (CopyBuffer(wprHandle, 0, 1, 1, wprValue) <= 0)

LogMessage("Failed to retrieve valid Williams %R value for current


bar.", LOG_LEVEL_WARNING);

LogFailedTrade("Williams %R Indicator Failure");

return;

wprValueGlobal = wprValue[0];

LogMessage("Williams %R Value: " + DoubleToString(wprValue[0], 2),


LOG_LEVEL_DEBUG);

bool buySignal = false, sellSignal = false;

double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);

// Fetch high and low volume levels from Volume Profile Indicator

double highVolumeLevel[1];

double lowVolumeLevel[1];

if (CopyBuffer(volumeProfileHandle, 0, 0, 1, highVolumeLevel) <= 0 ||


CopyBuffer(volumeProfileHandle, 1, 0, 1, lowVolumeLevel) <= 0)

LogMessage("Failed to retrieve volume profile data for


support/resistance levels.", LOG_LEVEL_WARNING);

return;

// Log Volume Profile Levels


LogMessage("High Volume Level: " +
DoubleToString(highVolumeLevel[0], _Digits) + ", Low Volume Level: " +
DoubleToString(lowVolumeLevel[0], _Digits), LOG_LEVEL_DEBUG,
LOG_CAT_ATR);

// Conditions for buy and sell signals based on Volume Profile levels and
trend direction

int trendDirection = GetTrendDirection();

if (wprValueGlobal <= WPR_Oversold && currentPrice <=


lowVolumeLevel[0] * 1.002 && trendDirection == 1)

buySignal = true;

LogMessage("Buy Signal with Volume Profile support and uptrend


alignment!", LOG_LEVEL_INFO, LOG_CAT_TRADE_EXECUTION);

if (wprValueGlobal >= WPR_Overbought && currentPrice >=


highVolumeLevel[0] * 0.998 && trendDirection == -1)

sellSignal = true;

LogMessage("Sell Signal with Volume Profile resistance and


downtrend alignment!", LOG_LEVEL_INFO, LOG_CAT_TRADE_EXECUTION);

// No signal detected; log and exit

if (!buySignal && !sellSignal)

LogMessage("No buy or sell signal triggered on this bar.",


LOG_LEVEL_DEBUG);

return;

}
// Check if tick volume is increasing

bool tickVolumeIncreasing = IsTickVolumeIncreasing();

// Execute Buy Trade if conditions are met

if (buySignal && tickVolumeIncreasing)

LogMessage("Executing Buy trade based on ATR, Williams %R,


Volume Profile support, and trend alignment.", LOG_LEVEL_INFO,
LOG_CAT_TRADE_EXECUTION);

ExecuteTrade(ORDER_TYPE_BUY, atrValue, "ATR, WPR, Volume Profile,


and Trend", wprValueGlobal);

tradeExecuted = true; // Set flag to indicate trade has been executed

lastTradeTime = TimeCurrent(); // Update lastTradeTime

else if (buySignal)

LogMessage("Buy signal detected but tick volume is not increasing or


trade already executed. Skipping trade.", LOG_LEVEL_INFO);

// Use 'else if' to prevent executing both trades

else if (sellSignal && tickVolumeIncreasing)

LogMessage("Executing Sell trade based on ATR, Williams %R,


Volume Profile resistance, and trend alignment.", LOG_LEVEL_INFO,
LOG_CAT_TRADE_EXECUTION);

ExecuteTrade(ORDER_TYPE_SELL, atrValue, "ATR, WPR, Volume


Profile, and Trend", wprValueGlobal);

tradeExecuted = true; // Set flag to indicate trade has been executed

lastTradeTime = TimeCurrent(); // Update lastTradeTime

}
else if (sellSignal)

LogMessage("Sell signal detected but tick volume is not increasing or


trade already executed. Skipping trade.", LOG_LEVEL_INFO);

//+------------------------------------------------------------------+

//| Open Position Function using CTrade with SL/TP Validation |

//+------------------------------------------------------------------+

bool OpenPosition(ENUM_ORDER_TYPE orderType, double lotSize, double


entryPrice, double slPrice, double tpPrice, string reason, double atrValue,
double wprValue)

// Validate SL and TP levels before opening the position

if (!CheckStopLossAndTakeProfit(orderType, slPrice, tpPrice, entryPrice))

LogFailedTrade("Invalid SL/TP levels", 0, "SL/TP levels do not meet


broker requirements");

return false;

// Execute the trade with proper SL and TP

trade.SetExpertMagicNumber(MagicNumber);

trade.SetDeviationInPoints(Slippage);

bool tradeResult = false;

ulong entryDealTicket = 0;

if (orderType == ORDER_TYPE_BUY)
{

tradeResult = trade.Buy(lotSize, NULL, entryPrice, slPrice, tpPrice,


"My EA Trade");

else

tradeResult = trade.Sell(lotSize, NULL, entryPrice, slPrice, tpPrice,


"My EA Trade");

if (tradeResult)

// Trade was successful, retrieve the deal ticket

entryDealTicket = trade.ResultDeal();

if (entryDealTicket == 0)

Print("Failed to get deal ticket after trade execution.");

return false;

// Log SL/TP values after trade execution

double executedSL = 0.0;

double executedTP = 0.0;

int retryCount = 0;

const int maxRetries = 5;

bool dealSelected = false;

// Retry to fetch SL/TP values, since they may not be immediately


available
while (retryCount < maxRetries)

if (HistoryDealSelect(entryDealTicket))

executedSL = HistoryDealGetDouble(entryDealTicket, DEAL_SL);

executedTP = HistoryDealGetDouble(entryDealTicket, DEAL_TP);

if (executedSL != 0.0 && executedTP != 0.0) // Ensure valid


values

dealSelected = true;

break;

Print("Waiting to retrieve SL/TP values...");

Sleep(200); // Wait 200ms before retrying

retryCount++;

if (!dealSelected)

Print("Failed to retrieve SL/TP values after retries.");

return false;

LogMessage("Executed SL=" + DoubleToString(executedSL, _Digits)


+ ", Executed TP=" + DoubleToString(executedTP, _Digits),
LOG_LEVEL_INFO);
// Check if executed SL/TP differs from calculated

if (NormalizeDouble(executedSL, _Digits) != NormalizeDouble(slPrice,


_Digits) ||

NormalizeDouble(executedTP, _Digits) !=
NormalizeDouble(tpPrice, _Digits))

LogMessage("ERROR: Broker executed different SL/TP than


expected.", LOG_LEVEL_ERROR);

// Retrieve the position ID from the deal

ulong positionID = HistoryDealGetInteger(entryDealTicket,


DEAL_POSITION_ID);

if (positionID == 0)

LogMessage("Failed to get position ID from deal.",


LOG_LEVEL_ERROR);

return false;

// Store the open position data

OpenPositionData newPosition;

newPosition.entryDealTicket = entryDealTicket;

newPosition.positionID = positionID;

newPosition.symbol = _Symbol;

newPosition.tradeType = (orderType == ORDER_TYPE_BUY) ? "Buy" :


"Sell";

newPosition.entryPrice = entryPrice;

newPosition.entryTime = TimeCurrent();

newPosition.atr = atrValue;

newPosition.wprValue = wprValue;
newPosition.lotSize = lotSize;

newPosition.reasonEntry = reason;

newPosition.date = TimeToString(newPosition.entryTime,
TIME_DATE);

newPosition.time = TimeToString(newPosition.entryTime,
TIME_SECONDS | TIME_MINUTES);

newPosition.currentStopLoss = slPrice;

newPosition.takeProfitPrice = tpPrice;

newPosition.profitLevelReached = 0;

newPosition.trailingStopActivated = false;

newPosition.profitLevelAtTrailingStop = 0.0;

newPosition.isLogged = false;

// Add the new position to the openPositions array

AddOpenPosition(newPosition);

// Debug logging

LogMessage("Opened position: positionID=" +


IntegerToString(positionID) + ", entryDealTicket=" +
IntegerToString(entryDealTicket) + ", symbol=" + _Symbol + ",
tradeType=" + newPosition.tradeType, LOG_LEVEL_INFO);

LogMessage("Current openPositions count: " +


IntegerToString(ArraySize(openPositions)), LOG_LEVEL_INFO);

// Update the last trade time to avoid overtrading

lastTradeTime = TimeCurrent();

// Set a flag to indicate a new trade was executed, requiring trailing


stop management

bool trailingStopPending = true;


return true;

else

// Log failed trade execution

uint errorCode = trade.ResultRetcode();

string errorDescription = trade.ResultRetcodeDescription();

LogFailedTrade("Trade Order Failed", errorCode, errorDescription);

return false;

//+------------------------------------------------------------------+

//| Adding Open Positions |

//+------------------------------------------------------------------+

void AddOpenPosition(OpenPositionData &positionData)

positionData.profitLevelReached = 0; // Initialize profitLevelReached

ArrayResize(openPositions, ArraySize(openPositions) + 1);

openPositions[ArraySize(openPositions) - 1] = positionData;

//+------------------------------------------------------------------+

//| Finding Open Positions |

//+------------------------------------------------------------------+

int FindOpenPositionIndex(ulong positionID)

for (int i = 0; i < ArraySize(openPositions); i++)

{
if (openPositions[i].positionID == positionID)

return i;

return -1; // Not found

//+------------------------------------------------------------------+

//| Removing Open Positions |

//+------------------------------------------------------------------+

void RemoveOpenPositionByIndex(int index)

if (index >= 0 && index < ArraySize(openPositions))

for (int i = index; i < ArraySize(openPositions) - 1; i++)

openPositions[i] = openPositions[i + 1];

ArrayResize(openPositions, ArraySize(openPositions) - 1);

//+------------------------------------------------------------------+

//| OnTradeTransaction Event Handler |

//+------------------------------------------------------------------+

void OnTradeTransaction(const MqlTradeTransaction &trans, const


MqlTradeRequest &request, const MqlTradeResult &result)

{
if (trans.type == TRADE_TRANSACTION_DEAL_ADD)

ulong dealTicket = trans.deal;

if (HistoryDealSelect(dealTicket))

ulong positionID = HistoryDealGetInteger(dealTicket,


DEAL_POSITION_ID);

ENUM_DEAL_ENTRY dealEntry =
(ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);

if (dealEntry == DEAL_ENTRY_OUT || dealEntry ==


DEAL_ENTRY_OUT_BY)

// Add this check to prevent duplicate logging

if (IsPositionLogged(positionID))

LogMessage("Position ID " + IntegerToString(positionID) + "


has already been logged. Skipping.", LOG_LEVEL_DEBUG);

return;

LogMessage("Deal " + IntegerToString(dealTicket) + " is a


closing deal for position " + IntegerToString(positionID),

LOG_LEVEL_INFO, LOG_CAT_TRADE_EXECUTION);

// Log the closed trade

LogClosedTrade(positionID, dealTicket);

// Remove the position from openPositions after logging

int index = FindOpenPositionIndex(positionID);


if (index != -1)

RemoveOpenPositionByIndex(index);

LogMessage("Removed position from openPositions in


OnTradeTransaction: positionID=" + IntegerToString(positionID),

LOG_LEVEL_DEBUG, LOG_CAT_TRADE_EXECUTION);

//+------------------------------------------------------------------+

//| Check for Closed Positions |

//+------------------------------------------------------------------+

void CheckForClosedPositions()

// Ensure the history is up to date

HistorySelect(0, TimeCurrent());

int i = 0;

while (i < ArraySize(openPositions))

ulong positionID = openPositions[i].positionID;

// Check if the position is still open

if (!PositionSelectByTicket(positionID))

// Position is no longer open; it has been closed


LogMessage("Position " + IntegerToString(positionID) + " is closed.
Attempting to find closing deal.", LOG_LEVEL_DEBUG);

// Check if positionID has already been logged

if (IsPositionLogged(positionID))

LogMessage("Position ID " + IntegerToString(positionID) + " has


already been logged. Skipping.", LOG_LEVEL_DEBUG);

// Remove from openPositions

RemoveOpenPositionByIndex(i);

continue;

ulong closingDealTicket = FindClosingDealForPosition(positionID);

if (closingDealTicket != 0)

// Log the trade

LogClosedTrade(positionID, closingDealTicket);

// Remove from openPositions

RemoveOpenPositionByIndex(i);

LogMessage("Removed position from openPositions:


positionID=" + IntegerToString(positionID), LOG_LEVEL_DEBUG);

// Do not increment i, as the array has shrunk

continue;

else

{
LogMessage("Closing deal not found for position ID: " +
IntegerToString(positionID), LOG_LEVEL_WARNING);

i++; // Increment i to check the next position

else

LogMessage("Position " + IntegerToString(positionID) + " is still


open.", LOG_LEVEL_DEBUG);

i++; // Only increment if no removal

//+------------------------------------------------------------------+

//| Find Closing Deal for Position |

//+------------------------------------------------------------------+

ulong FindClosingDealForPosition(ulong positionID)

// Ensure the history is refreshed

HistorySelect(0, TimeCurrent());

// Get the number of deals in history

int totalDeals = HistoryDealsTotal();

for (int i = totalDeals - 1; i >= 0; i--)

ulong dealTicket = HistoryDealGetTicket(i);

if (HistoryDealSelect(dealTicket))

{
ulong dealPositionID = HistoryDealGetInteger(dealTicket,
DEAL_POSITION_ID);

if (dealPositionID == positionID)

ENUM_DEAL_ENTRY dealEntry =
(ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);

if (dealEntry == DEAL_ENTRY_OUT || dealEntry ==


DEAL_ENTRY_OUT_BY)

LogMessage("Found closing deal: dealTicket=" +


IntegerToString(dealTicket) + " for positionID=" +
IntegerToString(positionID), LOG_LEVEL_DEBUG);

return dealTicket;

else

LogMessage("Failed to select dealTicket=" +


IntegerToString(dealTicket), LOG_LEVEL_WARNING);

LogMessage("No closing deal found for positionID=" +


IntegerToString(positionID), LOG_LEVEL_WARNING);

return 0; // No closing deal found

//+------------------------------------------------------------------+

//| Log Closed Trade Function |

//+------------------------------------------------------------------+

void LogClosedTrade(ulong positionID, ulong closingDealTicket)


{

// Check if the positionID has already been logged

if (IsPositionLogged(positionID))

LogMessage("Position ID " + IntegerToString(positionID) + " has


already been logged. Skipping.", LOG_LEVEL_DEBUG);

return;

// Ensure the closing deal exists in history

if (!HistoryDealSelect(closingDealTicket))

Print("Failed to select closing deal ", closingDealTicket);

return;

// Attempt to find the open position data

int index = FindOpenPositionIndex(positionID);

TradeData tradeData;

if (index == -1)

// Entry data not found; reconstruct trade data from deals

PrintFormat("Open position data not found for position ID: %I64u",


positionID);

// Get the opening deal

ulong openingDealTicket = FindOpeningDealForPosition(positionID);

if (openingDealTicket == 0)
{

PrintFormat("Opening deal not found for position ID: %I64u",


positionID);

return;

if (!HistoryDealSelect(openingDealTicket))

PrintFormat("Failed to select opening deal %I64u",


openingDealTicket);

return;

datetime entryTime =
(datetime)HistoryDealGetInteger(openingDealTicket, DEAL_TIME);

tradeData.entryDate = TimeToString(entryTime, TIME_DATE);

tradeData.entryTime = TimeToString(entryTime, TIME_SECONDS |


TIME_MINUTES);

tradeData.entryPrice = HistoryDealGetDouble(openingDealTicket,
DEAL_PRICE);

tradeData.reasonEntry = "Unknown (Entry data not found)";

tradeData.lotSize = HistoryDealGetDouble(openingDealTicket,
DEAL_VOLUME);

tradeData.duration = (long)(HistoryDealGetInteger(closingDealTicket,
DEAL_TIME) - HistoryDealGetInteger(openingDealTicket, DEAL_TIME));

tradeData.tradeType = HistoryDealGetInteger(openingDealTicket,
DEAL_TYPE) == DEAL_TYPE_BUY ? "Buy" : "Sell";

tradeData.symbol = HistoryDealGetString(closingDealTicket,
DEAL_SYMBOL);

tradeData.exitPrice = HistoryDealGetDouble(closingDealTicket,
DEAL_PRICE);
datetime exitTime =
(datetime)HistoryDealGetInteger(closingDealTicket, DEAL_TIME);

tradeData.exitDate = TimeToString(exitTime, TIME_DATE);

tradeData.exitTime = TimeToString(exitTime, TIME_SECONDS |


TIME_MINUTES);

// Retrieve profit, swap, and commission from the closing deal

double dealProfit = HistoryDealGetDouble(closingDealTicket,


DEAL_PROFIT);

double dealSwap = HistoryDealGetDouble(closingDealTicket,


DEAL_SWAP);

double dealCommission = HistoryDealGetDouble(closingDealTicket,


DEAL_COMMISSION);

tradeData.profitLoss = dealProfit + dealSwap + dealCommission;

tradeData.swap = dealSwap;

tradeData.commission = dealCommission;

// Use GetReasonExitString to get user-friendly reason

ENUM_DEAL_REASON reason =
(ENUM_DEAL_REASON)HistoryDealGetInteger(closingDealTicket,
DEAL_REASON);

tradeData.reasonExit = GetReasonExitString(reason);

tradeData.remarks = "Logged without open position data";

// Log the completed trade

PrintFormat("Logging trade (no open position data): positionID=


%I64u, symbol=%s, tradeType=%s", positionID, tradeData.symbol,
tradeData.tradeType);

LogTrade(tradeData);
LogMessage("Trade closed (no open position data): positionID=" +
IntegerToString(positionID) +

", symbol=" + tradeData.symbol +

", tradeType=" + tradeData.tradeType +

", profitLoss=" + DoubleToString(tradeData.profitLoss, 2),

LOG_LEVEL_INFO, LOG_CAT_TRADE_EXECUTION);

return;

// Code for when open position data is found

// Retrieve existing open position data to log it correctly

OpenPositionData entryData = openPositions[index];

// Prepare trade data using stored entry data

tradeData.positionID = entryData.positionID; // Position ID from open


position data

tradeData.entryDate = entryData.date;

tradeData.entryTime = entryData.time;

tradeData.entryPrice = entryData.entryPrice;

tradeData.reasonEntry = entryData.reasonEntry;

tradeData.lotSize = entryData.lotSize;

tradeData.duration = (long)(HistoryDealGetInteger(closingDealTicket,
DEAL_TIME) - entryData.entryTime);

tradeData.symbol = HistoryDealGetString(closingDealTicket,
DEAL_SYMBOL);

tradeData.exitPrice = HistoryDealGetDouble(closingDealTicket,
DEAL_PRICE);

tradeData.tradeType = entryData.tradeType; // Fetch trade type


datetime exitTime =
(datetime)HistoryDealGetInteger(closingDealTicket, DEAL_TIME);

tradeData.exitDate = TimeToString(exitTime, TIME_DATE);

tradeData.exitTime = TimeToString(exitTime, TIME_SECONDS |


TIME_MINUTES);

// Log additional relevant data

tradeData.atr = entryData.atr; // ATR from open position

tradeData.wprValue = entryData.wprValue; // Williams %R from open


position

ENUM_DEAL_REASON reason =
(ENUM_DEAL_REASON)HistoryDealGetInteger(closingDealTicket,
DEAL_REASON);

tradeData.reasonExit = GetReasonExitString(reason);

// Retrieve profit, swap, and commission from the closing deal

double dealProfit = HistoryDealGetDouble(closingDealTicket,


DEAL_PROFIT);

double dealSwap = HistoryDealGetDouble(closingDealTicket,


DEAL_SWAP);

double dealCommission = HistoryDealGetDouble(closingDealTicket,


DEAL_COMMISSION);

tradeData.profitLoss = dealProfit + dealSwap + dealCommission;

tradeData.swap = dealSwap;

tradeData.commission = dealCommission;

tradeData.remarks = ""; // No additional remarks needed when open


data is found

ArrayResize(loggedPositionIDs, ArraySize(loggedPositionIDs) + 1);


loggedPositionIDs[ArraySize(loggedPositionIDs) - 1] = positionID;

// Log the completed trade with open position data found

PrintFormat("Logging trade with open position data: positionID=%I64u,


symbol=%s, tradeType=%s", positionID, tradeData.symbol,
tradeData.tradeType);

LogTrade(tradeData);

//+------------------------------------------------------------------+

//| Find Opening Deal for Position |

//+------------------------------------------------------------------+

ulong FindOpeningDealForPosition(ulong positionID)

// Ensure the history is refreshed

HistorySelect(0, TimeCurrent());

// Get the number of deals in history

int totalDeals = HistoryDealsTotal();

for (int i = totalDeals - 1; i >= 0; i--)

ulong dealTicket = HistoryDealGetTicket(i);

if (HistoryDealSelect(dealTicket))

ulong dealPositionID = HistoryDealGetInteger(dealTicket,


DEAL_POSITION_ID);

if (dealPositionID == positionID)

ENUM_DEAL_ENTRY dealEntry =
(ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
if (dealEntry == DEAL_ENTRY_IN)

LogMessage("Found opening deal: dealTicket=" +


IntegerToString(dealTicket) + " for positionID=" +
IntegerToString(positionID), LOG_LEVEL_DEBUG);

return dealTicket;

else

LogMessage("Failed to select dealTicket=" +


IntegerToString(dealTicket), LOG_LEVEL_WARNING);

PrintFormat("No opening deal found for positionID=%I64u", positionID);

return 0; // No opening deal found

//+------------------------------------------------------------------+

//| Log Trade Function |

//+------------------------------------------------------------------+

void LogTrade(TradeData &tradeData)

// Sanitize strings for SQL

string sanitizedReasonEntry = tradeData.reasonEntry;

string sanitizedReasonExit = tradeData.reasonExit;

string sanitizedRemarks = tradeData.remarks;

StringReplace(sanitizedReasonEntry, "'", "''");

StringReplace(sanitizedReasonExit, "'", "''");


StringReplace(sanitizedRemarks, "'", "''");

// Prepare SQL INSERT statement without TradeID

string insertQuery = "INSERT INTO trades (EntryDate, EntryTime,


ExitDate, ExitTime, Symbol, TradeType, EntryPrice, ExitPrice, "

"ReasonEntry, ReasonExit, ProfitLoss, Swap, Commission,


ATR, WPRValue, Duration, LotSize, Remarks) VALUES (" +

"'" + tradeData.entryDate + "'," +

"'" + tradeData.entryTime + "'," +

"'" + tradeData.exitDate + "'," +

"'" + tradeData.exitTime + "'," +

"'" + tradeData.symbol + "'," +

"'" + tradeData.tradeType + "'," +

DoubleToString(tradeData.entryPrice, _Digits) + "," +

DoubleToString(tradeData.exitPrice, _Digits) + "," +

"'" + sanitizedReasonEntry + "'," +

"'" + sanitizedReasonExit + "'," +

DoubleToString(tradeData.profitLoss, 2) + "," +

DoubleToString(tradeData.swap, 2) + "," + // Include


swap

DoubleToString(tradeData.commission, 2) + "," + //
Include commission

DoubleToString(tradeData.atr, _Digits) + "," +

DoubleToString(tradeData.wprValue, 2) + "," +

IntegerToString(tradeData.duration) + "," +

DoubleToString(tradeData.lotSize, 2) + "," +

"'" + sanitizedRemarks + "');";

// Convert query to char array for ExecuteSQL

char query[4096];
StringToCharArray(insertQuery, query);

char errorMsg[256];

// Execute SQL without checking LogLevel

int execResult = ExecuteSQL(dbHandle, query, errorMsg,


sizeof(errorMsg));

if (execResult != 0)

PrintFormat("Error inserting trade log to database: %s",


CharArrayToString(errorMsg));

else

Print("Trade logged to database successfully.");

//+------------------------------------------------------------------+

//| Validate Indicator Value |

//+------------------------------------------------------------------+

bool IsValidValue(double value)

return MathIsValidNumber(value);

//+------------------------------------------------------------------+

//| Trading Hours Check |

//+------------------------------------------------------------------+

bool IsWithinTradingHours()

{
datetime currentTime = TimeCurrent();

MqlDateTime timeStruct;

TimeToStruct(currentTime, timeStruct);

// Exclude weekends (Saturday and Sunday)

if (timeStruct.day_of_week == 0 || timeStruct.day_of_week == 6)

return false;

return true;

//+------------------------------------------------------------------+

//| Execute Trade Function |

//+------------------------------------------------------------------+

void ExecuteTrade(ENUM_ORDER_TYPE orderType, double atrValue, string


reason, double wprValue)

double stopLossPoints, takeProfitPoints;

// Calculate SL/TP values based on ATR and risk-to-reward ratio

if (!CalculateSLTP(atrValue, stopLossPoints, takeProfitPoints))

LogFailedTrade("Failed to calculate valid SL/TP"); // Calls centralized


error logging

return;

// Calculate lot size based on risk level adjusted for volatility


double lotSize = CalculateLotSize(stopLossPoints, isHighVolatility ?
HighRisk : LowRisk);

// Get entry price based on order type

double entryPrice = (orderType == ORDER_TYPE_BUY) ?


SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol,
SYMBOL_BID);

double slPrice = 0.0, tpPrice = 0.0;

// Calculate Stop Loss (SL) and Take Profit (TP) prices

if (orderType == ORDER_TYPE_BUY)

slPrice = NormalizeDouble(entryPrice - stopLossPoints * _Point,


_Digits);

tpPrice = NormalizeDouble(entryPrice + takeProfitPoints * _Point,


_Digits);

else if (orderType == ORDER_TYPE_SELL)

slPrice = NormalizeDouble(entryPrice + stopLossPoints * _Point,


_Digits);

tpPrice = NormalizeDouble(entryPrice - takeProfitPoints * _Point,


_Digits);

// Attempt to open the position using the calculated SL and TP

bool tradeSuccess = OpenPosition(orderType, lotSize, entryPrice,


slPrice, tpPrice, reason, atrValue, wprValue);

// Log success or failure of the trade execution

if (tradeSuccess)

{
// Increment the daily trade count

dailyTradeCount++;

LogMessage("Trade executed successfully. Daily trade count: " +


IntegerToString(dailyTradeCount), LOG_LEVEL_INFO);

// Centralized logging for trade execution details

LogMessage("Trade Executed: " + ((orderType == ORDER_TYPE_BUY)


? "Buy" : "Sell") +

" Entry=" + DoubleToString(entryPrice, _Digits) +

" SL=" + DoubleToString(slPrice, _Digits) +

" TP=" + DoubleToString(tpPrice, _Digits) +

" Lot=" + DoubleToString(lotSize, 2), LOG_LEVEL_INFO,


LOG_CAT_TRADE_EXECUTION);

else

LogMessage("Trade execution failed", LOG_LEVEL_ERROR,


LOG_CAT_ERRORS);

//+------------------------------------------------------------------+

//| SL/TP Calculation: SL based on ATR, TP based on Risk-to-Reward |

//+------------------------------------------------------------------+

bool CalculateSLTP(double atr, double &stopLossPoints, double


&takeProfitPoints)

double riskToRewardRatio = RiskToRewardRatio; // Use the input


parameter

// Step 1: Set Stop Loss based on ATR and volatility


if (isHighVolatility)

stopLossPoints = (atr * atrStopLossMultiplierHighVolatility) / _Point; //


SL for high volatility

else

stopLossPoints = (atr * atrStopLossMultiplierLowVolatility) / _Point; //


SL for low volatility

// Step 2: Calculate Take Profit using the Risk-to-Reward ratio

takeProfitPoints = stopLossPoints * riskToRewardRatio;

// Log the calculated SL/TP values

LogMessage("SL=" + DoubleToString(stopLossPoints, _Digits) +

", TP=" + DoubleToString(takeProfitPoints, _Digits),


LOG_LEVEL_DEBUG, LOG_CAT_SLTP_VALUES);

// Step 3: Validate SL/TP against broker's minimum stop level

double minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,


SYMBOL_TRADE_STOPS_LEVEL);

if (minStopLevelPoints == 0)

minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,
SYMBOL_TRADE_FREEZE_LEVEL);

// Adjust SL/TP if needed


if (stopLossPoints < minStopLevelPoints)

Print("Stop Loss is less than the minimum stop level, adjusting: ",
stopLossPoints, " to ", minStopLevelPoints);

stopLossPoints = minStopLevelPoints; // Adjust to meet broker’s


minimum stop level

// Recalculate Take Profit based on the new Stop Loss

takeProfitPoints = stopLossPoints * riskToRewardRatio;

PrintFormat("Adjusted TP based on new SL: %.5f", takeProfitPoints);

return true;

//+------------------------------------------------------------------+

//| Lot Size Calculation based on Risk Percentage |

//+------------------------------------------------------------------+

double CalculateLotSize(double stopLossPoints, double riskPercentage)

double balance = AccountInfoDouble(ACCOUNT_BALANCE);

double riskAmount = balance * riskPercentage / 100.0;

double tickSize = SymbolInfoDouble(_Symbol,


SYMBOL_TRADE_TICK_SIZE);

double tickValue = SymbolInfoDouble(_Symbol,


SYMBOL_TRADE_TICK_VALUE);

// Calculate the value per point per lot

double pointValuePerLot = tickValue / tickSize;


// Calculate lot size

double lotSize = riskAmount / (stopLossPoints * _Point *


pointValuePerLot);

// Adjust for lot step and min/max volume

double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);

double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);

double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);

// Round down to nearest lot step

lotSize = MathFloor(lotSize / lotStep) * lotStep;

// Ensure lot size is within allowed limits

lotSize = MathMax(minLot, MathMin(lotSize, maxLot));

// Log risk management calculations

LogMessage("Risk Percentage: " + DoubleToString(riskPercentage, 2),


LOG_LEVEL_INFO, LOG_CAT_RISK_MANAGEMENT);

return lotSize;

//+------------------------------------------------------------------+

//| Check Stop Loss and Take Profit Levels |

//+------------------------------------------------------------------+

bool CheckStopLossAndTakeProfit(ENUM_ORDER_TYPE orderType, double


slPrice, double tpPrice, double entryPrice) {

double minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,


SYMBOL_TRADE_STOPS_LEVEL);

if (minStopLevelPoints == 0)
minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,
SYMBOL_TRADE_FREEZE_LEVEL);

double slDistance = 0.0;

double tpDistance = 0.0;

if (orderType == ORDER_TYPE_BUY) {

slDistance = (entryPrice - slPrice) / _Point;

tpDistance = (tpPrice - entryPrice) / _Point;

} else if (orderType == ORDER_TYPE_SELL) {

slDistance = (slPrice - entryPrice) / _Point;

tpDistance = (entryPrice - tpPrice) / _Point;

slDistance = MathAbs(slDistance);

tpDistance = MathAbs(tpDistance);

if (slDistance < minStopLevelPoints || tpDistance < minStopLevelPoints)


{

LogSLTPValidation(slPrice, tpPrice, minStopLevelPoints, orderType); //


Log SL/TP values if validation fails

LogError("SL or TP too close to price. Minimum stop level: " +


DoubleToString(minStopLevelPoints, _Digits));

return false;

LogMessage("SL=" + DoubleToString(slPrice, _Digits) + " and TP=" +


DoubleToString(tpPrice, _Digits) + " passed validation.", LOG_LEVEL_INFO,
LOG_CAT_SLTP_VALUES);

return true;

}
//+------------------------------------------------------------------+

//| Consolidate ATR Logging into LogMessage |

//+------------------------------------------------------------------+

double GetATR(int period) {

int atrHandle = iATR(_Symbol, ATRTimeframe, period); // Use


ATRTimeframe here

if (atrHandle == INVALID_HANDLE) {

LogMessage("Invalid ATR handle. Cannot retrieve ATR.",


LOG_LEVEL_ERROR, LOG_CAT_ERRORS);

return 0;

double atrValues[1];

if (CopyBuffer(atrHandle, 0, 1, 1, atrValues) <= 0) {

LogMessage("Failed to retrieve ATR values.", LOG_LEVEL_WARNING,


LOG_CAT_ERRORS);

return 0;

IndicatorRelease(atrHandle);

return atrValues[0];

//+------------------------------------------------------------------+

//| Helper Function to Get Indicator Value |

//+------------------------------------------------------------------+

double GetIndicatorValue(int handle, int bufferIndex = 0)

double value[1];
if (CopyBuffer(handle, bufferIndex, 1, 1, value) <= 0)

return 0.0;

else

return value[0];

//+------------------------------------------------------------------+

//| Log Failed Trade Function |

//+------------------------------------------------------------------+

void LogFailedTrade(string reason, uint errorCode = 0, string


errorDescription = "") {

datetime currentTime = TimeCurrent();

string date = TimeToString(currentTime, TIME_DATE);

string time = TimeToString(currentTime, TIME_SECONDS |


TIME_MINUTES);

string symbol = _Symbol;

// Sanitize and handle single quotes in reason

string sanitizedReason = SanitizeForSQL(reason);

// Fetch or generate the error code and description

if (errorCode == 0) {

errorCode = GetLastError();

errorDescription = ErrorDescription((int)errorCode);

ResetLastError();

// Use FormatErrorMessage to create the full reason message

string fullReason = sanitizedReason + " - " +


FormatErrorMessage(errorCode, errorDescription);
// Prepare the SQL query

string insertQuery = "INSERT INTO TradeLog (Date, Time, Symbol,


Remarks, ATR) VALUES ("

"'" + date + "',"

"'" + time + "',"

"'" + symbol + "',"

"'" + SanitizeForSQL(fullReason) + "',"

+ DoubleToString(GetATR(atrPeriod), _Digits) + ");";

// Execute the SQL query

char query[2048];

StringToCharArray(insertQuery, query);

char errorMsg[256];

// If the SQL execution fails, log the error in the console

int execResult = ExecuteSQL(dbHandle, query, errorMsg,


sizeof(errorMsg));

if (execResult != 0) {

PrintFormat("Error inserting failed trade log. Error: %s",


CharArrayToString(errorMsg));

//+------------------------------------------------------------------+

//| Error Description Function |

//+------------------------------------------------------------------+

string ErrorDescription(int errorCode)

switch(errorCode)
{

case 0: return "No error";

case 1: return "No result, but error is unknown";

case 2: return "Common error";

case 3: return "Invalid trade parameters";

case 4: return "Trade server busy";

case 5: return "Old version of the client terminal";

case 6: return "No connection with trade server";

case 7: return "Not enough rights";

case 8: return "Too frequent requests";

case 9: return "Malfunctional trade operation";

case 10: return "Invalid account";

case 11: return "Invalid price";

case 12: return "Invalid stops";

case 13: return "Invalid trade volume";

case 14: return "Market closed";

case 15: return "Trade disabled";

case 16: return "Not enough money";

case 17: return "Price changed";

// More cases can be added as needed...

default: return "Unknown or custom error code"; // Default for


unexpected error codes

//+------------------------------------------------------------------+

//| Get Reason Exit String |

//+------------------------------------------------------------------+

string GetReasonExitString(ENUM_DEAL_REASON reason)


{

switch(reason)

case DEAL_REASON_SL:

return "Stop Loss Hit";

case DEAL_REASON_TP:

return "Take Profit Hit";

case DEAL_REASON_SO:

return "Stop Out";

case DEAL_REASON_CLIENT:

return "Closed Manually";

case DEAL_REASON_EXPERT:

return "Closed by Expert";

// Add other cases as needed

default:

return "Unknown Reason";

//+------------------------------------------------------------------+

//| Export Trade Log to Excel |

//+------------------------------------------------------------------+

void ExportTradeLogToExcel()

// Ensure that logs are flushed to the database before exporting

FlushLogsToDatabase();

// Define the CSV file path


string filesDir = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\
Files";

string csvFilePath = filesDir + "\\TradeLog.csv";

char csvFile[512];

StringToCharArray(csvFilePath, csvFile);

// Error message buffer

char errorMsg[256];

ZeroMemory(errorMsg);

// Check if database handle is valid

if (dbHandle == 0)

Print("Database handle is invalid. Cannot export trade logs.");

return;

// Call the ExportTradeLogsToCSV function

int result = ExportTradeLogsToCSV(dbHandle, csvFile, errorMsg,


sizeof(errorMsg));

if (result != 0)

PrintFormat("Error exporting trade logs to CSV. Error: %s",


CharArrayToString(errorMsg));

return;

else

Print("Trade logs exported to CSV successfully.");


}

// Open the CSV file using ShellExecute

bool openResult = OpenCSVFileInExcel(csvFilePath);

if (!openResult)

Print("Failed to open the CSV file in Excel.");

//+------------------------------------------------------------------+

//| Open CSV File in Excel |

//+------------------------------------------------------------------+

bool OpenCSVFileInExcel(string csvFilePath)

// Use ShellExecute to open the CSV file with the default associated
program (Excel)

int res = ShellExecuteW(0, "open", csvFilePath, "", "", 1);

return (res > 32);

//+------------------------------------------------------------------+

//| Manage Trailing Stop to Lock in Profits based on percentages |

//+------------------------------------------------------------------+

void ManageTrailingStop(ulong positionID, double entryPrice, double


takeProfitPrice, double riskPercentage)

// Ensure trailing stop is enabled

if (!EnableTrailingStop)

LogMessage("Trailing stop is disabled.", LOG_LEVEL_INFO);


return;

// Select the position by ticket

if (!PositionSelectByTicket(positionID))

LogMessage("Failed to select position with ID " +


IntegerToString(positionID), LOG_LEVEL_ERROR, LOG_CAT_ERRORS);

return; // Do not remove the position here

// Find the position index

int index = FindOpenPositionIndex(positionID);

if (index == -1)

PrintFormat("Position %I64u not found in openPositions.", positionID);

return;

long tradeType = PositionGetInteger(POSITION_TYPE); // Buy or Sell

double currentSL = PositionGetDouble(POSITION_SL); // Current Stop


Loss

// Get the current price based on the position type

double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);

double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

double currentPriceForCalc = (tradeType == POSITION_TYPE_BUY) ?


currentBid : currentAsk;

// Calculate total profit target and current profit achieved


double totalProfitTarget = 0.0;

double profitAchieved = 0.0;

if (tradeType == POSITION_TYPE_BUY)

totalProfitTarget = takeProfitPrice - entryPrice;

if (totalProfitTarget <= 0)

LogMessage("Total profit target is zero or negative; cannot


calculate profit achieved.", LOG_LEVEL_WARNING);

return;

profitAchieved = currentPriceForCalc - entryPrice;

else if (tradeType == POSITION_TYPE_SELL)

totalProfitTarget = entryPrice - takeProfitPrice;

if (totalProfitTarget <= 0)

Print("Total profit target is zero or negative, cannot calculate profit


achieved.");

return;

profitAchieved = entryPrice - currentPriceForCalc;

double currentProfitPercentage = (profitAchieved / totalProfitTarget) *


100.0;

// Log the percentage of the profit target achieved


LogMessage("Current profit target achieved: " +
DoubleToString(currentProfitPercentage, 2) + "%", LOG_LEVEL_DEBUG);

// Define profit levels

double profitLevels[] = {ProfitLevel1, ProfitLevel2, ProfitLevel3,


ProfitLevel4, ProfitLevel5};

int numLevels = ArraySize(profitLevels);

// Use profitLevelReached from the position data

int profitLevelReached = openPositions[index].profitLevelReached;

for (int i = 0; i < numLevels; i++)

if (currentProfitPercentage >= profitLevels[i] && profitLevelReached


< (i + 1))

double desiredSL = 0.0;

// Move SL to lock in profits based on profit levels

if (tradeType == POSITION_TYPE_BUY)

// Calculate desired SL price

desiredSL = entryPrice + ((profitLevels[i] / 100.0) *


totalProfitTarget) * 0.5; // Lock in 50% of the profit at this level

desiredSL = MathMax(desiredSL, entryPrice); // Ensure SL is at


least at entry price

desiredSL = NormalizeDouble(desiredSL, _Digits);

else if (tradeType == POSITION_TYPE_SELL)

{
desiredSL = entryPrice - ((profitLevels[i] / 100.0) *
totalProfitTarget) * 0.5; // Lock in 50% of the profit at this level

desiredSL = MathMin(desiredSL, entryPrice); // Ensure SL is at


least at entry price

desiredSL = NormalizeDouble(desiredSL, _Digits);

LogMessage("Profit Target: " +


DoubleToString(currentProfitPercentage, 2) + "% achieved, Adjusting SL to
" + DoubleToString(desiredSL, _Digits), LOG_LEVEL_DEBUG);

// Try to modify the SL, adjusting if necessary due to broker


constraints

if (ModifyPositionSL(positionID, desiredSL, tradeType))

openPositions[index].currentStopLoss = desiredSL; // Update


current stop loss in the position data

openPositions[index].profitLevelReached = i + 1; // Update profit


level only if SL modification is successful

// Set trailing stop activation

openPositions[index].trailingStopActivated = true;

openPositions[index].profitLevelAtTrailingStop =
profitLevels[i]; // Record the profit level

LogMessage("Successfully modified SL for position ID " +


IntegerToString(positionID) + " to " + DoubleToString(desiredSL, _Digits),
LOG_LEVEL_DEBUG);

else

// If unable to set SL to desired level, set it as close as possible


double adjustedSL = AdjustSLToBrokerLimits(desiredSL,
tradeType);

if (MathAbs(adjustedSL - currentSL) > (_Point * 0.1)) // Check if


there is an actual change

if (ModifyPositionSL(positionID, adjustedSL, tradeType, true))

openPositions[index].currentStopLoss = adjustedSL; //
Update current stop loss in the position data

openPositions[index].profitLevelReached = i + 1; // Update
profit level only if SL modification is successful

// Set trailing stop activation

openPositions[index].trailingStopActivated = true;

openPositions[index].profitLevelAtTrailingStop =
profitLevels[i]; // Record the profit level

LogMessage("Adjusted SL for position ID " +


IntegerToString(positionID) + " to closest allowed level " +
DoubleToString(adjustedSL, _Digits), LOG_LEVEL_INFO);

else

LogMessage("Failed to modify SL for position ID " +


IntegerToString(positionID) + " even after adjustment",
LOG_LEVEL_WARNING);

// Do not update profitLevelReached so the EA can retry

else

{
LogMessage("SL is already at the closest possible level. No
modification made.", LOG_LEVEL_INFO);

// Update profitLevelReached to prevent repeated attempts

openPositions[index].profitLevelReached = i + 1;

break; // Only attempt once per tick

//+------------------------------------------------------------------+

//| Draw Trailing Stop Line |

//+------------------------------------------------------------------+

void DrawTrailingStop(ulong positionID, double newSL)

string objName = "TrailingStop_" + IntegerToString((int)positionID);

if(ObjectFind(0, objName) < 0)

ObjectCreate(0, objName, OBJ_HLINE, 0, 0, newSL);

ObjectSetInteger(0, objName, OBJPROP_COLOR, clrRed);

ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DASH);

ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);

else

ObjectSetDouble(0, objName, OBJPROP_PRICE, newSL);

}
//+------------------------------------------------------------------+

//| Modify Position Stop Loss and Preserve TP |

//+------------------------------------------------------------------+

bool ModifyPositionSL(ulong positionID, double desiredSL, long tradeType,


bool isAdjusted = false)

MqlTradeRequest request;

MqlTradeResult result;

ZeroMemory(request);

ZeroMemory(result);

if (!PositionSelectByTicket(positionID))

PrintFormat("ModifyPositionSL: Failed to select position ID %I64u.",


positionID);

return false;

double currentTP = PositionGetDouble(POSITION_TP); // Preserve the


current TP

double currentPrice = (tradeType == POSITION_TYPE_BUY) ?


SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol,
SYMBOL_ASK);

double currentSL = PositionGetDouble(POSITION_SL); // Get the


current SL

// Get the minimum stop level in points

double minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,


SYMBOL_TRADE_STOPS_LEVEL);

if (minStopLevelPoints == 0)

minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,
SYMBOL_TRADE_FREEZE_LEVEL);
double minStopDistance = minStopLevelPoints * _Point;

double newSL = desiredSL;

// Adjust newSL to account for the minimum stop distance

if (tradeType == POSITION_TYPE_BUY)

double minValidSL = currentPrice - minStopDistance;

if (newSL >= minValidSL)

if (!isAdjusted)

// Cannot set SL to desired level due to broker constraints

return false;

else

// Already adjusted, accept the newSL

newSL = minValidSL - (_Point * 0.1); // Slightly more to ensure it


passes

if (newSL <= currentSL)

LogMessage("New SL is not better than the current SL for BUY


position.", LOG_LEVEL_WARNING);

return false;

else if (tradeType == POSITION_TYPE_SELL)


{

double maxValidSL = currentPrice + minStopDistance;

if (newSL <= maxValidSL)

if (!isAdjusted)

// Cannot set SL to desired level due to broker constraints

return false;

else

// Already adjusted, accept the newSL

newSL = maxValidSL + (_Point * 0.1); // Slightly more to ensure


it passes

if (newSL >= currentSL)

LogMessage("New SL is not better than the current SL for SELL


position.", LOG_LEVEL_WARNING);

return false;

// Normalize the newSL

newSL = NormalizeDouble(newSL, _Digits);

// Prepare trade request

request.action = TRADE_ACTION_SLTP;

request.position = positionID;
request.symbol = _Symbol;

request.sl = newSL;

request.tp = currentTP; // Preserve the existing TP

request.magic = MagicNumber;

// Send the trade request

if (!OrderSend(request, result))

PrintFormat("Failed to modify SL for position ID %I64u. Retcode: %d,


Description: %s", positionID, result.retcode,
ErrorDescription((int)result.retcode));

return false;

PrintFormat("Successfully modified SL for position ID %I64u to %.5f",


positionID, newSL);

return true;

//+------------------------------------------------------------------+

//| Centralized logging function with category and level filtering |

//+------------------------------------------------------------------+

void LogMessage(string message, LogLevelEnum messageLevel =


LOG_LEVEL_INFO, uint category = LOG_CAT_OTHER) {

// Check if the message level is within the set LogLevel

if (LogLevel == LOG_LEVEL_NONE || messageLevel > LogLevel) return;

// Check if the category is enabled using bitwise AND

if ((LogCategories & category) == 0) return;


// Prepare the log message with text labels

string levelLabel, symbol;

switch (messageLevel) {

case LOG_LEVEL_ERROR: levelLabel = "ERROR: "; symbol = "?? ";


break;

case LOG_LEVEL_WARNING: levelLabel = "WARNING: "; symbol = "??


"; break;

case LOG_LEVEL_INFO: levelLabel = "INFO: "; symbol = "?? "; break;

case LOG_LEVEL_DEBUG: levelLabel = "DEBUG: "; symbol = "?? ";


break;

string logMessage = symbol + levelLabel + message;

// Log to terminal

Print(logMessage);

// Log to database

LogToDatabase(message, messageLevel, category);

//+------------------------------------------------------------------+

//| Check Volatility Function |

//+------------------------------------------------------------------+

void CheckVolatility(double atrValue) {

if (atrValue >= atrVolatilityThreshold) {

isHighVolatility = true;

// Optionally, log only state changes

if (!wasHighVolatility) {
LogMessage("Volatility changed to High. ATR: " +
DoubleToString(atrValue, _Digits), LOG_LEVEL_DEBUG, LOG_CAT_ATR);

} else {

isHighVolatility = false;

if (wasHighVolatility) {

LogMessage("Volatility changed to Low. ATR: " +


DoubleToString(atrValue, _Digits), LOG_LEVEL_DEBUG, LOG_CAT_ATR);

wasHighVolatility = isHighVolatility;

//+------------------------------------------------------------------+

//| Timer event function |

//+------------------------------------------------------------------+

void OnTimer()

// Close trades before end of day to avoid swaps

if (CloseTradesBeforeEndOfDay && IsTimeToCloseTrades())

LogMessage("End of day detected on timer. Closing all positions to


avoid swaps.", LOG_LEVEL_INFO);

CloseAllPositions();

return; // Skip further processing if positions are closed

// Flush logs to database

FlushLogsToDatabase();

}
//+------------------------------------------------------------------+

//| Adjust SL to the closest allowed level according to broker limits |

//+------------------------------------------------------------------+

double AdjustSLToBrokerLimits(double desiredSL, long tradeType)

double currentPrice = (tradeType == POSITION_TYPE_BUY) ?


SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol,
SYMBOL_ASK);

// Get the minimum stop level in points

double minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,


SYMBOL_TRADE_STOPS_LEVEL);

if (minStopLevelPoints == 0)

minStopLevelPoints = (double)SymbolInfoInteger(_Symbol,
SYMBOL_TRADE_FREEZE_LEVEL);

double minStopDistance = minStopLevelPoints * _Point;

double adjustedSL = desiredSL;

if (tradeType == POSITION_TYPE_BUY)

double minValidSL = currentPrice - minStopDistance;

adjustedSL = MathMin(desiredSL, minValidSL - (_Point * 0.1)); //


Slightly more to ensure it passes

adjustedSL = NormalizeDouble(adjustedSL, _Digits);

else if (tradeType == POSITION_TYPE_SELL)

double maxValidSL = currentPrice + minStopDistance;

adjustedSL = MathMax(desiredSL, maxValidSL + (_Point * 0.1)); //


Slightly more to ensure it passes
adjustedSL = NormalizeDouble(adjustedSL, _Digits);

return adjustedSL;

//+------------------------------------------------------------------+

//| Calculate Pivot Points |

//+------------------------------------------------------------------+

void CalculatePivotPoints(double &pivotPoint, double &support1, double


&resistance1, double &support2, double &resistance2)

// Use PivotTimeframe here

double high = iHigh(_Symbol, PivotTimeframe, 1);

double low = iLow(_Symbol, PivotTimeframe, 1);

double close = iClose(_Symbol, PivotTimeframe, 1);

if (high == 0 || low == 0 || close == 0) {

LogMessage("Failed to retrieve valid pivot point data.",


LOG_LEVEL_WARNING);

pivotPoint = support1 = resistance1 = support2 = resistance2 = 0.0;

return;

pivotPoint = (high + low + close) / 3.0;

resistance1 = (2 * pivotPoint) - low;

support1 = (2 * pivotPoint) - high;

resistance2 = pivotPoint + (high - low);

support2 = pivotPoint - (high - low);

}
//+------------------------------------------------------------------+

//| Check if Tick Volume is Increasing |

//+------------------------------------------------------------------+

bool IsTickVolumeIncreasing()

if (!EnableTickVolumeFilter)

LogMessage("Tick Volume Filter is disabled.", LOG_LEVEL_INFO);

return true; // If the filter is disabled, always return true

int periods = TickVolumePeriods; // Use input parameter

long volumes[]; // Array to store tick volumes

int copied = CopyTickVolume(_Symbol, TickVolumeTimeframe, 0,


periods + 1, volumes);

if (copied <= periods)

LogMessage("Failed to copy tick volume data or insufficient data.",


LOG_LEVEL_WARNING);

return false;

// Current period's volume is volumes[0], previous volumes are


volumes[1..periods]

// Calculate the average volume of previous periods

long avgVolume = 0;

for (int i = 1; i <= periods; i++)


{

avgVolume += volumes[i];

avgVolume /= periods;

// Calculate threshold

double threshold = avgVolume * TickVolumeMultiplier;

// Debug log for tick volume data

LogMessage("Current Volume: " + IntegerToString(volumes[0]) + ",


Average Volume: " + IntegerToString(avgVolume) + ", Threshold: " +
DoubleToString(threshold, 2), LOG_LEVEL_DEBUG);

// Return true if current volume is greater than threshold

if (volumes[0] > threshold)

LogMessage("Tick volume is increasing.", LOG_LEVEL_INFO);

return true;

else

LogMessage("Tick volume is not increasing.", LOG_LEVEL_INFO);

return false;

//+------------------------------------------------------------------+

//| Logging Info |

//+------------------------------------------------------------------+
void LogSLTPValidation(double sl, double tp, double minStopLevel,
ENUM_ORDER_TYPE orderType) {

string slType = (orderType == ORDER_TYPE_BUY) ? "Buy" : "Sell";

LogMessage("SL=" + DoubleToString(sl, _Digits) + ", TP=" +


DoubleToString(tp, _Digits), LOG_LEVEL_DEBUG, LOG_CAT_SLTP_VALUES);

bool IsLogEnabled(LogLevelEnum level) {

return (level <= LogLevel);

//+------------------------------------------------------------------+

//| Unified Error Logging Function |

//+------------------------------------------------------------------+

void LogError(string message, int errorCode = -1, bool


includeErrorDescription = true) {

string fullMessage = "ERROR: " + message;

if (errorCode >= 0) {

string errorDescription = includeErrorDescription ?


ErrorDescription(errorCode) : "";

fullMessage += " - " + FormatErrorMessage(errorCode,


errorDescription);

LogMessage(fullMessage, LOG_LEVEL_ERROR, LOG_CAT_ERRORS);

//+------------------------------------------------------------------+

//| Helper Function to Sanitize Strings for SQL Queries |

//+------------------------------------------------------------------+

string SanitizeForSQL(string text) {


StringReplace(text, "'", "''"); // Replace single quotes with two single
quotes for SQL compatibility

return text;

string FormatErrorMessage(int errorCode, string errorDescription) {

string formattedMessage = "Error Code: " + IntegerToString(errorCode);

if (errorDescription != "") {

formattedMessage += " - " + errorDescription;

return formattedMessage;

//+------------------------------------------------------------------+

//| Log to database with respect to input settings |

//+------------------------------------------------------------------+

void LogToDatabase(string message, LogLevelEnum logLevel, uint


category) {

// Check if database logging is enabled

if (!EnableDatabaseLogging) return;

// Sanitize message to remove special symbols

message = SanitizeForSQL(message);

// Get current date and time

string date = TimeToString(TimeCurrent(), TIME_DATE);

string time = TimeToString(TimeCurrent(), TIME_SECONDS |


TIME_MINUTES);

// Prepare log level and category strings


string logLevelStr = GetLogLevelString(logLevel);

string categoryStr = GetCategoryString(category);

// Create a new LogEntry

LogEntry entry;

entry.date = date;

entry.time = time;

entry.logLevel = logLevelStr;

entry.category = categoryStr;

entry.message = message;

// Add to the array

ArrayResize(logEntries, ArraySize(logEntries) + 1);

logEntries[ArraySize(logEntries) - 1] = entry;

//+------------------------------------------------------------------+

//| Helper Function to convert log level enum to string |

//+------------------------------------------------------------------+

string GetLogLevelString(LogLevelEnum logLevel) {

switch (logLevel) {

case LOG_LEVEL_ERROR: return "ERROR";

case LOG_LEVEL_WARNING: return "WARNING";

case LOG_LEVEL_INFO: return "INFO";

case LOG_LEVEL_DEBUG: return "DEBUG";

default: return "UNKNOWN";

}
//+------------------------------------------------------------------+

//| Helper Function to convert log category enum to string |

//+------------------------------------------------------------------+

string GetCategoryString(uint category) {

string categories = "";

if ((category & LOG_CAT_ATR) != 0) {

categories += "ATR|";

if ((category & LOG_CAT_TRADE_EXECUTION) != 0) {

categories += "TradeExecution|";

if ((category & LOG_CAT_SLTP_VALUES) != 0) {

categories += "SLTPValues|";

if ((category & LOG_CAT_RISK_MANAGEMENT) != 0) {

categories += "RiskManagement|";

if ((category & LOG_CAT_ERRORS) != 0) {

categories += "Errors|";

if ((category & LOG_CAT_OTHER) != 0) {

categories += "Other|";

// Remove the trailing '|'

if (StringLen(categories) > 0) {

categories = StringSubstr(categories, 0, StringLen(categories) - 1);

} else {
categories = "None";

return categories;

//+------------------------------------------------------------------+

//| Flush logs to database |

//+------------------------------------------------------------------+

void FlushLogsToDatabase()

// Declare variables

char errorMsg[256];

char query[4096];

char beginTransactionQuery[256];

char commitTransactionQuery[256];

// Convert strings to char arrays

StringToCharArray("BEGIN TRANSACTION;", beginTransactionQuery);

StringToCharArray("COMMIT;", commitTransactionQuery);

// Begin transaction

int execResult = ExecuteSQL(dbHandle, beginTransactionQuery,


errorMsg, sizeof(errorMsg));

if (execResult != 0)

PrintFormat("Error starting transaction: %s",


CharArrayToString(errorMsg));

return;

}
// Write each log entry

for (int i = 0; i < ArraySize(logEntries); i++)

LogEntry entry = logEntries[i];

// Construct SQL query

string insertLogQuery = StringFormat(

"INSERT INTO LogEntries (Date, Time, LogLevel, Category,


Message) VALUES ('%s', '%s', '%s', '%s', '%s');",

entry.date, entry.time, entry.logLevel, entry.category,


SanitizeForSQL(entry.message)

);

// Convert insertLogQuery to char array

StringToCharArray(insertLogQuery, query);

// Execute SQL query

execResult = ExecuteSQL(dbHandle, query, errorMsg,


sizeof(errorMsg));

if (execResult != 0)

PrintFormat("Error executing query: %s",


CharArrayToString(errorMsg));

// Optionally, handle the error (e.g., retry, log to file, etc.)

// Commit transaction

execResult = ExecuteSQL(dbHandle, commitTransactionQuery,


errorMsg, sizeof(errorMsg));
if (execResult != 0)

PrintFormat("Error committing transaction: %s",


CharArrayToString(errorMsg));

// Clear the array

ArrayResize(logEntries, 0);

//+------------------------------------------------------------------+

//| Process logic every 100 ticks |

//+------------------------------------------------------------------+

void ProcessTickChartLogic()

// Fetch the current ATR value + skip trading if ATR is below the
minimum threshold

double atrValue = GetATR(atrPeriod);

if (atrValue < atrMinThreshold)

LogMessage("ATR value below minimum threshold. No trade will be


placed. ATR: " + DoubleToString(atrValue, _Digits), LOG_LEVEL_WARNING,
LOG_CAT_ATR);

return;

// Check and log volatility status

CheckVolatility(atrValue);

{
LogMessage("ATR=" + DoubleToString(atrValue, _Digits) + ",
Volatility=" + (isHighVolatility ? "High" : "Low"), LOG_LEVEL_DEBUG,
LOG_CAT_ATR);

// Ensure trades are only processed within trading hours

if (!IsWithinTradingHours())

LogMessage("Outside trading hours.", LOG_LEVEL_WARNING);

return;

// Fetch Williams %R value for the current forming bar (index 0)

double wprValue[1];

LogMessage("Williams %R Value (Index 1): " +


DoubleToString(wprValue[0], 2), LOG_LEVEL_INFO);

if (CopyBuffer(wprHandle, 0, 1, 1, wprValue) <= 0)

LogMessage("Failed to retrieve valid Williams %R value for previous


bar.", LOG_LEVEL_WARNING);

LogFailedTrade("Williams %R Indicator Failure");

return;

wprValueGlobal = wprValue[0];

LogMessage("Williams %R Value: " + DoubleToString(wprValue[0], 2),


LOG_LEVEL_INFO);
}

bool buySignal = false, sellSignal = false;

double pivotPoint, support1, resistance1, support2, resistance2;

CalculatePivotPoints(pivotPoint, support1, resistance1, support2,


resistance2);

double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);

// Debug log

LogMessage("Debug Info: WPR: " + DoubleToString(wprValueGlobal, 2)


+ " | CurrentPrice: " + DoubleToString(currentPrice, _Digits) + " |
Support1: " + DoubleToString(support1, _Digits) + " | Resistance1: " +
DoubleToString(resistance1, _Digits), LOG_LEVEL_DEBUG);

if (wprValueGlobal <= WPR_Oversold &&

currentPrice <= support1 * 1.002)

buySignal = true;

LogMessage("Buy Signal detected!", LOG_LEVEL_INFO,


LOG_CAT_TRADE_EXECUTION);

if (wprValueGlobal >= WPR_Overbought &&

currentPrice >= resistance1 * 0.998)

sellSignal = true;

LogMessage("Sell Signal detected!", LOG_LEVEL_INFO,


LOG_CAT_TRADE_EXECUTION);

}
if (!buySignal && !sellSignal)

LogMessage("No buy or sell signal triggered on this bar.",


LOG_LEVEL_DEBUG);

return;

if (TimeCurrent() - lastTradeTime < cooldownTime)

LogMessage("Overtrading protection activated. Waiting before


placing new trades.", LOG_LEVEL_WARNING);

return;

// Check if tick volume is increasing

bool tickVolumeIncreasing = IsTickVolumeIncreasing();

if (buySignal && tickVolumeIncreasing)

LogMessage("Executing Buy trade based on ATR, Williams %R, and


increasing tick volume.", LOG_LEVEL_INFO, LOG_CAT_TRADE_EXECUTION);

ExecuteTrade(ORDER_TYPE_BUY, atrValue, "ATR, WPR, and Tick


Volume Signal", wprValueGlobal);

else if (buySignal)

LogMessage("Buy signal detected but tick volume is not increasing.


Skipping trade.", LOG_LEVEL_INFO);

}
if (sellSignal && tickVolumeIncreasing)

LogMessage("Executing Sell trade based on ATR, Williams %R, and


increasing tick volume.", LOG_LEVEL_INFO, LOG_CAT_TRADE_EXECUTION);

ExecuteTrade(ORDER_TYPE_SELL, atrValue, "ATR, WPR, and Tick


Volume Signal", wprValueGlobal);

else if (sellSignal)

LogMessage("Sell signal detected but tick volume is not increasing.


Skipping trade.", LOG_LEVEL_INFO);

bool IsPositionLogged(ulong positionID)

for (int i = 0; i < ArraySize(loggedPositionIDs); i++)

if (loggedPositionIDs[i] == positionID)

return true;

return false;

//+------------------------------------------------------------------+

//| Find Last Deal for Position |

//+------------------------------------------------------------------+

ulong FindLastDealForPosition(ulong positionID)

// Ensure the history is refreshed


HistorySelect(0, TimeCurrent());

// Get the number of deals in history

int totalDeals = HistoryDealsTotal();

for (int i = 0; i < totalDeals; i++)

ulong dealTicket = HistoryDealGetTicket(i);

if (HistoryDealSelect(dealTicket))

ulong dealPositionID = HistoryDealGetInteger(dealTicket,


DEAL_POSITION_ID);

if (dealPositionID == positionID)

return dealTicket;

return 0; // No deal found

//+------------------------------------------------------------------+

//| Check if it's time to close trades before end of day |

//+------------------------------------------------------------------+

bool IsTimeToCloseTrades()

datetime currentTime = TimeCurrent();

MqlDateTime timeStruct;

TimeToStruct(currentTime, timeStruct);
if (timeStruct.hour > CloseTradesHour || (timeStruct.hour ==
CloseTradesHour && timeStruct.min >= CloseTradesMinute))

return true;

return false;

//+------------------------------------------------------------------+

//| Close all open positions |

//+------------------------------------------------------------------+

void CloseAllPositions()

int totalPositions = PositionsTotal();

for (int i = totalPositions - 1; i >= 0; i--)

ulong ticket = PositionGetTicket(i);

if (PositionSelectByTicket(ticket))

ulong positionID = PositionGetInteger(POSITION_IDENTIFIER);

long tradeType = PositionGetInteger(POSITION_TYPE);

double volume = PositionGetDouble(POSITION_VOLUME);

// Close the position

trade.SetExpertMagicNumber(MagicNumber);

trade.SetDeviationInPoints(Slippage);

bool result = false;

if (tradeType == POSITION_TYPE_BUY)
{

result = trade.PositionClose(ticket);

else if (tradeType == POSITION_TYPE_SELL)

result = trade.PositionClose(ticket);

if (result)

LogMessage("Closed position ID " + IntegerToString(positionID)


+ " due to end of day.", LOG_LEVEL_INFO);

else

LogMessage("Failed to close position ID " +


IntegerToString(positionID) + ". Error: " +
trade.ResultRetcodeDescription(), LOG_LEVEL_ERROR);

//+------------------------------------------------------------------+

//| Determine Trend Direction |

//+------------------------------------------------------------------+

int GetTrendDirection()

// Get the last closed candle index

int lastClosedIndex = 1;
// Get the moving average handle

int maHandle = iMA(_Symbol, TrendTimeframe, TrendMAPeriod, 0,


TrendMAMethod, PRICE_CLOSE);

if (maHandle == INVALID_HANDLE)

LogMessage("Failed to create MA handle for trend detection.",


LOG_LEVEL_ERROR);

return 0; // Neutral trend if failed

// Copy the MA value

double maValue[1];

if (CopyBuffer(maHandle, 0, lastClosedIndex, 1, maValue) <= 0)

LogMessage("Failed to retrieve MA value for trend detection.",


LOG_LEVEL_ERROR);

IndicatorRelease(maHandle);

return 0; // Neutral trend if failed

// Get the close price of the last closed candle

double closePrice = iClose(_Symbol, TrendTimeframe, lastClosedIndex);

// Release the indicator handle

IndicatorRelease(maHandle);

if (closePrice > maValue[0])

LogMessage("Trend is UP.", LOG_LEVEL_DEBUG);


return 1; // Uptrend

else if (closePrice < maValue[0])

LogMessage("Trend is DOWN.", LOG_LEVEL_DEBUG);

return -1; // Downtrend

else

LogMessage("Trend is NEUTRAL.", LOG_LEVEL_DEBUG);

return 0; // Neutral

You might also like