├── README_Images └── Easy Trend Visualizer Shows Trends on Short-Term EURUSD Chart.png ├── README.md ├── LICENSE ├── EasyTrendVisualizer.mq4 └── EasyTrendVisualizer.mq5 /README_Images/Easy Trend Visualizer Shows Trends on Short-Term EURUSD Chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EarnForex/Easy-Trend-Visualizer/HEAD/README_Images/Easy Trend Visualizer Shows Trends on Short-Term EURUSD Chart.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Easy Trend Visualizer 2 | Easy Trend Visualizer is a custom indicator for MT4 and MT5 coded by EarnForex.com. It was originally developed by _KurlFX_. This indicator is an advanced trend detector that helps distiguish trending markets from sideways ones. 3 | 4 | It provides a range of configurable alerts for you to get norifications of various trend-related events on the chart. 5 | 6 | ![Easy Trend Visualizer indicator marks some important trend developments on the M1 EUR/USD chart](https://github.com/EarnForex/Easy-Trend-Visualizer/blob/main/README_Images/Easy%20Trend%20Visualizer%20Shows%20Trends%20on%20Short-Term%20EURUSD%20Chart.png) 7 | 8 | More information about this custom MetaTrader indicator is available here: https://www.earnforex.com/indicators/EasyTrendVisualizer/ 9 | 10 | Any contributions to the code are welcome! 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /EasyTrendVisualizer.mq4: -------------------------------------------------------------------------------- 1 | // Original indicator by KurlFX 23/6/09 2 | //+------------------------------------------------------------------+ 3 | //| Easy Trend Visualizer | 4 | //| Copyright © 2025, EarnForex | 5 | //| https://www.earnforex.com/ | 6 | //+------------------------------------------------------------------+ 7 | #property copyright "Copyright © 2025, EarnForex" 8 | #property link "https://www.earnforex.com/metatrader-indicators/EasyTrendVisualizer/" 9 | #property version "1.13" 10 | #property strict 11 | 12 | #property description "Easy Trend Visualizer - displays trend strength, direction, and support and resistance levels." 13 | 14 | #define Alvl 35.0 15 | #define Alvl2 30.0 16 | 17 | #property indicator_chart_window 18 | #property indicator_buffers 5 19 | #property indicator_color1 clrRed 20 | #property indicator_color2 clrSteelBlue 21 | #property indicator_width1 2 22 | #property indicator_width2 2 23 | #property indicator_color3 clrIndigo 24 | #property indicator_color4 clrIndigo 25 | #property indicator_color5 clrIndigo 26 | #property indicator_width3 1 27 | #property indicator_width4 1 28 | 29 | input int ADXperiod1 = 10; 30 | input int ADXperiod2 = 14; 31 | input int ADXperiod3 = 20; 32 | input bool UseAlertHorizontalLine = false; 33 | input bool UseAlertUpDownArrows = false; 34 | input bool UseAlertHorizontalLineCrossCurrent = false; 35 | input bool UseAlertHorizontalLineCrossPrevious = false; 36 | input int NumberPHLtoTrack = 0; // How many previous horizontal lines to track for alert purposes? 37 | input int IgnorePHLShorterThan = 2; // Ignore previous horizontal lines shorter than 38 | input color PHLC_Arrow_Color = clrChocolate; 39 | input color CHLC_Arrow_Color = clrFireBrick; 40 | input bool NativeAlerts = false; // Use pop-up alerts? 41 | input bool SendEmails = false; // Send alerts via email? 42 | input bool SendNotifications = false; // Send alerts via push notifications? 43 | 44 | int MxP, MnP, MdP; 45 | 46 | double was_alert_hl = EMPTY_VALUE; // Horizontal line 47 | double was_alert_au = EMPTY_VALUE; // Arrow up 48 | double was_alert_ad = EMPTY_VALUE; // Arrow down 49 | datetime was_alert_hlcross = 0; // Horizontal line cross 50 | 51 | double To[]; 52 | double Tc[]; 53 | double ADX1[]; 54 | double ADX2[]; 55 | double ADX3[]; 56 | double Up[]; 57 | double Dn[]; 58 | double Ex[]; 59 | 60 | double Last_Ex[]; // Buffer to hold previous "last line levels". Required for price/line alert because imaginary line cross also count. 61 | datetime was_alert_phlc[]; // Price crosses and closes above/below previous horizontal line. 62 | int last_trend_for_hl_alert = 0; // 0 - up, 1 - down. 63 | 64 | //+------------------------------------------------------------------+ 65 | //| Custom indicator initialization function | 66 | //+------------------------------------------------------------------+ 67 | int init() 68 | { 69 | MxP = MathMax(MathMax(ADXperiod1, ADXperiod2), ADXperiod3); 70 | MnP = MathMin(MathMin(ADXperiod1, ADXperiod2), ADXperiod3); 71 | if (MxP == ADXperiod1) MdP = MathMax(ADXperiod2, ADXperiod3); 72 | else if (MxP == ADXperiod2) MdP = MathMax(ADXperiod1, ADXperiod3); 73 | else MdP = MathMax(ADXperiod2, ADXperiod1); 74 | 75 | IndicatorShortName("ETV(" + IntegerToString(MnP) + "/" + IntegerToString(MdP) + "/" + IntegerToString(MxP) + ")"); 76 | 77 | IndicatorBuffers(8); 78 | SetIndexBuffer(0, To); 79 | SetIndexBuffer(1, Tc); 80 | SetIndexBuffer(2, Up); 81 | SetIndexBuffer(3, Dn); 82 | SetIndexBuffer(4, Ex); 83 | SetIndexBuffer(5, ADX1); 84 | SetIndexBuffer(6, ADX2); 85 | SetIndexBuffer(7, ADX3); 86 | SetIndexLabel(0, NULL); 87 | SetIndexLabel(1, NULL); 88 | SetIndexStyle(0, DRAW_HISTOGRAM); 89 | SetIndexStyle(1, DRAW_HISTOGRAM); 90 | SetIndexLabel(2, "Up"); 91 | SetIndexLabel(3, "Dn"); 92 | SetIndexLabel(4, "End"); 93 | SetIndexStyle(2, DRAW_ARROW); 94 | SetIndexStyle(3, DRAW_ARROW); 95 | SetIndexStyle(4, DRAW_LINE); 96 | SetIndexArrow(2, 225); 97 | SetIndexArrow(3, 226); 98 | 99 | ArrayResize(Last_Ex, PHLC_Arrow_Color); 100 | ArrayInitialize(Last_Ex, EMPTY_VALUE); 101 | 102 | ArrayResize(was_alert_phlc, PHLC_Arrow_Color); 103 | ArrayInitialize(was_alert_phlc, 0); 104 | 105 | return 0; 106 | } 107 | 108 | //+------------------------------------------------------------------+ 109 | //| Custom indicator deinitialization function | 110 | //+------------------------------------------------------------------+ 111 | void deinit() 112 | { 113 | // Delete all alert arrows. 114 | ObjectsDeleteAll(0, "PHLCArrow_", -1, OBJ_ARROW); 115 | ObjectsDeleteAll(0, "CHLCArrow_", -1, OBJ_ARROW); 116 | } 117 | 118 | //+------------------------------------------------------------------+ 119 | //| Custom Easy Trend Visualization indicator | 120 | //+------------------------------------------------------------------+ 121 | int start() 122 | { 123 | int cntbar = IndicatorCounted(); 124 | int limit = Bars - cntbar; 125 | if (cntbar == 0) limit -= MxP; 126 | // Need to update at least the most recent two values for use in the main cycle. 127 | for (int i = MathMax(limit - 1, 2); i >= 0; i--) 128 | { 129 | ADX1[i] = iADX(NULL, 0, MnP, PRICE_CLOSE, MODE_MAIN, i); 130 | ADX2[i] = iADX(NULL, 0, MdP, PRICE_CLOSE, MODE_MAIN, i); 131 | ADX3[i] = iADX(NULL, 0, MxP, PRICE_CLOSE, MODE_MAIN, i); 132 | } 133 | 134 | if (cntbar == 0) limit--; 135 | 136 | for (int i = limit - 1; i >= 0; i--) 137 | { 138 | bool f1 = false, f2 = false, f3 = false; 139 | To[i] = EMPTY_VALUE; 140 | Tc[i] = EMPTY_VALUE; 141 | Up[i] = EMPTY_VALUE; 142 | Dn[i] = EMPTY_VALUE; 143 | Ex[i] = EMPTY_VALUE; 144 | 145 | // Remember last lines for imaginary crosses. 146 | if ((NumberPHLtoTrack > 0) && (i + 2 < Bars)) // Check if we fit into arrays. 147 | { 148 | // New line found and it has not yet been included. 149 | if ((Ex[i + 1] == EMPTY_VALUE) && (Ex[i + 2] != EMPTY_VALUE) && (Last_Ex[0] != Ex[i + 2])) 150 | { 151 | // Count the length of the added line. 152 | int length = 1; 153 | for (int j = i + 3; j < Bars; j++) 154 | { 155 | if (Ex[j] == EMPTY_VALUE) break; 156 | length++; 157 | } 158 | 159 | if (length >= IgnorePHLShorterThan) 160 | { 161 | // Shift arrays (FIFO, 0 - newest): 162 | for (int j = NumberPHLtoTrack - 1; j >= 0; j--) 163 | { 164 | // This check is needed for a very special case of chart data not being fully loaded. 165 | // When it is being loaded the indicator is recalculated, but new bars appear from left, not from right as usually. 166 | // This keeps rewriting the Last_Ex array with basically the same values, which can lead to lots of unnecessary alerts. 167 | if (Time[i + 2] < was_alert_phlc[j]) break; 168 | if (j == 0) // Insert new value. 169 | { 170 | Last_Ex[j] = Ex[i + 2]; 171 | was_alert_phlc[j] = 0; 172 | } 173 | else // Shift. 174 | { 175 | Last_Ex[j] = Last_Ex[j - 1]; 176 | was_alert_phlc[j] = was_alert_phlc[j - 1]; 177 | } 178 | } 179 | } 180 | } 181 | } 182 | 183 | if (ADX1[i + 1] < ADX1[i]) f1 = true; 184 | if (ADX2[i + 1] < ADX2[i]) f2 = true; 185 | if (ADX3[i + 1] < ADX3[i]) f3 = true; 186 | 187 | if ((f1) && (f2) && (f3) && (ADX1[i] > Alvl) && (ADX2[i] > Alvl2)) 188 | { 189 | double di = iADX(NULL, 0, MnP, PRICE_CLOSE, MODE_PLUSDI, i) 190 | - iADX(NULL, 0, MnP, PRICE_CLOSE, MODE_MINUSDI, i); 191 | double hi = MathMax(Open[i], Close[i]); 192 | double lo = MathMin(Open[i], Close[i]); 193 | double op = Open[i]; 194 | if (di > 0) 195 | { 196 | To[i] = lo; 197 | Tc[i] = hi; 198 | if (To[i + 1] == EMPTY_VALUE) Up[i] = op; 199 | } 200 | else 201 | { 202 | To[i] = hi; 203 | Tc[i] = lo; 204 | if (To[i + 1] == EMPTY_VALUE) Dn[i] = op; 205 | } 206 | } 207 | else 208 | { 209 | if (To[i + 1] != EMPTY_VALUE) Ex[i] = Close[i + 1]; 210 | else Ex[i] = Ex[i + 1]; 211 | } 212 | if (Up[i] != EMPTY_VALUE) last_trend_for_hl_alert = 0; 213 | else if (Dn[i] != EMPTY_VALUE) last_trend_for_hl_alert = 1; 214 | } 215 | 216 | // If at least one type and one source of alerts is defined. 217 | if (((NativeAlerts) || (SendEmails) || (SendNotifications)) && ((UseAlertHorizontalLine) || (UseAlertUpDownArrows) || (UseAlertHorizontalLineCrossCurrent) || (UseAlertHorizontalLineCrossPrevious))) 218 | { 219 | string DateTime = TimeToString(Time[0]); 220 | string PerStr = PeriodToString(Period()); 221 | if (UseAlertHorizontalLine) 222 | { 223 | if ((Ex[1] != EMPTY_VALUE) && (Ex[1] != was_alert_hl) && (Ex[1] != Ex[2])) 224 | { 225 | string text = "ETV - HL Start "; 226 | if (last_trend_for_hl_alert == 0) text += "After Uptrend"; 227 | else text += "After Downtrend"; 228 | text += Symbol() + " @ " + PerStr; 229 | if (NativeAlerts) Alert(text); 230 | if (SendEmails) SendMail(text, text); 231 | if (SendNotifications) SendNotification(text); 232 | was_alert_hl = Ex[1]; 233 | } 234 | } 235 | if (UseAlertUpDownArrows) 236 | { 237 | if ((Up[0] != EMPTY_VALUE) && (Up[0] != was_alert_au)) 238 | { 239 | string text = "ETV - Arrow Up " + Symbol() + " @ " + PerStr; 240 | if (NativeAlerts) Alert(text); 241 | if (SendEmails) SendMail(text, text); 242 | if (SendNotifications) SendNotification(text); 243 | was_alert_au = Up[0]; 244 | } 245 | if ((Dn[0] != EMPTY_VALUE) && (Dn[0] != was_alert_ad)) 246 | { 247 | string text = "ETV - Arrow Down " + Symbol() + " @ " + PerStr; 248 | if (NativeAlerts) Alert(text); 249 | if (SendEmails) SendMail(text, text); 250 | if (SendNotifications) SendNotification(text); 251 | was_alert_ad = Dn[0]; 252 | } 253 | } 254 | if (UseAlertHorizontalLineCrossCurrent) 255 | { 256 | if ((Ex[1] != EMPTY_VALUE) && (Ex[2] != EMPTY_VALUE) && (was_alert_hlcross != Time[1]) && (((Close[1] > Ex[1]) && (Open[1] <= Ex[1])) || ((Close[1] <= Ex[1]) && (Open[1] > Ex[1])))) 257 | { 258 | string text = "ETV - HL Cross "; 259 | if (Open[1] < Close[1]) text += "Up "; 260 | else if (Open[1] > Close[1]) text += "Down "; 261 | text += Symbol() + " @ " + PerStr; 262 | if (NativeAlerts) Alert(text); 263 | if (SendEmails) SendMail(text, text); 264 | if (SendNotifications) SendNotification(text); 265 | was_alert_hlcross = Time[1]; 266 | string obj_name = "CHLCArrow_" + TimeToString(Time[1]); 267 | ObjectCreate(0, obj_name, OBJ_ARROW, 0, Time[1], Ex[1]); 268 | ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 200); 269 | ObjectSetInteger(0, obj_name, OBJPROP_COLOR, CHLC_Arrow_Color); 270 | string desc = "Price crossed: " + DoubleToString(Ex[1], Digits()) + " @ " + PerStr; 271 | ObjectSetString(0, obj_name, OBJPROP_TOOLTIP, desc); 272 | ObjectSetString(0, obj_name, OBJPROP_TEXT, desc); 273 | ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); 274 | ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, false); 275 | } 276 | } 277 | // Alerts for the previous HL crosses. 278 | if ((UseAlertHorizontalLineCrossPrevious) && (NumberPHLtoTrack > 0)) 279 | { 280 | DateTime = TimeToString(Time[1]); 281 | CheckImaginaryLinesCrosses(DateTime, PerStr); 282 | } 283 | } 284 | 285 | return 0; 286 | } 287 | 288 | string PeriodToString(int per) 289 | { 290 | switch (per) 291 | { 292 | case 60: 293 | return("H1"); 294 | case 240: 295 | return("H4"); 296 | case 1440: 297 | return("D1"); 298 | case 10080: 299 | return("W1"); 300 | case 43200: 301 | return("MN1"); 302 | case 30: 303 | return("M30"); 304 | case 15: 305 | return("M15"); 306 | case 5: 307 | return("M5"); 308 | case 1: 309 | return("M1"); 310 | } 311 | return ""; 312 | } 313 | 314 | void CheckImaginaryLinesCrosses(string DateTime, string PerStr) 315 | { 316 | for (int i = 0; i < NumberPHLtoTrack; i++) 317 | { 318 | if ((Last_Ex[i] != EMPTY_VALUE) && (was_alert_phlc[i] != Time[0]) && (((Close[1] > Last_Ex[i]) && (Open[1] <= Last_Ex[i])) || ((Close[1] <= Last_Ex[i]) && (Open[1] > Last_Ex[i])))) 319 | { 320 | string text = "ETV - PHLC Cross "; 321 | if (Open[1] < Close[1]) text += "Up "; 322 | else if (Open[1] > Close[1]) text += "Down "; 323 | text += " " + Symbol() + " @ " + PerStr; 324 | Alert(text); 325 | string obj_name = "PHLCArrow_" + DateTime + IntegerToString(i); 326 | ObjectCreate(0, obj_name, OBJ_ARROW, 0, Time[1], Last_Ex[i]); 327 | ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 200); 328 | ObjectSetInteger(0, obj_name, OBJPROP_COLOR, PHLC_Arrow_Color); 329 | string desc = "Price crossed: " + DoubleToString(Last_Ex[i], Digits()) + " @ " + PerStr; 330 | ObjectSetString(0, obj_name, OBJPROP_TOOLTIP, desc); 331 | ObjectSetString(0, obj_name, OBJPROP_TEXT, desc); 332 | ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); 333 | ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, false); 334 | text += " " + DateTime; 335 | if (SendEmails) SendMail(text, text); 336 | if (SendNotifications) SendNotification(text); 337 | was_alert_phlc[i] = Time[0]; 338 | } 339 | } 340 | } 341 | //+------------------------------------------------------------------+ -------------------------------------------------------------------------------- /EasyTrendVisualizer.mq5: -------------------------------------------------------------------------------- 1 | //+------------------------------------------------------------------+ 2 | //| Easy Trend Visualizer | 3 | //| Copyright © 2025, EarnForex | 4 | //| https://www.earnforex.com/ | 5 | //+------------------------------------------------------------------+ 6 | #property copyright "Copyright © 2025, EarnForex" 7 | #property link "https://www.earnforex.com/metatrader-indicators/EasyTrendVisualizer/" 8 | #property version "1.13" 9 | 10 | #property description "Easy Trend Visualizer - displays trend strength, direction, and support and resistance levels." 11 | 12 | #define Alvl 35.0 13 | #define Alvl2 30.0 14 | 15 | #property indicator_chart_window 16 | #property indicator_buffers 9 17 | #property indicator_plots 4 18 | #property indicator_color1 clrRed, clrSteelBlue 19 | #property indicator_width1 2 20 | #property indicator_color2 clrLime 21 | #property indicator_color3 clrLime 22 | #property indicator_color4 clrIndigo 23 | #property indicator_width2 2 24 | #property indicator_width3 2 25 | #property indicator_width4 1 26 | #property indicator_type1 DRAW_COLOR_HISTOGRAM2 27 | #property indicator_style1 STYLE_SOLID 28 | #property indicator_type2 DRAW_ARROW 29 | #property indicator_style2 STYLE_SOLID 30 | #property indicator_type3 DRAW_ARROW 31 | #property indicator_style3 STYLE_SOLID 32 | #property indicator_type4 DRAW_LINE 33 | #property indicator_style4 STYLE_SOLID 34 | 35 | input int ADXperiod1 = 10; 36 | input int ADXperiod2 = 14; 37 | input int ADXperiod3 = 20; 38 | input bool UseAlertHorizontalLine = false; 39 | input bool UseAlertUpDownArrows = false; 40 | input bool UseAlertHorizontalLineCrossCurrent = false; 41 | input bool UseAlertHorizontalLineCrossPrevious = false; 42 | input int NumberPHLtoTrack = 0; // How many previous horizontal lines to track for alert purposes? 43 | input int IgnorePHLShorterThan = 2; // Ignore previous horizontal lines shorter than 44 | input color PHLC_Arrow_Color = clrChocolate; 45 | input color CHLC_Arrow_Color = clrFireBrick; 46 | input bool NativeAlerts = false; // Use pop-up alerts? 47 | input bool SendEmails = false; // Send alerts via email? 48 | input bool SendNotifications = false; // Send alerts via push notifications? 49 | int MxP, MnP, MdP; 50 | 51 | double was_alert_hl = EMPTY_VALUE; // Horizontal line 52 | double was_alert_au = EMPTY_VALUE; // Arrow up 53 | double was_alert_ad = EMPTY_VALUE; // Arrow down 54 | datetime was_alert_hlcross = 0; // Horizontal line cross 55 | 56 | double To[]; 57 | double Tc[]; 58 | double Color[]; 59 | double Up[]; 60 | double Dn[]; 61 | double Ex[]; 62 | 63 | double ADX1[]; 64 | double ADX2[]; 65 | double ADX3[]; 66 | 67 | double Last_Ex[]; // Buffer to hold previous "last line levels". Required for price/line alert because imaginary line cross also count. 68 | datetime was_alert_phlc[]; // Price crosses and closes above/below previous horizontal line. 69 | int last_trend_for_hl_alert = 0; // 0 - up, 1 - down. 70 | 71 | //+------------------------------------------------------------------+ 72 | //| Custom indicator initialization function | 73 | //+------------------------------------------------------------------+ 74 | void OnInit() 75 | { 76 | MxP = MathMax(MathMax(ADXperiod1, ADXperiod2), ADXperiod3); 77 | MnP = MathMin(MathMin(ADXperiod1, ADXperiod2), ADXperiod3); 78 | if (MxP == ADXperiod1) MdP = MathMax(ADXperiod2, ADXperiod3); 79 | else if (MxP == ADXperiod2) MdP = MathMax(ADXperiod1, ADXperiod3); 80 | else MdP = MathMax(ADXperiod2, ADXperiod1); 81 | 82 | IndicatorSetString(INDICATOR_SHORTNAME, "ETV(" + IntegerToString(MnP) + "/" + IntegerToString(MdP) + "/" + IntegerToString(MxP) + ")"); 83 | 84 | SetIndexBuffer(0, To, INDICATOR_DATA); 85 | SetIndexBuffer(1, Tc, INDICATOR_DATA); 86 | SetIndexBuffer(2, Color, INDICATOR_COLOR_INDEX); 87 | SetIndexBuffer(3, Up, INDICATOR_DATA); 88 | SetIndexBuffer(4, Dn, INDICATOR_DATA); 89 | SetIndexBuffer(5, Ex, INDICATOR_DATA); 90 | SetIndexBuffer(6, ADX1, INDICATOR_CALCULATIONS); 91 | SetIndexBuffer(7, ADX2, INDICATOR_CALCULATIONS); 92 | SetIndexBuffer(8, ADX3, INDICATOR_CALCULATIONS); 93 | 94 | ArraySetAsSeries(To, true); 95 | ArraySetAsSeries(Tc, true); 96 | ArraySetAsSeries(Color, true); 97 | ArraySetAsSeries(Up, true); 98 | ArraySetAsSeries(Dn, true); 99 | ArraySetAsSeries(Ex, true); 100 | ArraySetAsSeries(ADX1, true); 101 | ArraySetAsSeries(ADX2, true); 102 | ArraySetAsSeries(ADX3, true); 103 | 104 | PlotIndexSetInteger(1, PLOT_ARROW, 225); 105 | PlotIndexSetInteger(2, PLOT_ARROW, 226); 106 | 107 | PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MxP); 108 | PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MxP); 109 | PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, MxP); 110 | PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, MxP + 1); 111 | 112 | PlotIndexSetString(1, PLOT_LABEL, "Up"); 113 | PlotIndexSetString(2, PLOT_LABEL, "Down"); 114 | PlotIndexSetString(3, PLOT_LABEL, "End"); 115 | 116 | ArrayResize(Last_Ex, PHLC_Arrow_Color); 117 | ArrayInitialize(Last_Ex, EMPTY_VALUE); 118 | 119 | ArrayResize(was_alert_phlc, PHLC_Arrow_Color); 120 | ArrayInitialize(was_alert_phlc, 0); 121 | } 122 | 123 | //+------------------------------------------------------------------+ 124 | //| Custom indicator deinitialization function | 125 | //+------------------------------------------------------------------+ 126 | void OnDeinit(const int reason) 127 | { 128 | // Delete all alert arrows. 129 | ObjectsDeleteAll(0, "PHLCArrow_", -1, OBJ_ARROW); 130 | ObjectsDeleteAll(0, "CHLCArrow_", -1, OBJ_ARROW); 131 | } 132 | 133 | //+------------------------------------------------------------------+ 134 | //| Custom Easy Trend Visualizer | 135 | //+------------------------------------------------------------------+ 136 | int OnCalculate(const int rates_total, 137 | const int prev_calculated, 138 | const datetime &Time[], 139 | const double &Open[], 140 | const double &high[], 141 | const double &low[], 142 | const double &Close[], 143 | const long &tick_volume[], 144 | const long &volume[], 145 | const int &spread[]) 146 | { 147 | ArraySetAsSeries(Open, true); 148 | ArraySetAsSeries(Close, true); 149 | ArraySetAsSeries(Time, true); 150 | 151 | int limit = rates_total - prev_calculated; 152 | if (prev_calculated == 0) limit -= MxP; 153 | double ADXBuffer1[], ADXBuffer2[], ADXBuffer3[], ADXBuffer1_1[], ADXBuffer1_2[]; 154 | int myADX = iADX(NULL, 0, MnP); 155 | if (CopyBuffer(myADX, MAIN_LINE, 0, rates_total, ADXBuffer1) != rates_total) return(0); 156 | if (CopyBuffer(myADX, PLUSDI_LINE, 0, rates_total, ADXBuffer1_1) != rates_total) return(0); 157 | if (CopyBuffer(myADX, MINUSDI_LINE, 0, rates_total, ADXBuffer1_2) != rates_total) return(0); 158 | myADX = iADX(NULL, 0, MdP); 159 | if (CopyBuffer(myADX, MAIN_LINE, 0, rates_total, ADXBuffer2) != rates_total) return(0); 160 | myADX = iADX(NULL, 0, MxP); 161 | if (CopyBuffer(myADX, MAIN_LINE, 0, rates_total, ADXBuffer3) != rates_total) return(0); 162 | 163 | for (int i = rates_total - 1; i >= 0; i--) 164 | { 165 | ADX1[i] = ADXBuffer1[rates_total - i - 1]; 166 | ADX2[i] = ADXBuffer2[rates_total - i - 1]; 167 | ADX3[i] = ADXBuffer3[rates_total - i - 1]; 168 | } 169 | 170 | if (prev_calculated == 0) limit--; 171 | 172 | if (limit < 1) limit = 2; 173 | for (int i = limit - 1; i >= 0; i--) 174 | { 175 | bool f1 = false, f2 = false, f3 = false; 176 | To[i] = EMPTY_VALUE; 177 | Tc[i] = EMPTY_VALUE; 178 | Up[i] = EMPTY_VALUE; 179 | Dn[i] = EMPTY_VALUE; 180 | Ex[i] = EMPTY_VALUE; 181 | 182 | int k = i + 1; 183 | 184 | // Remember last lines for imaginary crosses. 185 | if ((NumberPHLtoTrack > 0) && (i + 2 < rates_total)) // Check if we fit into arrays. 186 | { 187 | // New line found and it has not yet been included: 188 | // It did not exist on a previous bar, it did exist on the pre-previous bar and the latest recorded line is not it. 189 | if ((Ex[i + 1] == EMPTY_VALUE) && (Ex[i + 2] != EMPTY_VALUE) && (Last_Ex[0] != Ex[i + 2])) 190 | { 191 | // Count the length of the added line. 192 | int length = 1; 193 | for (int j = i + 3; j < rates_total; j++) 194 | { 195 | if (Ex[j] == EMPTY_VALUE) break; 196 | length++; 197 | } 198 | if (length >= IgnorePHLShorterThan) 199 | { 200 | // Shift arrays (FIFO, 0 - newest): 201 | for (int j = NumberPHLtoTrack - 1; j >= 0; j--) 202 | { 203 | // This check is needed for a very special case of chart data not being fully loaded. 204 | // When it is being loaded the indicator is recalculated, but new bars appear from left, not from right as usually. 205 | // This keeps rewriting the Last_Ex array with basically the same values, which can lead to lots of unnecessary alerts. 206 | if (Time[i + 2] < was_alert_phlc[j]) break; 207 | if (j == 0) // Insert new value. 208 | { 209 | Last_Ex[j] = Ex[i + 2]; 210 | was_alert_phlc[j] = 0; 211 | } 212 | else // Shift. 213 | { 214 | Last_Ex[j] = Last_Ex[j - 1]; 215 | was_alert_phlc[j] = was_alert_phlc[j - 1]; 216 | } 217 | } 218 | } 219 | } 220 | } 221 | 222 | if (ADX1[k] < ADX1[i]) f1 = true; 223 | if (ADX2[k] < ADX2[i]) f2 = true; 224 | if (ADX3[k] < ADX3[i]) f3 = true; 225 | 226 | if ((f1) && (f2) && (f3) && (ADX1[i] > Alvl) && (ADX2[i] > Alvl2)) 227 | { 228 | double di = ADXBuffer1_1[rates_total - i - 1] - ADXBuffer1_2[rates_total - i - 1]; 229 | double hi = MathMax(Open[i], Close[i]); 230 | double lo = MathMin(Open[i], Close[i]); 231 | double op = Open[i]; 232 | if (di > 0) 233 | { 234 | To[i] = lo; 235 | Tc[i] = hi; 236 | if (To[k] == EMPTY_VALUE) Up[i] = op; 237 | Color[i] = 1; 238 | 239 | } 240 | else 241 | { 242 | To[i] = hi; 243 | Tc[i] = lo; 244 | if (To[k] == EMPTY_VALUE) Dn[i] = op; 245 | Color[i] = 0; 246 | } 247 | } 248 | else 249 | { 250 | if (To[k] != EMPTY_VALUE) 251 | { 252 | Ex[i] = Close[i + 1]; 253 | } 254 | else Ex[i] = Ex[k]; 255 | } 256 | if (Up[i] != EMPTY_VALUE) last_trend_for_hl_alert = 0; 257 | else if (Dn[i] != EMPTY_VALUE) last_trend_for_hl_alert = 1; 258 | } 259 | 260 | // If at least one type and one source of alerts is defined. 261 | if (((NativeAlerts) || (SendEmails) || (SendNotifications)) && ((UseAlertHorizontalLine) || (UseAlertUpDownArrows) || (UseAlertHorizontalLineCrossCurrent) || (UseAlertHorizontalLineCrossPrevious))) 262 | { 263 | string DateTime = TimeToString(Time[0]); 264 | string PerStr = EnumToString(Period()); 265 | if (UseAlertHorizontalLine) 266 | { 267 | if ((Ex[1] != EMPTY_VALUE) && (Ex[1] != was_alert_hl) && (Ex[1] != Ex[2])) 268 | { 269 | string text = "ETV - HL Start "; 270 | if (last_trend_for_hl_alert == 0) text += "After Uptrend"; 271 | else text += "After Downtrend"; 272 | if (NativeAlerts) Alert(text); 273 | text += " " + Symbol() + " @ " + PerStr; 274 | if (SendEmails) SendMail(text, text); 275 | if (SendNotifications) SendNotification(text); 276 | was_alert_hl = Ex[1]; 277 | } 278 | } 279 | if (UseAlertUpDownArrows) 280 | { 281 | if ((Up[0] != 0) && (Up[0] != was_alert_au)) 282 | { 283 | string text = "ETV - Arrow Up"; 284 | if (NativeAlerts) Alert(text); 285 | text += " " + Symbol() + " @ " + PerStr; 286 | if (SendEmails) SendMail(text, text); 287 | if (SendNotifications) SendNotification(text); 288 | was_alert_au = Up[0]; 289 | } 290 | if ((Dn[0] != 0) && (Dn[0] != was_alert_ad)) 291 | { 292 | string text = "ETV - Arrow Down"; 293 | if (NativeAlerts) Alert(text); 294 | text += " " + Symbol() + " @ " + PerStr; 295 | if (SendEmails) SendMail(text, text); 296 | if (SendNotifications) SendNotification(text); 297 | was_alert_ad = Dn[0]; 298 | } 299 | } 300 | if (UseAlertHorizontalLineCrossCurrent) 301 | { 302 | if ((Ex[1] != EMPTY_VALUE) && (Ex[2] != EMPTY_VALUE) && (was_alert_hlcross != Time[1]) && (((Close[1] > Ex[1]) && (Open[1] <= Ex[1])) || ((Close[1] <= Ex[1]) && (Open[1] > Ex[1])))) 303 | { 304 | string text = "ETV - Current HL Cross "; 305 | if (Open[1] < Close[1]) text += "Up"; 306 | else if (Open[1] > Close[1]) text += "Down"; 307 | if (NativeAlerts) Alert(text); 308 | text += " " + Symbol() + " @ " + PerStr; 309 | if (SendEmails) SendMail(text, text); 310 | if (SendNotifications) SendNotification(text); 311 | was_alert_hlcross = Time[1]; 312 | string obj_name = "CHLCArrow_" + TimeToString(Time[1]); 313 | ObjectCreate(0, obj_name, OBJ_ARROW, 0, Time[1], Ex[1]); 314 | ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 200); 315 | ObjectSetInteger(0, obj_name, OBJPROP_COLOR, CHLC_Arrow_Color); 316 | string desc = "Price crossed: " + DoubleToString(Ex[1], Digits()) + " @ " + PerStr; 317 | ObjectSetString(0, obj_name, OBJPROP_TOOLTIP, desc); 318 | ObjectSetString(0, obj_name, OBJPROP_TEXT, desc); 319 | ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); 320 | ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, false); 321 | } 322 | } 323 | // Alerts for the previous HL crosses. 324 | if ((UseAlertHorizontalLineCrossPrevious) && (NumberPHLtoTrack > 0)) 325 | { 326 | DateTime = TimeToString(Time[1]); 327 | CheckImaginaryLinesCrosses(DateTime, PerStr, Time, Open, Close); 328 | } 329 | } 330 | 331 | return rates_total; 332 | } 333 | 334 | void CheckImaginaryLinesCrosses(const string DateTime, const string PerStr, const datetime &Time[], const double &Open[], const double &Close[]) 335 | { 336 | for (int i = 0; i < NumberPHLtoTrack; i++) 337 | { 338 | if ((Last_Ex[i] != EMPTY_VALUE) && (was_alert_phlc[i] != Time[0]) && (((Close[1] > Last_Ex[i]) && (Open[1] <= Last_Ex[i])) || ((Close[1] <= Last_Ex[i]) && (Open[1] > Last_Ex[i])))) 339 | { 340 | string text = "ETV - PHLC Cross "; 341 | if (Open[1] < Close[1]) text += "Up"; 342 | else if (Open[1] > Close[1]) text += "Down"; 343 | Alert(text); 344 | text += " " + DateTime; 345 | string obj_name = "PHLCArrow_" + DateTime + "-" + IntegerToString(i); 346 | ObjectCreate(0, obj_name, OBJ_ARROW, 0, Time[1], Last_Ex[i]); 347 | ObjectSetInteger(0, obj_name, OBJPROP_ARROWCODE, 200); 348 | ObjectSetInteger(0, obj_name, OBJPROP_COLOR, PHLC_Arrow_Color); 349 | string desc = "Price crossed: " + DoubleToString(Last_Ex[i], Digits()) + " @ " + PerStr; 350 | ObjectSetString(0, obj_name, OBJPROP_TOOLTIP, desc); 351 | ObjectSetString(0, obj_name, OBJPROP_TEXT, desc); 352 | ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false); 353 | ObjectSetInteger(0, obj_name, OBJPROP_HIDDEN, false); 354 | text += " " + Symbol() + " @ " + PerStr; 355 | if (SendEmails) SendMail(text, text); 356 | if (SendNotifications) SendNotification(text); 357 | was_alert_phlc[i] = Time[0]; 358 | } 359 | } 360 | } 361 | //+------------------------------------------------------------------+ --------------------------------------------------------------------------------