Programatically bring chart which generated pop-up window to front

Created at 03 Jun 2024, 20:23
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!
LU

lukaszpe

Joined 16.06.2023

Programatically bring chart which generated pop-up window to front
03 Jun 2024, 20:23


Hello!

I would like to achieve what the title says. My indicator generates signals and based on them I get pop-ups.

Almost everything works as intended, except “teleporting” me to a chart which generated the signal. How to achieve this?

Here is the code snippet for MessageBox (aka pop-up window:) ):

var _mbox = MessageBox.Show(Chart.SymbolName + " Take Long at " + DateTime.Now, "Long mode on " + Chart.SymbolName, MessageBoxButton.OK, MessageBoxImage.Information), MessageBoxResult.OK);
                Print(_mbox);
                 if (_mbox == MessageBoxResult.OK)
                     Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame,Chart.SymbolName); <-- my attempt...

Best,

Łukasz


@lukaszpe
Replies

PanagiotisCharalampous
04 Jun 2024, 05:41

Hi Lukasz,

Can you please share the complete indicator code and instructions how to reproduce this behavior?

Best regards,

Panagiotis


@PanagiotisCharalampous

lukaszpe
04 Jun 2024, 07:27

RE: Programatically bring chart which generated pop-up window to front

PanagiotisCharalampous said: 

Hi Lukasz,

Can you please share the complete indicator code and instructions how to reproduce this behavior?

Best regards,

Panagiotis

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

