From 9ab980be84a015a4b41b4a51a3188abd74dca382 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Wed, 10 Jul 2024 16:01:33 +0330 Subject: [PATCH] last works on trend detector and try to test it inside x-test.ea.mq5 ... --- Experts/x-test.ea.mq5 | 37 ++- Indicators/x-saherelm.xobd.mq5 | 395 +++++++++++++++++++++++++++++++++ Libraries/x-saherelm.tools.mq5 | 334 +++++++++++++++++++++------- 3 files changed, 684 insertions(+), 82 deletions(-) create mode 100644 Indicators/x-saherelm.xobd.mq5 diff --git a/Experts/x-test.ea.mq5 b/Experts/x-test.ea.mq5 index 4b9dd092..71556725 100644 --- a/Experts/x-test.ea.mq5 +++ b/Experts/x-test.ea.mq5 @@ -63,6 +63,9 @@ XPivot lastResistancePivot; XZone lastSupportZone; XZone lastResistanceZone; +// +XTrendDetector trendDetector; + // // Event Handlers ... @@ -157,6 +160,10 @@ void OnTick() // // Check Time Tracker ... // HandleCheckTimeTracker(); + + // + // Check Trend State ... + HandleCheckTrend(); } // @@ -524,38 +531,54 @@ void HandleCheckZones() } // -void HandleCheckTimeTracker() { +void HandleCheckTimeTracker() +{ // // Month ... - if (eaTimeTracker.IsNewMonth()) { + if (eaTimeTracker.IsNewMonth()) + { eaAlert.Alert("New Month ..."); } // // Week ... - if (eaTimeTracker.IsNewWeek()) { + if (eaTimeTracker.IsNewWeek()) + { eaAlert.Alert("New Week ..."); } // // Day ... - if (eaTimeTracker.IsNewDay()) { + if (eaTimeTracker.IsNewDay()) + { eaAlert.Alert("New Day ..."); } // // Hour ... - if (eaTimeTracker.IsNewHour()) { + if (eaTimeTracker.IsNewHour()) + { eaAlert.Alert("New Hour ..."); } // // Minute ... - if (eaTimeTracker.IsNewMinute()) { + if (eaTimeTracker.IsNewMinute()) + { eaAlert.Alert("New Minute ..."); } } +// +void HandleCheckTrend() +{ + // + bool hasTren = trendDetector.FindTrend( + _Symbol, + _Period // + ); +} + // void OldTests() { @@ -693,5 +716,5 @@ void OldTests() // eaBarTracker.Waits(); // } } - + // \ No newline at end of file diff --git a/Indicators/x-saherelm.xobd.mq5 b/Indicators/x-saherelm.xobd.mq5 new file mode 100644 index 00000000..a2531743 --- /dev/null +++ b/Indicators/x-saherelm.xobd.mq5 @@ -0,0 +1,395 @@ +///////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 OrderBlock Detector Indicator +// ----------------------------------------------------- +// Name: XOBD +// Description: detect Order Blocks based on Swings ... +// +// +// Maintainer: +// ------------ +// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) +// +////////////////////////////////////////////////////// +// +// Global Properties ... +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://saherelm.ir" +#property version "1.00" +#property description "SaherElm XOBD Indicator" +#property strict + +// +// START Constants ... +// +#define ShortName "XOBD" +// +// END Constants ... +// + +// +// START Inputs ... +// +// +input int length = 5; // Swing Length + +// +input uchar swingHighArrowCode = 108; // Swing High Arrow Code +input color swingHighArrowColor = clrMagenta; // Swing High Arrow Color + +// +input uchar swingLowArrowCode = 108; // Swing Low Arrow Code +input color swingLowArrowColor = clrAqua; // Swing Low Arrow Color +// +// END Inputs ... +// + +// +// Include Common and Models Library ... +#include "../Libraries/x-saherelm.common.lib.mq5" + +// +// Include Draw Library ... +#include "../Libraries/x-saherelm.draw.lib.mq5" + +// +// START Buffers ... +// +#property indicator_chart_window + +// +#property indicator_buffers 1 +#property indicator_plots 0 + +// +#define swingBufferIndex 0 +double swingBuffer[]; +// +// END Buffers ... +// + +// +// START Global Definitions: Variables, Properties and etc ... +// +// +// END Global Definitions: Variables, Properties and etc ... +// + +// +// START Event Handlers ... +// +// +// Initialization ... +int OnInit() +{ + // + // Validate Inputs ... + if (!ValidateInputs()) + { + return INIT_PARAMETERS_INCORRECT; + } + + // + drawPrefix = ShortName; + + // + // Define Index Buffers ... + DefineBuffers(); + + // + // Set Indicator ShortName ... + SetIndicatorName(); + + // + // Init Succeed ... + return INIT_SUCCEEDED; +} + +// +// DeInitialization ... +void OnDeinit(const int reason) +{ + // + // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function + // REASON_REMOVE 1 Program removed from a chart + // REASON_RECOMPILE 2 Program recompiled + // REASON_CHARTCHANGE 3 A symbol or a chart period is changed + // REASON_CHARTCLOSE 4 Chart closed + // REASON_PARAMETERS 5 Inputs changed by a user + // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings + // REASON_TEMPLATE 7 Another chart template applied + // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value + // REASON_CLOSE 9 Terminal closed +} + +// +// Calculations ... +// +// Calculating what we want ... +int OnCalculate( + // + // total Candles on chart ... + const int rates_total, + // + // total calculated Candles on charts ... + const int prev_calculated, + // + // history of Candles Open Time ... + const datetime &time[], + // + // history of Candles Open Price ... + const double &open[], + // + // history of Candles High Price ... + const double &high[], + // + // history of Candles Low Price ... + const double &low[], + // + // history of Candles Close Price ... + const double &close[], + // + // history of Tick Volumes on Candle ... + const long &tick_volume[], + // + // history of Trade Volumes ... + const long &volume[], + // + // history of Candles Spread Price ... + const int &spread[]) +{ + // + // Prepare Buffers ... + ArraySetAsSeries(time, true); + ArraySetAsSeries(open, true); + ArraySetAsSeries(high, true); + ArraySetAsSeries(low, true); + ArraySetAsSeries(close, true); + ArraySetAsSeries(tick_volume, true); + ArraySetAsSeries(volume, true); + ArraySetAsSeries(spread, true); + + // + // this counts Available Bars ... + int limit; + + // + // because in some cases we may have more than one input for + // calculation and we must prevent any calculation + // untill we pass the biggest input length, here we get max Input length + // and then wait until pass it ... + int maxLength = MathMax(0, length * 3); + + // + limit = prev_calculated < maxLength ? rates_total - (maxLength + 1) : rates_total - prev_calculated; + + // + // Main Loop ... + for (int i = limit; i >= 0 && !IsStopped(); i--) + { + // + CalculateBuffers(i); + } + + // + return rates_total; +} +// +// END Event Handlers ... +// + +// +// START Functions ... +// +// +// Validate Input Args for Initialization ... +bool ValidateInputs() +{ + // + bool result = false; + + // + // Validate Args ... + if (length >= 2) + { + result = true; + } + + // + return result; +} + +// +// Define Indexes and Styles ... +void DefineBuffers() +{ + // + ArraySetAsSeries(swingBuffer, true); + SetIndexBuffer(swingBufferIndex, swingBuffer, INDICATOR_CALCULATIONS); +} + +// +// Set Indicator Short Name and also we can define Buffers Labels ... +void SetIndicatorName() +{ + // + string indicatorShortName = ""; + StringConcatenate(indicatorShortName, ShortName, " ", "( ", length, " )"); + + // + IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortName); +} + +// +// Calculate Buffers ... +void CalculateBuffers(int bar_index) +{ + // + // GetCandleSwing(bar_index); + ENUM_X_SWING_TYPE type = X_NO_SWING; + + // + // Try To Detect Order Blocks ... + bool isSwingLow = false; + bool isSwingHigh = false; + + // + // Reading Candles Data ... + int start = bar_index; + int count = (length * 2) + 2; + int end = start + count; + XOHCL bars[]; + int readedBars = GetBars( + bars, + _Symbol, + _Period, + start, + count + // + ); + if (readedBars < count) { + return; + } + + // + // Swing Low ... + isSwingLow = + // + bars[1].close > bars[2].close && + bars[2].close > bars[3].close && + // + bars[3].close < bars[4].close && + // + bars[5].close > bars[4].close && + bars[6].close > bars[5].close + // + ; + + // + if (isSwingLow) + { + type = X_SWING_LOW; + } + + // + if (!isSwingLow) + { + // + // Swing High ... + isSwingHigh = + // + bars[1].close < bars[2].close && + bars[2].close < bars[3].close && + // + bars[3].close > bars[4].close && + // + bars[5].close < bars[4].close && + bars[6].close < bars[5].close + // + ; + + // + if (isSwingHigh) + { + type = X_SWING_HIGH; + } + } + + // + switch (type) + { + // + // Swing High ... + case X_SWING_HIGH: + // + swingBuffer[bar_index] = type; + DrawSwingArrow(1, bar_index); + break; + + // + // Swinmg Low ... + case X_SWING_LOW: + // + swingBuffer[bar_index] = type; + DrawSwingArrow(0, bar_index); + break; + + // + // No Swing ... + default: + case X_NO_SWING: + // + swingBuffer[bar_index] = 0; + break; + } +} + +// +// Draw a Swing Arrow Shape ... +void DrawSwingArrow( + int type, // Swing Type: 0 - Low / 1 - High ... + int bar_index // +) +{ + // + bool isSwingHigh = type == 1; + + // + if ((isSwingHigh && swingHighArrowCode == 0) || + (!isSwingHigh && swingLowArrowCode == 0)) + { + return; + } + + XOHCL candle; + candle.Init( + _Symbol, + _Period, + bar_index + 3); + double arrowDistanceInPoint = 0; + + // + // Define Required Object Properties fro Draw an Arrow ... + datetime time = iTime(_Symbol, _Period, bar_index + 3); + double purePrice = isSwingHigh ? candle.high : candle.low; + color clr = isSwingHigh ? swingHighArrowColor : swingLowArrowColor; + ENUM_ARROW_ANCHOR anchor = isSwingHigh ? ANCHOR_BOTTOM : ANCHOR_TOP; + uchar arrowCode = uchar(isSwingHigh ? swingHighArrowCode : swingLowArrowCode); + double price = isSwingHigh ? purePrice + arrowDistanceInPoint : purePrice - arrowDistanceInPoint; + string name = "Swing " + (isSwingHigh ? "High" : "Low") + "_" + (string)time + "_" + (string)purePrice; + + // + // Draw desired Arrow ... + DrawArrow( + 0, + name, + 0, + time, + price, + arrowCode, + anchor, + clr); +} +// +// END Functions ... +// diff --git a/Libraries/x-saherelm.tools.mq5 b/Libraries/x-saherelm.tools.mq5 index e3c817d5..bebdfefa 100644 --- a/Libraries/x-saherelm.tools.mq5 +++ b/Libraries/x-saherelm.tools.mq5 @@ -1426,7 +1426,8 @@ struct XZone // // Check Zone Rejected ... - bool IsSupportRejected() { + bool IsSupportRejected() + { // bool result = false; @@ -1434,17 +1435,17 @@ struct XZone // Check is Full Support or Not ... ENUM_XZONE_STATE state = GetState(); result = state == XZONE_STATE_FULL_SUPPORT; - if (!result) { + if (!result) + { return result; - } + } // // First Check Price Rejection Above Upper Line ... - // return result; - } + } // // Check Zone Breaked ... @@ -1704,89 +1705,272 @@ struct XZone // } // }; -// // -// // Detect Trends ... -// struct XTrendDetector -// { -// // -// string symbol; -// ENUM_TIMEFRAMES period; +// +// Detect Trends ... +struct XTrendDetector +{ + // + string symbol; + ENUM_TIMEFRAMES period; -// // -// double higherHighs[]; -// datetime higherHighTimes[]; + // + double higherHighs[]; + datetime higherHighTimes[]; -// // -// double higherLows[]; -// datetime higherLowTimes[]; + // + double higherLows[]; + datetime higherLowTimes[]; -// // -// double lowerHighs[]; -// datetime lowerHighTimes[]; + // + double lowerHighs[]; + datetime lowerHighTimes[]; -// // -// double lowerLows[]; -// datetime lowerLowTimes[]; + // + double lowerLows[]; + datetime lowerLowTimes[]; -// // -// int _numberOfPivots; -// int _pivotShoulders; + // + int _numberOfPivots; + int _pivotShoulders; -// // -// // Constructor ... -// XTrendDetector() -// { -// Clean(); -// } + // + // Constructor ... + XTrendDetector() + { + Clean(); + } -// // -// // Tools ... + // + bool FindTrend( + string _symbol, + ENUM_TIMEFRAMES _period, + int barIndex = 0, + int _requiredPivots = 5, + int _requiredShoulders = 5 // + ) + { + // + bool result = false; -// // -// // Cleanup ... -// void Clean() -// { -// // -// symbol = NULL; -// period = NULL; + // + if (barIndex < 0) + { + barIndex = 0; + } -// // -// _numberOfPivots = 0; -// _pivotShoulders = 0; + // + if (_requiredPivots < 5) + { + _requiredPivots = 5; + } -// // -// Clean(higherHighs); -// Clean(higherHighTimes); + // + if (_requiredShoulders < 5) + { + _requiredShoulders = 5; + } -// // -// Clean(higherLows); -// Clean(higherLowTimes); + // + result = + // + IsValid(_symbol) && + IsValid(_period) + // + ; + if (!result) + { + return result; + } -// // -// Clean(lowerHighs); -// Clean(lowerHighTimes); + // + // Find Swing Lows and Swing Highs ... -// // -// Clean(lowerLows); -// Clean(lowerLowTimes); -// } + // + int hhIndexes[]; + datetime hhTimes[]; + double hhs[]; -// // -// // Validate ... -// bool IsValid() -// { -// // -// bool result = false; + // + int llIndexes[]; + datetime llTimes[]; + double lls[]; -// // -// result = -// // -// IsValid(symbol) && -// IsValid(period) -// // -// ; + // + bool isEnough = false; + int lastHHIndex = barIndex; + int lastLLIndex = barIndex; + while (!isEnough) + { + // + int hhSize = ArraySize(hhs); + if (hhSize < _requiredPivots) + { + // + lastHHIndex = FindSwing( + _symbol, + _period, + X_SWING_HIGH, + _requiredShoulders, + lastHHIndex + 1 // + ); + if (lastHHIndex >= 0) + { + // + XOHCL hBar; + bool isInited = hBar.Init( + _symbol, + _period, + lastHHIndex // + ); + if (isInited) + { + // + Add( + hBar.high, + hhs // + ); -// // -// return result; -// } -// }; + // + Add( + hBar.time, + hhTimes // + ); + + // + Add( + lastHHIndex, + hhIndexes // + ); + } + } + } + + // + int llSize = ArraySize(lls); + if (llSize < _requiredPivots) + { + // + lastLLIndex = FindSwing( + _symbol, + _period, + X_SWING_LOW, + _requiredShoulders, + lastLLIndex + 1 // + ); + if (lastLLIndex >= 0) + { + // + XOHCL lBar; + bool isInited = lBar.Init( + _symbol, + _period, + lastLLIndex // + ); + if (isInited) + { + // + Add( + lBar.low, + lls // + ); + + // + Add( + lBar.time, + llTimes // + ); + + // + Add( + lastHHIndex, + llIndexes // + ); + } + } + } + + // + hhSize = ArraySize(hhs); + llSize = ArraySize(lls); + isEnough = + // + hhSize == _requiredPivots && + llSize == _requiredPivots + // + ; + } + + // // + // ArrayReverse(hhs); + // ArrayReverse(hhTimes); + // ArrayReverse(hhIndexes); + + // // + // ArrayReverse(lls); + // ArrayReverse(llTimes); + // ArrayReverse(llIndexes); + + // + symbol = _symbol; + period = _period; + _numberOfPivots = _requiredPivots; + _pivotShoulders = _requiredShoulders; + + // + result = IsValid(); + + // + return result; + } + + // + // Tools ... + + // + // Cleanup ... + void Clean() + { + // + symbol = NULL; + period = NULL; + + // + _numberOfPivots = 0; + _pivotShoulders = 0; + + // + Clean(higherHighs); + Clean(higherHighTimes); + + // + Clean(higherLows); + Clean(higherLowTimes); + + // + Clean(lowerHighs); + Clean(lowerHighTimes); + + // + Clean(lowerLows); + Clean(lowerLowTimes); + } + + // + // Validate ... + bool IsValid() + { + // + bool result = false; + + // + result = + // + IsValid(symbol) && + IsValid(period) + // + ; + + // + return result; + } +};