EnhancedHourlyHighLow by Naya
638 downloads / 573 views / Created: 07.07.2025 Average Rating: 0
Indicator Description
Calculates hourly high/low with advanced opening/closing logic
Comments
using System;
using System.Drawing;
using ForexStrategyBuilder.Infrastructure.Entities;
using ForexStrategyBuilder.Infrastructure.Enums;
using ForexStrategyBuilder.Infrastructure.Interfaces;
namespace ForexStrategyBuilder.Indicators.Store
{
public class EnhancedHourlyHighLow : Indicator
{
public EnhancedHourlyHighLow()
{
IndicatorName = "Enhanced Hourly High Low";
PossibleSlots = SlotTypes.Open | SlotTypes.OpenFilter | SlotTypes.Close | SlotTypes.CloseFilter;
IndicatorAuthor = "NAYA +237674724684";
IndicatorVersion = "2.0";
IndicatorDescription = "Calculates hourly high/low with advanced opening/closing logic";
}
public override void Initialize(SlotTypes slotType)
{
SlotType = slotType;
// The ComboBox parameters
IndParam.ListParam[0].Caption = "Logic";
if (slotType == SlotTypes.Open)
IndParam.ListParam[0].ItemList = new[]
{
"Enter long at the hourly high",
"Enter long at the hourly low"
};
else if (slotType == SlotTypes.OpenFilter)
IndParam.ListParam[0].ItemList = new[]
{
"The bar opens below the hourly high",
"The bar opens above the hourly high",
"The bar opens below the hourly low",
"The bar opens above the hourly low",
"The bar opens below the hourly high after opening above it",
"The bar opens above the hourly high after opening below it",
"The bar opens below the hourly low after opening above it",
"The bar opens above the hourly low after opening below it",
"the hourly high rises",
"the hourly high falls",
"the hourly low rises",
"the hourly low falls"
};
else if (slotType == SlotTypes.Close)
IndParam.ListParam[0].ItemList = new[]
{
"Exit long at the hourly high",
"Exit long at the hourly low"
};
else if (slotType == SlotTypes.CloseFilter)
IndParam.ListParam[0].ItemList = new[]
{
"The bar closes below the hourly high",
"The bar closes above the hourly high",
"The bar closes below the hourly low",
"The bar closes above the hourly low",
"the hourly high rises",
"the hourly high falls",
"the hourly low rises",
"the hourly low falls"
};
else
IndParam.ListParam[0].ItemList = new[] { "Not Defined" };
IndParam.ListParam[0].Index = 0;
IndParam.ListParam[0].Text = IndParam.ListParam[0].ItemList[0];
IndParam.ListParam[0].Enabled = true;
IndParam.ListParam[0].ToolTip = "Logic of application of the indicator.";
// The NumericUpDown parameters
IndParam.NumParam[0].Caption = "Start hour (incl.)";
IndParam.NumParam[0].Value = 1;
IndParam.NumParam[0].Min = 0;
IndParam.NumParam[0].Max = 24;
IndParam.NumParam[0].Enabled = true;
IndParam.NumParam[0].ToolTip = "The starting hour of the period.";
IndParam.NumParam[1].Caption = "Start minutes (incl.)";
IndParam.NumParam[1].Value = 0;
IndParam.NumParam[1].Min = 0;
IndParam.NumParam[1].Max = 59;
IndParam.NumParam[1].Enabled = true;
IndParam.NumParam[1].ToolTip = "The starting minutes of the period.";
IndParam.NumParam[2].Caption = "End hour (excl.)";
IndParam.NumParam[2].Value = 24;
IndParam.NumParam[2].Min = 0;
IndParam.NumParam[2].Max = 24;
IndParam.NumParam[2].Enabled = true;
IndParam.NumParam[2].ToolTip = "The ending hour of the period.";
IndParam.NumParam[3].Caption = "End minutes (excl.)";
IndParam.NumParam[3].Value = 0;
IndParam.NumParam[3].Min = 0;
IndParam.NumParam[3].Max = 59;
IndParam.NumParam[3].Enabled = true;
IndParam.NumParam[3].ToolTip = "The ending minutes of the period.";
IndParam.NumParam[4].Caption = "Vertical shift";
IndParam.NumParam[4].Value = 0;
IndParam.NumParam[4].Min = -2000;
IndParam.NumParam[4].Max = +2000;
IndParam.NumParam[4].Enabled = true;
IndParam.NumParam[4].ToolTip = "A vertical shift above the high and below the low price.";
}
public override void Calculate(IDataSet dataSet)
{
DataSet = dataSet;
var fromHour = (int)IndParam.NumParam[0].Value;
var fromMin = (int)IndParam.NumParam[1].Value;
var toHour = (int)IndParam.NumParam[2].Value;
var toMin = (int)IndParam.NumParam[3].Value;
double shift = IndParam.NumParam[4].Value * Point;
var fromTime = new TimeSpan(fromHour, fromMin, 0);
var toTime = new TimeSpan(toHour, toMin, 0);
const int firstBar = 2;
// Calculation
var highPrice = new double[Bars];
var lowPrice = new double[Bars];
double minPrice = double.MaxValue;
double maxPrice = double.MinValue;
highPrice[0] = 0;
lowPrice[0] = 0;
bool isOnTimePrev = false;
for (int bar = 1; bar < Bars; bar++)
{
bool isOnTime;
TimeSpan barTime = Time[bar].TimeOfDay;
if (fromTime < toTime)
isOnTime = barTime >= fromTime && barTime < toTime;
else if (fromTime > toTime)
isOnTime = barTime >= fromTime || barTime < toTime;
else
isOnTime = barTime != toTime;
if (isOnTime)
{
if (maxPrice < High[bar]) maxPrice = High[bar];
if (minPrice > Low[bar]) minPrice = Low[bar];
}
if (!isOnTime && isOnTimePrev)
{
highPrice[bar] = maxPrice + shift;
lowPrice[bar] = minPrice - shift;
maxPrice = double.MinValue;
minPrice = double.MaxValue;
}
else
{
highPrice[bar] = highPrice[bar - 1];
lowPrice[bar] = lowPrice[bar - 1];
}
isOnTimePrev = isOnTime;
}
// Create components
Component = new IndicatorComp[4];
Component[0] = new IndicatorComp
{
CompName = "Hourly High",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Level,
ChartColor = Color.DarkGreen,
FirstBar = firstBar,
Value = highPrice
};
Component[1] = new IndicatorComp
{
CompName = "Hourly Low",
DataType = IndComponentType.IndicatorValue,
ChartType = IndChartType.Level,
ChartColor = Color.DarkRed,
FirstBar = firstBar,
Value = lowPrice
};
Component[2] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = firstBar,
Value = new double[Bars]
};
Component[3] = new IndicatorComp
{
ChartType = IndChartType.NoChart,
FirstBar = firstBar,
Value = new double[Bars]
};
// Set component types based on slot
if (SlotType == SlotTypes.Open)
{
Component[2].DataType = IndComponentType.OpenLongPrice;
Component[2].CompName = "Long position entry price";
Component[3].DataType = IndComponentType.OpenShortPrice;
Component[3].CompName = "Short position entry price";
}
else if (SlotType == SlotTypes.OpenFilter)
{
Component[2].DataType = IndComponentType.AllowOpenLong;
Component[2].CompName = "Is long entry allowed";
Component[3].DataType = IndComponentType.AllowOpenShort;
Component[3].CompName = "Is short entry allowed";
}
else if (SlotType == SlotTypes.Close)
{
Component[2].DataType = IndComponentType.CloseLongPrice;
Component[2].CompName = "Long position closing price";
Component[3].DataType = IndComponentType.CloseShortPrice;
Component[3].CompName = "Short position closing price";
}
else if (SlotType == SlotTypes.CloseFilter)
{
Component[2].DataType = IndComponentType.ForceCloseLong;
Component[2].CompName = "Close out long position";
Component[3].DataType = IndComponentType.ForceCloseShort;
Component[3].CompName = "Close out short position";
}
// Apply the selected logic
string logic = IndParam.ListParam[0].Text;
if (SlotType == SlotTypes.Open || SlotType == SlotTypes.Close)
{
if (logic == "Enter long at the hourly high" || logic == "Exit long at the hourly high")
{
Component[2].Value = highPrice;
Component[3].Value = lowPrice;
}
else
{
Component[2].Value = lowPrice;
Component[3].Value = highPrice;
}
}
else
{
switch (logic)
{
case "The bar opens below the hourly high":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_below_the_Upper_Band);
break;
case "The bar opens above the hourly high":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_above_the_Upper_Band);
break;
case "The bar opens below the hourly low":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_below_the_Lower_Band);
break;
case "The bar opens above the hourly low":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_above_the_Lower_Band);
break;
case "The bar opens below the hourly high after opening above it":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_below_the_Upper_Band_after_opening_above_it);
break;
case "The bar opens above the hourly high after opening below it":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_above_the_Upper_Band_after_opening_below_it);
break;
case "The bar opens below the hourly low after opening above it":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_below_the_Lower_Band_after_opening_above_it);
break;
case "The bar opens above the hourly low after opening below it":
BandIndicatorLogic(firstBar, 0, highPrice, lowPrice,
ref Component[2], ref Component[3],
BandIndLogic.The_bar_opens_above_the_Lower_Band_after_opening_below_it);
break;
case "the hourly high rises":
IndicatorRisesLogic(firstBar, 0, highPrice,
ref Component[2], ref Component[3]);
break;
case "the hourly high falls":
IndicatorFallsLogic(firstBar, 0, highPrice,
ref Component[2], ref Component[3]);
break;
case "the hourly low rises":
IndicatorRisesLogic(firstBar, 0, lowPrice,
ref Component[2], ref Component[3]);
break;
case "the hourly low falls":
IndicatorFallsLogic(firstBar, 0, lowPrice,
ref Component[2], ref Component[3]);
break;
}
}
}
public override void SetDescription()
{
string fromTime = IndParam.NumParam[0].Value.ToString("00") + ":" +
IndParam.NumParam[1].Value.ToString("00");
string toTime = IndParam.NumParam[2].Value.ToString("00") + ":" +
IndParam.NumParam[3].Value.ToString("00");
string shift = IndParam.NumParam[4].ValueToString;
string timeRange = "({fromTime}-{toTime}, shift {shift} pts)";
string logic = IndParam.ListParam[0].Text;
switch (logic)
{
case "Enter long at the hourly high":
EntryPointLongDescription = "at hourly high {timeRange}";
EntryPointShortDescription = "at hourly low {timeRange}";
break;
case "Enter long at the hourly low":
EntryPointLongDescription = "at hourly low {timeRange}";
EntryPointShortDescription = "at hourly high {timeRange}";
break;
case "Exit long at the hourly high":
ExitPointLongDescription = "at hourly high {timeRange}";
ExitPointShortDescription = "at hourly low {timeRange}";
break;
case "Exit long at the hourly low":
ExitPointLongDescription = "at hourly low {timeRange}";
ExitPointShortDescription = "at hourly high {timeRange}";
break;
case "The bar opens below the hourly high":
EntryFilterLongDescription = "bar opens below hourly high {timeRange}";
EntryFilterShortDescription = "bar opens above hourly low {timeRange}";
break;
case "The bar opens above the hourly high":
EntryFilterLongDescription = "bar opens above hourly high {timeRange}";
EntryFilterShortDescription = "bar opens below hourly low {timeRange}";
break;
case "The bar opens below the hourly low":
EntryFilterLongDescription = "bar opens below hourly low {timeRange}";
EntryFilterShortDescription = "bar opens above hourly high {timeRange}";
break;
case "The bar opens above the hourly low":
EntryFilterLongDescription = "bar opens above hourly low {timeRange}";
EntryFilterShortDescription = "bar opens below hourly high {timeRange}";
break;
case "The bar opens below the hourly high after opening above it":
EntryFilterLongDescription = "bar opens below hourly high {timeRange} after previous opened above";
EntryFilterShortDescription = "bar opens above hourly low {timeRange} after previous opened below";
break;
case "The bar opens above the hourly high after opening below it":
EntryFilterLongDescription = "bar opens above hourly high {timeRange} after previous opened below";
EntryFilterShortDescription = "bar opens below hourly low {timeRange} after previous opened above";
break;
case "The bar opens below the hourly low after opening above it":
EntryFilterLongDescription = "bar opens below hourly low {timeRange} after previous opened above";
EntryFilterShortDescription = "bar opens above hourly high {timeRange} after previous opened below";
break;
case "The bar opens above the hourly low after opening below it":
EntryFilterLongDescription = "bar opens above hourly low {timeRange} after previous opened below";
EntryFilterShortDescription = "bar opens below hourly high {timeRange} after previous opened above";
break;
case "the hourly high rises":
EntryFilterLongDescription = "hourly high rises {timeRange}";
EntryFilterShortDescription = "hourly low falls {timeRange}";
break;
case "the hourly high falls":
EntryFilterLongDescription = "hourly high falls {timeRange}";
EntryFilterShortDescription = "hourly low rises {timeRange}";
break;
case "the hourly low rises":
EntryFilterLongDescription = "hourly low rises {timeRange}";
EntryFilterShortDescription = "hourly high falls {timeRange}";
break;
case "the hourly low falls":
EntryFilterLongDescription = "hourly low falls {timeRange}";
EntryFilterShortDescription = "hourly high rises {timeRange}";
break;
}
}
public override string ToString()
{
string fromTime = IndParam.NumParam[0].Value.ToString("00") + ":" +
IndParam.NumParam[1].Value.ToString("00");
string toTime = IndParam.NumParam[2].Value.ToString("00") + ":" +
IndParam.NumParam[3].Value.ToString("00");
string shift = IndParam.NumParam[4].ValueToString;
return "{IndicatorName} ({fromTime}-{toTime}, shift {shift} pts)";
}
}
}
//+------------------------------------------------------------------+ //| EnhancedHourlyHighLowWithAdvancedLogic.mqh | //| Copyright (C) 2025, NAYA +237674724684 | //| http://forexsb.com | //+------------------------------------------------------------------+ #property copyright "Copyright © 2025, NAYA +237674724684" #property link "http://forexsb.com" #property version "2.00" #property strict #include <Forexsb.com/Indicator.mqh> #include <Forexsb.com/Enumerations.mqh> //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ class EnhancedHourlyHighLow : public Indicator { public: EnhancedHourlyHighLow(SlotTypes slotType) { SlotType=slotType; IndicatorName="Enhanced Hourly High Low"; WarningMessage = ""; IsAllowLTF = true; ExecTime = ExecutionTime_DuringTheBar; IsSeparateChart = false; IsDiscreteValues = false; IsDefaultGroupAll = false; } virtual void Calculate(DataSet &dataSet); }; //+------------------------------------------------------------------+ //| | //+------------------------------------------------------------------+ void EnhancedHourlyHighLow::Calculate(DataSet &dataSet) { Data=GetPointer(dataSet); int fromHour = (int) NumParam[0].Value; int fromMin = (int) NumParam[1].Value; int toHour = (int) NumParam[2].Value; int toMin = (int) NumParam[3].Value; double shift = NumParam[4].Value * Data.Point; int fromTime = fromHour*60 + fromMin; int toTime = toHour*60 + toMin; const int firstBar=2; double adHighPrice[]; ArrayResize(adHighPrice,Data.Bars); ArrayInitialize(adHighPrice,0); double adLowPrice[]; ArrayResize(adLowPrice,Data.Bars); ArrayInitialize(adLowPrice,0); double dMinPrice = DBL_MAX; double dMaxPrice = DBL_MIN; adHighPrice[0] = 0; adLowPrice[0] = 0; bool isOnTimePrev=false; for(int bar=1; bar<Data.Bars; bar++) { bool isOnTime; MqlDateTime mqlTime; TimeToStruct(Data.Time[bar],mqlTime); int barTime=mqlTime.hour*60+mqlTime.min; if(fromTime<toTime) isOnTime=barTime>=fromTime && barTime<toTime; else if(fromTime>toTime) isOnTime=barTime>=fromTime || barTime<toTime; else isOnTime=barTime!=toTime; if(isOnTime) { if(dMaxPrice < Data.High[bar]) dMaxPrice = Data.High[bar]; if(dMinPrice > Data.Low[bar]) dMinPrice = Data.Low[bar]; } if(!isOnTime && isOnTimePrev) { adHighPrice[bar]= dMaxPrice + shift; adLowPrice[bar] = dMinPrice - shift; dMaxPrice = DBL_MIN; dMinPrice = DBL_MAX; } else { adHighPrice[bar] = adHighPrice[bar - 1]; adLowPrice[bar] = adLowPrice[bar - 1]; } isOnTimePrev=isOnTime; } // Create components array ArrayResize(Component,4); // Component[0]: Hourly High ArrayResize(Component[0].Value,Data.Bars); Component[0].CompName = "Hourly High"; Component[0].DataType = IndComponentType_IndicatorValue; Component[0].FirstBar = firstBar; ArrayCopy(Component[0].Value,adHighPrice); // Component[1]: Hourly Low ArrayResize(Component[1].Value,Data.Bars); Component[1].CompName = "Hourly Low"; Component[1].DataType = IndComponentType_IndicatorValue; Component[1].FirstBar = firstBar; ArrayCopy(Component[1].Value,adLowPrice); // Component[2] & [3] placeholders ArrayResize(Component[2].Value,Data.Bars); ArrayInitialize(Component[2].Value,0); Component[2].FirstBar = firstBar; ArrayResize(Component[3].Value,Data.Bars); ArrayInitialize(Component[3].Value,0); Component[3].FirstBar = firstBar; // Assign data types based on slot if(SlotType==SlotTypes_Open) { Component[2].DataType = IndComponentType_OpenLongPrice; Component[2].CompName = "Long position entry price"; Component[3].DataType = IndComponentType_OpenShortPrice; Component[3].CompName = "Short position entry price"; } else if(SlotType==SlotTypes_OpenFilter) { Component[2].DataType = IndComponentType_AllowOpenLong; Component[2].CompName = "Is long entry allowed"; Component[3].DataType = IndComponentType_AllowOpenShort; Component[3].CompName = "Is short entry allowed"; } else if(SlotType==SlotTypes_Close) { Component[2].DataType = IndComponentType_CloseLongPrice; Component[2].CompName = "Long position closing price"; Component[3].DataType = IndComponentType_CloseShortPrice; Component[3].CompName = "Short position closing price"; } else if(SlotType==SlotTypes_CloseFilter) { Component[2].DataType = IndComponentType_ForceCloseLong; Component[2].CompName = "Close out long position"; Component[3].DataType = IndComponentType_ForceCloseShort; Component[3].CompName = "Close out short position"; } // Apply the selected logic string logic = ListParam[0].Text; if(SlotType==SlotTypes_Open || SlotType==SlotTypes_Close) { if(logic=="Enter long at the hourly high" || logic=="Exit long at the hourly high") { ArrayCopy(Component[2].Value,adHighPrice); ArrayCopy(Component[3].Value,adLowPrice); } else { ArrayCopy(Component[2].Value,adLowPrice); ArrayCopy(Component[3].Value,adHighPrice); } } else { if(logic=="The bar opens below the hourly high") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_below_the_Upper_Band); else if(logic=="The bar opens above the hourly high") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_above_the_Upper_Band); else if(logic=="The bar opens below the hourly low") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_below_the_Lower_Band); else if(logic=="The bar opens above the hourly low") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_above_the_Lower_Band); else if(logic=="The bar opens below the hourly high after opening above it") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_below_Upper_Band_after_above); else if(logic=="The bar opens above the hourly high after opening below it") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_above_Upper_Band_after_below); else if(logic=="The bar opens below the hourly low after opening above it") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_below_Lower_Band_after_above); else if(logic=="The bar opens above the hourly low after opening below it") BandIndicatorLogic(firstBar,0,adHighPrice,adLowPrice,Component[2],Component[3],BandIndLogic_The_bar_opens_above_Lower_Band_after_below); else if(logic=="the hourly high rises") IndicatorRisesLogic(firstBar,0,adHighPrice,Component[2],Component[3]); else if(logic=="the hourly high falls") IndicatorFallsLogic(firstBar,0,adHighPrice,Component[2],Component[3]); else if(logic=="the hourly low rises") IndicatorRisesLogic(firstBar,0,adLowPrice,Component[2],Component[3]); else if(logic=="the hourly low falls") IndicatorFallsLogic(firstBar,0,adLowPrice,Component[2],Component[3]); } } //+------------------------------------------------------------------+
Risk warning: Forex, spread bets and CFD are leveraged products. They may not be suitable for you as they carry a high degree of risk to your capital and you can lose more than your initial investment. You should ensure you understand all of the risks.
Copyright © 2006 - 2025, Forex Software Ltd.;
Copyright © 2006 - 2025, Forex Software Ltd.;