cTrader 4.2.22 Reading History causes deadlock, cBot hangs until you stop it manually.

Created at 10 Oct 2022, 23:46
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!
ER

erdeizol

Joined 10.10.2022

cTrader 4.2.22 Reading History causes deadlock, cBot hangs until you stop it manually.
10 Oct 2022, 23:46


Hi, first of all, thank you for this awesome product, using dotnet is a pleasure after programming MT. However I encountered some issues after upgrading 4.2.2 and dotnet 6.0. Please help me with this one, as I cannot solve it by tweaking my codes.

Reproducing the issue:

 

  1. Compile the cBot code below using cTrader 4.2.22 and .NET 6.0 and place it on a chart (I've used US30 index).
  2. Try the code: it works if you open 1 or 2 position manually then start the bot: it closes your positions and Prints the details.
  3. Open 10 - 15 small positions manually then start the bot
  4. Notice that it closes the positions but then hangs: keeps running but even onTick() not called anymore
  5. Stop the bot manually
  6. Notice that it stops only after 10-15 seconds, and Prints an exception:

 

10/10/2022 21:53:45.808 | Exception occurred: cTrader.Automate.Host.Dispatcher.Exceptions.AutomateDispatcherAbortException: Exception of type 'cTrader.Automate.Host.Dispatcher.Exceptions.AutomateDispatcherAbortException' was thrown.
   at cTrader.Automate.Host.Dispatcher.AutomateTargetDispatcher.NestedLoop(CancellationToken cancellationToken, Boolean allowIdle)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.Wait[TMessage](Predicate`1 predicate)
   at cTrader.Automate.Host.Runtime.Controllers.Positions.SmallPositionApiController.cTrader.Automate.Domain.Shared.Messaging.IAutomateMessageHandler<cTrader.Automate.Domain.Shared.OrderApi.PositionClosedMessage>.Handle(PositionClosedMessage message)
   at cTrader.Automate.Domain.Shared.Dispatcher.DispatcherAction.DispatcherActionImpl`1.Invoke()
   at cTrader.Automate.Host.Dispatcher.AutomateTargetDispatcher.TryInvokeThreadSafeAction()
   at cTrader.Automate.Host.Dispatcher.AutomateTargetDispatcher.NestedLoop(CancellationToken cancellationToken, Boolean allowIdle)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.Wait[TMessage](Predicate`1 predicate)
   at cTrader.Automate.Host.Runtime.Controllers.Positions.SmallPositionApiController.cTrader.Automate.Domain.Shared.Messaging.IAutomateMessageHandler<cTrader.Automate.Domain.Shared.OrderApi.PositionClosedMessage>.Handle(PositionClosedMessage message)
   at cTrader.Automate.Domain.Shared.Dispatcher.DispatcherAction.DispatcherActionImpl`1.Invoke()
   at cTrader.Automate.Host.Dispatcher.AutomateTargetDispatcher.TryInvokeThreadSafeAction()
   at cTrader.Automate.Host.Dispatcher.AutomateTargetDispatcher.NestedLoop(CancellationToken cancellationToken, Boolean allowIdle)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.Wait[TMessage](Predicate`1 predicate)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.SendAndWait[TMessage](IAutomateMessage message, Predicate`1 predicate)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.SendAndWait[TMessage](IAutomateMessage message)
   at cTrader.Automate.Host.Runtime.Controllers.History.SmallHistoryApiController.Load()
   at cTrader.Automate.Host.Runtime.Api.History.SmallHistory.Activate()
   at cTrader.Automate.Host.Runtime.Api.History.SmallHistory.get_Count()
   at cTrader.Automate.Adapters.HistoryApi.HistoryAdapter.get_Count()
   at cTrader.Automate.Host.Enumerable.GenericEnumerator`1.MoveNext()
   at System.Linq.Enumerable.TryGetLast[TSource](IEnumerable`1 source, Func`2 predicate, Boolean& found)
   at System.Linq.Enumerable.LastOrDefault[TSource](IEnumerable`1 source, Func`2 predicate)
   at cAlgo.Hedge2.writeHedgedPositionData(Position position) in C:\Users\Erdei Zoltán\Documents\cAlgo\Sources\Robots\Hedge2\Hedge2\Hedge2.cs:line 96

 

Codes of the robot for testing:

 

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

namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class Hedge2 : Robot
    {
        private List<TradeOperation> operations = new List<TradeOperation>();
        private List<TradeResult> results = new List<TradeResult>();

        protected override void OnStart()
        {
            bool hasBuy = false;
            bool hasSell = false;
            var buys = new List<Position>();
            var sells = new List<Position>();

            foreach (var position in Positions.Where(pos => pos.SymbolName == SymbolName))
            {
                if (position.TradeType == TradeType.Buy)
                {
                    hasBuy = true;
                    buys.Add(position);
                }
                else if (position.TradeType == TradeType.Sell)
                {
                    hasSell = true;
                    sells.Add(position);
                }
            }

            if (!hasBuy && !hasSell)
            {
                Print("No position found. Stopping cBot..");
                Stop();
            }
            if (hasBuy && hasSell)
            {
                Print("Both direction positions found. Stopping cBot..");
                Stop();
            }

            if (hasBuy)
            {
                foreach (var position in buys)
                {
                    operations.Add(ClosePositionAsync(position, result => onPositionClosed(position, result)));
                }
            }

            if (hasSell)
            {
                foreach (var position in sells)
                {
                    operations.Add(ClosePositionAsync(position, result => onPositionClosed(position, result)));
                }
            }
        }

        protected override void OnTick()
        {
            Print("OnTick called");
            if (operations.Count > results.Count)
            {
                Print("Waiting for results, ready: " + results.Count + " / " + operations.Count);
            } 
            else
            {
                Print("Stopping cBot..");
                Stop();
            }
        }

        private void onPositionClosed(Position position, TradeResult result)
        {
            Print("Position closed result: IsSuccessful: " + result.IsSuccessful + ", id: " + position.Id);
            if (result.IsSuccessful)
            {
                writeHedgedPositionData(position);
            }
            else
            {
                Print("Closing failed, error: " + result.Error);
            }
            results.Add(result);
        }

        private void writeHedgedPositionData(Position position)
        {
            try
            {
                var historyPosition = History.LastOrDefault(hist => hist.PositionId == position.Id);
                var closePrice = historyPosition != null ? historyPosition.ClosingPrice : (position.TradeType == TradeType.Buy ? Symbol.Bid : Symbol.Ask);
                Print(Account.Number + ";" + position.SymbolName + ";" + position.TradeType + ";" + position.Quantity + ";" + position.VolumeInUnits + ";" +
                    position.EntryPrice + ";" + closePrice + ";" + (position.StopLoss == null ? "null" : "" + position.StopLoss) + ";" + (position.TakeProfit == null ? "null" : "" + position.TakeProfit));
            } catch (Exception e)
            {
                Print("Exception occurred: " + e);
                throw;
            }
        }

    }
}

 

This code above is the simplest way I found to reproduce the issue reliably. 
In my other codes this occurs randomly for time to time, that is very annoying. Please help by addressing this issue in an upcoming release.

Thanks,
Zoltán Erdei


@erdeizol
Replies

erdeizol
18 Nov 2022, 09:05

RE:

I've encountered a new exception stack trace, when calling Bars.LoadMoreHistory(); after a server reconnection:

cTrader.Automate.Host.Dispatcher.Exceptions.AutomateDispatcherAbortException: Exception of type 'cTrader.Automate.Host.Dispatcher.Exceptions.AutomateDispatcherAbortException' was thrown.
   at cTrader.Automate.Host.Dispatcher.AutomateTargetDispatcher.NestedLoop(CancellationToken cancellationToken, Boolean allowIdle)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.Wait[TMessage](Predicate`1 predicate)
   at cTrader.Automate.Host.Router.AutomateTargetRouter.SendAndWait[TMessage](IAutomateMessage message, Predicate`1 predicate)
   at cTrader.Automate.Host.Runtime.Controllers.Chart.SmallChartSourceApiController.LoadMore(ChartSourceId chartSourceId)
   at cTrader.Automate.Host.Runtime.Api.Market.ChartSources.SmallWritableChartSource.LoadMoreHistory()
   at cTrader.Automate.Adapters.MarketApi.BarsAdapter.LoadMoreHistory()

The robot was hanging for days, onTick() not called since the Bars.LoadMoreHistory(); call. This exception was thrown when I stoped manually.


@erdeizol