Trendline Indicator Pro by Naya

3 downloads / 18 views / Created: 29.10.2025
 Average Rating: 0

Indicator Description

Simple Trendlines

Comments

using System; using System.Drawing; using System.Runtime.CompilerServices; using ForexStrategyBuilder.Infrastructure.Entities; using ForexStrategyBuilder.Infrastructure.Enums; using ForexStrategyBuilder.Infrastructure.Interfaces; namespace ForexStrategyBuilder.Indicators.Store { public class TrendlineIndicatorPro : Indicator { private const bool DEBUG = false; private const double MIN_ANGLE = 15.0; private const double MAX_ANGLE = 60.0; private const double EPSILON = 1e-8; private const int MIN_BARS_REQUIRED = 20; private static readonly double MIN_SLOPE = Math.Tan(MIN_ANGLE * Math.PI / 180.0); private static readonly double MAX_SLOPE = Math.Tan(MAX_ANGLE * Math.PI / 180.0); private double[] swingHighs; private int[] swingHighBars; private double[] swingLows; private int[] swingLowBars; private int[] swingHighConfirmationBars; private int[] swingLowConfirmationBars; private int swingHighCount; private int swingLowCount; private PendingCandidate[] pendingHighs; private PendingCandidate[] pendingLows; private int pendingHighCount; private int pendingLowCount; private struct PendingCandidate { public int Index; public double Value; public int Remaining; public bool Invalidated; public void Reset(int index, double value, int remaining) { Index = index; Value = value; Remaining = remaining + 1; Invalidated = false; } } public TrendlineIndicatorPro() { IndicatorName = "Trendline Indicator Pro"; PossibleSlots = SlotTypes.Open | SlotTypes.OpenFilter | SlotTypes.Close | SlotTypes.CloseFilter; IndicatorAuthor = "NAYA"; IndicatorVersion = "3.0"; IndicatorDescription = "Non-look-ahead trendline indicator with persistent lines. Production hardened with error handling and validations."; } public override void Initialize(SlotTypes slotType) { SlotType = slotType; IndParam.ListParam[0].Caption = "Logic"; if (slotType == SlotTypes.Open) { IndParam.ListParam[0].ItemList = new[] { "Enter long at bullish trendline", "Enter long at bearish trendline" }; } else if (slotType == SlotTypes.OpenFilter) { IndParam.ListParam[0].ItemList = new[] { "Price is above bullish trendline", "Price is below bullish trendline", "Price is above bearish trendline", "Price is below bearish trendline", "Price breaks above bullish trendline", "Price breaks below bullish trendline", "Price breaks above bearish trendline", "Price breaks below bearish trendline" }; } else if (slotType == SlotTypes.Close) { IndParam.ListParam[0].ItemList = new[] { "Exit long at bullish trendline", "Exit long at bearish trendline" }; } else if (slotType == SlotTypes.CloseFilter) { IndParam.ListParam[0].ItemList = new[] { "Price is above bullish trendline", "Price is below bullish trendline", "Price is above bearish trendline", "Price is below bearish trendline", "Price breaks above bullish trendline", "Price breaks below bullish trendline", "Price breaks above bearish trendline", "Price breaks below bearish trendline" }; } 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 = "Trendline logic"; IndParam.ListParam[1].Caption = "Vertical Shift"; var verticalShift = new System.Collections.Generic.List(); for (int i = -2000; i <= 2000; i += 20) verticalShift.Add(i.ToString()); IndParam.ListParam[1].ItemList = verticalShift.ToArray(); IndParam.ListParam[1].Index = 100; // Default to 0 IndParam.ListParam[1].Text = IndParam.ListParam[1].ItemList[IndParam.ListParam[1].Index]; IndParam.ListParam[1].Enabled = true; IndParam.ListParam[1].ToolTip = "The vertical shift in steps of 200 points."; IndParam.NumParam[0].Caption = "Swing Bars"; IndParam.NumParam[0].Value = 5; IndParam.NumParam[0].Min = 1; IndParam.NumParam[0].Max = 15; IndParam.NumParam[0].Enabled = true; IndParam.NumParam[0].ToolTip = "Bars required to confirm swing high/low"; IndParam.NumParam[1].Caption = "Trend Shift"; IndParam.NumParam[1].Value = 1; IndParam.NumParam[1].Min = 1; IndParam.NumParam[1].Max = 3; IndParam.NumParam[1].Enabled = true; IndParam.NumParam[1].ToolTip = "Which swing points to connect (1=latest, 2=previous, etc.)"; IndParam.CheckParam[0].Caption = "Use previous bar value"; IndParam.CheckParam[0].Checked = true; IndParam.CheckParam[0].Enabled = true; IndParam.CheckParam[0].ToolTip = "If checked, the indicator will use the previous bar's value (shifted by one)."; } public override void Calculate(IDataSet dataSet) { try { ValidateInputData(dataSet); DataSet = dataSet; ValidateParameters(); CalculateTrendlinesSafely(); } catch (ArgumentException) { LogError("Invalid argument provided"); InitializeDefaultComponents(); throw; } catch (InvalidOperationException) { LogError("Invalid operation occurred"); InitializeDefaultComponents(); throw; } catch (Exception ex) { LogError("Unexpected error: " + ex.Message); InitializeDefaultComponents(); throw new InvalidOperationException("Trendline calculation failed: " + ex.Message, ex); } } private void ValidateInputData(IDataSet dataSet) { if (dataSet == null) throw new ArgumentNullException("dataSet", "DataSet cannot be null"); int swingBars = Math.Max(1, Math.Min((int)IndParam.NumParam[0].Value, 20)); int minRequired = Math.Max(MIN_BARS_REQUIRED, swingBars * 2 + 2); if (dataSet.Bars < minRequired) throw new ArgumentException("Insufficient data: need at least " + minRequired + " bars, got " + dataSet.Bars); for (int i = 0; i < Math.Min(dataSet.Bars, 50); i++) { ValidateOHLCBar(i, dataSet); } } private void ValidateOHLCBar(int index, IDataSet dataSet) { var open = dataSet.Open[index]; var high = dataSet.High[index]; var low = dataSet.Low[index]; var close = dataSet.Close[index]; if (!IsValidPrice(open) || !IsValidPrice(high) || !IsValidPrice(low) || !IsValidPrice(close)) throw new ArgumentException("Invalid price data at bar " + index); if (high < low - EPSILON || high < Math.Max(open, close) - EPSILON || low > Math.Min(open, close) + EPSILON) throw new ArgumentException("OHLC relationship violation at bar " + index); if (open <= 0 || high <= 0 || low <= 0 || close <= 0) throw new ArgumentException("Non-positive prices at bar " + index); } private bool IsValidPrice(double price) { return !double.IsNaN(price) && !double.IsInfinity(price) && price > 0; } private void ValidateParameters() { if (IndParam.ListParam == null || IndParam.ListParam.Length < 2) throw new InvalidOperationException("List parameters not initialized"); if (IndParam.NumParam == null || IndParam.NumParam.Length < 2) throw new InvalidOperationException("Numeric parameters not initialized"); var swingBars = IndParam.NumParam[0].Value; if (swingBars < IndParam.NumParam[0].Min || swingBars > IndParam.NumParam[0].Max) throw new ArgumentOutOfRangeException("swingBars", "Swing Bars " + swingBars + " out of range"); var trendShift = IndParam.NumParam[1].Value; if (trendShift < IndParam.NumParam[1].Min || trendShift > IndParam.NumParam[1].Max) throw new ArgumentOutOfRangeException("trendShift", "Trend Shift " + trendShift + " out of range"); } private void CalculateTrendlinesSafely() { try { int totalBars = Bars; int swingBars = Math.Max(1, Math.Min((int)IndParam.NumParam[0].Value, 20)); int barShift = Math.Max(1, Math.Min((int)IndParam.NumParam[1].Value, 3)); bool usePrevious = IndParam.CheckParam[0].Checked; int __vshift_int = 0; if (!int.TryParse(IndParam.ListParam[1].Text, out __vshift_int)) __vshift_int = 0; double vShift = __vshift_int * Point; InitializeArraysSafely(totalBars, swingBars); CalculateAllSwingsOptimized(swingBars, totalBars); int logicIndex = IndParam.ListParam[0].Index; int firstBar = swingBars * 2 + 1; InitializeComponentsSafely(firstBar); double[] bullishValues = new double[totalBars]; double[] bearishValues = new double[totalBars]; int lastBullishS1 = -1, lastBullishS2 = -1; int lastBearishS1 = -1, lastBearishS2 = -1; for (int bar = firstBar; bar < totalBars; bar++) { if (!IsValidBarForSwingDetection(bar)) continue; int currentBullishS1 = -1, currentBullishS2 = -1; int foundBullish = 0; for (int i = swingLowCount - 1; i >= 0 && foundBullish < barShift + 1; i--) { if (swingLowConfirmationBars[i] <= bar) { if (foundBullish == barShift - 1) currentBullishS1 = i; else if (foundBullish == barShift) currentBullishS2 = i; foundBullish++; } } if (foundBullish >= barShift + 1 && currentBullishS1 >= 0 && currentBullishS2 >= 0) { double p1 = swingLows[currentBullishS1]; double p2 = swingLows[currentBullishS2]; int b1 = swingLowBars[currentBullishS1]; int b2 = swingLowBars[currentBullishS2]; if (p1 > p2 + EPSILON && ValidateSlopeOptimized(p2, p1, b2, b1)) { lastBullishS1 = currentBullishS1; lastBullishS2 = currentBullishS2; } } if (lastBullishS1 >= 0 && lastBullishS2 >= 0) { double p1 = swingLows[lastBullishS1]; double p2 = swingLows[lastBullishS2]; int b1 = swingLowBars[lastBullishS1]; int b2 = swingLowBars[lastBullishS2]; bullishValues[bar] = CalculateTrendlineValueOptimized(p2, p1, b2, b1, bar); } int currentBearishS1 = -1, currentBearishS2 = -1; int foundBearish = 0; for (int i = swingHighCount - 1; i >= 0 && foundBearish < barShift + 1; i--) { if (swingHighConfirmationBars[i] <= bar) { if (foundBearish == barShift - 1) currentBearishS1 = i; else if (foundBearish == barShift) currentBearishS2 = i; foundBearish++; } } if (foundBearish >= barShift + 1 && currentBearishS1 >= 0 && currentBearishS2 >= 0) { double p1 = swingHighs[currentBearishS1]; double p2 = swingHighs[currentBearishS2]; int b1 = swingHighBars[currentBearishS1]; int b2 = swingHighBars[currentBearishS2]; if (p1 < p2 - EPSILON && ValidateSlopeOptimized(p2, p1, b2, b1)) { lastBearishS1 = currentBearishS1; lastBearishS2 = currentBearishS2; } } if (lastBearishS1 >= 0 && lastBearishS2 >= 0) { double p1 = swingHighs[lastBearishS1]; double p2 = swingHighs[lastBearishS2]; int b1 = swingHighBars[lastBearishS1]; int b2 = swingHighBars[lastBearishS2]; bearishValues[bar] = CalculateTrendlineValueOptimized(p2, p1, b2, b1, bar); } } // Apply vertical shift: subtract from bullish (support), add to bearish (resistance) for (int i = 0; i < totalBars; i++) { if (bullishValues[i] > 0) bullishValues[i] -= vShift; if (bearishValues[i] > 0) bearishValues[i] += vShift; } if (usePrevious) { for (int bar = totalBars - 1; bar > 0; bar--) { bullishValues[bar] = bullishValues[bar - 1]; bearishValues[bar] = bearishValues[bar - 1]; } bullishValues[0] = 0; bearishValues[0] = 0; } Component[0].Value = bullishValues; Component[1].Value = bearishValues; ApplyLogicOptimized(firstBar, totalBars, bullishValues, bearishValues, logicIndex); if (DEBUG) Console.WriteLine("[TrendlineIndicatorPro] Calculation finished: highs={0}, lows={1}", swingHighCount, swingLowCount); } catch (Exception) { LogError("Trendline calculation process failed"); InitializeDefaultComponents(); throw; } } private void ApplyLogicOptimized(int firstBar, int totalBars, double[] bullishValues, double[] bearishValues, int logicIndex) { try { for (int bar = firstBar; bar < totalBars; bar++) { ApplyLogicForBar(bar, bullishValues, bearishValues, logicIndex); } } catch (Exception) { LogError("Logic application failed"); throw; } } private void ApplyLogicForBar(int bar, double[] bullishValues, double[] bearishValues, int logicIndex) { if (Component == null || Component.Length < 4 || bar < 0 || bar >= Bars || Component[2] == null || Component[2].Value == null || Component[3] == null || Component[3].Value == null) return; if (!IsValidBarForSwingDetection(bar)) return; switch (SlotType) { case SlotTypes.Open: case SlotTypes.Close: ApplyOpenCloseLogic(bar, bullishValues[bar], bearishValues[bar], logicIndex); break; case SlotTypes.OpenFilter: case SlotTypes.CloseFilter: ApplyFilterLogic(bar, bullishValues, bearishValues, logicIndex); break; } } private void ApplyOpenCloseLogic(int bar, double bullish, double bearish, int logicIndex) { if (logicIndex == 0) { Component[2].Value[bar] = bullish > 0 ? bullish : Close[bar]; Component[3].Value[bar] = bearish > 0 ? bearish : Close[bar]; } else { Component[2].Value[bar] = bearish > 0 ? bearish : Close[bar]; Component[3].Value[bar] = bullish > 0 ? bullish : Close[bar]; } } private void ApplyFilterLogic(int bar, double[] bullishValues, double[] bearishValues, int logicIndex) { Component[2].Value[bar] = 0; Component[3].Value[bar] = 0; if (!IsValidBarForSwingDetection(bar)) return; double currentPrice = (SlotType == SlotTypes.OpenFilter) ? Open[bar] : Close[bar]; double currentBullish = bullishValues[bar]; double currentBearish = bearishValues[bar]; switch (logicIndex) { case 0: Component[2].Value[bar] = (currentBullish > 0 && currentPrice > currentBullish + EPSILON) ? 1 : 0; Component[3].Value[bar] = (currentBearish > 0 && currentPrice < currentBearish - EPSILON) ? 1 : 0; break; case 1: Component[2].Value[bar] = (currentBullish > 0 && currentPrice < currentBullish - EPSILON) ? 1 : 0; Component[3].Value[bar] = (currentBearish > 0 && currentPrice > currentBearish + EPSILON) ? 1 : 0; break; case 2: Component[2].Value[bar] = (currentBearish > 0 && currentPrice > currentBearish + EPSILON) ? 1 : 0; Component[3].Value[bar] = (currentBullish > 0 && currentPrice < currentBullish - EPSILON) ? 1 : 0; break; case 3: Component[2].Value[bar] = (currentBearish > 0 && currentPrice < currentBearish - EPSILON) ? 1 : 0; Component[3].Value[bar] = (currentBullish > 0 && currentPrice > currentBullish + EPSILON) ? 1 : 0; break; case 4: Component[2].Value[bar] = IsBreak(bar, bullishValues, true) ? 1 : 0; Component[3].Value[bar] = IsBreak(bar, bearishValues, false) ? 1 : 0; break; case 5: Component[2].Value[bar] = IsBreak(bar, bullishValues, false) ? 1 : 0; Component[3].Value[bar] = IsBreak(bar, bearishValues, true) ? 1 : 0; break; case 6: Component[2].Value[bar] = IsBreak(bar, bearishValues, true) ? 1 : 0; Component[3].Value[bar] = IsBreak(bar, bullishValues, false) ? 1 : 0; break; case 7: Component[2].Value[bar] = IsBreak(bar, bearishValues, false) ? 1 : 0; Component[3].Value[bar] = IsBreak(bar, bullishValues, true) ? 1 : 0; break; default: break; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsBreak(int bar, double[] trendlineValues, bool breakAbove) { if (bar < 1) return false; double priceNow = (SlotType == SlotTypes.OpenFilter) ? Open[bar] : Close[bar]; double pricePrev = bar > 0 ? ((SlotType == SlotTypes.OpenFilter) ? Open[bar - 1] : Close[bar - 1]) : 0; double indicatorNow = trendlineValues[bar]; double indicatorPrev = trendlineValues[bar - 1]; if (indicatorNow <= 0 || indicatorPrev <= 0) return false; if (breakAbove) { return priceNow > indicatorNow + EPSILON && pricePrev <= indicatorPrev + EPSILON; } else { return priceNow < indicatorNow - EPSILON && pricePrev >= indicatorPrev - EPSILON; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void InitializeArraysSafely(int totalBars, int swingBars) { try { int maxSwings = totalBars / Math.Max(1, swingBars) + 10; if (swingHighs == null || swingHighs.Length < maxSwings) { swingHighs = new double[maxSwings]; swingHighBars = new int[maxSwings]; swingHighConfirmationBars = new int[maxSwings]; swingLows = new double[maxSwings]; swingLowBars = new int[maxSwings]; swingLowConfirmationBars = new int[maxSwings]; } int pendingCap = Math.Min(totalBars, Math.Max(32, swingBars * 4 + 32)); if (pendingHighs == null || pendingHighs.Length < pendingCap) { pendingHighs = new PendingCandidate[pendingCap]; pendingLows = new PendingCandidate[pendingCap]; } swingHighCount = 0; swingLowCount = 0; pendingHighCount = 0; pendingLowCount = 0; if (DEBUG) Console.WriteLine("[TrendlineIndicatorPro] Arrays initialized: maxSwings={0}, pendingCap={1}", maxSwings, pendingCap); } catch (Exception) { LogError("Array initialization failed"); throw; } } private void CalculateAllSwingsOptimized(int swingBars, int totalBars) { try { for (int i = 0; i < totalBars; i++) { if (i >= swingBars) { if (!IsValidBarForSwingDetection(i)) continue; double candidateHigh = High[i]; double candidateLow = Low[i]; bool isPotentialHigh = true; bool isPotentialLow = true; for (int j = 1; j <= swingBars && (isPotentialHigh || isPotentialLow); j++) { int idx = i - j; if (idx < 0) break; if (!IsValidBarForSwingDetection(idx)) { LogWarning("Invalid bar " + idx + " during potential check at " + i); isPotentialHigh = false; isPotentialLow = false; break; } if (isPotentialHigh && candidateHigh <= High[idx] + EPSILON) isPotentialHigh = false; if (isPotentialLow && candidateLow >= Low[idx] - EPSILON) isPotentialLow = false; } if (isPotentialHigh) { if (pendingHighCount >= pendingHighs.Length) { Array.Resize(ref pendingHighs, Math.Min(totalBars, pendingHighs.Length + 16)); } pendingHighs[pendingHighCount].Reset(i, candidateHigh, swingBars); pendingHighCount++; } if (isPotentialLow) { if (pendingLowCount >= pendingLows.Length) { Array.Resize(ref pendingLows, Math.Min(totalBars, pendingLows.Length + 16)); } pendingLows[pendingLowCount].Reset(i, candidateLow, swingBars); pendingLowCount++; } } ProcessPendingHighs(i, swingBars); ProcessPendingLows(i, swingBars); } if (DEBUG) Console.WriteLine("[TrendlineIndicatorPro] swings confirmed: highs={0}, lows={1}", swingHighCount, swingLowCount); } catch (Exception) { LogError("Swing calculation failed"); throw; } } private bool IsValidBarForSwingDetection(int bar) { if (bar < 0 || bar >= Bars) return false; return IsValidPrice(Open[bar]) && IsValidPrice(High[bar]) && IsValidPrice(Low[bar]) && IsValidPrice(Close[bar]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessPendingHighs(int currentIndex, int swingBars) { for (int k = pendingHighCount - 1; k >= 0; k--) { var ph = pendingHighs[k]; if (currentIndex < High.Length && currentIndex > ph.Index && High[currentIndex] > ph.Value - EPSILON) ph.Invalidated = true; ph.Remaining = ph.Remaining - 1; if (ph.Remaining <= 0) { if (!ph.Invalidated && swingHighCount < swingHighs.Length) { int confirmedBar = currentIndex; if (confirmedBar >= Bars) { LogWarning("Invalid confirmed bar " + confirmedBar + " for high at " + ph.Index); continue; } swingHighs[swingHighCount] = ph.Value; swingHighBars[swingHighCount] = ph.Index; swingHighConfirmationBars[swingHighCount] = confirmedBar; swingHighCount++; if (DEBUG) Console.WriteLine("[TrendlineIndicatorPro] Confirm high: val={0}, actualBar={1}, confirmedAt={2}", ph.Value, ph.Index, confirmedBar); } pendingHighCount--; if (k < pendingHighCount) pendingHighs[k] = pendingHighs[pendingHighCount]; } else { pendingHighs[k] = ph; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void ProcessPendingLows(int currentIndex, int swingBars) { for (int k = pendingLowCount - 1; k >= 0; k--) { var pl = pendingLows[k]; if (currentIndex < Low.Length && currentIndex > pl.Index && Low[currentIndex] < pl.Value + EPSILON) pl.Invalidated = true; pl.Remaining = pl.Remaining - 1; if (pl.Remaining <= 0) { if (!pl.Invalidated && swingLowCount < swingLows.Length) { int confirmedBar = currentIndex; if (confirmedBar >= Bars) { LogWarning("Invalid confirmed bar " + confirmedBar + " for low at " + pl.Index); continue; } swingLows[swingLowCount] = pl.Value; swingLowBars[swingLowCount] = pl.Index; swingLowConfirmationBars[swingLowCount] = confirmedBar; swingLowCount++; if (DEBUG) Console.WriteLine("[TrendlineIndicatorPro] Confirm low: val={0}, actualBar={1}, confirmedAt={2}", pl.Value, pl.Index, confirmedBar); } pendingLowCount--; if (k < pendingLowCount) pendingLows[k] = pendingLows[pendingLowCount]; } else { pendingLows[k] = pl; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ValidateSlopeOptimized(double pA, double pB, int bA, int bB) { int dx = Math.Abs(bB - bA); if (dx == 0) return false; double slope = Math.Abs((pB - pA) / dx); return slope >= MIN_SLOPE && slope <= MAX_SLOPE; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private double CalculateTrendlineValueOptimized(double pA, double pB, int bA, int bB, int currentBar) { if (bB == bA) return pA; double t = (currentBar - bA) / (double)(bB - bA); return pA + (pB - pA) * t; } private void InitializeComponentsSafely(int firstBar) { try { Component = new IndicatorComp[4]; Component[0] = new IndicatorComp { CompName = "Bullish Trendline", DataType = IndComponentType.IndicatorValue, ChartType = IndChartType.Level, ChartColor = Color.Blue, FirstBar = firstBar, Value = new double[Bars] }; Component[1] = new IndicatorComp { CompName = "Bearish Trendline", DataType = IndComponentType.IndicatorValue, ChartType = IndChartType.Level, ChartColor = Color.Red, FirstBar = firstBar, Value = new double[Bars] }; Component[2] = new IndicatorComp { FirstBar = firstBar, Value = new double[Bars] }; Component[3] = new IndicatorComp { FirstBar = firstBar, Value = new double[Bars] }; if (SlotType == SlotTypes.Open) { Component[2].CompName = "Long position entry price"; Component[3].CompName = "Short position entry price"; Component[2].DataType = IndComponentType.OpenLongPrice; Component[3].DataType = IndComponentType.OpenShortPrice; } else if (SlotType == SlotTypes.OpenFilter) { Component[2].CompName = "Is long entry allowed"; Component[3].CompName = "Is short entry allowed"; Component[2].DataType = IndComponentType.AllowOpenLong; Component[3].DataType = IndComponentType.AllowOpenShort; } else if (SlotType == SlotTypes.Close) { Component[2].CompName = "Close out long position"; Component[3].CompName = "Close out short position"; Component[2].DataType = IndComponentType.CloseLongPrice; Component[3].DataType = IndComponentType.CloseShortPrice; } else if (SlotType == SlotTypes.CloseFilter) { Component[2].CompName = "Force close long position"; Component[3].CompName = "Force close short position"; Component[2].DataType = IndComponentType.ForceCloseLong; Component[3].DataType = IndComponentType.ForceCloseShort; } } catch (Exception) { LogError("Component initialization failed"); InitializeDefaultComponents(); throw; } } private void InitializeDefaultComponents() { try { int barsCount = Bars > 0 ? Bars : 0; int firstBar = MIN_BARS_REQUIRED; Component = new IndicatorComp[4]; var defaultValues = new double[barsCount]; Component[0] = new IndicatorComp { CompName = "Bullish Trendline", DataType = IndComponentType.IndicatorValue, ChartType = IndChartType.Level, ChartColor = Color.Blue, FirstBar = firstBar, Value = defaultValues }; Component[1] = new IndicatorComp { CompName = "Bearish Trendline", DataType = IndComponentType.IndicatorValue, ChartType = IndChartType.Level, ChartColor = Color.Red, FirstBar = firstBar, Value = defaultValues }; Component[2] = new IndicatorComp { FirstBar = firstBar, Value = defaultValues }; Component[3] = new IndicatorComp { FirstBar = firstBar, Value = defaultValues }; } catch (Exception) { LogError("Default component initialization failed"); Component = new IndicatorComp[0]; } } private void LogError(string message) { if (DEBUG) Console.WriteLine("ERROR: [TrendlineIndicatorPro] " + message); } private void LogWarning(string message) { if (DEBUG) Console.WriteLine("WARNING: [TrendlineIndicatorPro] " + message); } public override void SetDescription() { string paramDescr = string.Format("SwingBars: {0}, TrendShift: {1}, VerticalShift: {2}", IndParam.NumParam[0].Value, IndParam.NumParam[1].Value, IndParam.ListParam[1].Text); string logic = IndParam.ListParam[0].Text; int shiftVal = 0; if (!int.TryParse(IndParam.ListParam[1].Text, out shiftVal)) shiftVal = 0; string supportTrade, resistanceTrade; if (shiftVal > 0) { supportTrade = shiftVal + " points below the "; resistanceTrade = shiftVal + " points above the "; } else if (shiftVal == 0) { supportTrade = "the "; resistanceTrade = "the "; } else { supportTrade = (-shiftVal) + " points above the "; resistanceTrade = (-shiftVal) + " points below the "; } if (logic == "Enter long at bullish trendline") { EntryPointLongDescription = "at " + supportTrade + "bullish trendline (" + paramDescr + ")"; EntryPointShortDescription = "at " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; } else if (logic == "Enter long at bearish trendline") { EntryPointLongDescription = "at " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; EntryPointShortDescription = "at " + supportTrade + "bullish trendline (" + paramDescr + ")"; } else if (logic == "Exit long at bullish trendline") { ExitPointLongDescription = "at " + supportTrade + "bullish trendline (" + paramDescr + ")"; ExitPointShortDescription = "at " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; } else if (logic == "Exit long at bearish trendline") { ExitPointLongDescription = "at " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; ExitPointShortDescription = "at " + supportTrade + "bullish trendline (" + paramDescr + ")"; } else if (logic == "Price is above bullish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price is above " + supportTrade + "bullish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price is below " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; } else if (logic == "Price is below bullish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price is below " + supportTrade + "bullish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price is above " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; } else if (logic == "Price is above bearish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price is above " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price is below " + supportTrade + "bullish trendline (" + paramDescr + ")"; } else if (logic == "Price is below bearish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price is below " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price is above " + supportTrade + "bullish trendline (" + paramDescr + ")"; } else if (logic == "Price breaks above bullish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price breaks above " + supportTrade + "bullish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price breaks below " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; } else if (logic == "Price breaks below bullish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price breaks below " + supportTrade + "bullish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price breaks above " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; } else if (logic == "Price breaks above bearish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price breaks above " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price breaks below " + supportTrade + "bullish trendline (" + paramDescr + ")"; } else if (logic == "Price breaks below bearish trendline") { EntryFilterLongDescription = ExitFilterLongDescription = "price breaks below " + resistanceTrade + "bearish trendline (" + paramDescr + ")"; EntryFilterShortDescription = ExitFilterShortDescription = "price breaks above " + supportTrade + "bullish trendline (" + paramDescr + ")"; } } public override string ToString() { return IndicatorName + " (SwingBars: " + IndParam.NumParam[0].Value + ", TrendShift: " + IndParam.NumParam[1].Value + ", VerticalShift: " + IndParam.ListParam[1].Text + ")" + (IndParam.CheckParam[0].Checked ? " [Prev]" : "") + " [PROD v3.0]"; } } }
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.;