Bars..LastValue

Created at 28 Jan 2020, 07:35
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!
GE

gennimatas

Joined 19.09.2018

Bars..LastValue
28 Jan 2020, 07:35


HI

ver 3.7.65534.35735

In an indicator, LastValue and Last(0) contains a wrong value when

            Chart.LastVisibleBarIndex != Bars.ClosePrices.Count - 1

(which is also wrong)

To reproduce, press Home to load more bars on a minute chart then press End.

Regards


@gennimatas
Replies

PanagiotisCharalampous
29 Jan 2020, 12:00

Hi Takis Genn,

Can you please provide us with a complete code example that allows us to reproduce this issue?

Best Regards,

Panagiotis 

Join us on Telegram

 


@PanagiotisCharalampous

gennimatas
29 Jan 2020, 14:50 ( Updated at: 21 Dec 2023, 09:21 )

RE:

PanagiotisCharalampous said:

Hi Takis Genn,

Can you please provide us with a complete code example that allows us to reproduce this issue?

Best Regards,

Panagiotis 

Join us on Telegram

 

Hi Panagiotis,

It's a bit difficult, the indicator is calling another one 300 lines and a library 1700 lines.

I will see if i can include only the called functions.

In the meanwhile, i tried to reproduce the issue copy/pasting the part i thought was faulty in a new indicator without success.

What's happening is that the whole thing is inside the timer event.

There, a cluster is called to display stats about pairs and then i display the ticks per minute taken from DOM using a trendline at LastValue.

The indicator is updating normally, no exception shown in log, but the trendline is stuck.

If i use Symbol.Bid instead of Bars.ClosePrices.LastValue then everything works fine except the Bars.Count which is stuck at the same bar with LastValue.

What i understand is that something i previously call is braking the Bars class.

Here is an image of the thingy:) In Cyan is the faulty indi, in White the copy/pasted one which works. I've put captions to values on the right to make things more clear. The white horiz line is the TpM at the supposed current price.

Any ideas?

Regards,

Takis

 

 


@gennimatas

PanagiotisCharalampous
03 Feb 2020, 08:57

Hi Takis Genn,

Unfortunately I cannot know what the issue could be if I do not have any source code that reproduces such a behavior.

Best Regards,

Panagiotis 

Join us on Telegram


@PanagiotisCharalampous

gennimatas
03 Feb 2020, 16:41

RE:

PanagiotisCharalampous said:

Hi Takis Genn,

Unfortunately I cannot know what the issue could be if I do not have any source code that reproduces such a behavior.

Best Regards,

Panagiotis 

Join us on Telegram

Here is the code Panagiotis,

Regards

 

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;