namespace cAlgo
{
    [Cloud(topBand, bottomBand)]
    [Cloud(ClosePriceLine, OpenPriceLine, Opacity = 0.4)]
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class Advanced_Signal_Line : Indicator
    {
        public const string ClosePriceLine = "Close Price", OpenPriceLine = "Open Price",
        topBand = "Top Band", bottomBand = "Bottom Band";

        public enum priceType
        {
            Open,
            High,
            Low,
            Close,
            OC2,
            HL2,
            HLC3,
            OHLC4
        }
        public enum signalType
        {

            Open,
            High,
            Low,
            Close,
            OC2,
            HL2,
            HLC3,
            HLCC4,
            OHLC4,
            HeikenAshiOpen,
            HeikenAshiOC2
        }
        private double priceData(priceType reqPrice, int i)
        {
            var o = Bars.OpenPrices[i];
            var h = Bars.HighPrices[i];
            var l = Bars.LowPrices[i];
            var c = Bars.ClosePrices[i];
            switch (reqPrice)
            {
                case priceType.Open:
                    return o;
                case priceType.High:
                    return h;
                case priceType.Low:
                    return l;
                case priceType.Close:
                    return c;
                case priceType.OC2:
                    return (o + c) / 2.0;
                case priceType.HL2:
                    return (h + l) / 2.0;
                case priceType.HLC3:
                    return (h + l + c) / 3.0;
                case priceType.OHLC4:
                    return (o + h + l + c) / 4.0;
                default:
                    return c;
            }
        }
        private double signalLine(signalType st, int i)
        {
            var o = Bars.OpenPrices[i];
            var h = Bars.HighPrices[i];
            var l = Bars.LowPrices[i];
            var c = Bars.ClosePrices[i];
            switch (st)
            {
                case signalType.Open:
                    return o;
                case signalType.High:
                    return h;
                case signalType.Low:
                    return l;
                case signalType.Close:
                    return c;
                case signalType.OC2:
                    return (o + c) / 2.0;
                case signalType.HL2:
                    return (h + l) / 2.0;
                case signalType.HLC3:
                    return (h + l + c) / 3.0;
                case signalType.HLCC4:
                    return (h + l + c + c) / 4.0;
                case signalType.OHLC4:
                    return (o + h + l + c) / 4.0;
                case signalType.HeikenAshiOpen:
                    if (i > 0)
                        return (signalLine(signalType.HeikenAshiOpen, i - 1) + signalLine(signalType.OHLC4, i - 1)) / 2;
                    else
                        return priceData(priceType.OC2, i);
                case signalType.HeikenAshiOC2:
                    return (signalLine(signalType.HeikenAshiOpen, i) + signalLine(signalType.OHLC4, i)) / 2;
                default:
                    return c;
            }
        }
        public enum SigSrc
        {
            Middle,
            Middle_To_Bands,
            Bands,
            Mirrored_Bands,
            Band_without_neutral
        }

        [Parameter("Price source for MA Calculation", DefaultValue = priceType.Close, Group = "MA and ATR")]
        public priceType src { get; set; }

        [Parameter("MA Type", DefaultValue = MovingAverageType.WilderSmoothing, Group = "MA and ATR")]
        public MovingAverageType maType { get; set; }

        [Parameter("MA and ATR Period", DefaultValue = 30, MinValue = 1, Group = "MA and ATR")]
        public int maPeriod { get; set; }

        [Parameter("Use Hurst channel", DefaultValue = true, Group = "MA and ATR")]
        public bool maUseHurst { get; set; }

        [Parameter("ATR from MA offset Multiplier", DefaultValue = 2.0, MinValue = 0, Group = "MA and ATR")]
        public double maMultiplier { get; set; }

        [Parameter("Schow standard deviation lines", DefaultValue = true, Group = "MA and ATR")]
        public bool stDevLinesShow { get; set; }

        [Parameter("Standard deviation from MA offset Multiplier", DefaultValue = 2.0, MinValue = 0, Group = "MA and ATR")]
        public double stDevMultiplier { get; set; }

        [Parameter("Draw Heiken Ashi cloud", DefaultValue = false, Group = "Signal")]
        public bool haCloudDraw { get; set; }

        [Parameter("Signal line source", DefaultValue = signalType.Close, Group = "Signal")]
        public signalType haSigType { get; set; }

        [Parameter("Use MA on Signal", DefaultValue = false, Group = "Signal")]
        public bool sigMAUse { get; set; }

        [Parameter("Signal MA Type", DefaultValue = MovingAverageType.WilderSmoothing, Group = "Signal")]
        public MovingAverageType sigMAType { get; set; }

        [Parameter("Signal MA Period", DefaultValue = 30, MinValue = 1, Group = "Signal")]
        public int sigMAPeriod { get; set; }

        [Parameter("Signal boundary", DefaultValue = SigSrc.Middle, Group = "Signal")]
        public SigSrc haSigBoundary { get; set; }
        //MA Outputs
        [Output("StDev +", LineColor = "Yellow", Thickness = 1, LineStyle = LineStyle.Dots)]
        public IndicatorDataSeries stDevTop { get; set; }

        [Output(topBand, LineColor = "White", Thickness = 2)]
        public IndicatorDataSeries maTop { get; set; }

        [Output("Middle", LineColor = "White", Thickness = 1, LineStyle = LineStyle.LinesDots)]
        public IndicatorDataSeries maMiddle { get; set; }

        [Output(bottomBand, LineColor = "White", Thickness = 2)]
        public IndicatorDataSeries maBottom { get; set; }

        [Output("StDev -", LineColor = "Yellow", Thickness = 1, LineStyle = LineStyle.Dots)]
        public IndicatorDataSeries stDevBottom { get; set; }

        // Heikin Ashi outputs
        [Output(ClosePriceLine, LineColor = "Yellow", Thickness = 1)]
        public IndicatorDataSeries closePrice { get; set; }

        [Output("Average Heikin Ashi Price line", LineColor = "White", Thickness = 1, LineStyle = LineStyle.LinesDots)]
        public IndicatorDataSeries avgPrice { get; set; }

        [Output("Long signal", LineColor = "Lime", Thickness = 2, PlotType = PlotType.DiscontinuousLine)]
        public IndicatorDataSeries lngSig { get; set; }

        [Output("Short signal", LineColor = "Red", Thickness = 2, PlotType = PlotType.DiscontinuousLine)]
        public IndicatorDataSeries shrtSig { get; set; }

        [Output(OpenPriceLine, LineColor = "Aqua", Thickness = 1)]
        public IndicatorDataSeries openPrice { get; set; }

        private double stDevFcn(int i, int clcPeriod, ref IndicatorDataSeries price, double average)
        {
            var sum = 0.0;
            for (var period = 0; period < clcPeriod; period++)
                sum += Math.Pow(price[i - period] - average, 2.0);

            return Math.Sqrt(sum / clcPeriod);
        }

        private AverageTrueRange _atr;
        private IndicatorDataSeries _open, _close, _price, _sigLin;
        private MovingAverage _ma, _sigMA;
        private StandardDeviation _stDevFcn;

        private int _Per2, _Per4, haComparePeriod = 1, _barIndex = 0;
        private bool _lng, _shrt;
        protected override void Initialize()
        {
            _open = CreateDataSeries();
            _close = CreateDataSeries();
            _price = CreateDataSeries();
            _sigLin = CreateDataSeries();
            _Per2 = Convert.ToInt32(maPeriod / 2);
            _Per4 = Convert.ToInt32(maPeriod / 4);
            if (maUseHurst)
            {
                _atr = Indicators.AverageTrueRange(_Per2, maType);
                _ma = Indicators.MovingAverage(_price, _Per2, maType);
            }
            else
            {
                _atr = Indicators.AverageTrueRange(maPeriod, maType);
                _ma = Indicators.MovingAverage(_price, maPeriod, maType);
            }
            if (sigMAUse)
                _sigMA = Indicators.MovingAverage(_sigLin, sigMAPeriod, sigMAType);

            if (stDevLinesShow)
                _stDevFcn = Indicators.StandardDeviation(maMiddle, maPeriod, maType);

            _lng = false;
            _shrt = false;
        }

        TimeSpan _maxDiff = new TimeSpan(30000000);
        private void markLong(int i)
        {

            if (Double.IsNaN(lngSig[i - 1]) && _barIndex != i && IsLastBar)// && ((Server.Time - Bars.Last(0).OpenTime) < _maxDiff))
            {
                _barIndex = i;
                var _mbox = MessageBox.Show(DateTime.Now.TimeOfDay.ToString().Substring(0,8) + "\n" + "Long\n" + Chart.SymbolName, "Long mode on " + Chart.SymbolName, MessageBoxButton.OK, MessageBoxImage.Information), MessageBoxResult.OK);
                Print(_mbox);
                if (_mbox == MessageBoxResult.OK)
                   Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame,Chart.SymbolName);
            }
            if (Double.IsNaN(lngSig[i - 1]))
                lngSig[i - 1] = avgPrice[i - 1];
            lngSig[i] = avgPrice[i];
            _lng = true;
            _shrt = false;

        }
        private void markShort(int i)
        {
            if (Double.IsNaN(shrtSig[i - 1]) && _barIndex != i && IsLastBar)// && ((Server.Time - Bars.Last(0).OpenTime) < _maxDiff))
            {
                _barIndex = i;
                var _mbox = MessageBox.Show(DateTime.Now.TimeOfDay.ToString().Substring(0,8) + "\n" + "Short\n" + Chart.SymbolName, "Short mode on " + Chart.SymbolName, MessageBoxButton.OK, MessageBoxImage.Information), MessageBoxResult.OK);
                Print(_mbox);
                if (_mbox == MessageBoxResult.OK)
                   Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame,Chart.SymbolName);
            }
            if (Double.IsNaN(shrtSig[i - 1]))
                shrtSig[i - 1] = avgPrice[i - 1];
            shrtSig[i] = avgPrice[i];
            _lng = false;
            _shrt = true;
        }
        private void unmarkLong(int i)
        {
            lngSig[i] = Double.NaN;
            _lng = false;
        }
        private void unmarkShort(int i)
        {
            shrtSig[i] = Double.NaN;
            _shrt = false;
        }
        public override void Calculate(int i)
        {
            _price[i] = priceData(src, i);
            _sigLin[i] = signalLine(haSigType, i);
            lngSig[i] = Double.NaN;
            shrtSig[i] = Double.NaN;
            if (maUseHurst)
            {
                double ma = _ma.Result[i - _Per4];
                if (double.IsNaN(ma))
                    ma = _price[i];

                maTop[i] = ma + maMultiplier * _atr.Result[i];
                maBottom[i] = ma - maMultiplier * _atr.Result[i];
                maMiddle[i] = (maTop[i] + maBottom[i]) / 2.0;
            }
            else
            {
                maMiddle[i] = _ma.Result[i];
                maTop[i] = maMiddle[i] + maMultiplier * _atr.Result[i];
                maBottom[i] = maMiddle[i] - maMultiplier * _atr.Result[i];
            }
            if (stDevLinesShow)
            {
                stDevTop[i] = maMiddle[i] + stDevMultiplier * stDevFcn(i, maPeriod, ref _price, maMiddle[i]);
                stDevBottom[i] = maMiddle[i] - stDevMultiplier * stDevFcn(i, maPeriod, ref _price, maMiddle[i]);
            }
            //Heikin Ashi
            if (haCloudDraw)
            {
                openPrice[i] = signalLine(signalType.HeikenAshiOpen, i);
                closePrice[i] = signalLine(signalType.OHLC4, i);
            }
            if (sigMAUse)
                avgPrice[i] = _sigMA.Result[i];
            else
                avgPrice[i] = _sigLin[i];


            switch (haSigBoundary)
            {
                case SigSrc.Middle:
                default:
                    if (avgPrice[i] > maMiddle[i])
                    {
                        markLong(i);
                    }
                    if (avgPrice[i] < maMiddle[i])
                    {
                        markShort(i);
                    }
                    break;
                case SigSrc.Bands:
                    if ((avgPrice[i] > maBottom[i] && avgPrice[i - 1] < maBottom[i - 1]) || _lng)
                    {
                        markLong(i);
                    }
                    if ((avgPrice[i] < maTop[i] && avgPrice[i - 1] > maTop[i - 1]) || _shrt)
                    {
                        markShort(i);
                    }
                    if ((avgPrice[i] < maBottom[i] && avgPrice[i - 1] > maBottom[i - 1]) && _lng)
                        unmarkLong(i);
                    if ((avgPrice[i] > maTop[i] && avgPrice[i - 1] < maTop[i - 1]) && _shrt)
                        unmarkShort(i);
                    break;
                case SigSrc.Mirrored_Bands:
                    if ((avgPrice[i] > maTop[i] && avgPrice[i - 1] < maTop[i - 1]) || _lng)
                    {
                        markLong(i);
                    }
                    if ((avgPrice[i] < maBottom[i] && avgPrice[i - 1] > maBottom[i - 1]) || _shrt)
                    {
                        markShort(i);
                    }
                    if ((avgPrice[i] < maTop[i] && avgPrice[i - 1] > maTop[i - 1]) && _lng)
                        unmarkLong(i);
                    if ((avgPrice[i] > maBottom[i] && avgPrice[i - 1] < maBottom[i - 1]) && _shrt)
                        unmarkShort(i);
                    break;
                case SigSrc.Middle_To_Bands:
                    if ((avgPrice[i] > maMiddle[i] && avgPrice[i - 1] < maMiddle[i - 1]) || _lng)
                    {
                        markLong(i);
                    }
                    if ((avgPrice[i] < maMiddle[i] && avgPrice[i - 1] > maMiddle[i - 1]) || _shrt)
                    {
                        markShort(i);
                    }
                    if ((avgPrice[i] < maTop[i] && avgPrice[i - 1] > maTop[i - 1]) && _lng)
                        unmarkLong(i);
                    if ((avgPrice[i] > maBottom[i] && avgPrice[i - 1] < maBottom[i - 1]) && _shrt)
                        unmarkShort(i);
                    break;
                case SigSrc.Band_without_neutral:
                    if ((avgPrice[i] > maBottom[i] && avgPrice[i - 1] < maBottom[i - 1]) || _lng)
                    {
                        markLong(i);
                    }
                    if ((avgPrice[i] < maTop[i] && avgPrice[i - 1] > maTop[i - 1]) || _shrt)
                    {
                        markShort(i);
                    }
                    break;
            }
        }

    }

}

