cBot crashed: Error #65147970 / Crashed in trade result callback with ArgumentException:

Created at 14 Dec 2021, 21:29
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!
M4

m4trader4

Joined 19.09.2021

cBot crashed: Error #65147970 / Crashed in trade result callback with ArgumentException:
14 Dec 2021, 21:29


Hi,

I have a CBot running 24/5, which fires up three orders in parallel for every new bar of Renko and Range, and then modifies it for StopLoss and TakeProfit. I get error the following errors randomly. 

1. cBot crashed: Error #65147970

2. Crashed in trade result callback with ArgumentException: An item with the same key has already been added.

3. Crashed in trade result callback with IndexOutOfRangeException: Index was outside the bounds of the array.

This Bot works on about 8-14 charts/Symbols and randomly one of them gives this error, some times the order is filled without SL/TP or Partial order out of 3 parallel orders is filled with/without SL/TP and the CBot crashes. Which becomes a dangerous situation if especially SL is not there. 

Have onscreen buttons, checkbox, textbox attached pic

Appreciate quick response with resolution as its scary to run the CBot 24/5

Regards

Ahmed

 

using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using System.Threading;
using System.Threading.Tasks;
using System.Globalization;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ErrorBotV1 : Robot
    {
        public double Spread;

        [Parameter(DefaultValue = 0.0)]
        public double Parameter { get; set; }

        
        protected override void OnStart()
        {
            // Put your initialization logic here
            Spread = 0;
            Bars.BarOpened += Bars_BarOpened;
        }



        private void PlaceAutoOrder(double RiskAmt, string TempCBPCSP, double AOC1O, double AOC1H, double AOC1L, double AOC1C, double AOC2O, double AOC2H, double AOC2L, double AOC2C,
       double AOC3O, double AOC3H, double AOC3L, double AOC3C, TradeType TempTtype, string TempSymName, double TempSlRange, double TempAutoOrderIncrement, double TempAutoOrderTpIncrement, string TempAO)
        {
            double TempDiffPoints = AOC1H - AOC1L;
            double TempEntry = 0;
            string OrderComment = "";
            double TempSl = 0;
            double TempTp = 0;
            double TempSlComment = 0;
            double TempTpComment = 0;
            double TempSlBuy = 0;
            double TempSlSell = 0;

            TempSlBuy = Math.Round(AOC1L - Spread, Symbol.Digits + 1);
            TempSlSell = Math.Round(AOC1H + Spread, Symbol.Digits + 1);

            switch (TempTtype)
            {
                case TradeType.Sell:
                    TempEntry = AOC1L;
                    TempSlComment = TempSlSell;


                    if (TempTp == 0)
                    {
                        TempTpComment = 0;
                    }
                    if (TempTp != 0)
                    {
                        TempTpComment = TempEntry - TempTp;
                    }
                    // Do Not Change the Sequence If Changed Check Position call, total feilds 16

                    OrderComment = (TempAO + ":" + TempTtype + ":E:" + TempEntry + ":Sl:" + TempSlComment + ":Tp:" + TempTpComment + ":S:" + Spread + ":SLR:" + 0 + ":R:" + 0 + ":T:" + Chart.TimeFrame);
                    break;
                case TradeType.Buy:
                    TempEntry = AOC1H;
                    TempSlComment = TempSlBuy;

                    if (TempTp == 0)
                    {
                        TempTpComment = 0;
                    }
                    if (TempTp != 0)
                    {
                        TempTpComment = TempEntry + TempTp;
                    }
                  ;
                    // Do Not Change the Sequence If Changed Check Position call, total feilds 16
                    OrderComment = (TempAO + ":" + TempTtype + ":E:" + TempEntry + ":Sl:" + TempSlComment + ":Tp:" + TempTpComment + ":S:" + Spread + ":SLR:" + 0 + ":R:" + 0 + ":T:" + Chart.TimeFrame);
                    break;
            }

            try
            {
                Task.Run(() => ExecuteMarketOrderAsync(TempTtype, TempSymName, 1, "", 0, 0, OrderComment, PositionOpened));

            }
            catch (Exception exception)
            {
                Print("ExecuteMarketOrderAsync : " + exception.Message + "=StackTrace=" + exception.StackTrace + "=innerException=" + exception.InnerException);
            }

        }



        private void PositionOpened(TradeResult tradeResult)
        {
            var position = tradeResult.Position;

            //Print(tradeResult.ToString());
            if (tradeResult.IsSuccessful && position.Comment != "")
            {

                //  Print("Position=" + position.Id + "=PEnrty=" + position.EntryPrice + "=PSl=" + position.StopLoss + "=PComment=" + position.Comment);
                string[] TempSlArray = position.Comment.Split(new char[]
                {
                    ':'
                });

                if (TempSlArray.Length > 0 )
                {
                    double TempSl;
                    double TempTp;
                    double.TryParse(TempSlArray[5], out TempSl);
                    double.TryParse(TempSlArray[7], out TempTp);
                    //  Print("TempSLPrice=" + TempSl + "=PositionSl=" + position.StopLoss);

                    if (TempSl != position.StopLoss || TempTp != position.TakeProfit)
                    {
                        //    Print("StopLoss/TakeProfit not equal");
                        ModifyPositionAsync(position, TempSl, TempTp);

                    }

                }
            }
        }



        private void AutoOrder(Bars bars)
        {
            double AOC1O = bars.OpenPrices.Last(1);
            double AOC1H = bars.HighPrices.Last(1);
            double AOC1L = bars.LowPrices.Last(1);
            double AOC1C = bars.ClosePrices.Last(1);

            double AOC2O = Bars.OpenPrices[Bars.Count - 3];
            double AOC2H = Bars.HighPrices[Bars.Count - 3];
            double AOC2L = Bars.LowPrices[Bars.Count - 3];
            double AOC2C = Bars.ClosePrices[Bars.Count - 3];


            double AOC3O = Bars.OpenPrices[Bars.Count - 4];
            double AOC3H = Bars.HighPrices[Bars.Count - 4];
            double AOC3L = Bars.LowPrices[Bars.Count - 4];
            double AOC3C = Bars.ClosePrices[Bars.Count - 4];

            if (AOC2L > AOC1L )
            {

                try
                {
                    Parallel.Invoke(() => { PlaceAutoOrder(0, "", AOC1O, AOC1H, AOC1L, AOC1C, AOC2O, AOC2H, AOC2L, AOC2C, AOC3O, AOC3H, AOC3L, AOC3C, TradeType.Sell, Symbol.Name, 0, 0, 3, "AO1EVtx3"); },
                   () => { PlaceAutoOrder(0, "", AOC1O, AOC1H, AOC1L, AOC1C, AOC2O, AOC2H, AOC2L, AOC2C, AOC3O, AOC3H, AOC3L, AOC3C, TradeType.Sell, Symbol.Name, 0, 0, 1.75, "AO1EVtx1.75"); },
                   () => { PlaceAutoOrder(0, "", AOC1O, AOC1H, AOC1L, AOC1C, AOC2O, AOC2H, AOC2L, AOC2C, AOC3O, AOC3H, AOC3L, AOC3C, TradeType.Sell, Symbol.Name, 0, 0, 0, "AO1"); }
                   );
                }
                catch (AggregateException e)
                {

                    Print("Sell PlaceAutoOrderCall" + Symbol.Name + " Thread THIS WAS UNEXPECTED.\n{0}", e.InnerException.ToString() + "=InnerExeptions=" + e.InnerExceptions.ToString() + "=Message=" + e.Message);
                }

            }

            if (AOC1H > AOC2H)
            {
                try
                {
                    Parallel.Invoke(() => { PlaceAutoOrder(0, "", AOC1O, AOC1H, AOC1L, AOC1C, AOC2O, AOC2H, AOC2L, AOC2C, AOC3O, AOC3H, AOC3L, AOC3C, TradeType.Buy, Symbol.Name, 0, 0, 3, "AO1EVtx3"); },
                 () => { PlaceAutoOrder(0, "", AOC1O, AOC1H, AOC1L, AOC1C, AOC2O, AOC2H, AOC2L, AOC2C, AOC3O, AOC3H, AOC3L, AOC3C, TradeType.Buy, Symbol.Name, 0, 0, 1.75, "AO1EVtx1.75"); },
                 () => { PlaceAutoOrder(0, "", AOC1O, AOC1H, AOC1L, AOC1C, AOC2O, AOC2H, AOC2L, AOC2C, AOC3O, AOC3H, AOC3L, AOC3C, TradeType.Buy, Symbol.Name, 0, 0, 0, "AO1"); }
                 );

                }
                catch (AggregateException e)
                {
                    Print("Buy PlaceAutoOrderCall" + Symbol.Name + " Thread THIS WAS UNEXPECTED.\n{0}", e.InnerException.ToString() + "=InnerExeptions=" + e.InnerExceptions.ToString() + "=Message=" + e.Message);

                }
            }

        }

        private void Bars_BarOpened(BarOpenedEventArgs obj)
        {
            var bars = obj.Bars;

            try
            {
                Parallel.Invoke(() =>
                {

                    AutoOrder(bars);
                },
                () =>
                {
                    //Some Other Calls to move the stoploss and TakeProfit
                }
                );
            }
            catch (AggregateException e)
            {

                Print("Bars Opened" + Symbol.Name + " Thread THIS WAS UNEXPECTED.\n{0}", e.InnerException.ToString() + "=InnerExeptions=" + e.InnerExceptions.ToString() + "=Message=" + e.Message);
            }



        }

        protected override void OnTick()
        {
            // Put your core logic here
            Spread = Symbol.Spread;
        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }
    }
}

