/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Indicator // --------------------------------------------- // Name: XHULL // Description: Hull Trend Indicator ... // // // 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 XHULL Indicator" #property strict // // START Constants ... // // #define ShortName "XHULL" // struct XHullData { // double value; double value3; // double wsum1; double wsum2; double wsum3; // double lsum1; double lsum2; double lsum3; }; // // END Constants ... // // // START Includes and Imports ... // // // Includes Common Library ... #include "../Libraries/x-saherelm.common.lib.mq5" // // END Includes and Imports ... // // // START XSCHULL Class Definition ... // // // a Class For Hull Calculations ... class XSCHull { // public: // // Constructor ... XSCHull() : mFullPeriod(1), mHalfPeriod(1), mSqrtPeriod(1), mArraySize(-1) { // // These are a way above to assign Private variables at Construct time ... } // // Deconstructor ... ~XSCHull() { ArrayFree(mData); } // // Initial Hull ... bool Init( int mPeriod, double mDivisor) { // bool result = false; // mFullPeriod = (int)(mPeriod > 1 ? mPeriod : 1); mHalfPeriod = (int)(mFullPeriod > 1 ? mFullPeriod / (mDivisor > 1 ? mDivisor : 1) : 1); mSqrtPeriod = (int)MathSqrt(mFullPeriod); // mArraySize = -1; mWeight1 = mWeight2 = mWeight3 = 1; // result = true; // return result; } // // CalCulate Specific Value ... double Calculate( double value, int i, int bars) { // double result = 0; // if (mArraySize < bars) { // mArraySize = ArrayResize(mData, bars + 500); if (mArraySize < bars) return result; } // mData[i].value = value; if (i > mFullPeriod) { // mData[i].wsum1 = mData[i - 1].wsum1 + value * mHalfPeriod - mData[i - 1].lsum1; mData[i].lsum1 = mData[i - 1].lsum1 + value - mData[i - mHalfPeriod].value; mData[i].wsum2 = mData[i - 1].wsum2 + value * mFullPeriod - mData[i - 1].lsum2; mData[i].lsum2 = mData[i - 1].lsum2 + value - mData[i - mFullPeriod].value; } else { // mData[i].wsum1 = mData[i].wsum2 = mData[i].lsum1 = mData[i].lsum2 = mWeight1 = mWeight2 = 0; for (int k = 0, w1 = mHalfPeriod, w2 = mFullPeriod; w2 > 0 && i >= k; k++, w1--, w2--) { // if (w1 > 0) { // mData[i].wsum1 += mData[i - k].value * w1; mData[i].lsum1 += mData[i - k].value; mWeight1 += w1; } // mData[i].wsum2 += mData[i - k].value * w2; mData[i].lsum2 += mData[i - k].value; mWeight2 += w2; } } // mData[i].value3 = 2.0 * mData[i].wsum1 / mWeight1 - mData[i].wsum2 / mWeight2; // if (i > mSqrtPeriod) { // mData[i].wsum3 = mData[i - 1].wsum3 + mData[i].value3 * mSqrtPeriod - mData[i - 1].lsum3; mData[i].lsum3 = mData[i - 1].lsum3 + mData[i].value3 - mData[i - mSqrtPeriod].value3; } else { // mData[i].wsum3 = mData[i].lsum3 = mWeight3 = 0; // for (int k = 0, w3 = mSqrtPeriod; w3 > 0 && i >= k; k++, w3--) { // mData[i].wsum3 += mData[i - k].value3 * w3; mData[i].lsum3 += mData[i - k].value3; mWeight3 += w3; } } // result = mData[i].wsum3 / mWeight3; // return result; } // // Protected ... protected: // // Private ... private: // int mFullPeriod; int mHalfPeriod; int mSqrtPeriod; int mArraySize; double mWeight1; double mWeight2; double mWeight3; // XHullData mData[]; }; // // END XSCHULL Class Definition ... // // // START Inputs ... // // input group "Market"; input int length = 72; // Length input double divisor = 2.0; // Divisor (Speed) // input group "Calculation"; input ENUM_APPLIED_PRICE upAppliedTo = PRICE_HIGH; // Up Zone Applied to input ENUM_APPLIED_PRICE downAppliedTo = PRICE_LOW; // Down Zone Applied to // input group "Presentation"; input bool showUpZone = true; // Show Up Zone input bool showDownZone = true; // Show Down Zone // // END Inputs ... // // // START Buffers ... // // #property indicator_chart_window // #property indicator_buffers 4 #property indicator_plots 4 // // UP Zone ... // #define upZoneBufferIndex 0 #define upZoneColorBufferIndex 1 // double upZoneBuffer[]; double upZoneColorBuffer[]; // #property indicator_label1 "XHULL Up" #property indicator_type1 DRAW_COLOR_LINE #property indicator_color1 clrLightGray, clrAqua, clrMagenta #property indicator_width1 1 // // DOWN Zone ... // #define downZoneBufferIndex 2 #define downZoneColorBufferIndex 3 // double downZoneBuffer[]; double downZoneColorBuffer[]; // #property indicator_label2 "XHULL Down" #property indicator_type2 DRAW_COLOR_LINE #property indicator_color2 clrLightGray, clrAqua, clrMagenta #property indicator_width2 1 // // END Buffers ... // // // START Global Definitions: Variables, Properties and etc ... // // int maxLength; // XSCHull *mUpZHull; XSCHull *mDownZHull; // // END Global Definitions: Variables, Properties and etc ... // // // START Event Handlers ... // // // Initialization ... int OnInit() { // // Validate Inputs ... if (!ValidateInputs()) { return INIT_PARAMETERS_INCORRECT; } // // 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 ... maxLength = ExtractMaxLengthOfInputs(); // // Define Index Buffers ... DefineBuffers(); // // Set Indicator ShortName ... SetIndicatorName(); // // Make an Instance of Hull Class ... // mUpZHull = new XSCHull(); mUpZHull.Init( length, divisor); // mDownZHull = new XSCHull(); mDownZHull.Init( length, divisor); // // 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 mPeriod 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 // delete mUpZHull; delete mDownZHull; } // // Calculating what we want ... int OnCalculate( const int rates_total, // Total Bars on Chart ... const int prev_calculated, // Total Calculated Bars on Charts ... const datetime &time[], // History of Open Time ... const double &open[], // History of Open Price ... const double &high[], // History of High Price ... const double &low[], // History of Low Price ... const double &close[], // History of Close Price ... const long &tick_volume[], // History of Tick Volumes on Bar ... const long &volume[], // History of Trade Volumes ... const int &spread[] // History of Spread Price ... ) { // // this counts Available Bars ... int limit; // limit = prev_calculated - 1; if (limit < 0) { limit = 0; } // // Main Loop ... for (int i = limit; i < rates_total && !IsStopped(); i++) { // double upPrice = getPrice( upAppliedTo, open, high, low, close, i); // double downPrice = getPrice( downAppliedTo, open, high, low, close, i); // CalculateBuffers( i, rates_total, upPrice, downPrice); } // return rates_total; } // // END Event Handlers ... // // // START Functions ... // // // Validate Input Args for Initialization ... bool ValidateInputs() { // bool result = true; // result = length >= 9 && divisor >= 0.5; // return result; } // // Retrieve all Exists Input Max Length ... // use for Start Of Drawing ... int ExtractMaxLengthOfInputs() { // int result = 0; // result = length; // return result; } // // Set Indicator Short Name and also we can define Buffers Labels ... void SetIndicatorName() { // IndicatorSetString(INDICATOR_SHORTNAME, ShortName); } // // Define Indexes and Styles ... void DefineBuffers() { // // UP Zone ... // // Draw Type ... ENUM_DRAW_TYPE upZoneDrawType = showUpZone ? DRAW_COLOR_LINE : DRAW_NONE; // // Buffer ... SetIndexBuffer(upZoneBufferIndex, upZoneBuffer, INDICATOR_DATA); PlotIndexSetInteger(upZoneBufferIndex, PLOT_SHOW_DATA, showUpZone); PlotIndexSetInteger(upZoneBufferIndex, PLOT_DRAW_TYPE, upZoneDrawType); // // Color Buffer ... SetIndexBuffer(upZoneColorBufferIndex, upZoneColorBuffer, INDICATOR_COLOR_INDEX); // // DOWN Zone ... // // Draw Type ... ENUM_DRAW_TYPE downZoneDrawType = showDownZone ? DRAW_COLOR_LINE : DRAW_NONE; // // Buffer ... SetIndexBuffer(downZoneBufferIndex, downZoneBuffer, INDICATOR_DATA); PlotIndexSetInteger(downZoneBufferIndex - 1, PLOT_SHOW_DATA, showDownZone); PlotIndexSetInteger(downZoneBufferIndex - 1, PLOT_DRAW_TYPE, downZoneDrawType); // // Color Buffer ... SetIndexBuffer(downZoneColorBufferIndex, downZoneColorBuffer, INDICATOR_COLOR_INDEX); } // // Calculate Buffers ... void CalculateBuffers( int bar_index, // Selected Bar Index int ratesTotal, // Total Rates double upPrice, // Up Price ... double downPrice // Down Price ... ) { // double upValue = mUpZHull.Calculate( upPrice, bar_index, ratesTotal); upZoneBuffer[bar_index] = upValue; double upColorIDX = (bar_index > 0) ? (upZoneBuffer[bar_index] > upZoneBuffer[bar_index - 1]) ? 1 : (upZoneBuffer[bar_index] < upZoneBuffer[bar_index - 1]) ? 2 : upZoneColorBuffer[bar_index - 1] : 0; upZoneColorBuffer[bar_index] = upColorIDX; // double downValue = mDownZHull.Calculate( downPrice, bar_index, ratesTotal); downZoneBuffer[bar_index] = downValue; double downColorIDX = (bar_index > 0) ? (downZoneBuffer[bar_index] > downZoneBuffer[bar_index - 1]) ? 1 : (downZoneBuffer[bar_index] < downZoneBuffer[bar_index - 1]) ? 2 : downZoneColorBuffer[bar_index - 1] : 0; downZoneColorBuffer[bar_index] = downColorIDX; } // // END Functions ... // // // TEMPLATE Function ... template double getPrice(ENUM_APPLIED_PRICE tprice, T &open[], T &high[], T &low[], T &close[], int i) { switch (tprice) { case PRICE_CLOSE: return (close[i]); case PRICE_OPEN: return (open[i]); case PRICE_HIGH: return (high[i]); case PRICE_LOW: return (low[i]); case PRICE_MEDIAN: return ((high[i] + low[i]) / 2.0); case PRICE_TYPICAL: return ((high[i] + low[i] + close[i]) / 3.0); case PRICE_WEIGHTED: return ((high[i] + low[i] + close[i] + close[i]) / 4.0); } return (0); }