Compare commits
4
Commits
422e019acc
...
0edd3c1b71
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0edd3c1b71 | ||
|
|
4707856302 | ||
|
|
9d23e2cba1 | ||
|
|
d50eef35bc |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,557 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Context Generator Module ...
|
||||
* a module for Generating Context ...
|
||||
*
|
||||
* Maintainer
|
||||
*
|
||||
* Hadi Khazaee Asl <https://saherelm.ir> (hadi_khazaee_asl@yahoo.com)
|
||||
*/
|
||||
|
||||
//
|
||||
//#region Imports ...
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
//#endregion
|
||||
|
||||
//
|
||||
//#region Constants ...
|
||||
const outputFilePrefix = "xDashboard";
|
||||
const allowedPatterns = [
|
||||
// {
|
||||
// kind: "file",
|
||||
// mode: "allow",
|
||||
// match: "name",
|
||||
// pattern: /^*.mq5$/i
|
||||
// },
|
||||
// {
|
||||
// kind: "directory",
|
||||
// mode: "deny",
|
||||
// match: "name",
|
||||
// pattern: /^node_modules$/i
|
||||
// },
|
||||
// {
|
||||
// kind: "file",
|
||||
// mode: "allow",
|
||||
// match: "path",
|
||||
// pattern: /^x-saherelm.*.mq5$/i
|
||||
// },
|
||||
|
||||
//
|
||||
// Base Pilot Files ...
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.base.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.session-detector.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-account.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-alert.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-app-dialog.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-backtester.expert.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-bar.analyser.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-chart.helper.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-cobject.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-data.collector.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-expert.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-guard.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-helper.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-http.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-market.pattern.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-md5.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-poi.drawer.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-position.drawer.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-restrictions.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-rm.panel.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-signaller.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-target.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-trade-manager.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-trade.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-volume.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.backtester.ea.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-expert.ea.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x121.xcc.helper.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x121.xct.helper.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x121.xcc.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x121.xct.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.common.extensions.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.common.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.common.models.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.draw.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-enums.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-guard.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-ohcl.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-pivot.analysis.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-poi.extensions.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-poi.lib.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-trade.lib.mq5$/i
|
||||
},
|
||||
]
|
||||
//#endregion
|
||||
|
||||
//
|
||||
// Reading Required Info
|
||||
const inputPath = process.argv[2];
|
||||
const searchPath = path.resolve(inputPath || process.cwd());
|
||||
|
||||
// Check Given Path is Valid or not ...
|
||||
if (!fs.existsSync(searchPath)) {
|
||||
console.error(`Error: path not found: ${searchPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve Stats of Given Path ...
|
||||
const stat = fs.statSync(searchPath);
|
||||
if (!stat.isDirectory()) {
|
||||
console.error(`Error: path is not a directory: ${searchPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Generate Required Data ...
|
||||
const timestamp = getTimestamp(new Date());
|
||||
const folderName = path.basename(searchPath) || "root";
|
||||
const safeFolderName = sanitizeFileName(folderName);
|
||||
|
||||
// Generate Output file Name ...
|
||||
const outputFileName = inputPath
|
||||
? `${outputFilePrefix}-${safeFolderName}-${timestamp}.txt`
|
||||
: `${outputFilePrefix}-${timestamp}.txt`;
|
||||
|
||||
//
|
||||
const outputPath = path.join(
|
||||
process.cwd(),
|
||||
"Documents",
|
||||
"GeneratedContext",
|
||||
outputFileName
|
||||
);
|
||||
|
||||
// Create Output File ...
|
||||
fs.writeFileSync(outputPath, "", "utf8");
|
||||
|
||||
// Navigate to Context Generation ...
|
||||
walk(searchPath);
|
||||
|
||||
//
|
||||
console.log(`Context generated: ${outputPath}`);
|
||||
|
||||
//
|
||||
//#region Required Tools ...
|
||||
/**
|
||||
* Validate Timestamp ...
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
function pad(value) {
|
||||
return String(value).padStart(2, "0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Validate ...
|
||||
*
|
||||
* @param {*} date
|
||||
* @returns
|
||||
*/
|
||||
function getTimestamp(date) {
|
||||
//
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1);
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
const seconds = pad(date.getSeconds());
|
||||
|
||||
//
|
||||
return `${year}${month}${day}_${hours}${minutes}${seconds}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Leaf Folder ...
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
function sanitizeFileName(value) {
|
||||
return value.replace(/[\\/:*?"<>|]/g, "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Match Value ...
|
||||
*
|
||||
* @param {*} rule
|
||||
* @param {*} targetPath
|
||||
* @returns
|
||||
*/
|
||||
function getMatchValue(rule, targetPath) {
|
||||
//
|
||||
const normalizedPath = targetPath.replace(/\\/g, "/");
|
||||
if (rule.match === "path") {
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
//
|
||||
return path.basename(targetPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match Patterns ...
|
||||
*
|
||||
* @param {*} targetPath
|
||||
* @param {*} rules
|
||||
* @returns
|
||||
*/
|
||||
function matchesPatterns(targetPath, rules) {
|
||||
//
|
||||
return rules.some(rule => {
|
||||
const value = getMatchValue(rule, targetPath);
|
||||
return rule.pattern.test(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a Directory Path is Valid for Parsing or not ...
|
||||
*
|
||||
* @param {*} fileName
|
||||
* @returns
|
||||
*/
|
||||
function isAllowedFile(filePath) {
|
||||
//
|
||||
// Extract Deny Rules ...
|
||||
const denyRules = allowedPatterns.filter(
|
||||
p => p.kind === "file" && p.mode === "deny"
|
||||
);
|
||||
|
||||
//
|
||||
// Extract Allowed Rules ...
|
||||
const allowRules = allowedPatterns.filter(
|
||||
p => p.kind === "file" && p.mode === "allow"
|
||||
);
|
||||
|
||||
// Check Deny Rules First ...
|
||||
if (matchesPatterns(filePath, denyRules)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check Allowed Rules ...
|
||||
return matchesPatterns(filePath, allowRules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a Directory Path is Valid for Parsing or not ...
|
||||
*
|
||||
* @param {*} directoryPath
|
||||
* @returns
|
||||
*/
|
||||
function isAllowedDirectory(directoryPath) {
|
||||
//
|
||||
// Extract Deny Rules ...
|
||||
const denyRules = allowedPatterns.filter(
|
||||
p => p.kind === "directory" && p.mode === "deny"
|
||||
);
|
||||
|
||||
//
|
||||
// Extract Allowed Rules ...
|
||||
const allowRules = allowedPatterns.filter(
|
||||
p => p.kind === "directory" && p.mode === "allow"
|
||||
);
|
||||
|
||||
//
|
||||
// Check Deny Patterns as First ...
|
||||
if (matchesPatterns(directoryPath, denyRules)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Check Allow Patterns Exists ...
|
||||
if (allowRules.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Chck Allowed Patterns Matched ...
|
||||
return matchesPatterns(directoryPath, allowRules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through a Directory and Navigate files for including in Context ...
|
||||
*
|
||||
* @param {*} dir
|
||||
* @returns
|
||||
*/
|
||||
function walk(dir) {
|
||||
//
|
||||
// Reading Directory Content ...
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
fs.appendFileSync(
|
||||
outputPath,
|
||||
`\n### ERROR reading directory: ${dir}\n${error.message}\n`,
|
||||
"utf8"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Loop through Entries ...
|
||||
for (const entry of entries) {
|
||||
//
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
//
|
||||
// Ignore if Symlink ...
|
||||
if (entry.isSymbolicLink()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Handle if requested Path is Directory ...
|
||||
if (entry.isDirectory()) {
|
||||
//
|
||||
// Allowed Folders ...
|
||||
if (!isAllowedDirectory(fullPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
walk(fullPath);
|
||||
}
|
||||
else
|
||||
//
|
||||
// Handle if requested Path is File ...
|
||||
if (entry.isFile()) {
|
||||
//
|
||||
// Allowed File ...
|
||||
if (!isAllowedFile(fullPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Holds File Content ...
|
||||
let content;
|
||||
try {
|
||||
content = fs.readFileSync(fullPath, "utf8");
|
||||
} catch (error) {
|
||||
content = `// Error reading file: ${error.message}`;
|
||||
}
|
||||
|
||||
//
|
||||
// Append File Content for Context ...
|
||||
fs.appendFileSync(
|
||||
outputPath,
|
||||
`\n### FILE: ${fullPath}\n\n${content}\n`,
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,672 @@
|
||||
///////////////////////////////////////////////////////
|
||||
//
|
||||
// SaherElm IT Center MQL5 Expert Advisor
|
||||
// -------------------------------------------------
|
||||
// Name: XBaseEA
|
||||
// Description: Session Detector Test Expert Advisor ...
|
||||
//
|
||||
// Maintainer:
|
||||
// ------------
|
||||
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
||||
//
|
||||
//////////////////////////////////////////////////////
|
||||
//
|
||||
// Global Properties ...
|
||||
#property copyright "Copyright 2023, SaherElm IT Center"
|
||||
#property link "https://saherelm.ir"
|
||||
#property version "1.00"
|
||||
#property icon "../Images/SaherElm_Logo_Color.ico"
|
||||
#property description "SaherElm Session Detector Test Expert Advisor"
|
||||
#property strict
|
||||
|
||||
// #region Imports ...
|
||||
#include "../Classes/x-saherelm.pivot-detector.class.mq5"
|
||||
#include "../Classes/x-saherelm.session-detector.class.mq5"
|
||||
#include "../Classes/x-saherelm.x-poi.drawer.class.mq5"
|
||||
// #endregion
|
||||
|
||||
// #region Global Variables ...
|
||||
#define ShortName "XPivotDetectorTestEA"
|
||||
|
||||
//
|
||||
XCAlert *eaAlert;
|
||||
XCPOIDrawer *eaDrawer;
|
||||
XCSessionDetector *eaSessionDetector;
|
||||
|
||||
//
|
||||
XCPivotDetector *eaMajorPivotDetector;
|
||||
XCPivotDetector *eaMinorPivotDetector;
|
||||
|
||||
//
|
||||
// Last Processing Pivots Time ...
|
||||
bool eaDrawMajorPivots = true;
|
||||
bool eaDrawMinorPivots = true;
|
||||
XPOIDrawConfig eaMajorPivotsDrawConfig;
|
||||
XPOIDrawConfig eaMinorPivotsDrawConfig;
|
||||
datetime eaLastMajorPivotProcessing = NULL;
|
||||
datetime eaLastMinorPivotProcessing = NULL;
|
||||
XPOIDrawConfig eaPointedMajorPivotsDrawConfig;
|
||||
XPOIDrawConfig eaPointedMinorPivotsDrawConfig;
|
||||
ENUM_X_DIRECTION eaMajorBias = X_DIRECTION_NONE;
|
||||
ENUM_X_DIRECTION eaMinorBias = X_DIRECTION_NONE;
|
||||
// #endregion
|
||||
|
||||
// #region Global Event Handlers ...
|
||||
/**
|
||||
* Initialize EA Process ...
|
||||
*
|
||||
* @return ( int )
|
||||
*/
|
||||
int OnInit()
|
||||
{
|
||||
//
|
||||
bool result = true;
|
||||
|
||||
//
|
||||
result = InitializeRequirements();
|
||||
if (!result)
|
||||
{
|
||||
return INIT_FAILED;
|
||||
}
|
||||
|
||||
//
|
||||
return INIT_SUCCEEDED;
|
||||
}
|
||||
|
||||
/**
|
||||
* De Initialize Expert ...
|
||||
*
|
||||
* @param reason: int, DeInitialization Reason ...
|
||||
*
|
||||
* REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
|
||||
* REASON_REMOVE 1 Program removed from a chart
|
||||
* REASON_RECOMPILE 2 Program recompiled
|
||||
* REASON_CHARTCHANGE 3 A symbol or a chart period is changed
|
||||
* REASON_CHARTCLOSE 4 Chart closed
|
||||
* REASON_PARAMETERS 5 Inputs changed by a user
|
||||
* REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
|
||||
* REASON_TEMPLATE 7 Another chart template applied
|
||||
* REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
|
||||
* REASON_CLOSE 9 Terminal closed
|
||||
*
|
||||
*/
|
||||
void OnDeinit(const int reason)
|
||||
{
|
||||
//
|
||||
// Destroy Timer ...
|
||||
// If Initialized ...
|
||||
|
||||
//
|
||||
eaMajorPivotsDrawConfig.Clean();
|
||||
eaMinorPivotsDrawConfig.Clean();
|
||||
eaPointedMajorPivotsDrawConfig.Clean();
|
||||
eaPointedMinorPivotsDrawConfig.Clean();
|
||||
|
||||
//
|
||||
// Cleanup Resources ...
|
||||
ZeroMemory(eaAlert);
|
||||
ZeroMemory(eaDrawer);
|
||||
ZeroMemory(eaSessionDetector);
|
||||
ZeroMemory(eaMajorPivotDetector);
|
||||
ZeroMemory(eaMinorPivotDetector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Tick for Processing ...
|
||||
*/
|
||||
void OnTick()
|
||||
{
|
||||
//
|
||||
// Perform Session Detector Process ...
|
||||
eaSessionDetector.Process();
|
||||
|
||||
//
|
||||
// Process Pivot Detector ...
|
||||
HandleProcessMajorPviots();
|
||||
HandleProcessMinorPviots();
|
||||
|
||||
//
|
||||
// Detecting Bias ...
|
||||
HanldeBiasDetection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle On Chart Event Processing ...
|
||||
|
||||
* @param id: int, Specified Event ID ...
|
||||
* @param lparam: long, type event parameter
|
||||
* @param dparam: double, type event parameter
|
||||
* @param sparam: string, type event parameter
|
||||
*/
|
||||
void OnChartEvent(
|
||||
const int id,
|
||||
const long &lparam,
|
||||
const double &dparam,
|
||||
const string &sparam //
|
||||
)
|
||||
{
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Custom Actions ...
|
||||
/**
|
||||
* Initial Requirements of EA ...
|
||||
*
|
||||
* @return ( bool )
|
||||
*/
|
||||
bool InitializeRequirements()
|
||||
{
|
||||
//
|
||||
bool result = false;
|
||||
|
||||
//
|
||||
eaDrawer = new XCPOIDrawer();
|
||||
|
||||
//
|
||||
// Initialize Alert ...
|
||||
eaAlert = new XCAlert(
|
||||
ShortName,
|
||||
false, // Enable Alert ...
|
||||
false, // Log Alert ...
|
||||
false, // Terminal Alert ...
|
||||
false, // Mail Alert ...
|
||||
false, // Push Alerts ...
|
||||
false // Save Alerts ...
|
||||
);
|
||||
result = eaAlert != NULL;
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize Session Detector ...
|
||||
eaSessionDetector = new XCSessionDetector(
|
||||
eaAlert, // Alert ...
|
||||
true, // Use Server Time ...
|
||||
0 // Timezone Offset ...
|
||||
);
|
||||
|
||||
//
|
||||
// Register Event Handlers ...
|
||||
eaSessionDetector.AddOnSessionEndHandler(OnSessionEndHandler);
|
||||
eaSessionDetector.AddOnOverlapEndHandler(OnOverlapEndHandler);
|
||||
eaSessionDetector.AddOnSessionStartHandler(OnSessionStartHandler);
|
||||
eaSessionDetector.AddOnOverlapStartHandler(OnOverlapStartHandler);
|
||||
eaSessionDetector.AddOnSessionStateChangeHandler(OnSessionStateChangeHandler);
|
||||
|
||||
//
|
||||
result = eaSessionDetector != NULL;
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize Major Pivot Detector ...
|
||||
eaMajorPivotDetector = new XCPivotDetector(
|
||||
eaAlert, // Alert ...
|
||||
"X121SDTMajor", // Pivot Prefix ...
|
||||
false, // Support Inverted Zones ...
|
||||
0 // Processable Zones ...
|
||||
);
|
||||
|
||||
//
|
||||
// Register Event Handlers ...
|
||||
eaMajorPivotDetector.AddOnPeakBreakedHandler(OnMajorPeakBreakedHandler);
|
||||
eaMajorPivotDetector.AddOnValeBreakedHandler(OnMajorValeBreakedHandler);
|
||||
eaMajorPivotDetector.AddOnPeakDetectedHandler(OnMajorPeakDetectedHandler);
|
||||
eaMajorPivotDetector.AddOnValeDetectedHandler(OnMajorValeDetectedHandler);
|
||||
|
||||
//
|
||||
// Pivot Draw Config ...
|
||||
eaMajorPivotsDrawConfig.zonesWidth = 2;
|
||||
eaMajorPivotsDrawConfig.zonesFill = false;
|
||||
eaMajorPivotsDrawConfig.ignoreZonesAt = true;
|
||||
eaMajorPivotsDrawConfig.zonesStyle = STYLE_SOLID;
|
||||
eaMajorPivotsDrawConfig.bullishColor = clrAqua;
|
||||
eaMajorPivotsDrawConfig.bearishColor = clrMagenta;
|
||||
|
||||
//
|
||||
// Pointed Pivot Draw Config ...
|
||||
eaPointedMajorPivotsDrawConfig.zonesWidth = 2;
|
||||
eaPointedMajorPivotsDrawConfig.zonesFill = false;
|
||||
eaPointedMajorPivotsDrawConfig.ignoreZonesAt = true;
|
||||
eaPointedMajorPivotsDrawConfig.zonesStyle = STYLE_SOLID;
|
||||
eaPointedMajorPivotsDrawConfig.bullishColor = clrYellow;
|
||||
eaPointedMajorPivotsDrawConfig.bearishColor = clrOrange;
|
||||
|
||||
//
|
||||
result = eaMajorPivotDetector != NULL;
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Initialize Minor Pivot Detector ...
|
||||
eaMinorPivotDetector = new XCPivotDetector(
|
||||
eaAlert, // Alert ...
|
||||
"X121SDTMinor", // Pivot Prefix ...
|
||||
false, // Support Inverted Zones ...
|
||||
0 // Processable Zones ...
|
||||
);
|
||||
|
||||
//
|
||||
// Register Event Handlers ...
|
||||
eaMinorPivotDetector.AddOnPeakBreakedHandler(OnMinorPeakBreakedHandler);
|
||||
eaMinorPivotDetector.AddOnValeBreakedHandler(OnMinorValeBreakedHandler);
|
||||
eaMinorPivotDetector.AddOnPeakDetectedHandler(OnMinorPeakDetectedHandler);
|
||||
eaMinorPivotDetector.AddOnValeDetectedHandler(OnMinorValeDetectedHandler);
|
||||
|
||||
//
|
||||
// Pivot Draw Config ...
|
||||
eaMinorPivotsDrawConfig.zonesWidth = 1;
|
||||
eaMinorPivotsDrawConfig.zonesFill = false;
|
||||
eaMinorPivotsDrawConfig.ignoreZonesAt = true;
|
||||
eaMinorPivotsDrawConfig.bearishColor = clrRed;
|
||||
eaMinorPivotsDrawConfig.bullishColor = clrLime;
|
||||
eaMinorPivotsDrawConfig.zonesStyle = STYLE_DASHDOT;
|
||||
|
||||
//
|
||||
// Pointed Pivot Draw Config ...
|
||||
eaPointedMinorPivotsDrawConfig.zonesWidth = 2;
|
||||
eaPointedMinorPivotsDrawConfig.zonesFill = false;
|
||||
eaPointedMinorPivotsDrawConfig.ignoreZonesAt = true;
|
||||
eaPointedMinorPivotsDrawConfig.bearishColor = clrOrange;
|
||||
eaPointedMinorPivotsDrawConfig.bullishColor = clrYellow;
|
||||
eaPointedMinorPivotsDrawConfig.zonesStyle = STYLE_DASHDOT;
|
||||
|
||||
//
|
||||
result = eaMinorPivotDetector != NULL;
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Pivots ...
|
||||
*/
|
||||
void HandleProcessMajorPviots()
|
||||
{
|
||||
//
|
||||
// Initialize Pivot Processing Bar ...
|
||||
XOHCL bar;
|
||||
bool has = bar.Init(
|
||||
_Symbol,
|
||||
PERIOD_H4,
|
||||
1 // Last Completed Bar ...
|
||||
);
|
||||
if (!has)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Validate Passing Bar ...
|
||||
has =
|
||||
!IsXValid(eaLastMajorPivotProcessing) ||
|
||||
bar.time > eaLastMajorPivotProcessing;
|
||||
if (!has)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
eaLastMajorPivotProcessing = bar.time;
|
||||
|
||||
//
|
||||
eaMajorPivotDetector.Process(
|
||||
bar, // XOHCL Bar ...
|
||||
2, // Pivot Approvation Length ...
|
||||
20 // Loopback ...
|
||||
);
|
||||
|
||||
//
|
||||
// Handle Draw Pivots ...
|
||||
if (!eaDrawMajorPivots)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
XBoxZone tmps[];
|
||||
FillNotExists(
|
||||
tmps, // Dest ...
|
||||
eaMajorPivotDetector.peaks, // Source ...
|
||||
false // Force Clean ...
|
||||
);
|
||||
FillNotExists(
|
||||
tmps, // Dest ...
|
||||
eaMajorPivotDetector.vales, // Source ...
|
||||
false // Force Clean ...
|
||||
);
|
||||
if (HasChild(tmps))
|
||||
{
|
||||
eaDrawer.DrawZones(
|
||||
tmps, // Boxes ...
|
||||
eaMajorPivotsDrawConfig // Draw Config ...
|
||||
);
|
||||
}
|
||||
XClean(tmps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Pivots ...
|
||||
*/
|
||||
void HandleProcessMinorPviots()
|
||||
{
|
||||
//
|
||||
// Initialize Pivot Processing Bar ...
|
||||
XOHCL bar;
|
||||
bool has = bar.Init(
|
||||
_Symbol,
|
||||
PERIOD_M15,
|
||||
1 // Last Completed Bar ...
|
||||
);
|
||||
if (!has)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Validate Passing Bar ...
|
||||
has =
|
||||
!IsXValid(eaLastMinorPivotProcessing) ||
|
||||
bar.time > eaLastMinorPivotProcessing;
|
||||
if (!has)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
eaLastMinorPivotProcessing = bar.time;
|
||||
|
||||
//
|
||||
eaMinorPivotDetector.Process(
|
||||
bar, // XOHCL Bar ...
|
||||
5, // Pivot Approvation Length ...
|
||||
40 // Loopback ...
|
||||
);
|
||||
|
||||
//
|
||||
// Handle Draw Pivots ...
|
||||
if (!eaDrawMinorPivots)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
XBoxZone tmps[];
|
||||
FillNotExists(
|
||||
tmps, // Dest ...
|
||||
eaMinorPivotDetector.peaks, // Source ...
|
||||
false // Force Clean ...
|
||||
);
|
||||
FillNotExists(
|
||||
tmps, // Dest ...
|
||||
eaMinorPivotDetector.vales, // Source ...
|
||||
false // Force Clean ...
|
||||
);
|
||||
if (HasChild(tmps))
|
||||
{
|
||||
eaDrawer.DrawZones(
|
||||
tmps, // Boxes ...
|
||||
eaMinorPivotsDrawConfig // Draw Config ...
|
||||
);
|
||||
}
|
||||
XClean(tmps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Bias based on Pivots ...
|
||||
*/
|
||||
void HanldeBiasDetection()
|
||||
{
|
||||
//
|
||||
bool has = false;
|
||||
|
||||
//
|
||||
// Major Bias ...
|
||||
has =
|
||||
eaMajorPivotDetector.HasPeaks() &&
|
||||
eaMajorPivotDetector.HasVales();
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
int youngestPeakIDX = eaMajorPivotDetector.GetYoungestPeak();
|
||||
int youngestValeIDX = eaMajorPivotDetector.GetYoungestVale();
|
||||
has =
|
||||
IsValidIndex(youngestPeakIDX) &&
|
||||
IsValidIndex(youngestValeIDX);
|
||||
if (has)
|
||||
{
|
||||
//
|
||||
eaMajorBias =
|
||||
eaMajorPivotDetector.peaks[youngestPeakIDX].from > eaMajorPivotDetector.vales[youngestValeIDX].from
|
||||
? X_DIRECTION_BEARISH
|
||||
: eaMajorPivotDetector.vales[youngestValeIDX].from > eaMajorPivotDetector.peaks[youngestPeakIDX].from
|
||||
? X_DIRECTION_BULLISH
|
||||
: X_DIRECTION_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Custom Event Handlers ...
|
||||
|
||||
// #region Pivot Detector Event Handlers ...
|
||||
/**
|
||||
* Calls when a Peak Breaked ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMajorPeakBreakedHandler(XBoxZone &box)
|
||||
{
|
||||
// DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a Vale Breaked ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMajorValeBreakedHandler(XBoxZone &box)
|
||||
{
|
||||
// DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a New Peak Detected ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMajorPeakDetectedHandler(XBoxZone &box)
|
||||
{
|
||||
//
|
||||
eaDrawer.DrawZone(
|
||||
box, //
|
||||
eaPointedMajorPivotsDrawConfig //
|
||||
);
|
||||
|
||||
//
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a New Vale Detected ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMajorValeDetectedHandler(XBoxZone &box)
|
||||
{
|
||||
//
|
||||
eaDrawer.DrawZone(
|
||||
box, //
|
||||
eaPointedMajorPivotsDrawConfig //
|
||||
);
|
||||
|
||||
//
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a Peak Breaked ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMinorPeakBreakedHandler(XBoxZone &box)
|
||||
{
|
||||
// DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a Vale Breaked ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMinorValeBreakedHandler(XBoxZone &box)
|
||||
{
|
||||
// DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a New Peak Detected ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMinorPeakDetectedHandler(XBoxZone &box)
|
||||
{
|
||||
//
|
||||
eaDrawer.DrawZone(
|
||||
box, //
|
||||
eaPointedMinorPivotsDrawConfig //
|
||||
);
|
||||
|
||||
//
|
||||
DebugBreak();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls when a New Vale Detected ...
|
||||
*
|
||||
* @param box: XBoxZone instance ...
|
||||
*/
|
||||
void OnMinorValeDetectedHandler(XBoxZone &box)
|
||||
{
|
||||
//
|
||||
eaDrawer.DrawZone(
|
||||
box, //
|
||||
eaPointedMinorPivotsDrawConfig //
|
||||
);
|
||||
|
||||
//
|
||||
DebugBreak();
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #region Session Detector Event Handlers ...
|
||||
/**
|
||||
* Handle Session Start Event ...
|
||||
*
|
||||
* @param session: ENUM_X_SESSION_TYPE
|
||||
* @param startTime: datetime
|
||||
*/
|
||||
void OnSessionStartHandler(
|
||||
ENUM_X_SESSION_TYPE session,
|
||||
datetime startTime //
|
||||
)
|
||||
{
|
||||
// Print("Session Started: ", EnumToString(session), " at ", TimeToString(startTime));
|
||||
|
||||
// //
|
||||
// // Custom Testing ...
|
||||
// if (session == X_SESSION_NEWYORK)
|
||||
// {
|
||||
// DebugBreak();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Session End Event ...
|
||||
*
|
||||
* @param session: ENUM_X_SESSION_TYPE
|
||||
* @param startTime: datetime
|
||||
*/
|
||||
void OnSessionEndHandler(
|
||||
ENUM_X_SESSION_TYPE session,
|
||||
datetime startTime //
|
||||
)
|
||||
{
|
||||
// Print("Session Ended: ", EnumToString(session), " at ", TimeToString(startTime));
|
||||
|
||||
// //
|
||||
// // Custom Testing ...
|
||||
// if (session == X_SESSION_NEWYORK)
|
||||
// {
|
||||
// DebugBreak();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Overlap Start Event ...
|
||||
*
|
||||
* @param session: ENUM_X_SESSION_OVERLAP_TYPE
|
||||
* @param startTime: datetime
|
||||
*/
|
||||
void OnOverlapStartHandler(
|
||||
ENUM_X_SESSION_OVERLAP_TYPE overlap,
|
||||
datetime startTime //
|
||||
)
|
||||
{
|
||||
// Print("Overlap Started: ", EnumToString(overlap), " at ", TimeToString(startTime));
|
||||
|
||||
// //
|
||||
// // Custom Testing ...
|
||||
// if (overlap == X_OVERLAP_LONDON_NEWYORK)
|
||||
// {
|
||||
// DebugBreak();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Overlap End Event ...
|
||||
*
|
||||
* @param session: ENUM_X_SESSION_OVERLAP_TYPE
|
||||
* @param startTime: datetime
|
||||
*/
|
||||
void OnOverlapEndHandler(
|
||||
ENUM_X_SESSION_OVERLAP_TYPE overlap,
|
||||
datetime startTime //
|
||||
)
|
||||
{
|
||||
// Print("Overlap Ended: ", EnumToString(overlap), " at ", TimeToString(startTime));
|
||||
|
||||
// //
|
||||
// // Custom Testing ...
|
||||
// if (overlap == X_OVERLAP_LONDON_NEWYORK)
|
||||
// {
|
||||
// DebugBreak();
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle Session State Change Event ...
|
||||
*
|
||||
* @param session: ENUM_X_SESSION_TYPE
|
||||
* @param state: ENUM_X_SESSION_STATE
|
||||
*/
|
||||
void OnSessionStateChangeHandler(
|
||||
ENUM_X_SESSION_TYPE session,
|
||||
ENUM_X_SESSION_STATE state //
|
||||
)
|
||||
{
|
||||
// Print("Session State Changed: ", EnumToString(session), " State: ", EnumToString(state));
|
||||
}
|
||||
// #endregion
|
||||
|
||||
// #endregion
|
||||
@@ -3482,6 +3482,74 @@ int FillNotExists(
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
int FillNotExists(
|
||||
T &dest[],
|
||||
T &source[],
|
||||
bool forceClean = false,
|
||||
ENUM_X_DIRECTION forDir = X_DIRECTION_NONE,
|
||||
int maxAllowed = 0 //
|
||||
)
|
||||
{
|
||||
//
|
||||
int result = 0;
|
||||
|
||||
//
|
||||
// Prepare ...
|
||||
if (forceClean)
|
||||
{
|
||||
XClean(dest);
|
||||
}
|
||||
|
||||
//
|
||||
// Normalize ...
|
||||
maxAllowed = NormalizeInt(maxAllowed, 0);
|
||||
|
||||
//
|
||||
// Validate ...
|
||||
bool has = HasChild(source);
|
||||
if (!has)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
int count = ArraySize(source);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
//
|
||||
has =
|
||||
source[i].IsValid() &&
|
||||
(!HasDirection(forDir) || forDir == source[i].dir);
|
||||
if (!has)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
has = AddIfNotExists(source[i], dest);
|
||||
if (has)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Handle Cleanup Collection
|
||||
// if Max Reached ...
|
||||
if (maxAllowed > 0)
|
||||
{
|
||||
//
|
||||
CleanupArray(
|
||||
dest,
|
||||
maxAllowed //
|
||||
);
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
//+------------------------------------------------------------------+
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
@@ -3654,6 +3722,125 @@ bool IsBoxValid(
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a Box based on Price ...
|
||||
*
|
||||
* @param box: XBoxZone instance, box to Validate ...
|
||||
* @param barIndex: int, Start Validating Bar's Index ...
|
||||
* @param pType: ENUM_X_BOUNDARY_PRICE, Price Type for Validation ...
|
||||
*
|
||||
* @return ( bool )
|
||||
*/
|
||||
bool IsBoxValid(
|
||||
XBoxZone &box,
|
||||
int barIndex = 0,
|
||||
ENUM_X_BOUNDARY_PRICE pType = X_BOUNDARY_PRICE_UP_DOWN //
|
||||
)
|
||||
{
|
||||
//
|
||||
bool result = false;
|
||||
|
||||
//
|
||||
// Normalize Bar Index ...
|
||||
barIndex = NormalizeInt(barIndex, 0);
|
||||
|
||||
//
|
||||
// Validaet ...
|
||||
result =
|
||||
box.IsValid() &&
|
||||
IsXValid(pType);
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
bool isBullish = box.IsBullish();
|
||||
|
||||
//
|
||||
int fromIDX = box.FromIndex();
|
||||
result =
|
||||
IsValidIndex(fromIDX) &&
|
||||
barIndex < fromIDX;
|
||||
if (!result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Loop through ...
|
||||
XOHCL iBar;
|
||||
bool has = false;
|
||||
double appliedPrice = EMPTY_VALUE;
|
||||
for (int i = barIndex; i < fromIDX; i++)
|
||||
{
|
||||
//
|
||||
// Initialize Bar ...
|
||||
iBar.Clean();
|
||||
has = iBar.Init(box.symbol, box.period, i);
|
||||
if (!has)
|
||||
{
|
||||
//
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// Calculate Applied Price ...
|
||||
switch (pType)
|
||||
{
|
||||
//
|
||||
case X_BOUNDARY_PRICE_UP_DOWN:
|
||||
//
|
||||
appliedPrice =
|
||||
isBullish
|
||||
? iBar.GetDown()
|
||||
: iBar.GetUp();
|
||||
break;
|
||||
|
||||
//
|
||||
case X_BOUNDARY_PRICE_HIGH_LOW:
|
||||
//
|
||||
appliedPrice =
|
||||
isBullish
|
||||
? iBar.low
|
||||
: iBar.high;
|
||||
break;
|
||||
|
||||
//
|
||||
default:
|
||||
appliedPrice = EMPTY_VALUE;
|
||||
break;
|
||||
}
|
||||
has = NotEmptyZero(appliedPrice);
|
||||
if (!has)
|
||||
{
|
||||
//
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
has =
|
||||
has &&
|
||||
result &&
|
||||
(isBullish
|
||||
? appliedPrice >= box.lower
|
||||
: appliedPrice <= box.upper);
|
||||
if (!has) {
|
||||
//
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
iBar.Clean();
|
||||
}
|
||||
|
||||
//
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
void SortBoxes(
|
||||
XBoxZone &items[],
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Context Generator Module ...
|
||||
* a module for Generating Context ...
|
||||
*
|
||||
* Maintainer
|
||||
*
|
||||
* Hadi Khazaee Asl <https://saherelm.ir> (hadi_khazaee_asl@yahoo.com)
|
||||
*/
|
||||
|
||||
//
|
||||
//#region Imports ...
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
//#endregion
|
||||
|
||||
//
|
||||
//#region Constants ...
|
||||
const outputFilePrefix = "x121XSDT";
|
||||
const allowedPatterns = [
|
||||
//
|
||||
// Base Pilot Files ...
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.pivot-detector.class.mq5$/i
|
||||
},
|
||||
{
|
||||
kind: "file",
|
||||
mode: "allow",
|
||||
match: "name",
|
||||
pattern: /^x-saherelm.x-pivot-detector.test.mq5$/i
|
||||
},
|
||||
]
|
||||
//#endregion
|
||||
|
||||
//
|
||||
// Reading Required Info
|
||||
const inputPath = process.argv[2];
|
||||
const searchPath = path.resolve(inputPath || process.cwd());
|
||||
|
||||
// Check Given Path is Valid or not ...
|
||||
if (!fs.existsSync(searchPath)) {
|
||||
console.error(`Error: path not found: ${searchPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Retrieve Stats of Given Path ...
|
||||
const stat = fs.statSync(searchPath);
|
||||
if (!stat.isDirectory()) {
|
||||
console.error(`Error: path is not a directory: ${searchPath}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
//
|
||||
// Generate Required Data ...
|
||||
const timestamp = getTimestamp(new Date());
|
||||
const folderName = path.basename(searchPath) || "root";
|
||||
const safeFolderName = sanitizeFileName(folderName);
|
||||
|
||||
// Generate Output file Name ...
|
||||
const outputFileName = inputPath
|
||||
? `${outputFilePrefix}-${safeFolderName}-${timestamp}.txt`
|
||||
: `${outputFilePrefix}-${timestamp}.txt`;
|
||||
|
||||
//
|
||||
const outputPath = path.join(
|
||||
process.cwd(),
|
||||
"Documents",
|
||||
"GeneratedContext",
|
||||
outputFileName
|
||||
);
|
||||
|
||||
// Create Output File ...
|
||||
fs.writeFileSync(outputPath, "", "utf8");
|
||||
|
||||
// Navigate to Context Generation ...
|
||||
walk(searchPath);
|
||||
|
||||
//
|
||||
console.log(`Context generated: ${outputPath}`);
|
||||
|
||||
//
|
||||
//#region Required Tools ...
|
||||
/**
|
||||
* Validate Timestamp ...
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
function pad(value) {
|
||||
return String(value).padStart(2, "0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Validate ...
|
||||
*
|
||||
* @param {*} date
|
||||
* @returns
|
||||
*/
|
||||
function getTimestamp(date) {
|
||||
//
|
||||
const year = date.getFullYear();
|
||||
const month = pad(date.getMonth() + 1);
|
||||
const day = pad(date.getDate());
|
||||
const hours = pad(date.getHours());
|
||||
const minutes = pad(date.getMinutes());
|
||||
const seconds = pad(date.getSeconds());
|
||||
|
||||
//
|
||||
return `${year}${month}${day}_${hours}${minutes}${seconds}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Leaf Folder ...
|
||||
*
|
||||
* @param {*} value
|
||||
* @returns
|
||||
*/
|
||||
function sanitizeFileName(value) {
|
||||
return value.replace(/[\\/:*?"<>|]/g, "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* Match Value ...
|
||||
*
|
||||
* @param {*} rule
|
||||
* @param {*} targetPath
|
||||
* @returns
|
||||
*/
|
||||
function getMatchValue(rule, targetPath) {
|
||||
//
|
||||
const normalizedPath = targetPath.replace(/\\/g, "/");
|
||||
if (rule.match === "path") {
|
||||
return normalizedPath;
|
||||
}
|
||||
|
||||
//
|
||||
return path.basename(targetPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Match Patterns ...
|
||||
*
|
||||
* @param {*} targetPath
|
||||
* @param {*} rules
|
||||
* @returns
|
||||
*/
|
||||
function matchesPatterns(targetPath, rules) {
|
||||
//
|
||||
return rules.some(rule => {
|
||||
const value = getMatchValue(rule, targetPath);
|
||||
return rule.pattern.test(value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a Directory Path is Valid for Parsing or not ...
|
||||
*
|
||||
* @param {*} fileName
|
||||
* @returns
|
||||
*/
|
||||
function isAllowedFile(filePath) {
|
||||
//
|
||||
// Extract Deny Rules ...
|
||||
const denyRules = allowedPatterns.filter(
|
||||
p => p.kind === "file" && p.mode === "deny"
|
||||
);
|
||||
|
||||
//
|
||||
// Extract Allowed Rules ...
|
||||
const allowRules = allowedPatterns.filter(
|
||||
p => p.kind === "file" && p.mode === "allow"
|
||||
);
|
||||
|
||||
// Check Deny Rules First ...
|
||||
if (matchesPatterns(filePath, denyRules)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check Allowed Rules ...
|
||||
return matchesPatterns(filePath, allowRules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a Directory Path is Valid for Parsing or not ...
|
||||
*
|
||||
* @param {*} directoryPath
|
||||
* @returns
|
||||
*/
|
||||
function isAllowedDirectory(directoryPath) {
|
||||
//
|
||||
// Extract Deny Rules ...
|
||||
const denyRules = allowedPatterns.filter(
|
||||
p => p.kind === "directory" && p.mode === "deny"
|
||||
);
|
||||
|
||||
//
|
||||
// Extract Allowed Rules ...
|
||||
const allowRules = allowedPatterns.filter(
|
||||
p => p.kind === "directory" && p.mode === "allow"
|
||||
);
|
||||
|
||||
//
|
||||
// Check Deny Patterns as First ...
|
||||
if (matchesPatterns(directoryPath, denyRules)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Check Allow Patterns Exists ...
|
||||
if (allowRules.length === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Chck Allowed Patterns Matched ...
|
||||
return matchesPatterns(directoryPath, allowRules);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loop through a Directory and Navigate files for including in Context ...
|
||||
*
|
||||
* @param {*} dir
|
||||
* @returns
|
||||
*/
|
||||
function walk(dir) {
|
||||
//
|
||||
// Reading Directory Content ...
|
||||
let entries;
|
||||
try {
|
||||
entries = fs.readdirSync(dir, { withFileTypes: true });
|
||||
} catch (error) {
|
||||
fs.appendFileSync(
|
||||
outputPath,
|
||||
`\n### ERROR reading directory: ${dir}\n${error.message}\n`,
|
||||
"utf8"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// Loop through Entries ...
|
||||
for (const entry of entries) {
|
||||
//
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
//
|
||||
// Ignore if Symlink ...
|
||||
if (entry.isSymbolicLink()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Handle if requested Path is Directory ...
|
||||
if (entry.isDirectory()) {
|
||||
//
|
||||
// Allowed Folders ...
|
||||
if (!isAllowedDirectory(fullPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
walk(fullPath);
|
||||
}
|
||||
else
|
||||
//
|
||||
// Handle if requested Path is File ...
|
||||
if (entry.isFile()) {
|
||||
//
|
||||
// Allowed File ...
|
||||
if (!isAllowedFile(fullPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//
|
||||
// Holds File Content ...
|
||||
let content;
|
||||
try {
|
||||
content = fs.readFileSync(fullPath, "utf8");
|
||||
} catch (error) {
|
||||
content = `// Error reading file: ${error.message}`;
|
||||
}
|
||||
|
||||
//
|
||||
// Append File Content for Context ...
|
||||
fs.appendFileSync(
|
||||
outputPath,
|
||||
`\n### FILE: ${fullPath}\n\n${content}\n`,
|
||||
"utf8"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
Reference in New Issue
Block a user