@m4trader4
Replies

m4trader4
14 Dec 2021, 21:52

This happens when the

1. The Market is volatile or at events

2. When the new bar formed is with gap

3. At time of opening or closing

You can check this for gold (13:00 GMT) for a range bar/renko brick size of 10 pips, Dow (14:30 GMT) range bar/renko brick size of 10 pips and Nasdaq (14:30 GMT) for range bar/renko brick size of 10 pips 


@m4trader4

amusleh
15 Dec 2021, 09:52 ( Updated at: 15 Dec 2021, 18:13 )

Hi,

I checked your code, the problem is automate API is not thread safe, and when you use Parallel it executes all passed operations on several thread pool threads concurrently.

As the API is not thread safe it causes race condition and that causes those errors.

Please first use the Parallel correctly, why you keep calling Parallel.Invoke on several places, just call it where you need it.

And why you are using Task.Run? you are mixing everything.

I don't think you will get any performance benefit by using Parallel, because the orders must be sent to server via a socket connection, you can't send multiple messages via a single socket connections at the same time concurrently.

I think using ExecuteMarketOrderAsync is enough and that's the fastest possible way to execute multiple orders, don't try to use multiple threads to send several commands to server.

You can use multi threading on your indicator/cBot for processing large data but don't use it for order execution.