// The issue:
//  The condition: Chart.LastVisibleBarIndex != (Bars.ClosePrices.Count - 1)
//  should happen only when the chart's last bar is not visible.
//  In that case, at the Top Right below HHLL range, appear FirstVisibleBarIndex
//  and ClosePrices.Count
//  But there are cases where those two appear with the last bar visible, which is wrong.
//  The issue is intermidient.
//
// To reproduce the issue:
//  1.Run
//  2.Change to one minute timeframe
//  3.Load more bars (Home button)
//  4.Return to the right (End button)
//  5.If the problem doesn't show, change to m5 then back to m1 or load more bars.
//
// When reproduced, the "TicksLine" TrendLine will separate from the chart's Ask and Bid lines
// on Y axis while moving on the X axis.
//
// When function ShowRiskPct is placed inside the OnTimer event and
// commented out from Calc event, i could not reproduce the issue.

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class InfoTEST : Indicator
    {
        [Parameter("Offset", DefaultValue = 0, MinValue = -23, MaxValue = 23)]
        public int Offset { get; set; }

        [Parameter("Wellington", DefaultValue = 21, MinValue = -1, MaxValue = 23)]
        public int Wellington { get; set; }

        [Parameter("Sydney", DefaultValue = 22, MinValue = -1, MaxValue = 23)]
        public int Sydney { get; set; }

        [Parameter("Tokyo", DefaultValue = 0, MinValue = -1, MaxValue = 23)]
        public int Tokyo { get; set; }

        [Parameter("Singapore", DefaultValue = 1, MinValue = -1, MaxValue = 23)]
        public int Singapore { get; set; }

        [Parameter("Frankfurt", DefaultValue = 7, MinValue = -1, MaxValue = 23)]
        public int Frankfurt { get; set; }

        [Parameter("London", DefaultValue = 8, MinValue = -1, MaxValue = 23)]
        public int London { get; set; }

        [Parameter("NewYork", DefaultValue = 13, MinValue = -1, MaxValue = 23)]
        public int NewYork { get; set; }

        [Parameter("DrawDays", DefaultValue = 1, MinValue = 0, MaxValue = 31)]
        public int DrawDays { get; set; }

        [Parameter("WatchListName", DefaultValue = "Forex Cluster")]
        public string WatchListName { get; set; }

        [Parameter("ClusterDays\n  comma separated", DefaultValue = "")]
        public string ClusterDays { get; set; }

        [Parameter("SpreadF", DefaultValue = 1, MinValue = 1, MaxValue = 2)]
        public double SpreadF { get; set; }

        [Parameter("TicksMax", DefaultValue = 300)]
        public int TicksMax { get; set; }

        [Parameter("RiskPct", DefaultValue = 1, MinValue = 0, MaxValue = 100)]
        public double RiskPct { get; set; }

        //private CommonLib cLib;
        int[] Ticks = new int[60];
        int[] TicksDOM = new int[60];
        List<double> Spread = new List<double>();
        Dictionary<string, int> Sessions = new Dictionary<string, int>();
        //public List<ClusterOsc> iCluster = new List<ClusterOsc>();
        int DrawState = 0;
        string[] DrawStates = "info,tpm,hid".Split(',');
        Dictionary<string, Button> ClusterButtons = new Dictionary<string, Button>();
        Panel ClusterPanel;

        private string IndicatorName = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name;

        protected override void Initialize()
        {
            //cLib = Indicators.GetIndicator<CommonLib>();

            Sessions.Add("Wellington", Wellington);
            Sessions.Add("Sydney", Sydney);
            Sessions.Add("Tokyo", Tokyo);
            Sessions.Add("Singapore", Singapore);
            Sessions.Add("Frankfurt", Frankfurt);
            Sessions.Add("London", London);
            Sessions.Add("NewYork", NewYork);

            //if (ClusterDays != "")
            //    foreach (string pDays in ClusterDays.Split(','))
            //        iCluster.Add(Indicators.GetIndicator<ClusterOsc>(WatchListName, "*", Convert.ToInt32(pDays), 60));

            MarketDepth DOM = MarketData.GetMarketDepth(Bars.SymbolName);
            DOM.Updated += DOM_Updated;

            Ticks ticks = MarketData.GetTicks();
            foreach (Tick tick in ticks)
                Spread.Add(tick.Ask - tick.Bid);
            TimeSpan span = new TimeSpan(ticks.LastTick.Time.AddTicks(-ticks[0].Time.Ticks).Ticks);
            Print("SpreadTicks=" + ticks.Count + " Span=" + Convert.ToInt32(span.TotalHours));

            Panel panel = AddStackPanel(HorizontalAlignment.Left, VerticalAlignment.Top);
            AddButton(panel, 1, DrawStates[DrawState], Color.Cyan);

            //if (iCluster.Count > 0)
            //{
            //    int w = 72;
            //    int h = 17;
            //    double f = 11;
            //    ClusterPanel = AddWrapPanel(HorizontalAlignment.Left, VerticalAlignment.Bottom, Orientation.Horizontal);
            //    ClusterPanel.Width = w * iCluster.Count;
            //    for (int c = 0; c < iCluster.Count; c++)
            //    {
            //        Button button = AddButton(ClusterPanel, 2 + c, c.ToString(), Color.White, false, w, h, f);
            //        ClusterButtons.Add("Head." + c, button);
            //    }
            //    for (int r = 0; r < iCluster[0].CLUSTER.Count; r++)
            //        for (int c = 0; c < iCluster.Count; c++)
            //        {
            //            string key = r + "." + c;
            //            Button button = AddButton(ClusterPanel, 5 + (r * 3 + c), key, Color.Cyan, true, w, h, f);
            //            ClusterButtons.Add(key, button);
            //        }
            //}

            Timer.Start(1);
        }

        public override void Calculate(int index)
        {
            if (!IsLastBar)
            {
                DrawSessionBoxes(index);
                return;
            }
            Ticks[DateTime.Now.Second] += 1;

            if (IsLastBar)
                ShowRiskPct();
        }

        void DOM_Updated()
        {
            TicksDOM[DateTime.Now.Second] += 1;
        }

        protected override void OnTimer()
        {
            int sec = DateTime.Now.Second + 1;
            if (sec > 59)
                sec = 0;
            Ticks[sec] = 0;
            TicksDOM[sec] = 0;

            if (DrawState == 0 | DrawState == 1)
            {
                ShowText();
                ShowHiLo();
                DrawSessionBoxes(Bars.Count - 1);
                //if (IsLastBar)
                //    ShowRiskPct();
            }
            if (DrawState == 0)
            {
                ShowCluster();
            }
        }

        private void ShowText()
        {
            StringBuilder sb1 = new StringBuilder();
            int bars = Chart.LastVisibleBarIndex - Chart.FirstVisibleBarIndex;
            //int minutes = bars * cLib.TimeFrameToMinutes(Bars.TimeFrame);
            int minutes = bars * TimeFrameToMinutes(Bars.TimeFrame);
            TimeSpan chartSpan = new TimeSpan(0, minutes, 0);
            sb1.AppendLine(Chart.LastVisibleBarIndex - Chart.FirstVisibleBarIndex + "    " + chartSpan.ToString().Remove(chartSpan.ToString().LastIndexOf(":00")));
            sb1.AppendLine("v" + Application.Version);
            try
            {
                sb1.Append(Symbol.MarketHours.IsOpened() ? "Opened " : "Closed ");
                if (Symbol.MarketHours.IsOpened())
                    sb1.AppendLine(DateTime.UtcNow.AddTicks(Symbol.MarketHours.TimeTillClose().Ticks).ToLocalTime().ToShortTimeString());
                else
                    sb1.AppendLine(DateTime.UtcNow.AddTicks(Symbol.MarketHours.TimeTillOpen().Ticks).ToLocalTime().ToShortTimeString());
            } catch
            {
                sb1.AppendLine();
            }
            sb1.AppendLine("TpM:  " + Ticks.Sum() + " " + TicksDOM.Sum());
            sb1.AppendLine();
            int fb = Chart.FirstVisibleBarIndex;
            int lb = Chart.LastVisibleBarIndex;
            //int TicksR = (int)cLib.Map(TicksDOM.Sum(), 0, TicksMax, fb, lb);
            int TicksR = (int)Map(TicksDOM.Sum(), 0, TicksMax, fb, lb);
            double price = Bars.ClosePrices.LastValue;
            //double price = (Symbol.Ask + Symbol.Bid) / 2;
            Chart.DrawTrendLine("TicksLine", fb, price, TicksR, price, Color.White, 5, LineStyle.Solid);
            double SpreadAvg = Spread.ToArray().Average() / Symbol.PipSize;
            double SpreadNow = (Symbol.Ask - Symbol.Bid) / Symbol.PipSize;
            Color SpreadColor = Color.Cyan;
            if (SpreadNow > SpreadAvg * SpreadF)
                SpreadColor = Color.Red;
            Spread.Add(Symbol.Ask - Symbol.Bid);
            sb1.AppendLine("Spread: (" + Math.Round(SpreadAvg, 2) + ") " + Math.Round(SpreadNow, 2));
            sb1.AppendLine();
            Chart.DrawStaticText("Ticks", sb1.ToString(), VerticalAlignment.Top, HorizontalAlignment.Left, SpreadColor);
        }

        private void ShowCluster()
        {
            //if (iCluster.Count > 0)
            //{
            //    for (int c = 0; c < iCluster.Count; c++)
            //    {
            //        ClusterButtons["Head." + c].Text = iCluster[c].DaysActual.ToString();
            //    }
            //    for (int r = 0; r < iCluster[0].StrengthList.Count; r++)
            //    {
            //        for (int c = 0; c < iCluster.Count; c++)
            //        {
            //            ClusterButtons[r + "." + c].Text = iCluster[c].StrengthList[r].ToUpper();
            //        }
            //    }
            //}
        }

        private void ShowHiLo()
        {
            StringBuilder sb2 = new StringBuilder();
            //double H = cLib.SeriesMaximum(Bars.HighPrices, Chart.FirstVisibleBarIndex, Chart.LastVisibleBarIndex);
            double H = SeriesMaximum(Bars.HighPrices, Chart.FirstVisibleBarIndex, Chart.LastVisibleBarIndex);
            //double L = cLib.SeriesMinimum(Bars.LowPrices, Chart.FirstVisibleBarIndex, Chart.LastVisibleBarIndex);
            double L = SeriesMinimum(Bars.LowPrices, Chart.FirstVisibleBarIndex, Chart.LastVisibleBarIndex);
            sb2.AppendLine("" + Math.Round((H - L) / Symbol.PipSize, 0));
            if (Chart.LastVisibleBarIndex != (Bars.ClosePrices.Count - 1))
            {
                sb2.AppendLine("" + Chart.FirstVisibleBarIndex);
                sb2.AppendLine("" + Bars.ClosePrices.Count);
            }
            Chart.DrawStaticText("HiLo", sb2.ToString(), VerticalAlignment.Top, HorizontalAlignment.Right, Color.Cyan);
        }

        private void ShowRiskPct()
        {
            if (RiskPct > 0)
            {
                string txt = RiskPct + "% ";
                //txt += "U=" + (cLib.RiskToUnits(RiskPct, Symbol) / 1000).ToString() + "k";
                txt += "U=" + (RiskToUnits(RiskPct, Symbol) / 1000).ToString() + "k";
                //txt += " L=" + cLib.RiskToLots(RiskPct, Symbol).ToString();
                txt += " L=" + RiskToLots(RiskPct, Symbol).ToString();
                Chart.DrawStaticText("Volume", txt, VerticalAlignment.Bottom, HorizontalAlignment.Left, Color.White);
            }
        }

        private void DrawSessionBoxes(int idx)
        {
            DateTime ot = Bars.OpenTimes[idx].AddHours(1);
            if (ot < DateTime.Now.AddDays(-DrawDays))
                return;
            if (ot.Second != 0)
                return;

            foreach (KeyValuePair<string, int> kvp in Sessions)
            {
                if (kvp.Value >= 0)
                    try
                    {
                        if (ot.Hour >= kvp.Value && ot.Hour < kvp.Value + 9)
                            DrawSessionBox(kvp, ot);
                    } catch
                    {
                    }
            }
        }

        private void DrawSessionBox(KeyValuePair<string, int> kvp, DateTime BarTime)
        {
            DateTime d1 = new DateTime(BarTime.Year, BarTime.Month, BarTime.Day, 0, 0, 0).AddHours(kvp.Value - Offset);
            DateTime d2 = d1.AddHours(9).AddMinutes(-1);
            int x1 = Bars.OpenTimes.GetIndexByTime(d1);
            int x2 = Bars.OpenTimes.GetIndexByTime(d2);
            double y1 = MaxH(x1, x2);
            double y2 = MinL(x1, x2);

            Color c;
            if (kvp.Key.StartsWith("W"))
                c = Color.Magenta;
            else if (kvp.Key.StartsWith("Sy"))
                c = Color.Blue;
            else if (kvp.Key.StartsWith("T"))
                c = Color.Orange;
            else if (kvp.Key.StartsWith("Si"))
                c = Color.Green;
            else if (kvp.Key.StartsWith("F"))
                c = Color.Yellow;
            else if (kvp.Key.StartsWith("L"))
                c = Color.Lime;
            else if (kvp.Key.StartsWith("N"))
                c = Color.Red;
            else
                c = Color.Gray;
            string Session = kvp.Key + "-" + kvp.Value + "-" + BarTime.DayOfYear;
            Chart.DrawRectangle(Session, d1, y1, d2, y2, c, 1, LineStyle.DotsVeryRare);
            Chart.DrawText(Session + "Txt", Session.Split('-')[0].Substring(0, 1), d1, y1, c);
        }

        private double MaxH(int x1, int x2)
        {
            double max = double.MinValue;
            for (int i = x1; i <= x2; i++)
                max = Math.Max(max, Bars.HighPrices[i]);
            return max;
        }

        private double MinL(int x1, int x2)
        {
            double min = double.MaxValue;
            for (int i = x1; i <= x2; i++)
                min = Math.Min(min, Bars.LowPrices[i]);
            return min;
        }

        public StackPanel AddStackPanel(HorizontalAlignment hor = HorizontalAlignment.Right, VerticalAlignment ver = VerticalAlignment.Bottom, Orientation orient = Orientation.Vertical)
        {
            StackPanel panel = new StackPanel();
            if (IndicatorArea == null)
                Chart.AddControl(panel);
            else
                IndicatorArea.AddControl(panel);
            panel.HorizontalAlignment = hor;
            panel.VerticalAlignment = ver;
            panel.Orientation = orient;
            return panel;
        }

        public WrapPanel AddWrapPanel(HorizontalAlignment hor = HorizontalAlignment.Right, VerticalAlignment ver = VerticalAlignment.Bottom, Orientation orient = Orientation.Vertical)
        {
            WrapPanel panel = new WrapPanel();
            if (IndicatorArea == null)
                Chart.AddControl(panel);
            else
                IndicatorArea.AddControl(panel);
            panel.HorizontalAlignment = hor;
            panel.VerticalAlignment = ver;
            panel.Orientation = orient;
            return panel;
        }

        public Button AddButton(Panel panel, int ID, string txt, Color clr, bool WithEvent = true, int width = 40, int height = 20, double fsize = 11)
        {
            Button btn = new Button();
            btn.Left = ID;
            if (WithEvent)
                btn.Click += OnButtonClick;
            panel.AddChild(btn);
            btn.ForegroundColor = clr;
            btn.BackgroundColor = Color.Black;
            btn.BorderColor = btn.ForegroundColor;
            btn.Width = width;
            btn.Height = height;
            btn.Text = txt;
            btn.HorizontalContentAlignment = HorizontalAlignment.Left;
            btn.VerticalContentAlignment = VerticalAlignment.Center;
            btn.FontSize = fsize;
            return btn;
        }

        string Pri = "";
        string Sec = "";

        private void OnButtonClick(ButtonClickEventArgs obj)
        {
            Print(obj.Button.Left, " ", obj.Button.Text);
            if (obj.Button.Left == 1)
            {
                DrawState += 1;
                if (DrawState == DrawStates.Length)
                    DrawState = 0;
                obj.Button.Text = DrawStates[DrawState];
                if (DrawState == 0)
                    if (ClusterPanel != null)
                        ClusterPanel.IsVisible = true;
                if (DrawState == 1 | DrawState == 2)
                    if (ClusterPanel != null)
                        ClusterPanel.IsVisible = false;
                if (DrawState == 2)
                    Chart.RemoveAllObjects();
            }
            if (obj.Button.Left >= 2)
            {
                if (Sec != "")
                {
                    Pri = "";
                    Sec = "";
                }
                if (Pri == "")
                {
                    Pri = obj.Button.Text.Split(" ".ToCharArray(), 2)[1];
                }
                else
                {
                    Sec = obj.Button.Text.Split(" ".ToCharArray(), 2)[1];
                }
                Print(Pri, " ", Sec);
                if (Pri != "" && Sec != "")
                    if (!Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame, Pri + Sec))
                        Chart.TryChangeTimeFrameAndSymbol(Chart.TimeFrame, Sec + Pri);
            }
        }

