mirror of
https://github.com/rithsila/MT5-EA-Sniper-Strategy.git
synced 2026-08-19 21:58:12 +00:00
feat: Complete MT5 EA Sniper Strategy implementation with comprehensive documentation
- Add complete MT5 Expert Advisor with institutional trading concepts - Implement Order Blocks (OB), Break of Structure (BOS), Liquidity Sweeps, and Fair Value Gaps (FVG) - Include AI integration with GrokAI for enhanced market analysis - Add comprehensive risk management and session management systems - Implement advanced optimization and backtesting frameworks - Include complete test suite with integration, performance, and validation tests - Add professional documentation with API docs, deployment guide, and user manual - Update README.md with industry-standard documentation and Mermaid architecture diagram - Add comprehensive .gitignore for MT5 development environment - Include system validation and test results reports Features: ✅ Multi-timeframe analysis (1M, 15M, H4) ✅ Institutional trading concepts implementation ✅ AI-powered market structure analysis ✅ Advanced risk management with Monte Carlo simulation ✅ Real-time news filtering and fundamental analysis ✅ Adaptive parameter optimization ✅ Comprehensive testing and validation framework ✅ Professional documentation and deployment guides
This commit is contained in:
@@ -0,0 +1,697 @@
|
||||
//+------------------------------------------------------------------+
|
||||
//| LiquiditySweep.mqh |
|
||||
//| Copyright 2024, MT5 Sniper Strategy Team |
|
||||
//| https://www.mql5.com |
|
||||
//+------------------------------------------------------------------+
|
||||
#property copyright "Copyright 2024, MT5 Sniper Strategy Team"
|
||||
#property link "https://www.mql5.com"
|
||||
|
||||
#include "../Utils/Logger.mqh"
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Liquidity Zone Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct SLiquidityZone {
|
||||
datetime time; // Time of zone formation
|
||||
double price; // Price level of liquidity
|
||||
bool isHigh; // True for high liquidity, false for low
|
||||
bool isSwept; // Has been swept
|
||||
bool isValid; // Is the zone still valid
|
||||
int strength; // Strength of liquidity (1-5)
|
||||
double volume; // Volume at formation
|
||||
int touchCount; // Number of times price touched this level
|
||||
string timeframe; // Timeframe where zone was detected
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Liquidity Sweep Structure |
|
||||
//+------------------------------------------------------------------+
|
||||
struct SLiquiditySweep {
|
||||
datetime time; // Time of sweep
|
||||
double sweepPrice; // Price where sweep occurred
|
||||
double reversalPrice; // Price where reversal started
|
||||
bool isBullishSweep; // True for bullish sweep (sweep lows then up)
|
||||
bool isValid; // Is the sweep still valid
|
||||
bool isConfirmed; // Has the sweep been confirmed with reversal
|
||||
int strength; // Strength of the sweep (1-5)
|
||||
double sweepDistance; // Distance of the sweep
|
||||
string timeframe; // Timeframe where sweep was detected
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Liquidity Sweep Detector Class |
|
||||
//+------------------------------------------------------------------+
|
||||
class CLiquiditySweepDetector {
|
||||
private:
|
||||
string m_symbol;
|
||||
ENUM_TIMEFRAMES m_timeframe;
|
||||
CLogger* m_logger;
|
||||
|
||||
SLiquidityZone m_liquidityZones[];
|
||||
SLiquiditySweep m_sweeps[];
|
||||
int m_maxZones;
|
||||
int m_maxSweeps;
|
||||
|
||||
// Detection parameters
|
||||
int m_lookbackPeriod;
|
||||
double m_minSweepDistance;
|
||||
int m_reversalBars;
|
||||
double m_liquidityThreshold;
|
||||
bool m_useVolumeFilter;
|
||||
double m_volumeMultiplier;
|
||||
|
||||
// Helper methods
|
||||
bool DetectLiquidityZones();
|
||||
bool IsLiquidityLevel(int index, bool checkHigh);
|
||||
bool CheckForSweep();
|
||||
bool IsBullishSweep(double sweepPrice, double currentPrice);
|
||||
bool IsBearishSweep(double sweepPrice, double currentPrice);
|
||||
int CalculateSweepStrength(const SLiquiditySweep &sweep);
|
||||
bool ConfirmSweep(SLiquiditySweep &sweep);
|
||||
void CleanupOldData();
|
||||
SLiquidityZone GetNearestLiquidityZone(double price, bool isHigh);
|
||||
|
||||
public:
|
||||
CLiquiditySweepDetector();
|
||||
~CLiquiditySweepDetector();
|
||||
|
||||
bool Initialize(string symbol, ENUM_TIMEFRAMES timeframe, CLogger* logger);
|
||||
void SetParameters(int lookback, double minSweepDistance, int reversalBars,
|
||||
double liquidityThreshold, bool useVolume, double volumeMultiplier);
|
||||
|
||||
bool DetectSweeps();
|
||||
int GetSweepCount();
|
||||
SLiquiditySweep GetSweep(int index);
|
||||
SLiquiditySweep GetLatestSweep(bool bullish);
|
||||
|
||||
bool IsRecentBullishSweep(int lookbackBars = 10);
|
||||
bool IsRecentBearishSweep(int lookbackBars = 10);
|
||||
bool HasValidSweep(bool checkBullish = true, bool checkBearish = true);
|
||||
|
||||
// Liquidity analysis
|
||||
double GetNearestLiquidityHigh();
|
||||
double GetNearestLiquidityLow();
|
||||
bool IsLiquidityZone(double price, double tolerance = 0.0001);
|
||||
int GetLiquidityZoneCount();
|
||||
|
||||
// Sweep validation
|
||||
bool IsSweepAndReverse(bool bullish);
|
||||
double GetSweepReversalLevel(bool bullish);
|
||||
|
||||
// Visualization
|
||||
void DrawLiquidityZones();
|
||||
void DrawSweeps();
|
||||
void RemoveLiquidityObjects();
|
||||
};
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Constructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CLiquiditySweepDetector::CLiquiditySweepDetector() {
|
||||
m_symbol = "";
|
||||
m_timeframe = PERIOD_CURRENT;
|
||||
m_logger = NULL;
|
||||
m_maxZones = 30;
|
||||
m_maxSweeps = 20;
|
||||
|
||||
// Default parameters
|
||||
m_lookbackPeriod = 20;
|
||||
m_minSweepDistance = 0.0001;
|
||||
m_reversalBars = 5;
|
||||
m_liquidityThreshold = 0.0005;
|
||||
m_useVolumeFilter = false;
|
||||
m_volumeMultiplier = 1.5;
|
||||
|
||||
ArrayResize(m_liquidityZones, m_maxZones);
|
||||
ArrayResize(m_sweeps, m_maxSweeps);
|
||||
ArrayInitialize(m_liquidityZones, 0);
|
||||
ArrayInitialize(m_sweeps, 0);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Destructor |
|
||||
//+------------------------------------------------------------------+
|
||||
CLiquiditySweepDetector::~CLiquiditySweepDetector() {
|
||||
RemoveLiquidityObjects();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Initialize detector |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::Initialize(string symbol, ENUM_TIMEFRAMES timeframe, CLogger* logger) {
|
||||
m_symbol = symbol;
|
||||
m_timeframe = timeframe;
|
||||
m_logger = logger;
|
||||
|
||||
if(m_logger != NULL) {
|
||||
m_logger->Info(StringFormat("Liquidity Sweep Detector initialized for %s on %s",
|
||||
m_symbol, EnumToString(m_timeframe)));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Set detection parameters |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLiquiditySweepDetector::SetParameters(int lookback, double minSweepDistance, int reversalBars,
|
||||
double liquidityThreshold, bool useVolume, double volumeMultiplier) {
|
||||
m_lookbackPeriod = lookback;
|
||||
m_minSweepDistance = minSweepDistance;
|
||||
m_reversalBars = reversalBars;
|
||||
m_liquidityThreshold = liquidityThreshold;
|
||||
m_useVolumeFilter = useVolume;
|
||||
m_volumeMultiplier = volumeMultiplier;
|
||||
|
||||
if(m_logger != NULL) {
|
||||
m_logger->Debug(StringFormat("Liquidity Parameters: Lookback=%d, MinSweep=%.5f, Reversal=%d",
|
||||
lookback, minSweepDistance, reversalBars));
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detect liquidity sweeps |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::DetectSweeps() {
|
||||
if(m_symbol == "" || m_timeframe == PERIOD_CURRENT) return false;
|
||||
|
||||
// First detect liquidity zones
|
||||
if(!DetectLiquidityZones()) return false;
|
||||
|
||||
// Clean up old data
|
||||
CleanupOldData();
|
||||
|
||||
// Check for new sweeps
|
||||
return CheckForSweep();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Detect liquidity zones |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::DetectLiquidityZones() {
|
||||
int bars = iBars(m_symbol, m_timeframe);
|
||||
if(bars < m_lookbackPeriod + 10) return false;
|
||||
|
||||
int zoneCount = 0;
|
||||
|
||||
// Clear existing zones
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
m_liquidityZones[i].isValid = false;
|
||||
}
|
||||
|
||||
// Detect liquidity levels (equal highs/lows, support/resistance)
|
||||
for(int i = 5; i < bars - 5 && zoneCount < m_maxZones; i++) {
|
||||
// Check for liquidity high
|
||||
if(IsLiquidityLevel(i, true)) {
|
||||
m_liquidityZones[zoneCount].time = iTime(m_symbol, m_timeframe, i);
|
||||
m_liquidityZones[zoneCount].price = iHigh(m_symbol, m_timeframe, i);
|
||||
m_liquidityZones[zoneCount].isHigh = true;
|
||||
m_liquidityZones[zoneCount].isSwept = false;
|
||||
m_liquidityZones[zoneCount].isValid = true;
|
||||
m_liquidityZones[zoneCount].volume = iVolume(m_symbol, m_timeframe, i);
|
||||
m_liquidityZones[zoneCount].touchCount = 1;
|
||||
m_liquidityZones[zoneCount].timeframe = EnumToString(m_timeframe);
|
||||
m_liquidityZones[zoneCount].strength = 1;
|
||||
zoneCount++;
|
||||
}
|
||||
// Check for liquidity low
|
||||
else if(IsLiquidityLevel(i, false)) {
|
||||
m_liquidityZones[zoneCount].time = iTime(m_symbol, m_timeframe, i);
|
||||
m_liquidityZones[zoneCount].price = iLow(m_symbol, m_timeframe, i);
|
||||
m_liquidityZones[zoneCount].isHigh = false;
|
||||
m_liquidityZones[zoneCount].isSwept = false;
|
||||
m_liquidityZones[zoneCount].isValid = true;
|
||||
m_liquidityZones[zoneCount].volume = iVolume(m_symbol, m_timeframe, i);
|
||||
m_liquidityZones[zoneCount].touchCount = 1;
|
||||
m_liquidityZones[zoneCount].timeframe = EnumToString(m_timeframe);
|
||||
m_liquidityZones[zoneCount].strength = 1;
|
||||
zoneCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate strength and touch count for each zone
|
||||
for(int i = 0; i < zoneCount; i++) {
|
||||
if(!m_liquidityZones[i].isValid) continue;
|
||||
|
||||
int touches = 0;
|
||||
double zonePrice = m_liquidityZones[i].price;
|
||||
bool isHigh = m_liquidityZones[i].isHigh;
|
||||
|
||||
// Count how many times price touched this level
|
||||
for(int j = 0; j < bars - 1; j++) {
|
||||
double high = iHigh(m_symbol, m_timeframe, j);
|
||||
double low = iLow(m_symbol, m_timeframe, j);
|
||||
|
||||
if(isHigh) {
|
||||
if(MathAbs(high - zonePrice) <= m_liquidityThreshold) touches++;
|
||||
} else {
|
||||
if(MathAbs(low - zonePrice) <= m_liquidityThreshold) touches++;
|
||||
}
|
||||
}
|
||||
|
||||
m_liquidityZones[i].touchCount = touches;
|
||||
m_liquidityZones[i].strength = MathMin(touches, 5);
|
||||
}
|
||||
|
||||
if(m_logger != NULL) {
|
||||
m_logger->Debug(StringFormat("Detected %d liquidity zones", zoneCount));
|
||||
}
|
||||
|
||||
return zoneCount > 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if level is a liquidity level |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::IsLiquidityLevel(int index, bool checkHigh) {
|
||||
if(index <= 2 || index >= iBars(m_symbol, m_timeframe) - 2) return false;
|
||||
|
||||
double currentPrice = checkHigh ? iHigh(m_symbol, m_timeframe, index) : iLow(m_symbol, m_timeframe, index);
|
||||
int matches = 0;
|
||||
|
||||
// Look for equal highs/lows within the lookback period
|
||||
for(int i = index - m_lookbackPeriod; i <= index + m_lookbackPeriod; i++) {
|
||||
if(i == index || i < 0 || i >= iBars(m_symbol, m_timeframe)) continue;
|
||||
|
||||
double comparePrice = checkHigh ? iHigh(m_symbol, m_timeframe, i) : iLow(m_symbol, m_timeframe, i);
|
||||
|
||||
if(MathAbs(currentPrice - comparePrice) <= m_liquidityThreshold) {
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
// Need at least 2 matches to be considered liquidity
|
||||
return matches >= 2;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for liquidity sweep |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::CheckForSweep() {
|
||||
double currentPrice = iClose(m_symbol, m_timeframe, 0);
|
||||
bool foundSweep = false;
|
||||
|
||||
// Check each liquidity zone for potential sweep
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
if(!m_liquidityZones[i].isValid || m_liquidityZones[i].isSwept) continue;
|
||||
|
||||
double zonePrice = m_liquidityZones[i].price;
|
||||
bool isHigh = m_liquidityZones[i].isHigh;
|
||||
|
||||
// Check if price has swept through the liquidity zone
|
||||
bool swept = false;
|
||||
if(isHigh) {
|
||||
// For high liquidity, check if price went above and then reversed
|
||||
if(currentPrice > zonePrice + m_minSweepDistance) {
|
||||
// Check for reversal
|
||||
bool hasReversal = false;
|
||||
for(int j = 1; j <= m_reversalBars; j++) {
|
||||
double pastPrice = iClose(m_symbol, m_timeframe, j);
|
||||
if(pastPrice < zonePrice) {
|
||||
hasReversal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
swept = hasReversal;
|
||||
}
|
||||
} else {
|
||||
// For low liquidity, check if price went below and then reversed
|
||||
if(currentPrice < zonePrice - m_minSweepDistance) {
|
||||
// Check for reversal
|
||||
bool hasReversal = false;
|
||||
for(int j = 1; j <= m_reversalBars; j++) {
|
||||
double pastPrice = iClose(m_symbol, m_timeframe, j);
|
||||
if(pastPrice > zonePrice) {
|
||||
hasReversal = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
swept = hasReversal;
|
||||
}
|
||||
}
|
||||
|
||||
if(swept) {
|
||||
// Mark zone as swept
|
||||
m_liquidityZones[i].isSwept = true;
|
||||
|
||||
// Create sweep signal
|
||||
SLiquiditySweep newSweep;
|
||||
newSweep.time = TimeCurrent();
|
||||
newSweep.sweepPrice = zonePrice;
|
||||
newSweep.reversalPrice = currentPrice;
|
||||
newSweep.isBullishSweep = !isHigh; // Sweep lows = bullish, sweep highs = bearish
|
||||
newSweep.isValid = true;
|
||||
newSweep.isConfirmed = false;
|
||||
newSweep.sweepDistance = MathAbs(currentPrice - zonePrice);
|
||||
newSweep.timeframe = EnumToString(m_timeframe);
|
||||
newSweep.strength = CalculateSweepStrength(newSweep);
|
||||
|
||||
// Add to array
|
||||
for(int j = 0; j < ArraySize(m_sweeps); j++) {
|
||||
if(!m_sweeps[j].isValid) {
|
||||
m_sweeps[j] = newSweep;
|
||||
foundSweep = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(foundSweep && m_logger != NULL) {
|
||||
m_logger->LogMarketStructure(
|
||||
StringFormat("%s Liquidity Sweep", newSweep.isBullishSweep ? "Bullish" : "Bearish"),
|
||||
m_symbol, newSweep.sweepPrice, newSweep.time
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundSweep;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Calculate sweep strength |
|
||||
//+------------------------------------------------------------------+
|
||||
int CLiquiditySweepDetector::CalculateSweepStrength(const SLiquiditySweep &sweep) {
|
||||
int strength = 1;
|
||||
|
||||
// Distance of sweep
|
||||
double atr = iATR(m_symbol, m_timeframe, 14, 1);
|
||||
if(atr > 0) {
|
||||
double sweepRatio = sweep.sweepDistance / atr;
|
||||
if(sweepRatio > 0.5) strength++;
|
||||
if(sweepRatio > 1.0) strength++;
|
||||
}
|
||||
|
||||
// Volume confirmation
|
||||
if(m_useVolumeFilter) {
|
||||
double currentVolume = iVolume(m_symbol, m_timeframe, 0);
|
||||
double avgVolume = 0;
|
||||
for(int i = 1; i <= 10; i++) {
|
||||
avgVolume += iVolume(m_symbol, m_timeframe, i);
|
||||
}
|
||||
avgVolume /= 10;
|
||||
|
||||
if(currentVolume > avgVolume * m_volumeMultiplier) strength++;
|
||||
}
|
||||
|
||||
// Speed of reversal
|
||||
int reversalSpeed = 0;
|
||||
double startPrice = sweep.sweepPrice;
|
||||
double endPrice = sweep.reversalPrice;
|
||||
|
||||
for(int i = 1; i <= 5; i++) {
|
||||
double price = iClose(m_symbol, m_timeframe, i);
|
||||
if(sweep.isBullishSweep) {
|
||||
if(price > startPrice) {
|
||||
reversalSpeed = 6 - i; // Faster reversal = higher score
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if(price < startPrice) {
|
||||
reversalSpeed = 6 - i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(reversalSpeed >= 4) strength++;
|
||||
|
||||
return MathMin(strength, 5);
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get nearest liquidity zone |
|
||||
//+------------------------------------------------------------------+
|
||||
SLiquidityZone CLiquiditySweepDetector::GetNearestLiquidityZone(double price, bool isHigh) {
|
||||
SLiquidityZone nearestZone = {0};
|
||||
double nearestDistance = DBL_MAX;
|
||||
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
if(!m_liquidityZones[i].isValid || m_liquidityZones[i].isSwept) continue;
|
||||
if(m_liquidityZones[i].isHigh != isHigh) continue;
|
||||
|
||||
double distance = MathAbs(price - m_liquidityZones[i].price);
|
||||
if(distance < nearestDistance) {
|
||||
nearestDistance = distance;
|
||||
nearestZone = m_liquidityZones[i];
|
||||
}
|
||||
}
|
||||
|
||||
return nearestZone;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Clean up old data |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLiquiditySweepDetector::CleanupOldData() {
|
||||
datetime currentTime = TimeCurrent();
|
||||
|
||||
// Clean up old liquidity zones
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
if(m_liquidityZones[i].isValid) {
|
||||
if(currentTime - m_liquidityZones[i].time > PeriodSeconds(m_timeframe) * 100) {
|
||||
m_liquidityZones[i].isValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up old sweeps
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(m_sweeps[i].isValid) {
|
||||
if(currentTime - m_sweeps[i].time > PeriodSeconds(m_timeframe) * 50) {
|
||||
m_sweeps[i].isValid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get sweep count |
|
||||
//+------------------------------------------------------------------+
|
||||
int CLiquiditySweepDetector::GetSweepCount() {
|
||||
int count = 0;
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(m_sweeps[i].isValid) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get sweep by index |
|
||||
//+------------------------------------------------------------------+
|
||||
SLiquiditySweep CLiquiditySweepDetector::GetSweep(int index) {
|
||||
SLiquiditySweep emptySweep = {0};
|
||||
|
||||
if(index < 0 || index >= ArraySize(m_sweeps)) return emptySweep;
|
||||
if(!m_sweeps[index].isValid) return emptySweep;
|
||||
|
||||
return m_sweeps[index];
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get latest sweep |
|
||||
//+------------------------------------------------------------------+
|
||||
SLiquiditySweep CLiquiditySweepDetector::GetLatestSweep(bool bullish) {
|
||||
SLiquiditySweep latestSweep = {0};
|
||||
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(m_sweeps[i].isValid && m_sweeps[i].isBullishSweep == bullish) {
|
||||
if(latestSweep.time == 0 || m_sweeps[i].time > latestSweep.time) {
|
||||
latestSweep = m_sweeps[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return latestSweep;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for recent bullish sweep |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::IsRecentBullishSweep(int lookbackBars = 10) {
|
||||
datetime cutoffTime = TimeCurrent() - PeriodSeconds(m_timeframe) * lookbackBars;
|
||||
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(m_sweeps[i].isValid && m_sweeps[i].isBullishSweep &&
|
||||
m_sweeps[i].time >= cutoffTime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check for recent bearish sweep |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::IsRecentBearishSweep(int lookbackBars = 10) {
|
||||
datetime cutoffTime = TimeCurrent() - PeriodSeconds(m_timeframe) * lookbackBars;
|
||||
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(m_sweeps[i].isValid && !m_sweeps[i].isBullishSweep &&
|
||||
m_sweeps[i].time >= cutoffTime) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if has valid sweep |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::HasValidSweep(bool checkBullish = true, bool checkBearish = true) {
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(!m_sweeps[i].isValid) continue;
|
||||
|
||||
if(m_sweeps[i].isBullishSweep && checkBullish) return true;
|
||||
if(!m_sweeps[i].isBullishSweep && checkBearish) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get nearest liquidity high |
|
||||
//+------------------------------------------------------------------+
|
||||
double CLiquiditySweepDetector::GetNearestLiquidityHigh() {
|
||||
double currentPrice = iClose(m_symbol, m_timeframe, 0);
|
||||
SLiquidityZone nearestHigh = GetNearestLiquidityZone(currentPrice, true);
|
||||
|
||||
return nearestHigh.isValid ? nearestHigh.price : 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get nearest liquidity low |
|
||||
//+------------------------------------------------------------------+
|
||||
double CLiquiditySweepDetector::GetNearestLiquidityLow() {
|
||||
double currentPrice = iClose(m_symbol, m_timeframe, 0);
|
||||
SLiquidityZone nearestLow = GetNearestLiquidityZone(currentPrice, false);
|
||||
|
||||
return nearestLow.isValid ? nearestLow.price : 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if price is in liquidity zone |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::IsLiquidityZone(double price, double tolerance = 0.0001) {
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
if(!m_liquidityZones[i].isValid || m_liquidityZones[i].isSwept) continue;
|
||||
|
||||
if(MathAbs(price - m_liquidityZones[i].price) <= tolerance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get liquidity zone count |
|
||||
//+------------------------------------------------------------------+
|
||||
int CLiquiditySweepDetector::GetLiquidityZoneCount() {
|
||||
int count = 0;
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
if(m_liquidityZones[i].isValid && !m_liquidityZones[i].isSwept) count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Check if sweep and reverse pattern |
|
||||
//+------------------------------------------------------------------+
|
||||
bool CLiquiditySweepDetector::IsSweepAndReverse(bool bullish) {
|
||||
SLiquiditySweep latestSweep = GetLatestSweep(bullish);
|
||||
|
||||
if(!latestSweep.isValid) return false;
|
||||
|
||||
// Check if the sweep happened recently (within last 10 bars)
|
||||
datetime cutoffTime = TimeCurrent() - PeriodSeconds(m_timeframe) * 10;
|
||||
if(latestSweep.time < cutoffTime) return false;
|
||||
|
||||
// Check if price is moving in the expected direction after sweep
|
||||
double currentPrice = iClose(m_symbol, m_timeframe, 0);
|
||||
|
||||
if(bullish) {
|
||||
return currentPrice > latestSweep.sweepPrice;
|
||||
} else {
|
||||
return currentPrice < latestSweep.sweepPrice;
|
||||
}
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Get sweep reversal level |
|
||||
//+------------------------------------------------------------------+
|
||||
double CLiquiditySweepDetector::GetSweepReversalLevel(bool bullish) {
|
||||
SLiquiditySweep latestSweep = GetLatestSweep(bullish);
|
||||
|
||||
return latestSweep.isValid ? latestSweep.reversalPrice : 0;
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw liquidity zones |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLiquiditySweepDetector::DrawLiquidityZones() {
|
||||
for(int i = 0; i < ArraySize(m_liquidityZones); i++) {
|
||||
if(!m_liquidityZones[i].isValid) continue;
|
||||
|
||||
string objName = StringFormat("LIQ_%s_%d", m_symbol, i);
|
||||
color zoneColor = m_liquidityZones[i].isSwept ? clrGray :
|
||||
(m_liquidityZones[i].isHigh ? clrRed : clrBlue);
|
||||
|
||||
// Create horizontal line
|
||||
if(ObjectCreate(0, objName, OBJ_HLINE, 0, 0, m_liquidityZones[i].price)) {
|
||||
ObjectSetInteger(0, objName, OBJPROP_COLOR, zoneColor);
|
||||
ObjectSetInteger(0, objName, OBJPROP_STYLE, m_liquidityZones[i].isSwept ? STYLE_DOT : STYLE_DASH);
|
||||
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1);
|
||||
ObjectSetString(0, objName, OBJPROP_TOOLTIP,
|
||||
StringFormat("Liquidity %s (Strength: %d, Touches: %d)",
|
||||
m_liquidityZones[i].isHigh ? "High" : "Low",
|
||||
m_liquidityZones[i].strength,
|
||||
m_liquidityZones[i].touchCount));
|
||||
}
|
||||
}
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Draw sweeps |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLiquiditySweepDetector::DrawSweeps() {
|
||||
for(int i = 0; i < ArraySize(m_sweeps); i++) {
|
||||
if(!m_sweeps[i].isValid) continue;
|
||||
|
||||
string objName = StringFormat("SWEEP_%s_%d", m_symbol, i);
|
||||
color sweepColor = m_sweeps[i].isBullishSweep ? clrLime : clrRed;
|
||||
|
||||
// Create arrow object
|
||||
if(ObjectCreate(0, objName, OBJ_ARROW, 0, m_sweeps[i].time, m_sweeps[i].sweepPrice)) {
|
||||
ObjectSetInteger(0, objName, OBJPROP_COLOR, sweepColor);
|
||||
ObjectSetInteger(0, objName, OBJPROP_ARROWCODE, m_sweeps[i].isBullishSweep ? 241 : 242);
|
||||
ObjectSetInteger(0, objName, OBJPROP_WIDTH, 3);
|
||||
ObjectSetString(0, objName, OBJPROP_TOOLTIP,
|
||||
StringFormat("%s Liquidity Sweep (Strength: %d)",
|
||||
m_sweeps[i].isBullishSweep ? "Bullish" : "Bearish",
|
||||
m_sweeps[i].strength));
|
||||
}
|
||||
}
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
|
||||
//+------------------------------------------------------------------+
|
||||
//| Remove liquidity objects |
|
||||
//+------------------------------------------------------------------+
|
||||
void CLiquiditySweepDetector::RemoveLiquidityObjects() {
|
||||
string liqPrefix = StringFormat("LIQ_%s_", m_symbol);
|
||||
string sweepPrefix = StringFormat("SWEEP_%s_", m_symbol);
|
||||
|
||||
for(int i = ObjectsTotal(0) - 1; i >= 0; i--) {
|
||||
string objName = ObjectName(0, i);
|
||||
if(StringFind(objName, liqPrefix) == 0 || StringFind(objName, sweepPrefix) == 0) {
|
||||
ObjectDelete(0, objName);
|
||||
}
|
||||
}
|
||||
|
||||
ChartRedraw();
|
||||
}
|
||||
Reference in New Issue
Block a user