If you want to send multiple order requests concurrently then you can use Open API or FIX, there you are in charge of number of connections you make and you can send several messages at the same time from multiple socket connections.

 


@amusleh

m4trader4
16 Dec 2021, 20:32

RE:

Ahmad

Thanks for the response, appreciate it.

I was doing trial and error to avoid the race condition, so there is a mix of task run and generous use of parallel. To extent was successfull, but was not confident to run  24/5.

Have rewritten the block after you comment about API is not thread safe. Is there any thoughts to make the API thread safe?

Regards

Ahmed


@m4trader4

amusleh
17 Dec 2021, 09:28

Hi,

Concurrency and multi threading is your responsibility, APIs mostly aren't thread safe.

If you want to use multi threading you must do all the work.

After cTrader 4.2 release you will be able to use .NET async/await and Tasks, so you don't have to use Parallel or create new threads by your self.

Please read the documentation of Task, Parallel, and .NET multi threading before using them.


@amusleh

m4trader4
19 Dec 2021, 15:06

RE:

Ahmad

Sure will do that.

I am trying to get the guppys ema's check using parallel/concurency using concurrent collections, appreciate if you could have a look at the code provide pointers for faster calculations.





   public string GuppyCheck(int bI, int Period, double CandleHigh, double CandleLow)
        {
            string RetGuppyCheck = "False";

            var GuppyResult = Indicators.ExponentialMovingAverage(Bars.ClosePrices, Period).Result.Last(Bars.Count - bI - 1);
            if ((GuppyResult <= CandleHigh && GuppyResult >= CandleLow))
            {
               RetGuppyCheck = "True";
            }
            else
                RetGuppyCheck = "False";

            return RetGuppyCheck;
        }

        public string ChartRG(int bI, double CandleHigh, double CandleLow)
        {
            // Print("BarIndex=" + bI + "=BarCount=" + Bars.Count);

            string ChartRGResult = "nEVT";

            var TempShortList = new List<string>();
            var TempLongList = new List<string>();

            TempShortList.Add(GuppyCheck(bI, 3, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 5, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 8, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 10, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 12, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 15, CandleHigh, CandleLow));

            TempLongList.Add(GuppyCheck(bI, 30, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 35, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 40, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 45, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 50, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 60, CandleHigh, CandleLow));

            var CountTempShortTrue = TempShortList.Count(item => item.Contains("True"));
            var CountTempLongTrue = TempLongList.Count(item => item.Contains("True"));

            //Print("CountTempShortTrue=" + CountTempShortTrue + "CountTempLongTrue" + CountTempLongTrue);




            if (CountTempShortTrue == 6)
            {
                //   Print("All 6 In");
                ChartRGResult = "EVT";
            }


            if (CountTempShortTrue == 6 && CountTempLongTrue == 6)
            {
                // Print("All B6 In R6 In");
                ChartRGResult = "EVT";
            }

            if (CountTempShortTrue == 6 && CountTempLongTrue >= 1 && CountTempLongTrue < 6)
            {
                //Print("All B6 In R6 In");
                ChartRGResult = "nEVT";
            }


            return ChartRGResult;
        }

 