how the signals are generated is dealt with inside of switch (haSigBoundary) and the functions:

private void markLong(int i)
        {

            if (Double.IsNaN(lngSig[i - 1]) && _barIndex != i && IsLastBar)// && ((Server.Time - Bars.Last(0).OpenTime) < _maxDiff))
            {
                _barIndex = i;
                var _mbox = MessageBox.Show(DateTime.Now.TimeOfDay.ToString().Substring(0,8) + "\n" + "Long\n" + Chart.SymbolName, "Long mode on " + Chart.SymbolName, MessageBoxButton.OK, MessageBoxImage.Information), MessageBoxResult.OK);
                Print(_mbox);
                if (_mbox == MessageBoxResult.OK)
                    Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame,Chart.SymbolName);
            }
            if (Double.IsNaN(lngSig[i - 1]))
                lngSig[i - 1] = avgPrice[i - 1];
            lngSig[i] = avgPrice[i];
            _lng = true;
            _shrt = false;

        }
        private void markShort(int i)
        {
            if (Double.IsNaN(shrtSig[i - 1]) && _barIndex != i && IsLastBar)// && ((Server.Time - Bars.Last(0).OpenTime) < _maxDiff))
            {
                _barIndex = i;
                var _mbox = MessageBox.Show(DateTime.Now.TimeOfDay.ToString().Substring(0,8) + "\n" + "Short\n" + Chart.SymbolName, "Short mode on " + Chart.SymbolName, MessageBoxButton.OK, MessageBoxImage.Information), MessageBoxResult.OK);
                Print(_mbox);
                if (_mbox == MessageBoxResult.OK)
                   Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame,Chart.SymbolName);
            }
            if (Double.IsNaN(shrtSig[i - 1]))
                shrtSig[i - 1] = avgPrice[i - 1];
            shrtSig[i] = avgPrice[i];
            _lng = false;
            _shrt = true;
        }

are respoonsible for coloring the signal line.

To reproduce:

  1. Attach indicator to multiple charts in the same program window (best will be with small time frame like 1minute or even 10 - 20 ticks, depending on volatility)
  2. Wait for crossing of signal line with channel middle (with default settings)
  3.  The messagebox should come. Click ok.

After clicking ok I expect to be taken to the chart that generate the signal in the box. I couldn't find any function in CTrader's help that would what I am asking for.


@lukaszpe