//  imported from cLib VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV

        public int TimeFrameToMinutes(TimeFrame TF)
        {
            if (TF.ToString() == "Minute")
                return 1;
            else if (TF.ToString().StartsWith("Minute"))
                return int.Parse(TF.ToString().Replace("Minute", ""));
            else if (TF.ToString() == "Hour")
                return 60;
            else if (TF.ToString().StartsWith("Hour"))
                return 60 * int.Parse(TF.ToString().Replace("Hour", ""));
            else if (TF.ToString() == "Daily")
                return 60 * 24;
            else if (TF.ToString().StartsWith("Day"))
                return 60 * 24 * int.Parse(TF.ToString().Replace("Day", ""));
            else if (TF.ToString() == "Weekly")
                return 60 * 24 * 5;
            else if (TF.ToString() == "Monthly")
                return 60 * 24 * 5 * 4;
            else
                return 0;
        }

        public double Map(double v, string Scale)
        {
            double[] scale = StringToDoubleArray(Scale);
            return Map(v, scale[0], scale[1], scale[2], scale[3]);
        }

        public double Map(double v, double fromBot, double fromTop, double toBot, double toTop)
        {
            double ret = toBot + (v - fromBot) * (toTop - toBot) / (fromTop - fromBot);
            return ret;
        }

        public double[] StringToDoubleArray(string str)
        {
            return str.Split(',').Select(z => double.Parse(z)).ToArray();
        }

        public double SeriesMaximum(DataSeries s, int idx1, int idx2)
        {
            double max = double.MinValue;
            for (int i = idx1; i <= idx2; i++)
                max = Math.Max(max, s[i]);
            return max;
        }

        public double SeriesMinimum(DataSeries s, int idx1, int idx2)
        {
            double min = double.MaxValue;
            for (int i = idx1; i <= idx2; i++)
                min = Math.Min(min, s[i]);
            return min;
        }

        public double SeriesMaximum(IndicatorDataSeries s, int idx1, int idx2)
        {
            double max = double.MinValue;
            for (int i = idx1; i <= idx2; i++)
                max = Math.Max(max, s[i]);
            return max;
        }

        public double SeriesMinimum(IndicatorDataSeries s, int idx1, int idx2)
        {
            double min = double.MaxValue;
            for (int i = idx1; i <= idx2; i++)
                min = Math.Min(min, s[i]);
            return min;
        }

        public double RiskToLots(double RiskPct, Symbol s = null)
        {
            if (s == null)
                return Symbol.VolumeInUnitsToQuantity(RiskToUnits(RiskPct));
            else
                return s.VolumeInUnitsToQuantity(RiskToUnits(RiskPct, s));
        }

        public double RiskToUnits(double RiskPct, Symbol s = null)
        {
            if (s == null)
                return Symbol.NormalizeVolumeInUnits((Account.FreeMargin * RiskPct / 100 / Symbol.Ask * Account.PreciseLeverage), RoundingMode.Down);
            else
                return s.NormalizeVolumeInUnits((Account.FreeMargin * RiskPct / 100 / s.Ask * Account.PreciseLeverage), RoundingMode.Down);
        }

    }
}

 


@gennimatas

PanagiotisCharalampous
04 Feb 2020, 10:43

Hi Takis Genn,

I have being trying to reproduce this behavior but without success. It seems to work fine for me. Any chance you can record a short video demonstrating the steps you follow to reproduce this issue?

Best Regards,

Panagiotis 

Join us on Telegram

 


@PanagiotisCharalampous

gennimatas
04 Feb 2020, 11:00

Hi Panagiotis

weird, isn't it?

I have an i5 set to 15% max cpu load. Want to try it? lol

I will increase max cpu load, test it again and let you know.

I might also record it, i have the tools.

Takis


@gennimatas