@m4trader4

m4trader4
19 Dec 2021, 15:09

RE: RE:

Tried using concurrent collections given up...


@m4trader4

amusleh
20 Dec 2021, 09:49

RE: RE:

m4trader4 said:

Ahmad

Sure will do that.

I am trying to get the guppys ema's check using parallel/concurency using concurrent collections, appreciate if you could have a look at the code provide pointers for faster calculations.





   public string GuppyCheck(int bI, int Period, double CandleHigh, double CandleLow)
        {
            string RetGuppyCheck = "False";

            var GuppyResult = Indicators.ExponentialMovingAverage(Bars.ClosePrices, Period).Result.Last(Bars.Count - bI - 1);
            if ((GuppyResult <= CandleHigh && GuppyResult >= CandleLow))
            {
               RetGuppyCheck = "True";
            }
            else
                RetGuppyCheck = "False";

            return RetGuppyCheck;
        }

        public string ChartRG(int bI, double CandleHigh, double CandleLow)
        {
            // Print("BarIndex=" + bI + "=BarCount=" + Bars.Count);

            string ChartRGResult = "nEVT";

            var TempShortList = new List<string>();
            var TempLongList = new List<string>();

            TempShortList.Add(GuppyCheck(bI, 3, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 5, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 8, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 10, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 12, CandleHigh, CandleLow));
            TempShortList.Add(GuppyCheck(bI, 15, CandleHigh, CandleLow));

            TempLongList.Add(GuppyCheck(bI, 30, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 35, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 40, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 45, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 50, CandleHigh, CandleLow));
            TempLongList.Add(GuppyCheck(bI, 60, CandleHigh, CandleLow));

            var CountTempShortTrue = TempShortList.Count(item => item.Contains("True"));
            var CountTempLongTrue = TempLongList.Count(item => item.Contains("True"));

            //Print("CountTempShortTrue=" + CountTempShortTrue + "CountTempLongTrue" + CountTempLongTrue);




            if (CountTempShortTrue == 6)
            {
                //   Print("All 6 In");
                ChartRGResult = "EVT";
            }


            if (CountTempShortTrue == 6 && CountTempLongTrue == 6)
            {
                // Print("All B6 In R6 In");
                ChartRGResult = "EVT";
            }

            if (CountTempShortTrue == 6 && CountTempLongTrue >= 1 && CountTempLongTrue < 6)
            {
                //Print("All B6 In R6 In");
                ChartRGResult = "nEVT";
            }


            return ChartRGResult;
        }

 

Hi,

I don't see any concurrent collection or code on your posted code.

Some things you can use concurrency easily without any side effect will be Linq queries and deterministic methods or loops witch will not make any change on your app state.

For Linq queries you can use Parallel Linq and for loops you can use Parallet.For or Parallel.Foreach.

Concurrency is hard, if you want to use it you must have a good knowledge of it.

If the amount of data is not large or your functions that you want to run on each item of your data is not getting lots of time then using concurrency will have negative impact on your code performance.

If you are interested in this topic I recommend you to read this book: Concurrency in .NET: Modern patterns of concurrent and parallel programming: Terrell, Riccardo: 9781617292996: Amazon.com: Books

And as I said on my previous post, after cTrader 4.2 you will be able to use Tasks and async/await, which will allow you to run your code asynchronously, its not same thing as concurrency but it will help a lot in terms of performance and resource usage.

I have worked on lots of cTrader indicators and cBots, and I have never faced any need for using concurrency, before using it please ask yourself if you really need it or not?

And benchmark your code sequential version alongside concurrent version, be sure it gives you benefit and it doesn't have any negative impact on performance.


@amusleh