last ...
This commit is contained in:
@@ -1267,7 +1267,8 @@ class XCSessionDetector : public XCBase
|
|||||||
bool isActive = IsTimeInRange(
|
bool isActive = IsTimeInRange(
|
||||||
session.startTime,
|
session.startTime,
|
||||||
session.endTime,
|
session.endTime,
|
||||||
currentTime);
|
currentTime //
|
||||||
|
);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Session Started
|
// Session Started
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* XMQL Helper CLI Tools ...
|
||||||
|
* a module for handle MQL5 Commands running ...
|
||||||
|
*
|
||||||
|
* Maintainer
|
||||||
|
*
|
||||||
|
* Hadi Khazaee Asl <https://saherelm.ir> (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);
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,497 @@
|
|||||||
|
/**
|
||||||
|
* XProject Tools Module ...
|
||||||
|
* a module for handle MQL5 Projects Compile and Packing ...
|
||||||
|
*
|
||||||
|
* Maintainer
|
||||||
|
*
|
||||||
|
* Hadi Khazaee Asl <https://saherelm.ir> (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<string[]>
|
||||||
|
*/
|
||||||
|
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
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* XMQL Document Helper CLI Tools ...
|
||||||
|
* a module for handle MQL5 Documents Generating ...
|
||||||
|
*
|
||||||
|
* Maintainer
|
||||||
|
*
|
||||||
|
* Hadi Khazaee Asl <https://saherelm.ir> (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 <path_to_mql5_file> [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);
|
||||||
@@ -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
|
||||||
@@ -24,7 +24,10 @@ const workspacePaths = [
|
|||||||
"Helpers", // Indicator's Helpers Classes
|
"Helpers", // Indicator's Helpers Classes
|
||||||
"Indicators", // Required Indicators
|
"Indicators", // Required Indicators
|
||||||
"Libraries", // Required Libraries
|
"Libraries", // Required Libraries
|
||||||
|
"XOM1EA", // XOM1EA Projects ...
|
||||||
|
"XFIMAEA", // XOM1EA Projects ...
|
||||||
"XCATBEA", // XCATBEA 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 ...
|
// Extract Required to Compile Resources List ...
|
||||||
let compileList = await extractResources(resources, true);
|
let compileList = await extractResources(resources, true);
|
||||||
@@ -112,24 +133,6 @@ async function prepareProject(
|
|||||||
return;
|
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 ...
|
// Loop through Files ...
|
||||||
for (const f of packList) {
|
for (const f of packList) {
|
||||||
@@ -188,7 +191,7 @@ async function prepareProject(
|
|||||||
//
|
//
|
||||||
// Compile Indicators ...
|
// Compile Indicators ...
|
||||||
if (forceCompileIndicators) {
|
if (forceCompileIndicators) {
|
||||||
await compile("Indicators");
|
await runCommand("Indicators");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,5 +493,5 @@ module.exports = {
|
|||||||
cleanupWorkSpace,
|
cleanupWorkSpace,
|
||||||
compileWorkSpace,
|
compileWorkSpace,
|
||||||
prepareProject
|
prepareProject
|
||||||
}
|
};
|
||||||
//#endregion
|
//#endregion
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
//+------------------------------------------------------------------+
|
||||||
@@ -103,7 +103,7 @@ ENUM_X_SESSION_OVERLAP_TYPE eaAllowedOverlaps[]; // Allowed Overlaps for Analysi
|
|||||||
|
|
||||||
//
|
//
|
||||||
string eaTag; // EA Scoped Tag ...
|
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 ...
|
XCAlert *eaAlert; // EA Scope Alert Handler ...
|
||||||
@@ -172,7 +172,7 @@ void OnTick()
|
|||||||
|
|
||||||
//
|
//
|
||||||
// Check if Is New Bar ...
|
// Check if Is New Bar ...
|
||||||
isNewBar = eaBarTracker.IsNewBar();
|
isEaInNewBar = eaBarTracker.IsNewBar();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Check Analysis Enabled ...
|
// Check Analysis Enabled ...
|
||||||
@@ -184,7 +184,7 @@ void OnTick()
|
|||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Check New Bar or not ...
|
// Check New Bar or not ...
|
||||||
if (!isNewBar)
|
if (!isEaInNewBar)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"aab": "| Commons |",
|
"aab": "| Commons |",
|
||||||
"aac": "|----------|",
|
"aac": "|----------|",
|
||||||
"mqlHelper": "node mqlHelper.js",
|
"mqlHelper": "node mqlHelper.js",
|
||||||
|
"mqlDocumentHelper": "node ./Documents/JsModules/mqlDocumentHelper.js",
|
||||||
"cleanupEx": "npm run mqlHelper cleanup",
|
"cleanupEx": "npm run mqlHelper cleanup",
|
||||||
"compileWorkspace": "npm run mqlHelper *",
|
"compileWorkspace": "npm run mqlHelper *",
|
||||||
"compileExperts": "npm run mqlHelper experts",
|
"compileExperts": "npm run mqlHelper experts",
|
||||||
|
|||||||
Reference in New Issue
Block a user