ChartTrendLines don't get removed from OnDestroy() method when indicator removed from chart

Created at 01 Nov 2024, 01:52
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
FI

firemyst

Joined 26.03.2019

ChartTrendLines don't get removed from OnDestroy() method when indicator removed from chart
01 Nov 2024, 01:52


Hi everyone:

See code below. In the OnDestroy() method, I have two loops, neither of which work, which are intended to remove all the ChartTrendLines drawn on the chart, especially when the indicator is removed from the chart.

Code below. Can anyone tell me what I'm doing wrong? Thank you.

using System;
using System.Collections.Generic;
using System.Reflection;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo
{
    [Indicator(AccessRights = AccessRights.FullAccess, IsOverlay = true)]
    public class Test : Indicator
    {
        private const string CONST_IndicatorName = "TEST";

        [Parameter("Num Bars Back to Start (-1 for all)", DefaultValue = 21, MinValue = -1)]
        public int NumBarsBackToStart { get; set; }

        [Parameter("Line Color", DefaultValue = "White")]
        public Color LineColor { get; set; }

        [Parameter("Line Thickness", DefaultValue = 3)]
        public int LineThickness { get; set; }

        [Parameter("Line Style", DefaultValue = LineStyle.Solid)]
        public LineStyle LineStyleDrawn { get; set; }

        public IndicatorDataSeries ResultHighs { get; set; }

        private int _previousIndex;
        private int _indexToStart;

        private Dictionary<int, ChartTrendLine> _lowToHighsTrendLinesDrawn;

        protected override void Initialize()
        {
#if DEBUG
            var result = System.Diagnostics.Debugger.Launch();
#endif
            ResultHighs = CreateDataSeries();

            _indexToStart = (NumBarsBackToStart == -1 ? 2 : Bars.Count - NumBarsBackToStart);
            _previousIndex = _indexToStart - 2;

            //Used to keep track of every line we draw so we can clean up properly at the end
            _lowToHighsTrendLinesDrawn = new Dictionary<int, ChartTrendLine>(NumBarsBackToStart);

            Chart.ObjectsRemoved += Chart_ObjectsRemoved;
        }

        //Can't debug through this method in Visual Studio -- the first foreach always crashes because it seems like cTrader closes itself off before this method completes running.
        //
        //THis method also doesn't seem to remove all the drawings from the chart.
        //Why not?
        //
        //It doesn't matter which loop I use. Neither seems to work nor remove all the chart trend lines drawn.
        //
        //I DO NOT want to use Chart.RemoveAllObjects because there might be other objects drawn with this indicator I want to leave on the chart.
        //
        protected override void OnDestroy()
        {
            //THIS FOREACH LOOP DOESN"T REMOVE THE TRENDLINES FROM THE CHART
            //I can't debug this forweach loop either in Visual Studio as VS always seems to exit before completion.
            //
            //foreach (KeyValuePair<int, ChartTrendLine> kvp in _lowToHighsTrendLinesDrawn)
            //{
            //    //Set the object so it's no longer interactive and can be found in chart collections
            //    ChartObject co = kvp.Value;
            //    co.IsInteractive = false;
            //    //Remove. THis doesn't seem to work. Why?
            //    Chart.RemoveObject(CONST_IndicatorName + "LowsToHighs" + kvp.Key);
            //    co = null;
            //}

            //Try looping through each object in the dictionary to set to null to see if removed from chart.
            //THIS LOOP DOES NOT WORK EITHER. Why?
            //
            for (int x = _lowToHighsTrendLinesDrawn.Count - 1; x >= 0; x--)
            {
                if (_lowToHighsTrendLinesDrawn.ContainsKey(x))
                {
                    _lowToHighsTrendLinesDrawn[x].IsInteractive = false;
                    Chart.RemoveObject(CONST_IndicatorName + "LowsToHighs" + x);
                    _lowToHighsTrendLinesDrawn[x] = null;
                }
            }

            //Clean up the Dictionary object
            _lowToHighsTrendLinesDrawn.Clear();
            _lowToHighsTrendLinesDrawn = null;
        }

        public override void Calculate(int index)
        {
            if (index < _indexToStart)
            {
                _previousIndex = index;
                return;
            }

            //Only do this once per bar
            if (index > _previousIndex)
            {
                int previousIndexMinus1 = _previousIndex - 1;

                double highPricePrev = Bars.HighPrices[_previousIndex]; 
                double highPrice2BarsAgo = Bars.HighPrices[previousIndexMinus1];

                ResultHighs[_previousIndex] = highPricePrev;
                ResultHighs[previousIndexMinus1] = highPrice2BarsAgo;

                ChartTrendLine ctl = Chart.DrawTrendLine(CONST_IndicatorName  + "LowsToHighs" + previousIndexMinus1, previousIndexMinus1, ResultHighs[previousIndexMinus1], _previousIndex, ResultHighs[_previousIndex], LineColor, LineThickness, LineStyleDrawn);
                ctl.IsInteractive = true;

                if (_lowToHighsTrendLinesDrawn.ContainsKey(previousIndexMinus1))
                {
                    _lowToHighsTrendLinesDrawn[previousIndexMinus1].IsInteractive = false;
                    Chart.RemoveObject(CONST_IndicatorName + "LowsToHighs" + previousIndexMinus1);
                    _lowToHighsTrendLinesDrawn[previousIndexMinus1] = null;
                    _lowToHighsTrendLinesDrawn.Remove(previousIndexMinus1);
                }
                _lowToHighsTrendLinesDrawn.Add(previousIndexMinus1, ctl);

                _previousIndex = index;
            }

        }

        private void Chart_ObjectsRemoved(ChartObjectsRemovedEventArgs obj)
        {
            /* If the object is removed, we should rid of its
            reference. Otherwise, it will remain in memory. */
            for (int x = obj.ChartObjects.Count - 1; x >= 0 ; x--)
            {
                if (!obj.ChartObjects[x].IsAlive)
                {
                    ChartObject co = obj.ChartObjects[x];
                    co.IsInteractive = false;
                    co = null;
                }
            }
        }
    }
}

@firemyst
Replies

PanagiotisCharalampous
04 Nov 2024, 06:52

Hi firemyst,

Thank you for reporting this. We are investigating.

Best regards,

Panagiotis


@PanagiotisCharalampous

AlgoCreators
04 Nov 2024, 08:59

I think the OnDestroy function is never executed. There is no such feature in the indicators


@AlgoCreators

firemyst
04 Nov 2024, 23:23

RE: ChartTrendLines don't get removed from OnDestroy() method when indicator removed from chart

AlgoCreators said: 

I think the OnDestroy function is never executed. There is no such feature in the indicators

I'm not sure where you've been, but OnDestroy() has been there since version 4.2:


@firemyst