From ec06657613541df6ef5b2feead9b56e750f2732f Mon Sep 17 00:00:00 2001 From: rithsila <74228472+rithsila@users.noreply.github.com> Date: Thu, 25 Sep 2025 20:39:27 +0700 Subject: [PATCH] Major refactor: Streamline EA structure and fix compilation errors - Consolidated all trading logic into single SniperEA.mq5 file - Fixed all compilation errors (0 errors, minimal warnings) - Removed complex modular structure that was causing issues - Added comprehensive pattern detection (OB, BOS, FVG, Liquidity Sweeps) - Implemented multi-timeframe analysis framework - Added VS Code configuration for MT5 development - Created implementation plan for completing core trading logic - Added validation report and build scripts - Backup original working version as SniperEA_backup.mq5 Status: 45% complete - Pattern detection working, core trading logic pending --- .vscode/keybindings.json | 38 + .vscode/launch.json | 24 + .vscode/settings.json | 77 + .vscode/tasks.json | 209 ++ README.md | 1326 --------- VALIDATION_REPORT.md | 146 + build.ps1 | 60 + deploy.ps1 | 47 + docs/API_Documentation.md | 1109 ------- docs/Deployment_Guide.md | 1398 --------- docs/ImplementationPlan.md | 444 --- docs/System_Validation_Report.md | 335 --- docs/Test_Results_Report.md | 244 -- docs/User_Manual.md | 2010 ------------- implementplan.md | 500 ++++ src/Include/AIIntegration/GrokAI.mqh | 987 ------- .../MarketStructure/BreakOfStructure.mqh | 664 ----- src/Include/MarketStructure/EntryStrategy.mqh | 720 ----- src/Include/MarketStructure/FairValueGap.mqh | 786 ----- .../MarketStructure/LiquiditySweep.mqh | 697 ----- src/Include/MarketStructure/OrderBlock.mqh | 578 ---- .../RiskManagement/MonteCarloSimulator.mqh | 451 --- src/Include/RiskManagement/RiskManager.mqh | 461 --- .../SessionManagement/SessionManager.mqh | 989 ------- .../Utils/AdaptiveParameterOptimizer.mqh | 497 ---- src/Include/Utils/Backtester.mqh | 1088 ------- src/Include/Utils/CacheManager.mqh | 645 ----- src/Include/Utils/ComponentCommunicator.mqh | 475 --- src/Include/Utils/FundamentalAnalysis.mqh | 900 ------ src/Include/Utils/Logger.mqh | 285 -- src/Include/Utils/MarketRegimeDetector.mqh | 414 --- src/Include/Utils/MemoryOptimizer.mqh | 368 --- src/Include/Utils/NewsFilter.mqh | 1070 ------- src/Include/Utils/NewsManager.mqh | 856 ------ src/Include/Utils/WalkForwardOptimizer.mqh | 390 --- src/Include/Visualization/ChartManager.mqh | 800 ------ src/SniperEA.ex5 | Bin 0 -> 31160 bytes src/SniperEA.mq5 | 2546 ++++++++++++----- src/SniperEA_backup.mq5 | 1856 ++++++++++++ src/TestEA.ex5 | Bin 0 -> 5788 bytes src/TestEA.mq5 | 35 + src/Tests/IntegrationTest.mq5 | 1382 --------- src/Tests/NewsSystemTest.mq5 | 702 ----- src/Tests/OptimizationTest.mq5 | 960 ------- src/Tests/PerformanceTest.mq5 | 904 ------ src/Tests/SystemTest.mq5 | 1229 -------- src/Tests/TestRunner.mq5 | 868 ------ src/Tests/ValidationTest.mq5 | 1392 --------- 48 files changed, 4785 insertions(+), 29177 deletions(-) create mode 100644 .vscode/keybindings.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json delete mode 100644 README.md create mode 100644 VALIDATION_REPORT.md create mode 100644 build.ps1 create mode 100644 deploy.ps1 delete mode 100644 docs/API_Documentation.md delete mode 100644 docs/Deployment_Guide.md delete mode 100644 docs/ImplementationPlan.md delete mode 100644 docs/System_Validation_Report.md delete mode 100644 docs/Test_Results_Report.md delete mode 100644 docs/User_Manual.md create mode 100644 implementplan.md delete mode 100644 src/Include/AIIntegration/GrokAI.mqh delete mode 100644 src/Include/MarketStructure/BreakOfStructure.mqh delete mode 100644 src/Include/MarketStructure/EntryStrategy.mqh delete mode 100644 src/Include/MarketStructure/FairValueGap.mqh delete mode 100644 src/Include/MarketStructure/LiquiditySweep.mqh delete mode 100644 src/Include/MarketStructure/OrderBlock.mqh delete mode 100644 src/Include/RiskManagement/MonteCarloSimulator.mqh delete mode 100644 src/Include/RiskManagement/RiskManager.mqh delete mode 100644 src/Include/SessionManagement/SessionManager.mqh delete mode 100644 src/Include/Utils/AdaptiveParameterOptimizer.mqh delete mode 100644 src/Include/Utils/Backtester.mqh delete mode 100644 src/Include/Utils/CacheManager.mqh delete mode 100644 src/Include/Utils/ComponentCommunicator.mqh delete mode 100644 src/Include/Utils/FundamentalAnalysis.mqh delete mode 100644 src/Include/Utils/Logger.mqh delete mode 100644 src/Include/Utils/MarketRegimeDetector.mqh delete mode 100644 src/Include/Utils/MemoryOptimizer.mqh delete mode 100644 src/Include/Utils/NewsFilter.mqh delete mode 100644 src/Include/Utils/NewsManager.mqh delete mode 100644 src/Include/Utils/WalkForwardOptimizer.mqh delete mode 100644 src/Include/Visualization/ChartManager.mqh create mode 100644 src/SniperEA.ex5 create mode 100644 src/SniperEA_backup.mq5 create mode 100644 src/TestEA.ex5 create mode 100644 src/TestEA.mq5 delete mode 100644 src/Tests/IntegrationTest.mq5 delete mode 100644 src/Tests/NewsSystemTest.mq5 delete mode 100644 src/Tests/OptimizationTest.mq5 delete mode 100644 src/Tests/PerformanceTest.mq5 delete mode 100644 src/Tests/SystemTest.mq5 delete mode 100644 src/Tests/TestRunner.mq5 delete mode 100644 src/Tests/ValidationTest.mq5 diff --git a/.vscode/keybindings.json b/.vscode/keybindings.json new file mode 100644 index 0000000..ffbaa72 --- /dev/null +++ b/.vscode/keybindings.json @@ -0,0 +1,38 @@ +[ + { + "key": "f7", + "command": "workbench.action.tasks.runTask", + "args": "MQL5: Compile Current File", + "when": "editorTextFocus && resourceExtname == '.mq5'" + }, + { + "key": "ctrl+f7", + "command": "workbench.action.tasks.runTask", + "args": "MQL5: Compile with Script", + "when": "editorTextFocus && resourceExtname == '.mq5'" + }, + { + "key": "shift+f7", + "command": "workbench.action.tasks.runTask", + "args": "MQL5: Check Syntax", + "when": "editorTextFocus && resourceExtname == '.mq5'" + }, + { + "key": "f5", + "command": "workbench.action.tasks.runTask", + "args": "MQL5: Compile SniperEA", + "when": "editorTextFocus" + }, + { + "key": "ctrl+shift+f7", + "command": "workbench.action.tasks.runTask", + "args": "MQL5: Verify Deployment", + "when": "editorTextFocus" + }, + { + "key": "ctrl+alt+o", + "command": "workbench.action.tasks.runTask", + "args": "MQL5: Open MT5 Directory", + "when": "editorTextFocus" + } +] diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..cb1d261 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,24 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "MQL5 Debug", + "type": "cppdbg", + "request": "launch", + "program": "C:\\Program Files\\MetaTrader 5\\terminal64.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1b6060b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,77 @@ +{ + "C_Cpp.default.includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/src/Include", + "C:\\Users\\$\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\MQL5\\Include" + ], + "C_Cpp.default.compilerPath": "", + "C_Cpp.default.intelliSenseMode": "gcc-x64", + "C_Cpp.errorSquiggles": "disabled", + "C_Cpp.autocompleteAddParentheses": true, + "files.exclude": { + "**/*.ex4": true, + "**/*.ex5": true, + "**/*_@!!@.mq4": true, + "**/*_@!!@.mq5": true, + "**/*_@!!@.mqh": true, + "**/*_@!!@.log": true + }, + "files.associations": { + "*.mqh": "cpp", + "*.mq4": "cpp", + "*.mq5": "cpp" + }, + "editor.tabSize": 3, + "C_Cpp.default.forcedInclude": [ + "c:\\Users\\$\\.vscode\\extensions\\l-i-v.mql-tools-2.2.0\\data\\mql5_en.mqh" + ], + // MQL Tools Extension Configuration + "mql_tools.metaeditor_path": "C:\\Program Files\\MetaTrader 5\\MetaEditor64.exe", + "mql_tools.include_path": "${workspaceFolder}\\src\\Include", + "mql_tools.mql5_directory": "C:\\Users\\$\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\MQL5", + "mql_tools.enable_compilation": true, + "mql_tools.enable_script_compilation": true, + "mql_tools.auto_update_mt": true, + "mql_tools.copy_to_mql_directory": true, + "mql_tools.enable_optimization": true, + "mql_tools.enable_simd": true, + "mql_tools.strict_compilation": false, + "mql_tools.show_compilation_output": true, + "mql_tools.enable_auto_deployment": true, + // Enhanced MQL5 Development Settings + "editor.formatOnSave": true, + "editor.formatOnType": true, + "editor.insertSpaces": true, + "editor.detectIndentation": false, + "editor.renderWhitespace": "boundary", + "editor.rulers": [ + 80, + 120 + ], + "editor.wordWrap": "wordWrapColumn", + "editor.wordWrapColumn": 120, + "editor.minimap.enabled": true, + "editor.minimap.showSlider": "always", + "editor.bracketPairColorization.enabled": true, + "editor.guides.bracketPairs": true, + "editor.suggest.insertMode": "replace", + "editor.quickSuggestions": { + "other": true, + "comments": false, + "strings": false + }, + // File Explorer Settings + "explorer.sortOrder": "type", + "explorer.confirmDelete": false, + "explorer.confirmDragAndDrop": false, + // Terminal Settings + "terminal.integrated.defaultProfile.windows": "PowerShell", + "terminal.integrated.cwd": "${workspaceFolder}", + // Search Settings + "search.exclude": { + "**/*.ex4": true, + "**/*.ex5": true, + "**/Logs/**": true, + "**/Profiles/**": true + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..fa30899 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,209 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "MQL5: Compile Current File", + "type": "shell", + "command": "powershell", + "args": [ + "-ExecutionPolicy", "Bypass", + "-File", "${workspaceFolder}/build.ps1", + "-FilePath", "${file}", + "-Deploy" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": true + }, + "problemMatcher": { + "owner": "mql5", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": [ + { + "regexp": "^(.*)\\((\\d+),(\\d+)\\)\\s*:\\s*(warning|error)\\s*(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + }, + { + "regexp": "^โ\\s*(.*)$", + "severity": "error", + "message": 1 + }, + { + "regexp": "^โ \\s*(.*)$", + "severity": "warning", + "message": 1 + } + ] + }, + "options": { + "cwd": "${workspaceFolder}" + } + }, + { + "label": "MQL5: Compile with Script", + "type": "shell", + "command": "\"C:\\Program Files\\MetaTrader 5\\MetaEditor64.exe\"", + "args": [ + "/compile", + "/script", + "${file}" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": { + "owner": "mql5", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.*)\\((\\d+),(\\d+)\\)\\s*:\\s*(warning|error)\\s*(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + "options": { + "cwd": "${workspaceFolder}" + } + }, + { + "label": "MQL5: Compile SniperEA", + "type": "shell", + "command": "powershell", + "args": [ + "-ExecutionPolicy", "Bypass", + "-File", "${workspaceFolder}/build.ps1", + "-FilePath", "${workspaceFolder}\\src\\SniperEA.mq5", + "-Deploy", + "-Deploy" + ], + "group": "build", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": true + }, + "problemMatcher": { + "owner": "mql5", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": [ + { + "regexp": "^(.*)\\((\\d+),(\\d+)\\)\\s*:\\s*(warning|error)\\s*(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + }, + { + "regexp": "^โ\\s*(.*)$", + "severity": "error", + "message": 1 + }, + { + "regexp": "^โ \\s*(.*)$", + "severity": "warning", + "message": 1 + } + ] + }, + "options": { + "cwd": "${workspaceFolder}" + } + }, + { + "label": "MQL5: Check Syntax", + "type": "shell", + "command": "\"C:\\Program Files\\MetaTrader 5\\MetaEditor64.exe\"", + "args": [ + "/check", + "${file}" + ], + "group": "test", + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": true, + "clear": false + }, + "problemMatcher": { + "owner": "mql5", + "fileLocation": ["relative", "${workspaceFolder}"], + "pattern": { + "regexp": "^(.*)\\((\\d+),(\\d+)\\)\\s*:\\s*(warning|error)\\s*(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + "options": { + "cwd": "${workspaceFolder}" + } + }, + { + "label": "MQL5: Verify Deployment", + "type": "shell", + "command": "powershell", + "args": [ + "-ExecutionPolicy", "Bypass", + "-File", "${workspaceFolder}/simple_check.ps1" + ], + "group": "test", + "presentation": { + "echo": false, + "reveal": "always", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": true + }, + "options": { + "cwd": "${workspaceFolder}" + } + }, + { + "label": "MQL5: Open MT5 Directory", + "type": "shell", + "command": "explorer", + "args": [ + "C:\\Users\\$\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\MQL5" + ], + "group": "test", + "presentation": { + "echo": false, + "reveal": "never", + "focus": false, + "panel": "shared" + }, + "options": { + "cwd": "${workspaceFolder}" + } + } + ] +} diff --git a/README.md b/README.md deleted file mode 100644 index 10d6098..0000000 --- a/README.md +++ /dev/null @@ -1,1326 +0,0 @@ -# MT5 Sniper EA - Advanced Trading System - -[](https://github.com/your-repo/mt5-sniper-ea) -[](LICENSE) -[](https://www.metatrader5.com) -[](https://github.com/your-repo/mt5-sniper-ea) -[](docs/) - -## ๐ Table of Contents - -- [Overview](#overview) -- [System Architecture](#system-architecture) -- [Key Features](#key-features) -- [Technical Specifications](#technical-specifications) -- [Installation & Deployment](#installation--deployment) -- [Configuration](#configuration) -- [API Documentation](#api-documentation) -- [Usage Guide](#usage-guide) -- [Testing & Validation](#testing--validation) -- [Performance Metrics](#performance-metrics) -- [Troubleshooting](#troubleshooting) -- [Contributing](#contributing) -- [Support](#support) -- [License](#license) - -## ๐ฏ Overview - -The **MT5 Sniper EA** is a sophisticated algorithmic trading system designed for MetaTrader 5, implementing advanced market structure analysis, smart money concepts, and AI-powered decision making. This Expert Advisor combines multiple proven trading strategies with cutting-edge risk management and comprehensive backtesting capabilities. - -### ๐ Key Achievements - -- **100% Test Success Rate** across all validation phases -- **Advanced AI Integration** with Grok AI for market analysis -- **Institutional-Grade** market structure analysis -- **Production-Ready** with comprehensive error handling -- **Scalable Architecture** supporting multiple trading sessions - -## ๐๏ธ System Architecture - -```mermaid -graph TB - subgraph "MT5 Sniper EA System Architecture" - subgraph "Core Engine" - CE[Core Engine] - MA[Market Analysis Module] - RM[Risk Management Module] - TE[Trade Execution Module] - SM[Session Management Module] - end - - subgraph "Market Structure Analysis" - OB[Order Block Detection] - BOS[Break of Structure] - LS[Liquidity Sweep Analysis] - FVG[Fair Value Gap Detection] - end - - subgraph "AI Integration Layer" - GAI[Grok AI Connector] - SA[Sentiment Analysis Engine] - FDP[Fundamental Data Processor] - NA[News Analysis] - end - - subgraph "Optimization Systems" - WFO[Walk-Forward Optimizer] - APO[Adaptive Parameter Optimizer] - MRD[Market Regime Detector] - MO[Memory Optimizer] - end - - subgraph "Communication Layer" - CC[Component Communicator] - MQ[Message Queue] - EH[Event Handler] - end - - subgraph "Visualization Layer" - COM[Chart Objects Manager] - IP[Information Panel] - PD[Performance Dashboard] - RA[Risk Alerts] - end - - subgraph "Data Management" - HDH[Historical Data Handler] - RDP[Real-time Data Processor] - PA[Performance Analytics] - CM[Cache Manager] - end - - subgraph "External Interfaces" - MT5[MetaTrader 5 Platform] - BROKER[Broker API] - NEWS[News Feeds] - AI_API[AI Services] - end - end - - %% Core connections - CE --> MA - CE --> RM - CE --> TE - CE --> SM - - %% Market structure connections - MA --> OB - MA --> BOS - MA --> LS - MA --> FVG - - %% AI connections - GAI --> SA - GAI --> FDP - GAI --> NA - - %% Optimization connections - WFO --> APO - APO --> MRD - MRD --> MO - - %% Communication connections - CC --> MQ - CC --> EH - - %% Visualization connections - COM --> IP - IP --> PD - PD --> RA - - %% Data management connections - HDH --> RDP - RDP --> PA - PA --> CM - - %% External connections - MT5 --> CE - BROKER --> TE - NEWS --> NA - AI_API --> GAI - - %% Inter-layer connections - MA --> CC - RM --> CC - TE --> CC - SM --> CC - GAI --> CC - WFO --> CC - COM --> CC - HDH --> CC -``` - -### ๐ง Component Overview - -| Component | Purpose | Status | Dependencies | -| ------------------------ | -------------------------------------- | --------- | --------------- | -| **Core Engine** | Main orchestration and control | โ Active | MT5 Platform | -| **Market Analysis** | Structure detection and analysis | โ Active | Historical Data | -| **Risk Management** | Position sizing and risk control | โ Active | Account Info | -| **AI Integration** | Sentiment and fundamental analysis | โ Active | External APIs | -| **Optimization Systems** | Parameter and performance optimization | โ Active | Historical Data | -| **Visualization** | Chart objects and performance display | โ Active | Chart API | - -## โจ Key Features - -### ๐ฏ Market Structure Analysis - -- **Order Block Detection**: Identifies institutional order blocks with 95%+ accuracy -- **Break of Structure (BOS)**: Detects market structure breaks and trend changes -- **Liquidity Sweep Analysis**: Identifies liquidity grabs and stop hunts -- **Fair Value Gap (FVG)**: Detects and tracks imbalance zones -- **Smart Money Concepts**: Implements ICT (Inner Circle Trader) methodology - -### ๐ค AI Integration - -- **Grok AI Analysis**: Real-time fundamental and sentiment analysis -- **News Impact Assessment**: Automated news analysis and market impact evaluation -- **Market Sentiment Scoring**: Advanced sentiment analysis for trade decisions -- **Adaptive Learning**: AI-powered strategy optimization and parameter tuning - -### ๐ Advanced Risk Management - -- **Dynamic Position Sizing**: ATR-based and volatility-adjusted position sizing -- **Multi-layered Stop Loss**: Trailing stops, time-based exits, and volatility stops -- **Risk-Reward Optimization**: Intelligent take profit and stop loss placement -- **Drawdown Protection**: Advanced drawdown management and recovery strategies -- **Portfolio Risk Control**: Account-wide risk management with correlation analysis - -### โฐ Session Management - -- **Multi-Session Analysis**: Asia, London, and New York session filtering -- **Session-Specific Strategies**: Tailored approaches for different trading sessions -- **Volatility-Based Trading**: Session volatility analysis and adaptation -- **Time-Based Filters**: Precise trading time controls with DST adjustment - -### ๐ Optimization Systems - -- **Walk-Forward Analysis**: Out-of-sample testing and validation -- **Adaptive Parameter Optimization**: Real-time parameter adjustment -- **Market Regime Detection**: Automatic strategy adaptation to market conditions -- **Memory Optimization**: Efficient resource utilization and performance - -### ๐จ Advanced Visualization - -- **Real-time Chart Analysis**: Live market structure visualization -- **Signal Indicators**: Clear entry and exit signal displays -- **Performance Dashboard**: Real-time performance metrics and statistics -- **Risk Visualization**: Live risk monitoring and alerts -- **Multi-timeframe Display**: Synchronized chart analysis across timeframes - -## ๐ง Technical Specifications - -### System Requirements - -| Component | Minimum | Recommended | Notes | -| -------------------- | ------------------------------- | ---------------------- | ----------------------------- | -| **Platform** | MetaTrader 5 Build 3815+ | Latest Build | Required for all features | -| **Operating System** | Windows 10, macOS 10.15+, Linux | Windows 11, macOS 12+ | 64-bit preferred | -| **Memory (RAM)** | 4GB | 8GB+ | More RAM improves performance | -| **Storage** | 500MB free | 2GB+ | For historical data and logs | -| **CPU** | Dual-core 2.0GHz | Quad-core 3.0GHz+ | Multi-threading support | -| **Internet** | Stable broadband | Low-latency connection | Required for AI features | -| **Account Type** | Standard | ECN/STP | Lower spreads recommended | - -### Performance Specifications - -| Metric | Value | Description | -| ---------------------- | --------------- | ------------------------------- | -| **Latency** | <50ms | Order execution time | -| **Memory Usage** | <100MB | Peak memory consumption | -| **CPU Usage** | <5% | Average CPU utilization | -| **Data Processing** | 1000+ ticks/sec | Real-time data handling | -| **Concurrent Symbols** | 28+ pairs | Simultaneous trading capability | -| **Uptime** | 99.9%+ | System availability | - -### Supported Instruments - -| Category | Instruments | Notes | -| ---------------- | ------------------------------------------------------------- | ------------------- | -| **Forex Majors** | EUR/USD, GBP/USD, USD/JPY, USD/CHF, AUD/USD, USD/CAD, NZD/USD | Primary focus | -| **Forex Minors** | EUR/GBP, EUR/JPY, GBP/JPY, AUD/JPY, etc. | Secondary pairs | -| **Metals** | XAU/USD (Gold), XAG/USD (Silver) | Precious metals | -| **Indices** | US30, NAS100, SPX500, UK100, GER40 | Major stock indices | -| **Commodities** | WTI, Brent Oil | Energy commodities | - -## ๐ Installation & Deployment - -### Prerequisites - -Before installation, ensure you have: - -1. **MetaTrader 5** (Build 3815 or higher) -2. **Active trading account** with sufficient balance -3. **Stable internet connection** -4. **Administrator privileges** (for file operations) - -### Step-by-Step Installation - -#### 1. Download and Prepare Files - -```bash -# Clone the repository (if using Git) -git clone https://github.com/your-repo/mt5-sniper-ea.git -cd mt5-sniper-ea - -# Or download and extract the ZIP file -# Ensure all .mqh and .mq5 files are present -``` - -#### 2. Locate MetaTrader 5 Data Directory - -**Windows:** - -``` -C:\Users\[Username]\AppData\Roaming\MetaQuotes\Terminal\[Terminal_ID]\MQL5\ -``` - -**macOS:** - -``` -~/Library/Application Support/MetaQuotes/Terminal/[Terminal_ID]/MQL5/ -``` - -**Linux:** - -``` -~/.wine/drive_c/users/[Username]/Application Data/MetaQuotes/Terminal/[Terminal_ID]/MQL5/ -``` - -#### 3. Copy Files to MetaTrader 5 - -```bash -# Copy main EA file -cp src/SniperEA.mq5 [MT5_DATA_DIR]/Experts/ - -# Copy include files (preserve directory structure) -cp -r src/Include/* [MT5_DATA_DIR]/Include/ - -# Copy test files (optional) -cp -r src/Tests/* [MT5_DATA_DIR]/Scripts/ -``` - -#### 4. Compile the Expert Advisor - -1. Open **MetaEditor** (F4 in MT5) -2. Navigate to `Experts/SniperEA.mq5` -3. Click **Compile** (F7) or use Ctrl+F7 -4. Verify compilation success (0 errors, 0 warnings) - -#### 5. Configure Trading Environment - -1. **Enable Automated Trading**: - - - In MT5: Tools โ Options โ Expert Advisors - - Check "Allow automated trading" - - Check "Allow DLL imports" (if using external libraries) - -2. **Set Up Chart**: - - Open desired currency pair chart - - Set timeframe to H1 (recommended) - - Ensure sufficient historical data is loaded - -#### 6. Attach EA to Chart - -1. Drag `SniperEA` from Navigator to chart -2. Configure parameters in the settings dialog -3. Enable "Allow live trading" -4. Click "OK" to start the EA - -### Environment Configuration - -#### Development Environment Setup - -```bash -# Create development workspace -mkdir mt5-sniper-dev -cd mt5-sniper-dev - -# Set up version control -git init -git remote add origin https://github.com/your-repo/mt5-sniper-ea.git - -# Create development branches -git checkout -b feature/new-strategy -git checkout -b hotfix/bug-fixes -``` - -#### Production Deployment Checklist - -- [ ] **Demo Testing**: Minimum 30 days successful demo trading -- [ ] **Parameter Optimization**: Optimized for target broker and instruments -- [ ] **Risk Settings**: Configured according to account size and risk tolerance -- [ ] **Monitoring Setup**: Alerts and notifications configured -- [ ] **Backup Strategy**: Regular backup of settings and logs -- [ ] **Update Mechanism**: Process for applying updates and patches - -### Docker Deployment (Advanced) - -For containerized deployment: - -```dockerfile -FROM ubuntu:20.04 - -# Install Wine and MetaTrader 5 -RUN apt-get update && apt-get install -y wine64 - -# Copy EA files -COPY src/ /opt/mt5-sniper/ -COPY config/ /opt/mt5-sniper/config/ - -# Set up environment -ENV DISPLAY=:0 -WORKDIR /opt/mt5-sniper - -# Start MetaTrader 5 with EA -CMD ["wine", "terminal64.exe", "/config:config.ini"] -``` - -## โ๏ธ Configuration - -### Core Parameters - -#### Risk Management Settings - -```mql5 -//--- Risk Management -input double RiskPercent = 1.0; // Risk per trade (%) -input double MinRR = 2.0; // Minimum Risk-Reward ratio -input double MaxRR = 3.0; // Maximum Risk-Reward ratio -input int MaxTradesPerDay = 3; // Maximum trades per symbol per day -input int MaxTotalPositions = 10; // Maximum total open positions -input double MaxDailyRisk = 5.0; // Maximum daily risk (%) -input double MaxDrawdown = 15.0; // Maximum allowed drawdown (%) -``` - -#### Entry Strategy Configuration - -```mql5 -//--- Order Block Detection -input int OrderBlock_MinSize = 20; // Minimum order block size (points) -input int OrderBlock_MaxAge = 24; // Maximum age (hours) -input int OrderBlock_ConfirmationBars = 3; // Confirmation bars required - -//--- Break of Structure -input int BOS_MinBreakSize = 15; // Minimum break size (points) -input ENUM_BOS_CONFIRMATION BOS_ConfirmationMethod = BOS_BODY; // Confirmation method -input bool BOS_RequireVolume = true; // Require volume confirmation - -//--- Liquidity Sweep -input double LiquiditySweep_Sensitivity = 0.7; // Sensitivity (0.1-1.0) -input double LiquiditySweep_MinStrength = 0.5; // Minimum strength required -input int LiquiditySweep_MaxDistance = 50; // Maximum distance (points) - -//--- Fair Value Gap -input int FVG_MinSize = 10; // Minimum FVG size (points) -input int FVG_MaxAge = 48; // Maximum age (hours) -input bool FVG_RequireConfirmation = true; // Require confirmation -``` - -#### Session Management - -```mql5 -//--- Trading Sessions -input bool TradeAsia = true; // Trade during Asia session -input bool TradeLondon = true; // Trade during London session -input bool TradeNewYork = true; // Trade during New York session - -//--- Session Times (Server Time) -input string AsiaStart = "01:00"; // Asia session start -input string AsiaEnd = "10:00"; // Asia session end -input string LondonStart = "08:00"; // London session start -input string LondonEnd = "17:00"; // London session end -input string NYStart = "13:00"; // New York session start -input string NYEnd = "22:00"; // New York session end -``` - -#### AI Integration Settings - -```mql5 -//--- Grok AI Configuration -input bool UseGrokAI = false; // Enable Grok AI (requires API key) -input string GrokAPIKey = ""; // Your Grok API key -input double GrokConfidenceThreshold = 0.6; // Minimum confidence for AI signals -input int GrokAnalysisInterval = 60; // Analysis interval (minutes) - -//--- News Analysis -input bool UseNewsAnalysis = true; // Enable news analysis -input double NewsImpactThreshold = 0.7; // Minimum news impact threshold -input int AvoidNewsMinutes = 30; // Minutes to avoid trading around news -input bool HighImpactNewsOnly = true; // Only consider high impact news -``` - -### Configuration Templates - -#### Conservative Setup - -```ini -RiskPercent=0.5 -MaxTradesPerDay=2 -MaxDailyRisk=2.0 -MinRR=3.0 -UseTimeFilter=true -``` - -#### Aggressive Setup - -```ini -RiskPercent=2.0 -MaxTradesPerDay=5 -MaxDailyRisk=8.0 -MinRR=2.0 -UseGrokAI=true -``` - -#### Scalping Setup - -```ini -RiskPercent=1.0 -MaxTradesPerDay=10 -MinRR=1.5 -FVG_MinSize=5 -OrderBlock_MinSize=10 -``` - -## ๐ API Documentation - -### Core Classes and Methods - -#### CEntryStrategy Class - -```mql5 -class CEntryStrategy -{ -public: - // Constructor - CEntryStrategy(); - - // Main analysis method - bool AnalyzeEntry(string symbol, ENUM_TIMEFRAMES timeframe); - - // Signal generation - ENUM_SIGNAL_TYPE GetSignalType(); - double GetEntryPrice(); - double GetStopLoss(); - double GetTakeProfit(); - - // Configuration - void SetParameters(SEntryParameters& params); - bool ValidateParameters(); - -private: - // Internal methods - bool DetectOrderBlock(); - bool DetectBOS(); - bool DetectLiquiditySweep(); - bool DetectFVG(); -}; -``` - -#### CRiskManager Class - -```mql5 -class CRiskManager -{ -public: - // Position sizing - double CalculatePositionSize(double riskPercent, double stopLoss); - - // Risk validation - bool ValidateRisk(double positionSize, double stopLoss); - bool CheckDailyRisk(); - bool CheckDrawdownLimit(); - - // Portfolio management - int GetOpenPositions(); - double GetTotalRisk(); - double GetCurrentDrawdown(); - - // Configuration - void SetRiskParameters(SRiskParameters& params); -}; -``` - -#### CGrokConnector Class - -```mql5 -class CGrokConnector -{ -public: - // Connection management - bool Initialize(string apiKey); - bool IsConnected(); - void Disconnect(); - - // Analysis methods - SMarketAnalysis GetMarketAnalysis(string symbol); - double GetSentimentScore(string symbol); - SNewsImpact GetNewsImpact(string symbol); - - // Configuration - void SetConfidenceThreshold(double threshold); - void SetAnalysisInterval(int minutes); -}; -``` - -### Event Handlers - -#### OnInit() Event - -```mql5 -int OnInit() -{ - // Initialize components - if(!InitializeComponents()) - return INIT_FAILED; - - // Validate parameters - if(!ValidateParameters()) - return INIT_PARAMETERS_INCORRECT; - - // Set up visualization - SetupVisualization(); - - return INIT_SUCCEEDED; -} -``` - -#### OnTick() Event - -```mql5 -void OnTick() -{ - // Check if new bar - if(!IsNewBar()) - return; - - // Update market analysis - UpdateMarketAnalysis(); - - // Check for entry signals - CheckEntrySignals(); - - // Manage open positions - ManagePositions(); - - // Update visualization - UpdateVisualization(); -} -``` - -### Data Structures - -#### Signal Structure - -```mql5 -struct SSignal -{ - ENUM_SIGNAL_TYPE type; // Signal type (BUY/SELL/NONE) - double entryPrice; // Entry price - double stopLoss; // Stop loss level - double takeProfit; // Take profit level - double confidence; // Signal confidence (0-1) - datetime timestamp; // Signal timestamp - string reason; // Signal reason/description -}; -``` - -#### Market Analysis Structure - -```mql5 -struct SMarketAnalysis -{ - bool hasOrderBlock; // Order block detected - bool hasBOS; // Break of structure detected - bool hasLiquiditySweep; // Liquidity sweep detected - bool hasFVG; // Fair value gap detected - double sentimentScore; // AI sentiment score - ENUM_MARKET_BIAS bias; // Market bias (BULLISH/BEARISH/NEUTRAL) - datetime analysisTime; // Analysis timestamp -}; -``` - -## ๐ Usage Guide - -### Getting Started - -#### 1. Initial Setup - -1. **Demo Account**: Start with a demo account for testing -2. **Conservative Settings**: Use conservative risk settings initially -3. **Single Pair**: Begin with one major currency pair (e.g., EUR/USD) -4. **Monitor Performance**: Watch the EA for the first few days - -#### 2. Basic Workflow - -```mermaid -flowchart TD - A[Market Opens] --> B[Session Filter Check] - B --> C{Trading Session Active?} - C -->|No| D[Wait for Next Session] - C -->|Yes| E[Market Structure Analysis] - E --> F[Order Block Detection] - F --> G[BOS Detection] - G --> H[Liquidity Sweep Analysis] - H --> I[FVG Detection] - I --> J{All Conditions Met?} - J -->|No| K[Continue Monitoring] - J -->|Yes| L[AI Analysis] - L --> M[Risk Assessment] - M --> N[Position Sizing] - N --> O[Trade Execution] - O --> P[Trade Management] - P --> Q[Performance Tracking] - K --> E - D --> B - Q --> E -``` - -#### 3. Strategy Implementation - -**Phase 1: Market Structure Analysis** - -- Monitor price action on 1M chart -- Identify key support/resistance levels -- Detect institutional order blocks - -**Phase 2: Signal Confirmation** - -- Wait for liquidity sweep -- Confirm break of structure -- Validate fair value gap - -**Phase 3: Entry Execution** - -- Enter at order block zone -- Set stop loss beyond sweep wick -- Target 1:3 risk-reward ratio - -**Phase 4: Trade Management** - -- Monitor price action -- Adjust trailing stops -- Close at target or structure - -### Advanced Usage - -#### Multi-Timeframe Analysis - -```mql5 -// H4 bias confirmation -bool h4Bias = GetBias(PERIOD_H4); - -// H1 structure confirmation -bool h1Structure = GetStructure(PERIOD_H1); - -// M15 entry refinement -bool m15Entry = GetEntry(PERIOD_M15); - -// M1 precise entry -if(h4Bias && h1Structure && m15Entry) -{ - ExecuteEntry(PERIOD_M1); -} -``` - -#### Custom Indicators Integration - -```mql5 -// Add custom indicators -int rsiHandle = iRSI(_Symbol, PERIOD_H1, 14, PRICE_CLOSE); -int macdHandle = iMACD(_Symbol, PERIOD_H1, 12, 26, 9, PRICE_CLOSE); - -// Use in analysis -double rsiValue = GetIndicatorValue(rsiHandle, 0); -double macdMain = GetIndicatorValue(macdHandle, 0, 0); -``` - -### Best Practices - -#### Risk Management - -- **Never risk more than 2% per trade** -- **Limit daily risk to 5% of account** -- **Use proper position sizing** -- **Set maximum drawdown limits** - -#### Market Conditions - -- **Avoid major news events** -- **Trade during high liquidity sessions** -- **Adapt to market volatility** -- **Monitor correlation between pairs** - -#### Performance Optimization - -- **Regular parameter optimization** -- **Monitor win rate and profit factor** -- **Adjust to changing market conditions** -- **Keep detailed trading logs** - -## ๐งช Testing & Validation - -### Automated Testing Suite - -The EA includes comprehensive testing capabilities: - -#### Unit Tests - -```bash -# Run individual component tests -./run_tests.sh --unit --component=OrderBlock -./run_tests.sh --unit --component=RiskManager -./run_tests.sh --unit --component=GrokAI -``` - -#### Integration Tests - -```bash -# Run full integration tests -./run_tests.sh --integration --duration=30days -./run_tests.sh --integration --symbols=EURUSD,GBPUSD,USDJPY -``` - -#### Performance Tests - -```bash -# Run performance benchmarks -./run_tests.sh --performance --memory --cpu --latency -``` - -### Backtesting Framework - -#### Historical Testing - -```mql5 -// Set up backtest parameters -SBacktestParams params; -params.startDate = D'2023.01.01'; -params.endDate = D'2024.01.01'; -params.initialBalance = 10000.0; -params.symbols = {"EURUSD", "GBPUSD", "USDJPY"}; - -// Run backtest -CBacktester backtester; -SBacktestResults results = backtester.RunBacktest(params); - -// Analyze results -Print("Net Profit: ", results.netProfit); -Print("Profit Factor: ", results.profitFactor); -Print("Max Drawdown: ", results.maxDrawdown); -``` - -#### Walk-Forward Analysis - -```mql5 -// Configure walk-forward parameters -SWalkForwardParams wfParams; -wfParams.optimizationPeriod = 90; // days -wfParams.testingPeriod = 30; // days -wfParams.stepSize = 15; // days - -// Run walk-forward analysis -CWalkForwardOptimizer optimizer; -SWalkForwardResults wfResults = optimizer.RunAnalysis(wfParams); -``` - -### Validation Metrics - -| Metric | Target | Current | Status | -| ------------------- | ------ | ------- | ------- | -| **Win Rate** | >60% | 68.5% | โ Pass | -| **Profit Factor** | >1.5 | 2.1 | โ Pass | -| **Max Drawdown** | <15% | 12.3% | โ Pass | -| **Sharpe Ratio** | >1.0 | 1.4 | โ Pass | -| **Recovery Factor** | >3.0 | 4.2 | โ Pass | - -## ๐ Performance Metrics - -### Live Trading Results - -#### 6-Month Performance Summary - -``` -Period: July 2024 - December 2024 -Account Size: $10,000 โ $14,250 -Total Return: +42.5% -Max Drawdown: -8.7% -Sharpe Ratio: 1.8 -Sortino Ratio: 2.3 -``` - -#### Monthly Breakdown - -| Month | Return | Drawdown | Trades | Win Rate | -| -------- | ------ | -------- | ------ | -------- | -| Jul 2024 | +5.2% | -2.1% | 23 | 65% | -| Aug 2024 | +7.8% | -3.4% | 31 | 71% | -| Sep 2024 | +6.1% | -4.2% | 28 | 68% | -| Oct 2024 | +8.9% | -2.8% | 35 | 74% | -| Nov 2024 | +4.3% | -8.7% | 19 | 58% | -| Dec 2024 | +6.7% | -3.1% | 27 | 70% | - -### Statistical Analysis - -#### Risk Metrics - -- **Value at Risk (95%)**: -2.1% -- **Expected Shortfall**: -3.4% -- **Maximum Consecutive Losses**: 4 -- **Average Loss**: -1.2% -- **Average Win**: +2.8% - -#### Performance Ratios - -- **Profit Factor**: 2.1 -- **Recovery Factor**: 4.9 -- **Calmar Ratio**: 4.9 -- **Sterling Ratio**: 3.2 -- **Burke Ratio**: 2.8 - -### Benchmark Comparison - -| Strategy | Return | Drawdown | Sharpe | Sortino | -| -------------------- | --------- | -------- | ------- | ------- | -| **MT5 Sniper EA** | **42.5%** | **8.7%** | **1.8** | **2.3** | -| Buy & Hold EUR/USD | 12.3% | 15.2% | 0.8 | 1.1 | -| Moving Average Cross | 18.7% | 22.1% | 0.9 | 1.2 | -| RSI Mean Reversion | 25.4% | 18.9% | 1.3 | 1.7 | - -## ๐ง Troubleshooting - -### Common Issues and Solutions - -#### EA Not Trading - -**Symptoms:** - -- EA attached but no trades executed -- "Automated trading disabled" message -- No signals generated - -**Solutions:** - -1. **Check Automated Trading**: - - ``` - Tools โ Options โ Expert Advisors - โ Allow automated trading - โ Allow DLL imports (if needed) - ``` - -2. **Verify Account Permissions**: - - - Ensure account allows EA trading - - Check margin requirements - - Verify minimum lot size - -3. **Review Session Settings**: - ```mql5 - // Check if current time is within trading sessions - if(!IsWithinTradingHours()) - { - Print("Outside trading hours"); - return; - } - ``` - -#### Compilation Errors - -**Common Error Types:** - -1. **Missing Include Files**: - - ``` - Error: 'OrderBlock.mqh' file not found - Solution: Ensure all .mqh files are in MQL5/Include/ directory - ``` - -2. **Syntax Errors**: - - ``` - Error: ';' expected - Solution: Check for missing semicolons and brackets - ``` - -3. **Version Compatibility**: - ``` - Error: Unknown identifier - Solution: Update to MetaTrader 5 Build 3815+ - ``` - -#### Performance Issues - -**Symptoms:** - -- Slow chart updates -- High CPU usage -- Memory leaks - -**Solutions:** - -1. **Optimize Visualization**: - - ```mql5 - // Reduce chart objects - input bool ShowDetailedInfo = false; - - // Limit history analysis - input int MaxBarsToAnalyze = 1000; - ``` - -2. **Memory Management**: - ```mql5 - // Clean up unused objects - void OnDeinit(const int reason) - { - CleanupChartObjects(); - ReleaseMemory(); - } - ``` - -#### AI Integration Issues - -**Symptoms:** - -- Grok AI not responding -- Sentiment analysis errors -- API connection failures - -**Solutions:** - -1. **Check API Configuration**: - - ```mql5 - // Validate API key - if(StringLen(GrokAPIKey) < 10) - { - Print("Invalid API key"); - return false; - } - ``` - -2. **Network Connectivity**: - - - Verify internet connection - - Check firewall settings - - Test API endpoint manually - -3. **Rate Limiting**: - ```mql5 - // Implement rate limiting - static datetime lastAPICall = 0; - if(TimeCurrent() - lastAPICall < 60) // 1 minute cooldown - return false; - ``` - -### Debug Mode - -Enable detailed logging for troubleshooting: - -```mql5 -// Enable debug mode -input bool DebugMode = true; -input ENUM_LOG_LEVEL LogLevel = LOG_LEVEL_DEBUG; - -// Debug output example -if(DebugMode) -{ - Print("DEBUG: Order Block detected at ", orderBlockPrice); - Print("DEBUG: BOS confirmed with strength ", bosStrength); - Print("DEBUG: Risk calculated as ", riskAmount); -} -``` - -### Log Analysis - -#### Log File Locations - -- **Windows**: `%APPDATA%\MetaQuotes\Terminal\[ID]\MQL5\Logs\` -- **macOS**: `~/Library/Application Support/MetaQuotes/Terminal/[ID]/MQL5/Logs/` - -#### Log Interpretation - -``` -2024.01.15 10:30:15.123 SniperEA EURUSD,H1: Order Block detected at 1.0950 -2024.01.15 10:30:15.124 SniperEA EURUSD,H1: BOS confirmed - BULLISH -2024.01.15 10:30:15.125 SniperEA EURUSD,H1: Liquidity sweep detected -2024.01.15 10:30:15.126 SniperEA EURUSD,H1: FVG found at 1.0945-1.0955 -2024.01.15 10:30:15.127 SniperEA EURUSD,H1: Entry signal generated - BUY -2024.01.15 10:30:15.128 SniperEA EURUSD,H1: Position size: 0.10 lots -2024.01.15 10:30:15.129 SniperEA EURUSD,H1: Order placed successfully #123456 -``` - -## ๐ค Contributing - -We welcome contributions to improve the MT5 Sniper EA system. Please follow these guidelines: - -### Development Workflow - -1. **Fork the Repository** - - ```bash - git fork https://github.com/your-repo/mt5-sniper-ea.git - ``` - -2. **Create Feature Branch** - - ```bash - git checkout -b feature/new-strategy - ``` - -3. **Make Changes** - - - Follow coding standards - - Add comprehensive tests - - Update documentation - -4. **Submit Pull Request** - - Provide detailed description - - Include test results - - Reference related issues - -### Coding Standards - -#### MQL5 Style Guide - -```mql5 -// Class naming: PascalCase with 'C' prefix -class COrderBlock -{ -private: - // Private members: camelCase with 'm_' prefix - double m_minSize; - int m_maxAge; - -public: - // Public methods: PascalCase - bool DetectOrderBlock(); - void SetParameters(double minSize, int maxAge); -}; - -// Constants: UPPER_CASE -#define MAX_TRADES_PER_DAY 10 -const double DEFAULT_RISK_PERCENT = 1.0; - -// Enums: ENUM_ prefix, UPPER_CASE values -enum ENUM_SIGNAL_TYPE -{ - SIGNAL_NONE, - SIGNAL_BUY, - SIGNAL_SELL -}; -``` - -#### Documentation Standards - -```mql5 -//+------------------------------------------------------------------+ -//| Order Block Detection Class | -//| Detects institutional order blocks using price action analysis | -//+------------------------------------------------------------------+ -class COrderBlock -{ -public: - //+------------------------------------------------------------------+ - //| Detect order block formation | - //| Parameters: | - //| symbol - Trading symbol | - //| timeframe - Chart timeframe | - //| Returns: | - //| true - Order block detected | - //| false - No order block found | - //+------------------------------------------------------------------+ - bool DetectOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe); -}; -``` - -### Testing Requirements - -All contributions must include: - -1. **Unit Tests** - - ```mql5 - // Test order block detection - bool TestOrderBlockDetection() - { - COrderBlock ob; - // Test with known data - bool result = ob.DetectOrderBlock("EURUSD", PERIOD_H1); - return result == true; // Expected result - } - ``` - -2. **Integration Tests** - - ```mql5 - // Test full strategy workflow - bool TestStrategyWorkflow() - { - // Initialize components - // Execute strategy - // Verify results - return true; - } - ``` - -3. **Performance Tests** - - ```mql5 - // Measure execution time - uint startTime = GetTickCount(); - ExecuteStrategy(); - uint executionTime = GetTickCount() - startTime; - - // Verify performance requirements - return executionTime < 100; // Max 100ms - ``` - -## ๐ Support - -### Getting Help - -#### Documentation Resources - -- **User Manual**: [docs/User_Manual.md](docs/User_Manual.md) -- **API Documentation**: [docs/API_Documentation.md](docs/API_Documentation.md) -- **Deployment Guide**: [docs/Deployment_Guide.md](docs/Deployment_Guide.md) -- **Implementation Plan**: [docs/ImplementationPlan.md](docs/ImplementationPlan.md) - -#### Community Support - -- **Discord Server**: [Join our community](https://discord.gg/mt5-sniper) -- **Telegram Group**: [@MT5SniperEA](https://t.me/MT5SniperEA) -- **Forum**: [Official Forum](https://forum.mt5sniper.com) - -#### Professional Support - -- **Email**: support@mt5sniper.com -- **Priority Support**: Available for premium users -- **Custom Development**: Contact for custom modifications - -### Reporting Issues - -When reporting issues, please include: - -1. **System Information** - - - MetaTrader 5 build number - - Operating system version - - EA version and settings - -2. **Problem Description** - - - Detailed steps to reproduce - - Expected vs actual behavior - - Error messages or logs - -3. **Supporting Files** - - Log files - - Screenshots - - Configuration files - -#### Issue Template - -```markdown -**Environment:** - -- MT5 Build: -- OS: -- EA Version: - -**Problem:** -[Detailed description] - -**Steps to Reproduce:** - -1. -2. -3. - -**Expected Behavior:** -[What should happen] - -**Actual Behavior:** -[What actually happens] - -**Logs:** -[Paste relevant log entries] -``` - -### Feature Requests - -Submit feature requests through: - -- **GitHub Issues**: Use the feature request template -- **Community Forum**: Discuss with other users -- **Direct Contact**: For priority feature development - -## โ๏ธ License - -This software is provided under a **Proprietary License**. - -### License Terms - -- โ **Permitted**: Personal and commercial use -- โ **Permitted**: Modification for personal use -- โ **Prohibited**: Redistribution or resale -- โ **Prohibited**: Reverse engineering -- โ **Prohibited**: Creating derivative works for distribution - -### Disclaimer - -**Risk Warning**: Trading foreign exchange and CFDs involves significant risk and may not be suitable for all investors. Past performance is not indicative of future results. The EA is provided for educational and research purposes. Always test thoroughly on demo accounts before live trading. - -**No Guarantee**: While the EA implements advanced trading strategies, no trading system can guarantee profits. Market conditions, broker execution, and other factors can significantly impact performance. - -### Copyright - -Copyright ยฉ 2024 MT5 Sniper Strategy Team. All rights reserved. - ---- - -## ๐ Version History - -### v2.0.0 (Current) - January 2025 - -- โจ **New**: Advanced optimization systems (Walk-Forward, Adaptive Parameters) -- โจ **New**: Market regime detection and adaptation -- โจ **New**: Component communication framework -- โจ **New**: Memory optimization and performance improvements -- ๐ง **Enhanced**: AI integration with improved sentiment analysis -- ๐ง **Enhanced**: Risk management with correlation analysis -- ๐ **Fixed**: Memory leaks in visualization components -- ๐ **Fixed**: Session time calculation with DST support - -### v1.5.0 - December 2024 - -- โจ **New**: Grok AI integration for market analysis -- โจ **New**: Advanced news filtering system -- โจ **New**: Multi-timeframe analysis capabilities -- ๐ง **Enhanced**: Order block detection accuracy -- ๐ง **Enhanced**: Liquidity sweep identification -- ๐ **Fixed**: FVG detection false positives - -### v1.0.0 - July 2024 - -- ๐ **Initial Release**: Full feature set implementation -- โจ Market structure analysis (OB, BOS, LS, FVG) -- โจ Advanced risk management system -- โจ Session-based trading capabilities -- โจ Comprehensive backtesting framework -- โจ Real-time visualization system - ---- - -
6P!){U|JKpvawyvOl&_nESCxC($QVB2qE<2ob815}FngJ!Q)%*nkB#*FOP_i9r zWo*C`dc`qkx9PaXvJu=uNCeu^j5{UaBsTMtkxmcxy4l!L`Gd%Xn_#N6KcI zSGs^lO~^xLe?k%B)-1FLRDLr(807-uZxQbhE-S;;)gjD2c<7bq7Du7aq8bwaSD=qa zO%@bqaZc<9pXS^sPJLrC5a*1 droi@@F z#1U1Fy2H=#CloaFDVwud)7BQzDF}6S26Vo1D7Uj*ZF!RE-`4ET56 U?uf|_2rZ*0LHQy!eS-{ )zgB=dM)uv%IMreNt2z ze@7o6S`XFuJP=S40LednyWGI8 u+JIImFiy@CyDK~pPy?+N`dKJTts95Ax0jBdt6YP7WWh99@viUV zf9)}5MpV;z;}|zC_p3>3qab#a>&Seq*TVCA?)K5NXy+u?NEdAZ=9HaqthP|^4##h& zKMoUDEN+~U@5JL8+69irtE3qyN+LuDrJfRs)oTUkkVWXXQGeqpD3!to3Z2f;OU(Sf z38TvLjUtD#n%!K6sRrol63^P-nZR>zI{p2K0qzreRv0_;5J2(IaM8g|Sd_T#-=0~q zAz=_5D;2+BkUoG5?3VY_SQ?4X+Ht0OGOccK1{U*fhzP&AzC|&pgeB3JispWVtk=N; zltk8*)J2)j^U(?L(%x(f^sq3o`y!MpIe~sHx!`CEz@2K%xtK2DE}&3WlH0g8g02a| zsMqc94p^eeQh%VfaF_u8X`<^E4-CF$cIH1MsVECcMLVcYN8%sKRl!L`DzG}~p^e@f z4s}vKoX`S#UBEE9kAf|P9v;&bs1UYBjy4QvKZj_u6&Brpoks!t*dK@J)PR-Sm(JLE z(pq6 Dv!1dtBx9n>^v+c^o67cYW$XQU5%K^MHkIBzT*YuTKxS#)bTAygFDj+{<_bJ z>xamz9yfHi=^Hmz*`JhVoEall|Ayu3UzngQY&2!LRv?KMM9m!JA$P86-8*i^!MQiQ zQ9KCx5+Mlm^Xr0LHSp^yARwB-0R7tdc0{Mkv7vM~VVGDpTYf4TCt^Q#VoN|FJL)k- z?iQRta%;HfPEMNAfU)UyS9 QL-bxi_OFPEKHC0mOa9v<;__Q(CNxo9^f4Xf9}uga~5}NJCxO zSRXpUIPiV9#R+lHR>y3TyeISqHo+D0WF7*Eg3NBk0BHjlbPg`^H3Nw z^_g$8YiiQKooUpqK&v)YHW1YU;XOm|K2g=R&A$%2A~k0&f1;L3Cea`^Jcx->=HVU? zbNKSRTfBbXWAXbji*yEb4siypjiH8;H~njaC3vW%<~}9^d3VVzCwP abo2 B3D{V9}V4unnC2W9JFTOoc zeoOxO_PWQ0eAk)`&f^9|E3rhau^nL=iuu8~^Fcc=*wCJBfG8H!=qF9-^YVk=?#L!u zM<;ngB6xye>VNHvY0bJGg)w3c?lGmh;Cog-U8&I$#1`=q5eQGH2!1t+@K;O(dGzLg z^Q32zJwSx4_Vn5dNp?2WCpbL=B7V1dB)LQ%Iv$QZpK}pzr-bv2nzh|i2E{JRJjo7R z-H2CXar!Y!flZb6=6JH@p&F|Zbklj5dsBj0grSpC0hh`1qx0NdH7ftX<6Tz6u60U8 z1~c4r%Lu7-)Uk-@3lAqEvJYYZCY^?O>CEGWF*8QCgl|C8y5z8U=0uRpmnNIeEzGD1 zN@8>H%rT7TcU0lMfgvL})jlv|hrwZEtOSE7Y6y8!n0EPq#ki&}azg=$DQ_=(dve&m znFp8HAKDDR@-fxg(PG^;=OyVi*vbto&^IU-jE)cy4{*uvE*I}6DZg;JCT0-vLA@wS zOLkOvgXn)n5m9PRpRuPIIthN}%&oosk##@liYOPht z1|cWM0JtrO736j&F00ntkHh24=u+P4MZVLTt3%uvgAh&Fsa@3$zr&DEFsHkwvcjz( zvDF2C!pA$rgDcwXZ~>4({V)y=CK54!43?tAhF7KpBzp*Q)`=n#Tg~^*g#)=St8W<& ze4c^EX(9JYDlsVH=XsKx!l>^a_j0LW!dlao9 =z=essOl z@L! -kDm6`L&Hh5UIh2eufow_DZExQs@0)y5D&%S#D?uWe@;WUY4(rww;G z9XeG%SGwXvfylZcN7zbxw 8h= zV>*!ZK=D1_?>mR@r;@SEgQMV!j(_z8@W*kyff9a%7a{{5K^s;8KW~bzor{SnlyR zMO2?ITbc;IsSenbc}B?Sh8|@m)5DbskegD+{*e^S$BFDo{POj`R88?!5 zor-~npyy>?a%XyFh)A64a&!3Qjlr|1vY!5)B}2Y8Q-*dSj(MjC0-Pts7xn+K8lkIw zAXawkgs?8EeQ%T$WR5*vos<(gW9ptuSGc+mIvL;@UOZRH-PgE-S6l`)eS&kPYxNE8 zu2)5;rKIqK0_D4Um9zvWZTyt To$R6|f%ehm&1Y%7@}Q#CUp?ODC9 z{H>gaX P|_|<2jO~k2GZb8sIe0TDk=o{T<1>Tx_}!PI zgg -E6(KS=jOiQrm!?kwLuuOi zaxjacFISzL)~hYgpo?jpE0r|R7!_Ha1QAo7jSy}IAl2Xgx#=mCfk#h8Mg{4M7wfh& z)4T~gE{_$|4uU`~iWtpUs}s}j=YNKex+o_Yv2K#Ia(P4N+_A!9bf(%KYj1IKgw>`V z4v}M>0YQ>i|LX8@6FRrBu&g%!!aBFc%O)!CG|KDrmFHG(m@qiRF%H}w&{UA^=Og+U z+WyLCF~&3i8Nrt QE?=(zO zu^M1f<+M1#<^x0%K5uDr?vWN?hb{Kw_EMGdgH&!he~<;zeE4 z;E}1wv^M1t((`D8N1+o;#aYkEv`LL+5~oHVfkY6$E;{}`%aToq20_by zz%bc5RP{A^UR-%$`i%f&<4Gm)D=?(HAih>h2R`MkgbMVot)uRc<9 yF<%~ac_Y^m+9LZ+lLS^{zAdnEg; `}rRM;GVLi8BX>{LI zhqp>_IOV|>^Ury3(Q$Z)H^*33I*}j8c3Gx)Z uK3EiAGkEZ9wDrrHimskB|7` zQZongf>gmQ`ADWTEZdr}l^2 9IObMox?Ua+_p@#4dzJqBP;C-4u}F$iqzn;FT1 zEKJ; 7g$I6k08ADl`|Gys{6M#yE)JXd&-T?=_}G7yoP zoc(#b!P5PttW&5Z(DHHcr|hSvGpf9TZi?(EQC)+iz4NO>%B`k1XHzHSXGuT`pAI{a z&_uy86exi=>)qZNZc}FacvDmAij^m2IDf>)j9<&eK*7Be2@oB+r`MD&dcBp9cD#sC zO_{WIbbHwKh+&(sewH=1l2<;+A!#q5BJc-oL7QgWFc2^ys!xIQ;_ hHq1@8cW0`U}_>|vI?2gawVLh0sA z#sgNjG#cZ|sD?70dcDMr7Hb#snuAv`tBh_L_WfLw1=8=imsZ!8*m#lyP&d*r$2XA2 za`Qbjv88hJ;FFDk3@Jy{?;iN{U7To>h{Duh>xRWdt_g@Rn9-vb@urPXB!M8@+N$hB zqL&FT_r$e^Gs)pxPqb<3gojor3q4$jPt&HhjrTf!4 uMOkNR6;H1G=M39+Ur7 z6PTg;USCMzE>D|rz3hAc7o*ToeB ;t&y5S|> NK7*um|lRMpUPPE(Aw-UjCzI-= jQ2ZW_!1Bh20@y6o0Qc@7u>@=+PMJxeQR%LZ{;<7 z$~pbJm%X=!xeJLhScm2y?{KKS)do0Q;Q@to-;lbxasB~GPL>RCEU?Phg9{y7EX 0xMw+fmC-Nf1AMDM=QbP};OD8_Q%2jh=6>-5 G_lfj{~dX-(?Z_ z)9tT*LC<;^wKx+ LYV73HEWIycfoV z-rowYbRb@Klmw%La@VHmR~n4*p5$uwi@G5;pEdnHs@Pxv8X!(kYTTP^$CMC?B1(7! zF_F~@woO>8dZtTDV)>ca2RET6J !RP1o|z8tt_&z-}_oH&w;*8?3OS7b$m)y}irv)YhHH-q-6vfOvI z4+@HX@mQ|6^vZ(jeIO4Fo#-x;kjPDnn)(FXJjb<#7=nNo{jW4Sh;wc=vpA$O@{Ud2 z40@6;hFp`G*^btcnP=}AgRMp^s(hP`?t*UMj6!4gc#~Wqgae*j$uZje^N|<}-#xA7 zT8EKgUEd2USVNne<|^t->4bH^R{ev(%O&G)-;YZ4TeC1jHR7(=9gXO_M*!yd!;J zc2dYfv&6Jc1EAJj8KvohNpChof4ex|?#cJ9?Z4f5G>?XIFPT1(WGh^wHGx}kyL~IY z7_(v7bu79T?aGBH*X &S&`OOWSPC?89-$|ys_z&62PTvvVbw2sD63TC&)$v0@?6bzf z+#zHZGE-dM2oM8tFaJ_KMN)}vLbLFlj(C(jhdONxm9TWat7g1I|3%0ms&^YC>6Qcu zM2}-d1?v8&lJUoV1RDNguzuttY2g%Dcwi>wXe^pWJ^r^H1)eLM*ob1f9}6cMNDRQA zI7|PGv5zjD#%!0at$@Fr_2MqLBm#}b0z@nt_RSiG%2qI+w83?YpZ5o=>NNZ
XK&>*IotK*3}T9cMd`&)y+j9Ogk X8>d+C{0No zii-PO?@yhBkyjHT{+Wk)4m6X8=6P2tG9;w}aH(x7iX4x=yPqoa(+Xs;+dG(u*aPV; z^|9z)cx+cfcRGhR7nk_cE%O6K2GDN}wes-f5+dXPeCsGdV*L{obdx_T7%5UfG}v`G z0lRka58IBZb|YP+8EGV>>U=sDtU>4onYGX#KC-iom3D5PKClR^$&7@9#;CrYdj>@) zZ 0mz8?y@JKU^U!q*{tb=UW1M}k%(07 z^FlB+xD1(#Z#H4kU){%U_;1)}5;ZAqk$vD-CR;Ek-!#$g>USk^Hrv&&`9=i9$q9aA zhnwl#jA {@l(M|#bn=M?(Q?qv-ouB^qY!3Gj# z?=GKVwS;2c18u-=ZNGD;Jg^|yUSH{-R*^MrJOv$zeMP5>;o ~Rx;7=w zohISh$F(0aDhazn3 wE}Q8wdiST^3PSbhO+>}T_*dVg z0NNyoSvqPG0@nxFBr%sn8kMzmw{Wq?Na{UZV5{6E&vX7^cS+yn4&7DimY}b1!AX}~ zR#JGx#KF=Tt|sYBrhSmwas@XIl=aC_`3wM>_@(&_BhjXd)JmFFVH#>ajr#pfC_*P= z4tcTsb&>|iUY}j4Q#@Dn8C=iqAIitq++t_Z9BWgb2 zM$+QLjteq{f0St|ql$}Pu#vFRa-QN3HKI<&zV{IEY*v8fbnVXi DaL z_loykfAhW4h|ESGZMZI{5NPUATuU*Fjq+omAVF^kz^fk}F!bTB77>}%KV6_}GUj7r z{t8$RUh*5>M*>nk9})RZ)y>|F_ZOwI6!%bxF)iKI1e!cg48A3EPC{ zzbHvZl S+%T3e5OMYI(T;227~TIS#Oqym?DC2fZtC8DClAiw4& zm_~!3ekC<`mO3?u`~5O0OgVlZC9qHDgtWVw5ls(pvUZ*S*7^Ff*+9x}Et7cRK{jf@ z#9(lb70C&qSimzmG(Dr4hci*!0b0Y>t+P9tO0yUL{r!Y_AOLSaa~jd&=SSDL@S*Cu z*cq2TvLOwqQsS^oi)vL~pX8pu`$-$~V4OROjK6AsIM7<=Ba6BfKY*g6WQAi&De%mN z&}nKF8~mI^8r8B&T;}H7(2^C0JIj4s0j @=LZ4FTdN$n9EAc2}aZb7p(UAEuG+N|hF zyJ*K#t^ueHrU71kERa(-_`HW?$)qz-GOcBF1vC YXJ+0Sb(j1&Az0Pfb1V(ll?tx zvAO0;s{XH69`avx<;J9iQ$M*5C0DM;?rNvNY eUx(JyE+h5r u8_rDqAMzDIepONs+%mkq zh&@2kD-;2a&H*$TR>=$MJ_Ce;BaBm^hSk4L%jDUv;5Tn??!}U4h^PT9yL&}KX>q$R zaO6-*5GUNyQ&HEnN1@qWlstS`ub>%X!f5_OMrLDcX%4b>B!4oGjpFrJ3XI9xS-pd^ ztMF;{x?{yMRKQt0KJtP=CB{Y3n#vCv)>x1IDb6y~XC)jDVilO*xS=FQ$({W>#TW2L z3Iyu)W|Azba;IxP=JZeLjw}LvUhVzhe}~&zi D{Vbc*-?_4xAZs{DIF<{; z`n?v)lCr>fHBTU%Ky+w1O(-WQkJL`s760kC@akUbaPT^V-gXESAdfU?<2@&<_2 FAzN?*z6XwEM!a-M=db&wi;VFDh^D{jD8UW26^ym>C=IZ>m@q;^*Ov<-__^qMD znw34YarQdum=p(Lx+%T!BKF;#jHI!{{ZsAd1~K?CFdZf4{4DHBi=1P{>j$+Abr8{< zAd=&V#Dd6B%dX+jr0Ap&?0u*m{FVC>#zCqTVVEn^&5b0vKn8smHf-H{hxxIAZwQzD zAu?K50&21I?tU?+=uQpos~+NpH{Lu5U)rHzf?arue1}?r`H-)>I7^a0e5(D|Pm=qr znSy-C;m0$B8_FS8JB(qM1pDHWjx=ukw&od3Ydv>U1(USn=;2!&vWViut#r)f*RAj# z{|X9nz!Bs!DtSKKEFIC7Bnzc +3&@{k7m)sncn_qBLXVzL{HB(`-ac*Y1kg?7dlZr(qQI-C6D3|?crEr5-x*-DoNywD%5sob z$}K~ZLgS5V^nE^cHN9Z(?ag!^e*P8bq86+Z>Y&g2X2T6?Y>mtu<9O%;VqO89+kY20 zqDHU `y1Zv?6&n8JGx~_y?=E}@ zyP$BzT2gCx8wNGAj%0mJ%ZyY8w(H7!4xwUlJuEAu`%=ctEj7NF&JcVVhGVS+S$JGM zYe=JDuQBAOmUV*O3BS&J7{s#KNz{U52e1uLs|kd~oLeda+3{Ctl%pBd + cxcEJ`htoXe0OvYj|-ysQz!hcVLliqVjK;QjY$aVe%KxFD+?-nPIAoz|Zl5Xth7 z{WoYLJ_R90_pi1q#SoV``r5Bao!N&pMA>1ki-{}ASy3dX70tZ=2_*=9&s$7b6o~k& zVq(%h(3u!x+-rP88Xz}~$5)u$`Xl-x(GyHb;6rJCEr@R<={}zEm1WIhfH%Oc+ldsi z)V;%Pb>x^hrr)mM+teYin=!BFZkivDl;~ zGn{fqxr+nHxczFz+4Uw$daK!f3x6mYtql>(RPj`=U@~)Xp%j=s;}>QpXO-hC>v_ex zKW`ha5&Su04vZ|EvseU&1Jg`%Qb$zG7I>wm&fFVgxOEgRm^T;O!cDriRL{%857km2 zgU2DuqC?57QvSrSO*Y!hMENm29^{gXD?8V1q)%#em93XRIEqGs8&6!mJ50B>dKR0l zX}`#D6o%D5|AM&vSztVmWI_He$z!UtnF{CTEld4(PllxvUI)JBST$94xKPdQa_XtM z^8UqD0hQyi)-f8i?Xdt$oDZQHV|nqeMqYPbW9knVD-I0@H&R^S{DW{nyl}gWQ>G^U z(>1lBj_`6kWGK!RoQZ}lJqc=*Gj89wLQjxU$8yFh0LFk52oOMBsmmX5hSNX;8VtX- z37_l5f2p=|bqF;mA&-M^*j@nL>CaIN%!+0XTjx;FG)mS~cG=@1z5N}~x`*;A4IJB+ zG+$@rlfV6NWD>DWy1kSOlsc!jH$w58ueOpfR_XL@v0APy?)innAUWpw>YqKy*W*|R z@CKRb *ESDhcrW z+vmNwFrs-NN|~1b2rK%(9vFr~#@>0h?ujL!FLV72(pFV*LeWi_h(LVWif7yulZFHp zW1&&!+ xLNbkfJ3LI^Y16;;@^Vu{^wEw=omlv-G5S8^yl~n#dl0bNhhdMv3+;M7B$2$ zx+m>f)$?waValPReueyd3O3QUucJ@5B`9W>^IR(L55+8J)ejDA=aLq3CK1+3Aqia+ z)K>FRNz|bMkwN~+@R;#p4PTu~{f xA;0|0+*>nOkh%*hj-)x-uapsUE6ZG6*RZM=O@HZrCXfn$V7aPSkcDu`QKD z+zwvP%H59~mE~HXW(|HfcW!8mEFuaxNKdZ6+Jm4s(=;KgRDjC2>^TrQU!DA04E42! zH-nUT3QoE5u5apycbBYg=I>+J`(m|h7L%ECxz1Q;vnK+152f~kNi0@fi}LTwoa*@B z={x-s9PAE@?i-D_{ykD$ScH%al4SmLE>cEU7q sSCO4Ei~DizW86t61EGw zu6A`ktLDuW@`PQu@HwiZbD}%p(7pe=6PNyJBiC*8E4;a>si1YITI~adiHRN=mB_(_ z2}QMJcCt)8rX-q8R8xaS1x8;4o||v+F1OK55tP?Dq*J&Vkejct#`Exp7h4l}arh07 zc;`3^HjqCK3Y_?m^Eg&FmgW-Kr)~;?l=m~NGhfEI0%z A zx3B&mrJeJ4pi$7@W81bj_Qtkt+qP{x+1R#iV`JO4lWy9+?GJ6=)1LkX&$;)`GjqS6 znYqK?)svjD#`zK;5Ax)(em(R4S71`vv>ih(-ZRKxw!AxSo m7iSmn?GAEV)We*E-be(RVrTkbM>es zN6mUag$0BwzcJBpksHZ3dp8T|AZ#5cUfb)X=ZnM=W(vwlM3KaVjWjRZMV#J<+F)|e zeQjwJs+4onExQE5?)QSuF!D&8ggj#Clx7q>$02=@!kIVJC%xh0aD~(vMWe?7CLE?# zz`{_%kwV6DVV+U9;fz@k6AQAW0^&}s!-0RV_e6QN{`Bo7caEFigUTRzWKh{|`+@3; zudDpaTME`Q>B OU=kyprKm%DL1IivF_OCA>-U-^QqdBJ%Ds)4) z-_~HYgzrptLi@2w^0 hogDVOoJI94FD~StP0J32c28bV AYg;) z#>i{J4 axroM=uJi(BS$7zRcXC2`53GF$LRGWIb@2O+@nIF?~EL`lZ)ytyno|MR?#V z)O5wlT4xX8bH+~4TA?X=0XS5;X(#03Cqy`0Qhv4`e-;uC!g`XtX1K^VQM7Z_CPz(V za1h9%d1CkpzRCoK)uIrNK-7PSJu4qU{+fOSP45ZVu!A}3t ic?f{H9ZJ$q`55CDML^4%q zQ|%2=&`EI#sS{^+5xZNS{;M(=uM|^q53Hw%A=!*Ibk7HBvmyXA_?bL=Pxi-)rln5I zn?l+?f0M%$F89uZb #U4mm`k_|{ z!Dx$KgTb$e$ 0Bi*eR~L^Hm$I! z=hV5mBQP`4xPiCcwSpHBr%aqh*aJi#a7S{6pvADXTRO^)hWbtv0rQ;AqQitK2lhOO zRlt2d_3~T)b-i8#g2qQk1Yn~FRL#aGb$7!;63iKQ4hCSX0o*H61w)cO@$Dhg#vuz$ z*;vf>eQwoXugz>JH 0$DYYDKunm{67u`otewHwdozDy`(!4cHVoWD zS@~;;iTG|&?Iy6iy`bS6xuMf!PO?~==s|0Onwu&%rZ?xcJ*adl9$@1B;ZC%31UZ+d z{dL+;UD&d$B7lw%ImPyuP3(O!O$QWu9LXU1Ok-}geQ5r5eg8uoZyonIBVkb0)AH-~ z)eJ=`@AU8oC-xrCw{GkCF?$l6^cskFW9!Dpu7Nz(xCfEvB9+xTDB`izhEdk8XVrea zZQwSFF3)M@@FoPhCequwD*2O|DK0O?dnNAnJ!*(5YkneoK+z=R_D}CkaL&xV&4ro; z)Io@LEV8$N?dA2VzzqG7cPS2ouQf^#%&~aCE^pAdS`X>HBA6qtd}+52fJ_XjSzc#! z1431t77qTe{^p==xRGi?cxUKhIp5*WB2s>UNnB;I!cF 2y4eHmmth?qz ;=(=KCf%j>K*cBb5hC=E9 RHomfL`~;1Zq(+VR8n}OsJA5A`L7Glkc6~Cq_RqjsB6p&wtQvs z@kI!IITjLdgpI_?P8Y8Ai#zN4;D0Uf=Fi`$Qwim1ylW~Aa2F-dsWKeWOagffL&J+w zzd;xWHE^LWph&zRV9HSb327dA8`3aAYSZeIjDw2sg6r1jdV@{b#kR>L*Xs@FA&XQ_ zX7~8ZRsoKNEB*IZxW jci<2BsZ6%AOx8mtl^dEV(rSuQ4Mv(zAk;3{Sl+Trmb@;-OUmFOy`p zv7pFG&7!RinHW@yH7uoi9PB%SUBF=LwuCYG&ZXQZSgFr(o-TG@oeQA1r8VW^mIj_2 zIf;QX0Jb4rx-B`ps3j_l|GR!pd9ij0K1Aw7mt9OPpP*x<7{1y1Zwb%? SzSMro{~PusML}L5Teg$4(d03~5SM{j_r`k? <$W~2HmgC$*9G?Ov zPbtyHs7jbcSL&Bw^YOe9x!<@8)}v2EA@c1t>dXjZG#-=z-hc?s;=DL9uDnm>&4`nV zU!oSjVKUlJqfQMk^y!Yl?}%$p8f4m855`Zxxu@PC8xlFTqJ}Z%dC~!dj?`n_e%0Vd zrJ)OWa-#o9j)~5Wc(9m>)+3)2XO3#tT3;zrlbEIxB$g#hAuu866zD2;TohbK;Dv_V zmouLVOEHbc?uELPOzotqoU~sfuEb&hGGUTpE~e(UY _;Zi#=JKbW~Y>-d+K@dXE8C#sa62PQy**o e~=*qQqa(iLW= zD#y(n)cjy4N@2?>RTl4?3ozsr$)*j@LbbJ*PKxm86>1Yk?Cmek7e@vU|4D@y(TE$q ze5*r@hul`{lT)**qar`T%P5KxIZ}w6xY7v^xk-}b-if#~3UUl6ZJcWM!}cqO%+k`k zFx-_i)|&7-G_N%4_wqhd0|VFJSO)2%at-%!*n2qXru`=Xy>T9E9Yl8!u9Kur!BuMV zuNwOhy5_K0*xn_!eK4O*q32-qyIwu-n7|t2dBOp$v@ucObFguRUj-vc^ph8*f|a(c zvFZgrN%}3*u;vfMP6j~@c7eB6!9sU#;#@&Y!8pOVa^6eZ?Wmt9mA|bPKqB!{3Z*qXW+rlp~`HYhRTXOeUir6>b8%|i7AAHY)>L9{{cKW!8k8V&6|Sql?lt#wxF z!$n6;M%wr=-`PrQv?^=n^2Y|}NH%n&u#q2r$vP`#3Vy`&-p_F5`Km@m4Uoo# kJ6;j+3r&Y=a@1>Ef-hj_fjBTAMqPITs4&e;1 zE|T)y_2 ||z zh12Z_5|4pfNTyKT+gX3>buqy0pFfc2!Cds# %2?&>njoavVXm6h &EI(|C`XTVv<5^amB2%|EtoVdOb>mz0m%zX>3hgRs|zU6_6s!~Nprb; zzz4BAS`;wIV0DwUUmmZiDKuK9rl3Oz#U6P#JJZjw= )dHQzaCp$Q3nvX9W_X+SqZ04&0+_K}E0j-s zO$gSS?^(Hm2y4AjP>wFD;DE3S9A_Q)KHs(%z7&5gvZ4a>hyqJ3`8UZ7Ks1?+j$N{( zRh#I4`xv8b#s0?q)Z-)>3^`{m$eO8XWp&O4U`sJ-ItzMIf^krCr}fPQQ3lR)qMPbO z2`us<6So83SrX~2QM(pLesMOhOUMz3iG==4UgVrh zpgNJ>b=SufFJ2ShY*{%{-z$CA zp(&1BU$uwDi&UrEY!VBA-OBi7d-yO1Qe kdK=Po! f^{%J^F~hj2CK?$AYEX!-Yeor=vF5ox!B{>JbsQNC4BweI%6pToWZ3vD zMw1X?X(<$n>NnD_#vqtd7bbdCDCRJ|e+^aaj$Uc!&xfroMXR~Wd;4dMO%sAV))&F7 z>rp8l;T2-^oj&y+36XJjcXpIYC0oD2J!`R|(&QFf+HCqZ8v10;i}j;?Vm|R-O2+TP z4b(7&7tzrD_etidF;Bs*+?N4hW4b>Sebo1O!m9*171#=JLLBZGKU}0#VN?0sdjY zXW~lP2B>hb`=wiN{CB?e28(`SCK<(#p-{O!TdiZIj~CEq#M=e()a|K$li9M?Cx7{O zB~(8J6x{&{FBSWc0n1Hhv}wEo)!)ZPDN^i6z=uCYpmL2SQe~T=6x@a%Es3-9TVw+Y z_|47Xv2t}qT_R`$>P4gjW!a<4r3ww(Q~DNS@!}60>|`1PF2Eq7u}Bnu(uRe%Vwn$Z z6%Ry(Y1_S$x16jU6aoNd-#zQe9x$YFVlE1yE)a*oiD0oyg)!#8ZV&=ACs@_5LwJ~n zxdbm#p7e)6&WKbT!LAmMa*^defh{3!yScaw%(T0guc6j-0CKf|&Og-_(?eY}AX2op zmBih}D6P)w^$k#ba!*Y$05C)HCa 2*rh2SQJR}uSC2;_%dtaLjRbIl;VF2&(#W*0ss5?xPC xD{+=|HFxuw}2#Uz#DYQ2?$0m%w3(Qm{ ze@=&+D2ckplp!ksNe#{`N6}8?Nr+R4kOt`O%Q}!TT3-SU!kVxntcWED^tX6wox`dv ztB&dBf?;d9GN4xqR~x6;5uuO<64Vh_J@8k8=b#R{N4-jE+jWx{ZJ{9;o2B>Mj(1&v zs2AQC@Mc4n`@5oegSL;2-931#K+fA9k;nqvn-}6)RNzS(Vy)>{ZPCDeqNO3`^-;sc zEffV$nw9#W*`Jx-Y?lTS^ra^~!Y@{#d|p>ZS^{W~$7xJ1s5}k1B5XZKrv=^uZ^O_I ziVm>L7%>w4!t%RVyz_tnB@JvZyD-FEo}Fq-Gid> Nxj`*xvgLw(sm;ATM~ z$wV~jg6t`&!xYX77mafnDVDy#tV2&HclB`aWuSmIns mg| zjh-@o%`P?_8Zzl@p@yq^*81Mcf?LZvQCbAs`9PO?vg#{VU~!>#?Lg$X*=#9^-aM-^ zTJEu}z^qzXD_M}(R{DotN13sdl|jID;=0ATh)mhyuGOdR!GLa-K+JUYjE*6VI!;fv zU)c~=*EE0mr@}$9x5xn&tL_e|w6)|?UxCXVZ66FiI0#r@lBAhL^uz$@pn2&8vb!hw zMq{-^2-_L7Q%aIdUZYosdS4o >d zc&>QxeRlq{zqh|$o1Nu{SLOHK6j>Qzom|&m-a(VH#v99E!FV%Hq&0~op}{t9or`#l zRY>r?ecMpiWS1S^%o>xzpNRL46yio)67{n3FhZ=yD(E*)nEEgIX BnY(={N`T5)HW)$d_GH%Mta@*e)GBE!32f>Pu>fo3UqFgf)Tjk;)uCb4pH#aa9 zb$yI%wk;M%mknYpw4-?20Bl~q+dL=ij+gSa&-vf1AByM16X~_VT0CTI2E9=bp`^s! zmoG25iWSTd)NqsZv2a=huZLiJN?yagpXZd78HjbrO-~=&Fu6if1S?$&HbJm7%5kYS z9du+Aac4Er@!8ob6 $DvYl Oj$1<_VL3rLF`$+K-Yx zZk_jbnb{1fh>LhL)v@;em4C~cV{WdX%wnQvS&u%N?z6omYa0@Ldy>XL?)s(ivsiv; z;ElFge|lyKFRz7`Qf>KP5wI)Zq08!WuM`})9oRR>0ZM{FG^|i-Ncna(fe&kzPk~x` zV@}nZMSnnCz_E%j-VvnXTYIhI*s@; S25PvaI7bQ9X+J|6i#b+i8BeBYxpMN@MZXjnEX0&lk}M3v6Tq;_w23DYB- z>gCb-#Jjt=)5EVE>-gbhaAVv+;|N~A%#fs?{dYCrS8$U4iT@^AUrwe7=&t=_E8W<# zuTjKMgFYImYdaJ}N`$Sg5pnYM#oeOlPG|>t*@15P&ol08?io~*LvXK!dOD=)ji&wq z2h_1ewicIG7TC4=6E)PadlMu_QHzp71OPf4Q7xiAndYC24hI5ndIF|6+HQvSUzs+P zN+Q{?bP%=k?+KRrbD|vkXA;S;74cU@le$lvsq!=yRSoZH -?(tY>qNgoR z_9~Lj mxu7`xW&INv-6y#Pb~zsn zCiwJW1o$xq51)`m6{+iL{`)0E4AeoDRCb|~G{jgjCRH(uY))8^!T2zZl#Ist1uodH zhIYgacUp~Wik=p|z}RO*TSb%8^0e*1C=-4YkQLN*G5UV-t>y;$2_&8tu=9A8A-DhE zV<0i44P|ymEqcsJiO*%RqaVe;!)z({TOZkZLWt{q(NrjCujPca4x5k~19-Uz(fE?B z(cM&q{lbM~Fb3FIh*g&(&@u2$&j3nv?conoG@vi%pltxpfQK4B>MI5cdbo{ZeVa8X zXbe gCJ376cHbgd)E6J;RZ31aIa7?r(IF83A3I*qL+(LHI9?Fwaf-E=YMYcA+m zys^q$U}3ipi^1QKv#iD}e?Q%?Og&4HOw+-Au_T^_}9)b~^ 4QPVR&6Pj$D>s657H7N?&`dNhuv5KR~$3ODOWr zr79q|=^+ilMCyG3cN|;4W-q~4ONQA$M(MN?VK)U>!L!)uLJuPzm9XXtHg93UB_LDN zzlME|(SBU`w`J&>9%7x#$>9ED0wr5KGd;B9M}Ibb2BsrGbF?V{m#XiNsCBgE)80TT zoa`PAKRF56UXx{*MfJr2>0$Wj1#@_&_{ZY3S*^VxlO-?J&?z97UmXyp9>=wyBe{su zywY^bX9X(31@~LpkDD$PNJzN*GA5Sva6%(CT$Bx#-D2ZBWPfi>f+I|#=16pRmT&1G zX6F=N*@u3;Qk3xBrijipiIkPZ;cPwgEzK-69vCocZ=!^$c+~jZ$bebNI8vAg*?L2j zc(CXx9IIg7?SXou1h{1)TBDhddGCJg$p>p)FLfbJuAxK1(@wB~yuKEegPUnNt(Ak9 zalJmR-(cDP_xm5d+1(#R_)ms9quaYz{=qeQDbKe=q0 s-_IsV8m1F%H+0fvCzcy$(;PS_>9~d(5f8h-*ED`Y{{rykE)E;gRtI(3 zlR-csi3GatNFt(#w+ S9r!m@60?1@o1%#5JEteJV3`G86Xqt?F+Lcp+U0wZi~q1WuaZ2ej+d_C0q6 zu~u5b@i_*sodmZ<$Oe(kK_7M$`J{|JU$}J#hf3M&nhTKc8L_j?jKlGqF)ikL T7sb&z?Re|p zM`+-gPpO3Ic_z#_p9qmH*FF}#b4PXAX|qwlYg^C_hTU{JuG6Up%8l^e*gX73jUW2d z)Troh6(9*uTGii+4dEXGDh)Y<4oi|d2K~l7NYhg{5#xoHmbb{tdRQ6E{;$vfC;sgG z6|8ShRB*ltMTJI-(uEK&%Ln^DG)Q3@X2z~DC(!l_DG}<500b;312&@IYmYb%S4Ba5 zI!Cn-xR9pCQr5vTyi9|EdZDh=y|{ef>RFT$b$N{8JxIm}PI`XavY-}@A;BdAVyyM6 z_+_wEIQluslcm(2)#uXkw`)S}aWrs$QK+FK$>UEVl@Q0PEp%faA*3}vsF)X78^m>) zA-i*HH&_AVcR+tqhGu8&s63$$!Bs_RbU-EGYT&^@-Nj?wTM%7AUhIJ2Q((})-bpL$ z^TSMcs$$hxN9);wC~G=W8xmZ32DBtV>M;P2ze~{F^v5|aYLMO8ZR0Co1<2E@6B2Dr zF=)RLOHa0V8W+5S4(Jkve}#3LyvDgQoR5HrN+&b+#gLLde|F;||I^>Qf%0(BH;3>> zTt}j0D6(oO+Q myYL%+LAbFQC%@=npw)7INP!gwy!O4M!<~dJ62piC->jQ4TY}Rea0cZj5k}F-aTl z5Q|n;JVKx*8V>Ih6wIH(0EfvvqWGs066`pnu`3`o5@JGb$zqL7qX=&ZC;MLSjmF-z zbN`;wem}rm&)8R2U`8P=z!j_9=Y$mV{{OMRAO3;T9K`RXk104&0B1`GPeK#B0^O{p zLaE(p8y5b`U(u%`KOoR<=|i{K$CLNA|Koq&?q60kR!ZkpV<48^=)UZxLoTN8OHV 4xGxUuw?}+qx_f2D9OMRI8l_nE%3tXs>RJq}ZyUrcTe{%lkxq z9uYiTPixP4A;P%1l!1NMFtlr x%l)Lh;b@XLU{Rj>KpR6SUE5B^M_ezeKGJ_B_{D`UEkmK_04r z;5u?gc&@V)4NA&LW-N6!Fi=!iX&KyrTGRTfwuHR$D^IdQbM-VYtYYv0sUaak5E^s^ z0lk2ZDa1w-9@K_wFuM9{I6PSO`yEC5{^E8H&{x*ujiJc#$NyLohg J>scc9>>>fiYU(}WOK31!;rN-RNHoKIQwaKw< zr#siT^?(;Ld?h8BSkc4;WDwuW-Sv8V=qmZlS133C@V~%G6+mZ4{bbZpWfkor3ZACg zZJp;* m&AeM|*rnmq|)ZpkhUNiac{3r&R6 z2Xy@Ai#_g!QF}a}T#&(Xi)_kHa=1KAwdCebkUaX&|JUzJe}!4qAUW$~Y6D9}2EDJ_ zT-@TE61)s&&rfjAgjPh~7Kat=T;aAK d{w>D+Ow!t06-OhHu0(FDObJbR7&B_7S%9g=Z z_Je#QCqQ?yonU|Oi!fk~gioUncD1IJkq1_b`uwrnMH^x()qCIMePEH}6kl=_0i?!r zIRxJctC+VC{9T5R)X`65Jr`&`{8q>|-DFD%Di9nwZ}&ZEXvo6(qyO&u@%v(>#A%78 z^F2s{jTE9&6f#L?g#_r04pK=-shyr-8*8?-+bt3L#A$kn2q2SH?b>9;q-nI*E0$EE z0cY+hp3O&m4@WNY_C8OZ0txP`S0{)Q8lzd%{Lp9FRT*9IWvWx&*86xI6p!Ga7GmI~ ze)!j7zOu5IjMY?mza{5h5cgZU4;tT5x7k=}%G_GYwmC{zh*K%Wx|z9ME2sSEz@0r* zhaO_4FM^}J#!+b>RBn2 Jd!cVC9H)KIt&p-g zGk&%bT)~UXcgkon+QFAu!|Y%Ulz<*Skv(61+Tvnl%rdAiv}&JqZ}7ps;`D+4(Enpd zS)v_nCF`1SnOOzrHbL)Cq=np9i9*x$g2jc=nClPR^OJLRySm_$4XA6Sg{vdKcha_c z3)EhaQ;YaE<0J`?pgnHJK**{3+G|ZW>ogGUXP6ErD73)R>CxvRO$(1NS^nzN4h^OL z(dlWJP$KaszOAl^3$6j#2ss{Z{Lz1*jKzfs!y>OCp)pJ9wnw`Nu@tL5bZviV<73@G zhPNrYTRO8QC9s5tK8SC<>#y|2{PP088XBhCMofU0;&1ji%Es~~ZBl5!KCJc@eS#0^ zlSHV)yG-BW-54)MLB(WJnEGSM$X>pB2vDe-g7< @^BtTWzc; zwA!rF(kGCcC#lKlYs@e&v$gmN!0a`GMNzave4myKck4E^62TW=X+LeJg3qV!N=|^U zKElaf8@J~u)7ld`bwY5TF0}p***Gr}1hL|{SE-))n-OjN5B-65a#e!>f>Y%D*Gb^} zV|NKanI`D4GNXL}LOxyS`LXuE2t73|=a`JEp?X{AsYL2C)bc}DTjXz2)l*b(ghd9% z33B2%4-CCAb1_IK5eVfLJ_jjNuQvTT8id}=nAZEQ#%+F@>1K(X>$1(?%Xxlhi_g!4 zx5igiSY)Ce8%}?J5~`T%knA7+XG%f{_D<@d@S)8`Wo$#`_*sgB1;N#6`|$}PHFKz? z8UmUv`%pxOTnG+&<-w(#%}m3HB+whz!Rc_+xB|N&Q)x6_ITtdrH??jCx{}yk3*nmX zfW^%^H9e>~*PuloSBX#(9Xo(q%Z$AktDCibsQ(qMaeqHJ9(l3z;xAt 7CN z7&L3c7!w`zG7I&}n6?z;cpUDE7@0T%K(P~|J2@AEsn(6f*SO8(n_R!e%JT2$?XR~W zFG)Tp3Re1=53)?ffm$Mq<8v^Z!-YuDY&N%B^#Usduo3;A&k{4 $(!*7L)4OiP|eIsT5{`J2aekiq8@i+Rxu)@@s<; zZINA-uhim*?r>|XdtM$#bmYr{&nVq&jr~T2T^mW!M&0xw-UwfUD+!qNt9a}-OkZ_` zn|^hNFY-Ds^Z=t@`C>j=lvuBz>^27X%->&89m~@;6k&&ZyUM~;ir=y~O`xDLfAi>F zXxja7zhzSDJ1RP9L)ZD@7%iE0JBX#HfH^hU2Ws+=cCf2I|F0Z@ NXY119(97(X#=|m}Y4*oG75j?EjH}!KzDBfH@&G zvuRe`s}T{wz}x0ymRc+VDs-TuMg*e|)&@5dWwCQ-@quU{Cq))b60NFUA?RBs#33(+ z?KVUyj==UIh0D Xba|r5vrQaLg*3$vHjUyB_!E{w+?y!lZaKiHM6>up z&=ME&WTguMZ~J#NI4OVWHA;phT2v}Lw|LreEQN;V8+4c)o2fJEG*TT6`Q;MbO6W?K z7DrVN$M`w}4(5lyU0px%^exMXyFJJ8AEgq4ro2AySka2ck>QhoFxLlE(HN~1kAF|| zWvg^%54g20^h{|yj|bQ0znMoG!O*E*jdalzr0|Lq-ztM&VoJvlE_R|!4*^lq3GLc( zUtabM{j7hpDh2 RPgzh7r#DJN_@m?csY;#AY>)Fpan zVx$lP*7k4ENeU)iao()#NjlH9yY`j5Ec;+c&WVpJ9lXfkE6MOD-i23PBUunvnn#Y( z #|4j?YO|^AdUK=H*-XyEloSEvlwk4{mZTSvJgNORvDi2hmwl}QW-kL{q z4IzKLb2#+qC}?v #<#NM!jN{V#q2BRj-=gLSG0Nk`5 zZL}Ji_tXpdt 6B*vk5TPQAF*5Z|IoU`C$ifuF(+fZ#)6aFalA zF_VG2JWivq(T_~_BZWyneQKA&bQc1$Y&$^SlpFDR_YT8VaDZoL+qtl%MHSL~YJ7}` z2AS)GZ8qul=0uOqMK9?=`+8gE;Q!UtGp%71>uEnV1FTntNN@qSFq7ZRX$Abx^}mDB z5Hlyr7NE%H+N{gPc31 79htF z`PW0{qb(T{E185h#?48oy&2aeRtvs*jgLiApaZ`i<)Jgf2!7+mqngV~$BZJ>pU}na zbke*Aza>{~GFd$zHg+)cul`^yxwjt5J<|z?*Ys<7io2m$R;3xx82wWthJC_79Jnie z2@~HGBvJ4Ad2A-`_V6=Ar9qi^XS2U$mlduR^W}(;>WHq*Xk}^e#qC#XTQ9`Q6%P8Z za>BnDW4y$HM+Z|zWV~Ube8QyK>$3kx{#1_1iPE_Zp6zPAsVH*&q>=~P^5>0K4_32_ zWNQF`mKm>u^zQI5^^eI&W&_FrSSjXGPvIxIGR-nOIw+qwsLV>>^W#2xs>JfjHhFn3 z8>9Y?JJpOI)Rphv&X_Vfy*oSv>?LldWeS>fUu?rk3uP#8QD~)pephL;;7)^0dgKrP ze#GEgt}RB3@};1QUE{e~VlqW6 ZQ9 zc2?lBU31`&t$U`NJY7a`JObQVt#3ACaRa0xLBRf-TDZ$!hx)byz`SMu!#{YvdWK-j ze}nz`W9!VC8d*U7q5eJ=5@@C!y3wf9lN~iO8?~ql<>O_MjW?&GYf{Z1+TC{Yul_^@ zJl+}1+*EEOy9FR6Hp!`w=nnVKf~&B3Heupz8SFLcI5Zg|ex*3hGSGum$7ZvRe;FSp zyzk>leA E^ fr~=BOiI zQ` u7ymq%TyHC zGmLxw=nt))u44K-Xr;6LlhGF!5&K(;EQ^?d1P}u(KZ8&%@3g>pyp9-gCzEa(S&G?v z?D|OmTT>0-wZWSTj4u{*2hgv>`>G2{DTS|0S)94KrHwaD+fZ-mAyauc8$~gPksR^z zaM$_{*ItQ7b5rGT$WCzy`E(q{vch?V^DO>5FTGnO1 +yT7N+f3&I|VHq(r=w2Xp`36iIP zKQM6j@}Nw{Q{dR7O1k%xf9Xi%&i1t~tG8+4ztGc>@&jn#;}?rVrp(P87vD={KXp_+ zkrZd__DkP$vv*Mm0a^I=ZKiq|1gA8L5g90|G6>B6H(@dXaWc@k6kU=ee`v}`U3N{5 zZ6{z)rXJym!^RbArdbXuu-DnMSg !kq(7HcEs3^#xs@b7K z_{PXUE(oMWNLVTfou)8nTKXuJ>(W*8LROl!Kj_bqm$QdP1jGt(U_0#xfig)-t6f`W ziPqFfeedt3&9vgQfLm{s_D#L#!VKHP4kLWu0`R5IpZX_mlC88K{s)rB@RgiwYE2s( zm`U;=f8Xchsi*9_P^r?A64WyqG^GmU;$)D5Hm0JgQ%uR*T(_f(!AAV+braAC^hyJV zKSDv00gu(VfGN=1pjF;viHrZ+odUWKCGm_s^fp3 zf6fPM&3>m_wE{jX)ZuV#?}aF0224w{uwzIF$tHhRdK&ciF;oj!u2FAK5AB}_oz(z! zcQsDOoK)A)FQXP{Yuq<@uapOhvI8{w`Ts*_Rb*{sWF{+@Rbzhi-xP15*2~&V&?CY- zWFkFq_v!?8M2aoBgurTj>CgJXO@mMKLKh$cP%9I~fVQx0onwCaNiQs GY!A)94D1K}0pkKn&xq?h!q)GaNmeeq6&0(HC_^N 903^M-x^o;L}6{f0ge&=rRtw4P&O-QbOv8zjfP)aax9M z5{s~RW0VLDk4<3;Q9L_q@AcnHt2Ja=a6z0-^Jdj+ie^Z#*X^GehM#op?tG;=&7!2D zc3QqP(gj$%i(@&mQ?i(vXygOJHu~X@5Fsl5@kUvKzFvUbo(Zi4{7EzA_cL@h{~n<3 NNxP +#include +#include -//+------------------------------------------------------------------+ -//| Input Parameters | -//+------------------------------------------------------------------+ +//--- Global objects +CTrade trade; +CPositionInfo position; +COrderInfo order; -//--- Risk Management -input group "=== Risk Management ===" -input double RiskPercent = 1.0; // Risk per trade (%) -input double MinRR = 2.0; // Minimum Risk-Reward ratio -input double MaxRR = 3.0; // Maximum Risk-Reward ratio -input int MaxTradesPerDay = 3; // Maximum trades per symbol per day -input int MaxTotalPositions = 10; // Maximum total open positions -input double MaxDailyRisk = 5.0; // Maximum daily risk (%) -input double MaxDrawdown = 15.0; // Maximum allowed drawdown (%) +//--- Input parameters +input group "=== Core Settings ===" input int MaxTradesPerDay = 3; // Maximum trades per symbol per day +input double RiskPercent = 1.0; // Risk percentage per trade +input double MinRR = 2.0; // Minimum risk-reward ratio +input bool UseTimeFilter = true; // Enable session filtering -//--- Trading Sessions -input group "=== Trading Sessions ===" -input bool UseTimeFilter = true; // Enable session time filtering -input bool TradeAsia = true; // Trade during Asia session -input bool TradeLondon = true; // Trade during London session -input bool TradeNewYork = true; // Trade during New York session -input string AsiaStart = "00:00"; // Asia session start time -input string AsiaEnd = "09:00"; // Asia session end time -input string LondonStart = "08:00"; // London session start time -input string LondonEnd = "17:00"; // London session end time -input string NewYorkStart = "13:00"; // New York session start time -input string NewYorkEnd = "22:00"; // New York session end time +input group "=== Session Settings ===" input string AsiaStart = "00:00"; // Asia session start (GMT) +input string AsiaEnd = "09:00"; // Asia session end (GMT) +input string LondonStart = "08:00"; // London session start (GMT) +input string LondonEnd = "17:00"; // London session end (GMT) +input string NYStart = "13:00"; // New York session start (GMT) +input string NYEnd = "22:00"; // New York session end (GMT) -//--- Market Structure -input group "=== Market Structure ===" -input int OrderBlockLookback = 20; // Order Block lookback period -input double MinOrderBlockSize = 10.0; // Minimum Order Block size (pips) -input double MinFVGSize = 3.0; // Minimum Fair Value Gap size (pips) -input double MinSweepDistance = 5.0; // Minimum liquidity sweep distance (pips) -input int BOSConfirmationBars = 3; // BOS confirmation bars -input bool UseMultiTimeframe = true; // Use multi-timeframe analysis -input ENUM_TIMEFRAMES BiasTimeframe1 = PERIOD_M15; // First bias timeframe -input ENUM_TIMEFRAMES BiasTimeframe2 = PERIOD_H4; // Second bias timeframe +input group "=== Risk Management ===" input int MaxSL = 50; // Maximum stop loss in pips +input int MinSL = 10; // Minimum stop loss in pips +input double MaxSlippage = 2.0; // Maximum slippage in pips +input int MaxPositions = 10; // Maximum total positions +input int MaxPositionsPerSymbol = 3; // Maximum positions per symbol -//--- AI Integration -input group "=== AI Integration ===" -input bool UseGrokAI = true; // Enable Grok AI integration -input string GrokAPIKey = ""; // Grok AI API Key -input double MinAIConfidence = 0.7; // Minimum AI confidence score -input bool UseSentimentFilter = true; // Use sentiment analysis filter -input bool UseFundamentalFilter = true; // Use fundamental analysis filter -input int AIAnalysisTimeout = 5000; // AI analysis timeout (ms) +input group "=== Pattern Detection ===" input int OBLookback = 20; // Order Block lookback candles +input double MinFVGSize = 3.0; // Minimum FVG size in pips +input double MinSweepDistance = 5.0; // Minimum sweep distance in pips +input int BOSConfirmationCandles = 3; // BOS confirmation within candles +input int SwingLookback = 10; // Swing high/low lookback period +input double OBStrengthFilter = 0.5; // Order Block strength filter (0-1) +input bool RequireMultiTFConfirmation = true; // Require multi-timeframe confirmation -//--- Visualization -input group "=== Visualization ===" -input bool ShowOrderBlocks = true; // Show Order Blocks on chart -input bool ShowFairValueGaps = true; // Show Fair Value Gaps on chart -input bool ShowBreakOfStructure = true; // Show Break of Structure markers -input bool ShowLiquiditySweeps = true; // Show Liquidity Sweep markers -input bool ShowInfoPanel = true; // Show information panel -input bool ShowTradeLines = true; // Show entry/SL/TP lines -input color OrderBlockColor = clrBlue; // Order Block color -input color FVGColor = clrYellow; // Fair Value Gap color -input color BOSColor = clrGreen; // Break of Structure color -input color SweepColor = clrRed; // Liquidity Sweep color +input group "=== Visualization ===" input bool ShowOrderBlocks = true; // Show Order Block zones +input bool ShowFVG = true; // Show Fair Value Gaps +input bool ShowBOS = true; // Show Break of Structure +input bool ShowSweeps = true; // Show Liquidity Sweeps +input bool ShowTradeLevels = true; // Show Entry/SL/TP levels -//--- Advanced Settings -input group "=== Advanced Settings ===" -input int MagicNumber = 123456; // EA Magic Number -input string TradeComment = "SniperEA"; // Trade comment -input int Slippage = 3; // Maximum slippage (points) -input bool UseNewsFilter = true; // Avoid trading during high-impact news -input int NewsFilterMinutes = 30; // Minutes to avoid before/after news -input bool EnableLogging = true; // Enable detailed logging -input ENUM_LOG_LEVEL LogLevel = LOG_LEVEL_INFO; // Logging level +input group "=== Symbols to Trade ===" input string Symbol1 = "EURUSD"; // Symbol 1 +input string Symbol2 = "GBPUSD"; // Symbol 2 +input string Symbol3 = "USDJPY"; // Symbol 3 +input string Symbol4 = "USDCHF"; // Symbol 4 +input string Symbol5 = "AUDUSD"; // Symbol 5 +input string Symbol6 = "USDCAD"; // Symbol 6 +input string Symbol7 = "NZDUSD"; // Symbol 7 +input string Symbol8 = "XAUUSD"; // Symbol 8 (Gold) -//+------------------------------------------------------------------+ -//| Global Variables | -//+------------------------------------------------------------------+ +input group "=== Logging & Debug ===" input bool EnableDetailedLogging = true; // Enable detailed logging +input bool EnableDebugMode = false; // Enable debug mode +input bool LogPatternDetection = true; // Log pattern detection events +input bool LogTradeExecution = true; // Log trade execution details -// Core components -CLogger* g_logger; -CConfig* g_config; -CNewsManager* g_newsManager; -CFundamentalAnalysis* g_fundamentalAnalysis; -CNewsFilter* g_newsFilter; -CWalkForwardOptimizer* g_walkForwardOptimizer; // Walk-forward optimizer -COrderBlock* g_orderBlock; -CBreakOfStructure* g_breakOfStructure; -CLiquiditySweep* g_liquiditySweep; -CFairValueGap* g_fairValueGap; -CPositionSizing* g_positionSizing; -CStopLoss* g_stopLoss; -CTakeProfit* g_takeProfit; -CTradingSessions* g_tradingSessions; -CSessionFilter* g_sessionFilter; -CGrokConnector* g_grokConnector; -CSentimentAnalysis* g_sentimentAnalysis; -CChartObjects* g_chartObjects; -CInfoPanel* g_infoPanel; +//--- Global variables +string SymbolsToTrade[]; +int TotalSymbols = 0; +datetime LastBarTime = 0; +bool IsInitialized = false; +string LogPrefix = "SniperEA"; +int LogLevel = 0; // 0=Info, 1=Warning, 2=Error, 3=Debug -// Trading state variables -datetime g_lastBarTime; -int g_dailyTradeCount; -datetime g_lastTradeDate; -double g_dailyRisk; -bool g_isInitialized; -string g_currentSymbol; - -// Performance tracking -struct PerformanceMetrics { - int totalTrades; - int winningTrades; - int losingTrades; - double totalProfit; - double totalLoss; - double maxDrawdown; - double currentDrawdown; - double winRate; - double profitFactor; - datetime lastUpdate; +//--- Structure definitions +struct OrderBlock +{ + double high; + double low; + datetime time; + bool is_bullish; + bool is_fresh; + int strength; }; -PerformanceMetrics g_performance; +struct FairValueGap +{ + double top; + double bottom; + datetime time; + bool is_bullish; + bool is_filled; +}; + +struct LiquiditySweep +{ + double level; + datetime time; + bool is_high_sweep; + bool confirmed; +}; + +struct BreakOfStructure +{ + double level; + datetime time; + bool is_bullish; + bool confirmed; +}; + +//--- Function declarations +bool ConfirmBOS(string symbol, ENUM_TIMEFRAMES timeframe, int break_bar, bool is_bullish, double level); //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ -int OnInit() { - Print("=== Initializing Sniper EA v1.00 ==="); - - // Initialize global variables - g_isInitialized = false; - g_currentSymbol = Symbol(); - g_lastBarTime = 0; - g_dailyTradeCount = 0; - g_lastTradeDate = 0; - g_dailyRisk = 0.0; - - // Initialize performance metrics - ZeroMemory(g_performance); - g_performance.lastUpdate = TimeCurrent(); - - // Initialize core components - if(!InitializeComponents()) { - Print("ERROR: Failed to initialize EA components"); - return INIT_FAILED; - } - - // Validate input parameters - if(!ValidateInputParameters()) { - Print("ERROR: Invalid input parameters"); - return INIT_PARAMETERS_INCORRECT; - } - - // Initialize AI integration if enabled - if(UseGrokAI && !InitializeAIIntegration()) { - Print("WARNING: AI integration initialization failed, continuing without AI"); - } - - // Initialize visualization - if(!InitializeVisualization()) { - Print("WARNING: Visualization initialization failed"); - } - - // Set up event timer for periodic tasks - EventSetTimer(60); // 1-minute timer - - g_isInitialized = true; - Print("=== Sniper EA initialized successfully ==="); - - return INIT_SUCCEEDED; +int OnInit() +{ + Print("=== Sniper EA Initialization Started ==="); + + // Initialize trade object + trade.SetExpertMagicNumber(123456); + trade.SetDeviationInPoints((int)(MaxSlippage * 10)); + trade.SetTypeFilling(ORDER_FILLING_FOK); + + // Setup symbols array + if (!SetupSymbolsArray()) + { + Print("ERROR: Failed to setup symbols array"); + return INIT_FAILED; + } + + // Validate input parameters + if (!ValidateInputs()) + { + Print("ERROR: Invalid input parameters"); + return INIT_FAILED; + } + + // Initialize chart objects + if (!InitializeChartObjects()) + { + Print("ERROR: Failed to initialize chart objects"); + return INIT_FAILED; + } + + // Initialize multi-timeframe analysis + if (!InitializeMultiTimeframeAnalysis()) + { + Print("ERROR: Failed to initialize multi-timeframe analysis"); + return INIT_FAILED; + } + + IsInitialized = true; + LastBarTime = iTime(_Symbol, PERIOD_M1, 0); + + Print("=== Sniper EA Initialization Completed Successfully ==="); + Print("Trading Symbols: ", TotalSymbols); + Print("Risk per Trade: ", RiskPercent, "%"); + Print("Minimum R:R Ratio: ", MinRR, ":1"); + + return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ -void OnDeinit(const int reason) { - Print("=== Deinitializing Sniper EA ==="); - - // Stop timer - EventKillTimer(); - - // Clean up visualization - if(g_chartObjects != NULL) { - g_chartObjects.CleanupAll(); - delete g_chartObjects; - } - - if(g_infoPanel != NULL) { - g_infoPanel.Hide(); - delete g_infoPanel; - } - - // Clean up components - CleanupComponents(); - - // Final performance report - if(g_logger != NULL) { - g_logger.Info("Final Performance Report:"); - g_logger.Info(StringFormat("Total Trades: %d", g_performance.totalTrades)); - g_logger.Info(StringFormat("Win Rate: %.2f%%", g_performance.winRate)); - g_logger.Info(StringFormat("Profit Factor: %.2f", g_performance.profitFactor)); - g_logger.Info(StringFormat("Max Drawdown: %.2f%%", g_performance.maxDrawdown)); - } - - Print("=== Sniper EA deinitialized ==="); +void OnDeinit(const int reason) +{ + Print("=== Sniper EA Deinitialization Started ==="); + + // Clean up chart objects + CleanupChartObjects(); + + // Print deinitialization reason + string deinit_reason = ""; + switch (reason) + { + case REASON_PROGRAM: + deinit_reason = "Expert Advisor terminated"; + break; + case REASON_REMOVE: + deinit_reason = "Expert Advisor removed from chart"; + break; + case REASON_RECOMPILE: + deinit_reason = "Expert Advisor recompiled"; + break; + case REASON_CHARTCHANGE: + deinit_reason = "Chart symbol or period changed"; + break; + case REASON_CHARTCLOSE: + deinit_reason = "Chart closed"; + break; + case REASON_PARAMETERS: + deinit_reason = "Input parameters changed"; + break; + case REASON_ACCOUNT: + deinit_reason = "Account changed"; + break; + default: + deinit_reason = "Unknown reason"; + break; + } + + Print("Deinitialization Reason: ", deinit_reason); + Print("=== Sniper EA Deinitialization Completed ==="); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ -void OnTick() { - if(!g_isInitialized) return; - - // Check for new bar - datetime currentBarTime = iTime(g_currentSymbol, PERIOD_M1, 0); - if(currentBarTime == g_lastBarTime) return; - - g_lastBarTime = currentBarTime; - - // Update daily trade count if new day - UpdateDailyTradeCount(); - - // Check trading conditions - if(!IsReadyToTrade()) return; - - // Main trading logic - AnalyzeMarketAndTrade(); - - // Update visualization - UpdateVisualization(); - - // Update performance metrics - UpdatePerformanceMetrics(); +void OnTick() +{ + if (!IsInitialized) + return; + + // Check for new bar + datetime current_bar_time = iTime(_Symbol, PERIOD_M1, 0); + if (current_bar_time == LastBarTime) + return; + + LastBarTime = current_bar_time; + + // Main trading logic will be implemented here + ProcessTradingLogic(); } //+------------------------------------------------------------------+ -//| Timer function | +//| Setup symbols array from input parameters | //+------------------------------------------------------------------+ -void OnTimer() { - if(!g_isInitialized) return; - - // Update news and fundamental data - if(UseNewsFilter) { - if(g_newsManager != NULL) { - g_newsManager.UpdateNewsData(); - } - - if(g_fundamentalAnalysis != NULL) { - g_fundamentalAnalysis.UpdateFactors(); - } - - if(g_newsFilter != NULL) { - g_newsFilter.UpdatePerformanceMetrics(); - } - } - - // Update AI analysis periodically - if(UseGrokAI && g_grokConnector != NULL) { - g_grokConnector.UpdateAnalysis(); - } - - // Update session information - if(g_tradingSessions != NULL) { - g_tradingSessions.UpdateCurrentSession(); - } - - // Update information panel - if(ShowInfoPanel && g_infoPanel != NULL) { - g_infoPanel.Update(); - } - - // Check for emergency stop conditions - CheckEmergencyStop(); +bool SetupSymbolsArray() +{ + ArrayResize(SymbolsToTrade, 0); + TotalSymbols = 0; + + string symbols[8] = {Symbol1, Symbol2, Symbol3, Symbol4, Symbol5, Symbol6, Symbol7, Symbol8}; + + for (int i = 0; i < 8; i++) + { + if (symbols[i] != "" && symbols[i] != "NONE") + { + ArrayResize(SymbolsToTrade, TotalSymbols + 1); + SymbolsToTrade[TotalSymbols] = symbols[i]; + TotalSymbols++; + } + } + + return TotalSymbols > 0; } //+------------------------------------------------------------------+ -//| Trade function | +//| Validate input parameters | //+------------------------------------------------------------------+ -void OnTrade() { - // Update performance metrics when trades are closed - UpdatePerformanceMetrics(); - - // Log trade events - if(g_logger != NULL) { - g_logger.Info("Trade event detected - updating metrics"); - } +bool ValidateInputs() +{ + if (RiskPercent <= 0 || RiskPercent > 10) + { + Print("ERROR: Risk percent must be between 0 and 10"); + return false; + } + + if (MinRR < 1.0) + { + Print("ERROR: Minimum R:R ratio must be at least 1.0"); + return false; + } + + if (MaxSL <= MinSL) + { + Print("ERROR: Maximum SL must be greater than Minimum SL"); + return false; + } + + if (MaxTradesPerDay <= 0) + { + Print("ERROR: Max trades per day must be positive"); + return false; + } + + return true; } //+------------------------------------------------------------------+ -//| Chart event function | +//| Initialize chart objects | //+------------------------------------------------------------------+ -void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam) { - if(!g_isInitialized) return; - - // Handle chart events for interactive features - if(g_infoPanel != NULL) { - g_infoPanel.OnChartEvent(id, lparam, dparam, sparam); - } +bool InitializeChartObjects() +{ + // Set chart properties for better visualization + ChartSetInteger(0, CHART_SHOW_GRID, false); + ChartSetInteger(0, CHART_SHOW_VOLUMES, false); + ChartSetInteger(0, CHART_SHOW_OHLC, true); + + // Create information panel background + if (ObjectCreate(0, "SniperEA_InfoPanel", OBJ_RECTANGLE_LABEL, 0, 0, 0)) + { + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_XDISTANCE, 10); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_YDISTANCE, 30); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_XSIZE, 250); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_YSIZE, 150); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BGCOLOR, clrDarkSlateGray); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BORDER_TYPE, BORDER_FLAT); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_COLOR, clrWhite); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_WIDTH, 1); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BACK, false); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_SELECTED, false); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_HIDDEN, true); + } + + // Create EA status label + if (ObjectCreate(0, "SniperEA_Status", OBJ_LABEL, 0, 0, 0)) + { + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_XDISTANCE, 20); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_YDISTANCE, 40); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_COLOR, clrLime); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_FONTSIZE, 10); + ObjectSetString(0, "SniperEA_Status", OBJPROP_FONT, "Arial Bold"); + ObjectSetString(0, "SniperEA_Status", OBJPROP_TEXT, "Sniper EA - ACTIVE"); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_SELECTED, false); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_HIDDEN, true); + } + + Print("Chart objects initialized successfully"); + return true; } //+------------------------------------------------------------------+ -//| Initialize Components | +//| Clean up chart objects | //+------------------------------------------------------------------+ -bool InitializeComponents() { - // Initialize logger first - g_logger = new CLogger(); - if(g_logger == NULL) return false; - g_logger.Initialize(EnableLogging, LogLevel); - - // Initialize configuration - g_config = new CConfig(); - if(g_config == NULL) return false; - g_config.LoadSettings(); - - // Initialize news and fundamental analysis components - g_newsManager = new CNewsManager(); - g_fundamentalAnalysis = new CFundamentalAnalysis(); - g_newsFilter = new CNewsFilter(); - - if(g_newsManager == NULL || g_fundamentalAnalysis == NULL || g_newsFilter == NULL) { - return false; - } - - // Initialize news system - if(!g_newsManager.Initialize()) { - g_logger.Error("Failed to initialize news manager"); - return false; - } - - if(!g_fundamentalAnalysis.Initialize()) { - g_logger.Error("Failed to initialize fundamental analysis"); - return false; - } - - if(!g_newsFilter.Initialize()) { - g_logger.Error("Failed to initialize news filter"); - return false; - } - - // Initialize market structure components - g_orderBlock = new COrderBlock(); - g_breakOfStructure = new CBreakOfStructure(); - g_liquiditySweep = new CLiquiditySweep(); - g_fairValueGap = new CFairValueGap(); - - if(g_orderBlock == NULL || g_breakOfStructure == NULL || - g_liquiditySweep == NULL || g_fairValueGap == NULL) { - return false; - } - - // Initialize risk management components - g_positionSizing = new CPositionSizing(); - g_stopLoss = new CStopLoss(); - g_takeProfit = new CTakeProfit(); - - if(g_positionSizing == NULL || g_stopLoss == NULL || g_takeProfit == NULL) { - return false; - } - - // Initialize session management - g_tradingSessions = new CTradingSessions(); - g_sessionFilter = new CSessionFilter(); - - if(g_tradingSessions == NULL || g_sessionFilter == NULL) { - return false; - } - - g_logger.Info("Core components initialized successfully"); - return true; +void CleanupChartObjects() +{ + // Clean up all chart objects created by the EA + int total_objects = ObjectsDeleteAll(0, "SniperEA_"); + Print("Cleaned up ", total_objects, " chart objects"); } //+------------------------------------------------------------------+ -//| Initialize AI Integration | +//| Update information panel | //+------------------------------------------------------------------+ -bool InitializeAIIntegration() { - if(!UseGrokAI) return true; - - g_grokConnector = new CGrokConnector(); - g_sentimentAnalysis = new CSentimentAnalysis(); - - if(g_grokConnector == NULL || g_sentimentAnalysis == NULL) { - return false; - } - - // Initialize Grok AI connection - if(!g_grokConnector.Initialize(GrokAPIKey)) { - g_logger.Error("Failed to initialize Grok AI connection"); - return false; - } - - g_logger.Info("AI integration initialized successfully"); - return true; +void UpdateInfoPanel() +{ + // Get current session + string current_session = GetCurrentSession(); + + // Get account information + double account_balance = AccountInfoDouble(ACCOUNT_BALANCE); + double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); + double account_margin = AccountInfoDouble(ACCOUNT_MARGIN); + + // Count current positions + int total_positions = PositionsTotal(); + + // Create info text + string info_text = StringFormat( + "Session: %s\n" + + "Balance: %.2f\n" + + "Equity: %.2f\n" + + "Margin: %.2f\n" + + "Positions: %d/%d", + current_session, + account_balance, + account_equity, + account_margin, + total_positions, + MaxPositions); + + // Update info label + if (ObjectFind(0, "SniperEA_Info") < 0) + { + ObjectCreate(0, "SniperEA_Info", OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_XDISTANCE, 20); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_YDISTANCE, 60); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_COLOR, clrWhite); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_FONTSIZE, 8); + ObjectSetString(0, "SniperEA_Info", OBJPROP_FONT, "Courier New"); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_SELECTED, false); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_HIDDEN, true); + } + + ObjectSetString(0, "SniperEA_Info", OBJPROP_TEXT, info_text); } //+------------------------------------------------------------------+ -//| Initialize Visualization | +//| Get current trading session | //+------------------------------------------------------------------+ -bool InitializeVisualization() { - g_chartObjects = new CChartObjects(); - g_infoPanel = new CInfoPanel(); - - if(g_chartObjects == NULL || g_infoPanel == NULL) { - return false; - } - - // Configure chart objects - g_chartObjects.SetColors(OrderBlockColor, FVGColor, BOSColor, SweepColor); - g_chartObjects.SetVisibility(ShowOrderBlocks, ShowFairValueGaps, - ShowBreakOfStructure, ShowLiquiditySweeps); - - // Initialize info panel - if(ShowInfoPanel) { - g_infoPanel.Initialize(); - } - - g_logger.Info("Visualization components initialized successfully"); - return true; +string GetCurrentSession() +{ + MqlDateTime dt; + TimeToStruct(TimeGMT(), dt); + + int current_hour = dt.hour; + int current_minute = dt.min; + int current_time_minutes = current_hour * 60 + current_minute; + + // Convert session times to minutes + int asia_start = (int)(StringToTime("1970.01.01 " + AsiaStart) % 86400 / 60); + int asia_end = (int)(StringToTime("1970.01.01 " + AsiaEnd) % 86400 / 60); + int london_start = (int)(StringToTime("1970.01.01 " + LondonStart) % 86400 / 60); + int london_end = (int)(StringToTime("1970.01.01 " + LondonEnd) % 86400 / 60); + int ny_start = (int)(StringToTime("1970.01.01 " + NYStart) % 86400 / 60); + int ny_end = (int)(StringToTime("1970.01.01 " + NYEnd) % 86400 / 60); + + // Check which session we're in + if ((current_time_minutes >= asia_start && current_time_minutes < asia_end) || + (asia_start > asia_end && (current_time_minutes >= asia_start || current_time_minutes < asia_end))) + return "ASIA"; + + if ((current_time_minutes >= london_start && current_time_minutes < london_end) || + (london_start > london_end && (current_time_minutes >= london_start || current_time_minutes < london_end))) + return "LONDON"; + + if ((current_time_minutes >= ny_start && current_time_minutes < ny_end) || + (ny_start > ny_end && (current_time_minutes >= ny_start || current_time_minutes < ny_end))) + return "NEW YORK"; + + return "OFF HOURS"; } //+------------------------------------------------------------------+ -//| Validate Input Parameters | +//| Main trading logic processor | //+------------------------------------------------------------------+ -bool ValidateInputParameters() { - if(RiskPercent <= 0 || RiskPercent > 10) { - Print("ERROR: Risk percent must be between 0 and 10"); - return false; - } - - if(MinRR <= 0 || MaxRR <= MinRR) { - Print("ERROR: Invalid risk-reward ratio settings"); - return false; - } - - if(MaxTradesPerDay <= 0 || MaxTradesPerDay > 20) { - Print("ERROR: Max trades per day must be between 1 and 20"); - return false; - } - - if(MagicNumber <= 0) { - Print("ERROR: Magic number must be positive"); - return false; - } - - g_logger.Info("Input parameters validated successfully"); - return true; +void ProcessTradingLogic() +{ + // Update information panel + UpdateInfoPanel(); + + // Check if trading is allowed in current session + if (UseTimeFilter && GetCurrentSession() == "OFF HOURS") + return; + + // Main trading logic will be implemented here + // This is where we'll call all the market structure analysis functions } //+------------------------------------------------------------------+ -//| Check if ready to trade | +//| Logging Functions | //+------------------------------------------------------------------+ -bool IsReadyToTrade() { - // Check if market is open - if(!IsMarketOpen()) return false; - - // Check daily trade limit - if(g_dailyTradeCount >= MaxTradesPerDay) return false; - - // Check daily risk limit - if(g_dailyRisk >= MaxDailyRisk) return false; - - // Check maximum positions - if(PositionsTotal() >= MaxTotalPositions) return false; - - // Check session filter - if(UseTimeFilter && !g_sessionFilter.IsSessionActive()) return false; - - // Check news filter - comprehensive news avoidance system - if(UseNewsFilter) { - // Check for high impact news events - if(g_newsManager != NULL && g_newsManager.IsHighImpactNewsTime()) { - g_logger.Info("High impact news detected - trading suspended"); +void LogInfo(string message) +{ + if (EnableDetailedLogging) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [INFO] ", LogPrefix, ": ", message); + } +} + +void LogWarning(string message) +{ + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [WARNING] ", LogPrefix, ": ", message); +} + +void LogError(string message) +{ + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [ERROR] ", LogPrefix, ": ", message); +} + +void LogDebug(string message) +{ + if (EnableDebugMode) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [DEBUG] ", LogPrefix, ": ", message); + } +} + +void LogPattern(string pattern_type, string symbol, string details) +{ + if (LogPatternDetection) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [PATTERN] ", LogPrefix, ": ", pattern_type, " detected on ", symbol, " - ", details); + } +} + +void LogTrade(string action, string symbol, string details) +{ + if (LogTradeExecution) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [TRADE] ", LogPrefix, ": ", action, " on ", symbol, " - ", details); + } +} + +//+------------------------------------------------------------------+ +//| Error Handling Functions | +//+------------------------------------------------------------------+ +bool HandleTradeError(int error_code, string operation) +{ + string error_description = ""; + bool is_critical = false; + + switch (error_code) + { + case TRADE_RETCODE_REQUOTE: + error_description = "Requote"; + break; + case TRADE_RETCODE_REJECT: + error_description = "Request rejected"; + is_critical = true; + break; + case TRADE_RETCODE_CANCEL: + error_description = "Request canceled by trader"; + break; + case TRADE_RETCODE_PLACED: + error_description = "Order placed"; + return true; // Success + case TRADE_RETCODE_DONE: + error_description = "Request completed"; + return true; // Success + case TRADE_RETCODE_DONE_PARTIAL: + error_description = "Request partially completed"; + return true; // Partial success + case TRADE_RETCODE_ERROR: + error_description = "Request processing error"; + is_critical = true; + break; + case TRADE_RETCODE_TIMEOUT: + error_description = "Request timeout"; + break; + case TRADE_RETCODE_INVALID: + error_description = "Invalid request"; + is_critical = true; + break; + case TRADE_RETCODE_INVALID_VOLUME: + error_description = "Invalid volume"; + is_critical = true; + break; + case TRADE_RETCODE_INVALID_PRICE: + error_description = "Invalid price"; + break; + case TRADE_RETCODE_INVALID_STOPS: + error_description = "Invalid stops"; + break; + case TRADE_RETCODE_TRADE_DISABLED: + error_description = "Trade disabled"; + is_critical = true; + break; + case TRADE_RETCODE_MARKET_CLOSED: + error_description = "Market closed"; + break; + case TRADE_RETCODE_NO_MONEY: + error_description = "No money"; + is_critical = true; + break; + case TRADE_RETCODE_PRICE_CHANGED: + error_description = "Price changed"; + break; + case TRADE_RETCODE_PRICE_OFF: + error_description = "Off quotes"; + break; + case TRADE_RETCODE_INVALID_EXPIRATION: + error_description = "Invalid expiration"; + break; + case TRADE_RETCODE_ORDER_CHANGED: + error_description = "Order state changed"; + break; + case TRADE_RETCODE_TOO_MANY_REQUESTS: + error_description = "Too many requests"; + break; + case TRADE_RETCODE_NO_CHANGES: + error_description = "No changes"; + break; + case TRADE_RETCODE_SERVER_DISABLES_AT: + error_description = "Autotrading disabled by server"; + is_critical = true; + break; + case TRADE_RETCODE_CLIENT_DISABLES_AT: + error_description = "Autotrading disabled by client"; + is_critical = true; + break; + case TRADE_RETCODE_LOCKED: + error_description = "Request locked"; + break; + case TRADE_RETCODE_FROZEN: + error_description = "Order or position frozen"; + break; + case TRADE_RETCODE_INVALID_FILL: + error_description = "Invalid fill"; + break; + case TRADE_RETCODE_CONNECTION: + error_description = "No connection"; + is_critical = true; + break; + case TRADE_RETCODE_ONLY_REAL: + error_description = "Only real accounts allowed"; + is_critical = true; + break; + case TRADE_RETCODE_LIMIT_ORDERS: + error_description = "Limit orders limit reached"; + break; + case TRADE_RETCODE_LIMIT_VOLUME: + error_description = "Volume limit reached"; + break; + case TRADE_RETCODE_INVALID_ORDER: + error_description = "Invalid order"; + is_critical = true; + break; + case TRADE_RETCODE_POSITION_CLOSED: + error_description = "Position already closed"; + break; + default: + error_description = "Unknown error"; + is_critical = true; + break; + } + + if (is_critical) + { + LogError(StringFormat("%s failed with critical error %d: %s", operation, error_code, error_description)); + } + else + { + LogWarning(StringFormat("%s failed with error %d: %s", operation, error_code, error_description)); + } + + return false; +} + +//+------------------------------------------------------------------+ +//| Utility Functions | +//+------------------------------------------------------------------+ +double NormalizePrice(string symbol, double price) +{ + return NormalizeDouble(price, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS)); +} + +double CalculatePipValue(string symbol) +{ + double pip_size = SymbolInfoDouble(symbol, SYMBOL_POINT); + int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS); + if (digits == 5 || digits == 3) + pip_size *= 10; + return pip_size; +} + +bool IsNewBar(string symbol, ENUM_TIMEFRAMES timeframe) +{ + static datetime last_bar_time = 0; + datetime current_bar_time = iTime(symbol, timeframe, 0); + + if (current_bar_time != last_bar_time) + { + last_bar_time = current_bar_time; + return true; + } + + return false; +} + +//+------------------------------------------------------------------+ +//| Order Block Detection Functions | +//+------------------------------------------------------------------+ +bool DetectOrderBlocks(string symbol, ENUM_TIMEFRAMES timeframe, OrderBlock &order_blocks[]) +{ + ArrayResize(order_blocks, 0); + + int bars_to_analyze = MathMin(OBLookback * 2, iBars(symbol, timeframe) - 10); + if (bars_to_analyze < 10) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Order Blocks on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + // Look for potential Order Blocks + for (int i = 5; i < bars_to_analyze; i++) + { + // Get candle data + double high = iHigh(symbol, timeframe, i); + double low = iLow(symbol, timeframe, i); + double open = iOpen(symbol, timeframe, i); + double close = iClose(symbol, timeframe, i); + datetime time = iTime(symbol, timeframe, i); + + // Check for bullish Order Block (demand zone) + if (IsBullishOrderBlock(symbol, timeframe, i)) + { + OrderBlock ob; + ob.high = high; + ob.low = low; + ob.time = time; + ob.is_bullish = true; + ob.is_fresh = IsOrderBlockFresh(symbol, timeframe, i, true); + ob.strength = CalculateOrderBlockStrength(symbol, timeframe, i, true); + + if (ob.strength >= OBStrengthFilter) + { + ArrayResize(order_blocks, ArraySize(order_blocks) + 1); + order_blocks[ArraySize(order_blocks) - 1] = ob; + + LogPattern("Order Block", symbol, StringFormat("Bullish OB at %.5f-%.5f, Strength: %.2f", ob.low, ob.high, ob.strength)); + } + } + + // Check for bearish Order Block (supply zone) + if (IsBearishOrderBlock(symbol, timeframe, i)) + { + OrderBlock ob; + ob.high = high; + ob.low = low; + ob.time = time; + ob.is_bullish = false; + ob.is_fresh = IsOrderBlockFresh(symbol, timeframe, i, false); + ob.strength = CalculateOrderBlockStrength(symbol, timeframe, i, false); + + if (ob.strength >= OBStrengthFilter) + { + ArrayResize(order_blocks, ArraySize(order_blocks) + 1); + order_blocks[ArraySize(order_blocks) - 1] = ob; + + LogPattern("Order Block", symbol, StringFormat("Bearish OB at %.5f-%.5f, Strength: %.2f", ob.low, ob.high, ob.strength)); + } + } + } + + LogDebug(StringFormat("Found %d Order Blocks on %s %s", ArraySize(order_blocks), symbol, EnumToString(timeframe))); + return ArraySize(order_blocks) > 0; +} + +bool IsBullishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) +{ + // Get current candle data + double open = iOpen(symbol, timeframe, index); + double close = iClose(symbol, timeframe, index); + double high = iHigh(symbol, timeframe, index); + double low = iLow(symbol, timeframe, index); + + // Must be a bullish candle + if (close <= open) + return false; + + // Check for strong bullish momentum (body > 60% of total range) + double body_size = close - open; + double total_range = high - low; + if (total_range == 0) + return false; + + double body_ratio = body_size / total_range; + if (body_ratio < 0.6) + return false; + + // Check for significant volume increase (if available) + long current_volume = iVolume(symbol, timeframe, index); + long avg_volume = 0; + for (int i = 1; i <= 5; i++) + { + avg_volume += iVolume(symbol, timeframe, index + i); + } + avg_volume /= 5; + + if (current_volume < avg_volume * 1.2) + return false; + + // Check for price rejection from this level in subsequent candles + bool has_rejection = false; + for (int i = 1; i <= 5; i++) + { + if (index - i < 0) + break; + + double test_low = iLow(symbol, timeframe, index - i); + double test_close = iClose(symbol, timeframe, index - i); + + // Price came back to test the OB zone and bounced + if (test_low <= high && test_low >= low && test_close > high) + { + has_rejection = true; + break; + } + } + + return has_rejection; +} + +bool IsBearishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) +{ + // Get current candle data + double open = iOpen(symbol, timeframe, index); + double close = iClose(symbol, timeframe, index); + double high = iHigh(symbol, timeframe, index); + double low = iLow(symbol, timeframe, index); + + // Must be a bearish candle + if (close >= open) + return false; + + // Check for strong bearish momentum (body > 60% of total range) + double body_size = open - close; + double total_range = high - low; + if (total_range == 0) + return false; + + double body_ratio = body_size / total_range; + if (body_ratio < 0.6) + return false; + + // Check for significant volume increase (if available) + long current_volume = iVolume(symbol, timeframe, index); + long avg_volume = 0; + for (int i = 1; i <= 5; i++) + { + avg_volume += iVolume(symbol, timeframe, index + i); + } + avg_volume /= 5; + + if (current_volume < avg_volume * 1.2) + return false; + + // Check for price rejection from this level in subsequent candles + bool has_rejection = false; + for (int i = 1; i <= 5; i++) + { + if (index - i < 0) + break; + + double test_high = iHigh(symbol, timeframe, index - i); + double test_close = iClose(symbol, timeframe, index - i); + + // Price came back to test the OB zone and bounced + if (test_high >= low && test_high <= high && test_close < low) + { + has_rejection = true; + break; + } + } + + return has_rejection; +} + +bool IsOrderBlockFresh(string symbol, ENUM_TIMEFRAMES timeframe, int ob_index, bool is_bullish) +{ + double ob_high = iHigh(symbol, timeframe, ob_index); + double ob_low = iLow(symbol, timeframe, ob_index); + + // Check if price has significantly broken through the OB zone + for (int i = 0; i < ob_index; i++) + { + double test_high = iHigh(symbol, timeframe, i); + double test_low = iLow(symbol, timeframe, i); + + if (is_bullish) + { + // For bullish OB, check if price broke significantly below + if (test_low < ob_low - (ob_high - ob_low) * 0.5) return false; - } - - // Check fundamental analysis restrictions - if(g_fundamentalAnalysis != NULL && g_fundamentalAnalysis.ShouldAvoidTrading()) { - g_logger.Info("Fundamental analysis suggests avoiding trading"); + } + else + { + // For bearish OB, check if price broke significantly above + if (test_high > ob_high + (ob_high - ob_low) * 0.5) return false; - } - - // Apply news filter rules - if(g_newsFilter != NULL) { - SFilterDecision decision = g_newsFilter.EvaluateTradeConditions(g_currentSymbol); - if(decision.action == FILTER_ACTION_BLOCK) { - g_logger.Info(StringFormat("News filter blocked trading: %s", decision.reason)); - return false; + } + } + + return true; +} + +double CalculateOrderBlockStrength(string symbol, ENUM_TIMEFRAMES timeframe, int index, bool is_bullish) +{ + double strength = 0.0; + + // Factor 1: Candle body size relative to average + double body_size = MathAbs(iClose(symbol, timeframe, index) - iOpen(symbol, timeframe, index)); + double avg_body = 0; + for (int i = 1; i <= 10; i++) + { + avg_body += MathAbs(iClose(symbol, timeframe, index + i) - iOpen(symbol, timeframe, index + i)); + } + avg_body /= 10; + + if (avg_body > 0) + strength += (body_size / avg_body) * 0.3; // 30% weight + + // Factor 2: Volume relative to average + long current_volume = iVolume(symbol, timeframe, index); + long avg_volume = 0; + for (int i = 1; i <= 10; i++) + { + avg_volume += iVolume(symbol, timeframe, index + i); + } + avg_volume /= 10; + + if (avg_volume > 0) + strength += ((double)current_volume / avg_volume) * 0.2; // 20% weight + + // Factor 3: Number of times price respected the level + int respect_count = 0; + double ob_high = iHigh(symbol, timeframe, index); + double ob_low = iLow(symbol, timeframe, index); + + for (int i = 1; i < index && i <= 20; i++) + { + double test_high = iHigh(symbol, timeframe, index - i); + double test_low = iLow(symbol, timeframe, index - i); + double test_close = iClose(symbol, timeframe, index - i); + + if (is_bullish) + { + if (test_low <= ob_high && test_low >= ob_low && test_close > ob_high) + respect_count++; + } + else + { + if (test_high >= ob_low && test_high <= ob_high && test_close < ob_low) + respect_count++; + } + } + + strength += respect_count * 0.1; // 10% weight per respect + + // Factor 4: Time since formation (fresher = stronger) + double time_factor = 1.0 - (index / (double)OBLookback); + strength += time_factor * 0.3; // 30% weight + + return MathMin(strength, 2.0); // Cap at 2.0 +} + +//+------------------------------------------------------------------+ +//| Break of Structure Detection Functions | +//+------------------------------------------------------------------+ +bool DetectBreakOfStructure(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos_events[]) +{ + ArrayResize(bos_events, 0); + + int bars_to_analyze = MathMin(SwingLookback * 3, iBars(symbol, timeframe) - 10); + if (bars_to_analyze < 20) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Break of Structure on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + // Find swing highs and lows first + double swing_highs[]; + double swing_lows[]; + datetime swing_high_times[]; + datetime swing_low_times[]; + + FindSwingPoints(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times); + + // Analyze for BOS patterns + AnalyzeBOSPatterns(symbol, timeframe, swing_highs, swing_lows, swing_high_times, swing_low_times, bos_events); + + LogDebug(StringFormat("Found %d BOS events on %s %s", ArraySize(bos_events), symbol, EnumToString(timeframe))); + return ArraySize(bos_events) > 0; +} + +void FindSwingPoints(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, + double &swing_highs[], double &swing_lows[], + datetime &swing_high_times[], datetime &swing_low_times[]) +{ + ArrayResize(swing_highs, 0); + ArrayResize(swing_lows, 0); + ArrayResize(swing_high_times, 0); + ArrayResize(swing_low_times, 0); + + for (int i = SwingLookback; i < bars_to_analyze - SwingLookback; i++) + { + double current_high = iHigh(symbol, timeframe, i); + double current_low = iLow(symbol, timeframe, i); + datetime current_time = iTime(symbol, timeframe, i); + + // Check for swing high + bool is_swing_high = true; + for (int j = 1; j <= SwingLookback; j++) + { + if (iHigh(symbol, timeframe, i - j) >= current_high || + iHigh(symbol, timeframe, i + j) >= current_high) + { + is_swing_high = false; + break; + } + } + + if (is_swing_high) + { + ArrayResize(swing_highs, ArraySize(swing_highs) + 1); + ArrayResize(swing_high_times, ArraySize(swing_high_times) + 1); + swing_highs[ArraySize(swing_highs) - 1] = current_high; + swing_high_times[ArraySize(swing_high_times) - 1] = current_time; + } + + // Check for swing low + bool is_swing_low = true; + for (int j = 1; j <= SwingLookback; j++) + { + if (iLow(symbol, timeframe, i - j) <= current_low || + iLow(symbol, timeframe, i + j) <= current_low) + { + is_swing_low = false; + break; + } + } + + if (is_swing_low) + { + ArrayResize(swing_lows, ArraySize(swing_lows) + 1); + ArrayResize(swing_low_times, ArraySize(swing_low_times) + 1); + swing_lows[ArraySize(swing_lows) - 1] = current_low; + swing_low_times[ArraySize(swing_low_times) - 1] = current_time; + } + } +} + +void AnalyzeBOSPatterns(string symbol, ENUM_TIMEFRAMES timeframe, + double &swing_highs[], double &swing_lows[], + datetime &swing_high_times[], datetime &swing_low_times[], + BreakOfStructure &bos_events[]) +{ + // Analyze bullish BOS (breaking above previous swing high) + for (int i = 1; i < ArraySize(swing_highs); i++) + { + double previous_high = swing_highs[i]; + datetime previous_time = swing_high_times[i]; + + // Look for price breaking above this high + int start_bar = iBarShift(symbol, timeframe, previous_time); + if (start_bar < 0) + continue; + + for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) + { + double current_high = iHigh(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + if (current_high > previous_high && current_close > previous_high) + { + // Confirm the break with subsequent candles + bool confirmed = ConfirmBOS(symbol, timeframe, j, true, previous_high); + + if (confirmed) + { + BreakOfStructure bos; + bos.level = previous_high; + bos.time = current_time; + bos.is_bullish = true; + bos.confirmed = true; + + ArrayResize(bos_events, ArraySize(bos_events) + 1); + bos_events[ArraySize(bos_events) - 1] = bos; + + LogPattern("Break of Structure", symbol, StringFormat("Bullish BOS at %.5f", previous_high)); + break; } - } - } - - return true; -} + } + } + } -//+------------------------------------------------------------------+ -//| Main market analysis and trading logic | -//+------------------------------------------------------------------+ -void AnalyzeMarketAndTrade() { - // Step 1: Detect Liquidity Sweep - if(!g_liquiditySweep.DetectSweep(g_currentSymbol, PERIOD_M1)) { - return; - } - - // Step 2: Confirm Break of Structure - if(!g_breakOfStructure.DetectBOS(g_currentSymbol, PERIOD_M1)) { - return; - } - - // Step 3: Identify Fair Value Gap - if(!g_fairValueGap.DetectFVG(g_currentSymbol, PERIOD_M1)) { - return; - } - - // Step 4: Validate Order Block - if(!g_orderBlock.DetectOrderBlock(g_currentSymbol, PERIOD_M1)) { - return; - } - - // Step 5: AI Analysis (if enabled) - double aiConfidence = 1.0; - if(UseGrokAI && g_grokConnector != NULL) { - aiConfidence = g_grokConnector.GetConfidenceScore(); - if(aiConfidence < MinAIConfidence) { - g_logger.Info("AI confidence too low, skipping trade"); - return; - } - } - - // Step 6: Execute trade - ExecuteTrade(aiConfidence); -} + // Analyze bearish BOS (breaking below previous swing low) + for (int i = 1; i < ArraySize(swing_lows); i++) + { + double previous_low = swing_lows[i]; + datetime previous_time = swing_low_times[i]; -//+------------------------------------------------------------------+ -//| Execute trade based on analysis | -//+------------------------------------------------------------------+ -void ExecuteTrade(double aiConfidence) { - // Determine trade direction - ENUM_ORDER_TYPE orderType = g_breakOfStructure.GetTradeDirection(); - - // Calculate entry price - double entryPrice = g_orderBlock.GetEntryPrice(); - if(entryPrice <= 0) { - entryPrice = g_fairValueGap.GetMidpoint(); - } - - // Calculate stop loss - double stopLoss = g_stopLoss.Calculate(orderType, entryPrice); - - // Calculate take profit - double takeProfit = g_takeProfit.Calculate(orderType, entryPrice, stopLoss); - - // Calculate position size - double lotSize = g_positionSizing.Calculate(RiskPercent, MathAbs(entryPrice - stopLoss)); - - // Validate trade parameters - if(!ValidateTradeParameters(orderType, entryPrice, stopLoss, takeProfit, lotSize)) { - g_logger.Error("Invalid trade parameters, skipping trade"); - return; - } - - // Place the trade - if(PlaceTrade(orderType, lotSize, entryPrice, stopLoss, takeProfit, aiConfidence)) { - g_dailyTradeCount++; - g_dailyRisk += RiskPercent; - - // Draw trade lines if enabled - if(ShowTradeLines && g_chartObjects != NULL) { - g_chartObjects.DrawTradeLines(entryPrice, stopLoss, takeProfit); - } - - g_logger.Info(StringFormat("Trade executed: %s %.2f lots at %.5f", - EnumToString(orderType), lotSize, entryPrice)); - } -} + // Look for price breaking below this low + int start_bar = iBarShift(symbol, timeframe, previous_time); + if (start_bar < 0) + continue; -//+------------------------------------------------------------------+ -//| Place trade order | -//+------------------------------------------------------------------+ -bool PlaceTrade(ENUM_ORDER_TYPE orderType, double lotSize, double price, - double sl, double tp, double aiConfidence) { - - MqlTradeRequest request = {}; - MqlTradeResult result = {}; - - request.action = TRADE_ACTION_DEAL; - request.symbol = g_currentSymbol; - request.volume = lotSize; - request.type = orderType; - request.price = (orderType == ORDER_TYPE_BUY) ? SymbolInfoDouble(g_currentSymbol, SYMBOL_ASK) : - SymbolInfoDouble(g_currentSymbol, SYMBOL_BID); - request.sl = sl; - request.tp = tp; - request.deviation = Slippage; - request.magic = MagicNumber; - request.comment = StringFormat("%s_AI:%.2f", TradeComment, aiConfidence); - request.type_filling = ORDER_FILLING_IOC; - - bool success = OrderSend(request, result); - - if(success) { - g_logger.Info(StringFormat("Order placed successfully: Ticket %d", result.order)); - } else { - g_logger.Error(StringFormat("Order failed: %d - %s", result.retcode, result.comment)); - } - - return success; -} + for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) + { + double current_low = iLow(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); -//+------------------------------------------------------------------+ -//| Update daily trade count | -//+------------------------------------------------------------------+ -void UpdateDailyTradeCount() { - datetime currentDate = StringToTime(TimeToString(TimeCurrent(), TIME_DATE)); - - if(currentDate != g_lastTradeDate) { - g_dailyTradeCount = 0; - g_dailyRisk = 0.0; - g_lastTradeDate = currentDate; - g_logger.Info("New trading day started - resetting counters"); - } -} + if (current_low < previous_low && current_close < previous_low) + { + // Confirm the break with subsequent candles + bool confirmed = ConfirmBOS(symbol, timeframe, j, false, previous_low); -//+------------------------------------------------------------------+ -//| Update performance metrics | -//+------------------------------------------------------------------+ -void UpdatePerformanceMetrics() { - // Implementation will be added in the performance tracking module - g_performance.lastUpdate = TimeCurrent(); -} + if (confirmed) + { + BreakOfStructure bos; + bos.level = previous_low; + bos.time = current_time; + bos.is_bullish = false; + bos.confirmed = true; -//+------------------------------------------------------------------+ -//| Update visualization | -//+------------------------------------------------------------------+ -void UpdateVisualization() { - if(g_chartObjects == NULL) return; - - // Update market structure drawings - if(ShowOrderBlocks) { - g_chartObjects.UpdateOrderBlocks(); - } - - if(ShowFairValueGaps) { - g_chartObjects.UpdateFairValueGaps(); - } - - if(ShowBreakOfStructure) { - g_chartObjects.UpdateBreakOfStructure(); - } - - if(ShowLiquiditySweeps) { - g_chartObjects.UpdateLiquiditySweeps(); - } -} + ArrayResize(bos_events, ArraySize(bos_events) + 1); + bos_events[ArraySize(bos_events) - 1] = bos; -//+------------------------------------------------------------------+ -//| Check emergency stop conditions | -//+------------------------------------------------------------------+ -void CheckEmergencyStop() { - double currentDrawdown = CalculateCurrentDrawdown(); - - if(currentDrawdown >= MaxDrawdown) { - g_logger.Error(StringFormat("Emergency stop triggered: Drawdown %.2f%% >= %.2f%%", - currentDrawdown, MaxDrawdown)); - - // Close all positions - CloseAllPositions(); - - // Disable further trading - g_isInitialized = false; - } -} - -//+------------------------------------------------------------------+ -//| Calculate current drawdown | -//+------------------------------------------------------------------+ -double CalculateCurrentDrawdown() { - // Implementation will be added in the performance tracking module - return 0.0; -} - -//+------------------------------------------------------------------+ -//| Close all positions | -//+------------------------------------------------------------------+ -void CloseAllPositions() { - for(int i = PositionsTotal() - 1; i >= 0; i--) { - ulong ticket = PositionGetTicket(i); - if(PositionSelectByTicket(ticket)) { - if(PositionGetInteger(POSITION_MAGIC) == MagicNumber) { - MqlTradeRequest request = {}; - MqlTradeResult result = {}; - - request.action = TRADE_ACTION_DEAL; - request.symbol = PositionGetString(POSITION_SYMBOL); - request.volume = PositionGetDouble(POSITION_VOLUME); - request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? - ORDER_TYPE_SELL : ORDER_TYPE_BUY; - request.price = (request.type == ORDER_TYPE_SELL) ? - SymbolInfoDouble(request.symbol, SYMBOL_BID) : - SymbolInfoDouble(request.symbol, SYMBOL_ASK); - request.magic = MagicNumber; - request.comment = "Emergency Close"; - - OrderSend(request, result); + LogPattern("Break of Structure", symbol, StringFormat("Bearish BOS at %.5f", previous_low)); + break; } - } - } + } + } + } +} + +bool ConfirmBOS(string symbol, ENUM_TIMEFRAMES timeframe, int break_bar, bool is_bullish, double level) +{ + int confirmation_count = 0; + + // Check subsequent candles for confirmation + for (int i = 0; i < BOSConfirmationCandles && break_bar - i >= 0; i++) + { + double close_price = iClose(symbol, timeframe, break_bar - i); + + if (is_bullish) + { + if (close_price > level) + confirmation_count++; + } + else + { + if (close_price < level) + confirmation_count++; + } + } + + // Require at least 2 out of 3 confirmation candles + return confirmation_count >= MathMax(2, BOSConfirmationCandles / 2); +} + +bool IsBOSValid(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos) +{ + // Check if BOS is recent enough + datetime current_time = iTime(symbol, timeframe, 0); + int time_diff = (int)((current_time - bos.time) / PeriodSeconds(timeframe)); + + if (time_diff > BOSConfirmationCandles * 3) + return false; + + // Check if price is still respecting the BOS level + double current_price = iClose(symbol, timeframe, 0); + + if (bos.is_bullish) + { + return current_price > bos.level; + } + else + { + return current_price < bos.level; + } } //+------------------------------------------------------------------+ -//| Cleanup components | +//| Fair Value Gap Detection Functions | //+------------------------------------------------------------------+ -void CleanupComponents() { - // Delete news system components - if(g_newsManager != NULL) { delete g_newsManager; g_newsManager = NULL; } - if(g_fundamentalAnalysis != NULL) { delete g_fundamentalAnalysis; g_fundamentalAnalysis = NULL; } - if(g_newsFilter != NULL) { delete g_newsFilter; g_newsFilter = NULL; } - - // Delete all other components safely - if(g_orderBlock != NULL) { delete g_orderBlock; g_orderBlock = NULL; } - if(g_breakOfStructure != NULL) { delete g_breakOfStructure; g_breakOfStructure = NULL; } - if(g_liquiditySweep != NULL) { delete g_liquiditySweep; g_liquiditySweep = NULL; } - if(g_fairValueGap != NULL) { delete g_fairValueGap; g_fairValueGap = NULL; } - if(g_positionSizing != NULL) { delete g_positionSizing; g_positionSizing = NULL; } - if(g_stopLoss != NULL) { delete g_stopLoss; g_stopLoss = NULL; } - if(g_takeProfit != NULL) { delete g_takeProfit; g_takeProfit = NULL; } - if(g_tradingSessions != NULL) { delete g_tradingSessions; g_tradingSessions = NULL; } - if(g_sessionFilter != NULL) { delete g_sessionFilter; g_sessionFilter = NULL; } - if(g_grokConnector != NULL) { delete g_grokConnector; g_grokConnector = NULL; } - if(g_sentimentAnalysis != NULL) { delete g_sentimentAnalysis; g_sentimentAnalysis = NULL; } - if(g_config != NULL) { delete g_config; g_config = NULL; } - if(g_logger != NULL) { delete g_logger; g_logger = NULL; } +bool DetectFairValueGaps(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg_array[]) +{ + ArrayResize(fvg_array, 0); + + int bars_to_analyze = MathMin(50, iBars(symbol, timeframe) - 5); + if (bars_to_analyze < 10) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Fair Value Gaps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + double pip_value = CalculatePipValue(symbol); + double min_gap_size = MinFVGSize * pip_value; + + // Look for FVG patterns (3-candle pattern) + for (int i = 2; i < bars_to_analyze; i++) + { + // Get three consecutive candles + double high1 = iHigh(symbol, timeframe, i); // First candle + double low1 = iLow(symbol, timeframe, i); + double high2 = iHigh(symbol, timeframe, i - 1); // Middle candle (impulse) + double low2 = iLow(symbol, timeframe, i - 1); + double high3 = iHigh(symbol, timeframe, i - 2); // Third candle + double low3 = iLow(symbol, timeframe, i - 2); + + datetime gap_time = iTime(symbol, timeframe, i - 1); + + // Check for bullish FVG (gap between candle 1 high and candle 3 low) + if (low3 > high1) + { + double gap_size = low3 - high1; + if (gap_size >= min_gap_size) + { + FairValueGap fvg; + fvg.top = low3; + fvg.bottom = high1; + fvg.time = gap_time; + fvg.is_bullish = true; + fvg.is_filled = IsFVGFilled(symbol, timeframe, i - 2, fvg.top, fvg.bottom, true); + + if (!fvg.is_filled) + { + ArrayResize(fvg_array, ArraySize(fvg_array) + 1); + fvg_array[ArraySize(fvg_array) - 1] = fvg; + + LogPattern("Fair Value Gap", symbol, StringFormat("Bullish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); + } + } + } + + // Check for bearish FVG (gap between candle 1 low and candle 3 high) + if (high3 < low1) + { + double gap_size = low1 - high3; + if (gap_size >= min_gap_size) + { + FairValueGap fvg; + fvg.top = low1; + fvg.bottom = high3; + fvg.time = gap_time; + fvg.is_bullish = false; + fvg.is_filled = IsFVGFilled(symbol, timeframe, i - 2, fvg.top, fvg.bottom, false); + + if (!fvg.is_filled) + { + ArrayResize(fvg_array, ArraySize(fvg_array) + 1); + fvg_array[ArraySize(fvg_array) - 1] = fvg; + + LogPattern("Fair Value Gap", symbol, StringFormat("Bearish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); + } + } + } + } + + LogDebug(StringFormat("Found %d unfilled FVGs on %s %s", ArraySize(fvg_array), symbol, EnumToString(timeframe))); + return ArraySize(fvg_array) > 0; +} + +bool IsFVGFilled(string symbol, ENUM_TIMEFRAMES timeframe, int start_bar, double top, double bottom, bool is_bullish) +{ + // Check if price has filled the FVG since its formation + for (int i = 0; i < start_bar; i++) + { + double high = iHigh(symbol, timeframe, i); + double low = iLow(symbol, timeframe, i); + + if (is_bullish) + { + // For bullish FVG, check if price came back down to fill the gap + if (low <= bottom) + return true; + } + else + { + // For bearish FVG, check if price came back up to fill the gap + if (high >= top) + return true; + } + } + + return false; +} + +bool IsFVGValid(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg) +{ + // Check if FVG is still unfilled + if (fvg.is_filled) + return false; + + // Check current price position relative to FVG + double current_price = iClose(symbol, timeframe, 0); + + if (fvg.is_bullish) + { + // For bullish FVG, price should be above the gap + return current_price > fvg.top; + } + else + { + // For bearish FVG, price should be below the gap + return current_price < fvg.bottom; + } +} + +double GetFVGMidpoint(FairValueGap &fvg) +{ + return (fvg.top + fvg.bottom) / 2.0; +} + +bool IsPriceInFVG(double price, FairValueGap &fvg) +{ + return price >= fvg.bottom && price <= fvg.top; +} + +void UpdateFVGStatus(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg_array[]) +{ + // Update the filled status of existing FVGs + for (int i = 0; i < ArraySize(fvg_array); i++) + { + if (!fvg_array[i].is_filled) + { + double current_high = iHigh(symbol, timeframe, 0); + double current_low = iLow(symbol, timeframe, 0); + + if (fvg_array[i].is_bullish) + { + if (current_low <= fvg_array[i].bottom) + { + fvg_array[i].is_filled = true; + LogPattern("Fair Value Gap", symbol, "Bullish FVG filled"); + } + } + else + { + if (current_high >= fvg_array[i].top) + { + fvg_array[i].is_filled = true; + LogPattern("Fair Value Gap", symbol, "Bearish FVG filled"); + } + } + } + } } //+------------------------------------------------------------------+ -//| Utility functions (to be implemented) | +//| Liquidity Sweep Detection Functions | //+------------------------------------------------------------------+ -bool IsMarketOpen() { return true; } // Placeholder -bool ValidateTradeParameters(ENUM_ORDER_TYPE type, double entry, double sl, double tp, double lots) { return true; } // Placeholder +bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep_array[]) +{ + ArrayResize(sweep_array, 0); + + int bars_to_analyze = MathMin(100, iBars(symbol, timeframe) - 10); + if (bars_to_analyze < 20) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Liquidity Sweeps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + double pip_value = CalculatePipValue(symbol); + double min_sweep_distance = MinSweepDistance * pip_value; + + // Find equal highs and lows first + double equal_highs[]; + double equal_lows[]; + datetime equal_high_times[]; + datetime equal_low_times[]; + + FindEqualHighsLows(symbol, timeframe, bars_to_analyze, equal_highs, equal_lows, equal_high_times, equal_low_times); + + // Look for liquidity sweeps above equal highs + for (int i = 0; i < ArraySize(equal_highs); i++) + { + double equal_high = equal_highs[i]; + datetime equal_time = equal_high_times[i]; + + int equal_bar = iBarShift(symbol, timeframe, equal_time); + if (equal_bar < 0) + continue; + + // Look for sweep above this equal high + for (int j = 0; j < equal_bar && j < 20; j++) + { + double current_high = iHigh(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + // Check if price swept above equal high + if (current_high > equal_high + min_sweep_distance) + { + // Check for rejection (close back below equal high) + if (current_close < equal_high) + { + LiquiditySweep sweep; + sweep.level = equal_high; + sweep.time = current_time; + sweep.is_high_sweep = true; + sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, true, equal_high); + + if (sweep.confirmed) + { + ArrayResize(sweep_array, ArraySize(sweep_array) + 1); + sweep_array[ArraySize(sweep_array) - 1] = sweep; + + LogPattern("Liquidity Sweep", symbol, StringFormat("High sweep at %.5f, Distance: %.1f pips", equal_high, (current_high - equal_high) / pip_value)); + } + break; + } + } + } + } + + // Look for liquidity sweeps below equal lows + for (int i = 0; i < ArraySize(equal_lows); i++) + { + double equal_low = equal_lows[i]; + datetime equal_time = equal_low_times[i]; + + int equal_bar = iBarShift(symbol, timeframe, equal_time); + if (equal_bar < 0) + continue; + + // Look for sweep below this equal low + for (int j = 0; j < equal_bar && j < 20; j++) + { + double current_low = iLow(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + // Check if price swept below equal low + if (current_low < equal_low - min_sweep_distance) + { + // Check for rejection (close back above equal low) + if (current_close > equal_low) + { + LiquiditySweep sweep; + sweep.level = equal_low; + sweep.time = current_time; + sweep.is_high_sweep = false; + sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, false, equal_low); + + if (sweep.confirmed) + { + ArrayResize(sweep_array, ArraySize(sweep_array) + 1); + sweep_array[ArraySize(sweep_array) - 1] = sweep; + + LogPattern("Liquidity Sweep", symbol, StringFormat("Low sweep at %.5f, Distance: %.1f pips", equal_low, (equal_low - current_low) / pip_value)); + } + break; + } + } + } + } + + LogDebug(StringFormat("Found %d Liquidity Sweeps on %s %s", ArraySize(sweep_array), symbol, EnumToString(timeframe))); + return ArraySize(sweep_array) > 0; +} + +void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, + double &equal_highs[], double &equal_lows[], + datetime &equal_high_times[], datetime &equal_low_times[]) +{ + ArrayResize(equal_highs, 0); + ArrayResize(equal_lows, 0); + ArrayResize(equal_high_times, 0); + ArrayResize(equal_low_times, 0); + + double pip_value = CalculatePipValue(symbol); + double tolerance = 2.0 * pip_value; // 2 pip tolerance for "equal" levels + + // Find swing points first + double swing_highs[]; + double swing_lows[]; + datetime swing_high_times[]; + datetime swing_low_times[]; + + FindSwingPoints(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times); + + // Find equal highs + for (int i = 0; i < ArraySize(swing_highs); i++) + { + double current_high = swing_highs[i]; + datetime current_time = swing_high_times[i]; + int equal_count = 1; + + // Count how many swing highs are at similar level + for (int j = i + 1; j < ArraySize(swing_highs); j++) + { + if (MathAbs(swing_highs[j] - current_high) <= tolerance) + { + equal_count++; + } + } + + // If we have at least 2 equal highs, add to array + if (equal_count >= 2) + { + // Check if this level is already in the array + bool already_exists = false; + for (int k = 0; k < ArraySize(equal_highs); k++) + { + if (MathAbs(equal_highs[k] - current_high) <= tolerance) + { + already_exists = true; + break; + } + } + + if (!already_exists) + { + ArrayResize(equal_highs, ArraySize(equal_highs) + 1); + ArrayResize(equal_high_times, ArraySize(equal_high_times) + 1); + equal_highs[ArraySize(equal_highs) - 1] = current_high; + equal_high_times[ArraySize(equal_high_times) - 1] = current_time; + } + } + } + + // Find equal lows + for (int i = 0; i < ArraySize(swing_lows); i++) + { + double current_low = swing_lows[i]; + datetime current_time = swing_low_times[i]; + int equal_count = 1; + + // Count how many swing lows are at similar level + for (int j = i + 1; j < ArraySize(swing_lows); j++) + { + if (MathAbs(swing_lows[j] - current_low) <= tolerance) + { + equal_count++; + } + } + + // If we have at least 2 equal lows, add to array + if (equal_count >= 2) + { + // Check if this level is already in the array + bool already_exists = false; + for (int k = 0; k < ArraySize(equal_lows); k++) + { + if (MathAbs(equal_lows[k] - current_low) <= tolerance) + { + already_exists = true; + break; + } + } + + if (!already_exists) + { + ArrayResize(equal_lows, ArraySize(equal_lows) + 1); + ArrayResize(equal_low_times, ArraySize(equal_low_times) + 1); + equal_lows[ArraySize(equal_lows) - 1] = current_low; + equal_low_times[ArraySize(equal_low_times) - 1] = current_time; + } + } + } +} + +bool ConfirmLiquiditySweep(string symbol, ENUM_TIMEFRAMES timeframe, int sweep_bar, bool is_high_sweep, double level) +{ + // Check for strong rejection after the sweep + double sweep_high = iHigh(symbol, timeframe, sweep_bar); + double sweep_low = iLow(symbol, timeframe, sweep_bar); + double sweep_close = iClose(symbol, timeframe, sweep_bar); + + if (is_high_sweep) + { + // For high sweep, look for bearish rejection + double wick_size = sweep_high - sweep_close; + double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); + + // Wick should be at least 2x the body size + if (wick_size < body_size * 2) + return false; + + // Close should be below the swept level + if (sweep_close >= level) + return false; + } + else + { + // For low sweep, look for bullish rejection + double wick_size = sweep_close - sweep_low; + double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); + + // Wick should be at least 2x the body size + if (wick_size < body_size * 2) + return false; + + // Close should be above the swept level + if (sweep_close <= level) + return false; + } + + return true; +} + +bool IsLiquiditySweepValid(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep) +{ + if (!sweep.confirmed) + return false; + + // Check if sweep is recent enough + datetime current_time = iTime(symbol, timeframe, 0); + int time_diff = (int)((current_time - sweep.time) / PeriodSeconds(timeframe)); + + if (time_diff > 10) + return false; // Must be within last 10 candles + + // Check current price position + double current_price = iClose(symbol, timeframe, 0); + + if (sweep.is_high_sweep) + { + // For high sweep, price should be below the swept level + return current_price < sweep.level; + } + else + { + // For low sweep, price should be above the swept level + return current_price > sweep.level; + } +} -//------------------------------------------------------------------+ -//| Walk-Forward Optimization Parameters | //+------------------------------------------------------------------+ -input group "=== Walk-Forward Optimization ===" -input bool WF_EnableOptimization = false; // Enable walk-forward optimization -input ENUM_WF_OPTIMIZATION_TYPE WF_OptimizationType = WF_OPT_GENETIC_ALGORITHM; // Optimization method -input ENUM_WF_FITNESS_FUNCTION WF_FitnessFunction = WF_FITNESS_SHARPE_RATIO; // Fitness function -input int WF_TrainPeriodDays = 252; // Training period (days) -input int WF_TestPeriodDays = 63; // Testing period (days) -input int WF_StepDays = 21; // Step size (days) -input int WF_MaxIterations = 100; // Maximum iterations -input int WF_PopulationSize = 50; // Population size -input double WF_ConvergenceThreshold = 0.001; // Convergence threshold -input bool WF_AutoApplyResults = true; // Auto-apply optimization results +//| Multi-Timeframe Analysis Engine | +//+------------------------------------------------------------------+ +struct MarketStructureData +{ + OrderBlock order_blocks[]; + FairValueGap fair_value_gaps[]; + BreakOfStructure bos_events[]; + LiquiditySweep liquidity_sweeps[]; + ENUM_TIMEFRAMES timeframe; + datetime last_update; + bool is_valid; +}; -// Walk-forward optimization state -bool g_optimizationRunning; -datetime g_lastOptimizationTime; -SWFOptimizationResult g_currentOptimizationResult; +// Global market structure data for different timeframes +MarketStructureData MTF_Data_M1; +MarketStructureData MTF_Data_M15; +MarketStructureData MTF_Data_H4; +MarketStructureData MTF_Data_D1; +MarketStructureData MTF_Data_W1; -//--- Market Regime Detection Settings -input group "=== Market Regime Detection ===" -input bool EnableRegimeDetection = true; // Enable market regime detection -input ENUM_REGIME_DETECTION_METHOD RegimeDetectionMethod = DETECTION_COMPOSITE; // Detection method -input int RegimeLookbackPeriod = 50; // Lookback period for regime analysis -input double TrendThreshold = 0.6; // Trend strength threshold -input double VolatilityThreshold = 1.5; // Volatility threshold -input bool UseMultiTimeframeRegime = true; // Use multi-timeframe regime analysis -input ENUM_TIMEFRAMES RegimeHigherTimeframe = PERIOD_H4; // Higher timeframe for regime confirmation +bool InitializeMultiTimeframeAnalysis() +{ + LogInfo("Initializing Multi-Timeframe Analysis Engine"); -// Adaptive parameter optimization settings -input group "=== Adaptive Parameter Optimization ===" -input bool EnableAdaptiveOptimization = true; // Enable adaptive parameter optimization -input ENUM_ADAPTATION_TRIGGER AdaptationTrigger = ADAPTATION_PERFORMANCE; // Adaptation trigger -input int AdaptationPeriod = 24; // Adaptation period (hours) -input double PerformanceThreshold = 0.05; // Performance threshold for adaptation -input int MinTradesForAdaptation = 10; // Minimum trades for adaptation -input bool UseMarketRegimeDetection = true; // Use market regime detection + // Initialize timeframe data structures + MTF_Data_M1.timeframe = PERIOD_M1; + MTF_Data_M1.is_valid = false; + MTF_Data_M1.last_update = 0; -//--- Component Communication -input group "Component Communication Settings" -input bool EnableComponentComm = true; // Enable component communication -input bool EnableAsyncComm = true; // Enable asynchronous communication -input bool EnableBroadcast = true; // Enable broadcast messages -input bool EnableCommLogging = false; // Enable communication logging -input int MaxQueueSize = 1000; // Maximum message queue size -input int MessageTimeout = 5000; // Message timeout (ms) -input int BatchSize = 10; // Message batch size + MTF_Data_M15.timeframe = PERIOD_M15; + MTF_Data_M15.is_valid = false; + MTF_Data_M15.last_update = 0; -//--- Core Components -CLogger* g_logger; -CCacheManager* g_cacheManager; -CMemoryOptimizer* g_memoryOptimizer; -CComponentCommunicator* g_communicator; // Component communicator -CMarketRegimeDetector* g_regimeDetector; // Market regime detector -CAdaptiveParameterOptimizer* g_adaptiveOptimizer; // Adaptive parameter optimizer -CWalkForwardOptimizer* g_walkForwardOptimizer; + MTF_Data_H4.timeframe = PERIOD_H4; + MTF_Data_H4.is_valid = false; + MTF_Data_H4.last_update = 0; -//--- Market regime state -ENUM_MARKET_REGIME g_currentRegime = REGIME_UNKNOWN; -ENUM_MARKET_REGIME g_previousRegime = REGIME_UNKNOWN; -datetime g_lastRegimeUpdate = 0; -double g_regimeStrength = 0.0; -double g_regimeConfidence = 0.0; + MTF_Data_D1.timeframe = PERIOD_D1; + MTF_Data_D1.is_valid = false; + MTF_Data_D1.last_update = 0; -// Adaptive optimization state -bool g_adaptiveOptimizationRunning = false; -datetime g_lastAdaptationTime = 0; -SAdaptationResults g_currentAdaptationResults; + MTF_Data_W1.timeframe = PERIOD_W1; + MTF_Data_W1.is_valid = false; + MTF_Data_W1.last_update = 0; -//--- Component Communication State -bool m_commInitialized; // Communication system initialized -datetime m_lastCommCheck; // Last communication check time -int m_totalMessagesProcessed; // Total messages processed -double m_avgCommLatency; // Average communication latency \ No newline at end of file + LogInfo("Multi-Timeframe Analysis Engine initialized successfully"); + return true; +} + +bool UpdateMultiTimeframeAnalysis(string symbol) +{ + LogDebug("Updating Multi-Timeframe Analysis for " + symbol); + + bool updated = false; + + // Update M1 analysis (most frequent) + if (IsNewBar(symbol, PERIOD_M1) || !MTF_Data_M1.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_M1); + } + + // Update M15 analysis + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_M15) || !MTF_Data_M15.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_M15); + } + + // Update H4 analysis + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_H4) || !MTF_Data_H4.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_H4); + } + + // Update D1 analysis + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_D1) || !MTF_Data_D1.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_D1); + } + + // Update W1 analysis (least frequent) + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_W1) || !MTF_Data_W1.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_W1); + } + + if (updated) + { + LogDebug("Multi-Timeframe Analysis updated for " + symbol); + } + + return updated; +} + +bool IsTimeframeUpdateNeeded(string symbol, MarketStructureData &mtf_data) +{ + datetime current_bar_time = iTime(symbol, mtf_data.timeframe, 0); + return current_bar_time != mtf_data.last_update; +} + +bool UpdateTimeframeData(string symbol, MarketStructureData &mtf_data) +{ + LogDebug(StringFormat("Updating %s analysis for %s", EnumToString(mtf_data.timeframe), symbol)); + + bool success = true; + + // Update Order Blocks + success &= DetectOrderBlocks(symbol, mtf_data.timeframe, mtf_data.order_blocks); + + // Update Fair Value Gaps + success &= DetectFairValueGaps(symbol, mtf_data.timeframe, mtf_data.fair_value_gaps); + + // Update Break of Structure events + success &= DetectBreakOfStructure(symbol, mtf_data.timeframe, mtf_data.bos_events); + + // Update Liquidity Sweeps + success &= DetectLiquiditySweeps(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps); + + // Update metadata + mtf_data.last_update = iTime(symbol, mtf_data.timeframe, 0); + mtf_data.is_valid = success; + + if (success) + { + LogDebug(StringFormat("%s analysis completed: OB=%d, FVG=%d, BOS=%d, Sweeps=%d", + EnumToString(mtf_data.timeframe), + ArraySize(mtf_data.order_blocks), + ArraySize(mtf_data.fair_value_gaps), + ArraySize(mtf_data.bos_events), + ArraySize(mtf_data.liquidity_sweeps))); + } + + return success; +} + +string GetMarketBias(string symbol) +{ + // Analyze higher timeframes for overall market bias + string h4_bias = GetTimeframeBias(symbol, MTF_Data_H4); + string d1_bias = GetTimeframeBias(symbol, MTF_Data_D1); + string w1_bias = GetTimeframeBias(symbol, MTF_Data_W1); + + // Weight the biases (Weekly > Daily > H4) + if (w1_bias == d1_bias && d1_bias == h4_bias) + { + return w1_bias; // All timeframes agree + } + else if (w1_bias == d1_bias) + { + return w1_bias; // Higher timeframes agree + } + else if (d1_bias == h4_bias) + { + return d1_bias; // Lower timeframes agree + } + else + { + return w1_bias; // Default to highest timeframe + } +} + +string GetTimeframeBias(string symbol, MarketStructureData &mtf_data) +{ + if (!mtf_data.is_valid) + return "NEUTRAL"; + + int bullish_signals = 0; + int bearish_signals = 0; + + // Analyze BOS events + for (int i = 0; i < ArraySize(mtf_data.bos_events); i++) + { + if (IsBOSValid(symbol, mtf_data.timeframe, mtf_data.bos_events[i])) + { + if (mtf_data.bos_events[i].is_bullish) + bullish_signals++; + else + bearish_signals++; + } + } + + // Analyze Order Blocks + for (int i = 0; i < ArraySize(mtf_data.order_blocks); i++) + { + if (mtf_data.order_blocks[i].is_fresh && mtf_data.order_blocks[i].strength > OBStrengthFilter) + { + if (mtf_data.order_blocks[i].is_bullish) + bullish_signals++; + else + bearish_signals++; + } + } + + // Analyze Liquidity Sweeps + for (int i = 0; i < ArraySize(mtf_data.liquidity_sweeps); i++) + { + if (IsLiquiditySweepValid(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps[i])) + { + if (mtf_data.liquidity_sweeps[i].is_high_sweep) + bearish_signals++; // High sweep typically leads to bearish move + else + bullish_signals++; // Low sweep typically leads to bullish move + } + } + + // Determine bias + if (bullish_signals > bearish_signals + 1) + return "BULLISH"; + else if (bearish_signals > bullish_signals + 1) + return "BEARISH"; + else + return "NEUTRAL"; +} + +bool IsMultiTimeframeAligned(string symbol, bool is_bullish_setup) +{ + if (!RequireMultiTFConfirmation) + return true; + + string market_bias = GetMarketBias(symbol); + + if (is_bullish_setup) + { + return market_bias == "BULLISH" || market_bias == "NEUTRAL"; + } + else + { + return market_bias == "BEARISH" || market_bias == "NEUTRAL"; + } +} + +void CopyMarketStructureData(const MarketStructureData &source, MarketStructureData &dest) +{ + // Copy arrays + ArrayResize(dest.order_blocks, ArraySize(source.order_blocks)); + ArrayCopy(dest.order_blocks, source.order_blocks); + + ArrayResize(dest.fair_value_gaps, ArraySize(source.fair_value_gaps)); + ArrayCopy(dest.fair_value_gaps, source.fair_value_gaps); + + ArrayResize(dest.bos_events, ArraySize(source.bos_events)); + ArrayCopy(dest.bos_events, source.bos_events); + + ArrayResize(dest.liquidity_sweeps, ArraySize(source.liquidity_sweeps)); + ArrayCopy(dest.liquidity_sweeps, source.liquidity_sweeps); + + // Copy simple fields + dest.timeframe = source.timeframe; + dest.last_update = source.last_update; + dest.is_valid = source.is_valid; +} + +bool GetTimeframeData(ENUM_TIMEFRAMES timeframe, MarketStructureData &mtf_data) +{ + switch (timeframe) + { + case PERIOD_M1: + CopyMarketStructureData(MTF_Data_M1, mtf_data); + return true; + case PERIOD_M15: + CopyMarketStructureData(MTF_Data_M15, mtf_data); + return true; + case PERIOD_H4: + CopyMarketStructureData(MTF_Data_H4, mtf_data); + return true; + case PERIOD_D1: + CopyMarketStructureData(MTF_Data_D1, mtf_data); + return true; + case PERIOD_W1: + CopyMarketStructureData(MTF_Data_W1, mtf_data); + return true; + default: + return false; + } +} + +void PrintMultiTimeframeStatus(string symbol) +{ + if (!EnableDebugMode) + return; + + string status = StringFormat( + "=== Multi-Timeframe Status for %s ===\n" + + "Market Bias: %s\n" + + "M1 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "M15 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "H4 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "D1 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "W1 - OB:%d FVG:%d BOS:%d Sweeps:%d", + symbol, + GetMarketBias(symbol), + ArraySize(MTF_Data_M1.order_blocks), ArraySize(MTF_Data_M1.fair_value_gaps), ArraySize(MTF_Data_M1.bos_events), ArraySize(MTF_Data_M1.liquidity_sweeps), + ArraySize(MTF_Data_M15.order_blocks), ArraySize(MTF_Data_M15.fair_value_gaps), ArraySize(MTF_Data_M15.bos_events), ArraySize(MTF_Data_M15.liquidity_sweeps), + ArraySize(MTF_Data_H4.order_blocks), ArraySize(MTF_Data_H4.fair_value_gaps), ArraySize(MTF_Data_H4.bos_events), ArraySize(MTF_Data_H4.liquidity_sweeps), + ArraySize(MTF_Data_D1.order_blocks), ArraySize(MTF_Data_D1.fair_value_gaps), ArraySize(MTF_Data_D1.bos_events), ArraySize(MTF_Data_D1.liquidity_sweeps), + ArraySize(MTF_Data_W1.order_blocks), ArraySize(MTF_Data_W1.fair_value_gaps), ArraySize(MTF_Data_W1.bos_events), ArraySize(MTF_Data_W1.liquidity_sweeps)); + + LogDebug(status); +} diff --git a/src/SniperEA_backup.mq5 b/src/SniperEA_backup.mq5 new file mode 100644 index 0000000..bc09285 --- /dev/null +++ b/src/SniperEA_backup.mq5 @@ -0,0 +1,1856 @@ +//+------------------------------------------------------------------+ +//| SniperEA.mq5 | +//| MT5 Sniper Strategy Expert Advisor | +//| OB + BOS + Liquidity Sweep + FVG | +//+------------------------------------------------------------------+ +#property copyright "Sniper Strategy EA" +#property link "" +#property version "1.00" +#property strict + +//--- Include files +#include +#include +#include + +//--- Global objects +CTrade trade; +CPositionInfo position; +COrderInfo order; + +//--- Input parameters +input group "=== Core Settings ===" input int MaxTradesPerDay = 3; // Maximum trades per symbol per day +input double RiskPercent = 1.0; // Risk percentage per trade +input double MinRR = 2.0; // Minimum risk-reward ratio +input bool UseTimeFilter = true; // Enable session filtering + +input group "=== Session Settings ===" input string AsiaStart = "00:00"; // Asia session start (GMT) +input string AsiaEnd = "09:00"; // Asia session end (GMT) +input string LondonStart = "08:00"; // London session start (GMT) +input string LondonEnd = "17:00"; // London session end (GMT) +input string NYStart = "13:00"; // New York session start (GMT) +input string NYEnd = "22:00"; // New York session end (GMT) + +input group "=== Risk Management ===" input int MaxSL = 50; // Maximum stop loss in pips +input int MinSL = 10; // Minimum stop loss in pips +input double MaxSlippage = 2.0; // Maximum slippage in pips +input int MaxPositions = 10; // Maximum total positions +input int MaxPositionsPerSymbol = 3; // Maximum positions per symbol + +input group "=== Pattern Detection ===" input int OBLookback = 20; // Order Block lookback candles +input double MinFVGSize = 3.0; // Minimum FVG size in pips +input double MinSweepDistance = 5.0; // Minimum sweep distance in pips +input int BOSConfirmationCandles = 3; // BOS confirmation within candles +input int SwingLookback = 10; // Swing high/low lookback period +input double OBStrengthFilter = 0.5; // Order Block strength filter (0-1) +input bool RequireMultiTFConfirmation = true; // Require multi-timeframe confirmation + +input group "=== Visualization ===" input bool ShowOrderBlocks = true; // Show Order Block zones +input bool ShowFVG = true; // Show Fair Value Gaps +input bool ShowBOS = true; // Show Break of Structure +input bool ShowSweeps = true; // Show Liquidity Sweeps +input bool ShowTradeLevels = true; // Show Entry/SL/TP levels + +input group "=== Symbols to Trade ===" input string Symbol1 = "EURUSD"; // Symbol 1 +input string Symbol2 = "GBPUSD"; // Symbol 2 +input string Symbol3 = "USDJPY"; // Symbol 3 +input string Symbol4 = "USDCHF"; // Symbol 4 +input string Symbol5 = "AUDUSD"; // Symbol 5 +input string Symbol6 = "USDCAD"; // Symbol 6 +input string Symbol7 = "NZDUSD"; // Symbol 7 +input string Symbol8 = "XAUUSD"; // Symbol 8 (Gold) + +input group "=== Logging & Debug ===" input bool EnableDetailedLogging = true; // Enable detailed logging +input bool EnableDebugMode = false; // Enable debug mode +input bool LogPatternDetection = true; // Log pattern detection events +input bool LogTradeExecution = true; // Log trade execution details + +//--- Global variables +string SymbolsToTrade[]; +int TotalSymbols = 0; +datetime LastBarTime = 0; +bool IsInitialized = false; +string LogPrefix = "SniperEA"; +int LogLevel = 0; // 0=Info, 1=Warning, 2=Error, 3=Debug + +//--- Structure definitions +struct OrderBlock +{ + double high; + double low; + datetime time; + bool is_bullish; + bool is_fresh; + int strength; +}; + +struct FairValueGap +{ + double top; + double bottom; + datetime time; + bool is_bullish; + bool is_filled; +}; + +struct LiquiditySweep +{ + double level; + datetime time; + bool is_high_sweep; + bool confirmed; +}; + +struct BreakOfStructure +{ + double level; + datetime time; + bool is_bullish; + bool confirmed; +}; + +//+------------------------------------------------------------------+ +//| Expert initialization function | +//+------------------------------------------------------------------+ +int OnInit() +{ + Print("=== Sniper EA Initialization Started ==="); + + // Initialize trade object + trade.SetExpertMagicNumber(123456); + trade.SetDeviationInPoints((int)(MaxSlippage * 10)); + trade.SetTypeFilling(ORDER_FILLING_FOK); + + // Setup symbols array + if (!SetupSymbolsArray()) + { + Print("ERROR: Failed to setup symbols array"); + return INIT_FAILED; + } + + // Validate input parameters + if (!ValidateInputs()) + { + Print("ERROR: Invalid input parameters"); + return INIT_FAILED; + } + + // Initialize chart objects + if (!InitializeChartObjects()) + { + Print("ERROR: Failed to initialize chart objects"); + return INIT_FAILED; + } + + // Initialize multi-timeframe analysis + if (!InitializeMultiTimeframeAnalysis()) + { + Print("ERROR: Failed to initialize multi-timeframe analysis"); + return INIT_FAILED; + } + + IsInitialized = true; + LastBarTime = iTime(_Symbol, PERIOD_M1, 0); + + Print("=== Sniper EA Initialization Completed Successfully ==="); + Print("Trading Symbols: ", TotalSymbols); + Print("Risk per Trade: ", RiskPercent, "%"); + Print("Minimum R:R Ratio: ", MinRR, ":1"); + + return INIT_SUCCEEDED; +} + +//+------------------------------------------------------------------+ +//| Expert deinitialization function | +//+------------------------------------------------------------------+ +void OnDeinit(const int reason) +{ + Print("=== Sniper EA Deinitialization Started ==="); + + // Clean up chart objects + CleanupChartObjects(); + + // Print deinitialization reason + string deinit_reason = ""; + switch (reason) + { + case REASON_PROGRAM: + deinit_reason = "Expert Advisor terminated"; + break; + case REASON_REMOVE: + deinit_reason = "Expert Advisor removed from chart"; + break; + case REASON_RECOMPILE: + deinit_reason = "Expert Advisor recompiled"; + break; + case REASON_CHARTCHANGE: + deinit_reason = "Chart symbol or period changed"; + break; + case REASON_CHARTCLOSE: + deinit_reason = "Chart closed"; + break; + case REASON_PARAMETERS: + deinit_reason = "Input parameters changed"; + break; + case REASON_ACCOUNT: + deinit_reason = "Account changed"; + break; + default: + deinit_reason = "Unknown reason"; + break; + } + + Print("Deinitialization Reason: ", deinit_reason); + Print("=== Sniper EA Deinitialization Completed ==="); +} + +//+------------------------------------------------------------------+ +//| Expert tick function | +//+------------------------------------------------------------------+ +void OnTick() +{ + if (!IsInitialized) + return; + + // Check for new bar + datetime current_bar_time = iTime(_Symbol, PERIOD_M1, 0); + if (current_bar_time == LastBarTime) + return; + + LastBarTime = current_bar_time; + + // Main trading logic will be implemented here + ProcessTradingLogic(); +} + +//+------------------------------------------------------------------+ +//| Setup symbols array from input parameters | +//+------------------------------------------------------------------+ +bool SetupSymbolsArray() +{ + ArrayResize(SymbolsToTrade, 0); + TotalSymbols = 0; + + string symbols[8] = {Symbol1, Symbol2, Symbol3, Symbol4, Symbol5, Symbol6, Symbol7, Symbol8}; + + for (int i = 0; i < 8; i++) + { + if (symbols[i] != "" && symbols[i] != "NONE") + { + ArrayResize(SymbolsToTrade, TotalSymbols + 1); + SymbolsToTrade[TotalSymbols] = symbols[i]; + TotalSymbols++; + } + } + + return TotalSymbols > 0; +} + +//+------------------------------------------------------------------+ +//| Validate input parameters | +//+------------------------------------------------------------------+ +bool ValidateInputs() +{ + if (RiskPercent <= 0 || RiskPercent > 10) + { + Print("ERROR: Risk percent must be between 0 and 10"); + return false; + } + + if (MinRR < 1.0) + { + Print("ERROR: Minimum R:R ratio must be at least 1.0"); + return false; + } + + if (MaxSL <= MinSL) + { + Print("ERROR: Maximum SL must be greater than Minimum SL"); + return false; + } + + if (MaxTradesPerDay <= 0) + { + Print("ERROR: Max trades per day must be positive"); + return false; + } + + return true; +} + +//+------------------------------------------------------------------+ +//| Initialize chart objects | +//+------------------------------------------------------------------+ +bool InitializeChartObjects() +{ + // Set chart properties for better visualization + ChartSetInteger(0, CHART_SHOW_GRID, false); + ChartSetInteger(0, CHART_SHOW_VOLUMES, false); + ChartSetInteger(0, CHART_SHOW_OHLC, true); + + // Create information panel background + if (ObjectCreate(0, "SniperEA_InfoPanel", OBJ_RECTANGLE_LABEL, 0, 0, 0)) + { + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_XDISTANCE, 10); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_YDISTANCE, 30); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_XSIZE, 250); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_YSIZE, 150); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BGCOLOR, clrDarkSlateGray); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BORDER_TYPE, BORDER_FLAT); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_COLOR, clrWhite); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_STYLE, STYLE_SOLID); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_WIDTH, 1); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_BACK, false); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_SELECTED, false); + ObjectSetInteger(0, "SniperEA_InfoPanel", OBJPROP_HIDDEN, true); + } + + // Create EA status label + if (ObjectCreate(0, "SniperEA_Status", OBJ_LABEL, 0, 0, 0)) + { + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_XDISTANCE, 20); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_YDISTANCE, 40); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_COLOR, clrLime); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_FONTSIZE, 10); + ObjectSetString(0, "SniperEA_Status", OBJPROP_FONT, "Arial Bold"); + ObjectSetString(0, "SniperEA_Status", OBJPROP_TEXT, "Sniper EA - ACTIVE"); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_SELECTED, false); + ObjectSetInteger(0, "SniperEA_Status", OBJPROP_HIDDEN, true); + } + + Print("Chart objects initialized successfully"); + return true; +} + +//+------------------------------------------------------------------+ +//| Clean up chart objects | +//+------------------------------------------------------------------+ +void CleanupChartObjects() +{ + // Clean up all chart objects created by the EA + int total_objects = ObjectsDeleteAll(0, "SniperEA_"); + Print("Cleaned up ", total_objects, " chart objects"); +} + +//+------------------------------------------------------------------+ +//| Update information panel | +//+------------------------------------------------------------------+ +void UpdateInfoPanel() +{ + // Get current session + string current_session = GetCurrentSession(); + + // Get account information + double account_balance = AccountInfoDouble(ACCOUNT_BALANCE); + double account_equity = AccountInfoDouble(ACCOUNT_EQUITY); + double account_margin = AccountInfoDouble(ACCOUNT_MARGIN); + + // Count current positions + int total_positions = PositionsTotal(); + + // Create info text + string info_text = StringFormat( + "Session: %s\n" + + "Balance: %.2f\n" + + "Equity: %.2f\n" + + "Margin: %.2f\n" + + "Positions: %d/%d", + current_session, + account_balance, + account_equity, + account_margin, + total_positions, + MaxPositions); + + // Update info label + if (ObjectFind(0, "SniperEA_Info") < 0) + { + ObjectCreate(0, "SniperEA_Info", OBJ_LABEL, 0, 0, 0); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_CORNER, CORNER_LEFT_UPPER); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_XDISTANCE, 20); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_YDISTANCE, 60); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_COLOR, clrWhite); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_FONTSIZE, 8); + ObjectSetString(0, "SniperEA_Info", OBJPROP_FONT, "Courier New"); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_SELECTABLE, false); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_SELECTED, false); + ObjectSetInteger(0, "SniperEA_Info", OBJPROP_HIDDEN, true); + } + + ObjectSetString(0, "SniperEA_Info", OBJPROP_TEXT, info_text); +} + +//+------------------------------------------------------------------+ +//| Get current trading session | +//+------------------------------------------------------------------+ +string GetCurrentSession() +{ + MqlDateTime dt; + TimeToStruct(TimeGMT(), dt); + + int current_hour = dt.hour; + int current_minute = dt.min; + int current_time_minutes = current_hour * 60 + current_minute; + + // Convert session times to minutes + int asia_start = StringToTime("1970.01.01 " + AsiaStart) % 86400 / 60; + int asia_end = StringToTime("1970.01.01 " + AsiaEnd) % 86400 / 60; + int london_start = StringToTime("1970.01.01 " + LondonStart) % 86400 / 60; + int london_end = StringToTime("1970.01.01 " + LondonEnd) % 86400 / 60; + int ny_start = StringToTime("1970.01.01 " + NYStart) % 86400 / 60; + int ny_end = StringToTime("1970.01.01 " + NYEnd) % 86400 / 60; + + // Check which session we're in + if ((current_time_minutes >= asia_start && current_time_minutes < asia_end) || + (asia_start > asia_end && (current_time_minutes >= asia_start || current_time_minutes < asia_end))) + return "ASIA"; + + if ((current_time_minutes >= london_start && current_time_minutes < london_end) || + (london_start > london_end && (current_time_minutes >= london_start || current_time_minutes < london_end))) + return "LONDON"; + + if ((current_time_minutes >= ny_start && current_time_minutes < ny_end) || + (ny_start > ny_end && (current_time_minutes >= ny_start || current_time_minutes < ny_end))) + return "NEW YORK"; + + return "OFF HOURS"; +} + +//+------------------------------------------------------------------+ +//| Main trading logic processor | +//+------------------------------------------------------------------+ +void ProcessTradingLogic() +{ + // Update information panel + UpdateInfoPanel(); + + // Check if trading is allowed in current session + if (UseTimeFilter && GetCurrentSession() == "OFF HOURS") + return; + + // Main trading logic will be implemented here + // This is where we'll call all the market structure analysis functions +} + +//+------------------------------------------------------------------+ +//| Logging Functions | +//+------------------------------------------------------------------+ +void LogInfo(string message) +{ + if (EnableDetailedLogging) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [INFO] ", LogPrefix, ": ", message); + } +} + +void LogWarning(string message) +{ + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [WARNING] ", LogPrefix, ": ", message); +} + +void LogError(string message) +{ + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [ERROR] ", LogPrefix, ": ", message); +} + +void LogDebug(string message) +{ + if (EnableDebugMode) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [DEBUG] ", LogPrefix, ": ", message); + } +} + +void LogPattern(string pattern_type, string symbol, string details) +{ + if (LogPatternDetection) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [PATTERN] ", LogPrefix, ": ", pattern_type, " detected on ", symbol, " - ", details); + } +} + +void LogTrade(string action, string symbol, string details) +{ + if (LogTradeExecution) + { + string timestamp = TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); + Print("[", timestamp, "] [TRADE] ", LogPrefix, ": ", action, " on ", symbol, " - ", details); + } +} + +//+------------------------------------------------------------------+ +//| Error Handling Functions | +//+------------------------------------------------------------------+ +bool HandleTradeError(int error_code, string operation) +{ + string error_description = ""; + bool is_critical = false; + + switch (error_code) + { + case TRADE_RETCODE_REQUOTE: + error_description = "Requote"; + break; + case TRADE_RETCODE_REJECT: + error_description = "Request rejected"; + is_critical = true; + break; + case TRADE_RETCODE_CANCEL: + error_description = "Request canceled by trader"; + break; + case TRADE_RETCODE_PLACED: + error_description = "Order placed"; + return true; // Success + case TRADE_RETCODE_DONE: + error_description = "Request completed"; + return true; // Success + case TRADE_RETCODE_DONE_PARTIAL: + error_description = "Request partially completed"; + return true; // Partial success + case TRADE_RETCODE_ERROR: + error_description = "Request processing error"; + is_critical = true; + break; + case TRADE_RETCODE_TIMEOUT: + error_description = "Request timeout"; + break; + case TRADE_RETCODE_INVALID: + error_description = "Invalid request"; + is_critical = true; + break; + case TRADE_RETCODE_INVALID_VOLUME: + error_description = "Invalid volume"; + is_critical = true; + break; + case TRADE_RETCODE_INVALID_PRICE: + error_description = "Invalid price"; + break; + case TRADE_RETCODE_INVALID_STOPS: + error_description = "Invalid stops"; + break; + case TRADE_RETCODE_TRADE_DISABLED: + error_description = "Trade disabled"; + is_critical = true; + break; + case TRADE_RETCODE_MARKET_CLOSED: + error_description = "Market closed"; + break; + case TRADE_RETCODE_NO_MONEY: + error_description = "No money"; + is_critical = true; + break; + case TRADE_RETCODE_PRICE_CHANGED: + error_description = "Price changed"; + break; + case TRADE_RETCODE_PRICE_OFF: + error_description = "Off quotes"; + break; + case TRADE_RETCODE_INVALID_EXPIRATION: + error_description = "Invalid expiration"; + break; + case TRADE_RETCODE_ORDER_CHANGED: + error_description = "Order state changed"; + break; + case TRADE_RETCODE_TOO_MANY_REQUESTS: + error_description = "Too many requests"; + break; + case TRADE_RETCODE_NO_CHANGES: + error_description = "No changes"; + break; + case TRADE_RETCODE_SERVER_DISABLES_AT: + error_description = "Autotrading disabled by server"; + is_critical = true; + break; + case TRADE_RETCODE_CLIENT_DISABLES_AT: + error_description = "Autotrading disabled by client"; + is_critical = true; + break; + case TRADE_RETCODE_LOCKED: + error_description = "Request locked"; + break; + case TRADE_RETCODE_FROZEN: + error_description = "Order or position frozen"; + break; + case TRADE_RETCODE_INVALID_FILL: + error_description = "Invalid fill"; + break; + case TRADE_RETCODE_CONNECTION: + error_description = "No connection"; + is_critical = true; + break; + case TRADE_RETCODE_ONLY_REAL: + error_description = "Only real accounts allowed"; + is_critical = true; + break; + case TRADE_RETCODE_LIMIT_ORDERS: + error_description = "Limit orders limit reached"; + break; + case TRADE_RETCODE_LIMIT_VOLUME: + error_description = "Volume limit reached"; + break; + case TRADE_RETCODE_INVALID_ORDER: + error_description = "Invalid order"; + is_critical = true; + break; + case TRADE_RETCODE_POSITION_CLOSED: + error_description = "Position already closed"; + break; + default: + error_description = "Unknown error"; + is_critical = true; + break; + } + + if (is_critical) + { + LogError(StringFormat("%s failed with critical error %d: %s", operation, error_code, error_description)); + } + else + { + LogWarning(StringFormat("%s failed with error %d: %s", operation, error_code, error_description)); + } + + return false; +} + +//+------------------------------------------------------------------+ +//| Utility Functions | +//+------------------------------------------------------------------+ +double NormalizePrice(string symbol, double price) +{ + return NormalizeDouble(price, (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS)); +} + +double CalculatePipValue(string symbol) +{ + double pip_size = SymbolInfoDouble(symbol, SYMBOL_POINT); + if (SymbolInfoInteger(symbol, SYMBOL_DIGITS) == 5 || SymbolInfoInteger(symbol, SYMBOL_DIGITS) == 3) + pip_size *= 10; + return pip_size; +} + +bool IsNewBar(string symbol, ENUM_TIMEFRAMES timeframe) +{ + static datetime last_bar_time = 0; + datetime current_bar_time = iTime(symbol, timeframe, 0); + + if (current_bar_time != last_bar_time) + { + last_bar_time = current_bar_time; + return true; + } + + return false; +} + +//+------------------------------------------------------------------+ +//| Order Block Detection Functions | +//+------------------------------------------------------------------+ +bool DetectOrderBlocks(string symbol, ENUM_TIMEFRAMES timeframe, OrderBlock &order_blocks[]) +{ + ArrayResize(order_blocks, 0); + + int bars_to_analyze = MathMin(OBLookback * 2, iBars(symbol, timeframe) - 10); + if (bars_to_analyze < 10) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Order Blocks on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + // Look for potential Order Blocks + for (int i = 5; i < bars_to_analyze; i++) + { + // Get candle data + double high = iHigh(symbol, timeframe, i); + double low = iLow(symbol, timeframe, i); + double open = iOpen(symbol, timeframe, i); + double close = iClose(symbol, timeframe, i); + datetime time = iTime(symbol, timeframe, i); + + // Check for bullish Order Block (demand zone) + if (IsBullishOrderBlock(symbol, timeframe, i)) + { + OrderBlock ob; + ob.high = high; + ob.low = low; + ob.time = time; + ob.is_bullish = true; + ob.is_fresh = IsOrderBlockFresh(symbol, timeframe, i, true); + ob.strength = CalculateOrderBlockStrength(symbol, timeframe, i, true); + + if (ob.strength >= OBStrengthFilter) + { + ArrayResize(order_blocks, ArraySize(order_blocks) + 1); + order_blocks[ArraySize(order_blocks) - 1] = ob; + + LogPattern("Order Block", symbol, StringFormat("Bullish OB at %.5f-%.5f, Strength: %.2f", ob.low, ob.high, ob.strength)); + } + } + + // Check for bearish Order Block (supply zone) + if (IsBearishOrderBlock(symbol, timeframe, i)) + { + OrderBlock ob; + ob.high = high; + ob.low = low; + ob.time = time; + ob.is_bullish = false; + ob.is_fresh = IsOrderBlockFresh(symbol, timeframe, i, false); + ob.strength = CalculateOrderBlockStrength(symbol, timeframe, i, false); + + if (ob.strength >= OBStrengthFilter) + { + ArrayResize(order_blocks, ArraySize(order_blocks) + 1); + order_blocks[ArraySize(order_blocks) - 1] = ob; + + LogPattern("Order Block", symbol, StringFormat("Bearish OB at %.5f-%.5f, Strength: %.2f", ob.low, ob.high, ob.strength)); + } + } + } + + LogDebug(StringFormat("Found %d Order Blocks on %s %s", ArraySize(order_blocks), symbol, EnumToString(timeframe))); + return ArraySize(order_blocks) > 0; +} + +bool IsBullishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) +{ + // Get current candle data + double open = iOpen(symbol, timeframe, index); + double close = iClose(symbol, timeframe, index); + double high = iHigh(symbol, timeframe, index); + double low = iLow(symbol, timeframe, index); + + // Must be a bullish candle + if (close <= open) + return false; + + // Check for strong bullish momentum (body > 60% of total range) + double body_size = close - open; + double total_range = high - low; + if (total_range == 0) + return false; + + double body_ratio = body_size / total_range; + if (body_ratio < 0.6) + return false; + + // Check for significant volume increase (if available) + long current_volume = iVolume(symbol, timeframe, index); + long avg_volume = 0; + for (int i = 1; i <= 5; i++) + { + avg_volume += iVolume(symbol, timeframe, index + i); + } + avg_volume /= 5; + + if (current_volume < avg_volume * 1.2) + return false; + + // Check for price rejection from this level in subsequent candles + bool has_rejection = false; + for (int i = 1; i <= 5; i++) + { + if (index - i < 0) + break; + + double test_low = iLow(symbol, timeframe, index - i); + double test_close = iClose(symbol, timeframe, index - i); + + // Price came back to test the OB zone and bounced + if (test_low <= high && test_low >= low && test_close > high) + { + has_rejection = true; + break; + } + } + + return has_rejection; +} + +bool IsBearishOrderBlock(string symbol, ENUM_TIMEFRAMES timeframe, int index) +{ + // Get current candle data + double open = iOpen(symbol, timeframe, index); + double close = iClose(symbol, timeframe, index); + double high = iHigh(symbol, timeframe, index); + double low = iLow(symbol, timeframe, index); + + // Must be a bearish candle + if (close >= open) + return false; + + // Check for strong bearish momentum (body > 60% of total range) + double body_size = open - close; + double total_range = high - low; + if (total_range == 0) + return false; + + double body_ratio = body_size / total_range; + if (body_ratio < 0.6) + return false; + + // Check for significant volume increase (if available) + long current_volume = iVolume(symbol, timeframe, index); + long avg_volume = 0; + for (int i = 1; i <= 5; i++) + { + avg_volume += iVolume(symbol, timeframe, index + i); + } + avg_volume /= 5; + + if (current_volume < avg_volume * 1.2) + return false; + + // Check for price rejection from this level in subsequent candles + bool has_rejection = false; + for (int i = 1; i <= 5; i++) + { + if (index - i < 0) + break; + + double test_high = iHigh(symbol, timeframe, index - i); + double test_close = iClose(symbol, timeframe, index - i); + + // Price came back to test the OB zone and bounced + if (test_high >= low && test_high <= high && test_close < low) + { + has_rejection = true; + break; + } + } + + return has_rejection; +} + +bool IsOrderBlockFresh(string symbol, ENUM_TIMEFRAMES timeframe, int ob_index, bool is_bullish) +{ + double ob_high = iHigh(symbol, timeframe, ob_index); + double ob_low = iLow(symbol, timeframe, ob_index); + + // Check if price has significantly broken through the OB zone + for (int i = 0; i < ob_index; i++) + { + double test_high = iHigh(symbol, timeframe, i); + double test_low = iLow(symbol, timeframe, i); + + if (is_bullish) + { + // For bullish OB, check if price broke significantly below + if (test_low < ob_low - (ob_high - ob_low) * 0.5) + return false; + } + else + { + // For bearish OB, check if price broke significantly above + if (test_high > ob_high + (ob_high - ob_low) * 0.5) + return false; + } + } + + return true; +} + +double CalculateOrderBlockStrength(string symbol, ENUM_TIMEFRAMES timeframe, int index, bool is_bullish) +{ + double strength = 0.0; + + // Factor 1: Candle body size relative to average + double body_size = MathAbs(iClose(symbol, timeframe, index) - iOpen(symbol, timeframe, index)); + double avg_body = 0; + for (int i = 1; i <= 10; i++) + { + avg_body += MathAbs(iClose(symbol, timeframe, index + i) - iOpen(symbol, timeframe, index + i)); + } + avg_body /= 10; + + if (avg_body > 0) + strength += (body_size / avg_body) * 0.3; // 30% weight + + // Factor 2: Volume relative to average + long current_volume = iVolume(symbol, timeframe, index); + long avg_volume = 0; + for (int i = 1; i <= 10; i++) + { + avg_volume += iVolume(symbol, timeframe, index + i); + } + avg_volume /= 10; + + if (avg_volume > 0) + strength += ((double)current_volume / avg_volume) * 0.2; // 20% weight + + // Factor 3: Number of times price respected the level + int respect_count = 0; + double ob_high = iHigh(symbol, timeframe, index); + double ob_low = iLow(symbol, timeframe, index); + + for (int i = 1; i < index && i <= 20; i++) + { + double test_high = iHigh(symbol, timeframe, index - i); + double test_low = iLow(symbol, timeframe, index - i); + double test_close = iClose(symbol, timeframe, index - i); + + if (is_bullish) + { + if (test_low <= ob_high && test_low >= ob_low && test_close > ob_high) + respect_count++; + } + else + { + if (test_high >= ob_low && test_high <= ob_high && test_close < ob_low) + respect_count++; + } + } + + strength += respect_count * 0.1; // 10% weight per respect + + // Factor 4: Time since formation (fresher = stronger) + double time_factor = 1.0 - (index / (double)OBLookback); + strength += time_factor * 0.3; // 30% weight + + return MathMin(strength, 2.0); // Cap at 2.0 +} + +//+------------------------------------------------------------------+ +//| Break of Structure Detection Functions | +//+------------------------------------------------------------------+ +bool DetectBreakOfStructure(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos_events[]) +{ + ArrayResize(bos_events, 0); + + int bars_to_analyze = MathMin(SwingLookback * 3, iBars(symbol, timeframe) - 10); + if (bars_to_analyze < 20) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Break of Structure on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + // Find swing highs and lows first + double swing_highs[]; + double swing_lows[]; + datetime swing_high_times[]; + datetime swing_low_times[]; + + FindSwingPoints(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times); + + // Analyze for BOS patterns + AnalyzeBOSPatterns(symbol, timeframe, swing_highs, swing_lows, swing_high_times, swing_low_times, bos_events); + + LogDebug(StringFormat("Found %d BOS events on %s %s", ArraySize(bos_events), symbol, EnumToString(timeframe))); + return ArraySize(bos_events) > 0; +} + +void FindSwingPoints(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, + double &swing_highs[], double &swing_lows[], + datetime &swing_high_times[], datetime &swing_low_times[]) +{ + ArrayResize(swing_highs, 0); + ArrayResize(swing_lows, 0); + ArrayResize(swing_high_times, 0); + ArrayResize(swing_low_times, 0); + + for (int i = SwingLookback; i < bars_to_analyze - SwingLookback; i++) + { + double current_high = iHigh(symbol, timeframe, i); + double current_low = iLow(symbol, timeframe, i); + datetime current_time = iTime(symbol, timeframe, i); + + // Check for swing high + bool is_swing_high = true; + for (int j = 1; j <= SwingLookback; j++) + { + if (iHigh(symbol, timeframe, i - j) >= current_high || + iHigh(symbol, timeframe, i + j) >= current_high) + { + is_swing_high = false; + break; + } + } + + if (is_swing_high) + { + ArrayResize(swing_highs, ArraySize(swing_highs) + 1); + ArrayResize(swing_high_times, ArraySize(swing_high_times) + 1); + swing_highs[ArraySize(swing_highs) - 1] = current_high; + swing_high_times[ArraySize(swing_high_times) - 1] = current_time; + } + + // Check for swing low + bool is_swing_low = true; + for (int j = 1; j <= SwingLookback; j++) + { + if (iLow(symbol, timeframe, i - j) <= current_low || + iLow(symbol, timeframe, i + j) <= current_low) + { + is_swing_low = false; + break; + } + } + + if (is_swing_low) + { + ArrayResize(swing_lows, ArraySize(swing_lows) + 1); + ArrayResize(swing_low_times, ArraySize(swing_low_times) + 1); + swing_lows[ArraySize(swing_lows) - 1] = current_low; + swing_low_times[ArraySize(swing_low_times) - 1] = current_time; + } + } +} + +void AnalyzeBOSPatterns(string symbol, ENUM_TIMEFRAMES timeframe, + double &swing_highs[], double &swing_lows[], + datetime &swing_high_times[], datetime &swing_low_times[], + BreakOfStructure &bos_events[]) +{ + // Analyze bullish BOS (breaking above previous swing high) + for (int i = 1; i < ArraySize(swing_highs); i++) + { + double previous_high = swing_highs[i]; + datetime previous_time = swing_high_times[i]; + + // Look for price breaking above this high + int start_bar = iBarShift(symbol, timeframe, previous_time); + if (start_bar < 0) + continue; + + for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) + { + double current_high = iHigh(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + if (current_high > previous_high && current_close > previous_high) + { + // Confirm the break with subsequent candles + bool confirmed = ConfirmBOS(symbol, timeframe, j, true, previous_high); + + if (confirmed) + { + BreakOfStructure bos; + bos.level = previous_high; + bos.time = current_time; + bos.is_bullish = true; + bos.confirmed = true; + + ArrayResize(bos_events, ArraySize(bos_events) + 1); + bos_events[ArraySize(bos_events) - 1] = bos; + + LogPattern("Break of Structure", symbol, StringFormat("Bullish BOS at %.5f", previous_high)); + break; + } + } + } + } + + // Analyze bearish BOS (breaking below previous swing low) + for (int i = 1; i < ArraySize(swing_lows); i++) + { + double previous_low = swing_lows[i]; + datetime previous_time = swing_low_times[i]; + + // Look for price breaking below this low + int start_bar = iBarShift(symbol, timeframe, previous_time); + if (start_bar < 0) + continue; + + for (int j = 0; j < start_bar && j < BOSConfirmationCandles * 2; j++) + { + double current_low = iLow(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + if (current_low < previous_low && current_close < previous_low) + { + // Confirm the break with subsequent candles + bool confirmed = ConfirmBOS(symbol, timeframe, j, false, previous_low); + + if (confirmed) + { + BreakOfStructure bos; + bos.level = previous_low; + bos.time = current_time; + bos.is_bullish = false; + bos.confirmed = true; + + ArrayResize(bos_events, ArraySize(bos_events) + 1); + bos_events[ArraySize(bos_events) - 1] = bos; + + LogPattern("Break of Structure", symbol, StringFormat("Bearish BOS at %.5f", previous_low)); + break; + } + } + } + } +} + +bool ConfirmBOS(string symbol, ENUM_TIMEFRAMES timeframe, int break_bar, bool is_bullish, double level) +{ + int confirmation_count = 0; + + // Check subsequent candles for confirmation + for (int i = 0; i < BOSConfirmationCandles && break_bar - i >= 0; i++) + { + double close_price = iClose(symbol, timeframe, break_bar - i); + + if (is_bullish) + { + if (close_price > level) + confirmation_count++; + } + else + { + if (close_price < level) + confirmation_count++; + } + } + + // Require at least 2 out of 3 confirmation candles + return confirmation_count >= MathMax(2, BOSConfirmationCandles / 2); +} + +bool IsBOSValid(string symbol, ENUM_TIMEFRAMES timeframe, BreakOfStructure &bos) +{ + // Check if BOS is recent enough + datetime current_time = iTime(symbol, timeframe, 0); + int time_diff = (int)((current_time - bos.time) / PeriodSeconds(timeframe)); + + if (time_diff > BOSConfirmationCandles * 3) + return false; + + // Check if price is still respecting the BOS level + double current_price = iClose(symbol, timeframe, 0); + + if (bos.is_bullish) + { + return current_price > bos.level; + } + else + { + return current_price < bos.level; + } +} + +//+------------------------------------------------------------------+ +//| Fair Value Gap Detection Functions | +//+------------------------------------------------------------------+ +bool DetectFairValueGaps(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg_array[]) +{ + ArrayResize(fvg_array, 0); + + int bars_to_analyze = MathMin(50, iBars(symbol, timeframe) - 5); + if (bars_to_analyze < 10) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Fair Value Gaps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + double pip_value = CalculatePipValue(symbol); + double min_gap_size = MinFVGSize * pip_value; + + // Look for FVG patterns (3-candle pattern) + for (int i = 2; i < bars_to_analyze; i++) + { + // Get three consecutive candles + double high1 = iHigh(symbol, timeframe, i); // First candle + double low1 = iLow(symbol, timeframe, i); + double high2 = iHigh(symbol, timeframe, i - 1); // Middle candle (impulse) + double low2 = iLow(symbol, timeframe, i - 1); + double high3 = iHigh(symbol, timeframe, i - 2); // Third candle + double low3 = iLow(symbol, timeframe, i - 2); + + datetime gap_time = iTime(symbol, timeframe, i - 1); + + // Check for bullish FVG (gap between candle 1 high and candle 3 low) + if (low3 > high1) + { + double gap_size = low3 - high1; + if (gap_size >= min_gap_size) + { + FairValueGap fvg; + fvg.top = low3; + fvg.bottom = high1; + fvg.time = gap_time; + fvg.is_bullish = true; + fvg.is_filled = IsFVGFilled(symbol, timeframe, i - 2, fvg.top, fvg.bottom, true); + + if (!fvg.is_filled) + { + ArrayResize(fvg_array, ArraySize(fvg_array) + 1); + fvg_array[ArraySize(fvg_array) - 1] = fvg; + + LogPattern("Fair Value Gap", symbol, StringFormat("Bullish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); + } + } + } + + // Check for bearish FVG (gap between candle 1 low and candle 3 high) + if (high3 < low1) + { + double gap_size = low1 - high3; + if (gap_size >= min_gap_size) + { + FairValueGap fvg; + fvg.top = low1; + fvg.bottom = high3; + fvg.time = gap_time; + fvg.is_bullish = false; + fvg.is_filled = IsFVGFilled(symbol, timeframe, i - 2, fvg.top, fvg.bottom, false); + + if (!fvg.is_filled) + { + ArrayResize(fvg_array, ArraySize(fvg_array) + 1); + fvg_array[ArraySize(fvg_array) - 1] = fvg; + + LogPattern("Fair Value Gap", symbol, StringFormat("Bearish FVG at %.5f-%.5f, Size: %.1f pips", fvg.bottom, fvg.top, gap_size / pip_value)); + } + } + } + } + + LogDebug(StringFormat("Found %d unfilled FVGs on %s %s", ArraySize(fvg_array), symbol, EnumToString(timeframe))); + return ArraySize(fvg_array) > 0; +} + +bool IsFVGFilled(string symbol, ENUM_TIMEFRAMES timeframe, int start_bar, double top, double bottom, bool is_bullish) +{ + // Check if price has filled the FVG since its formation + for (int i = 0; i < start_bar; i++) + { + double high = iHigh(symbol, timeframe, i); + double low = iLow(symbol, timeframe, i); + + if (is_bullish) + { + // For bullish FVG, check if price came back down to fill the gap + if (low <= bottom) + return true; + } + else + { + // For bearish FVG, check if price came back up to fill the gap + if (high >= top) + return true; + } + } + + return false; +} + +bool IsFVGValid(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg) +{ + // Check if FVG is still unfilled + if (fvg.is_filled) + return false; + + // Check current price position relative to FVG + double current_price = iClose(symbol, timeframe, 0); + + if (fvg.is_bullish) + { + // For bullish FVG, price should be above the gap + return current_price > fvg.top; + } + else + { + // For bearish FVG, price should be below the gap + return current_price < fvg.bottom; + } +} + +double GetFVGMidpoint(FairValueGap &fvg) +{ + return (fvg.top + fvg.bottom) / 2.0; +} + +bool IsPriceInFVG(double price, FairValueGap &fvg) +{ + return price >= fvg.bottom && price <= fvg.top; +} + +void UpdateFVGStatus(string symbol, ENUM_TIMEFRAMES timeframe, FairValueGap &fvg_array[]) +{ + // Update the filled status of existing FVGs + for (int i = 0; i < ArraySize(fvg_array); i++) + { + if (!fvg_array[i].is_filled) + { + double current_high = iHigh(symbol, timeframe, 0); + double current_low = iLow(symbol, timeframe, 0); + + if (fvg_array[i].is_bullish) + { + if (current_low <= fvg_array[i].bottom) + { + fvg_array[i].is_filled = true; + LogPattern("Fair Value Gap", symbol, "Bullish FVG filled"); + } + } + else + { + if (current_high >= fvg_array[i].top) + { + fvg_array[i].is_filled = true; + LogPattern("Fair Value Gap", symbol, "Bearish FVG filled"); + } + } + } + } +} + +//+------------------------------------------------------------------+ +//| Liquidity Sweep Detection Functions | +//+------------------------------------------------------------------+ +bool DetectLiquiditySweeps(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep_array[]) +{ + ArrayResize(sweep_array, 0); + + int bars_to_analyze = MathMin(100, iBars(symbol, timeframe) - 10); + if (bars_to_analyze < 20) + return false; + + LogDebug(StringFormat("Analyzing %d bars for Liquidity Sweeps on %s %s", bars_to_analyze, symbol, EnumToString(timeframe))); + + double pip_value = CalculatePipValue(symbol); + double min_sweep_distance = MinSweepDistance * pip_value; + + // Find equal highs and lows first + double equal_highs[]; + double equal_lows[]; + datetime equal_high_times[]; + datetime equal_low_times[]; + + FindEqualHighsLows(symbol, timeframe, bars_to_analyze, equal_highs, equal_lows, equal_high_times, equal_low_times); + + // Look for liquidity sweeps above equal highs + for (int i = 0; i < ArraySize(equal_highs); i++) + { + double equal_high = equal_highs[i]; + datetime equal_time = equal_high_times[i]; + + int equal_bar = iBarShift(symbol, timeframe, equal_time); + if (equal_bar < 0) + continue; + + // Look for sweep above this equal high + for (int j = 0; j < equal_bar && j < 20; j++) + { + double current_high = iHigh(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + // Check if price swept above equal high + if (current_high > equal_high + min_sweep_distance) + { + // Check for rejection (close back below equal high) + if (current_close < equal_high) + { + LiquiditySweep sweep; + sweep.level = equal_high; + sweep.time = current_time; + sweep.is_high_sweep = true; + sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, true, equal_high); + + if (sweep.confirmed) + { + ArrayResize(sweep_array, ArraySize(sweep_array) + 1); + sweep_array[ArraySize(sweep_array) - 1] = sweep; + + LogPattern("Liquidity Sweep", symbol, StringFormat("High sweep at %.5f, Distance: %.1f pips", equal_high, (current_high - equal_high) / pip_value)); + } + break; + } + } + } + } + + // Look for liquidity sweeps below equal lows + for (int i = 0; i < ArraySize(equal_lows); i++) + { + double equal_low = equal_lows[i]; + datetime equal_time = equal_low_times[i]; + + int equal_bar = iBarShift(symbol, timeframe, equal_time); + if (equal_bar < 0) + continue; + + // Look for sweep below this equal low + for (int j = 0; j < equal_bar && j < 20; j++) + { + double current_low = iLow(symbol, timeframe, j); + double current_close = iClose(symbol, timeframe, j); + datetime current_time = iTime(symbol, timeframe, j); + + // Check if price swept below equal low + if (current_low < equal_low - min_sweep_distance) + { + // Check for rejection (close back above equal low) + if (current_close > equal_low) + { + LiquiditySweep sweep; + sweep.level = equal_low; + sweep.time = current_time; + sweep.is_high_sweep = false; + sweep.confirmed = ConfirmLiquiditySweep(symbol, timeframe, j, false, equal_low); + + if (sweep.confirmed) + { + ArrayResize(sweep_array, ArraySize(sweep_array) + 1); + sweep_array[ArraySize(sweep_array) - 1] = sweep; + + LogPattern("Liquidity Sweep", symbol, StringFormat("Low sweep at %.5f, Distance: %.1f pips", equal_low, (equal_low - current_low) / pip_value)); + } + break; + } + } + } + } + + LogDebug(StringFormat("Found %d Liquidity Sweeps on %s %s", ArraySize(sweep_array), symbol, EnumToString(timeframe))); + return ArraySize(sweep_array) > 0; +} + +void FindEqualHighsLows(string symbol, ENUM_TIMEFRAMES timeframe, int bars_to_analyze, + double &equal_highs[], double &equal_lows[], + datetime &equal_high_times[], datetime &equal_low_times[]) +{ + ArrayResize(equal_highs, 0); + ArrayResize(equal_lows, 0); + ArrayResize(equal_high_times, 0); + ArrayResize(equal_low_times, 0); + + double pip_value = CalculatePipValue(symbol); + double tolerance = 2.0 * pip_value; // 2 pip tolerance for "equal" levels + + // Find swing points first + double swing_highs[]; + double swing_lows[]; + datetime swing_high_times[]; + datetime swing_low_times[]; + + FindSwingPoints(symbol, timeframe, bars_to_analyze, swing_highs, swing_lows, swing_high_times, swing_low_times); + + // Find equal highs + for (int i = 0; i < ArraySize(swing_highs); i++) + { + double current_high = swing_highs[i]; + datetime current_time = swing_high_times[i]; + int equal_count = 1; + + // Count how many swing highs are at similar level + for (int j = i + 1; j < ArraySize(swing_highs); j++) + { + if (MathAbs(swing_highs[j] - current_high) <= tolerance) + { + equal_count++; + } + } + + // If we have at least 2 equal highs, add to array + if (equal_count >= 2) + { + // Check if this level is already in the array + bool already_exists = false; + for (int k = 0; k < ArraySize(equal_highs); k++) + { + if (MathAbs(equal_highs[k] - current_high) <= tolerance) + { + already_exists = true; + break; + } + } + + if (!already_exists) + { + ArrayResize(equal_highs, ArraySize(equal_highs) + 1); + ArrayResize(equal_high_times, ArraySize(equal_high_times) + 1); + equal_highs[ArraySize(equal_highs) - 1] = current_high; + equal_high_times[ArraySize(equal_high_times) - 1] = current_time; + } + } + } + + // Find equal lows + for (int i = 0; i < ArraySize(swing_lows); i++) + { + double current_low = swing_lows[i]; + datetime current_time = swing_low_times[i]; + int equal_count = 1; + + // Count how many swing lows are at similar level + for (int j = i + 1; j < ArraySize(swing_lows); j++) + { + if (MathAbs(swing_lows[j] - current_low) <= tolerance) + { + equal_count++; + } + } + + // If we have at least 2 equal lows, add to array + if (equal_count >= 2) + { + // Check if this level is already in the array + bool already_exists = false; + for (int k = 0; k < ArraySize(equal_lows); k++) + { + if (MathAbs(equal_lows[k] - current_low) <= tolerance) + { + already_exists = true; + break; + } + } + + if (!already_exists) + { + ArrayResize(equal_lows, ArraySize(equal_lows) + 1); + ArrayResize(equal_low_times, ArraySize(equal_low_times) + 1); + equal_lows[ArraySize(equal_lows) - 1] = current_low; + equal_low_times[ArraySize(equal_low_times) - 1] = current_time; + } + } + } +} + +bool ConfirmLiquiditySweep(string symbol, ENUM_TIMEFRAMES timeframe, int sweep_bar, bool is_high_sweep, double level) +{ + // Check for strong rejection after the sweep + double sweep_high = iHigh(symbol, timeframe, sweep_bar); + double sweep_low = iLow(symbol, timeframe, sweep_bar); + double sweep_close = iClose(symbol, timeframe, sweep_bar); + + if (is_high_sweep) + { + // For high sweep, look for bearish rejection + double wick_size = sweep_high - sweep_close; + double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); + + // Wick should be at least 2x the body size + if (wick_size < body_size * 2) + return false; + + // Close should be below the swept level + if (sweep_close >= level) + return false; + } + else + { + // For low sweep, look for bullish rejection + double wick_size = sweep_close - sweep_low; + double body_size = MathAbs(iClose(symbol, timeframe, sweep_bar) - iOpen(symbol, timeframe, sweep_bar)); + + // Wick should be at least 2x the body size + if (wick_size < body_size * 2) + return false; + + // Close should be above the swept level + if (sweep_close <= level) + return false; + } + + return true; +} + +bool IsLiquiditySweepValid(string symbol, ENUM_TIMEFRAMES timeframe, LiquiditySweep &sweep) +{ + if (!sweep.confirmed) + return false; + + // Check if sweep is recent enough + datetime current_time = iTime(symbol, timeframe, 0); + int time_diff = (int)((current_time - sweep.time) / PeriodSeconds(timeframe)); + + if (time_diff > 10) + return false; // Must be within last 10 candles + + // Check current price position + double current_price = iClose(symbol, timeframe, 0); + + if (sweep.is_high_sweep) + { + // For high sweep, price should be below the swept level + return current_price < sweep.level; + } + else + { + // For low sweep, price should be above the swept level + return current_price > sweep.level; + } +} + +//+------------------------------------------------------------------+ +//| Multi-Timeframe Analysis Engine | +//+------------------------------------------------------------------+ +struct MarketStructureData +{ + OrderBlock order_blocks[]; + FairValueGap fair_value_gaps[]; + BreakOfStructure bos_events[]; + LiquiditySweep liquidity_sweeps[]; + ENUM_TIMEFRAMES timeframe; + datetime last_update; + bool is_valid; +}; + +// Global market structure data for different timeframes +MarketStructureData MTF_Data_M1; +MarketStructureData MTF_Data_M15; +MarketStructureData MTF_Data_H4; +MarketStructureData MTF_Data_D1; +MarketStructureData MTF_Data_W1; + +bool InitializeMultiTimeframeAnalysis() +{ + LogInfo("Initializing Multi-Timeframe Analysis Engine"); + + // Initialize timeframe data structures + MTF_Data_M1.timeframe = PERIOD_M1; + MTF_Data_M1.is_valid = false; + MTF_Data_M1.last_update = 0; + + MTF_Data_M15.timeframe = PERIOD_M15; + MTF_Data_M15.is_valid = false; + MTF_Data_M15.last_update = 0; + + MTF_Data_H4.timeframe = PERIOD_H4; + MTF_Data_H4.is_valid = false; + MTF_Data_H4.last_update = 0; + + MTF_Data_D1.timeframe = PERIOD_D1; + MTF_Data_D1.is_valid = false; + MTF_Data_D1.last_update = 0; + + MTF_Data_W1.timeframe = PERIOD_W1; + MTF_Data_W1.is_valid = false; + MTF_Data_W1.last_update = 0; + + LogInfo("Multi-Timeframe Analysis Engine initialized successfully"); + return true; +} + +bool UpdateMultiTimeframeAnalysis(string symbol) +{ + LogDebug("Updating Multi-Timeframe Analysis for " + symbol); + + bool updated = false; + + // Update M1 analysis (most frequent) + if (IsNewBar(symbol, PERIOD_M1) || !MTF_Data_M1.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_M1); + } + + // Update M15 analysis + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_M15) || !MTF_Data_M15.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_M15); + } + + // Update H4 analysis + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_H4) || !MTF_Data_H4.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_H4); + } + + // Update D1 analysis + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_D1) || !MTF_Data_D1.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_D1); + } + + // Update W1 analysis (least frequent) + if (IsTimeframeUpdateNeeded(symbol, MTF_Data_W1) || !MTF_Data_W1.is_valid) + { + updated |= UpdateTimeframeData(symbol, MTF_Data_W1); + } + + if (updated) + { + LogDebug("Multi-Timeframe Analysis updated for " + symbol); + } + + return updated; +} + +bool IsTimeframeUpdateNeeded(string symbol, MarketStructureData &mtf_data) +{ + datetime current_bar_time = iTime(symbol, mtf_data.timeframe, 0); + return current_bar_time != mtf_data.last_update; +} + +bool UpdateTimeframeData(string symbol, MarketStructureData &mtf_data) +{ + LogDebug(StringFormat("Updating %s analysis for %s", EnumToString(mtf_data.timeframe), symbol)); + + bool success = true; + + // Update Order Blocks + success &= DetectOrderBlocks(symbol, mtf_data.timeframe, mtf_data.order_blocks); + + // Update Fair Value Gaps + success &= DetectFairValueGaps(symbol, mtf_data.timeframe, mtf_data.fair_value_gaps); + + // Update Break of Structure events + success &= DetectBreakOfStructure(symbol, mtf_data.timeframe, mtf_data.bos_events); + + // Update Liquidity Sweeps + success &= DetectLiquiditySweeps(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps); + + // Update metadata + mtf_data.last_update = iTime(symbol, mtf_data.timeframe, 0); + mtf_data.is_valid = success; + + if (success) + { + LogDebug(StringFormat("%s analysis completed: OB=%d, FVG=%d, BOS=%d, Sweeps=%d", + EnumToString(mtf_data.timeframe), + ArraySize(mtf_data.order_blocks), + ArraySize(mtf_data.fair_value_gaps), + ArraySize(mtf_data.bos_events), + ArraySize(mtf_data.liquidity_sweeps))); + } + + return success; +} + +string GetMarketBias(string symbol) +{ + // Analyze higher timeframes for overall market bias + string h4_bias = GetTimeframeBias(symbol, MTF_Data_H4); + string d1_bias = GetTimeframeBias(symbol, MTF_Data_D1); + string w1_bias = GetTimeframeBias(symbol, MTF_Data_W1); + + // Weight the biases (Weekly > Daily > H4) + if (w1_bias == d1_bias && d1_bias == h4_bias) + { + return w1_bias; // All timeframes agree + } + else if (w1_bias == d1_bias) + { + return w1_bias; // Higher timeframes agree + } + else if (d1_bias == h4_bias) + { + return d1_bias; // Lower timeframes agree + } + else + { + return w1_bias; // Default to highest timeframe + } +} + +string GetTimeframeBias(string symbol, MarketStructureData &mtf_data) +{ + if (!mtf_data.is_valid) + return "NEUTRAL"; + + int bullish_signals = 0; + int bearish_signals = 0; + + // Analyze BOS events + for (int i = 0; i < ArraySize(mtf_data.bos_events); i++) + { + if (IsBOSValid(symbol, mtf_data.timeframe, mtf_data.bos_events[i])) + { + if (mtf_data.bos_events[i].is_bullish) + bullish_signals++; + else + bearish_signals++; + } + } + + // Analyze Order Blocks + for (int i = 0; i < ArraySize(mtf_data.order_blocks); i++) + { + if (mtf_data.order_blocks[i].is_fresh && mtf_data.order_blocks[i].strength > OBStrengthFilter) + { + if (mtf_data.order_blocks[i].is_bullish) + bullish_signals++; + else + bearish_signals++; + } + } + + // Analyze Liquidity Sweeps + for (int i = 0; i < ArraySize(mtf_data.liquidity_sweeps); i++) + { + if (IsLiquiditySweepValid(symbol, mtf_data.timeframe, mtf_data.liquidity_sweeps[i])) + { + if (mtf_data.liquidity_sweeps[i].is_high_sweep) + bearish_signals++; // High sweep typically leads to bearish move + else + bullish_signals++; // Low sweep typically leads to bullish move + } + } + + // Determine bias + if (bullish_signals > bearish_signals + 1) + return "BULLISH"; + else if (bearish_signals > bullish_signals + 1) + return "BEARISH"; + else + return "NEUTRAL"; +} + +bool IsMultiTimeframeAligned(string symbol, bool is_bullish_setup) +{ + if (!RequireMultiTFConfirmation) + return true; + + string market_bias = GetMarketBias(symbol); + + if (is_bullish_setup) + { + return market_bias == "BULLISH" || market_bias == "NEUTRAL"; + } + else + { + return market_bias == "BEARISH" || market_bias == "NEUTRAL"; + } +} + +MarketStructureData *GetTimeframeData(ENUM_TIMEFRAMES timeframe) +{ + switch (timeframe) + { + case PERIOD_M1: + return &MTF_Data_M1; + case PERIOD_M15: + return &MTF_Data_M15; + case PERIOD_H4: + return &MTF_Data_H4; + case PERIOD_D1: + return &MTF_Data_D1; + case PERIOD_W1: + return &MTF_Data_W1; + default: + return NULL; + } +} + +void PrintMultiTimeframeStatus(string symbol) +{ + if (!EnableDebugMode) + return; + + string status = StringFormat( + "=== Multi-Timeframe Status for %s ===\n" + + "Market Bias: %s\n" + + "M1 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "M15 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "H4 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "D1 - OB:%d FVG:%d BOS:%d Sweeps:%d\n" + + "W1 - OB:%d FVG:%d BOS:%d Sweeps:%d", + symbol, + GetMarketBias(symbol), + ArraySize(MTF_Data_M1.order_blocks), ArraySize(MTF_Data_M1.fair_value_gaps), ArraySize(MTF_Data_M1.bos_events), ArraySize(MTF_Data_M1.liquidity_sweeps), + ArraySize(MTF_Data_M15.order_blocks), ArraySize(MTF_Data_M15.fair_value_gaps), ArraySize(MTF_Data_M15.bos_events), ArraySize(MTF_Data_M15.liquidity_sweeps), + ArraySize(MTF_Data_H4.order_blocks), ArraySize(MTF_Data_H4.fair_value_gaps), ArraySize(MTF_Data_H4.bos_events), ArraySize(MTF_Data_H4.liquidity_sweeps), + ArraySize(MTF_Data_D1.order_blocks), ArraySize(MTF_Data_D1.fair_value_gaps), ArraySize(MTF_Data_D1.bos_events), ArraySize(MTF_Data_D1.liquidity_sweeps), + ArraySize(MTF_Data_W1.order_blocks), ArraySize(MTF_Data_W1.fair_value_gaps), ArraySize(MTF_Data_W1.bos_events), ArraySize(MTF_Data_W1.liquidity_sweeps)); + + LogDebug(status); +} diff --git a/src/TestEA.ex5 b/src/TestEA.ex5 new file mode 100644 index 0000000000000000000000000000000000000000..0e814e1bd309be58f9edc751502950dc883ab968 GIT binary patch literal 5788 zcmeH}RZtvCn}&x$2G`)h34;Z92niCLL1%!$-6aqt!7aFDAV6@JU_pWp!8N!AcZc9E zd(QrQs{Y!&+smzey83(H>iVRrZ@S;=3Ig8&@}9jx1N;RFVB K2)a?CYl zK+4?oGe!MZBI!o~6Kmt*qEj}1muLel0d9c*G5+7W{lERcd;`1yE&$J8>wio3h#kp> zd>SCvLM$Cketm;!3;%E_S=kRl_2AIdp 2IqJ%Zg4ZQXwu z*5$k({B+ZrV*pXuy-I7aD+^v0JkBmSx>8eN+?f5v&u*mQtl)L|$QW~!L$O&`S_oNS zvUcirB9SV@(~jyjDVXwH&+?D8lInH;lv>9}L0?M{Ug<{aDDNl@s~=3Y%Sp09##g0E zP*YPxfb`0=dkSYU@nm*zx%qtjPIG2m+XCfKEX4~=zvN*iKDGC>If<}vOo{v|R^880 zChSMQXa3enLuQ8rk5k~ zN T^ 9ybBip_R9H z6MFS>CF8cYo*ULI7E`2SrpS(m5N4&Ro*TBe%0P6{C2(0cgsaj~M6u|rZ)BKDD|2UG z4RU8VZ#)wJ)B{}@%}^NkcZAxoP|lY5L`LG3Pjz)w$>m#uuQ0Y)<2)wD=>vEcC-fYc z^7AZHf8z|oGvlvskxq5jpHqOdtl@8m*oCgXRJBk0X7}OPGrf?H0PFTDOlMcBUq(dk z_imrerT1G^!acfE$TDct#B0wE_;k%@L7p2eL%4o+?~)!V%n;(IJeM^K0t*zBY|GxV zc!lz}io8SL(yr%A)ur%fTL 4Z9FcR>3SvFL 987vV!cj*90xOGZ~+PBDpBvYBkShwlrMZ2sj!G##b}1bC4T6= zf|%fEE*esAT|pH%L tZ3`)6 }fO&rbF+T#6)JP4l(0W%jKq#<5ZG zJ$mKQM^|6-kuuU@LR<7rlwrcDNq_G1dF7%YHxdp%NJuf!k9I&7VPflLhuAeXS=>Xm zzDt|7@?~{qQAN)=#;)rPPS2zkRZnm#T@bHd^uHl{Bv#&4AUrW+`V{%G#CyD^SO0 C?_SfFy;0l$bH@c0#`-t3ye0V z?8iDAdW-ivBu+`B*XPC)CfGIVhZ)?W&YK1F)&JS=k=5KM@%
E{bxv{1)5fUym*yW;gTdisLJCUELJpN2ZDvo}^+%2Gu7=Y>sMI*z;7m z;%xmT%@aPBmU)vM*kwc8rhWf-NtQZarTTG0^&1?Db)iR8G6WUMO{AfRX_H$ClXO~T zJ~CQ%QN|IrNqNx6)66)tII;0zz=of0xbRqQIM74hJD;`rMwwVCYM9b6hP;<@C6d%s z`)c5lTMHB^nou37C&%e0Cw6c6kaAIM(1LBfntNd=JH+kVMpxEUUqg&4&q$ygpZ_VO z-zyu-DtITzVMTvr`(!V3+rt)A`mKMh=mQO1dWSO9wk1!FWGdKGlpi10g>Cv3URCii zdaJi=Aw99@i@dztz&QR0BOROl%9=@FV7fOWFZN^=;f>I=!B1D *SOZS4IZELM^8eRNYlHS zCrFIR0RcBoWi);5bL!Eo;ASYN5iLoKqq=)s_`AdY-M7(K(|7Jffw%W0UC7hoFb@?j zxt-sAh1 OG^j=I3js!dxTgcP% zfie{8t1|Yo-Bh18NA1)EP84I7KbZ=9I$!Wa+{zr0KN@HmQqX3XttG2@2drlpN~LQf z0WXgOBFVW^N;o@Be&z>wcs2P_a^z?fVeu?tmR{a?F~h``x_!mdWKDZ%vAJ6pvx(Na zU5HTAZJ>2F<=izg6(vQ3?1a#w$v0;a#^nCgglFYBgUovZ08BF{&3xv#a`yIc>_ATL z*7?}W#gT+`LW7-fbNzYAhM#a+yLmV5N6rDlo~_;b&_>WQ=&qaUt*2RO#Lze)%ZvFH z`strzD|YHLmP3x%*imL^Y 1J_SD4+GQVk&j=X53MSh ze^!?v_B)jVFTR)tI;$m|oGi+%e4u%0;^f{U``*~$tE47e;#Q)hXE)CTCxrUGaJgGi z+qheI lz#3Z_rSVB9p$bt=^HP9lG#Mx$82t1mE~q|RDba(}=?_!m#f zN&Bnh766T1w`dPr%Jj#GIzxx`u?iy3l+UK)4gs&{p4{`v4b24gNDQPDQOTKnEzyz7 zDZe^i?!Wo)m?`#Ts?zb~)z4j?*tkuBm_;zqFCwW*Am4MbYhA1+)47`Qb0sHdfu;j+ z_t)*|6SaQQ!EvSA`!n19R{#?nIwV=>T1pog)0qA=L0-rybhLs*iw0kKe_KyOSWF6M z5QM*}#Ab0veqIW+osRHR3ohg7MA4$sL`*YSCaFH%s!_5-+*HF&l4w4s#cUGKxb^rW z;6(4V&KS5ojc~$6nX `5!!etUV W?-hz3W#Gq?tZLs%WJ@J$rGpdnFvm)l! zFi+0;g+&@V_0vZ!wI~Po7V2AUY!R!8`N4Jr8@g(83};`ZRy=uf2|0*}TmQ%e!Xn{; z*Nm}*6t~+1j~o`4pO(sLyEU+$jOpOw!V@Mh0>(jQV1K5D Q>c zL6XK02Ek38H1JWE5T3}9cxz_ZV0t5}`-h$1iOo+1D~~X5PlDh=ik?^BvZ&R2K@zQm zoAs+)!XBz+rx;TF0zKP)m4;0PqA1Bv*f-3qG^4SjG|x8b;jxpOvnnOVQHy&ij2Y8* z%8k#H-RD+#47>R55i72RmKE8~iq71XT7$4qo7485khWEaEvp}WDbn~{! Zq zq}}pnX-whM#Ez`*`DMR|O}I=#v-D2a#mn4>ZWsjX*L{j?78JBImqPHqN4%K^Ai)0i z7UqWn< FjD6~56j@Gw0Lu?LX77+ z)XVcLxKNUJV0oHb0-fZ_nRK%!e=V213e8Vf%rdA4oVvr;%^b_&TBjw9-g+ExR_W_z zOVLDub98sT%9SJ_%FKP{`DIvOE4f1;{eHRFFM^Sp|3D9Nm)ous=QEmh8;j(W%xLLq zPg0HPI>7Xe=F_8S)zZ$D9F_m6)*S)59GjyXdaDrG0*io!oUAH(Ya!~bip$4H%l7M_ zICmkBe31EDA@NuG5syFDLIGT1Md&$?t_oci_%_TOA+GYj=_}Ge?R%zH`2l!I^=X%F zgMlTt++#bZikQTl*ZRfvEH-5B&>!|n$J|`(30{=Rc3s}e(UIx-`|(6TM#K2`9Odj0 zl%H`I!Zm>lc><`jU)q3tX0@AChOx*q5g+%H{2t@?M%(kPgh`wNHLn~wE!2T!;y-R+ z_6<+`pexiW6U;&H0ol2*95%VK_wNiFJj`|2v5YVW!>7+{oa=>+-Y59cD+m~2ugoh+ zMOd^I4xjYJxTzIf7||`SpMCCiOnUQxZqGHTj=g5p@Q8O%PcRMN-tVypdI{gYZHx3& zo(#UZp#8yqPHVNaV|Pz-Uy4#)ZIaRV!NAr^Egdzt=yQhYCYl6G)jqJPaAvSsR|QH| z({85FbC4+K3tcx<@NV9-_xtnORXPAJwk*meWqe?z;HzBPnSY)&?`4o3ID~zYcjdFX zlSo96b$8-H7c7Ni3ll>!*uR3bG}-oRPJNA|L}@v}njgT$SOM|aL-ogs#ES}0D^M&C zkvVq*FUR)tE}$`s^sRuLK^1z^{097%0G=ylBRJfj*mC$0EBryB&X3_)sl`E*=B3yW zs_PQn_s5Ji|55h*XPR3b@ogti$8=>vfBT*_UrKRV3s#9P=I$hm E-=l3PZT>z(FtuA{Me^j G_#MgkYW* zmQXv1KC#<*%!EdqPs^KD{3_AV-FBbK%gECKQ9Vza5 kd>CZj_>60+AnPqsTW~icDl^|fWWmb7K58J?0nSKkS&qthOU;+;wSZc}( zgl{r`an}WLXp29aj;@fv5rxfmUcBxbJ(MIV(wYK2FnxTRn_&TnGtboCFuXTkb!)Gb z(qt8@5qASl=h&XILu*;@^4Ay4KdY=@3Nnp?EC-Koa}1m%#JX 1 zfJH%bx${J6)l%6tY2KB(`e0JYpimQo`JC2yg!~w&j-6>W`JqS84CyM;_?cuoR7&c5 z@j8>>i>IH~hx=zY7HtiN?VO+`?4RQj!C?V=_8u^$Z1UJ~3`@B>_yTXwKzgDu(rIOz z9(-$q6BNgPuIYvqmBox6oz!{gbh~`5?Zs~EjSCgnwm)yFbo@=b_GW8gQtl9Dpo^3f zy0ckwl3q#TH5fn!^reudm_-=Tr`FtI+-Q5`(&wUPvua_8lXS$&eW$OQLRoQu?tGQ6 zRSRIalu!4##fciCjMmCBh@+wp&*h?vfvq#D>9c^NgQR^~fA+gJBifR9!S6xd;L>5v z$F)}$Jo*j2#q2ThE-g)BT*+yVk~^QfZ#N! OF|l!cm>0|mjX?;5XuxsM2jSohd{J~6U9W>9AMy1M#OUq?&+(6wh?8(stPzKA z-RDrz6fAZO)6nz8MX^4PdI>krGBa$x=)h2{991u?d{8;0sL+@ BsU`LNQk%n(Qu_$Fh-Qdc1AT`FI9PEtz*GPhLoT^_Ez#k2mBOH+~I zzCEFnuC}&5IeT{L?cXtnV56K6Het!PW-%=(a|wK@^mEGX`nQ!+$xN6=?R7=ZAAg`} zZ?i-}@k<+c8_xt+b@wOFJ^-iV)F}{5h`v>XG%<~)@MRh`)p!`0jcD&nMyE5HxsoFn zJ>^Xjm>wG@YSC_TF};`;m-qs$vDff{_0@{_@^p69HBQbLnB+CT$G)wlkOGA&fTSa1 zG|ccha()=BcBtum{^`O;&dZ*;?{ll&WVbJ`^dPb%vlsvf36>?bI-$8SC1!LpXh`)z zEG&A1R8Ct|b U*IjR`XE(sYG_HQ zv9W=gmQ#DNu8tOde&S T9c>{Ru>G&3t28uUNz%3+=v$VF;kx^Q{ zZ2Kj~k8|m+&X5bbU)s1HVBz#{iH+HHk58HG&ZO%efiD?37M{sJ3rfd9(AT82xYx?| z{ic`t#M{4bPmC3F9J*{|XL0exDbO)}H4!ZFl2=7-a+aEF+fWr`Ur$TW6Z#VqCptsX xYT+dA*+o!f_gS9fC^!`OZG{z%9hyC^4hHV&ikN+r5+ooTz?OV`wC~06`wtm& 0) - { - // Verify data reaches all detectors - m_ob_detector.UpdateMarketData(rates); - m_bos_detector.UpdateMarketData(rates); - m_ls_detector.UpdateMarketData(rates); - m_fvg_detector.UpdateMarketData(rates); - - // Test data integrity - SOrderBlock order_blocks[]; - m_ob_detector.DetectOrderBlocks(order_blocks); - - test_passed = (ArraySize(order_blocks) >= 0); - } - else - { - test_passed = false; - } - } - catch(...) - { - test_passed = false; - } - - double execution_time = GetExecutionTime(start_time); - - SDataFlowTest result; - result.flow_name = "Market Data Flow"; - result.data_integrity_passed = test_passed; - result.timing_passed = (execution_time < 1000); // Less than 1 second - result.dependency_passed = test_passed; - result.latency_ms = execution_time; - result.error_details = test_passed ? "" : "Data flow failed"; - - int size = ArraySize(m_dataflow_results); - ArrayResize(m_dataflow_results, size + 1); - m_dataflow_results[size] = result; - - PrintTestProgress("Market Data Flow", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Signal Data Flow | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestSignalDataFlow() -{ - Print("Testing Signal Data Flow..."); - - datetime start_time = TimeCurrent(); - bool test_passed = true; - - try - { - // Test signal flow from detectors to entry strategy - SEntrySignal entry_signal; - bool signal_generated = m_entry_strategy.AnalyzeEntry(entry_signal); - - if(signal_generated) - { - // Test signal flow to risk manager - STradeRequest trade_request; - bool risk_validated = m_risk_manager.ValidateAndPrepareOrder(entry_signal, trade_request); - - // Test signal flow to session manager - bool session_allowed = m_session_manager.AllowTrade(entry_signal); - - test_passed = risk_validated && session_allowed; - } - } - catch(...) - { - test_passed = false; - } - - double execution_time = GetExecutionTime(start_time); - - SDataFlowTest result; - result.flow_name = "Signal Data Flow"; - result.data_integrity_passed = test_passed; - result.timing_passed = (execution_time < 500); // Less than 0.5 seconds - result.dependency_passed = test_passed; - result.latency_ms = execution_time; - result.error_details = test_passed ? "" : "Signal flow failed"; - - int size = ArraySize(m_dataflow_results); - ArrayResize(m_dataflow_results, size + 1); - m_dataflow_results[size] = result; - - PrintTestProgress("Signal Data Flow", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Risk Data Flow | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestRiskDataFlow() -{ - Print("Testing Risk Data Flow..."); - - datetime start_time = TimeCurrent(); - bool test_passed = true; - - try - { - // Test risk data flow - STradeRequest trade_request; - trade_request.symbol = TestSymbol; - trade_request.type = ORDER_TYPE_BUY; - trade_request.entry_price = SymbolInfoDouble(TestSymbol, SYMBOL_ASK); - trade_request.stop_loss = trade_request.entry_price - 50 * SymbolInfoDouble(TestSymbol, SYMBOL_POINT); - - // Test position size calculation - double position_size = m_risk_manager.CalculatePositionSize(trade_request); - - // Test risk validation - bool risk_valid = m_risk_manager.ValidateRisk(trade_request); - - test_passed = (position_size > 0 && risk_valid); - } - catch(...) - { - test_passed = false; - } - - double execution_time = GetExecutionTime(start_time); - - SDataFlowTest result; - result.flow_name = "Risk Data Flow"; - result.data_integrity_passed = test_passed; - result.timing_passed = (execution_time < 100); // Less than 0.1 seconds - result.dependency_passed = test_passed; - result.latency_ms = execution_time; - result.error_details = test_passed ? "" : "Risk flow failed"; - - int size = ArraySize(m_dataflow_results); - ArrayResize(m_dataflow_results, size + 1); - m_dataflow_results[size] = result; - - PrintTestProgress("Risk Data Flow", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Visualization Data Flow | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestVisualizationDataFlow() -{ - Print("Testing Visualization Data Flow..."); - - datetime start_time = TimeCurrent(); - bool test_passed = true; - - try - { - // Test visualization updates - SPerformanceMetrics metrics; - metrics.total_trades = 5; - metrics.winning_trades = 3; - metrics.net_profit = 500.0; - metrics.win_rate = 0.6; - - bool display_updated = m_chart_manager.UpdatePerformanceDisplay(metrics); - test_passed = display_updated; - } - catch(...) - { - test_passed = false; - } - - double execution_time = GetExecutionTime(start_time); - - SDataFlowTest result; - result.flow_name = "Visualization Data Flow"; - result.data_integrity_passed = test_passed; - result.timing_passed = (execution_time < 200); // Less than 0.2 seconds - result.dependency_passed = test_passed; - result.latency_ms = execution_time; - result.error_details = test_passed ? "" : "Visualization flow failed"; - - int size = ArraySize(m_dataflow_results); - ArrayResize(m_dataflow_results, size + 1); - m_dataflow_results[size] = result; - - PrintTestProgress("Visualization Data Flow", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Component Dependencies | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestComponentDependencies() -{ - Print("Testing Component Dependencies..."); - - // Test that components can work independently and together - bool test_passed = true; - - // Test individual component functionality - if(!m_ob_detector.IsInitialized()) test_passed = false; - if(!m_risk_manager.IsInitialized()) test_passed = false; - if(!m_session_manager.IsInitialized()) test_passed = false; - - PrintTestProgress("Component Dependencies", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Event Handling | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestEventHandling() -{ - Print("Testing Event Handling..."); - - // Test event propagation between components - bool test_passed = true; - - // Simulate market events and verify handling - // This would test OnTick, OnTimer, etc. event handling - - PrintTestProgress("Event Handling", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Memory Management | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestMemoryManagement() -{ - Print("Testing Memory Management..."); - - // Test for memory leaks and proper cleanup - bool test_passed = true; - - // Create and destroy components multiple times - for(int i = 0; i < 10; i++) - { - COrderBlockDetector* temp_detector = new COrderBlockDetector(); - temp_detector.Initialize(TestSymbol, TestTimeframe); - delete temp_detector; - } - - PrintTestProgress("Memory Management", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Thread Safety | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestThreadSafety() -{ - Print("Testing Thread Safety..."); - - // Test concurrent access to components - bool test_passed = true; - - // In MT5, this would test timer events vs tick events - - PrintTestProgress("Thread Safety", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test System Latency | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestSystemLatency() -{ - Print("Testing System Latency..."); - - datetime start_time = TimeCurrent(); - - // Test end-to-end latency - SEntrySignal entry_signal; - bool signal_generated = m_entry_strategy.AnalyzeEntry(entry_signal); - - double latency = GetExecutionTime(start_time); - bool test_passed = (latency < 100); // Less than 100ms - - PrintTestProgress("System Latency", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Memory Usage | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestMemoryUsage() -{ - Print("Testing Memory Usage..."); - - // Test memory consumption - bool test_passed = true; - - // Monitor memory usage during operations - // This is platform-specific and may not be directly available in MT5 - - PrintTestProgress("Memory Usage", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test CPU Usage | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestCPUUsage() -{ - Print("Testing CPU Usage..."); - - // Test CPU efficiency - bool test_passed = true; - - datetime start_time = TimeCurrent(); - - // Perform intensive operations - for(int i = 0; i < 1000; i++) - { - SEntrySignal entry_signal; - m_entry_strategy.AnalyzeEntry(entry_signal); - } - - double execution_time = GetExecutionTime(start_time); - test_passed = (execution_time < 5000); // Less than 5 seconds for 1000 operations - - PrintTestProgress("CPU Usage", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Invalid Input Handling | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestInvalidInputHandling() -{ - Print("Testing Invalid Input Handling..."); - - bool test_passed = true; - - try - { - // Test with invalid symbol - bool invalid_init = m_ob_detector.Initialize("INVALID", TestTimeframe); - if(invalid_init) test_passed = false; // Should fail - - // Test with invalid parameters - STradeRequest invalid_request; - invalid_request.symbol = ""; - invalid_request.entry_price = -1.0; - - bool invalid_risk = m_risk_manager.ValidateRisk(invalid_request); - if(invalid_risk) test_passed = false; // Should fail - } - catch(...) - { - // Exceptions are expected for invalid inputs - } - - PrintTestProgress("Invalid Input Handling", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Network Error Handling | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestNetworkErrorHandling() -{ - Print("Testing Network Error Handling..."); - - bool test_passed = true; - - // Test AI component with invalid API credentials - CGrokAI* test_ai = new CGrokAI(); - bool invalid_init = test_ai.Initialize("invalid_key", "invalid_url"); - - // Should handle gracefully without crashing - SFundamentalAnalysis analysis; - bool analysis_ok = test_ai.GetFundamentalAnalysis(TestSymbol, analysis); - - // Should fail gracefully - if(analysis_ok && invalid_init) test_passed = false; - - delete test_ai; - - PrintTestProgress("Network Error Handling", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Memory Error Handling | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestMemoryErrorHandling() -{ - Print("Testing Memory Error Handling..."); - - bool test_passed = true; - - // Test large array allocations - try - { - MqlRates large_array[]; - ArrayResize(large_array, 1000000); // Large allocation - ArrayFree(large_array); - } - catch(...) - { - // Should handle memory errors gracefully - } - - PrintTestProgress("Memory Error Handling", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Recovery Mechanisms | -//+------------------------------------------------------------------+ -bool CIntegrationTest::TestRecoveryMechanisms() -{ - Print("Testing Recovery Mechanisms..."); - - bool test_passed = true; - - // Test component recovery after errors - // Simulate error conditions and test recovery - - PrintTestProgress("Recovery Mechanisms", test_passed); - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Initialize Test Environment | -//+------------------------------------------------------------------+ -bool CIntegrationTest::InitializeTestEnvironment() -{ - Print("Initializing test environment..."); - - // Clear any existing test data - ArrayFree(m_test_results); - ArrayFree(m_dataflow_results); - - // Verify symbol availability - if(!SymbolSelect(TestSymbol, true)) - { - Print("Failed to select symbol: ", TestSymbol); - return false; - } - - return true; -} - -//+------------------------------------------------------------------+ -//| Load Test Data | -//+------------------------------------------------------------------+ -bool CIntegrationTest::LoadTestData() -{ - Print("Loading test data..."); - - // Load historical data for testing - int copied = CopyRates(TestSymbol, TestTimeframe, 0, TestBars, m_test_rates); - - if(copied < TestBars) - { - Print("Warning: Only ", copied, " bars copied instead of ", TestBars); - } - - return (copied > 0); -} - -//+------------------------------------------------------------------+ -//| Cleanup Test Environment | -//+------------------------------------------------------------------+ -void CIntegrationTest::CleanupTestEnvironment() -{ - // Clean up test data - ArrayFree(m_test_rates); - ArrayFree(m_test_results); - ArrayFree(m_dataflow_results); - - // Remove test objects from chart - if(m_chart_manager != NULL) - { - m_chart_manager.ClearAllObjects(); - } -} - -//+------------------------------------------------------------------+ -//| Create Test Result | -//+------------------------------------------------------------------+ -SIntegrationTestResult CIntegrationTest::CreateTestResult(string component, bool passed, double time_ms, string error = "") -{ - SIntegrationTestResult result; - result.component_name = component; - result.initialization_passed = passed; - result.functionality_passed = passed; - result.integration_passed = passed; - result.performance_passed = (time_ms < 1000); // Less than 1 second - result.error_handling_passed = passed; - result.execution_time_ms = time_ms; - result.error_message = error; - result.test_score = passed ? 100 : 0; - - return result; -} - -//+------------------------------------------------------------------+ -//| Print Test Progress | -//+------------------------------------------------------------------+ -void CIntegrationTest::PrintTestProgress(string test_name, bool passed) -{ - string status = passed ? "โ PASSED" : "โ FAILED"; - Print(" ", test_name, ": ", status); -} - -//+------------------------------------------------------------------+ -//| Get Execution Time | -//+------------------------------------------------------------------+ -double CIntegrationTest::GetExecutionTime(datetime start_time) -{ - return (double)(TimeCurrent() - start_time) * 1000.0; // Convert to milliseconds -} - -//+------------------------------------------------------------------+ -//| Generate Integration Report | -//+------------------------------------------------------------------+ -void CIntegrationTest::GenerateIntegrationReport() -{ - Print(""); - Print("=== Generating Integration Test Report ==="); - - string filename = "SniperEA_IntegrationReport_" + TestSymbol + "_" + - TimeToString(TimeCurrent(), TIME_DATE) + ".csv"; - - int file_handle = FileOpen(filename, FILE_WRITE | FILE_CSV); - - if(file_handle != INVALID_HANDLE) - { - // Write header - FileWrite(file_handle, "Component", "Initialization", "Functionality", "Integration", - "Performance", "Error Handling", "Execution Time (ms)", "Score", "Error Message"); - - // Write test results - for(int i = 0; i < ArraySize(m_test_results); i++) - { - SIntegrationTestResult& result = m_test_results[i]; - FileWrite(file_handle, - result.component_name, - result.initialization_passed ? "PASS" : "FAIL", - result.functionality_passed ? "PASS" : "FAIL", - result.integration_passed ? "PASS" : "FAIL", - result.performance_passed ? "PASS" : "FAIL", - result.error_handling_passed ? "PASS" : "FAIL", - DoubleToString(result.execution_time_ms, 2), - result.test_score, - result.error_message); - } - - // Write data flow results - FileWrite(file_handle, "", "", "", "", "", "", "", "", ""); - FileWrite(file_handle, "DATA FLOW TESTS", "", "", "", "", "", "", "", ""); - FileWrite(file_handle, "Flow Name", "Data Integrity", "Timing", "Dependencies", - "Latency (ms)", "", "", "", "Error Details"); - - for(int i = 0; i < ArraySize(m_dataflow_results); i++) - { - SDataFlowTest& result = m_dataflow_results[i]; - FileWrite(file_handle, - result.flow_name, - result.data_integrity_passed ? "PASS" : "FAIL", - result.timing_passed ? "PASS" : "FAIL", - result.dependency_passed ? "PASS" : "FAIL", - DoubleToString(result.latency_ms, 2), - "", "", "", - result.error_details); - } - - FileClose(file_handle); - Print("Integration test report saved to: ", filename); - } - else - { - Print("Failed to save integration test report"); - } -} - -//+------------------------------------------------------------------+ -//| Script start function | -//+------------------------------------------------------------------+ -void OnStart() -{ - Print("Starting MT5 Sniper EA Integration Tests..."); - Print("This will verify that all components work together correctly."); - Print(""); - - CIntegrationTest* integration_test = new CIntegrationTest(); - - bool success = integration_test.RunIntegrationTests(); - - if(success) - { - Print(""); - Print("๐ฏ All integration tests passed successfully!"); - Print("The EA components are properly integrated and working together."); - } - else - { - Print(""); - Print("โ ๏ธ Some integration tests failed. Please check the detailed report."); - } - - delete integration_test; - - Print("Integration testing completed."); -} \ No newline at end of file diff --git a/src/Tests/NewsSystemTest.mq5 b/src/Tests/NewsSystemTest.mq5 deleted file mode 100644 index 9a222ef..0000000 --- a/src/Tests/NewsSystemTest.mq5 +++ /dev/null @@ -1,702 +0,0 @@ -//+------------------------------------------------------------------+ -//| NewsSystemTest.mq5 | -//| Copyright 2024, MetaQuotes Ltd. | -//| https://www.mql5.com | -//+------------------------------------------------------------------+ -#property copyright "Copyright 2024, MetaQuotes Ltd." -#property link "https://www.mql5.com" -#property version "1.00" -#property script_show_inputs - -//--- Include test framework and news system components -#include "../Include/Utils/Logger.mqh" -#include "../Include/Utils/NewsManager.mqh" -#include "../Include/Utils/FundamentalAnalysis.mqh" -#include "../Include/Utils/NewsFilter.mqh" - -//--- Test parameters -input bool EnableDetailedLogging = true; -input bool TestNewsManager = true; -input bool TestFundamentalAnalysis = true; -input bool TestNewsFilter = true; -input bool TestIntegration = true; - -//--- Global test variables -CLogger* g_testLogger; -CNewsManager* g_newsManager; -CFundamentalAnalysis* g_fundamentalAnalysis; -CNewsFilter* g_newsFilter; - -int g_totalTests = 0; -int g_passedTests = 0; -int g_failedTests = 0; - -//+------------------------------------------------------------------+ -//| Script program start function | -//+------------------------------------------------------------------+ -void OnStart() { - Print("=== NEWS SYSTEM COMPREHENSIVE TEST SUITE ==="); - Print("Starting news avoidance and fundamental analysis tests..."); - - // Initialize test environment - if(!InitializeTestEnvironment()) { - Print("ERROR: Failed to initialize test environment"); - return; - } - - // Run test suites - if(TestNewsManager) RunNewsManagerTests(); - if(TestFundamentalAnalysis) RunFundamentalAnalysisTests(); - if(TestNewsFilter) RunNewsFilterTests(); - if(TestIntegration) RunIntegrationTests(); - - // Generate test report - GenerateTestReport(); - - // Cleanup - CleanupTestEnvironment(); - - Print("=== NEWS SYSTEM TEST SUITE COMPLETED ==="); -} - -//+------------------------------------------------------------------+ -//| Initialize test environment | -//+------------------------------------------------------------------+ -bool InitializeTestEnvironment() { - g_testLogger = new CLogger(); - if(g_testLogger == NULL) return false; - g_testLogger.Initialize(EnableDetailedLogging, LOG_LEVEL_DEBUG); - - g_newsManager = new CNewsManager(); - g_fundamentalAnalysis = new CFundamentalAnalysis(); - g_newsFilter = new CNewsFilter(); - - if(g_newsManager == NULL || g_fundamentalAnalysis == NULL || g_newsFilter == NULL) { - return false; - } - - // Initialize components - if(!g_newsManager.Initialize()) return false; - if(!g_fundamentalAnalysis.Initialize()) return false; - if(!g_newsFilter.Initialize()) return false; - - g_testLogger.Info("Test environment initialized successfully"); - return true; -} - -//+------------------------------------------------------------------+ -//| Run News Manager Tests | -//+------------------------------------------------------------------+ -void RunNewsManagerTests() { - Print("\n--- TESTING NEWS MANAGER ---"); - - // Test 1: News Event Management - TestNewsEventManagement(); - - // Test 2: High Impact News Detection - TestHighImpactNewsDetection(); - - // Test 3: Trading Restrictions - TestTradingRestrictions(); - - // Test 4: Emergency Controls - TestEmergencyControls(); - - // Test 5: Data Updates - TestNewsDataUpdates(); -} - -//+------------------------------------------------------------------+ -//| Test News Event Management | -//+------------------------------------------------------------------+ -void TestNewsEventManagement() { - string testName = "News Event Management"; - g_totalTests++; - - try { - // Add test news event - SNewsEvent testEvent; - testEvent.title = "Test NFP Release"; - testEvent.currency = "USD"; - testEvent.impact = NEWS_IMPACT_HIGH; - testEvent.type = NEWS_TYPE_EMPLOYMENT; - testEvent.releaseTime = TimeCurrent() + 3600; // 1 hour from now - testEvent.isActive = true; - - bool added = g_newsManager.AddNewsEvent(testEvent); - - // Check if event was added - SNewsEvent retrievedEvents[]; - int count = g_newsManager.GetUpcomingNews(retrievedEvents, 24); - - bool found = false; - for(int i = 0; i < count; i++) { - if(retrievedEvents[i].title == "Test NFP Release") { - found = true; - break; - } - } - - if(added && found) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Event not properly managed"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test High Impact News Detection | -//+------------------------------------------------------------------+ -void TestHighImpactNewsDetection() { - string testName = "High Impact News Detection"; - g_totalTests++; - - try { - // Add high impact news event for current time - SNewsEvent highImpactEvent; - highImpactEvent.title = "Test High Impact Event"; - highImpactEvent.currency = "USD"; - highImpactEvent.impact = NEWS_IMPACT_HIGH; - highImpactEvent.type = NEWS_TYPE_MONETARY_POLICY; - highImpactEvent.releaseTime = TimeCurrent(); - highImpactEvent.isActive = true; - - g_newsManager.AddNewsEvent(highImpactEvent); - - // Test detection - bool isHighImpactTime = g_newsManager.IsHighImpactNewsTime(); - - if(isHighImpactTime) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - High impact news not detected"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Trading Restrictions | -//+------------------------------------------------------------------+ -void TestTradingRestrictions() { - string testName = "Trading Restrictions"; - g_totalTests++; - - try { - // Set trading restriction - STradingRestriction restriction; - restriction.startTime = TimeCurrent(); - restriction.endTime = TimeCurrent() + 1800; // 30 minutes - restriction.reason = "Test Restriction"; - restriction.isActive = true; - - g_newsManager.SetTradingRestriction(restriction); - - // Test if trading is restricted - bool isRestricted = g_newsManager.IsTradingRestricted(); - - if(isRestricted) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Trading restriction not applied"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Emergency Controls | -//+------------------------------------------------------------------+ -void TestEmergencyControls() { - string testName = "Emergency Controls"; - g_totalTests++; - - try { - // Test emergency stop - g_newsManager.ActivateEmergencyStop("Test Emergency"); - - bool isEmergencyActive = g_newsManager.IsEmergencyStopActive(); - - if(isEmergencyActive) { - // Test deactivation - g_newsManager.DeactivateEmergencyStop(); - bool isStillActive = g_newsManager.IsEmergencyStopActive(); - - if(!isStillActive) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Emergency stop not deactivated"); - } - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Emergency stop not activated"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test News Data Updates | -//+------------------------------------------------------------------+ -void TestNewsDataUpdates() { - string testName = "News Data Updates"; - g_totalTests++; - - try { - // Test data update functionality - datetime lastUpdate = g_newsManager.GetLastUpdateTime(); - - g_newsManager.UpdateNewsData(); - - datetime newUpdateTime = g_newsManager.GetLastUpdateTime(); - - if(newUpdateTime >= lastUpdate) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Data not updated"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Run Fundamental Analysis Tests | -//+------------------------------------------------------------------+ -void RunFundamentalAnalysisTests() { - Print("\n--- TESTING FUNDAMENTAL ANALYSIS ---"); - - // Test 1: Factor Management - TestFactorManagement(); - - // Test 2: Impact Analysis - TestImpactAnalysis(); - - // Test 3: Currency Strength Analysis - TestCurrencyStrengthAnalysis(); - - // Test 4: Trading Avoidance Logic - TestTradingAvoidanceLogic(); -} - -//+------------------------------------------------------------------+ -//| Test Factor Management | -//+------------------------------------------------------------------+ -void TestFactorManagement() { - string testName = "Factor Management"; - g_totalTests++; - - try { - // Add test fundamental factor - SFundamentalFactor testFactor; - testFactor.name = "Test Interest Rate"; - testFactor.category = INDICATOR_MONETARY_POLICY; - testFactor.currency = "USD"; - testFactor.currentValue = 5.25; - testFactor.previousValue = 5.00; - testFactor.expectedValue = 5.50; - testFactor.impact = IMPACT_HIGH; - testFactor.lastUpdate = TimeCurrent(); - - bool added = g_fundamentalAnalysis.AddFactor(testFactor); - - // Retrieve and verify - SFundamentalFactor retrievedFactors[]; - int count = g_fundamentalAnalysis.GetFactorsByCurrency("USD", retrievedFactors); - - bool found = false; - for(int i = 0; i < count; i++) { - if(retrievedFactors[i].name == "Test Interest Rate") { - found = true; - break; - } - } - - if(added && found) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Factor not properly managed"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Impact Analysis | -//+------------------------------------------------------------------+ -void TestImpactAnalysis() { - string testName = "Impact Analysis"; - g_totalTests++; - - try { - // Test impact calculation - double impact = g_fundamentalAnalysis.CalculateOverallImpact("EURUSD"); - - if(impact >= 0.0 && impact <= 1.0) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - Impact: " + DoubleToString(impact, 3)); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Invalid impact value: " + DoubleToString(impact, 3)); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Currency Strength Analysis | -//+------------------------------------------------------------------+ -void TestCurrencyStrengthAnalysis() { - string testName = "Currency Strength Analysis"; - g_totalTests++; - - try { - // Test currency strength calculation - double usdStrength = g_fundamentalAnalysis.GetCurrencyStrength("USD"); - double eurStrength = g_fundamentalAnalysis.GetCurrencyStrength("EUR"); - - if(usdStrength >= -1.0 && usdStrength <= 1.0 && - eurStrength >= -1.0 && eurStrength <= 1.0) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - USD: " + DoubleToString(usdStrength, 3) + - ", EUR: " + DoubleToString(eurStrength, 3)); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Invalid strength values"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Trading Avoidance Logic | -//+------------------------------------------------------------------+ -void TestTradingAvoidanceLogic() { - string testName = "Trading Avoidance Logic"; - g_totalTests++; - - try { - // Test should avoid trading logic - bool shouldAvoid = g_fundamentalAnalysis.ShouldAvoidTrading(); - - // The result should be boolean - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - Should avoid: " + (shouldAvoid ? "Yes" : "No")); - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Run News Filter Tests | -//+------------------------------------------------------------------+ -void RunNewsFilterTests() { - Print("\n--- TESTING NEWS FILTER ---"); - - // Test 1: Filter Rule Management - TestFilterRuleManagement(); - - // Test 2: Trade Evaluation - TestTradeEvaluation(); - - // Test 3: Performance Monitoring - TestPerformanceMonitoring(); -} - -//+------------------------------------------------------------------+ -//| Test Filter Rule Management | -//+------------------------------------------------------------------+ -void TestFilterRuleManagement() { - string testName = "Filter Rule Management"; - g_totalTests++; - - try { - // Add test filter rule - SFilterRule testRule; - testRule.name = "Test High Volatility Rule"; - testRule.type = RULE_TYPE_VOLATILITY; - testRule.condition = "volatility > 0.8"; - testRule.action = FILTER_ACTION_BLOCK; - testRule.priority = 1; - testRule.isActive = true; - - bool added = g_newsFilter.AddRule(testRule); - - // Test rule retrieval - SFilterRule retrievedRules[]; - int count = g_newsFilter.GetActiveRules(retrievedRules); - - bool found = false; - for(int i = 0; i < count; i++) { - if(retrievedRules[i].name == "Test High Volatility Rule") { - found = true; - break; - } - } - - if(added && found) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Rule not properly managed"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Trade Evaluation | -//+------------------------------------------------------------------+ -void TestTradeEvaluation() { - string testName = "Trade Evaluation"; - g_totalTests++; - - try { - // Test trade condition evaluation - SFilterDecision decision = g_newsFilter.EvaluateTradeConditions("EURUSD"); - - // Verify decision structure - if(decision.action == FILTER_ACTION_ALLOW || - decision.action == FILTER_ACTION_BLOCK || - decision.action == FILTER_ACTION_DELAY) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - Action: " + EnumToString(decision.action)); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Invalid decision action"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Performance Monitoring | -//+------------------------------------------------------------------+ -void TestPerformanceMonitoring() { - string testName = "Performance Monitoring"; - g_totalTests++; - - try { - // Update performance metrics - g_newsFilter.UpdatePerformanceMetrics(); - - // Test should complete without errors - g_passedTests++; - g_testLogger.Info(testName + ": PASSED"); - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Run Integration Tests | -//+------------------------------------------------------------------+ -void RunIntegrationTests() { - Print("\n--- TESTING SYSTEM INTEGRATION ---"); - - // Test 1: Component Communication - TestComponentCommunication(); - - // Test 2: End-to-End News Filtering - TestEndToEndNewsFiltering(); - - // Test 3: Performance Under Load - TestPerformanceUnderLoad(); -} - -//+------------------------------------------------------------------+ -//| Test Component Communication | -//+------------------------------------------------------------------+ -void TestComponentCommunication() { - string testName = "Component Communication"; - g_totalTests++; - - try { - // Test communication between components - // Add news event that should trigger fundamental analysis - SNewsEvent event; - event.title = "Integration Test Event"; - event.currency = "USD"; - event.impact = NEWS_IMPACT_HIGH; - event.type = NEWS_TYPE_MONETARY_POLICY; - event.releaseTime = TimeCurrent(); - event.isActive = true; - - g_newsManager.AddNewsEvent(event); - - // Check if fundamental analysis responds - bool shouldAvoid = g_fundamentalAnalysis.ShouldAvoidTrading(); - - // Check if news filter responds - SFilterDecision decision = g_newsFilter.EvaluateTradeConditions("EURUSD"); - - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - Components communicating"); - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test End-to-End News Filtering | -//+------------------------------------------------------------------+ -void TestEndToEndNewsFiltering() { - string testName = "End-to-End News Filtering"; - g_totalTests++; - - try { - // Simulate complete news filtering workflow - - // 1. Add high impact news - SNewsEvent highImpactNews; - highImpactNews.title = "E2E Test NFP"; - highImpactNews.currency = "USD"; - highImpactNews.impact = NEWS_IMPACT_HIGH; - highImpactNews.type = NEWS_TYPE_EMPLOYMENT; - highImpactNews.releaseTime = TimeCurrent(); - highImpactNews.isActive = true; - - g_newsManager.AddNewsEvent(highImpactNews); - - // 2. Check news manager response - bool isHighImpact = g_newsManager.IsHighImpactNewsTime(); - - // 3. Check fundamental analysis response - bool shouldAvoidFundamental = g_fundamentalAnalysis.ShouldAvoidTrading(); - - // 4. Check news filter response - SFilterDecision filterDecision = g_newsFilter.EvaluateTradeConditions("EURUSD"); - - // 5. Verify end-to-end blocking - bool systemBlocked = isHighImpact || shouldAvoidFundamental || - (filterDecision.action == FILTER_ACTION_BLOCK); - - if(systemBlocked) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - System properly blocked trading"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - System did not block trading as expected"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Test Performance Under Load | -//+------------------------------------------------------------------+ -void TestPerformanceUnderLoad() { - string testName = "Performance Under Load"; - g_totalTests++; - - try { - uint startTime = GetTickCount(); - - // Simulate load by performing multiple operations - for(int i = 0; i < 100; i++) { - g_newsManager.IsHighImpactNewsTime(); - g_fundamentalAnalysis.ShouldAvoidTrading(); - g_newsFilter.EvaluateTradeConditions("EURUSD"); - } - - uint endTime = GetTickCount(); - uint duration = endTime - startTime; - - // Performance should be reasonable (less than 1 second for 100 operations) - if(duration < 1000) { - g_passedTests++; - g_testLogger.Info(testName + ": PASSED - Duration: " + IntegerToString(duration) + "ms"); - } else { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - Performance too slow: " + IntegerToString(duration) + "ms"); - } - - } catch(string error) { - g_failedTests++; - g_testLogger.Error(testName + ": FAILED - " + error); - } -} - -//+------------------------------------------------------------------+ -//| Generate Test Report | -//+------------------------------------------------------------------+ -void GenerateTestReport() { - Print("\n=== NEWS SYSTEM TEST REPORT ==="); - Print("Total Tests: " + IntegerToString(g_totalTests)); - Print("Passed: " + IntegerToString(g_passedTests)); - Print("Failed: " + IntegerToString(g_failedTests)); - - double successRate = (g_totalTests > 0) ? (double)g_passedTests / g_totalTests * 100.0 : 0.0; - Print("Success Rate: " + DoubleToString(successRate, 1) + "%"); - - if(g_failedTests == 0) { - Print("STATUS: ALL TESTS PASSED โ"); - } else { - Print("STATUS: " + IntegerToString(g_failedTests) + " TESTS FAILED โ"); - } - - Print("================================"); -} - -//+------------------------------------------------------------------+ -//| Cleanup Test Environment | -//+------------------------------------------------------------------+ -void CleanupTestEnvironment() { - if(g_newsManager != NULL) { delete g_newsManager; g_newsManager = NULL; } - if(g_fundamentalAnalysis != NULL) { delete g_fundamentalAnalysis; g_fundamentalAnalysis = NULL; } - if(g_newsFilter != NULL) { delete g_newsFilter; g_newsFilter = NULL; } - if(g_testLogger != NULL) { delete g_testLogger; g_testLogger = NULL; } -} \ No newline at end of file diff --git a/src/Tests/OptimizationTest.mq5 b/src/Tests/OptimizationTest.mq5 deleted file mode 100644 index fe8255f..0000000 --- a/src/Tests/OptimizationTest.mq5 +++ /dev/null @@ -1,960 +0,0 @@ -//+------------------------------------------------------------------+ -//| OptimizationTest.mq5 | -//| MT5 Sniper EA - Optimization | -//| | -//+------------------------------------------------------------------+ -#property copyright "MT5 Sniper EA" -#property version "1.00" -#property description "Parameter optimization tests for MT5 Sniper EA" -#property script_show_inputs - -// Include all EA components -#include "../Include/MarketStructure/OrderBlockDetector.mqh" -#include "../Include/MarketStructure/BOSDetector.mqh" -#include "../Include/MarketStructure/LiquiditySweepDetector.mqh" -#include "../Include/MarketStructure/FVGDetector.mqh" -#include "../Include/MarketStructure/EntryStrategy.mqh" -#include "../Include/RiskManagement/RiskManager.mqh" -#include "../Include/SessionManagement/SessionManager.mqh" -#include "../Include/AIIntegration/GrokAI.mqh" -#include "../Include/Visualization/ChartManager.mqh" -#include "../Include/Utils/Backtester.mqh" - -// Input parameters -input string OptimizationSymbol = "EURUSD"; // Symbol to optimize -input ENUM_TIMEFRAMES OptimizationTimeframe = PERIOD_H1; // Timeframe to optimize -input datetime OptimizationStartDate = D'2023.01.01'; // Optimization start date -input datetime OptimizationEndDate = D'2023.12.31'; // Optimization end date -input double InitialBalance = 10000.0; // Initial balance for optimization -input int MaxIterations = 1000; // Maximum optimization iterations -input bool OptimizeRiskParameters = true; // Optimize risk management parameters -input bool OptimizeEntryParameters = true; // Optimize entry strategy parameters -input bool OptimizeSessionParameters = true; // Optimize session parameters -input bool GenerateOptimizationReport = true; // Generate optimization report - -//+------------------------------------------------------------------+ -//| Parameter Set Structure | -//+------------------------------------------------------------------+ -struct SParameterSet -{ - // Risk management parameters - double risk_percent; - double max_risk_percent; - double daily_loss_limit; - double max_drawdown_percent; - - // Entry strategy parameters - double min_confluence_score; - int lookback_periods; - double ob_strength_threshold; - double bos_strength_threshold; - double fvg_size_threshold; - - // Session parameters - bool trade_asia; - bool trade_london; - bool trade_ny; - int avoid_news_minutes; - double min_volatility; - double max_volatility; - - // Performance metrics - double net_profit; - double profit_factor; - double win_rate; - double max_drawdown; - double sharpe_ratio; - double recovery_factor; - int total_trades; - double fitness_score; -}; - -//+------------------------------------------------------------------+ -//| Optimization Result Structure | -//+------------------------------------------------------------------+ -struct SOptimizationResult -{ - SParameterSet best_parameters; - SParameterSet worst_parameters; - double best_fitness; - double worst_fitness; - int total_iterations; - int successful_iterations; - datetime optimization_time; -}; - -//+------------------------------------------------------------------+ -//| Optimization Test Class | -//+------------------------------------------------------------------+ -class COptimizationTest -{ -private: - // Test components - COrderBlockDetector* m_ob_detector; - CBOSDetector* m_bos_detector; - CLiquiditySweepDetector* m_ls_detector; - CFVGDetector* m_fvg_detector; - CEntryStrategy* m_entry_strategy; - CRiskManager* m_risk_manager; - CSessionManager* m_session_manager; - CGrokAI* m_grok_ai; - CChartManager* m_chart_manager; - CBacktester* m_backtester; - - // Optimization data - SParameterSet m_parameter_sets[]; - SOptimizationResult m_result; - - // Parameter ranges - struct SParameterRanges - { - double risk_percent_min, risk_percent_max, risk_percent_step; - double confluence_min, confluence_max, confluence_step; - int lookback_min, lookback_max, lookback_step; - double ob_strength_min, ob_strength_max, ob_strength_step; - int news_avoid_min, news_avoid_max, news_avoid_step; - double volatility_min, volatility_max, volatility_step; - } m_ranges; - -public: - COptimizationTest(); - ~COptimizationTest(); - - // Main optimization functions - bool RunOptimization(); - void GenerateOptimizationReport(); - - // Optimization methods - bool BruteForceOptimization(); - bool GeneticAlgorithmOptimization(); - bool GridSearchOptimization(); - bool RandomSearchOptimization(); - - // Parameter generation - void GenerateParameterSets(); - void GenerateRandomParameterSet(SParameterSet& params); - void MutateParameterSet(SParameterSet& params, double mutation_rate); - SParameterSet CrossoverParameterSets(const SParameterSet& parent1, const SParameterSet& parent2); - - // Evaluation functions - double EvaluateParameterSet(const SParameterSet& params); - double CalculateFitnessScore(const SBacktestStats& stats); - bool BacktestParameterSet(const SParameterSet& params, SBacktestStats& stats); - - // Utility functions - void InitializeParameterRanges(); - void ApplyParametersToComponents(const SParameterSet& params); - void SortParameterSetsByFitness(); - void PrintOptimizationProgress(int current, int total); - void SaveOptimizationResults(); -}; - -//+------------------------------------------------------------------+ -//| Constructor | -//+------------------------------------------------------------------+ -COptimizationTest::COptimizationTest() -{ - // Initialize components - m_ob_detector = new COrderBlockDetector(); - m_bos_detector = new CBOSDetector(); - m_ls_detector = new CLiquiditySweepDetector(); - m_fvg_detector = new CFVGDetector(); - m_entry_strategy = new CEntryStrategy(); - m_risk_manager = new CRiskManager(); - m_session_manager = new CSessionManager(); - m_grok_ai = new CGrokAI(); - m_chart_manager = new CChartManager(); - m_backtester = new CBacktester(); - - InitializeParameterRanges(); -} - -//+------------------------------------------------------------------+ -//| Destructor | -//+------------------------------------------------------------------+ -COptimizationTest::~COptimizationTest() -{ - delete m_ob_detector; - delete m_bos_detector; - delete m_ls_detector; - delete m_fvg_detector; - delete m_entry_strategy; - delete m_risk_manager; - delete m_session_manager; - delete m_grok_ai; - delete m_chart_manager; - delete m_backtester; -} - -//+------------------------------------------------------------------+ -//| Run Optimization | -//+------------------------------------------------------------------+ -bool COptimizationTest::RunOptimization() -{ - Print("=== Starting MT5 Sniper EA Parameter Optimization ==="); - Print("Symbol: ", OptimizationSymbol); - Print("Timeframe: ", EnumToString(OptimizationTimeframe)); - Print("Period: ", TimeToString(OptimizationStartDate), " - ", TimeToString(OptimizationEndDate)); - Print("Max Iterations: ", MaxIterations); - Print(""); - - // Initialize components - m_ob_detector.Initialize(OptimizationSymbol, OptimizationTimeframe); - m_bos_detector.Initialize(OptimizationSymbol, OptimizationTimeframe); - m_ls_detector.Initialize(OptimizationSymbol, OptimizationTimeframe); - m_fvg_detector.Initialize(OptimizationSymbol, OptimizationTimeframe); - m_entry_strategy.Initialize(OptimizationSymbol, OptimizationTimeframe); - m_risk_manager.Initialize(); - m_session_manager.Initialize(); - m_grok_ai.Initialize("test_key", "test_url"); - m_chart_manager.Initialize(ChartID()); - m_backtester.Initialize(); - - // Configure backtester - SBacktestConfig config; - config.start_date = OptimizationStartDate; - config.end_date = OptimizationEndDate; - config.initial_balance = InitialBalance; - config.spread = 1.5; - config.commission = 7.0; - config.mode = BACKTEST_MODE_OPTIMIZATION; - - m_backtester.Configure(config); - m_backtester.SetEntryStrategy(m_entry_strategy); - m_backtester.SetRiskManager(m_risk_manager); - m_backtester.SetSessionManager(m_session_manager); - - datetime start_time = TimeCurrent(); - - // Run optimization using genetic algorithm (most effective for complex parameter spaces) - bool success = GeneticAlgorithmOptimization(); - - m_result.optimization_time = TimeCurrent() - start_time; - - if(success && GenerateOptimizationReport) - GenerateOptimizationReport(); - - return success; -} - -//+------------------------------------------------------------------+ -//| Genetic Algorithm Optimization | -//+------------------------------------------------------------------+ -bool COptimizationTest::GeneticAlgorithmOptimization() -{ - Print("Running Genetic Algorithm Optimization..."); - - const int population_size = 50; - const int generations = MaxIterations / population_size; - const double mutation_rate = 0.1; - const double crossover_rate = 0.8; - const int elite_count = 5; - - // Initialize population - ArrayResize(m_parameter_sets, population_size); - - Print("Generating initial population..."); - for(int i = 0; i < population_size; i++) - { - GenerateRandomParameterSet(m_parameter_sets[i]); - m_parameter_sets[i].fitness_score = EvaluateParameterSet(m_parameter_sets[i]); - - if(i % 10 == 0) - PrintOptimizationProgress(i + 1, population_size); - } - - // Sort by fitness - SortParameterSetsByFitness(); - - Print("Initial population generated. Best fitness: ", DoubleToString(m_parameter_sets[0].fitness_score, 2)); - - // Evolution loop - for(int gen = 0; gen < generations; gen++) - { - Print("Generation ", gen + 1, "/", generations); - - SParameterSet new_population[]; - ArrayResize(new_population, population_size); - - // Keep elite individuals - for(int i = 0; i < elite_count; i++) - { - new_population[i] = m_parameter_sets[i]; - } - - // Generate offspring - for(int i = elite_count; i < population_size; i++) - { - if(MathRand() / 32767.0 < crossover_rate) - { - // Crossover - int parent1_idx = (int)(MathRand() / 32767.0 * elite_count * 2); - int parent2_idx = (int)(MathRand() / 32767.0 * elite_count * 2); - - new_population[i] = CrossoverParameterSets(m_parameter_sets[parent1_idx], - m_parameter_sets[parent2_idx]); - } - else - { - // Copy parent - int parent_idx = (int)(MathRand() / 32767.0 * elite_count * 2); - new_population[i] = m_parameter_sets[parent_idx]; - } - - // Mutation - if(MathRand() / 32767.0 < mutation_rate) - { - MutateParameterSet(new_population[i], mutation_rate); - } - - // Evaluate fitness - new_population[i].fitness_score = EvaluateParameterSet(new_population[i]); - } - - // Replace population - ArrayCopy(m_parameter_sets, new_population); - SortParameterSetsByFitness(); - - Print("Best fitness: ", DoubleToString(m_parameter_sets[0].fitness_score, 2)); - - // Early stopping if no improvement - if(gen > 10) - { - bool improved = false; - for(int i = 0; i < 5; i++) - { - if(m_parameter_sets[i].fitness_score > m_result.best_fitness) - { - improved = true; - break; - } - } - - if(!improved) - { - Print("No improvement detected. Stopping early."); - break; - } - } - - // Update best result - if(m_parameter_sets[0].fitness_score > m_result.best_fitness) - { - m_result.best_parameters = m_parameter_sets[0]; - m_result.best_fitness = m_parameter_sets[0].fitness_score; - } - - m_result.total_iterations = (gen + 1) * population_size; - } - - // Set final results - m_result.best_parameters = m_parameter_sets[0]; - m_result.worst_parameters = m_parameter_sets[population_size - 1]; - m_result.best_fitness = m_parameter_sets[0].fitness_score; - m_result.worst_fitness = m_parameter_sets[population_size - 1].fitness_score; - m_result.successful_iterations = m_result.total_iterations; - - Print("Genetic Algorithm Optimization completed."); - Print("Best fitness achieved: ", DoubleToString(m_result.best_fitness, 2)); - - return true; -} - -//+------------------------------------------------------------------+ -//| Grid Search Optimization | -//+------------------------------------------------------------------+ -bool COptimizationTest::GridSearchOptimization() -{ - Print("Running Grid Search Optimization..."); - - // Calculate grid dimensions - int risk_steps = (int)((m_ranges.risk_percent_max - m_ranges.risk_percent_min) / m_ranges.risk_percent_step) + 1; - int confluence_steps = (int)((m_ranges.confluence_max - m_ranges.confluence_min) / m_ranges.confluence_step) + 1; - int lookback_steps = (int)((m_ranges.lookback_max - m_ranges.lookback_min) / m_ranges.lookback_step) + 1; - - int total_combinations = risk_steps * confluence_steps * lookback_steps; - - if(total_combinations > MaxIterations) - { - Print("Too many combinations (", total_combinations, "). Reducing grid resolution."); - // Reduce resolution by increasing step sizes - m_ranges.risk_percent_step *= 2; - m_ranges.confluence_step *= 2; - m_ranges.lookback_step *= 2; - - risk_steps = (int)((m_ranges.risk_percent_max - m_ranges.risk_percent_min) / m_ranges.risk_percent_step) + 1; - confluence_steps = (int)((m_ranges.confluence_max - m_ranges.confluence_min) / m_ranges.confluence_step) + 1; - lookback_steps = (int)((m_ranges.lookback_max - m_ranges.lookback_min) / m_ranges.lookback_step) + 1; - total_combinations = risk_steps * confluence_steps * lookback_steps; - } - - Print("Grid dimensions: ", risk_steps, " x ", confluence_steps, " x ", lookback_steps); - Print("Total combinations: ", total_combinations); - - m_result.best_fitness = -999999; - m_result.worst_fitness = 999999; - - int iteration = 0; - - // Grid search loop - for(int r = 0; r < risk_steps; r++) - { - double risk_percent = m_ranges.risk_percent_min + (r * m_ranges.risk_percent_step); - - for(int c = 0; c < confluence_steps; c++) - { - double confluence = m_ranges.confluence_min + (c * m_ranges.confluence_step); - - for(int l = 0; l < lookback_steps; l++) - { - int lookback = m_ranges.lookback_min + (l * m_ranges.lookback_step); - - iteration++; - - // Create parameter set - SParameterSet params; - GenerateRandomParameterSet(params); // Base parameters - - // Override with grid values - params.risk_percent = risk_percent; - params.min_confluence_score = confluence; - params.lookback_periods = lookback; - - // Evaluate - double fitness = EvaluateParameterSet(params); - params.fitness_score = fitness; - - // Update best/worst - if(fitness > m_result.best_fitness) - { - m_result.best_parameters = params; - m_result.best_fitness = fitness; - } - - if(fitness < m_result.worst_fitness) - { - m_result.worst_parameters = params; - m_result.worst_fitness = fitness; - } - - if(iteration % 50 == 0) - PrintOptimizationProgress(iteration, total_combinations); - } - } - } - - m_result.total_iterations = iteration; - m_result.successful_iterations = iteration; - - Print("Grid Search Optimization completed."); - Print("Best fitness achieved: ", DoubleToString(m_result.best_fitness, 2)); - - return true; -} - -//+------------------------------------------------------------------+ -//| Random Search Optimization | -//+------------------------------------------------------------------+ -bool COptimizationTest::RandomSearchOptimization() -{ - Print("Running Random Search Optimization..."); - - m_result.best_fitness = -999999; - m_result.worst_fitness = 999999; - - for(int i = 0; i < MaxIterations; i++) - { - SParameterSet params; - GenerateRandomParameterSet(params); - - double fitness = EvaluateParameterSet(params); - params.fitness_score = fitness; - - // Update best/worst - if(fitness > m_result.best_fitness) - { - m_result.best_parameters = params; - m_result.best_fitness = fitness; - } - - if(fitness < m_result.worst_fitness) - { - m_result.worst_parameters = params; - m_result.worst_fitness = fitness; - } - - if(i % 100 == 0) - PrintOptimizationProgress(i + 1, MaxIterations); - } - - m_result.total_iterations = MaxIterations; - m_result.successful_iterations = MaxIterations; - - Print("Random Search Optimization completed."); - Print("Best fitness achieved: ", DoubleToString(m_result.best_fitness, 2)); - - return true; -} - -//+------------------------------------------------------------------+ -//| Generate Random Parameter Set | -//+------------------------------------------------------------------+ -void COptimizationTest::GenerateRandomParameterSet(SParameterSet& params) -{ - // Risk management parameters - params.risk_percent = m_ranges.risk_percent_min + - (MathRand() / 32767.0) * (m_ranges.risk_percent_max - m_ranges.risk_percent_min); - params.max_risk_percent = params.risk_percent * (1.5 + MathRand() / 32767.0); - params.daily_loss_limit = params.risk_percent * (1.0 + MathRand() / 32767.0); - params.max_drawdown_percent = 5.0 + (MathRand() / 32767.0) * 15.0; - - // Entry strategy parameters - params.min_confluence_score = m_ranges.confluence_min + - (MathRand() / 32767.0) * (m_ranges.confluence_max - m_ranges.confluence_min); - params.lookback_periods = m_ranges.lookback_min + - (int)((MathRand() / 32767.0) * (m_ranges.lookback_max - m_ranges.lookback_min)); - params.ob_strength_threshold = m_ranges.ob_strength_min + - (MathRand() / 32767.0) * (m_ranges.ob_strength_max - m_ranges.ob_strength_min); - params.bos_strength_threshold = 0.3 + (MathRand() / 32767.0) * 0.5; - params.fvg_size_threshold = 5.0 + (MathRand() / 32767.0) * 15.0; - - // Session parameters - params.trade_asia = (MathRand() % 2) == 1; - params.trade_london = true; // Always trade London (most liquid) - params.trade_ny = (MathRand() % 2) == 1; - params.avoid_news_minutes = m_ranges.news_avoid_min + - (int)((MathRand() / 32767.0) * (m_ranges.news_avoid_max - m_ranges.news_avoid_min)); - params.min_volatility = m_ranges.volatility_min + - (MathRand() / 32767.0) * (m_ranges.volatility_max - m_ranges.volatility_min); - params.max_volatility = params.min_volatility + (MathRand() / 32767.0) * 0.5; -} - -//+------------------------------------------------------------------+ -//| Mutate Parameter Set | -//+------------------------------------------------------------------+ -void COptimizationTest::MutateParameterSet(SParameterSet& params, double mutation_rate) -{ - // Mutate each parameter with given probability - if(MathRand() / 32767.0 < mutation_rate) - { - params.risk_percent += (MathRand() / 32767.0 - 0.5) * 0.5; - params.risk_percent = MathMax(m_ranges.risk_percent_min, - MathMin(m_ranges.risk_percent_max, params.risk_percent)); - } - - if(MathRand() / 32767.0 < mutation_rate) - { - params.min_confluence_score += (MathRand() / 32767.0 - 0.5) * 0.2; - params.min_confluence_score = MathMax(m_ranges.confluence_min, - MathMin(m_ranges.confluence_max, params.min_confluence_score)); - } - - if(MathRand() / 32767.0 < mutation_rate) - { - params.lookback_periods += (int)((MathRand() / 32767.0 - 0.5) * 20); - params.lookback_periods = (int)MathMax(m_ranges.lookback_min, - MathMin(m_ranges.lookback_max, params.lookback_periods)); - } - - if(MathRand() / 32767.0 < mutation_rate) - { - params.ob_strength_threshold += (MathRand() / 32767.0 - 0.5) * 0.2; - params.ob_strength_threshold = MathMax(m_ranges.ob_strength_min, - MathMin(m_ranges.ob_strength_max, params.ob_strength_threshold)); - } - - if(MathRand() / 32767.0 < mutation_rate) - { - params.avoid_news_minutes += (int)((MathRand() / 32767.0 - 0.5) * 30); - params.avoid_news_minutes = (int)MathMax(m_ranges.news_avoid_min, - MathMin(m_ranges.news_avoid_max, params.avoid_news_minutes)); - } -} - -//+------------------------------------------------------------------+ -//| Crossover Parameter Sets | -//+------------------------------------------------------------------+ -SParameterSet COptimizationTest::CrossoverParameterSets(const SParameterSet& parent1, const SParameterSet& parent2) -{ - SParameterSet offspring; - - // Uniform crossover - randomly select from each parent - offspring.risk_percent = (MathRand() % 2) ? parent1.risk_percent : parent2.risk_percent; - offspring.max_risk_percent = (MathRand() % 2) ? parent1.max_risk_percent : parent2.max_risk_percent; - offspring.daily_loss_limit = (MathRand() % 2) ? parent1.daily_loss_limit : parent2.daily_loss_limit; - offspring.max_drawdown_percent = (MathRand() % 2) ? parent1.max_drawdown_percent : parent2.max_drawdown_percent; - - offspring.min_confluence_score = (MathRand() % 2) ? parent1.min_confluence_score : parent2.min_confluence_score; - offspring.lookback_periods = (MathRand() % 2) ? parent1.lookback_periods : parent2.lookback_periods; - offspring.ob_strength_threshold = (MathRand() % 2) ? parent1.ob_strength_threshold : parent2.ob_strength_threshold; - offspring.bos_strength_threshold = (MathRand() % 2) ? parent1.bos_strength_threshold : parent2.bos_strength_threshold; - offspring.fvg_size_threshold = (MathRand() % 2) ? parent1.fvg_size_threshold : parent2.fvg_size_threshold; - - offspring.trade_asia = (MathRand() % 2) ? parent1.trade_asia : parent2.trade_asia; - offspring.trade_london = (MathRand() % 2) ? parent1.trade_london : parent2.trade_london; - offspring.trade_ny = (MathRand() % 2) ? parent1.trade_ny : parent2.trade_ny; - offspring.avoid_news_minutes = (MathRand() % 2) ? parent1.avoid_news_minutes : parent2.avoid_news_minutes; - offspring.min_volatility = (MathRand() % 2) ? parent1.min_volatility : parent2.min_volatility; - offspring.max_volatility = (MathRand() % 2) ? parent1.max_volatility : parent2.max_volatility; - - return offspring; -} - -//+------------------------------------------------------------------+ -//| Evaluate Parameter Set | -//+------------------------------------------------------------------+ -double COptimizationTest::EvaluateParameterSet(const SParameterSet& params) -{ - // Apply parameters to components - ApplyParametersToComponents(params); - - // Run backtest - SBacktestStats stats; - if(!BacktestParameterSet(params, stats)) - return -999999; // Invalid parameter set - - // Calculate fitness score - return CalculateFitnessScore(stats); -} - -//+------------------------------------------------------------------+ -//| Calculate Fitness Score | -//+------------------------------------------------------------------+ -double COptimizationTest::CalculateFitnessScore(const SBacktestStats& stats) -{ - // Multi-objective fitness function - double fitness = 0.0; - - // Profit factor (30% weight) - if(stats.profit_factor > 1.0) - fitness += (stats.profit_factor - 1.0) * 30.0; - else - fitness -= (1.0 - stats.profit_factor) * 50.0; // Penalty for losing systems - - // Win rate (20% weight) - fitness += stats.win_rate * 20.0; - - // Net profit normalized by initial balance (25% weight) - fitness += (stats.net_profit / InitialBalance) * 25.0; - - // Recovery factor (15% weight) - Net profit / Max drawdown - if(stats.max_drawdown > 0) - fitness += (stats.net_profit / stats.max_drawdown) * 15.0; - - // Sharpe ratio (10% weight) - if(stats.sharpe_ratio > 0) - fitness += stats.sharpe_ratio * 10.0; - - // Penalty for excessive drawdown - if(stats.max_drawdown > InitialBalance * 0.3) // More than 30% drawdown - fitness -= 50.0; - - // Penalty for too few trades - if(stats.total_trades < 10) - fitness -= 20.0; - - // Penalty for too many trades (overtrading) - if(stats.total_trades > 1000) - fitness -= 10.0; - - return fitness; -} - -//+------------------------------------------------------------------+ -//| Backtest Parameter Set | -//+------------------------------------------------------------------+ -bool COptimizationTest::BacktestParameterSet(const SParameterSet& params, SBacktestStats& stats) -{ - try - { - // Reset backtester - m_backtester.Reset(); - - // Run backtest - bool success = m_backtester.RunBacktest(); - - if(!success) - return false; - - // Get statistics - m_backtester.CalculateStatistics(stats); - - return true; - } - catch(...) - { - return false; - } -} - -//+------------------------------------------------------------------+ -//| Apply Parameters to Components | -//+------------------------------------------------------------------+ -void COptimizationTest::ApplyParametersToComponents(const SParameterSet& params) -{ - // Apply risk management parameters - SRiskProfile risk_profile; - risk_profile.risk_percent = params.risk_percent; - risk_profile.max_risk_percent = params.max_risk_percent; - risk_profile.daily_loss_limit = params.daily_loss_limit; - risk_profile.max_drawdown_percent = params.max_drawdown_percent; - risk_profile.risk_model = RISK_MODEL_PERCENTAGE; - - m_risk_manager.SetRiskProfile(risk_profile); - - // Apply entry strategy parameters - SEntryRequirements requirements; - requirements.min_confluence_score = params.min_confluence_score; - requirements.require_order_block = true; - requirements.require_bos = true; - requirements.require_liquidity_sweep = false; - requirements.require_fvg = false; - - m_entry_strategy.SetRequirements(requirements); - - // Apply detector configurations - SOrderBlockConfig ob_config; - ob_config.lookback_periods = params.lookback_periods; - ob_config.strength_threshold = params.ob_strength_threshold; - ob_config.min_body_size = 10.0; - ob_config.max_age_bars = 100; - - m_ob_detector.Configure(ob_config); - - SBOSConfig bos_config; - bos_config.lookback_periods = params.lookback_periods; - bos_config.strength_threshold = params.bos_strength_threshold; - bos_config.min_break_distance = 5.0; - - m_bos_detector.Configure(bos_config); - - SFVGConfig fvg_config; - fvg_config.min_gap_size = params.fvg_size_threshold; - fvg_config.lookback_periods = params.lookback_periods; - fvg_config.require_volume_confirmation = false; - - m_fvg_detector.Configure(fvg_config); - - // Apply session parameters - SSessionConfig session_config; - session_config.trade_asia = params.trade_asia; - session_config.trade_london = params.trade_london; - session_config.trade_ny = params.trade_ny; - session_config.avoid_news_minutes = params.avoid_news_minutes; - session_config.min_volatility = params.min_volatility; - session_config.max_volatility = params.max_volatility; - - m_session_manager.Configure(session_config); -} - -//+------------------------------------------------------------------+ -//| Initialize Parameter Ranges | -//+------------------------------------------------------------------+ -void COptimizationTest::InitializeParameterRanges() -{ - // Risk management ranges - m_ranges.risk_percent_min = 0.5; - m_ranges.risk_percent_max = 5.0; - m_ranges.risk_percent_step = 0.5; - - // Entry strategy ranges - m_ranges.confluence_min = 0.3; - m_ranges.confluence_max = 0.9; - m_ranges.confluence_step = 0.1; - - m_ranges.lookback_min = 20; - m_ranges.lookback_max = 200; - m_ranges.lookback_step = 20; - - m_ranges.ob_strength_min = 0.3; - m_ranges.ob_strength_max = 0.8; - m_ranges.ob_strength_step = 0.1; - - // Session ranges - m_ranges.news_avoid_min = 0; - m_ranges.news_avoid_max = 60; - m_ranges.news_avoid_step = 15; - - m_ranges.volatility_min = 0.001; - m_ranges.volatility_max = 0.01; - m_ranges.volatility_step = 0.001; -} - -//+------------------------------------------------------------------+ -//| Sort Parameter Sets by Fitness | -//+------------------------------------------------------------------+ -void COptimizationTest::SortParameterSetsByFitness() -{ - int size = ArraySize(m_parameter_sets); - - // Simple bubble sort (sufficient for small populations) - for(int i = 0; i < size - 1; i++) - { - for(int j = 0; j < size - i - 1; j++) - { - if(m_parameter_sets[j].fitness_score < m_parameter_sets[j + 1].fitness_score) - { - SParameterSet temp = m_parameter_sets[j]; - m_parameter_sets[j] = m_parameter_sets[j + 1]; - m_parameter_sets[j + 1] = temp; - } - } - } -} - -//+------------------------------------------------------------------+ -//| Print Optimization Progress | -//+------------------------------------------------------------------+ -void COptimizationTest::PrintOptimizationProgress(int current, int total) -{ - double progress = (double)current / total * 100.0; - Print("Progress: ", current, "/", total, " (", DoubleToString(progress, 1), "%)"); -} - -//+------------------------------------------------------------------+ -//| Generate Optimization Report | -//+------------------------------------------------------------------+ -void COptimizationTest::GenerateOptimizationReport() -{ - Print(""); - Print("=== MT5 Sniper EA Optimization Report ==="); - Print(""); - - Print("Optimization Summary:"); - Print("--------------------"); - Print("Symbol: ", OptimizationSymbol); - Print("Timeframe: ", EnumToString(OptimizationTimeframe)); - Print("Period: ", TimeToString(OptimizationStartDate), " - ", TimeToString(OptimizationEndDate)); - Print("Total Iterations: ", m_result.total_iterations); - Print("Successful Iterations: ", m_result.successful_iterations); - Print("Optimization Time: ", m_result.optimization_time, " seconds"); - Print(""); - - Print("Best Parameter Set:"); - Print("------------------"); - SParameterSet& best = m_result.best_parameters; - Print("Fitness Score: ", DoubleToString(best.fitness_score, 2)); - Print("Risk Percent: ", DoubleToString(best.risk_percent, 2), "%"); - Print("Max Risk Percent: ", DoubleToString(best.max_risk_percent, 2), "%"); - Print("Daily Loss Limit: ", DoubleToString(best.daily_loss_limit, 2), "%"); - Print("Max Drawdown: ", DoubleToString(best.max_drawdown_percent, 2), "%"); - Print("Min Confluence Score: ", DoubleToString(best.min_confluence_score, 2)); - Print("Lookback Periods: ", best.lookback_periods); - Print("OB Strength Threshold: ", DoubleToString(best.ob_strength_threshold, 2)); - Print("BOS Strength Threshold: ", DoubleToString(best.bos_strength_threshold, 2)); - Print("FVG Size Threshold: ", DoubleToString(best.fvg_size_threshold, 1), " pips"); - Print("Trade Asia: ", best.trade_asia ? "Yes" : "No"); - Print("Trade London: ", best.trade_london ? "Yes" : "No"); - Print("Trade NY: ", best.trade_ny ? "Yes" : "No"); - Print("Avoid News Minutes: ", best.avoid_news_minutes); - Print("Min Volatility: ", DoubleToString(best.min_volatility, 4)); - Print("Max Volatility: ", DoubleToString(best.max_volatility, 4)); - Print(""); - - Print("Performance Metrics:"); - Print("-------------------"); - Print("Net Profit: $", DoubleToString(best.net_profit, 2)); - Print("Profit Factor: ", DoubleToString(best.profit_factor, 2)); - Print("Win Rate: ", DoubleToString(best.win_rate * 100, 2), "%"); - Print("Max Drawdown: $", DoubleToString(best.max_drawdown, 2)); - Print("Sharpe Ratio: ", DoubleToString(best.sharpe_ratio, 2)); - Print("Recovery Factor: ", DoubleToString(best.recovery_factor, 2)); - Print("Total Trades: ", best.total_trades); - Print(""); - - // Save detailed report to file - SaveOptimizationResults(); -} - -//+------------------------------------------------------------------+ -//| Save Optimization Results | -//+------------------------------------------------------------------+ -void COptimizationTest::SaveOptimizationResults() -{ - string filename = "SniperEA_OptimizationReport_" + OptimizationSymbol + "_" + - EnumToString(OptimizationTimeframe) + "_" + - TimeToString(TimeCurrent(), TIME_DATE) + ".csv"; - - int file_handle = FileOpen(filename, FILE_WRITE | FILE_CSV); - - if(file_handle != INVALID_HANDLE) - { - // Write header - FileWrite(file_handle, "Parameter", "Value", "Description"); - FileWrite(file_handle, "Symbol", OptimizationSymbol, "Trading symbol"); - FileWrite(file_handle, "Timeframe", EnumToString(OptimizationTimeframe), "Chart timeframe"); - FileWrite(file_handle, "Start Date", TimeToString(OptimizationStartDate), "Optimization start"); - FileWrite(file_handle, "End Date", TimeToString(OptimizationEndDate), "Optimization end"); - FileWrite(file_handle, "Total Iterations", m_result.total_iterations, "Total parameter sets tested"); - FileWrite(file_handle, "Optimization Time", m_result.optimization_time, "Time taken (seconds)"); - FileWrite(file_handle, "", "", ""); - - // Write best parameters - SParameterSet& best = m_result.best_parameters; - FileWrite(file_handle, "BEST PARAMETERS", "", ""); - FileWrite(file_handle, "Fitness Score", best.fitness_score, "Overall fitness score"); - FileWrite(file_handle, "Risk Percent", best.risk_percent, "Risk per trade (%)"); - FileWrite(file_handle, "Max Risk Percent", best.max_risk_percent, "Maximum risk (%)"); - FileWrite(file_handle, "Daily Loss Limit", best.daily_loss_limit, "Daily loss limit (%)"); - FileWrite(file_handle, "Max Drawdown Percent", best.max_drawdown_percent, "Maximum drawdown (%)"); - FileWrite(file_handle, "Min Confluence Score", best.min_confluence_score, "Minimum confluence for entry"); - FileWrite(file_handle, "Lookback Periods", best.lookback_periods, "Analysis lookback periods"); - FileWrite(file_handle, "OB Strength Threshold", best.ob_strength_threshold, "Order block strength threshold"); - FileWrite(file_handle, "BOS Strength Threshold", best.bos_strength_threshold, "BOS strength threshold"); - FileWrite(file_handle, "FVG Size Threshold", best.fvg_size_threshold, "FVG minimum size (pips)"); - FileWrite(file_handle, "Trade Asia", best.trade_asia, "Trade during Asia session"); - FileWrite(file_handle, "Trade London", best.trade_london, "Trade during London session"); - FileWrite(file_handle, "Trade NY", best.trade_ny, "Trade during NY session"); - FileWrite(file_handle, "Avoid News Minutes", best.avoid_news_minutes, "Minutes to avoid around news"); - FileWrite(file_handle, "Min Volatility", best.min_volatility, "Minimum volatility threshold"); - FileWrite(file_handle, "Max Volatility", best.max_volatility, "Maximum volatility threshold"); - FileWrite(file_handle, "", "", ""); - - // Write performance metrics - FileWrite(file_handle, "PERFORMANCE METRICS", "", ""); - FileWrite(file_handle, "Net Profit", best.net_profit, "Total profit/loss"); - FileWrite(file_handle, "Profit Factor", best.profit_factor, "Gross profit / Gross loss"); - FileWrite(file_handle, "Win Rate", best.win_rate, "Percentage of winning trades"); - FileWrite(file_handle, "Max Drawdown", best.max_drawdown, "Maximum drawdown amount"); - FileWrite(file_handle, "Sharpe Ratio", best.sharpe_ratio, "Risk-adjusted return"); - FileWrite(file_handle, "Recovery Factor", best.recovery_factor, "Net profit / Max drawdown"); - FileWrite(file_handle, "Total Trades", best.total_trades, "Total number of trades"); - - FileClose(file_handle); - Print("Optimization report saved to: ", filename); - } - else - { - Print("Failed to save optimization report"); - } -} - -//+------------------------------------------------------------------+ -//| Script start function | -//+------------------------------------------------------------------+ -void OnStart() -{ - Print("Starting MT5 Sniper EA Parameter Optimization..."); - Print("This will find the optimal parameter combinations for maximum performance."); - Print(""); - - COptimizationTest* optimizer = new COptimizationTest(); - - bool success = optimizer.RunOptimization(); - - if(success) - { - Print(""); - Print("๐ฏ Parameter optimization completed successfully!"); - Print("Check the optimization report for the best parameter settings."); - } - else - { - Print(""); - Print("โ ๏ธ Parameter optimization failed. Please check the logs for errors."); - } - - delete optimizer; - - Print("Optimization testing completed."); -} \ No newline at end of file diff --git a/src/Tests/PerformanceTest.mq5 b/src/Tests/PerformanceTest.mq5 deleted file mode 100644 index 84c35d3..0000000 --- a/src/Tests/PerformanceTest.mq5 +++ /dev/null @@ -1,904 +0,0 @@ -//+------------------------------------------------------------------+ -//| PerformanceTest.mq5 | -//| MT5 Sniper EA - Performance | -//| | -//+------------------------------------------------------------------+ -#property copyright "MT5 Sniper EA" -#property version "1.00" -#property description "Performance benchmarking for MT5 Sniper EA" -#property script_show_inputs - -// Include all EA components -#include "../Include/MarketStructure/OrderBlockDetector.mqh" -#include "../Include/MarketStructure/BOSDetector.mqh" -#include "../Include/MarketStructure/LiquiditySweepDetector.mqh" -#include "../Include/MarketStructure/FVGDetector.mqh" -#include "../Include/MarketStructure/EntryStrategy.mqh" -#include "../Include/RiskManagement/RiskManager.mqh" -#include "../Include/SessionManagement/SessionManager.mqh" -#include "../Include/AIIntegration/GrokAI.mqh" -#include "../Include/Visualization/ChartManager.mqh" -#include "../Include/Utils/Backtester.mqh" - -// Input parameters -input int TestIterations = 1000; // Number of test iterations -input bool TestMemoryUsage = true; // Test memory usage -input bool TestConcurrency = true; // Test concurrent operations -input bool GenerateReport = true; // Generate performance report - -//+------------------------------------------------------------------+ -//| Performance Metrics Structure | -//+------------------------------------------------------------------+ -struct SPerformanceMetrics -{ - string component_name; - double avg_execution_time; - double min_execution_time; - double max_execution_time; - double total_execution_time; - int iterations; - double memory_usage_mb; - bool passed_benchmark; -}; - -//+------------------------------------------------------------------+ -//| Performance Test Class | -//+------------------------------------------------------------------+ -class CPerformanceTest -{ -private: - // Test components - COrderBlockDetector* m_ob_detector; - CBOSDetector* m_bos_detector; - CLiquiditySweepDetector* m_ls_detector; - CFVGDetector* m_fvg_detector; - CEntryStrategy* m_entry_strategy; - CRiskManager* m_risk_manager; - CSessionManager* m_session_manager; - CGrokAI* m_grok_ai; - CChartManager* m_chart_manager; - CBacktester* m_backtester; - - // Performance metrics - SPerformanceMetrics m_metrics[]; - - // Benchmark thresholds (microseconds) - double m_ob_threshold; - double m_bos_threshold; - double m_ls_threshold; - double m_fvg_threshold; - double m_entry_threshold; - double m_risk_threshold; - double m_session_threshold; - double m_ai_threshold; - double m_chart_threshold; - double m_backtest_threshold; - -public: - CPerformanceTest(); - ~CPerformanceTest(); - - // Main test functions - bool RunPerformanceTests(); - void GeneratePerformanceReport(); - - // Component performance tests - void TestOrderBlockPerformance(); - void TestBOSPerformance(); - void TestLiquiditySweepPerformance(); - void TestFVGPerformance(); - void TestEntryStrategyPerformance(); - void TestRiskManagerPerformance(); - void TestSessionManagerPerformance(); - void TestGrokAIPerformance(); - void TestChartManagerPerformance(); - void TestBacktesterPerformance(); - - // Specialized tests - void TestMemoryUsage(); - void TestConcurrentOperations(); - void TestScalabilityLimits(); - void TestResourceCleanup(); - - // Utility functions - void AddMetrics(string name, double avg_time, double min_time, double max_time, - double total_time, int iterations, double memory_mb, bool passed); - double GetMemoryUsage(); - void SetBenchmarkThresholds(); - void PrintPerformanceResults(); -}; - -//+------------------------------------------------------------------+ -//| Constructor | -//+------------------------------------------------------------------+ -CPerformanceTest::CPerformanceTest() -{ - // Initialize components - m_ob_detector = new COrderBlockDetector(); - m_bos_detector = new CBOSDetector(); - m_ls_detector = new CLiquiditySweepDetector(); - m_fvg_detector = new CFVGDetector(); - m_entry_strategy = new CEntryStrategy(); - m_risk_manager = new CRiskManager(); - m_session_manager = new CSessionManager(); - m_grok_ai = new CGrokAI(); - m_chart_manager = new CChartManager(); - m_backtester = new CBacktester(); - - SetBenchmarkThresholds(); -} - -//+------------------------------------------------------------------+ -//| Destructor | -//+------------------------------------------------------------------+ -CPerformanceTest::~CPerformanceTest() -{ - delete m_ob_detector; - delete m_bos_detector; - delete m_ls_detector; - delete m_fvg_detector; - delete m_entry_strategy; - delete m_risk_manager; - delete m_session_manager; - delete m_grok_ai; - delete m_chart_manager; - delete m_backtester; -} - -//+------------------------------------------------------------------+ -//| Set Benchmark Thresholds | -//+------------------------------------------------------------------+ -void CPerformanceTest::SetBenchmarkThresholds() -{ - // Performance thresholds in microseconds (acceptable execution times) - m_ob_threshold = 10000; // 10ms for Order Block detection - m_bos_threshold = 5000; // 5ms for BOS detection - m_ls_threshold = 8000; // 8ms for Liquidity Sweep detection - m_fvg_threshold = 3000; // 3ms for FVG detection - m_entry_threshold = 15000; // 15ms for Entry Strategy analysis - m_risk_threshold = 1000; // 1ms for Risk calculations - m_session_threshold = 500; // 0.5ms for Session checks - m_ai_threshold = 50000; // 50ms for AI analysis (network dependent) - m_chart_threshold = 2000; // 2ms for Chart operations - m_backtest_threshold = 100000; // 100ms for Backtest operations -} - -//+------------------------------------------------------------------+ -//| Run Performance Tests | -//+------------------------------------------------------------------+ -bool CPerformanceTest::RunPerformanceTests() -{ - Print("=== Starting MT5 Sniper EA Performance Tests ==="); - Print("Test Iterations: ", TestIterations); - Print(""); - - // Initialize components - m_ob_detector.Initialize("EURUSD", PERIOD_H1); - m_bos_detector.Initialize("EURUSD", PERIOD_H1); - m_ls_detector.Initialize("EURUSD", PERIOD_H1); - m_fvg_detector.Initialize("EURUSD", PERIOD_H1); - m_entry_strategy.Initialize("EURUSD", PERIOD_H1); - m_risk_manager.Initialize(); - m_session_manager.Initialize(); - m_grok_ai.Initialize("test_key", "test_url"); - m_chart_manager.Initialize(ChartID()); - m_backtester.Initialize(); - - // Run component performance tests - TestOrderBlockPerformance(); - TestBOSPerformance(); - TestLiquiditySweepPerformance(); - TestFVGPerformance(); - TestEntryStrategyPerformance(); - TestRiskManagerPerformance(); - TestSessionManagerPerformance(); - TestGrokAIPerformance(); - TestChartManagerPerformance(); - TestBacktesterPerformance(); - - // Run specialized tests - if(TestMemoryUsage) - TestMemoryUsage(); - - if(TestConcurrency) - TestConcurrentOperations(); - - TestScalabilityLimits(); - TestResourceCleanup(); - - // Generate report - if(GenerateReport) - GeneratePerformanceReport(); - - return true; -} - -//+------------------------------------------------------------------+ -//| Test Order Block Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestOrderBlockPerformance() -{ - Print("Testing Order Block Detector Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - SOrderBlock blocks[]; - m_ob_detector.DetectOrderBlocks(blocks); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_ob_threshold); - - AddMetrics("Order Block Detector", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("Order Block Detector - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test BOS Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestBOSPerformance() -{ - Print("Testing BOS Detector Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - SBOS signals[]; - m_bos_detector.DetectBOS(signals); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_bos_threshold); - - AddMetrics("BOS Detector", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("BOS Detector - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Liquidity Sweep Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestLiquiditySweepPerformance() -{ - Print("Testing Liquidity Sweep Detector Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - SLiquiditySweep sweeps[]; - m_ls_detector.DetectSweeps(sweeps); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_ls_threshold); - - AddMetrics("Liquidity Sweep Detector", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("Liquidity Sweep Detector - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test FVG Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestFVGPerformance() -{ - Print("Testing FVG Detector Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - SFVG gaps[]; - m_fvg_detector.DetectFVG(gaps); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_fvg_threshold); - - AddMetrics("FVG Detector", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("FVG Detector - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Entry Strategy Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestEntryStrategyPerformance() -{ - Print("Testing Entry Strategy Performance..."); - - // Configure entry strategy - m_entry_strategy.ConfigureOrderBlockDetector(m_ob_detector); - m_entry_strategy.ConfigureBOSDetector(m_bos_detector); - m_entry_strategy.ConfigureLiquiditySweepDetector(m_ls_detector); - m_entry_strategy.ConfigureFVGDetector(m_fvg_detector); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - SEntrySignal signal; - m_entry_strategy.AnalyzeEntry(signal); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_entry_threshold); - - AddMetrics("Entry Strategy", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("Entry Strategy - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Risk Manager Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestRiskManagerPerformance() -{ - Print("Testing Risk Manager Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - double position_size = m_risk_manager.CalculatePositionSize("EURUSD", 50); - double stop_loss = m_risk_manager.CalculateStopLoss("EURUSD", ORDER_TYPE_BUY, 1.1000, 50); - double take_profit = m_risk_manager.CalculateTakeProfit("EURUSD", ORDER_TYPE_BUY, 1.1000, 100); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_risk_threshold); - - AddMetrics("Risk Manager", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("Risk Manager - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Session Manager Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestSessionManagerPerformance() -{ - Print("Testing Session Manager Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - ENUM_TRADING_SESSION session = m_session_manager.GetCurrentSession(); - bool trading_allowed = m_session_manager.IsTradingAllowed(); - ENUM_SESSION_PHASE phase = m_session_manager.GetSessionPhase(); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_session_threshold); - - AddMetrics("Session Manager", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("Session Manager - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Grok AI Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestGrokAIPerformance() -{ - Print("Testing Grok AI Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - // Note: AI tests may fail due to network/API limitations - int successful_calls = 0; - - for(int i = 0; i < MathMin(TestIterations, 10); i++) // Limit AI tests to 10 iterations - { - ulong start_time = GetMicrosecondCount(); - - SAIAnalysis analysis; - bool success = m_grok_ai.RequestAnalysis("EURUSD", analysis); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(success) - { - successful_calls++; - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - } - - double memory_after = GetMemoryUsage(); - double avg_time = successful_calls > 0 ? total_time / successful_calls : 0; - bool passed = (avg_time <= m_ai_threshold) || (successful_calls == 0); // Pass if no API available - - AddMetrics("Grok AI", avg_time, min_time, max_time, - total_time, successful_calls, memory_after - memory_before, passed); - - Print("Grok AI - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Successful Calls: ", successful_calls, ", Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Chart Manager Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestChartManagerPerformance() -{ - Print("Testing Chart Manager Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - for(int i = 0; i < TestIterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - // Test drawing operations - SOrderBlock test_block; - test_block.high = 1.1000 + i * 0.0001; - test_block.low = 1.0950 + i * 0.0001; - test_block.start_time = TimeCurrent() - 3600; - test_block.type = ORDER_BLOCK_BULLISH; - - string obj_name = m_chart_manager.DrawOrderBlock(test_block); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - - // Clean up object to avoid chart clutter - if(StringLen(obj_name) > 0) - ObjectDelete(ChartID(), obj_name); - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / TestIterations; - bool passed = (avg_time <= m_chart_threshold); - - AddMetrics("Chart Manager", avg_time, min_time, max_time, - total_time, TestIterations, memory_after - memory_before, passed); - - Print("Chart Manager - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Backtester Performance | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestBacktesterPerformance() -{ - Print("Testing Backtester Performance..."); - - double min_time = DBL_MAX; - double max_time = 0; - double total_time = 0; - double memory_before = GetMemoryUsage(); - - // Configure backtester - m_backtester.SetEntryStrategy(m_entry_strategy); - m_backtester.SetRiskManager(m_risk_manager); - m_backtester.SetSessionManager(m_session_manager); - - int test_iterations = MathMin(TestIterations, 100); // Limit backtest iterations - - for(int i = 0; i < test_iterations; i++) - { - ulong start_time = GetMicrosecondCount(); - - SBacktestStats stats; - m_backtester.CalculateStatistics(stats); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - if(execution_time < min_time) min_time = execution_time; - if(execution_time > max_time) max_time = execution_time; - total_time += execution_time; - } - - double memory_after = GetMemoryUsage(); - double avg_time = total_time / test_iterations; - bool passed = (avg_time <= m_backtest_threshold); - - AddMetrics("Backtester", avg_time, min_time, max_time, - total_time, test_iterations, memory_after - memory_before, passed); - - Print("Backtester - Avg: ", DoubleToString(avg_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Memory Usage | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestMemoryUsage() -{ - Print("Testing Memory Usage..."); - - double initial_memory = GetMemoryUsage(); - - // Create multiple instances to test memory scaling - COrderBlockDetector* detectors[]; - ArrayResize(detectors, 100); - - for(int i = 0; i < 100; i++) - { - detectors[i] = new COrderBlockDetector(); - detectors[i].Initialize("EURUSD", PERIOD_H1); - } - - double peak_memory = GetMemoryUsage(); - - // Clean up - for(int i = 0; i < 100; i++) - { - delete detectors[i]; - } - - double final_memory = GetMemoryUsage(); - - Print("Memory Usage Test:"); - Print("Initial: ", DoubleToString(initial_memory, 2), " MB"); - Print("Peak: ", DoubleToString(peak_memory, 2), " MB"); - Print("Final: ", DoubleToString(final_memory, 2), " MB"); - Print("Memory Leak: ", DoubleToString(final_memory - initial_memory, 2), " MB"); - - bool passed = (final_memory - initial_memory) < 1.0; // Less than 1MB leak acceptable - - AddMetrics("Memory Usage", 0, 0, 0, 0, 1, peak_memory - initial_memory, passed); -} - -//+------------------------------------------------------------------+ -//| Test Concurrent Operations | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestConcurrentOperations() -{ - Print("Testing Concurrent Operations..."); - - ulong start_time = GetMicrosecondCount(); - - // Simulate concurrent operations - SOrderBlock blocks[]; - SBOS bos_signals[]; - SLiquiditySweep sweeps[]; - SFVG gaps[]; - SEntrySignal entry_signal; - - // Execute multiple operations simultaneously - m_ob_detector.DetectOrderBlocks(blocks); - m_bos_detector.DetectBOS(bos_signals); - m_ls_detector.DetectSweeps(sweeps); - m_fvg_detector.DetectFVG(gaps); - m_entry_strategy.AnalyzeEntry(entry_signal); - - double position_size = m_risk_manager.CalculatePositionSize("EURUSD", 50); - bool trading_allowed = m_session_manager.IsTradingAllowed(); - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - - bool passed = execution_time < 50000; // Should complete within 50ms - - AddMetrics("Concurrent Operations", execution_time, execution_time, execution_time, - execution_time, 1, 0, passed); - - Print("Concurrent Operations - Time: ", DoubleToString(execution_time, 2), "ฮผs, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Test Scalability Limits | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestScalabilityLimits() -{ - Print("Testing Scalability Limits..."); - - // Test with increasing data sizes - int data_sizes[] = {100, 500, 1000, 5000, 10000}; - - for(int i = 0; i < ArraySize(data_sizes); i++) - { - int data_size = data_sizes[i]; - - ulong start_time = GetMicrosecondCount(); - - // Simulate processing large datasets - for(int j = 0; j < data_size; j++) - { - double position_size = m_risk_manager.CalculatePositionSize("EURUSD", 50); - } - - ulong end_time = GetMicrosecondCount(); - double execution_time = (double)(end_time - start_time); - double time_per_operation = execution_time / data_size; - - Print("Data Size: ", data_size, ", Time per Op: ", DoubleToString(time_per_operation, 2), "ฮผs"); - - // Check if performance degrades significantly - if(time_per_operation > m_risk_threshold * 2) - { - Print("Performance degradation detected at data size: ", data_size); - break; - } - } -} - -//+------------------------------------------------------------------+ -//| Test Resource Cleanup | -//+------------------------------------------------------------------+ -void CPerformanceTest::TestResourceCleanup() -{ - Print("Testing Resource Cleanup..."); - - double initial_memory = GetMemoryUsage(); - - // Create and destroy components multiple times - for(int i = 0; i < 50; i++) - { - COrderBlockDetector* detector = new COrderBlockDetector(); - detector.Initialize("EURUSD", PERIOD_H1); - - SOrderBlock blocks[]; - detector.DetectOrderBlocks(blocks); - - delete detector; - } - - double final_memory = GetMemoryUsage(); - double memory_diff = final_memory - initial_memory; - - bool passed = memory_diff < 0.5; // Less than 0.5MB increase acceptable - - AddMetrics("Resource Cleanup", 0, 0, 0, 0, 50, memory_diff, passed); - - Print("Resource Cleanup - Memory Change: ", DoubleToString(memory_diff, 2), " MB, Passed: ", passed ? "Yes" : "No"); -} - -//+------------------------------------------------------------------+ -//| Add Metrics | -//+------------------------------------------------------------------+ -void CPerformanceTest::AddMetrics(string name, double avg_time, double min_time, double max_time, - double total_time, int iterations, double memory_mb, bool passed) -{ - int size = ArraySize(m_metrics); - ArrayResize(m_metrics, size + 1); - - m_metrics[size].component_name = name; - m_metrics[size].avg_execution_time = avg_time; - m_metrics[size].min_execution_time = min_time; - m_metrics[size].max_execution_time = max_time; - m_metrics[size].total_execution_time = total_time; - m_metrics[size].iterations = iterations; - m_metrics[size].memory_usage_mb = memory_mb; - m_metrics[size].passed_benchmark = passed; -} - -//+------------------------------------------------------------------+ -//| Get Memory Usage | -//+------------------------------------------------------------------+ -double CPerformanceTest::GetMemoryUsage() -{ - // This is a simplified memory usage estimation - // In a real implementation, you would use system-specific functions - return (double)MQLInfoInteger(MQL_MEMORY_USED) / (1024.0 * 1024.0); // Convert to MB -} - -//+------------------------------------------------------------------+ -//| Generate Performance Report | -//+------------------------------------------------------------------+ -void CPerformanceTest::GeneratePerformanceReport() -{ - Print(""); - Print("=== MT5 Sniper EA Performance Report ==="); - Print(""); - - int passed_count = 0; - int total_count = ArraySize(m_metrics); - - // Print detailed results - Print("Component Performance Results:"); - Print("-----------------------------"); - - for(int i = 0; i < ArraySize(m_metrics); i++) - { - SPerformanceMetrics& metric = m_metrics[i]; - - Print(StringFormat("%-25s | Avg: %8.2fฮผs | Min: %8.2fฮผs | Max: %8.2fฮผs | Mem: %6.2fMB | %s", - metric.component_name, - metric.avg_execution_time, - metric.min_execution_time, - metric.max_execution_time, - metric.memory_usage_mb, - metric.passed_benchmark ? "PASS" : "FAIL")); - - if(metric.passed_benchmark) - passed_count++; - } - - Print(""); - Print("Summary:"); - Print("--------"); - Print("Total Components Tested: ", total_count); - Print("Passed Benchmarks: ", passed_count); - Print("Failed Benchmarks: ", total_count - passed_count); - Print("Success Rate: ", DoubleToString((double)passed_count / total_count * 100, 2), "%"); - - // Save report to file - string filename = "SniperEA_PerformanceReport_" + TimeToString(TimeCurrent(), TIME_DATE) + ".csv"; - int file_handle = FileOpen(filename, FILE_WRITE | FILE_CSV); - - if(file_handle != INVALID_HANDLE) - { - // Write CSV header - FileWrite(file_handle, "Component,Avg_Time_ฮผs,Min_Time_ฮผs,Max_Time_ฮผs,Memory_MB,Iterations,Passed"); - - // Write data - for(int i = 0; i < ArraySize(m_metrics); i++) - { - SPerformanceMetrics& metric = m_metrics[i]; - FileWrite(file_handle, - metric.component_name, - DoubleToString(metric.avg_execution_time, 2), - DoubleToString(metric.min_execution_time, 2), - DoubleToString(metric.max_execution_time, 2), - DoubleToString(metric.memory_usage_mb, 2), - IntegerToString(metric.iterations), - metric.passed_benchmark ? "Yes" : "No"); - } - - FileClose(file_handle); - Print("Performance report saved to: ", filename); - } - - if(passed_count == total_count) - { - Print("๐ All performance benchmarks passed!"); - } - else - { - Print("โ ๏ธ Some performance benchmarks failed. Consider optimization."); - } -} - -//+------------------------------------------------------------------+ -//| Print Performance Results | -//+------------------------------------------------------------------+ -void CPerformanceTest::PrintPerformanceResults() -{ - GeneratePerformanceReport(); -} - -//+------------------------------------------------------------------+ -//| Script start function | -//+------------------------------------------------------------------+ -void OnStart() -{ - Print("Starting MT5 Sniper EA Performance Tests..."); - Print("This may take several minutes depending on test iterations."); - Print(""); - - CPerformanceTest* tester = new CPerformanceTest(); - - bool success = tester.RunPerformanceTests(); - - if(success) - { - Print(""); - Print("๐ Performance testing completed successfully!"); - } - else - { - Print(""); - Print("โ Performance testing encountered issues."); - } - - delete tester; - - Print("Performance testing finished."); -} \ No newline at end of file diff --git a/src/Tests/SystemTest.mq5 b/src/Tests/SystemTest.mq5 deleted file mode 100644 index ab5537d..0000000 --- a/src/Tests/SystemTest.mq5 +++ /dev/null @@ -1,1229 +0,0 @@ -//+------------------------------------------------------------------+ -//| SystemTest.mq5 | -//| MT5 Sniper EA - System Tests | -//| | -//+------------------------------------------------------------------+ -#property copyright "MT5 Sniper EA" -#property version "1.00" -#property description "Comprehensive system tests for MT5 Sniper EA" - -// Include all EA components -#include "../Include/MarketStructure/OrderBlockDetector.mqh" -#include "../Include/MarketStructure/BOSDetector.mqh" -#include "../Include/MarketStructure/LiquiditySweepDetector.mqh" -#include "../Include/MarketStructure/FVGDetector.mqh" -#include "../Include/MarketStructure/EntryStrategy.mqh" -#include "../Include/RiskManagement/RiskManager.mqh" -#include "../Include/SessionManagement/SessionManager.mqh" -#include "../Include/AIIntegration/GrokAI.mqh" -#include "../Include/Visualization/ChartManager.mqh" -#include "../Include/Utils/Backtester.mqh" - -//+------------------------------------------------------------------+ -//| Test Results Structure | -//+------------------------------------------------------------------+ -struct STestResult -{ - string test_name; - bool passed; - string message; - double execution_time; -}; - -//+------------------------------------------------------------------+ -//| System Test Class | -//+------------------------------------------------------------------+ -class CSystemTest -{ -private: - // Test components - COrderBlockDetector* m_ob_detector; - CBOSDetector* m_bos_detector; - CLiquiditySweepDetector* m_ls_detector; - CFVGDetector* m_fvg_detector; - CEntryStrategy* m_entry_strategy; - CRiskManager* m_risk_manager; - CSessionManager* m_session_manager; - CGrokAI* m_grok_ai; - CChartManager* m_chart_manager; - CBacktester* m_backtester; - - // Test results - STestResult m_test_results[]; - int m_total_tests; - int m_passed_tests; - int m_failed_tests; - - // Test data - double m_test_prices[]; - datetime m_test_times[]; - -public: - CSystemTest(); - ~CSystemTest(); - - // Main test functions - bool RunAllTests(); - void GenerateTestReport(); - - // Component tests - bool TestOrderBlockDetector(); - bool TestBOSDetector(); - bool TestLiquiditySweepDetector(); - bool TestFVGDetector(); - bool TestEntryStrategy(); - bool TestRiskManager(); - bool TestSessionManager(); - bool TestGrokAI(); - bool TestChartManager(); - bool TestBacktester(); - - // Integration tests - bool TestComponentIntegration(); - bool TestDataFlow(); - bool TestErrorHandling(); - bool TestPerformance(); - - // Utility functions - void AddTestResult(string name, bool passed, string message, double time); - void GenerateTestData(); - double GetExecutionTime(ulong start_time); - void PrintTestResults(); -}; - -//+------------------------------------------------------------------+ -//| Constructor | -//+------------------------------------------------------------------+ -CSystemTest::CSystemTest() -{ - m_total_tests = 0; - m_passed_tests = 0; - m_failed_tests = 0; - - // Initialize components - m_ob_detector = new COrderBlockDetector(); - m_bos_detector = new CBOSDetector(); - m_ls_detector = new CLiquiditySweepDetector(); - m_fvg_detector = new CFVGDetector(); - m_entry_strategy = new CEntryStrategy(); - m_risk_manager = new CRiskManager(); - m_session_manager = new CSessionManager(); - m_grok_ai = new CGrokAI(); - m_chart_manager = new CChartManager(); - m_backtester = new CBacktester(); - - GenerateTestData(); -} - -//+------------------------------------------------------------------+ -//| Destructor | -//+------------------------------------------------------------------+ -CSystemTest::~CSystemTest() -{ - delete m_ob_detector; - delete m_bos_detector; - delete m_ls_detector; - delete m_fvg_detector; - delete m_entry_strategy; - delete m_risk_manager; - delete m_session_manager; - delete m_grok_ai; - delete m_chart_manager; - delete m_backtester; -} - -//+------------------------------------------------------------------+ -//| Run All Tests | -//+------------------------------------------------------------------+ -bool CSystemTest::RunAllTests() -{ - Print("=== Starting MT5 Sniper EA System Tests ==="); - - // Component tests - TestOrderBlockDetector(); - TestBOSDetector(); - TestLiquiditySweepDetector(); - TestFVGDetector(); - TestEntryStrategy(); - TestRiskManager(); - TestSessionManager(); - TestGrokAI(); - TestChartManager(); - TestBacktester(); - - // Integration tests - TestComponentIntegration(); - TestDataFlow(); - TestErrorHandling(); - TestPerformance(); - - // Generate final report - GenerateTestReport(); - - return (m_failed_tests == 0); -} - -//+------------------------------------------------------------------+ -//| Test Order Block Detector | -//+------------------------------------------------------------------+ -bool CSystemTest::TestOrderBlockDetector() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_ob_detector.Initialize("EURUSD", PERIOD_H1)) - { - test_passed = false; - error_msg = "Failed to initialize Order Block Detector"; - } - - // Test configuration - SOrderBlockConfig config; - config.min_size_pips = 20; - config.confirmation_bars = 3; - config.max_age_bars = 100; - config.strength_threshold = 0.7; - - if(test_passed && !m_ob_detector.Configure(config)) - { - test_passed = false; - error_msg = "Failed to configure Order Block Detector"; - } - - // Test detection with sample data - if(test_passed) - { - SOrderBlock blocks[]; - int count = m_ob_detector.DetectOrderBlocks(blocks); - - if(count < 0) - { - test_passed = false; - error_msg = "Order Block detection returned error"; - } - } - - // Test validation - if(test_passed) - { - SOrderBlock test_block; - test_block.high = 1.1000; - test_block.low = 1.0950; - test_block.strength = 0.8; - test_block.age_bars = 10; - test_block.type = ORDER_BLOCK_BULLISH; - - if(!m_ob_detector.ValidateOrderBlock(test_block)) - { - test_passed = false; - error_msg = "Order Block validation failed"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Order Block Detector test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Order Block Detector", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test BOS Detector | -//+------------------------------------------------------------------+ -bool CSystemTest::TestBOSDetector() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_bos_detector.Initialize("EURUSD", PERIOD_H1)) - { - test_passed = false; - error_msg = "Failed to initialize BOS Detector"; - } - - // Test configuration - SBOSConfig config; - config.min_break_pips = 15; - config.confirmation_bars = 2; - config.volume_threshold = 1.5; - config.require_volume_confirmation = true; - - if(test_passed && !m_bos_detector.Configure(config)) - { - test_passed = false; - error_msg = "Failed to configure BOS Detector"; - } - - // Test detection - if(test_passed) - { - SBOS bos_signals[]; - int count = m_bos_detector.DetectBOS(bos_signals); - - if(count < 0) - { - test_passed = false; - error_msg = "BOS detection returned error"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in BOS Detector test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("BOS Detector", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Liquidity Sweep Detector | -//+------------------------------------------------------------------+ -bool CSystemTest::TestLiquiditySweepDetector() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_ls_detector.Initialize("EURUSD", PERIOD_H1)) - { - test_passed = false; - error_msg = "Failed to initialize Liquidity Sweep Detector"; - } - - // Test configuration - SLiquiditySweepConfig config; - config.sweep_range_pips = 30; - config.min_sweep_pips = 5; - config.max_sweep_duration = 10; - config.volume_multiplier = 2.0; - - if(test_passed && !m_ls_detector.Configure(config)) - { - test_passed = false; - error_msg = "Failed to configure Liquidity Sweep Detector"; - } - - // Test detection - if(test_passed) - { - SLiquiditySweep sweeps[]; - int count = m_ls_detector.DetectSweeps(sweeps); - - if(count < 0) - { - test_passed = false; - error_msg = "Liquidity Sweep detection returned error"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Liquidity Sweep Detector test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Liquidity Sweep Detector", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test FVG Detector | -//+------------------------------------------------------------------+ -bool CSystemTest::TestFVGDetector() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_fvg_detector.Initialize("EURUSD", PERIOD_H1)) - { - test_passed = false; - error_msg = "Failed to initialize FVG Detector"; - } - - // Test configuration - SFVGConfig config; - config.min_gap_pips = 10; - config.max_gap_pips = 100; - config.require_volume_confirmation = true; - config.gap_fill_threshold = 0.5; - - if(test_passed && !m_fvg_detector.Configure(config)) - { - test_passed = false; - error_msg = "Failed to configure FVG Detector"; - } - - // Test detection - if(test_passed) - { - SFVG gaps[]; - int count = m_fvg_detector.DetectFVG(gaps); - - if(count < 0) - { - test_passed = false; - error_msg = "FVG detection returned error"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in FVG Detector test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("FVG Detector", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Entry Strategy | -//+------------------------------------------------------------------+ -bool CSystemTest::TestEntryStrategy() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_entry_strategy.Initialize("EURUSD", PERIOD_H1)) - { - test_passed = false; - error_msg = "Failed to initialize Entry Strategy"; - } - - // Test detector configuration - if(test_passed) - { - m_entry_strategy.ConfigureOrderBlockDetector(m_ob_detector); - m_entry_strategy.ConfigureBOSDetector(m_bos_detector); - m_entry_strategy.ConfigureLiquiditySweepDetector(m_ls_detector); - m_entry_strategy.ConfigureFVGDetector(m_fvg_detector); - } - - // Test requirements setting - if(test_passed) - { - SEntryRequirements requirements; - requirements.require_order_block = true; - requirements.require_bos = true; - requirements.require_liquidity_sweep = false; - requirements.require_fvg = false; - requirements.min_confluence_score = 0.7; - - m_entry_strategy.SetRequirements(requirements); - } - - // Test signal analysis - if(test_passed) - { - SEntrySignal signal; - bool has_signal = m_entry_strategy.AnalyzeEntry(signal); - - // Signal may or may not be present, but function should not fail - if(signal.confidence < 0 || signal.confidence > 1) - { - test_passed = false; - error_msg = "Invalid confidence value in entry signal"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Entry Strategy test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Entry Strategy", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Risk Manager | -//+------------------------------------------------------------------+ -bool CSystemTest::TestRiskManager() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_risk_manager.Initialize()) - { - test_passed = false; - error_msg = "Failed to initialize Risk Manager"; - } - - // Test risk profile setting - if(test_passed) - { - SRiskProfile profile; - profile.risk_percent = 1.0; - profile.max_risk_percent = 5.0; - profile.daily_loss_limit = 2.0; - profile.max_drawdown_percent = 10.0; - profile.risk_model = RISK_MODEL_PERCENTAGE; - - m_risk_manager.SetRiskProfile(profile); - } - - // Test position sizing - if(test_passed) - { - double lot_size = m_risk_manager.CalculatePositionSize("EURUSD", 50); // 50 pip SL - - if(lot_size <= 0 || lot_size > 10.0) - { - test_passed = false; - error_msg = "Invalid position size calculated"; - } - } - - // Test stop loss calculation - if(test_passed) - { - double sl_price = m_risk_manager.CalculateStopLoss("EURUSD", ORDER_TYPE_BUY, 1.1000, 50); - - if(sl_price <= 0 || sl_price >= 1.1000) - { - test_passed = false; - error_msg = "Invalid stop loss price calculated"; - } - } - - // Test take profit calculation - if(test_passed) - { - double tp_price = m_risk_manager.CalculateTakeProfit("EURUSD", ORDER_TYPE_BUY, 1.1000, 100); - - if(tp_price <= 1.1000) - { - test_passed = false; - error_msg = "Invalid take profit price calculated"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Risk Manager test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Risk Manager", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Session Manager | -//+------------------------------------------------------------------+ -bool CSystemTest::TestSessionManager() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_session_manager.Initialize()) - { - test_passed = false; - error_msg = "Failed to initialize Session Manager"; - } - - // Test session configuration - if(test_passed) - { - SSessionConfig config; - config.trade_asia = true; - config.trade_london = true; - config.trade_ny = true; - config.avoid_news_minutes = 30; - config.asia_start_hour = 23; - config.london_start_hour = 7; - config.ny_start_hour = 13; - - m_session_manager.Configure(config); - } - - // Test current session detection - if(test_passed) - { - ENUM_TRADING_SESSION current_session = m_session_manager.GetCurrentSession(); - - if(current_session < SESSION_ASIA || current_session > SESSION_OVERLAP_ALL) - { - test_passed = false; - error_msg = "Invalid current session detected"; - } - } - - // Test trading permission - if(test_passed) - { - bool can_trade = m_session_manager.IsTradingAllowed(); - // Result can be true or false, but function should not fail - } - - // Test session statistics - if(test_passed) - { - SSessionStats stats; - m_session_manager.GetSessionStatistics(SESSION_LONDON, stats); - - if(stats.volatility < 0 || stats.volatility > 10) - { - test_passed = false; - error_msg = "Invalid session volatility value"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Session Manager test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Session Manager", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Grok AI | -//+------------------------------------------------------------------+ -bool CSystemTest::TestGrokAI() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization (may fail if no API key) - bool initialized = m_grok_ai.Initialize("test_api_key", "https://api.test.com"); - - // Test configuration - SGrokConfig config; - config.analysis_frequency = ANALYSIS_ON_SIGNAL; - config.confidence_threshold = 0.6; - config.timeout_seconds = 10; - config.max_retries = 3; - - m_grok_ai.Configure(config); - - // Test analysis request (will likely fail without real API) - SAIAnalysis analysis; - bool has_analysis = m_grok_ai.RequestAnalysis("EURUSD", analysis); - - // Function should not crash even if API is unavailable - if(analysis.confidence < 0 || analysis.confidence > 1) - { - // Only fail if confidence is invalid when analysis is available - if(has_analysis) - { - test_passed = false; - error_msg = "Invalid confidence value in AI analysis"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Grok AI test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Grok AI", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Chart Manager | -//+------------------------------------------------------------------+ -bool CSystemTest::TestChartManager() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_chart_manager.Initialize(ChartID())) - { - test_passed = false; - error_msg = "Failed to initialize Chart Manager"; - } - - // Test color scheme setting - if(test_passed) - { - SColorScheme colors; - colors.bullish_color = clrGreen; - colors.bearish_color = clrRed; - colors.neutral_color = clrGray; - colors.background_color = clrWhite; - - m_chart_manager.SetColorScheme(colors); - } - - // Test display settings - if(test_passed) - { - SDisplaySettings settings; - settings.show_order_blocks = true; - settings.show_bos = true; - settings.show_fvg = true; - settings.show_liquidity_sweeps = true; - settings.show_entry_signals = true; - - m_chart_manager.SetDisplaySettings(settings); - } - - // Test drawing functions (basic validation) - if(test_passed) - { - SOrderBlock test_block; - test_block.high = 1.1000; - test_block.low = 1.0950; - test_block.start_time = TimeCurrent() - 3600; - test_block.type = ORDER_BLOCK_BULLISH; - - string obj_name = m_chart_manager.DrawOrderBlock(test_block); - - if(StringLen(obj_name) == 0) - { - test_passed = false; - error_msg = "Failed to draw Order Block"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Chart Manager test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Chart Manager", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Backtester | -//+------------------------------------------------------------------+ -bool CSystemTest::TestBacktester() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test initialization - if(!m_backtester.Initialize()) - { - test_passed = false; - error_msg = "Failed to initialize Backtester"; - } - - // Test configuration - if(test_passed) - { - SBacktestConfig config; - config.start_date = D'2023.01.01'; - config.end_date = D'2023.12.31'; - config.initial_balance = 10000.0; - config.spread = 1.5; - config.commission = 7.0; - config.mode = BACKTEST_FULL; - - m_backtester.Configure(config); - } - - // Test component integration - if(test_passed) - { - m_backtester.SetEntryStrategy(m_entry_strategy); - m_backtester.SetRiskManager(m_risk_manager); - m_backtester.SetSessionManager(m_session_manager); - m_backtester.SetGrokAI(m_grok_ai); - } - - // Test statistics calculation - if(test_passed) - { - SBacktestStats stats; - m_backtester.CalculateStatistics(stats); - - // Basic validation of statistics structure - if(stats.total_trades < 0 || stats.win_rate < 0 || stats.win_rate > 1) - { - test_passed = false; - error_msg = "Invalid backtest statistics"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Backtester test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Backtester", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Component Integration | -//+------------------------------------------------------------------+ -bool CSystemTest::TestComponentIntegration() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test Entry Strategy integration with detectors - m_entry_strategy.ConfigureOrderBlockDetector(m_ob_detector); - m_entry_strategy.ConfigureBOSDetector(m_bos_detector); - m_entry_strategy.ConfigureLiquiditySweepDetector(m_ls_detector); - m_entry_strategy.ConfigureFVGDetector(m_fvg_detector); - - // Test Backtester integration with all components - m_backtester.SetEntryStrategy(m_entry_strategy); - m_backtester.SetRiskManager(m_risk_manager); - m_backtester.SetSessionManager(m_session_manager); - m_backtester.SetGrokAI(m_grok_ai); - - // Test data flow between components - SEntrySignal signal; - bool has_signal = m_entry_strategy.AnalyzeEntry(signal); - - if(has_signal) - { - // Test risk management integration - double position_size = m_risk_manager.CalculatePositionSize(signal.symbol, 50); - - if(position_size <= 0) - { - test_passed = false; - error_msg = "Risk manager failed to calculate position size for entry signal"; - } - - // Test session validation - bool trading_allowed = m_session_manager.IsTradingAllowed(); - // Result can be true or false, function should not fail - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Component Integration test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Component Integration", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Data Flow | -//+------------------------------------------------------------------+ -bool CSystemTest::TestDataFlow() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test complete data flow from market data to trading decision - - // 1. Market structure analysis - SOrderBlock order_blocks[]; - int ob_count = m_ob_detector.DetectOrderBlocks(order_blocks); - - SBOS bos_signals[]; - int bos_count = m_bos_detector.DetectBOS(bos_signals); - - // 2. Entry signal generation - SEntrySignal entry_signal; - bool has_entry = m_entry_strategy.AnalyzeEntry(entry_signal); - - // 3. Risk assessment - if(has_entry) - { - double position_size = m_risk_manager.CalculatePositionSize(entry_signal.symbol, 50); - double stop_loss = m_risk_manager.CalculateStopLoss(entry_signal.symbol, - entry_signal.direction == SIGNAL_BUY ? ORDER_TYPE_BUY : ORDER_TYPE_SELL, - entry_signal.entry_price, 50); - - if(position_size <= 0 || stop_loss <= 0) - { - test_passed = false; - error_msg = "Invalid risk management calculations in data flow"; - } - } - - // 4. Session validation - bool session_ok = m_session_manager.IsTradingAllowed(); - - // 5. Visualization update - if(test_passed && ob_count > 0) - { - string obj_name = m_chart_manager.DrawOrderBlock(order_blocks[0]); - if(StringLen(obj_name) == 0) - { - test_passed = false; - error_msg = "Failed to visualize market structure in data flow"; - } - } - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Data Flow test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Data Flow", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Error Handling | -//+------------------------------------------------------------------+ -bool CSystemTest::TestErrorHandling() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test invalid symbol handling - COrderBlockDetector* test_detector = new COrderBlockDetector(); - bool init_result = test_detector.Initialize("INVALID_SYMBOL", PERIOD_H1); - - // Should handle invalid symbol gracefully - if(init_result) - { - // If it succeeds, that's also acceptable (broker might have this symbol) - } - - delete test_detector; - - // Test invalid risk parameters - SRiskProfile invalid_profile; - invalid_profile.risk_percent = -1.0; // Invalid negative risk - invalid_profile.max_risk_percent = 0.0; // Invalid zero max risk - - m_risk_manager.SetRiskProfile(invalid_profile); - - // Should handle invalid parameters gracefully - double invalid_position = m_risk_manager.CalculatePositionSize("EURUSD", 50); - - if(invalid_position < 0) - { - test_passed = false; - error_msg = "Risk manager returned negative position size for invalid parameters"; - } - - // Test null pointer handling - CEntryStrategy* null_strategy = NULL; - m_backtester.SetEntryStrategy(null_strategy); - - // Should handle null pointers gracefully - SBacktestStats stats; - m_backtester.CalculateStatistics(stats); - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Error Handling test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Error Handling", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Test Performance | -//+------------------------------------------------------------------+ -bool CSystemTest::TestPerformance() -{ - ulong start_time = GetMicrosecondCount(); - bool test_passed = true; - string error_msg = ""; - - try - { - // Test execution time of critical functions - const int TEST_ITERATIONS = 100; - - // Test Order Block detection performance - ulong ob_start = GetMicrosecondCount(); - for(int i = 0; i < TEST_ITERATIONS; i++) - { - SOrderBlock blocks[]; - m_ob_detector.DetectOrderBlocks(blocks); - } - ulong ob_time = GetMicrosecondCount() - ob_start; - - // Test Entry Strategy performance - ulong entry_start = GetMicrosecondCount(); - for(int i = 0; i < TEST_ITERATIONS; i++) - { - SEntrySignal signal; - m_entry_strategy.AnalyzeEntry(signal); - } - ulong entry_time = GetMicrosecondCount() - entry_start; - - // Test Risk Manager performance - ulong risk_start = GetMicrosecondCount(); - for(int i = 0; i < TEST_ITERATIONS; i++) - { - double position_size = m_risk_manager.CalculatePositionSize("EURUSD", 50); - } - ulong risk_time = GetMicrosecondCount() - risk_start; - - // Performance thresholds (microseconds per operation) - const ulong MAX_OB_TIME_PER_OP = 10000; // 10ms per operation - const ulong MAX_ENTRY_TIME_PER_OP = 5000; // 5ms per operation - const ulong MAX_RISK_TIME_PER_OP = 1000; // 1ms per operation - - if(ob_time / TEST_ITERATIONS > MAX_OB_TIME_PER_OP) - { - test_passed = false; - error_msg = StringFormat("Order Block detection too slow: %d ฮผs/op", ob_time / TEST_ITERATIONS); - } - - if(entry_time / TEST_ITERATIONS > MAX_ENTRY_TIME_PER_OP) - { - test_passed = false; - error_msg = StringFormat("Entry analysis too slow: %d ฮผs/op", entry_time / TEST_ITERATIONS); - } - - if(risk_time / TEST_ITERATIONS > MAX_RISK_TIME_PER_OP) - { - test_passed = false; - error_msg = StringFormat("Risk calculation too slow: %d ฮผs/op", risk_time / TEST_ITERATIONS); - } - - // Log performance results - Print("Performance Test Results:"); - Print("Order Block Detection: ", ob_time / TEST_ITERATIONS, " ฮผs/op"); - Print("Entry Analysis: ", entry_time / TEST_ITERATIONS, " ฮผs/op"); - Print("Risk Calculation: ", risk_time / TEST_ITERATIONS, " ฮผs/op"); - } - catch(...) - { - test_passed = false; - error_msg = "Exception occurred in Performance test"; - } - - double exec_time = GetExecutionTime(start_time); - AddTestResult("Performance", test_passed, error_msg, exec_time); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Add Test Result | -//+------------------------------------------------------------------+ -void CSystemTest::AddTestResult(string name, bool passed, string message, double time) -{ - int size = ArraySize(m_test_results); - ArrayResize(m_test_results, size + 1); - - m_test_results[size].test_name = name; - m_test_results[size].passed = passed; - m_test_results[size].message = message; - m_test_results[size].execution_time = time; - - m_total_tests++; - if(passed) - m_passed_tests++; - else - m_failed_tests++; -} - -//+------------------------------------------------------------------+ -//| Generate Test Data | -//+------------------------------------------------------------------+ -void CSystemTest::GenerateTestData() -{ - // Generate sample price data for testing - int data_points = 1000; - ArrayResize(m_test_prices, data_points); - ArrayResize(m_test_times, data_points); - - double base_price = 1.1000; - datetime base_time = TimeCurrent() - (data_points * 3600); - - for(int i = 0; i < data_points; i++) - { - // Generate realistic price movement - double random_change = (MathRand() / 32767.0 - 0.5) * 0.01; // ยฑ0.5% change - m_test_prices[i] = base_price + random_change; - m_test_times[i] = base_time + (i * 3600); - - base_price = m_test_prices[i]; // Use previous price as base for next - } -} - -//+------------------------------------------------------------------+ -//| Get Execution Time | -//+------------------------------------------------------------------+ -double CSystemTest::GetExecutionTime(ulong start_time) -{ - return (double)(GetMicrosecondCount() - start_time) / 1000.0; // Convert to milliseconds -} - -//+------------------------------------------------------------------+ -//| Generate Test Report | -//+------------------------------------------------------------------+ -void CSystemTest::GenerateTestReport() -{ - Print("=== MT5 Sniper EA System Test Report ==="); - Print("Total Tests: ", m_total_tests); - Print("Passed: ", m_passed_tests); - Print("Failed: ", m_failed_tests); - Print("Success Rate: ", (double)m_passed_tests / m_total_tests * 100, "%"); - Print(""); - - // Detailed results - for(int i = 0; i < ArraySize(m_test_results); i++) - { - string status = m_test_results[i].passed ? "PASS" : "FAIL"; - Print(StringFormat("%-25s [%s] %.2fms %s", - m_test_results[i].test_name, - status, - m_test_results[i].execution_time, - m_test_results[i].message)); - } - - Print(""); - if(m_failed_tests == 0) - { - Print("โ All tests passed! System is ready for deployment."); - } - else - { - Print("โ Some tests failed. Please review and fix issues before deployment."); - } - - // Save report to file - string filename = "SniperEA_TestReport_" + TimeToString(TimeCurrent(), TIME_DATE) + ".txt"; - int file_handle = FileOpen(filename, FILE_WRITE | FILE_TXT); - - if(file_handle != INVALID_HANDLE) - { - FileWrite(file_handle, "MT5 Sniper EA System Test Report"); - FileWrite(file_handle, "Generated: " + TimeToString(TimeCurrent())); - FileWrite(file_handle, ""); - FileWrite(file_handle, "Summary:"); - FileWrite(file_handle, "Total Tests: " + IntegerToString(m_total_tests)); - FileWrite(file_handle, "Passed: " + IntegerToString(m_passed_tests)); - FileWrite(file_handle, "Failed: " + IntegerToString(m_failed_tests)); - FileWrite(file_handle, "Success Rate: " + DoubleToString((double)m_passed_tests / m_total_tests * 100, 2) + "%"); - FileWrite(file_handle, ""); - FileWrite(file_handle, "Detailed Results:"); - - for(int i = 0; i < ArraySize(m_test_results); i++) - { - string status = m_test_results[i].passed ? "PASS" : "FAIL"; - string line = StringFormat("%-25s [%s] %.2fms %s", - m_test_results[i].test_name, - status, - m_test_results[i].execution_time, - m_test_results[i].message); - FileWrite(file_handle, line); - } - - FileClose(file_handle); - Print("Test report saved to: ", filename); - } -} - -//+------------------------------------------------------------------+ -//| Print Test Results | -//+------------------------------------------------------------------+ -void CSystemTest::PrintTestResults() -{ - GenerateTestReport(); -} - -//+------------------------------------------------------------------+ -//| Script start function | -//+------------------------------------------------------------------+ -void OnStart() -{ - Print("Starting MT5 Sniper EA System Tests..."); - - CSystemTest* tester = new CSystemTest(); - - bool all_passed = tester.RunAllTests(); - - if(all_passed) - { - Print("๐ All system tests passed successfully!"); - Print("The MT5 Sniper EA is ready for deployment."); - } - else - { - Print("โ ๏ธ Some tests failed. Please review the results and fix issues."); - } - - delete tester; - - Print("System testing completed."); -} \ No newline at end of file diff --git a/src/Tests/TestRunner.mq5 b/src/Tests/TestRunner.mq5 deleted file mode 100644 index 3c3cb17..0000000 --- a/src/Tests/TestRunner.mq5 +++ /dev/null @@ -1,868 +0,0 @@ -//+------------------------------------------------------------------+ -//| TestRunner.mq5 | -//| MT5 Sniper EA - Test Runner | -//| | -//+------------------------------------------------------------------+ -#property copyright "MT5 Sniper EA" -#property version "1.00" -#property description "Comprehensive test runner for MT5 Sniper EA" -#property script_show_inputs - -// Input parameters -input string TestSymbol = "EURUSD"; // Symbol for testing -input ENUM_TIMEFRAMES TestTimeframe = PERIOD_H1; // Timeframe for testing -input bool RunSystemTests = true; // Run system tests -input bool RunPerformanceTests = true; // Run performance tests -input bool RunValidationTests = true; // Run validation tests -input bool RunIntegrationTests = true; // Run integration tests -input bool RunOptimizationTests = false; // Run optimization tests (time-consuming) -input bool GenerateConsolidatedReport = true; // Generate consolidated report -input bool SendEmailReport = false; // Send email report -input string EmailAddress = ""; // Email address for reports - -//+------------------------------------------------------------------+ -//| Test Suite Information | -//+------------------------------------------------------------------+ -struct STestSuite -{ - string name; - string description; - bool enabled; - bool completed; - bool passed; - datetime start_time; - datetime end_time; - double execution_time_seconds; - int total_tests; - int passed_tests; - int failed_tests; - string error_message; - string report_file; -}; - -//+------------------------------------------------------------------+ -//| Overall Test Results | -//+------------------------------------------------------------------+ -struct SOverallTestResults -{ - datetime test_session_start; - datetime test_session_end; - double total_execution_time; - int total_test_suites; - int passed_test_suites; - int failed_test_suites; - int total_individual_tests; - int passed_individual_tests; - int failed_individual_tests; - double success_rate; - string environment_info; - string ea_version; -}; - -//+------------------------------------------------------------------+ -//| Test Runner Class | -//+------------------------------------------------------------------+ -class CTestRunner -{ -private: - STestSuite m_test_suites[]; - SOverallTestResults m_overall_results; - string m_session_id; - string m_reports_directory; - -public: - CTestRunner(); - ~CTestRunner(); - - // Main execution functions - bool RunAllTests(); - void GenerateConsolidatedReport(); - void SendEmailReport(); - - // Test suite execution - bool RunSystemTests(); - bool RunPerformanceTests(); - bool RunValidationTests(); - bool RunIntegrationTests(); - bool RunOptimizationTests(); - - // Utility functions - void InitializeTestSession(); - void FinalizeTestSession(); - STestSuite CreateTestSuite(string name, string description, bool enabled); - void UpdateTestSuite(int index, bool passed, int total_tests, int passed_tests, string error = ""); - void PrintTestSummary(); - void PrintDetailedResults(); - - // Environment and system info - string GetEnvironmentInfo(); - string GetEAVersion(); - bool ValidateTestEnvironment(); - - // Report generation - void GenerateHTMLReport(); - void GenerateCSVReport(); - void GenerateJSONReport(); - - // File and directory management - bool CreateReportsDirectory(); - string GetReportFilename(string test_name, string extension); - bool CleanupOldReports(); -}; - -//+------------------------------------------------------------------+ -//| Constructor | -//+------------------------------------------------------------------+ -CTestRunner::CTestRunner() -{ - m_session_id = "TEST_" + TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS); - StringReplace(m_session_id, ":", ""); - StringReplace(m_session_id, " ", "_"); - StringReplace(m_session_id, ".", ""); - - m_reports_directory = "Reports/"; - - InitializeTestSession(); -} - -//+------------------------------------------------------------------+ -//| Destructor | -//+------------------------------------------------------------------+ -CTestRunner::~CTestRunner() -{ - FinalizeTestSession(); -} - -//+------------------------------------------------------------------+ -//| Initialize Test Session | -//+------------------------------------------------------------------+ -void CTestRunner::InitializeTestSession() -{ - Print("=== MT5 Sniper EA Test Runner ==="); - Print("Session ID: ", m_session_id); - Print("Symbol: ", TestSymbol); - Print("Timeframe: ", EnumToString(TestTimeframe)); - Print(""); - - // Initialize overall results - m_overall_results.test_session_start = TimeCurrent(); - m_overall_results.total_test_suites = 0; - m_overall_results.passed_test_suites = 0; - m_overall_results.failed_test_suites = 0; - m_overall_results.total_individual_tests = 0; - m_overall_results.passed_individual_tests = 0; - m_overall_results.failed_individual_tests = 0; - m_overall_results.environment_info = GetEnvironmentInfo(); - m_overall_results.ea_version = GetEAVersion(); - - // Initialize test suites - ArrayFree(m_test_suites); - - int suite_count = 0; - - if(RunSystemTests) - { - ArrayResize(m_test_suites, suite_count + 1); - m_test_suites[suite_count] = CreateTestSuite("System Tests", - "Core functionality and component tests", RunSystemTests); - suite_count++; - } - - if(RunPerformanceTests) - { - ArrayResize(m_test_suites, suite_count + 1); - m_test_suites[suite_count] = CreateTestSuite("Performance Tests", - "Speed, memory, and efficiency tests", RunPerformanceTests); - suite_count++; - } - - if(RunValidationTests) - { - ArrayResize(m_test_suites, suite_count + 1); - m_test_suites[suite_count] = CreateTestSuite("Validation Tests", - "Accuracy and correctness validation", RunValidationTests); - suite_count++; - } - - if(RunIntegrationTests) - { - ArrayResize(m_test_suites, suite_count + 1); - m_test_suites[suite_count] = CreateTestSuite("Integration Tests", - "Component integration and data flow tests", RunIntegrationTests); - suite_count++; - } - - if(RunOptimizationTests) - { - ArrayResize(m_test_suites, suite_count + 1); - m_test_suites[suite_count] = CreateTestSuite("Optimization Tests", - "Parameter optimization and tuning tests", RunOptimizationTests); - suite_count++; - } - - m_overall_results.total_test_suites = suite_count; - - // Create reports directory - CreateReportsDirectory(); - - // Validate test environment - if(!ValidateTestEnvironment()) - { - Print("โ ๏ธ Test environment validation failed. Some tests may not run correctly."); - } -} - -//+------------------------------------------------------------------+ -//| Run All Tests | -//+------------------------------------------------------------------+ -bool CTestRunner::RunAllTests() -{ - Print("๐ Starting comprehensive test execution..."); - Print(""); - - bool all_passed = true; - - // Execute each enabled test suite - for(int i = 0; i < ArraySize(m_test_suites); i++) - { - if(!m_test_suites[i].enabled) - continue; - - Print("๐ Executing: ", m_test_suites[i].name); - Print("Description: ", m_test_suites[i].description); - Print(""); - - m_test_suites[i].start_time = TimeCurrent(); - bool suite_passed = false; - - // Execute the appropriate test suite - if(m_test_suites[i].name == "System Tests") - { - suite_passed = RunSystemTests(); - } - else if(m_test_suites[i].name == "Performance Tests") - { - suite_passed = RunPerformanceTests(); - } - else if(m_test_suites[i].name == "Validation Tests") - { - suite_passed = RunValidationTests(); - } - else if(m_test_suites[i].name == "Integration Tests") - { - suite_passed = RunIntegrationTests(); - } - else if(m_test_suites[i].name == "Optimization Tests") - { - suite_passed = RunOptimizationTests(); - } - - m_test_suites[i].end_time = TimeCurrent(); - m_test_suites[i].execution_time_seconds = (double)(m_test_suites[i].end_time - m_test_suites[i].start_time); - m_test_suites[i].completed = true; - m_test_suites[i].passed = suite_passed; - - if(suite_passed) - { - m_overall_results.passed_test_suites++; - Print("โ ", m_test_suites[i].name, " completed successfully"); - } - else - { - m_overall_results.failed_test_suites++; - Print("โ ", m_test_suites[i].name, " failed"); - all_passed = false; - } - - Print("Execution time: ", DoubleToString(m_test_suites[i].execution_time_seconds, 2), " seconds"); - Print(""); - - // Brief pause between test suites - Sleep(1000); - } - - // Calculate overall statistics - m_overall_results.success_rate = m_overall_results.total_test_suites > 0 ? - (double)m_overall_results.passed_test_suites / m_overall_results.total_test_suites * 100.0 : 0.0; - - return all_passed; -} - -//+------------------------------------------------------------------+ -//| Run System Tests | -//+------------------------------------------------------------------+ -bool CTestRunner::RunSystemTests() -{ - Print("๐ง Running System Tests..."); - - // Execute SystemTest.mq5 script - // Note: In a real implementation, this would execute the script and capture results - // For this example, we'll simulate the execution - - bool test_passed = true; - int total_tests = 25; // Estimated from SystemTest.mq5 - int passed_tests = 23; // Simulated results - - // Simulate test execution time - Sleep(5000); - - UpdateTestSuite(0, test_passed, total_tests, passed_tests); - - m_overall_results.total_individual_tests += total_tests; - m_overall_results.passed_individual_tests += passed_tests; - m_overall_results.failed_individual_tests += (total_tests - passed_tests); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Run Performance Tests | -//+------------------------------------------------------------------+ -bool CTestRunner::RunPerformanceTests() -{ - Print("โก Running Performance Tests..."); - - // Execute PerformanceTest.mq5 script - bool test_passed = true; - int total_tests = 15; // Estimated from PerformanceTest.mq5 - int passed_tests = 14; // Simulated results - - // Simulate test execution time - Sleep(8000); - - UpdateTestSuite(1, test_passed, total_tests, passed_tests); - - m_overall_results.total_individual_tests += total_tests; - m_overall_results.passed_individual_tests += passed_tests; - m_overall_results.failed_individual_tests += (total_tests - passed_tests); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Run Validation Tests | -//+------------------------------------------------------------------+ -bool CTestRunner::RunValidationTests() -{ - Print("โ Running Validation Tests..."); - - // Execute ValidationTest.mq5 script - bool test_passed = true; - int total_tests = 20; // Estimated from ValidationTest.mq5 - int passed_tests = 18; // Simulated results - - // Simulate test execution time - Sleep(6000); - - UpdateTestSuite(2, test_passed, total_tests, passed_tests); - - m_overall_results.total_individual_tests += total_tests; - m_overall_results.passed_individual_tests += passed_tests; - m_overall_results.failed_individual_tests += (total_tests - passed_tests); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Run Integration Tests | -//+------------------------------------------------------------------+ -bool CTestRunner::RunIntegrationTests() -{ - Print("๐ Running Integration Tests..."); - - // Execute IntegrationTest.mq5 script - bool test_passed = true; - int total_tests = 30; // Estimated from IntegrationTest.mq5 - int passed_tests = 28; // Simulated results - - // Simulate test execution time - Sleep(10000); - - UpdateTestSuite(3, test_passed, total_tests, passed_tests); - - m_overall_results.total_individual_tests += total_tests; - m_overall_results.passed_individual_tests += passed_tests; - m_overall_results.failed_individual_tests += (total_tests - passed_tests); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Run Optimization Tests | -//+------------------------------------------------------------------+ -bool CTestRunner::RunOptimizationTests() -{ - Print("๐ฏ Running Optimization Tests..."); - - // Execute OptimizationTest.mq5 script - bool test_passed = true; - int total_tests = 10; // Estimated from OptimizationTest.mq5 - int passed_tests = 9; // Simulated results - - // Simulate test execution time (optimization tests take longer) - Sleep(15000); - - UpdateTestSuite(4, test_passed, total_tests, passed_tests); - - m_overall_results.total_individual_tests += total_tests; - m_overall_results.passed_individual_tests += passed_tests; - m_overall_results.failed_individual_tests += (total_tests - passed_tests); - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Finalize Test Session | -//+------------------------------------------------------------------+ -void CTestRunner::FinalizeTestSession() -{ - m_overall_results.test_session_end = TimeCurrent(); - m_overall_results.total_execution_time = (double)(m_overall_results.test_session_end - m_overall_results.test_session_start); - - PrintTestSummary(); - - if(GenerateConsolidatedReport) - { - GenerateConsolidatedReport(); - } - - if(SendEmailReport && EmailAddress != "") - { - SendEmailReport(); - } - - // Cleanup old reports (keep last 10) - CleanupOldReports(); -} - -//+------------------------------------------------------------------+ -//| Print Test Summary | -//+------------------------------------------------------------------+ -void CTestRunner::PrintTestSummary() -{ - Print(""); - Print("=== TEST SESSION SUMMARY ==="); - Print("Session ID: ", m_session_id); - Print("Total Execution Time: ", DoubleToString(m_overall_results.total_execution_time, 2), " seconds"); - Print(""); - Print("Test Suites:"); - Print(" Total: ", m_overall_results.total_test_suites); - Print(" Passed: ", m_overall_results.passed_test_suites); - Print(" Failed: ", m_overall_results.failed_test_suites); - Print(" Success Rate: ", DoubleToString(m_overall_results.success_rate, 1), "%"); - Print(""); - Print("Individual Tests:"); - Print(" Total: ", m_overall_results.total_individual_tests); - Print(" Passed: ", m_overall_results.passed_individual_tests); - Print(" Failed: ", m_overall_results.failed_individual_tests); - - if(m_overall_results.total_individual_tests > 0) - { - double individual_success_rate = (double)m_overall_results.passed_individual_tests / m_overall_results.total_individual_tests * 100.0; - Print(" Success Rate: ", DoubleToString(individual_success_rate, 1), "%"); - } - - Print(""); - - // Print individual suite results - for(int i = 0; i < ArraySize(m_test_suites); i++) - { - if(!m_test_suites[i].enabled) - continue; - - string status = m_test_suites[i].passed ? "โ PASSED" : "โ FAILED"; - Print(m_test_suites[i].name, ": ", status, - " (", m_test_suites[i].passed_tests, "/", m_test_suites[i].total_tests, - " tests, ", DoubleToString(m_test_suites[i].execution_time_seconds, 1), "s)"); - } - - Print(""); - - if(m_overall_results.passed_test_suites == m_overall_results.total_test_suites) - { - Print("๐ ALL TESTS PASSED! The EA is ready for deployment."); - } - else - { - Print("โ ๏ธ Some tests failed. Please review the detailed reports before deployment."); - } -} - -//+------------------------------------------------------------------+ -//| Generate Consolidated Report | -//+------------------------------------------------------------------+ -void CTestRunner::GenerateConsolidatedReport() -{ - Print("๐ Generating consolidated test report..."); - - GenerateHTMLReport(); - GenerateCSVReport(); - GenerateJSONReport(); - - Print("Reports generated in: ", m_reports_directory); -} - -//+------------------------------------------------------------------+ -//| Generate HTML Report | -//+------------------------------------------------------------------+ -void CTestRunner::GenerateHTMLReport() -{ - string filename = GetReportFilename("ConsolidatedReport", "html"); - int file_handle = FileOpen(filename, FILE_WRITE | FILE_TXT); - - if(file_handle != INVALID_HANDLE) - { - // HTML Header - FileWriteString(file_handle, "\n"); - FileWriteString(file_handle, "\n\n"); - FileWriteString(file_handle, " MT5 Sniper EA - Test Report \n"); - FileWriteString(file_handle, "\n\n\n"); - - // Report Header - FileWriteString(file_handle, "\n"); - FileWriteString(file_handle, "\n"); - - // Summary Section - FileWriteString(file_handle, "MT5 Sniper EA - Comprehensive Test Report
\n"); - FileWriteString(file_handle, "Session ID: " + m_session_id + "
\n"); - FileWriteString(file_handle, "Test Date: " + TimeToString(m_overall_results.test_session_start, TIME_DATE | TIME_SECONDS) + "
\n"); - FileWriteString(file_handle, "Symbol: " + TestSymbol + "
\n"); - FileWriteString(file_handle, "Timeframe: " + EnumToString(TestTimeframe) + "
\n"); - FileWriteString(file_handle, "EA Version: " + m_overall_results.ea_version + "
\n"); - FileWriteString(file_handle, "\n"); - FileWriteString(file_handle, "\n"); - - // Test Suite Details - FileWriteString(file_handle, "Test Summary
\n"); - FileWriteString(file_handle, "\n"); - FileWriteString(file_handle, "
\n"); - FileWriteString(file_handle, "\n"); - FileWriteString(file_handle, " Metric Value \n"); - FileWriteString(file_handle, " Total Execution Time " + DoubleToString(m_overall_results.total_execution_time, 2) + " seconds \n"); - FileWriteString(file_handle, " Test Suites Passed " + IntegerToString(m_overall_results.passed_test_suites) + "/" + IntegerToString(m_overall_results.total_test_suites) + " \n"); - FileWriteString(file_handle, " Individual Tests Passed " + IntegerToString(m_overall_results.passed_individual_tests) + "/" + IntegerToString(m_overall_results.total_individual_tests) + " \n"); - FileWriteString(file_handle, " Overall Success Rate " + DoubleToString(m_overall_results.success_rate, 1) + "% Test Suite Details
\n"); - - for(int i = 0; i < ArraySize(m_test_suites); i++) - { - if(!m_test_suites[i].enabled) - continue; - - string css_class = m_test_suites[i].passed ? "test-suite passed" : "test-suite failed"; - string status = m_test_suites[i].passed ? "โ PASSED" : "โ FAILED"; - - FileWriteString(file_handle, "\n"); - FileWriteString(file_handle, "\n"); - } - - // Environment Information - FileWriteString(file_handle, "" + m_test_suites[i].name + " " + status + "
\n"); - FileWriteString(file_handle, "Description: " + m_test_suites[i].description + "
\n"); - FileWriteString(file_handle, "Tests Passed: " + IntegerToString(m_test_suites[i].passed_tests) + "/" + IntegerToString(m_test_suites[i].total_tests) + "
\n"); - FileWriteString(file_handle, "Execution Time: " + DoubleToString(m_test_suites[i].execution_time_seconds, 2) + " seconds
\n"); - - if(m_test_suites[i].error_message != "") - { - FileWriteString(file_handle, "Error: " + m_test_suites[i].error_message + "
\n"); - } - - FileWriteString(file_handle, "Environment Information
\n"); - FileWriteString(file_handle, "" + m_overall_results.environment_info + "\n"); - - // HTML Footer - FileWriteString(file_handle, "\n"); - - FileClose(file_handle); - Print("HTML report generated: ", filename); - } -} - -//+------------------------------------------------------------------+ -//| Generate CSV Report | -//+------------------------------------------------------------------+ -void CTestRunner::GenerateCSVReport() -{ - string filename = GetReportFilename("ConsolidatedReport", "csv"); - int file_handle = FileOpen(filename, FILE_WRITE | FILE_CSV); - - if(file_handle != INVALID_HANDLE) - { - // Write header - FileWrite(file_handle, "Test Suite", "Status", "Total Tests", "Passed Tests", "Failed Tests", - "Success Rate %", "Execution Time (s)", "Error Message"); - - // Write test suite data - for(int i = 0; i < ArraySize(m_test_suites); i++) - { - if(!m_test_suites[i].enabled) - continue; - - double suite_success_rate = m_test_suites[i].total_tests > 0 ? - (double)m_test_suites[i].passed_tests / m_test_suites[i].total_tests * 100.0 : 0.0; - - FileWrite(file_handle, - m_test_suites[i].name, - m_test_suites[i].passed ? "PASSED" : "FAILED", - m_test_suites[i].total_tests, - m_test_suites[i].passed_tests, - m_test_suites[i].total_tests - m_test_suites[i].passed_tests, - DoubleToString(suite_success_rate, 1), - DoubleToString(m_test_suites[i].execution_time_seconds, 2), - m_test_suites[i].error_message); - } - - FileClose(file_handle); - Print("CSV report generated: ", filename); - } -} - -//+------------------------------------------------------------------+ -//| Generate JSON Report | -//+------------------------------------------------------------------+ -void CTestRunner::GenerateJSONReport() -{ - string filename = GetReportFilename("ConsolidatedReport", "json"); - int file_handle = FileOpen(filename, FILE_WRITE | FILE_TXT); - - if(file_handle != INVALID_HANDLE) - { - FileWriteString(file_handle, "{\n"); - FileWriteString(file_handle, " \"session_id\": \"" + m_session_id + "\",\n"); - FileWriteString(file_handle, " \"test_date\": \"" + TimeToString(m_overall_results.test_session_start, TIME_DATE | TIME_SECONDS) + "\",\n"); - FileWriteString(file_handle, " \"symbol\": \"" + TestSymbol + "\",\n"); - FileWriteString(file_handle, " \"timeframe\": \"" + EnumToString(TestTimeframe) + "\",\n"); - FileWriteString(file_handle, " \"ea_version\": \"" + m_overall_results.ea_version + "\",\n"); - FileWriteString(file_handle, " \"total_execution_time\": " + DoubleToString(m_overall_results.total_execution_time, 2) + ",\n"); - FileWriteString(file_handle, " \"overall_success_rate\": " + DoubleToString(m_overall_results.success_rate, 1) + ",\n"); - FileWriteString(file_handle, " \"test_suites\": [\n"); - - for(int i = 0; i < ArraySize(m_test_suites); i++) - { - if(!m_test_suites[i].enabled) - continue; - - FileWriteString(file_handle, " {\n"); - FileWriteString(file_handle, " \"name\": \"" + m_test_suites[i].name + "\",\n"); - FileWriteString(file_handle, " \"passed\": " + (m_test_suites[i].passed ? "true" : "false") + ",\n"); - FileWriteString(file_handle, " \"total_tests\": " + IntegerToString(m_test_suites[i].total_tests) + ",\n"); - FileWriteString(file_handle, " \"passed_tests\": " + IntegerToString(m_test_suites[i].passed_tests) + ",\n"); - FileWriteString(file_handle, " \"execution_time\": " + DoubleToString(m_test_suites[i].execution_time_seconds, 2) + "\n"); - FileWriteString(file_handle, " }"); - - if(i < ArraySize(m_test_suites) - 1) - FileWriteString(file_handle, ","); - - FileWriteString(file_handle, "\n"); - } - - FileWriteString(file_handle, " ]\n"); - FileWriteString(file_handle, "}\n"); - - FileClose(file_handle); - Print("JSON report generated: ", filename); - } -} - -//+------------------------------------------------------------------+ -//| Create Test Suite | -//+------------------------------------------------------------------+ -STestSuite CTestRunner::CreateTestSuite(string name, string description, bool enabled) -{ - STestSuite suite; - suite.name = name; - suite.description = description; - suite.enabled = enabled; - suite.completed = false; - suite.passed = false; - suite.start_time = 0; - suite.end_time = 0; - suite.execution_time_seconds = 0.0; - suite.total_tests = 0; - suite.passed_tests = 0; - suite.failed_tests = 0; - suite.error_message = ""; - suite.report_file = ""; - - return suite; -} - -//+------------------------------------------------------------------+ -//| Update Test Suite | -//+------------------------------------------------------------------+ -void CTestRunner::UpdateTestSuite(int index, bool passed, int total_tests, int passed_tests, string error = "") -{ - if(index >= 0 && index < ArraySize(m_test_suites)) - { - m_test_suites[index].passed = passed; - m_test_suites[index].total_tests = total_tests; - m_test_suites[index].passed_tests = passed_tests; - m_test_suites[index].failed_tests = total_tests - passed_tests; - m_test_suites[index].error_message = error; - } -} - -//+------------------------------------------------------------------+ -//| Get Environment Info | -//+------------------------------------------------------------------+ -string CTestRunner::GetEnvironmentInfo() -{ - string info = ""; - info += "Terminal: " + TerminalInfoString(TERMINAL_NAME) + " " + TerminalInfoString(TERMINAL_BUILD) + "\n"; - info += "Company: " + TerminalInfoString(TERMINAL_COMPANY) + "\n"; - info += "Path: " + TerminalInfoString(TERMINAL_PATH) + "\n"; - info += "Data Path: " + TerminalInfoString(TERMINAL_DATA_PATH) + "\n"; - info += "Common Path: " + TerminalInfoString(TERMINAL_COMMONDATA_PATH) + "\n"; - info += "Language: " + TerminalInfoString(TERMINAL_LANGUAGE) + "\n"; - info += "CPU Cores: " + IntegerToString(TerminalInfoInteger(TERMINAL_CPU_CORES)) + "\n"; - info += "Memory (Physical): " + IntegerToString(TerminalInfoInteger(TERMINAL_MEMORY_PHYSICAL)) + " MB\n"; - info += "Memory (Total): " + IntegerToString(TerminalInfoInteger(TERMINAL_MEMORY_TOTAL)) + " MB\n"; - info += "Memory (Available): " + IntegerToString(TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE)) + " MB\n"; - info += "Memory (Used): " + IntegerToString(TerminalInfoInteger(TERMINAL_MEMORY_USED)) + " MB\n"; - - return info; -} - -//+------------------------------------------------------------------+ -//| Get EA Version | -//+------------------------------------------------------------------+ -string CTestRunner::GetEAVersion() -{ - return "1.00"; // This should be dynamically retrieved from the EA -} - -//+------------------------------------------------------------------+ -//| Validate Test Environment | -//+------------------------------------------------------------------+ -bool CTestRunner::ValidateTestEnvironment() -{ - // Check if symbol is available - if(!SymbolSelect(TestSymbol, true)) - { - Print("โ Symbol ", TestSymbol, " is not available"); - return false; - } - - // Check if we have enough historical data - int bars = Bars(TestSymbol, TestTimeframe); - if(bars < 1000) - { - Print("โ ๏ธ Limited historical data available: ", bars, " bars"); - } - - // Check memory availability - int available_memory = TerminalInfoInteger(TERMINAL_MEMORY_AVAILABLE); - if(available_memory < 100) // Less than 100 MB - { - Print("โ ๏ธ Low memory available: ", available_memory, " MB"); - } - - return true; -} - -//+------------------------------------------------------------------+ -//| Create Reports Directory | -//+------------------------------------------------------------------+ -bool CTestRunner::CreateReportsDirectory() -{ - // MT5 doesn't have direct directory creation, but we can try to create a file - // to ensure the directory structure exists - string test_file = m_reports_directory + "test.txt"; - int handle = FileOpen(test_file, FILE_WRITE | FILE_TXT); - - if(handle != INVALID_HANDLE) - { - FileClose(handle); - FileDelete(test_file); - return true; - } - - return false; -} - -//+------------------------------------------------------------------+ -//| Get Report Filename | -//+------------------------------------------------------------------+ -string CTestRunner::GetReportFilename(string test_name, string extension) -{ - return m_reports_directory + test_name + "_" + m_session_id + "." + extension; -} - -//+------------------------------------------------------------------+ -//| Cleanup Old Reports | -//+------------------------------------------------------------------+ -bool CTestRunner::CleanupOldReports() -{ - // This would implement cleanup logic to keep only the last N reports - // MT5 file system access is limited, so this is a simplified version - return true; -} - -//+------------------------------------------------------------------+ -//| Send Email Report | -//+------------------------------------------------------------------+ -void CTestRunner::SendEmailReport() -{ - if(EmailAddress == "") - return; - - string subject = "MT5 Sniper EA Test Report - " + m_session_id; - string body = "Test session completed.\n\n"; - body += "Summary:\n"; - body += "- Test Suites: " + IntegerToString(m_overall_results.passed_test_suites) + "/" + IntegerToString(m_overall_results.total_test_suites) + " passed\n"; - body += "- Individual Tests: " + IntegerToString(m_overall_results.passed_individual_tests) + "/" + IntegerToString(m_overall_results.total_individual_tests) + " passed\n"; - body += "- Success Rate: " + DoubleToString(m_overall_results.success_rate, 1) + "%\n"; - body += "- Execution Time: " + DoubleToString(m_overall_results.total_execution_time, 2) + " seconds\n\n"; - body += "Please check the detailed reports for more information."; - - bool email_sent = SendMail(subject, body); - - if(email_sent) - { - Print("๐ง Email report sent to: ", EmailAddress); - } - else - { - Print("โ Failed to send email report"); - } -} - -//+------------------------------------------------------------------+ -//| Script start function | -//+------------------------------------------------------------------+ -void OnStart() -{ - Print("๐ Starting MT5 Sniper EA Comprehensive Test Suite"); - Print("This will run all enabled test suites and generate detailed reports."); - Print(""); - - CTestRunner* test_runner = new CTestRunner(); - - bool all_tests_passed = test_runner.RunAllTests(); - - Print(""); - if(all_tests_passed) - { - Print("๐ ALL TEST SUITES COMPLETED SUCCESSFULLY!"); - Print("The MT5 Sniper EA has passed comprehensive testing and is ready for deployment."); - } - else - { - Print("โ ๏ธ SOME TESTS FAILED!"); - Print("Please review the detailed reports and fix any issues before deployment."); - } - - delete test_runner; - - Print(""); - Print("Test execution completed. Check the Reports directory for detailed results."); -} \ No newline at end of file diff --git a/src/Tests/ValidationTest.mq5 b/src/Tests/ValidationTest.mq5 deleted file mode 100644 index 2971ffd..0000000 --- a/src/Tests/ValidationTest.mq5 +++ /dev/null @@ -1,1392 +0,0 @@ -//+------------------------------------------------------------------+ -//| ValidationTest.mq5 | -//| MT5 Sniper EA - Validation | -//| | -//+------------------------------------------------------------------+ -#property copyright "MT5 Sniper EA" -#property version "1.00" -#property description "Validation tests for MT5 Sniper EA trading logic" -#property script_show_inputs - -// Include all EA components -#include "../Include/MarketStructure/OrderBlockDetector.mqh" -#include "../Include/MarketStructure/BOSDetector.mqh" -#include "../Include/MarketStructure/LiquiditySweepDetector.mqh" -#include "../Include/MarketStructure/FVGDetector.mqh" -#include "../Include/MarketStructure/EntryStrategy.mqh" -#include "../Include/RiskManagement/RiskManager.mqh" -#include "../Include/SessionManagement/SessionManager.mqh" -#include "../Include/AIIntegration/GrokAI.mqh" -#include "../Include/Visualization/ChartManager.mqh" -#include "../Include/Utils/Backtester.mqh" - -// Input parameters -input bool ValidateMarketStructure = true; // Validate market structure detection -input bool ValidateRiskCalculations = true; // Validate risk management calculations -input bool ValidateSessionLogic = true; // Validate session management logic -input bool ValidateEntrySignals = true; // Validate entry signal generation -input bool ValidateBacktestAccuracy = true; // Validate backtest calculations -input bool GenerateValidationReport = true; // Generate validation report - -//+------------------------------------------------------------------+ -//| Validation Result Structure | -//+------------------------------------------------------------------+ -struct SValidationResult -{ - string test_name; - bool passed; - string expected_result; - string actual_result; - double accuracy_percentage; - string error_message; -}; - -//+------------------------------------------------------------------+ -//| Test Data Structure | -//+------------------------------------------------------------------+ -struct STestCandle -{ - datetime time; - double open; - double high; - double low; - double close; - long volume; -}; - -//+------------------------------------------------------------------+ -//| Validation Test Class | -//+------------------------------------------------------------------+ -class CValidationTest -{ -private: - // Test components - COrderBlockDetector* m_ob_detector; - CBOSDetector* m_bos_detector; - CLiquiditySweepDetector* m_ls_detector; - CFVGDetector* m_fvg_detector; - CEntryStrategy* m_entry_strategy; - CRiskManager* m_risk_manager; - CSessionManager* m_session_manager; - CGrokAI* m_grok_ai; - CChartManager* m_chart_manager; - CBacktester* m_backtester; - - // Validation results - SValidationResult m_results[]; - - // Test data - STestCandle m_test_data[]; - -public: - CValidationTest(); - ~CValidationTest(); - - // Main validation functions - bool RunValidationTests(); - void GenerateValidationReport(); - - // Market structure validation - bool ValidateOrderBlockDetection(); - bool ValidateBOSDetection(); - bool ValidateLiquiditySweepDetection(); - bool ValidateFVGDetection(); - - // Risk management validation - bool ValidatePositionSizing(); - bool ValidateStopLossCalculation(); - bool ValidateTakeProfitCalculation(); - bool ValidateRiskRewardRatio(); - bool ValidateDrawdownLimits(); - - // Session management validation - bool ValidateSessionDetection(); - bool ValidateSessionOverlaps(); - bool ValidateNewsAvoidance(); - bool ValidateVolatilityCalculation(); - - // Entry signal validation - bool ValidateConfluenceScoring(); - bool ValidateSignalTiming(); - bool ValidateSignalAccuracy(); - bool ValidateSignalFiltering(); - - // Backtest validation - bool ValidateTradeExecution(); - bool ValidateStatisticsCalculation(); - bool ValidateProfitLossCalculation(); - bool ValidateSlippageHandling(); - - // Utility functions - void AddValidationResult(string name, bool passed, string expected, string actual, - double accuracy, string error); - void GenerateTestData(); - void CreateKnownPatterns(); - double CalculateAccuracy(double expected, double actual, double tolerance); - void PrintValidationResults(); -}; - -//+------------------------------------------------------------------+ -//| Constructor | -//+------------------------------------------------------------------+ -CValidationTest::CValidationTest() -{ - // Initialize components - m_ob_detector = new COrderBlockDetector(); - m_bos_detector = new CBOSDetector(); - m_ls_detector = new CLiquiditySweepDetector(); - m_fvg_detector = new CFVGDetector(); - m_entry_strategy = new CEntryStrategy(); - m_risk_manager = new CRiskManager(); - m_session_manager = new CSessionManager(); - m_grok_ai = new CGrokAI(); - m_chart_manager = new CChartManager(); - m_backtester = new CBacktester(); - - GenerateTestData(); -} - -//+------------------------------------------------------------------+ -//| Destructor | -//+------------------------------------------------------------------+ -CValidationTest::~CValidationTest() -{ - delete m_ob_detector; - delete m_bos_detector; - delete m_ls_detector; - delete m_fvg_detector; - delete m_entry_strategy; - delete m_risk_manager; - delete m_session_manager; - delete m_grok_ai; - delete m_chart_manager; - delete m_backtester; -} - -//+------------------------------------------------------------------+ -//| Run Validation Tests | -//+------------------------------------------------------------------+ -bool CValidationTest::RunValidationTests() -{ - Print("=== Starting MT5 Sniper EA Validation Tests ==="); - Print(""); - - // Initialize components - m_ob_detector.Initialize("EURUSD", PERIOD_H1); - m_bos_detector.Initialize("EURUSD", PERIOD_H1); - m_ls_detector.Initialize("EURUSD", PERIOD_H1); - m_fvg_detector.Initialize("EURUSD", PERIOD_H1); - m_entry_strategy.Initialize("EURUSD", PERIOD_H1); - m_risk_manager.Initialize(); - m_session_manager.Initialize(); - m_grok_ai.Initialize("test_key", "test_url"); - m_chart_manager.Initialize(ChartID()); - m_backtester.Initialize(); - - bool all_passed = true; - - // Market structure validation - if(ValidateMarketStructure) - { - Print("Validating Market Structure Detection..."); - if(!ValidateOrderBlockDetection()) all_passed = false; - if(!ValidateBOSDetection()) all_passed = false; - if(!ValidateLiquiditySweepDetection()) all_passed = false; - if(!ValidateFVGDetection()) all_passed = false; - } - - // Risk management validation - if(ValidateRiskCalculations) - { - Print("Validating Risk Management Calculations..."); - if(!ValidatePositionSizing()) all_passed = false; - if(!ValidateStopLossCalculation()) all_passed = false; - if(!ValidateTakeProfitCalculation()) all_passed = false; - if(!ValidateRiskRewardRatio()) all_passed = false; - if(!ValidateDrawdownLimits()) all_passed = false; - } - - // Session management validation - if(ValidateSessionLogic) - { - Print("Validating Session Management Logic..."); - if(!ValidateSessionDetection()) all_passed = false; - if(!ValidateSessionOverlaps()) all_passed = false; - if(!ValidateNewsAvoidance()) all_passed = false; - if(!ValidateVolatilityCalculation()) all_passed = false; - } - - // Entry signal validation - if(ValidateEntrySignals) - { - Print("Validating Entry Signal Generation..."); - if(!ValidateConfluenceScoring()) all_passed = false; - if(!ValidateSignalTiming()) all_passed = false; - if(!ValidateSignalAccuracy()) all_passed = false; - if(!ValidateSignalFiltering()) all_passed = false; - } - - // Backtest validation - if(ValidateBacktestAccuracy) - { - Print("Validating Backtest Accuracy..."); - if(!ValidateTradeExecution()) all_passed = false; - if(!ValidateStatisticsCalculation()) all_passed = false; - if(!ValidateProfitLossCalculation()) all_passed = false; - if(!ValidateSlippageHandling()) all_passed = false; - } - - // Generate report - if(GenerateValidationReport) - GenerateValidationReport(); - - return all_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Order Block Detection | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateOrderBlockDetection() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Test with known order block pattern - CreateKnownPatterns(); - - SOrderBlock blocks[]; - int count = m_ob_detector.DetectOrderBlocks(blocks); - - // Expected: At least 1 order block should be detected in test data - string expected = "At least 1 order block detected"; - string actual = IntegerToString(count) + " order blocks detected"; - - if(count < 1) - { - test_passed = false; - error_msg = "No order blocks detected in test data with known patterns"; - } - - // Validate order block properties - if(test_passed && count > 0) - { - SOrderBlock& block = blocks[0]; - - // Check if order block has valid price levels - if(block.high <= block.low) - { - test_passed = false; - error_msg = "Invalid order block price levels: high <= low"; - } - - // Check if strength is within valid range - if(block.strength < 0 || block.strength > 1) - { - test_passed = false; - error_msg = "Invalid order block strength: " + DoubleToString(block.strength, 2); - } - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Order Block Detection", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Order Block Detection", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate BOS Detection | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateBOSDetection() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - SBOS signals[]; - int count = m_bos_detector.DetectBOS(signals); - - string expected = "Valid BOS signals"; - string actual = IntegerToString(count) + " BOS signals detected"; - - // Validate BOS signal properties if any detected - if(count > 0) - { - SBOS& signal = signals[0]; - - // Check if BOS has valid direction - if(signal.direction != BOS_BULLISH && signal.direction != BOS_BEARISH) - { - test_passed = false; - error_msg = "Invalid BOS direction"; - } - - // Check if strength is valid - if(signal.strength < 0 || signal.strength > 1) - { - test_passed = false; - error_msg = "Invalid BOS strength: " + DoubleToString(signal.strength, 2); - } - - // Check if break level is valid - if(signal.break_level <= 0) - { - test_passed = false; - error_msg = "Invalid BOS break level"; - } - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("BOS Detection", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("BOS Detection", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Liquidity Sweep Detection | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateLiquiditySweepDetection() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - SLiquiditySweep sweeps[]; - int count = m_ls_detector.DetectSweeps(sweeps); - - string expected = "Valid liquidity sweeps"; - string actual = IntegerToString(count) + " sweeps detected"; - - // Validate sweep properties if any detected - if(count > 0) - { - SLiquiditySweep& sweep = sweeps[0]; - - // Check if sweep has valid direction - if(sweep.direction != SWEEP_BULLISH && sweep.direction != SWEEP_BEARISH) - { - test_passed = false; - error_msg = "Invalid sweep direction"; - } - - // Check if sweep distance is positive - if(sweep.sweep_distance <= 0) - { - test_passed = false; - error_msg = "Invalid sweep distance: " + DoubleToString(sweep.sweep_distance, 5); - } - - // Check if liquidity level is valid - if(sweep.liquidity_level <= 0) - { - test_passed = false; - error_msg = "Invalid liquidity level"; - } - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Liquidity Sweep Detection", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Liquidity Sweep Detection", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate FVG Detection | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateFVGDetection() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - SFVG gaps[]; - int count = m_fvg_detector.DetectFVG(gaps); - - string expected = "Valid FVG gaps"; - string actual = IntegerToString(count) + " gaps detected"; - - // Validate FVG properties if any detected - if(count > 0) - { - SFVG& gap = gaps[0]; - - // Check if gap has valid price levels - if(gap.high <= gap.low) - { - test_passed = false; - error_msg = "Invalid FVG price levels: high <= low"; - } - - // Check if gap type is valid - if(gap.type != FVG_BULLISH && gap.type != FVG_BEARISH) - { - test_passed = false; - error_msg = "Invalid FVG type"; - } - - // Check if gap size is positive - double gap_size = gap.high - gap.low; - if(gap_size <= 0) - { - test_passed = false; - error_msg = "Invalid FVG size: " + DoubleToString(gap_size, 5); - } - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("FVG Detection", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("FVG Detection", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Position Sizing | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidatePositionSizing() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Set known risk profile - SRiskProfile profile; - profile.risk_percent = 2.0; - profile.max_risk_percent = 5.0; - profile.daily_loss_limit = 3.0; - profile.max_drawdown_percent = 10.0; - profile.risk_model = RISK_MODEL_PERCENTAGE; - - m_risk_manager.SetRiskProfile(profile); - - // Test position sizing with known parameters - double account_balance = 10000.0; - double stop_loss_pips = 50.0; - double expected_risk_amount = account_balance * (profile.risk_percent / 100.0); // $200 - - double position_size = m_risk_manager.CalculatePositionSize("EURUSD", stop_loss_pips); - - // Calculate expected position size - double pip_value = 10.0; // $10 per pip for 1 lot EURUSD - double expected_position_size = expected_risk_amount / (stop_loss_pips * pip_value); - - string expected = DoubleToString(expected_position_size, 2) + " lots"; - string actual = DoubleToString(position_size, 2) + " lots"; - - // Allow 5% tolerance - double accuracy = CalculateAccuracy(expected_position_size, position_size, 0.05); - - if(accuracy < 95.0) - { - test_passed = false; - error_msg = "Position size calculation inaccurate"; - } - - // Validate position size is positive and reasonable - if(position_size <= 0 || position_size > 10.0) - { - test_passed = false; - error_msg = "Position size out of reasonable range: " + DoubleToString(position_size, 2); - } - - AddValidationResult("Position Sizing", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Position Sizing", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Stop Loss Calculation | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateStopLossCalculation() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - double entry_price = 1.1000; - double stop_loss_pips = 50.0; - - // Test buy order stop loss - double buy_sl = m_risk_manager.CalculateStopLoss("EURUSD", ORDER_TYPE_BUY, entry_price, stop_loss_pips); - double expected_buy_sl = entry_price - (stop_loss_pips * 0.0001); // 1.0950 - - string expected = DoubleToString(expected_buy_sl, 5); - string actual = DoubleToString(buy_sl, 5); - - double accuracy = CalculateAccuracy(expected_buy_sl, buy_sl, 0.001); - - if(accuracy < 99.0) - { - test_passed = false; - error_msg = "Buy stop loss calculation inaccurate"; - } - - // Test sell order stop loss - double sell_sl = m_risk_manager.CalculateStopLoss("EURUSD", ORDER_TYPE_SELL, entry_price, stop_loss_pips); - double expected_sell_sl = entry_price + (stop_loss_pips * 0.0001); // 1.1050 - - if(MathAbs(sell_sl - expected_sell_sl) > 0.0001) - { - test_passed = false; - error_msg = "Sell stop loss calculation inaccurate"; - } - - AddValidationResult("Stop Loss Calculation", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Stop Loss Calculation", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Take Profit Calculation | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateTakeProfitCalculation() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - double entry_price = 1.1000; - double take_profit_pips = 100.0; - - // Test buy order take profit - double buy_tp = m_risk_manager.CalculateTakeProfit("EURUSD", ORDER_TYPE_BUY, entry_price, take_profit_pips); - double expected_buy_tp = entry_price + (take_profit_pips * 0.0001); // 1.1100 - - string expected = DoubleToString(expected_buy_tp, 5); - string actual = DoubleToString(buy_tp, 5); - - double accuracy = CalculateAccuracy(expected_buy_tp, buy_tp, 0.001); - - if(accuracy < 99.0) - { - test_passed = false; - error_msg = "Buy take profit calculation inaccurate"; - } - - // Test sell order take profit - double sell_tp = m_risk_manager.CalculateTakeProfit("EURUSD", ORDER_TYPE_SELL, entry_price, take_profit_pips); - double expected_sell_tp = entry_price - (take_profit_pips * 0.0001); // 1.0900 - - if(MathAbs(sell_tp - expected_sell_tp) > 0.0001) - { - test_passed = false; - error_msg = "Sell take profit calculation inaccurate"; - } - - AddValidationResult("Take Profit Calculation", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Take Profit Calculation", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Risk Reward Ratio | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateRiskRewardRatio() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - double entry_price = 1.1000; - double stop_loss = 1.0950; // 50 pips risk - double take_profit = 1.1100; // 100 pips reward - - double risk_reward = m_risk_manager.CalculateRiskRewardRatio(entry_price, stop_loss, take_profit, ORDER_TYPE_BUY); - double expected_rr = 2.0; // 100 pips reward / 50 pips risk - - string expected = DoubleToString(expected_rr, 2); - string actual = DoubleToString(risk_reward, 2); - - double accuracy = CalculateAccuracy(expected_rr, risk_reward, 0.01); - - if(accuracy < 95.0) - { - test_passed = false; - error_msg = "Risk reward ratio calculation inaccurate"; - } - - AddValidationResult("Risk Reward Ratio", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Risk Reward Ratio", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Drawdown Limits | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateDrawdownLimits() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Set drawdown limit - SRiskProfile profile; - profile.max_drawdown_percent = 10.0; - m_risk_manager.SetRiskProfile(profile); - - // Simulate account with drawdown - double initial_balance = 10000.0; - double current_balance = 9000.0; // 10% drawdown - double drawdown_percent = ((initial_balance - current_balance) / initial_balance) * 100.0; - - bool should_stop_trading = m_risk_manager.IsDrawdownLimitReached(initial_balance, current_balance); - - string expected = "Trading should be stopped"; - string actual = should_stop_trading ? "Trading stopped" : "Trading allowed"; - - if(!should_stop_trading) - { - test_passed = false; - error_msg = "Drawdown limit not enforced at " + DoubleToString(drawdown_percent, 2) + "%"; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Drawdown Limits", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Drawdown Limits", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Session Detection | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateSessionDetection() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Test session detection at known times - datetime london_time = StringToTime("2024.01.15 08:00"); // London session - datetime ny_time = StringToTime("2024.01.15 14:00"); // NY session - datetime asia_time = StringToTime("2024.01.15 23:00"); // Asia session - - // Note: This is a simplified test - actual implementation would need to handle timezone conversions - ENUM_TRADING_SESSION current_session = m_session_manager.GetCurrentSession(); - - string expected = "Valid session detected"; - string actual = EnumToString(current_session); - - // Validate session is within valid range - if(current_session < SESSION_ASIA || current_session > SESSION_OVERLAP_ALL) - { - test_passed = false; - error_msg = "Invalid session detected: " + actual; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Session Detection", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Session Detection", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Session Overlaps | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateSessionOverlaps() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Test overlap detection - bool is_overlap = m_session_manager.IsSessionOverlap(); - ENUM_TRADING_SESSION current_session = m_session_manager.GetCurrentSession(); - - string expected = "Consistent overlap detection"; - string actual = is_overlap ? "Overlap detected" : "No overlap"; - - // Validate overlap consistency - if(is_overlap && (current_session < SESSION_OVERLAP_LONDON_NY)) - { - test_passed = false; - error_msg = "Overlap detected but session doesn't indicate overlap"; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Session Overlaps", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Session Overlaps", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate News Avoidance | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateNewsAvoidance() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Configure news avoidance - SSessionConfig config; - config.avoid_news_minutes = 30; - m_session_manager.Configure(config); - - // Test trading permission during news - bool trading_allowed = m_session_manager.IsTradingAllowed(); - - string expected = "News avoidance working"; - string actual = trading_allowed ? "Trading allowed" : "Trading blocked"; - - // This test is simplified - actual implementation would need news calendar integration - double accuracy = 100.0; // Assume working if no exception - AddValidationResult("News Avoidance", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("News Avoidance", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Volatility Calculation | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateVolatilityCalculation() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - SSessionStats stats; - m_session_manager.GetSessionStatistics(SESSION_LONDON, stats); - - string expected = "Valid volatility value"; - string actual = DoubleToString(stats.volatility, 4); - - // Validate volatility is within reasonable range - if(stats.volatility < 0 || stats.volatility > 10.0) - { - test_passed = false; - error_msg = "Volatility out of reasonable range: " + actual; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Volatility Calculation", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Volatility Calculation", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Confluence Scoring | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateConfluenceScoring() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Configure entry strategy - m_entry_strategy.ConfigureOrderBlockDetector(m_ob_detector); - m_entry_strategy.ConfigureBOSDetector(m_bos_detector); - m_entry_strategy.ConfigureLiquiditySweepDetector(m_ls_detector); - m_entry_strategy.ConfigureFVGDetector(m_fvg_detector); - - SEntrySignal signal; - bool has_signal = m_entry_strategy.AnalyzeEntry(signal); - - string expected = "Valid confluence score (0-1)"; - string actual = DoubleToString(signal.confidence, 2); - - // Validate confluence score is within valid range - if(signal.confidence < 0 || signal.confidence > 1) - { - test_passed = false; - error_msg = "Confluence score out of range: " + actual; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Confluence Scoring", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Confluence Scoring", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Signal Timing | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateSignalTiming() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - SEntrySignal signal; - bool has_signal = m_entry_strategy.AnalyzeEntry(signal); - - string expected = "Valid signal timestamp"; - string actual = TimeToString(signal.timestamp); - - // Validate signal timestamp is recent - datetime current_time = TimeCurrent(); - if(has_signal && (current_time - signal.timestamp) > 3600) // More than 1 hour old - { - test_passed = false; - error_msg = "Signal timestamp too old"; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Signal Timing", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Signal Timing", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Signal Accuracy | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateSignalAccuracy() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // This would require historical data and known outcomes - // For now, we'll validate signal structure - SEntrySignal signal; - bool has_signal = m_entry_strategy.AnalyzeEntry(signal); - - string expected = "Valid signal structure"; - string actual = has_signal ? "Signal generated" : "No signal"; - - if(has_signal) - { - // Validate signal properties - if(signal.entry_price <= 0) - { - test_passed = false; - error_msg = "Invalid entry price"; - } - - if(signal.direction != SIGNAL_BUY && signal.direction != SIGNAL_SELL) - { - test_passed = false; - error_msg = "Invalid signal direction"; - } - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Signal Accuracy", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Signal Accuracy", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Signal Filtering | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateSignalFiltering() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Set high confidence threshold - SEntryRequirements requirements; - requirements.min_confluence_score = 0.9; // Very high threshold - m_entry_strategy.SetRequirements(requirements); - - SEntrySignal signal; - bool has_signal = m_entry_strategy.AnalyzeEntry(signal); - - string expected = "Proper signal filtering"; - string actual = has_signal ? "Signal passed filter" : "Signal filtered out"; - - // If signal passes, it should meet the high threshold - if(has_signal && signal.confidence < 0.9) - { - test_passed = false; - error_msg = "Signal passed filter but doesn't meet threshold"; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Signal Filtering", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Signal Filtering", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Trade Execution | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateTradeExecution() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Configure backtester - m_backtester.SetEntryStrategy(m_entry_strategy); - m_backtester.SetRiskManager(m_risk_manager); - - SBacktestConfig config; - config.start_date = D'2023.01.01'; - config.end_date = D'2023.01.31'; - config.initial_balance = 10000.0; - config.spread = 1.5; - config.commission = 7.0; - - m_backtester.Configure(config); - - // Test trade execution logic - STradeData trade; - trade.entry_price = 1.1000; - trade.stop_loss = 1.0950; - trade.take_profit = 1.1100; - trade.lot_size = 0.1; - trade.direction = ORDER_TYPE_BUY; - - bool execution_result = m_backtester.ProcessTrade(trade); - - string expected = "Successful trade processing"; - string actual = execution_result ? "Trade processed" : "Trade failed"; - - if(!execution_result) - { - test_passed = false; - error_msg = "Trade execution failed"; - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Trade Execution", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Trade Execution", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Statistics Calculation | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateStatisticsCalculation() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - SBacktestStats stats; - m_backtester.CalculateStatistics(stats); - - string expected = "Valid statistics"; - string actual = "Win rate: " + DoubleToString(stats.win_rate * 100, 2) + "%"; - - // Validate statistics are within reasonable ranges - if(stats.win_rate < 0 || stats.win_rate > 1) - { - test_passed = false; - error_msg = "Invalid win rate: " + DoubleToString(stats.win_rate, 2); - } - - if(stats.total_trades < 0) - { - test_passed = false; - error_msg = "Invalid total trades: " + IntegerToString(stats.total_trades); - } - - if(stats.profit_factor < 0) - { - test_passed = false; - error_msg = "Invalid profit factor: " + DoubleToString(stats.profit_factor, 2); - } - - double accuracy = test_passed ? 100.0 : 0.0; - AddValidationResult("Statistics Calculation", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Statistics Calculation", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Profit Loss Calculation | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateProfitLossCalculation() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Test P&L calculation with known values - double entry_price = 1.1000; - double exit_price = 1.1100; // 100 pips profit - double lot_size = 0.1; - - double expected_profit = 100.0; // 100 pips * $1 per pip for 0.1 lot - double actual_profit = m_backtester.CalculateProfitLoss(entry_price, exit_price, lot_size, ORDER_TYPE_BUY, "EURUSD"); - - string expected = DoubleToString(expected_profit, 2); - string actual = DoubleToString(actual_profit, 2); - - double accuracy = CalculateAccuracy(expected_profit, actual_profit, 0.01); - - if(accuracy < 95.0) - { - test_passed = false; - error_msg = "P&L calculation inaccurate"; - } - - AddValidationResult("Profit Loss Calculation", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Profit Loss Calculation", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Validate Slippage Handling | -//+------------------------------------------------------------------+ -bool CValidationTest::ValidateSlippageHandling() -{ - bool test_passed = true; - string error_msg = ""; - - try - { - // Test slippage application - double requested_price = 1.1000; - double slippage_pips = 2.0; - - double actual_price = m_backtester.ApplySlippage(requested_price, slippage_pips, ORDER_TYPE_BUY); - double expected_price = requested_price + (slippage_pips * 0.0001); // Worse fill for buy - - string expected = DoubleToString(expected_price, 5); - string actual = DoubleToString(actual_price, 5); - - double accuracy = CalculateAccuracy(expected_price, actual_price, 0.0001); - - if(accuracy < 99.0) - { - test_passed = false; - error_msg = "Slippage calculation inaccurate"; - } - - AddValidationResult("Slippage Handling", test_passed, expected, actual, accuracy, error_msg); - } - catch(...) - { - test_passed = false; - AddValidationResult("Slippage Handling", false, "No exception", "Exception occurred", 0.0, "Unexpected exception"); - } - - return test_passed; -} - -//+------------------------------------------------------------------+ -//| Add Validation Result | -//+------------------------------------------------------------------+ -void CValidationTest::AddValidationResult(string name, bool passed, string expected, string actual, - double accuracy, string error) -{ - int size = ArraySize(m_results); - ArrayResize(m_results, size + 1); - - m_results[size].test_name = name; - m_results[size].passed = passed; - m_results[size].expected_result = expected; - m_results[size].actual_result = actual; - m_results[size].accuracy_percentage = accuracy; - m_results[size].error_message = error; -} - -//+------------------------------------------------------------------+ -//| Generate Test Data | -//+------------------------------------------------------------------+ -void CValidationTest::GenerateTestData() -{ - // Generate realistic test data - int data_points = 1000; - ArrayResize(m_test_data, data_points); - - double base_price = 1.1000; - datetime base_time = TimeCurrent() - (data_points * 3600); - - for(int i = 0; i < data_points; i++) - { - m_test_data[i].time = base_time + (i * 3600); - m_test_data[i].open = base_price; - - // Generate realistic OHLC data - double volatility = 0.002; // 0.2% volatility - double high_offset = (MathRand() / 32767.0) * volatility; - double low_offset = (MathRand() / 32767.0) * volatility; - double close_offset = (MathRand() / 32767.0 - 0.5) * volatility; - - m_test_data[i].high = base_price + high_offset; - m_test_data[i].low = base_price - low_offset; - m_test_data[i].close = base_price + close_offset; - m_test_data[i].volume = 1000 + (long)(MathRand() / 32767.0 * 5000); - - base_price = m_test_data[i].close; - } -} - -//+------------------------------------------------------------------+ -//| Create Known Patterns | -//+------------------------------------------------------------------+ -void CValidationTest::CreateKnownPatterns() -{ - // This would create specific market patterns for testing - // For now, we'll use the generated test data - Print("Using generated test data with known patterns"); -} - -//+------------------------------------------------------------------+ -//| Calculate Accuracy | -//+------------------------------------------------------------------+ -double CValidationTest::CalculateAccuracy(double expected, double actual, double tolerance) -{ - if(expected == 0) - return (actual == 0) ? 100.0 : 0.0; - - double error = MathAbs(expected - actual) / MathAbs(expected); - return MathMax(0.0, (1.0 - error / tolerance) * 100.0); -} - -//+------------------------------------------------------------------+ -//| Generate Validation Report | -//+------------------------------------------------------------------+ -void CValidationTest::GenerateValidationReport() -{ - Print(""); - Print("=== MT5 Sniper EA Validation Report ==="); - Print(""); - - int passed_count = 0; - int total_count = ArraySize(m_results); - double total_accuracy = 0.0; - - // Print detailed results - Print("Validation Test Results:"); - Print("------------------------"); - - for(int i = 0; i < ArraySize(m_results); i++) - { - SValidationResult& result = m_results[i]; - - Print(StringFormat("%-30s | %s | Accuracy: %6.2f%% | %s", - result.test_name, - result.passed ? "PASS" : "FAIL", - result.accuracy_percentage, - result.error_message)); - - if(result.passed) - passed_count++; - - total_accuracy += result.accuracy_percentage; - } - - Print(""); - Print("Summary:"); - Print("--------"); - Print("Total Tests: ", total_count); - Print("Passed: ", passed_count); - Print("Failed: ", total_count - passed_count); - Print("Success Rate: ", DoubleToString((double)passed_count / total_count * 100, 2), "%"); - Print("Average Accuracy: ", DoubleToString(total_accuracy / total_count, 2), "%"); - - // Save report to file - string filename = "SniperEA_ValidationReport_" + TimeToString(TimeCurrent(), TIME_DATE) + ".txt"; - int file_handle = FileOpen(filename, FILE_WRITE | FILE_TXT); - - if(file_handle != INVALID_HANDLE) - { - FileWrite(file_handle, "MT5 Sniper EA Validation Report"); - FileWrite(file_handle, "Generated: " + TimeToString(TimeCurrent())); - FileWrite(file_handle, ""); - FileWrite(file_handle, "Summary:"); - FileWrite(file_handle, "Total Tests: " + IntegerToString(total_count)); - FileWrite(file_handle, "Passed: " + IntegerToString(passed_count)); - FileWrite(file_handle, "Failed: " + IntegerToString(total_count - passed_count)); - FileWrite(file_handle, "Success Rate: " + DoubleToString((double)passed_count / total_count * 100, 2) + "%"); - FileWrite(file_handle, "Average Accuracy: " + DoubleToString(total_accuracy / total_count, 2) + "%"); - FileWrite(file_handle, ""); - FileWrite(file_handle, "Detailed Results:"); - - for(int i = 0; i < ArraySize(m_results); i++) - { - SValidationResult& result = m_results[i]; - FileWrite(file_handle, ""); - FileWrite(file_handle, "Test: " + result.test_name); - FileWrite(file_handle, "Status: " + (result.passed ? "PASS" : "FAIL")); - FileWrite(file_handle, "Expected: " + result.expected_result); - FileWrite(file_handle, "Actual: " + result.actual_result); - FileWrite(file_handle, "Accuracy: " + DoubleToString(result.accuracy_percentage, 2) + "%"); - if(StringLen(result.error_message) > 0) - FileWrite(file_handle, "Error: " + result.error_message); - } - - FileClose(file_handle); - Print("Validation report saved to: ", filename); - } - - if(passed_count == total_count) - { - Print("โ All validation tests passed!"); - } - else - { - Print("โ ๏ธ Some validation tests failed. Review results for issues."); - } -} - -//+------------------------------------------------------------------+ -//| Print Validation Results | -//+------------------------------------------------------------------+ -void CValidationTest::PrintValidationResults() -{ - GenerateValidationReport(); -} - -//+------------------------------------------------------------------+ -//| Script start function | -//+------------------------------------------------------------------+ -void OnStart() -{ - Print("Starting MT5 Sniper EA Validation Tests..."); - Print("This will validate the accuracy and correctness of trading logic."); - Print(""); - - CValidationTest* tester = new CValidationTest(); - - bool all_passed = tester.RunValidationTests(); - - if(all_passed) - { - Print(""); - Print("๐ฏ All validation tests passed! Trading logic is accurate."); - } - else - { - Print(""); - Print("โ ๏ธ Some validation tests failed. Please review and fix issues."); - } - - delete tester; - - Print("Validation testing completed."); -} \ No newline at end of file