From 369e6f086376ca0d6866f222b8cc7eff82e7599b Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Fri, 10 Jul 2026 04:24:32 +0330 Subject: [PATCH] last ... --- Classes/x-saherelm.session-detector.class.mq5 | 3 +- Documents/BKP/Docs/XCAccount.md | 136 +++++ Documents/BKP/Docs/XCAlert.md | 143 +++++ Documents/BKP/Docs/XCBaseSignaller.md | 0 Documents/BKP/Docs/XCDataCollector.md | 80 +++ Documents/BKP/Docs/XCRestrictions.md | 0 Documents/BKP/Docs/XCSessionDetector.md | 0 Documents/BKP/Docs/XCTarget.md | 0 Documents/BKP/Docs/XCTrade.md | 11 + Documents/BKP/Docs/XCTradeManager.md | 0 Documents/BKP/Docs/XCVolume.md | 25 + Documents/Docs/.gitkeep | 0 Documents/JsModules/BKPRoot/mqlHelper.js | 19 + .../JsModules/BKPRoot/prepareX121XKIEA.js | 29 + Documents/JsModules/BKPRoot/prepareXCATBEA.js | 35 ++ Documents/JsModules/BKPRoot/prepareXFIMAEA.js | 31 ++ Documents/JsModules/BKPRoot/prepareXOM1EA.js | 29 + .../JsModules/BKPRoot/prepareXRRTools.js | 27 + .../JsModules/BKPRoot/x-mql-project.tools.js | 497 ++++++++++++++++++ Documents/JsModules/mqlDocumentHelper.js | 45 ++ Documents/JsModules/x-mql-document.tools.js | 183 +++++++ Documents/JsModules/x-mql-project.tools.js | 43 +- Experts/SmartShadowScanner.ea.mq5 | 360 +++++++++++++ Experts/x-saherelm.x-expert.ea.v2.mq5 | 6 +- package.json | 1 + 25 files changed, 1679 insertions(+), 24 deletions(-) create mode 100644 Documents/BKP/Docs/XCAccount.md create mode 100644 Documents/BKP/Docs/XCAlert.md create mode 100644 Documents/BKP/Docs/XCBaseSignaller.md create mode 100644 Documents/BKP/Docs/XCDataCollector.md create mode 100644 Documents/BKP/Docs/XCRestrictions.md create mode 100644 Documents/BKP/Docs/XCSessionDetector.md create mode 100644 Documents/BKP/Docs/XCTarget.md create mode 100644 Documents/BKP/Docs/XCTrade.md create mode 100644 Documents/BKP/Docs/XCTradeManager.md create mode 100644 Documents/BKP/Docs/XCVolume.md create mode 100644 Documents/Docs/.gitkeep create mode 100644 Documents/JsModules/BKPRoot/mqlHelper.js create mode 100644 Documents/JsModules/BKPRoot/prepareX121XKIEA.js create mode 100644 Documents/JsModules/BKPRoot/prepareXCATBEA.js create mode 100644 Documents/JsModules/BKPRoot/prepareXFIMAEA.js create mode 100644 Documents/JsModules/BKPRoot/prepareXOM1EA.js create mode 100644 Documents/JsModules/BKPRoot/prepareXRRTools.js create mode 100644 Documents/JsModules/BKPRoot/x-mql-project.tools.js create mode 100644 Documents/JsModules/mqlDocumentHelper.js create mode 100644 Documents/JsModules/x-mql-document.tools.js create mode 100644 Experts/SmartShadowScanner.ea.mq5 diff --git a/Classes/x-saherelm.session-detector.class.mq5 b/Classes/x-saherelm.session-detector.class.mq5 index e39c7f45..c6db55b4 100644 --- a/Classes/x-saherelm.session-detector.class.mq5 +++ b/Classes/x-saherelm.session-detector.class.mq5 @@ -1267,7 +1267,8 @@ class XCSessionDetector : public XCBase bool isActive = IsTimeInRange( session.startTime, session.endTime, - currentTime); + currentTime // + ); // // Session Started diff --git a/Documents/BKP/Docs/XCAccount.md b/Documents/BKP/Docs/XCAccount.md new file mode 100644 index 00000000..e7c18763 --- /dev/null +++ b/Documents/BKP/Docs/XCAccount.md @@ -0,0 +1,136 @@ +# XCAccount + +it is a module which used for Accoess Trading Account Data in SaherElm's XTrade Framework. + +## Dependencies + +there is no Dependencies on this modules. + +## Instancing + +there is not any args in Constructor. + +## Instructions + +use provided Actions for Access Account informations in where you need. + +## Actions + +### long GetUserAccount() + +retrieve Current User Identifier. + +### long GetLeverage() + +retrieve Current Account Leverage. + +### bool CanExpertTrade() + +retrieve Flag which Specified Trading using Experts or Manual. + +### ENUM_ACCOUNT_TRADE_MODE GetTradeMode() + +retrieve Current Account Trading Mode. + +- ACCOUNT_TRADE_MODE_DEMO; +- ACCOUNT_TRADE_MODE_CONTEST; +- ACCOUNT_TRADE_MODE_REAL; + +### double GetBalance() + +retrieve Current Account Balance. + +### double GetCredit() + +retrieve Current Account Credit. + +### double GetProfit() + +retrieve Current Account's Amount of Profits. + +### double GetEquity() + +retrieve Current Account's Amount of Equity. + +### double GetMargin() + +retrieve Current Account's Amount of Margin. + +### double GetFreeMargin() + +retrieve Current Account's Amount of Free Margin. + +### double GetMarginLevel() + +retrieve Current Account's Margin Level. + +### double GetMarginCall() + +retrieve Current Account's Margin Call Level. + +### double GetMarginStopOut() + +retrieve Current Account's Margin Stopout Level. + +### string GetName() + +retrieve Current Account's User Name. + +### string GetServerName() + +retrieve Current Account's Server Name. + +### string GetCurrency() + +retrieve Current Account's Currency. + +### string GetCompany() + +retrieve Current Account's Market Name. + +### double CalculateTradeProfit(const string symbol, ENUM_ORDER_TYPE type, double volume, double entry, double exit) + +Calculate and Retrieved Trade Profit based on Account Currency. + +### double CalculateMarging(const string symbol, ENUM_ORDER_TYPE type, double volume, double entry) + +Calculate and Retrieved Specified Trades used Marigin. + +### double CalculateFreeMarging(const string symbol, ENUM_ORDER_TYPE type, double volume, double entry) + +Calculate and Retrieved Specified Trades Free Marigin. + +### double CalculateMaxVolume(const string symbol, ENUM_ORDER_TYPE type, double entry, double percent = 100) + +Calculate Max Allowed Specified Trade's Volume for Entry. + +### double GetPointValue(string symbol) + +Retrieve Specified Symbol's Point Value. + +### double CalculateRiskAmount(string symbol, double points, double volume) + +Calculate How many Risk Currency in a Trade when risk Points and Trade Volume is Specified. +used when we have to know how many risk in $ Currency, for a Trade when we risk 100 Point, by Providing 0.01 Lot as Volume. + +### double CalculateRiskPoints(string symbol, double volume, double amount) + +Calculate Risk Points in a Trade when Volume and Curreny Amount is Specified. +used when we want to know How many points risk in a Trade when we have to risk 5$ in a Trade by 0.01 Lot as Volume. + +### double CalculateVolume(string symbol, double amount, double points) + +Calculate Trade Volume when Risk Amount in Currency and Risk Amount in Points Specified. +used when we want to know Open a Trade by how Size of Volume when we have to risk 5$ in a Trade when 100 points risking. + +### string GetBalanceReportMessage() + +Prepare and Retrieve Balance Report Message for Current Account. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/Documents/BKP/Docs/XCAlert.md b/Documents/BKP/Docs/XCAlert.md new file mode 100644 index 00000000..6b7316f2 --- /dev/null +++ b/Documents/BKP/Docs/XCAlert.md @@ -0,0 +1,143 @@ +# XCAlert + +it is a module which used for Providing Logs in SaherElm's XTrade Framework. + +## Dependencies + +this Module used XCDataCollector for Saving Logs in Files. + +## Instancing + +using this syntax for instancing : + +```mql5 +XCAlert( + string prefix = "X-Alert", // Alerts Prefixe + bool enableAlerts = true, // Enable Alerts + bool logAlerts = true, // Log Alerts + bool terminalAlerts = false, // Terminal Alerts + bool mailAlerts = false, // Mail Alerts + bool pushAlerts = false, // Push Alerts + bool saveAlerts = false // Save Alerts +) {} +``` + +all of Constructor Input args is Optional, means have default values. +you can set them after instancing using Properties Setter/Getter (s). + +## Instructions + +- Prefix: string, Specified Logger Prefix. this for Separate Different Logger Instances. +- EnableAlerts: boolean, Specified Logging is Enabled or not. +- SaveAlerts: boolean, Specified Saving Log(s) or not. +- LogAlerts: boolean, Specified Can Print Logs on Console or not. +- TerminalAlerts: boolean, Specified Send Logs in Terminal or not. +- MailAlerts: boolean, Specified Send Logs as Mail or not. +- PushAlerts: boolean, Specified Send Logs as Push or not. + +## Actions + +### LogAlert(string message) + +do specified type of Logs. +do log by Print in Console. + +### TerminalAlert(string message) + +do specified type of Logs. +do log by sending Terminal Alert. + +### MailAlert(string message) + +do specified type of Logs. +do log by sending Mail Alert. + +### PushAlert(string message) + +do specified type of Logs. +do log by sending Push Notification Alert. + +### SendAlert(string message) + +do sending log by configuration. +retrieve log and based on configuration send each specified enabled logs separately. + +### Log(string message) + +Checking Log Flag and Print Message. + +### Log(string tag, string message) + +Checking Log Flag and Print Specified Tag & Message. + +### LogEmpty() + +Checking Log Flag and Print Empty Message. + +### LogArray(T &array[], string label = NULL) + +Checking Log Flag and Print Array. + +### LogError(int error = -1) + +Checking Log Flag and then Generate Error Message based on provided error number, +then Print Tag Specified Message. + +### string GetErrorDescription(int err_code) + +Generate Specified Error Messaged based on Provided error code. + +### string GetTradeServerReturnCodeDescription(int return_code) + +Generate Specified Error Message based on Provided server error code. + +### bool CanLog() + +protected +return a Flag for Specified Flag which allowed Print Logs or not. + +### bool CanLogAlert() + +protected +return a Flag for Specified Flag which allowed Print Logs and also Enabled or not. + +### bool CanTerminalAlert() + +protected +return a Flag for Specified Flag which allowed Sending Logs to Terminal and also Enabled or not. + +### bool CanMailAlert() + +protected +return a Flag for Specified Flag which allowed Sending Logs as Mail and also Enabled or not. + +### bool CanPushAlert() + +protected +return a Flag for Speified Flag which allowed Sending Logs as Push and also Enabled or not. + +### Save(string message) + +protected +Saving Specified Message into Logger File. if alert Saving is Enabled. + +#### Descriptions + +- Preparing a File Name by Combining setted Prefix + ".XLogs". +- Generating File Path based on XCDataCollector instance Path. +- Validate Generated filePath. +- Preparing Log Message for Saving in Log File by attaching Current Time. +- Append prepared Message to Log File using XCDataCollector instance. + +### string PrepareMessage(string message) + +private +Replace Escaped strings and Attach Prefix to Message. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/Documents/BKP/Docs/XCBaseSignaller.md b/Documents/BKP/Docs/XCBaseSignaller.md new file mode 100644 index 00000000..e69de29b diff --git a/Documents/BKP/Docs/XCDataCollector.md b/Documents/BKP/Docs/XCDataCollector.md new file mode 100644 index 00000000..314b2b3d --- /dev/null +++ b/Documents/BKP/Docs/XCDataCollector.md @@ -0,0 +1,80 @@ +# XCDataCollector + +it is a module which used for Save and Retrieve Data in SaherElm's XTrade Framework. +this tool helps all required modules to save data for next usage and analytics. +we can implement Save To File and Read From File actions to all Structures and Classes and Modules. + +## Dependencies + +there is no Dependencies on this modules. + +## Instancing + +there is not any args in Constructor. + +## Instructions + +- using Path Action of instance for Setting File Path; +- using Save, Append, Read for File Manipulations; + +## Actions + +### Path + +is a Property Setter and Getter Action for Setting Collector Path. +for setter it's have an input Parameters (string value) which Specified the File's Path. + +### GetFilePath(string fileName) + +retrieve Full path (including provided file name) based on Current Configured Path for instance. + +### GetFileHandlerForWrite(string filePath) + +retrieve File Handler by Write Access for Writing Data to File. +you have to provide Fully File Path for access Handler. +this for Use Implementing Custom Collecting Senarios out of Instance Box. +commonly it's used by built in Actions for Regular Saving Data. + +### GetFileHandlerForRead(string filePath) + +retrieve File Handler by Read Access for Reading Data from File. +you have to provide Fully File Path for access Handler. +this for Use Implementing Custom Retrieving Senarios out of Instance Box. +commonly it's used by built in Actions for Regular Reading Data. + +### Save(string fileName, string content) + +Save Specified Content to Specified File Nmae. +here the file name must Contains fully file path including file name. +it's only one time writing file. if it's file exists before overwrite it's content. + +### Append(string fileName, string content) + +Append Specified Contonet to end of Specified File. +here the file name must Contains fully file path including file name. +if file Exists, add to end of it. if not, Create file at Specified Provided path, then write content. + +### Read(string fileName, string &content) + +Read all of File Content at once from Specified File. +here the file name must Contains fully file path including file name. +content arg is a reference string variable to hold file content. + +### Read(string fileName, string &content[]) + +Read all of File Content Lines at once from Specified File. +here the file name must Contains fully file path including file name. +content arg is a reference string array variable to hold file content lines. + +### IsExists(string fileName) + +Check Specified File Exists or not ... +here the file name must Contains fully file path including file name. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/Documents/BKP/Docs/XCRestrictions.md b/Documents/BKP/Docs/XCRestrictions.md new file mode 100644 index 00000000..e69de29b diff --git a/Documents/BKP/Docs/XCSessionDetector.md b/Documents/BKP/Docs/XCSessionDetector.md new file mode 100644 index 00000000..e69de29b diff --git a/Documents/BKP/Docs/XCTarget.md b/Documents/BKP/Docs/XCTarget.md new file mode 100644 index 00000000..e69de29b diff --git a/Documents/BKP/Docs/XCTrade.md b/Documents/BKP/Docs/XCTrade.md new file mode 100644 index 00000000..03ff4c6f --- /dev/null +++ b/Documents/BKP/Docs/XCTrade.md @@ -0,0 +1,11 @@ +# XCTrade + +it is a module which used for Providing Trade Related Actions in SaherElm's XTrade Framework. + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/Documents/BKP/Docs/XCTradeManager.md b/Documents/BKP/Docs/XCTradeManager.md new file mode 100644 index 00000000..e69de29b diff --git a/Documents/BKP/Docs/XCVolume.md b/Documents/BKP/Docs/XCVolume.md new file mode 100644 index 00000000..369d6604 --- /dev/null +++ b/Documents/BKP/Docs/XCVolume.md @@ -0,0 +1,25 @@ +# XCVolume + +it is a module which used for Accoess Trading Account Data in SaherElm's XTrade Framework. + +## Dependencies + +there is no Dependencies on this modules. + +## Instancing + +there is not any args in Constructor. + +## Instructions + +use provided Actions for Access Account informations in where you need. + +## Actions + +## Maintainer + +Hadi Khazaee asl + +[https://www.saherelm.ir](https://www.saherelm.ir) + +[hadi_khazaee_asl@yahoo.com](mailto:hadi_khazaee_asl@yahoo.com) diff --git a/Documents/Docs/.gitkeep b/Documents/Docs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/Documents/JsModules/BKPRoot/mqlHelper.js b/Documents/JsModules/BKPRoot/mqlHelper.js new file mode 100644 index 00000000..8fe4e41b --- /dev/null +++ b/Documents/JsModules/BKPRoot/mqlHelper.js @@ -0,0 +1,19 @@ +/** + * XMQL Helper CLI Tools ... + * a module for handle MQL5 Commands running ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +// Import Requirements ... +const XProjectTools = require("./x-mql-project.tools"); + +// +// Read Command Arguments ... +let cmdName = process.argv[2]; + +// +XProjectTools.runCommand(cmdName); diff --git a/Documents/JsModules/BKPRoot/prepareX121XKIEA.js b/Documents/JsModules/BKPRoot/prepareX121XKIEA.js new file mode 100644 index 00000000..a147dd58 --- /dev/null +++ b/Documents/JsModules/BKPRoot/prepareX121XKIEA.js @@ -0,0 +1,29 @@ +// +// Import Requirements ... +const XProjectTools = require("./x-mql-project.tools"); + +// +// define constants ... +const projectName = "X121XKIEA"; + +// +// Projects Resources ... +const projectResources = [ + "Libraries/*", // Required Libraries + "Classes/*", // Required Classes + "X121XKIEA/*", // X121XKIEA Projects ... + "Indicators/x-saherelm.x121.xki", // Required Indicators + "Helpers/x-saherelm.x121.xki.helper", // Indicator's Helpers Classes + "Experts/x-saherelm.x121.xki.backtester.ea", // Expert Advisers +]; + +// +run(); + +/** + * Main Project Function ... + */ +async function run() { + // + await XProjectTools.prepareProject(projectName, projectResources); +} diff --git a/Documents/JsModules/BKPRoot/prepareXCATBEA.js b/Documents/JsModules/BKPRoot/prepareXCATBEA.js new file mode 100644 index 00000000..797e2134 --- /dev/null +++ b/Documents/JsModules/BKPRoot/prepareXCATBEA.js @@ -0,0 +1,35 @@ +// +// Import Requirements ... +const XProjectTools = require("./x-mql-project.tools"); + +// +// define constants ... +const projectName = "XCATBEA"; + +// +// Projects Resources ... +const projectResources = [ + "Libraries/*", // Required Libraries + "Classes/*", // Required Classes + "XCATBEA/*", // XCATBEA Projects ... + "Indicators/x-saherelm.x121.xcatb", // Required Indicators + "Helpers/x-saherelm.x121.xcatb.helper", // Indicator's Helpers Classes + "Experts/x-saherelm.x121.xcatb.ea", // Expert Advisers +]; + +// +run(); + +/** + * Main Project Function ... + */ +async function run() { + // + await XProjectTools.prepareProject( + projectName, + projectResources + ); +} + + + diff --git a/Documents/JsModules/BKPRoot/prepareXFIMAEA.js b/Documents/JsModules/BKPRoot/prepareXFIMAEA.js new file mode 100644 index 00000000..b1dd0809 --- /dev/null +++ b/Documents/JsModules/BKPRoot/prepareXFIMAEA.js @@ -0,0 +1,31 @@ +// +// Import Requirements ... +const XProjectTools = require("./x-mql-project.tools"); + +// +// define constants ... +const projectName = "XFIMAEA"; + +// +// Projects Resources ... +const projectResources = [ + "Libraries/*", // Required Libraries + "Classes/*", // Required Classes + "XFIMAEA/*", // XOM1EA Projects ... + "Indicators/x-saherelm.xfima", // Required Indicators + "Indicators/x-saherelm.x121.xct", // Required Indicators + "Helpers/x-saherelm.xfima.helper", // Indicator's Helpers Classes + "Experts/x-saherelm.xfima.backtester.ea", // Expert Advisers + "Experts/x-saherelm.xfima.ea", // Expert Advisers +]; + +// +run(); + +/** + * Main Project Function ... + */ +async function run() { + // + await XProjectTools.prepareProject(projectName, projectResources); +} diff --git a/Documents/JsModules/BKPRoot/prepareXOM1EA.js b/Documents/JsModules/BKPRoot/prepareXOM1EA.js new file mode 100644 index 00000000..82f073d5 --- /dev/null +++ b/Documents/JsModules/BKPRoot/prepareXOM1EA.js @@ -0,0 +1,29 @@ +// +// Import Requirements ... +const XProjectTools = require("./x-mql-project.tools"); + +// +// define constants ... +const projectName = "XOM1EA"; + +// +// Projects Resources ... +const projectResources = [ + "Libraries/*", // Required Libraries + "Classes/*", // Required Classes + "XOM1EA/*", // XOM1EA Projects ... + "Indicators/x-saherelm.xom1", // Required Indicators + "Helpers/x-saherelm.xom1.helper", // Indicator's Helpers Classes + "Experts/x-saherelm.xom1.ea", // Expert Advisers +]; + +// +run(); + +/** + * Main Project Function ... + */ +async function run() { + // + await XProjectTools.prepareProject(projectName, projectResources); +} diff --git a/Documents/JsModules/BKPRoot/prepareXRRTools.js b/Documents/JsModules/BKPRoot/prepareXRRTools.js new file mode 100644 index 00000000..b6597dda --- /dev/null +++ b/Documents/JsModules/BKPRoot/prepareXRRTools.js @@ -0,0 +1,27 @@ +// +// Import Requirements ... +const XProjectTools = require("./x-mql-project.tools"); + +// +// define constants ... +const projectName = "XRRTools"; + +// +// Projects Resources ... +const projectResources = [ + "Libraries/*", // Required Libraries + "Classes/*", // Required Classes + "Indicators/x-saherelm.x121.xcc", // Required Indicators + "Experts/x-saherelm.rr.tools", // Expert Advisers +]; + +// +run(); + +/** + * Main Project Function ... + */ +async function run() { + // + await XProjectTools.prepareProject(projectName, projectResources); +} diff --git a/Documents/JsModules/BKPRoot/x-mql-project.tools.js b/Documents/JsModules/BKPRoot/x-mql-project.tools.js new file mode 100644 index 00000000..25534e6d --- /dev/null +++ b/Documents/JsModules/BKPRoot/x-mql-project.tools.js @@ -0,0 +1,497 @@ +/** + * XProject Tools Module ... + * a module for handle MQL5 Projects Compile and Packing ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +// Import Requirements ... +const execSync = require("child_process").execSync; +const XFileTools = require("./Documents/JsModules/x-file.tools"); +const XShellTools = require("./Documents/JsModules/x-shell.tools"); +const XColorTools = require("./Documents/JsModules/x-color.tools"); +const XTypeTools = require("./Documents/JsModules/x-type-detector.tools"); + +// +// Definitions ... +const packsPath = XFileTools.joinPath(".", "Packs"); +const workspacePaths = [ + "Classes", // Required Classes + "Experts", // Expert Advisers + "Helpers", // Indicator's Helpers Classes + "Indicators", // Required Indicators + "Libraries", // Required Libraries + "XOM1EA", // XOM1EA Projects ... + "XFIMAEA", // XOM1EA Projects ... + "XCATBEA", // XCATBEA Projects ... + "X121XKIEA", // X121XKIEA Projects ... +]; + +// +//#region Tools ... + +/** + * Handle Compile and Pack Projects ... + * + * @param {string} projectName + * @param {string[]} resources + * @param {boolean} forceCleanup + * @param {boolean} forceCompileIndicators + */ +async function prepareProject( + projectName = "", + resources = [], + forceCleanup = true, + forceCompileIndicators = true +) { + // + // Check Resources Has Child ... + let isValidArgs = + resources.length > 0 && + XTypeTools.isArray(resources); + if (!isValidArgs) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Invalid Resource List ...", XColorTools.COLOR_NAMES.Red) + ); + } + + // + // Check Project Name is Valid ... + isValidArgs = + projectName.length > 0 && + XTypeTools.isString(projectName); + if (!isValidArgs) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Empty Project Name ...", XColorTools.COLOR_NAMES.Red) + ); + } + + // + // Prepare Pack Folder Exists ... + let isExists = XFileTools.isDirectoryExists(packsPath); + if (!isExists) { + XFileTools.createDirectory(packsPath); + } + + // + // Check Folder Exists or not ... + // if Exists Remove it ... + // Create Project Folder on Packs ... + const prjFolderOnPacks = XFileTools.joinPath(packsPath, projectName); + isExists = XFileTools.isDirectoryExists(prjFolderOnPacks); + if (isExists) { + await XFileTools.removeDirectory(prjFolderOnPacks, true); + } + XFileTools.createDirectory(prjFolderOnPacks); + + // + // Extract Required to Compile Resources List ... + let compileList = await extractResources(resources, true); + if (!compileList || compileList.length == 0) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Resource List Extraction to Compile Failed ...", XColorTools.COLOR_NAMES.Red) + ); + + // + return; + } + + // + // Compile Resources Files ... + for (const f of compileList) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to Compile: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue) + ); + + // + await recursiveTriesToCompileFile(f, 0); + } + + // + // Extract Required to Pack Resources List ... + let packList = await extractResources(resources, false); + if (!packList || packList.length == 0) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Resource List Extraction to Pack Failed ...", XColorTools.COLOR_NAMES.Red) + ); + + // + return; + } + + // + // Loop through Files ... + for (const f of packList) { + // + // Prepare Destination Path ... + const fBasePath = XFileTools.basename(f); + let fDestPath = f.replace(fBasePath, ""); + fDestPath = fDestPath.substring(0, fDestPath.length - 1); + fDestPath = XFileTools.joinPath(prjFolderOnPacks, fDestPath); + + // + const isFDestPathExists = XFileTools.isDirectoryExists(fDestPath); + if (!isFDestPathExists) { + XFileTools.createDirectory(fDestPath); + } + + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to copy: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue), + XColorTools.applyForegroundColor(" to : ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(fDestPath, XColorTools.COLOR_NAMES.Magenta) + ); + + // + let result = await XFileTools.copyFile(f, fDestPath); + if (result) { + // + const msg = "copy successfully ..."; + console.log( + XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green) + ); + } else { + // + const msg = "copy failed ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + } + + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Project: ", XColorTools.COLOR_NAMES.Green), + XColorTools.applyForegroundColor(projectName, XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(" Compiled and Packed Successfully ...", XColorTools.COLOR_NAMES.Green), + ); + + // + // Cleanup WorkSpace if Provided ... + if (forceCleanup) { + await cleanupWorkSpace(); + } + + // + // Compile Indicators ... + if (forceCompileIndicators) { + await runCommand("Indicators"); + } +} + +/** + * Extract required Files List ... + * + * @param {boolean} forCompile + * + * @returns Promise + */ +async function extractResources( + resources = [], + forCompile = true +) { + // + let result = []; + + // + // Loop Through Project Resources and Extract Files List to Compile ... + for (const resource of resources) { + // + // Prepare Normalized Resource Path ... + let resourcePath = XFileTools.joinPath(".", resource); + + // + // Handle Wild Card Resources ... + if (resourcePath.endsWith("//*") || + resourcePath.endsWith("\\*")) { + // + resourcePath = resourcePath.substring(0, resourcePath.length - 2); + const files = await XFileTools.extractFiles( + resourcePath, + forCompile ? [".mq5", ".mq4"] : [".ex5", ".ex4"] + ); + + // + result = result.concat(files); + } + // + // Handle Direct File Resources ... + else { + // + // Check MQL 5 Version ... + let fileResourcePath = resourcePath + (forCompile ? ".mq5" : ".ex5"); + + // + // Check File Exists ... + let isExists = XFileTools.isFileExists(fileResourcePath); + if (!isExists) { + // + // Check MQL 4 Version ... + fileResourcePath = resourcePath + (forCompile ? ".mq4" : ".ex4"); + isExists = XFileTools.isFileExists(fileResourcePath); + } + + // + // Add File Path to Files List if Exists ... + if (isExists) { + result.push(fileResourcePath); + } + } + } + + // + return result; +} + +/** + * try to compile a file ... + * @param {string} f + * @param {number} numberOfTries + */ +async function recursiveTriesToCompileFile(f = "", numberOfTries = 0) { + // + let result = await handleFileCompile(f); + if (result) { + // + const msg = "compiled successfully ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green)); + return result; + } else { + // + if (numberOfTries < 10) { + // + numberOfTries++; + + // + const msg = "retry (" + (numberOfTries + 1) + ") ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.LightMagenta)); + await recursiveTriesToCompileFile(f, numberOfTries); + } else { + // + const msg = "failed ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + + // + return false; + } + } +} + +/** + * execute compile task on Specific File ... + * @param {string} f file path to Execute Compile task ... + * @returns + */ +async function handleFileCompile(f = "") { + // + let result = false; + + // + let exResult = await compileMQLFile(f); + if (exResult.includes("0 errors")) { + result = true; + } else { + result = false; + } + + // + return result; +} + +/** + * Compile Specific MQL File ... + * + * @param {string} path MQL file path ... + * @returns + */ +async function compileMQLFile(path = "") { + // + // Check File Exists ... + const isExists = XFileTools.isFileExists(path); + if (!isExists) { + // + const msg = "file not found ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + + // + const ext = XFileTools.getFileExtension(path); + if (ext !== ".mq5" && ext !== "mq4") { + // + const msg = "invalid file extension ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + + // + const cwd = __dirname; + const cmd = "compile.cmd " + path; + + // + const result = await XShellTools.execute(cmd, cwd); + return result; +} + +/** + * Run Specified Command in workspace ... + * + * @param {string} cmd + * @returns + */ +async function runCommand(cmd = "") { + // + // Validate Args ... + let isValidArgs = cmd.length > 0 && + XTypeTools.isString(cmd); + + // + // Validate Commands ... + if (isValidArgs) { + // + // Check Available Commands ... + isValidArgs = + cmd === "*" || + cmd === "cleanup" || + workspacePaths.includes(cmd) || + workspacePaths.map(x => x.toLowerCase(x)).includes(cmd.toLowerCase()); + } + if (!isValidArgs) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Invalid Command ...", XColorTools.COLOR_NAMES.Red) + ); + } + + // + // Compile ... + if (cmd == "*") { + await compileWorkSpace(); + } else if (cmd == "cleanup") { + await cleanupWorkSpace(); + } else { + // + // Select Workspace to Compile ... + const folder = workspacePaths + .find(x => x.toLowerCase() === cmd.toLowerCase()); + isValidArgs = + XTypeTools.isString(folder) && + folder.length > 0; + if (!isValidArgs) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Invalid Command ...", XColorTools.COLOR_NAMES.Red) + ); + } + + // + // Extract Required to Compile Resources List ... + let compileList = await XFileTools.extractFiles( + folder, + [".mq5", ".mq4"] + ); + if (!compileList || compileList.length == 0) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("Extraction to Compile Failed ...", XColorTools.COLOR_NAMES.Red) + ); + + // + return; + } + + // + // Compile Resources Files ... + for (const f of compileList) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to Compile: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue) + ); + + // + await recursiveTriesToCompileFile(f, 0); + } + } +} + +/** + * Cleanup Workspace compiled files ... + */ +async function cleanupWorkSpace() { + // + for (const path of workspacePaths) { + // + const fSourcePath = XFileTools.joinPath(".", path); + const filesToRemove = await XFileTools.extractFiles(fSourcePath, [".ex5", ".ex4"]); + for (const f of filesToRemove) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor("try to Remove: ", XColorTools.COLOR_NAMES.Yellow), + XColorTools.applyForegroundColor(f, XColorTools.COLOR_NAMES.Blue) + ); + + // + let result = await XFileTools.removeFile(f); + if (result) { + // + const msg = "removed successfully ..."; + console.log( + XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Green) + ); + } else { + // + const msg = "removed failed ..."; + console.log(XColorTools.applyForegroundColor(msg, XColorTools.COLOR_NAMES.Red)); + throw msg; + } + } + } +} + +/** + * Compile Workspace ... + */ +async function compileWorkSpace() { + // + for (const path of workspacePaths) { + await runCommand(path); + } +} +//#endregion + +// +//#region Module Exports ... +module.exports = { + extractResources, + recursiveTriesToCompileFile, + handleFileCompile, + compileMQLFile, + runCommand, + cleanupWorkSpace, + compileWorkSpace, + prepareProject +}; +//#endregion \ No newline at end of file diff --git a/Documents/JsModules/mqlDocumentHelper.js b/Documents/JsModules/mqlDocumentHelper.js new file mode 100644 index 00000000..347549a2 --- /dev/null +++ b/Documents/JsModules/mqlDocumentHelper.js @@ -0,0 +1,45 @@ +/** + * XMQL Document Helper CLI Tools ... + * a module for handle MQL5 Documents Generating ... + * + * Maintainer + * + * Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) + */ + +// +// Import Requirements ... +const XColorTools = require("./x-color.tools"); +const XDocumentTools = require("./x-mql-document.tools"); + +const args = process.argv.slice(2); +if (args.length < 1) { + // + // Log ... + console.log( + XColorTools.applyForegroundColor( + "Invalid Usage Command ...", + XColorTools.COLOR_NAMES.Red + ) + ); + console.log( + XColorTools.applyForegroundColor( + "Usage: node mql5-doc-extractor.js [output_md_file]", + XColorTools.COLOR_NAMES.Yellow + ) + ); + console.log( + XColorTools.applyForegroundColor( + "Example: node mqlDocumentHelper.js ./MyEA.mq5 ./docs/MyEA.md", + XColorTools.COLOR_NAMES.LightBlue + ) + ); + + // + process.exit(1); +} + +const inputFile = args[0]; +const outputFile = args[1] || inputFile.replace(/\.(mq5|mqh)$/i, '_DOCS.md'); + +XDocumentTools.extractMQL5Documentation(inputFile, outputFile); \ No newline at end of file diff --git a/Documents/JsModules/x-mql-document.tools.js b/Documents/JsModules/x-mql-document.tools.js new file mode 100644 index 00000000..a2ec09c3 --- /dev/null +++ b/Documents/JsModules/x-mql-document.tools.js @@ -0,0 +1,183 @@ +const fs = require('fs'); +const path = require('path'); + +// +//#region Tools ... +/** + * Extracts Doxygen-style documentation from MQL5 files and generates Markdown. + * @param {string} filePath - Path to the .mq5 or .mqh file + * @param {string} outputPath - Path to save the generated .md file + */ +function extractMQL5Documentation(filePath, outputPath) { + // + if (!fs.existsSync(filePath)) { + console.error(`Error: File not found at ${filePath}`); + return; + } + + // + const content = fs.readFileSync(filePath, 'utf8'); + const lines = content.split('\n'); + const extractedDocs = []; + + // + let currentCommentBlock = []; + let isInsideBlockComment = false; + + // + // State machine to parse comments and map them to the next line of code + for (let i = 0; i < lines.length; i++) { + // + const line = lines[i]; + const trimmedLine = line.trim(); + + // + // 1. Handle Block Comments (/* ... */) + if (trimmedLine.startsWith('/*') || trimmedLine.startsWith('/**')) { + // + isInsideBlockComment = true; + currentCommentBlock.push(trimmedLine); + if (trimmedLine.endsWith('*/')) { + isInsideBlockComment = false; + } + continue; + } + + // + if (isInsideBlockComment) { + // + currentCommentBlock.push(trimmedLine); + if (trimmedLine.endsWith('*/')) { + isInsideBlockComment = false; + } + continue; + } + + // + // 2. Handle Line Comments (/// or //) + if (trimmedLine.startsWith('///') || trimmedLine.startsWith('//')) { + // + currentCommentBlock.push(trimmedLine); + continue; + } + + // + // 3. Handle Empty Lines (Keep them to preserve spacing before code) + if (trimmedLine === '') { + // + if (currentCommentBlock.length > 0) { + currentCommentBlock.push(''); + } + continue; + } + + // + // 4. We hit actual code. If we have a comment block, map it. + if (currentCommentBlock.length > 0) { + // + // Clean up trailing empty lines in the comment block + while (currentCommentBlock.length > 0 && + currentCommentBlock[currentCommentBlock.length - 1].trim() === '' + ) { + currentCommentBlock.pop(); + } + + // + if (currentCommentBlock.length > 0) { + // + extractedDocs.push({ + comment: currentCommentBlock.join('\n'), + code: trimmedLine + }); + } + + // + currentCommentBlock = []; + } + } + + // + // Generate Markdown Output + generateMarkdown(filePath, extractedDocs, outputPath); +} + +/** + * Formats the extracted data into a clean Markdown document. + */ +function generateMarkdown(filePath, docs, outputPath) { + // + const fileName = path.basename(filePath); + let md = `# Documentation for \`${fileName}\`\n\n`; + md += `*Extracted on: ${new Date().toLocaleDateString()}*\n\n---\n\n`; + + // + let fileHeaderFound = false; + + // + docs.forEach((doc, index) => { + // + // Check if this is the file-level header (usually the very first block) + if (index === 0 && + (doc.code.startsWith('#property') || doc.code.startsWith('//+--'))) { + // + md += `## File Overview\n`; + md += formatComment(doc.comment) + '\n\n'; + fileHeaderFound = true; + return; + } + + // + // Only document actual functions, classes, structs, or important variables + const isDocumentableCode = /^(void|int|double|bool|string|long|ulong|uint|class|struct|enum|input)\s+/.test(doc.code) + || doc.code.includes('class ') + || doc.code.includes('struct '); + + if (isDocumentableCode) { + // + md += `### \`${extractSignature(doc.code)}\`\n\n`; + md += `**Declaration:**\n\`\`\`cpp\n${doc.code}\n\`\`\`\n\n`; + md += `**Documentation:**\n${formatComment(doc.comment)}\n\n---\n\n`; + } + }); + + // + fs.writeFileSync(outputPath, md, 'utf8'); + console.log(`✅ Successfully extracted documentation to: ${outputPath}`); +} + +/** + * Cleans up Doxygen tags for better Markdown readability. + */ +function formatComment(comment) { + return comment + .replace(/\/\*\*?|\*\//g, '') // Remove block comment markers + .replace(/^\s*\*\s?/gm, '') // Remove leading asterisks + .replace(/^\/\/\/?\s?/gm, '') // Remove line comment markers + .replace(/@param\s+(\w+)\s*/g, '**Param `$1`:** ') + .replace(/@return\s*/g, '**Returns:** ') + .replace(/@brief\s*/g, '**Summary:** ') + .replace(/@note\s*/g, '*Note:* ') + .replace(/@warning\s*/g, '> **Warning:** ') + .trim(); +} + +/** + * Extracts a clean function/class signature from the code line. + */ +function extractSignature(codeLine) { + // + // Basic extraction, stops at the first opening parenthesis or brace + const match = codeLine.match(/^(.*?)[\({]/); + return match ? match[1].trim() : codeLine; +} +//#endregion + +// +//#region Module Exports ... +module.exports = { + formatComment, + generateMarkdown, + extractSignature, + extractMQL5Documentation, +} +//#endregion \ No newline at end of file diff --git a/Documents/JsModules/x-mql-project.tools.js b/Documents/JsModules/x-mql-project.tools.js index 4a507a48..25534e6d 100644 --- a/Documents/JsModules/x-mql-project.tools.js +++ b/Documents/JsModules/x-mql-project.tools.js @@ -24,7 +24,10 @@ const workspacePaths = [ "Helpers", // Indicator's Helpers Classes "Indicators", // Required Indicators "Libraries", // Required Libraries + "XOM1EA", // XOM1EA Projects ... + "XFIMAEA", // XOM1EA Projects ... "XCATBEA", // XCATBEA Projects ... + "X121XKIEA", // X121XKIEA Projects ... ]; // @@ -70,6 +73,24 @@ async function prepareProject( ); } + // + // Prepare Pack Folder Exists ... + let isExists = XFileTools.isDirectoryExists(packsPath); + if (!isExists) { + XFileTools.createDirectory(packsPath); + } + + // + // Check Folder Exists or not ... + // if Exists Remove it ... + // Create Project Folder on Packs ... + const prjFolderOnPacks = XFileTools.joinPath(packsPath, projectName); + isExists = XFileTools.isDirectoryExists(prjFolderOnPacks); + if (isExists) { + await XFileTools.removeDirectory(prjFolderOnPacks, true); + } + XFileTools.createDirectory(prjFolderOnPacks); + // // Extract Required to Compile Resources List ... let compileList = await extractResources(resources, true); @@ -112,24 +133,6 @@ async function prepareProject( return; } - // - // Prepare Pack Folder Exists ... - let isExists = XFileTools.isDirectoryExists(packsPath); - if (!isExists) { - XFileTools.createDirectory(packsPath); - } - - // - // Check Folder Exists or not ... - // if Exists Remove it ... - // Create Project Folder on Packs ... - const prjFolderOnPacks = XFileTools.joinPath(packsPath, projectName); - isExists = XFileTools.isDirectoryExists(prjFolderOnPacks); - if (isExists) { - await XFileTools.removeDirectory(prjFolderOnPacks, true); - } - XFileTools.createDirectory(prjFolderOnPacks); - // // Loop through Files ... for (const f of packList) { @@ -188,7 +191,7 @@ async function prepareProject( // // Compile Indicators ... if (forceCompileIndicators) { - await compile("Indicators"); + await runCommand("Indicators"); } } @@ -490,5 +493,5 @@ module.exports = { cleanupWorkSpace, compileWorkSpace, prepareProject -} +}; //#endregion \ No newline at end of file diff --git a/Experts/SmartShadowScanner.ea.mq5 b/Experts/SmartShadowScanner.ea.mq5 new file mode 100644 index 00000000..8905b155 --- /dev/null +++ b/Experts/SmartShadowScanner.ea.mq5 @@ -0,0 +1,360 @@ +//+------------------------------------------------------------------+ +//| SmartShadowScanner.mq5 | +//| Advanced Shadow Scanner with 3-Step Checklist Filter | +//+------------------------------------------------------------------+ +#property copyright "Professional Trader Setup 2026" +#property version "3.00" +#property description "Scans for large rejection shadows filtered by Sweep, POI, and Session" + +//--- User Inputs +input group "=== Base Shadow Settings ===" +input double ShadowMultiplier = 2.0; // Shadow to Body Multiplier (e.g., 2.0 means shadow is 2x body) +input double MinRangeATR_Multi = 0.5; // Min Candle Range as ATR Multiplier (0.5 = 50% of ATR) +input int MinCandleSizePoints = 30; // Min Absolute Candle Size in Points (Filters micro-candles) +input ENUM_TIMEFRAMES Timeframe = PERIOD_H4; // Timeframe to Analyze + +input group "=== Filter 1: Liquidity Sweep ===" +input bool EnableSweepFilter = true; // Enable Liquidity Sweep Filter +input int SwingLookback = 10; // Swing Lookback Period (Left & Right candles) + +input group "=== Filter 2: Point of Interest (POI) ===" +input bool EnablePOIFilter = true; // Enable POI Filter +input int POI_ProximityPoints = 50; // Allowed Distance to POI in Points +input bool CheckPDH_PDL = true; // Check Previous Day High/Low +input bool CheckOrderBlock = true; // Check Recent Order Blocks + +input group "=== Filter 3: Trading Session ===" +input bool EnableSessionFilter = true; // Enable Session Time Filter +input int LondonStartHour = 8; // London Start Hour (Server Time) +input int LondonEndHour = 12; // London End Hour (Server Time) +input int NYStartHour = 13; // New York Start Hour (Server Time) +input int NYEndHour = 18; // New York End Hour (Server Time) + +input group "=== Display & Alert Settings ===" +input bool DrawArrows = true; // Draw Arrows on Chart +input bool ShowAlerts = true; // Show Popup Alerts +input bool SendPush = false; // Send Push Notifications +input color BullishArrowColor = clrLime; // Bullish Arrow Color (Lower Shadow) +input color BearishArrowColor = clrRed; // Bearish Arrow Color (Upper Shadow) +input int ArrowDistancePoints = 10; // Arrow Distance from Shadow Tip (Points) + +//--- Global Variables +datetime lastBarTime = 0; +int atrHandle = INVALID_HANDLE; + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + // Initialize ATR indicator handle for volatility filtering + atrHandle = iATR(_Symbol, Timeframe, 14); + if(atrHandle == INVALID_HANDLE) + { + Print("Error creating ATR indicator handle!"); + return(INIT_FAILED); + } + + Print("=== Smart Shadow Scanner v3.0 Initialized ==="); + Print("Sweep Filter: ", EnableSweepFilter ? "Enabled" : "Disabled"); + Print("POI Filter: ", EnablePOIFilter ? "Enabled" : "Disabled"); + Print("Session Filter: ", EnableSessionFilter ? "Enabled" : "Disabled"); + return(INIT_SUCCEEDED); +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + // Clean up chart objects and release indicator memory + ObjectsDeleteAll(0, "ShadowArrow_"); + IndicatorRelease(atrHandle); + ChartRedraw(); +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + // Process only on new bar close to avoid redundant calculations + datetime currentBarTime = iTime(_Symbol, Timeframe, 0); + if(currentBarTime == lastBarTime) return; + lastBarTime = currentBarTime; + + // Copy required bars (Need enough history for Swing Lookback) + int barsNeeded = SwingLookback * 2 + 10; + MqlRates rates[]; + ArraySetAsSeries(rates, true); + if(CopyRates(_Symbol, Timeframe, 0, barsNeeded, rates) < barsNeeded) return; + + // Copy ATR value for the closed candle (index 1) + double atrBuffer[]; + ArraySetAsSeries(atrBuffer, true); + if(CopyBuffer(atrHandle, 0, 1, 1, atrBuffer) < 1) return; + double currentATR = atrBuffer[0]; + + // Extract OHLC data for the target candle (index 1) + double openPrice = rates[1].open; + double highPrice = rates[1].high; + double lowPrice = rates[1].low; + double closePrice = rates[1].close; + datetime barTime = rates[1].time; + + // Calculate candle metrics + double body = MathAbs(openPrice - closePrice); + double upperShadow = highPrice - MathMax(openPrice, closePrice); + double lowerShadow = MathMin(openPrice, closePrice) - lowPrice; + double totalRange = highPrice - lowPrice; + + // ========================================== + // Base Filters (Volatility & Size) + // ========================================== + // Filter 1: Absolute minimum size (ignores dead-market micro candles) + if(totalRange < MinCandleSizePoints * _Point) return; + + // Filter 2: Dynamic ATR filter (candle must be at least X% of average range) + if(totalRange < (currentATR * MinRangeATR_Multi)) return; + + // Identify shadow type + bool isBullishShadow = (lowerShadow >= (ShadowMultiplier * body) && lowerShadow > upperShadow); + bool isBearishShadow = (upperShadow >= (ShadowMultiplier * body) && upperShadow > lowerShadow); + + if(!isBullishShadow && !isBearishShadow) return; + + // ========================================== + // Advanced 3-Step Checklist + // ========================================== + bool passedSweep = !EnableSweepFilter || CheckLiquiditySweep(rates, isBullishShadow); + bool passedPOI = !EnablePOIFilter || CheckNearPOI(rates, isBullishShadow, barTime); + bool passedSession = !EnableSessionFilter || CheckSession(barTime); + + // Trigger final signal only if ALL enabled filters are passed + if(passedSweep && passedPOI && passedSession) + { + ProcessSignal(isBullishShadow, barTime, isBullishShadow ? lowPrice : highPrice); + } +} + +//+------------------------------------------------------------------+ +//| Filter 1: Check for Liquidity Sweep | +//+------------------------------------------------------------------+ +bool CheckLiquiditySweep(const MqlRates &rates[], bool isBullish) +{ + int targetIndex = 1; // Target candle is at index 1 + + if(isBullish) // For lower shadow, it must have swept a previous Swing Low + { + for(int i = targetIndex + 2; i < ArraySize(rates) - SwingLookback; i++) + { + if(IsSwingLow(rates, i, SwingLookback)) + { + double swingLow = rates[i].low; + // Sweep condition: Wick pierces the swing low, but body closes above it + if(rates[targetIndex].low < swingLow && rates[targetIndex].close > swingLow) + { + return true; // Valid liquidity sweep detected + } + } + } + } + else // For upper shadow, it must have swept a previous Swing High + { + for(int i = targetIndex + 2; i < ArraySize(rates) - SwingLookback; i++) + { + if(IsSwingHigh(rates, i, SwingLookback)) + { + double swingHigh = rates[i].high; + // Sweep condition: Wick pierces the swing high, but body closes below it + if(rates[targetIndex].high > swingHigh && rates[targetIndex].close < swingHigh) + { + return true; // Valid liquidity sweep detected + } + } + } + } + + return false; // No valid sweep found +} + +//+------------------------------------------------------------------+ +//| Identify Swing High | +//+------------------------------------------------------------------+ +bool IsSwingHigh(const MqlRates &rates[], int index, int lookback) +{ + if(index - lookback < 0 || index + lookback >= ArraySize(rates)) return false; + + double high = rates[index].high; + for(int i = 1; i <= lookback; i++) + { + if(rates[index - i].high >= high) return false; + if(rates[index + i].high >= high) return false; + } + return true; +} + +//+------------------------------------------------------------------+ +//| Identify Swing Low | +//+------------------------------------------------------------------+ +bool IsSwingLow(const MqlRates &rates[], int index, int lookback) +{ + if(index - lookback < 0 || index + lookback >= ArraySize(rates)) return false; + + double low = rates[index].low; + for(int i = 1; i <= lookback; i++) + { + if(rates[index - i].low <= low) return false; + if(rates[index + i].low <= low) return false; + } + return true; +} + +//+------------------------------------------------------------------+ +//| Filter 2: Check Proximity to Point of Interest (POI) | +//+------------------------------------------------------------------+ +bool CheckNearPOI(const MqlRates &rates[], bool isBullish, datetime barTime) +{ + double currentPrice = isBullish ? rates[1].low : rates[1].high; + double proximity = POI_ProximityPoints * _Point; + + // POI Type 1: Previous Day High / Previous Day Low (PDH/PDL) + if(CheckPDH_PDL) + { + MqlRates dayRates[]; + ArraySetAsSeries(dayRates, true); + + // Fetch previous daily candle data + if(CopyRates(_Symbol, PERIOD_D1, 1, 1, dayRates) >= 1) + { + double pdh = dayRates[0].high; + double pdl = dayRates[0].low; + + if(isBullish && MathAbs(currentPrice - pdl) <= proximity) return true; // Near PDL + if(!isBullish && MathAbs(currentPrice - pdh) <= proximity) return true; // Near PDH + } + } + + // POI Type 2: Recent Order Blocks (OB) + if(CheckOrderBlock) + { + // Scan the last 20 candles for Order Blocks + for(int i = 2; i < MathMin(20, ArraySize(rates) - 1); i++) + { + // Bullish OB: Bearish candle followed by a strong bullish displacement + if(isBullish && IsBullishOrderBlock(rates, i)) + { + double obLow = rates[i].low; + double obHigh = rates[i].high; + if(currentPrice >= (obLow - proximity) && currentPrice <= (obHigh + proximity)) + return true; + } + + // Bearish OB: Bullish candle followed by a strong bearish displacement + if(!isBullish && IsBearishOrderBlock(rates, i)) + { + double obLow = rates[i].low; + double obHigh = rates[i].high; + if(currentPrice >= (obLow - proximity) && currentPrice <= (obHigh + proximity)) + return true; + } + } + } + + return false; // Not near any valid POI +} + +//+------------------------------------------------------------------+ +//| Identify Bullish Order Block | +//+------------------------------------------------------------------+ +bool IsBullishOrderBlock(const MqlRates &rates[], int index) +{ + // Current candle must be bearish + if(rates[index].close >= rates[index].open) return false; + + if(index - 1 < 0) return false; + + // Next candle (index-1) must be a strong bullish displacement + double nextBody = MathAbs(rates[index-1].close - rates[index-1].open); + double nextRange = rates[index-1].high - rates[index-1].low; + + if(rates[index-1].close <= rates[index-1].open) return false; // Must be bullish + if(nextBody < (nextRange * 0.6)) return false; // Body must be at least 60% of total range + + return true; +} + +//+------------------------------------------------------------------+ +//| Identify Bearish Order Block | +//+------------------------------------------------------------------+ +bool IsBearishOrderBlock(const MqlRates &rates[], int index) +{ + // Current candle must be bullish + if(rates[index].close <= rates[index].open) return false; + + if(index - 1 < 0) return false; + + // Next candle (index-1) must be a strong bearish displacement + double nextBody = MathAbs(rates[index-1].close - rates[index-1].open); + double nextRange = rates[index-1].high - rates[index-1].low; + + if(rates[index-1].close >= rates[index-1].open) return false; // Must be bearish + if(nextBody < (nextRange * 0.6)) return false; + + return true; +} + +//+------------------------------------------------------------------+ +//| Filter 3: Check Trading Session Time | +//+------------------------------------------------------------------+ +bool CheckSession(datetime barTime) +{ + MqlDateTime dt; + TimeToStruct(barTime, dt); + int hour = dt.hour; + + // London Session + if(hour >= LondonStartHour && hour < LondonEndHour) return true; + + // New York Session + if(hour >= NYStartHour && hour < NYEndHour) return true; + + return false; // Outside active sessions (e.g., Asian session) +} + +//+------------------------------------------------------------------+ +//| Process Final Signal (Draw, Alert, Push) | +//+------------------------------------------------------------------+ +void ProcessSignal(bool isBullish, datetime barTime, double extremePrice) +{ + string direction = isBullish ? "BULLISH" : "BEARISH"; + string message = StringFormat("✅ Golden %s Signal | %s | Price: %s", + direction, + TimeToString(barTime, TIME_DATE|TIME_MINUTES), + DoubleToString(extremePrice, _Digits)); + + // 1. Draw Arrow on Chart + if(DrawArrows) + { + string objName = "ShadowArrow_" + IntegerToString(barTime); + int arrowCode = isBullish ? 233 : 234; // 233: Up arrow, 234: Down arrow + color arrowColor = isBullish ? BullishArrowColor : BearishArrowColor; + double arrowPrice = isBullish ? + (extremePrice - ArrowDistancePoints * _Point) : + (extremePrice + ArrowDistancePoints * _Point); + + ObjectCreate(0, objName, OBJ_ARROW, 0, barTime, arrowPrice); + ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, arrowCode); + ObjectSetInteger(0, objName, OBJPROP_COLOR, arrowColor); + ObjectSetInteger(0, objName, OBJPROP_WIDTH, 3); + ObjectSetInteger(0, objName, OBJPROP_ANCHOR, isBullish ? ANCHOR_TOP : ANCHOR_BOTTOM); + } + + // 2. Trigger Alerts + if(ShowAlerts) Alert(message); + if(SendPush) SendNotification(message); + + // 3. Print to Experts Log + Print(message); +} +//+------------------------------------------------------------------+ \ No newline at end of file diff --git a/Experts/x-saherelm.x-expert.ea.v2.mq5 b/Experts/x-saherelm.x-expert.ea.v2.mq5 index e5511268..34f99aa4 100644 --- a/Experts/x-saherelm.x-expert.ea.v2.mq5 +++ b/Experts/x-saherelm.x-expert.ea.v2.mq5 @@ -103,7 +103,7 @@ ENUM_X_SESSION_OVERLAP_TYPE eaAllowedOverlaps[]; // Allowed Overlaps for Analysi // string eaTag; // EA Scoped Tag ... -bool isNewBar = false; // EA Is New Bar ... +bool isEaInNewBar = false; // EA Is New Bar ... // XCAlert *eaAlert; // EA Scope Alert Handler ... @@ -172,7 +172,7 @@ void OnTick() // // Check if Is New Bar ... - isNewBar = eaBarTracker.IsNewBar(); + isEaInNewBar = eaBarTracker.IsNewBar(); // // Check Analysis Enabled ... @@ -184,7 +184,7 @@ void OnTick() { // // Check New Bar or not ... - if (!isNewBar) + if (!isEaInNewBar) { return; } diff --git a/package.json b/package.json index bef79617..b743b91d 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "aab": "| Commons |", "aac": "|----------|", "mqlHelper": "node mqlHelper.js", + "mqlDocumentHelper": "node ./Documents/JsModules/mqlDocumentHelper.js", "cleanupEx": "npm run mqlHelper cleanup", "compileWorkspace": "npm run mqlHelper *", "compileExperts": "npm run mqlHelper experts",