Topics

Forum Topics not found

Replies

amusleh
15 Mar 2022, 08:54

Hi,

Installing a new broker cTrader will not delete or replace your current indicators/cBots/templates.

All installed cTrader instances will use the same automate folder data.


@amusleh

amusleh
15 Mar 2022, 08:52

Hi,

This can be caused by symbol spread or something might be wrong with your code, can you post a sample cBot code that can reproduce this issue on visual back test?


@amusleh

amusleh
14 Mar 2022, 18:10

RE: RE:

undseth said:

amusleh said:

Hi,

The data series itself has a minimum method, so you can use is like this:

// This gives you the lowest price in last 20 bars lows
var lowestLow = Bars.LowPrices.Minimum(20)

 

 

Hi again,

This will get the lowest low from start index 1, but I need it to get the lowest low from eks start index 10, so it shuld

start at index 10 and get the lowest low in a periode of 20 bars. Hope this make sense??

 

Thanks for all the help!

Halvard

Then use the method I posted on my first post, you can set the start/end index and it will give you the low, ex:

// This will return the lowest low between bar index 90 and 100
var low = Minimum(Bars.LowPrices, 90, 100);

 


@amusleh

amusleh
14 Mar 2022, 08:58

Hi,

You can use the loaded Bars BarOpened event instead of Bot OnBar method, ex:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class AFFS : Robot
    {
        [Parameter("Name", DefaultValue = "AFFS")]
        public string _name { get; set; }

        [Parameter("Start Hour", DefaultValue = 6.0)]
        public double _starttime { get; set; }

        [Parameter("Stop Hour", DefaultValue = 18.0)]
        public double _stoptime { get; set; }

        [Parameter("Max Lots", DefaultValue = 100.0, MinValue = 0.01, MaxValue = 1000.0)]
        public double _maxlots { get; set; }

        [Parameter("Starting Risk (%)", DefaultValue = 1.0, MinValue = 0.01, MaxValue = 100.0)]
        public double _startingrisk { get; set; }

        [Parameter("Risk Step (%)", DefaultValue = 1.0, MinValue = 0.01, MaxValue = 100.0)]
        public double _riskstep { get; set; }

        [Parameter("Take Profit", DefaultValue = 5.0, MinValue = 0.1, MaxValue = 100.0)]
        public double _takeprofit { get; set; }

        [Parameter("Stop Loss", DefaultValue = 10.0, MinValue = 0.1, MaxValue = 100.0)]
        public double _stoploss { get; set; }

        [Parameter("Safety (Pips)", DefaultValue = 3.0, MinValue = 0.1, MaxValue = 100.0)]
        public double _safety { get; set; }

        [Parameter("High/Low Timeframe", DefaultValue = "Daily")]
        public TimeFrame _hltf { get; set; }

        private Bars _hlbars;
        private double _bullprice, _bearprice, _previoushigh, _previouslow, _maxunits, _activerisk;
        private bool _todaybuy, _todaysell;

        protected override void OnStart()
        {
            _hlbars = MarketData.GetBars(_hltf);

            _hlbars.BarOpened += _hlbars_BarOpened;
            _safety *= Symbol.PipSize;
        }

        private void _hlbars_BarOpened(BarOpenedEventArgs obj)
        {
            var _currenthours = Server.Time.TimeOfDay.TotalHours;
            bool _tradetime = _starttime < _stoptime ? _currenthours > _starttime && _currenthours < _stoptime : _currenthours < _stoptime || _currenthours > _starttime;

            _activerisk = GetRisk();
            var _volume = GetVolume();
            _maxunits = Symbol.QuantityToVolumeInUnits(_maxlots);
            if (_volume > _maxunits)
            {
                _volume = _maxunits;
            }
            _previoushigh = _hlbars.HighPrices.Last(1);
            _previouslow = _hlbars.LowPrices.Last(1);
            _bullprice = Symbol.Ask + _safety;
            _bearprice = Symbol.Bid - _safety;

            if (_tradetime && !_todaybuy)
            {
                if (_bullprice > _previoushigh)
                {
                    ExecuteMarketOrder(TradeType.Buy, Symbol.Name, _volume, _name, _stoploss, _takeprofit);
                    _todaybuy = true;
                }
            }
            if (!_tradetime && _todaybuy)
            {
                _todaybuy = false;
            }

            if (_tradetime && !_todaysell)
            {
                if (_bearprice < _previouslow)
                {
                    ExecuteMarketOrder(TradeType.Sell, Symbol.Name, _volume, _name, _stoploss, _takeprofit);
                    _todaysell = true;
                }
            }
            if (!_tradetime && _todaysell)
            {
                _todaysell = false;
            }
        }

        private double GetRisk()
        {
            double _result = 0.0;
            double _maxrisk = 10.0;
            var _lastposition = History.FindLast(_name);
            bool _win = _lastposition != null && _lastposition.NetProfit > 0.0 ? true : false;
            if (_win && _activerisk < _maxrisk)
            {
                _result = _activerisk + _riskstep;
            }
            else if (!_win)
            {
                _result = _startingrisk;
            }
            return _result;
        }

        private double GetVolume()
        {
            double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;

            double baseNumber = Account.Balance;

            double sizeInLots = Math.Round((baseNumber * _activerisk / 100) / (_stoploss * costPerPip), 2);

            var result = Symbol.QuantityToVolumeInUnits(sizeInLots);

            if (result > Symbol.VolumeInUnitsMax)
            {
                result = Symbol.VolumeInUnitsMax;
            }
            else if (result < Symbol.VolumeInUnitsMin)
            {
                result = Symbol.VolumeInUnitsMin;
            }
            else if (result % Symbol.VolumeInUnitsStep != 0)
            {
                result = result - (result % Symbol.VolumeInUnitsStep);
            }

            return result;
        }
    }
}

This way the execution logic will be executed only once per bar based on your selected time frame.


@amusleh

amusleh
14 Mar 2022, 08:50

RE: RE: RE: can this be used for indicators as well as cbots?

bonedrak said:

amusleh said:

bonedrak said:

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 

Hi,

You should use RenkoSeries OnBar only in backtesting environment and the Calculate method on live mode.

I want to use a custom indicator in backtesting a cbot, but it wont allow me because it says the time frames are different, for the cbot  (which uses your renko bars) and the indicator (which uses "calculate(int index)").  So my question is, can an indicator be built that uses your library and renko bars? or will it only work on cbots?

Hi,

You can't feed a custom indicator the Renko generated bars, it's not possible.

The solution is to create a class for your indicator inside cBot project, pass it the Renko generated bars data and then call it's OnBar method whenever you receive a bar from Renko library.

It's little complicated but it's possible.


@amusleh

amusleh
14 Mar 2022, 08:46

Hi,

The data series itself has a minimum method, so you can use is like this:

// This gives you the lowest price in last 20 bars lows
var lowestLow = Bars.LowPrices.Minimum(20)

 


@amusleh

amusleh
14 Mar 2022, 08:43

Hi,

What do you mean by executing market order and buy £10 per point?

For profit/loss calculation you can use symbol Pip/Tick value.

A symbol tick/Pip value gives you one tick/Pip of symbol monetary value in account currency.

If you want to set a £50 stop loss and your account currency is GBP then you can divide 50 to symbol pip value and use the result as stop loss in Pips.


@amusleh

amusleh
14 Mar 2022, 08:38

Hi,

It's not possible, in automate section the logs tab content is cleared only if you rebuild or restart your indicator/cBot.

You can open a thread under suggestions section for this feature if you want to.

And for now it's not on our timeline.


@amusleh

amusleh
14 Mar 2022, 08:36

Hi,

The API can go down for short period of time during weekends for maintenance.

If the disconnection happens from server side then it means API is down and you should keep trying to reconnect until you succeed.

 


@amusleh

amusleh
14 Mar 2022, 08:34 ( Updated at: 14 Mar 2022, 08:35 )

Hi,

It's possible, you can load multiple symbols Bars inside your cBot on backtest and use them.

You can use a symbol Tick event or it's Bars BarOpened event.

cAlgo API Reference - Symbol Interface (ctrader.com)

cAlgo API Reference - Bars Interface (ctrader.com)


@amusleh

amusleh
14 Mar 2022, 08:32

RE: how to send messages to telegram channel?

meeting.chegini said:

I want to send messages to a telegram channel
How can I do this?
Thanks

Hi,

You have to add the Telegram bot as an admin on your channel and then you will be able to get a chat ID with it that you can use it to send messages.

Here is a tutorial: Telegram · afhacker/ctrader-alert_popup Wiki (github.com)


@amusleh

amusleh
14 Mar 2022, 08:29

RE: can this be used for indicators as well as cbots?

bonedrak said:

is this possible using "private void RenkoSeries_OnBar(object sender, OhlcBar newBar, OhlcBar oldBar)" instead of "calculate(int index)"

 

Hi,

You should use RenkoSeries OnBar only in backtesting environment and the Calculate method on live mode.


@amusleh

amusleh
11 Mar 2022, 08:07

RE: RE: RE:

ys2310 said:

Hi amusleh,

 

I'm trying the following code and the index is -1

DateTime dt = Server.Time.AddMinutes(-128160);
int index = MarketData.GetBars(TimeFrame.Minute).OpenTimes.GetIndexByTime(dt);
Print("{0} {1}", index, MarketData.GetBars(TimeFrame.Minute).ClosePrices[index]);

if I instead use the code below the index has a value.

DateTime dt = Server.Time.AddMinutes(-1);
int index = MarketData.GetBars(TimeFrame.Minute).OpenTimes.GetIndexByTime(dt);
Print("{0} {1}", index, MarketData.GetBars(TimeFrame.Minute).ClosePrices[index]);

Is this because I'm referring way back in the past and there is no data with it?

How can I work around this problem? I want to get a correct value with the specified date and time.

Best  regards,

 

It returns -1 if there is no matching bar, to solve this issue you can load more bars by calling Bars LoadMoreHistory method.


@amusleh

amusleh
11 Mar 2022, 08:06

RE: RE:

66281850 said:

PanagiotisCharalampous said:

Dear 66281850,

Unfortunately there is no such option at the moment

FishProvider,

We offer higher limits to our clients.

Best Regards,

Panagiotis 

Join us on Telegram and Facebook

Are there plans to provide such an API in the future?

I think it is technically possible to combine multiple closing orders into one request。

Hi,

No, we don't have any plan to increase the current limits for now.

If your application trading volume is high enough we can increase the limit.

But for most apps the current limit is enough.


@amusleh

amusleh
10 Mar 2022, 14:49

Hi,

Our FIX server never sends back something like: 

FIX.4.4:demo.ctrader.3449248->CSERVER:SessionTrade

If server is not responding to your message then most probably your message checksum is invalid.

Please try our Python FIX package: spotware/cTraderFixPy: A Python package for interacting with cTrader FIX API. (github.com)

It's not released yet, but you can install it from TestPyPi: 

Once you installed it try to use the Console sample which is inside it's repository.


@amusleh

amusleh
10 Mar 2022, 10:26

Hi,

If you want to get the lowest/minimum value x bars back you can use this method:

using System;
using cAlgo.API;

namespace cAlgo
{
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class NewIndicator : Indicator
    {
        protected override void Initialize()
        {
            // Initialize and create nested indicators
        }

        public override void Calculate(int index)
        {
            // To get index on a cBot use
            // var index = Bars.Count - 1;

            // low of 20 previous bars
            var low = Minimum(Bars.LowPrices, index - 20, index);

            Print(low);
        }

        /// <summary>
        /// Returns the minimum value between start and end (inclusive) index in a dataseries
        /// </summary>
        /// <param name="dataSeries"></param>
        /// <param name="startIndex">Start index (Ex: 1)</param>
        /// <param name="endIndex">End index (Ex: 10)</param>
        /// <returns>double</returns>
        private double Minimum(DataSeries dataSeries, int startIndex, int endIndex)
        {
            var min = double.PositiveInfinity;

            for (var i = startIndex; i <= endIndex; i++)
            {
                min = Math.Min(dataSeries[i], min);
            }

            return min;
        }
    }
}

 


@amusleh

amusleh
10 Mar 2022, 10:21

Hi,

Try this:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class NYOpen : Robot
    {
        [Parameter("Entry Time (Hours)", DefaultValue = 14)]
        public double EntryTime { get; set; }

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

        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 1, MinValue = 0.01, Step = 0.01)]
        public double Quantity { get; set; }

        [Parameter("Stop Loss (Pips)", DefaultValue = 20)]
        public int SL { get; set; }

        [Parameter("Take Profit (Pips)", DefaultValue = 10)]
        public int TP { get; set; }

        protected override void OnStart()
        {
            Timer.Start(TimeSpan.FromHours(1));
        }

        protected override void OnTimer()
        {
            if (Server.Time.TimeOfDay.Hours != EntryTime) return;

            PlaceStopLimitOrder(TradeType.Buy, SymbolName, VolumeInUnits, Symbol.Ask + (Entry * Symbol.PipSize), 0.0, "NY Open", SL, TP, Server.Time.AddHours(12));
            PlaceStopLimitOrder(TradeType.Sell, SymbolName, VolumeInUnits, Symbol.Bid - (Entry * Symbol.PipSize), 0.0, "NY Open", SL, TP, Server.Time.AddHours(12));
        }

        protected override void OnTick()
        {
            //Close Trades
        }

        private double VolumeInUnits
        {
            get { return Symbol.QuantityToVolumeInUnits(Quantity); }
        }

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

 


@amusleh

amusleh
10 Mar 2022, 10:16

Hi,

Try this:

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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class AFFS : Robot
    {
        [Parameter("Name", DefaultValue = "AFFS")]
        public string _name { get; set; }

        [Parameter("Start Hour", DefaultValue = 6.0)]
        public double _starttime { get; set; }

        [Parameter("Stop Hour", DefaultValue = 18.0)]
        public double _stoptime { get; set; }

        [Parameter("Max Lots", DefaultValue = 100.0, MinValue = 0.01, MaxValue = 1000.0)]
        public double _maxlots { get; set; }

        [Parameter("Starting Risk (%)", DefaultValue = 1.0, MinValue = 0.01, MaxValue = 100.0)]
        public double _startingrisk { get; set; }

        [Parameter("Risk Step (%)", DefaultValue = 1.0, MinValue = 0.01, MaxValue = 100.0)]
        public double _riskstep { get; set; }

        [Parameter("Take Profit", DefaultValue = 5.0, MinValue = 0.1, MaxValue = 100.0)]
        public double _takeprofit { get; set; }

        [Parameter("Stop Loss", DefaultValue = 10.0, MinValue = 0.1, MaxValue = 100.0)]
        public double _stoploss { get; set; }

        [Parameter("Safety (Pips)", DefaultValue = 3.0, MinValue = 0.1, MaxValue = 100.0)]
        public double _safety { get; set; }

        [Parameter("High/Low Timeframe", DefaultValue = "Daily")]
        public TimeFrame _hltf { get; set; }

        private Bars _hlbars;
        private double _bullprice, _bearprice, _previoushigh, _previouslow, _maxunits, _activerisk;
        private bool _todaybuy, _todaysell;

        protected override void OnStart()
        {
            _hlbars = MarketData.GetBars(_hltf);

            _safety *= Symbol.PipSize;
        }

        protected override void OnBar()
        {
            var _currenthours = Server.Time.TimeOfDay.TotalHours;
            bool _tradetime = _starttime < _stoptime ? _currenthours > _starttime && _currenthours < _stoptime : _currenthours < _stoptime || _currenthours > _starttime;

            _activerisk = GetRisk();
            var _volume = GetVolume();
            _maxunits = Symbol.QuantityToVolumeInUnits(_maxlots);
            if (_volume > _maxunits)
            {
                _volume = _maxunits;
            }
            _previoushigh = _hlbars.HighPrices.Last(1);
            _previouslow = _hlbars.LowPrices.Last(1);
            _bullprice = Symbol.Ask + _safety;
            _bearprice = Symbol.Bid - _safety;

            if (_tradetime && !_todaybuy)
            {
                if (_bullprice > _previoushigh)
                {
                    ExecuteMarketOrder(TradeType.Buy, Symbol.Name, _volume, _name, _stoploss, _takeprofit);
                    _todaybuy = true;
                }
            }
            if (!_tradetime && _todaybuy)
            {
                _todaybuy = false;
            }

            if (_tradetime && !_todaysell)
            {
                if (_bearprice < _previouslow)
                {
                    ExecuteMarketOrder(TradeType.Sell, Symbol.Name, _volume, _name, _stoploss, _takeprofit);
                    _todaysell = true;
                }
            }
            if (!_tradetime && _todaysell)
            {
                _todaysell = false;
            }
        }

        private double GetRisk()
        {
            double _result = 0.0;
            double _maxrisk = 10.0;
            var _lastposition = History.FindLast(_name);
            bool _win = _lastposition != null && _lastposition.NetProfit > 0.0 ? true : false;
            if (_win && _activerisk < _maxrisk)
            {
                _result = _activerisk + _riskstep;
            }
            else if (!_win)
            {
                _result = _startingrisk;
            }
            return _result;
        }

        private double GetVolume()
        {
            double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;

            double baseNumber = Account.Balance;

            double sizeInLots = Math.Round((baseNumber * _activerisk / 100) / (_stoploss * costPerPip), 2);

            var result = Symbol.QuantityToVolumeInUnits(sizeInLots);

            if (result > Symbol.VolumeInUnitsMax)
            {
                result = Symbol.VolumeInUnitsMax;
            }
            else if (result < Symbol.VolumeInUnitsMin)
            {
                result = Symbol.VolumeInUnitsMin;
            }
            else if (result % Symbol.VolumeInUnitsStep != 0)
            {
                result = result - (result % Symbol.VolumeInUnitsStep);
            }

            return result;
        }
    }
}

 


@amusleh

amusleh
10 Mar 2022, 10:11

Hi,

cTrader has three different APIs:

  • cTrader Automate API: It's part of cTrader desktop app, you can use it to create indicators and cBots for cTrader desktop app.
  • FIX API: It's the standard FIX API implementation that is available for cTrader trading accounts, you can use it to trade and get price data of your cTrader trading account, but the FIX standard is very limited and old, you can get your trading account FIX API credentials from cTrader app settings section, we have some samples on our Github that you can use.
  • Open API: This is another API cTrader provides, it's not dependent on cTrader desktop/web/mobile apps and it's fully separate from the two abovementioned APIs, this is the API that you can use to create fully featured trading platforms or trading algorithms that will operate on your or any other cTrader users trading accounts. if you want to create an app or a trading algorithm outside cTrader official provided apps that will be your go to option. we have plenty of rich samples for this API on our Github for different programming languages, we have developed SDKs for this and also some third party developers made lots of contribution to develop different packages/wrappers for this cTrader API, you can check our OpenAPI.NET or OpenApiPy Github repositories.

Now regarding your question, you can use Open API for personal use, there is nothing that limits you.

You can provide some arbitrary Company name and site and we will approve your application.

But if you want to operate only on your own trading accounts then the best option for you is to use Automate API or FIX.


@amusleh

amusleh
10 Mar 2022, 10:00 ( Updated at: 10 Mar 2022, 10:05 )

Hi,

First of all this is great idea! we might develop an indicator for this.

The issue with your code is you take the mouse up event time value and you use it as target for another chart.

The time value of mouse up event is not related to scrolling at all, it's the time point where the mouse cursor was.

To solve the issue you can use the chart scroll changed event instead, ex:

using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Threading;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.IsraelStandardTime, AccessRights = AccessRights.FileSystem)]
    public class SyncObjectsInstance : Indicator
    {
        protected override void Initialize()
        {
            Synchronizer.Instance.Register(this);
            Timer.Start(Synchronizer.HeartbeatRate);
        }

        protected override void OnTimer()
        {
            Synchronizer.Instance.Heartbeat(this);
        }

        public override void Calculate(int index)
        {
            // do nothing
        }
    }

    public class Synchronizer
    {
        public static readonly TimeSpan HeartbeatTimeout = TimeSpan.FromSeconds(5);
        public static readonly TimeSpan HeartbeatRate = TimeSpan.FromSeconds(4);

        private static readonly object _sync = new object();
        private static Synchronizer _instance = null;

        public static Synchronizer Instance
        {
            get
            {
                if (_instance != null)
                    return _instance;

                lock (_sync)
                {
                    if (_instance != null)
                        return _instance;

                    _instance = new Synchronizer();
                }

                return _instance;
            }
        }

        private readonly Dictionary<string, HashSet<SyncObjectsInstance>> _instances;
        private readonly Dictionary<SyncObjectsInstance, DateTime> _instanceHeartbeats;

        public Synchronizer()
        {
            _instances = new Dictionary<string, HashSet<SyncObjectsInstance>>(StringComparer.OrdinalIgnoreCase);
            _instanceHeartbeats = new Dictionary<SyncObjectsInstance, DateTime>();
        }

        public void Register(SyncObjectsInstance instance)
        {
            // instance.Print("Register");

            lock (_sync)
            {
                Restore(instance);
            }
        }

        public void Heartbeat(SyncObjectsInstance instance)
        {
            //   instance.Print("Heartbeat");

            lock (_sync)
            {
                var now = DateTime.Now;

                _instanceHeartbeats[instance] = now;

                var expiredInstances = _instanceHeartbeats.Where(hb => now - hb.Value > HeartbeatTimeout).Select(hb => hb.Key).ToArray();

                foreach (var expiredInstance in expiredInstances)
                {
                    expiredInstance.Chart.ScrollChanged -= Chart_ScrollChanged;
                    _instanceHeartbeats.Remove(expiredInstance);
                    _instances[expiredInstance.SymbolName].Remove(expiredInstance);
                }
            }
        }

        private void Chart_ScrollChanged(ChartScrollEventArgs obj)
        {
            lock (_sync)
            {
                HashSet<SyncObjectsInstance> symbolInstances;

                if (!_instances.TryGetValue(obj.Chart.SymbolName, out symbolInstances))
                    return;

                foreach (var instance in symbolInstances)
                {
                    if (instance.Chart == obj.Chart)
                        continue;

                    instance.BeginInvokeOnMainThread(() => ScrollChart(instance, obj.Chart.Bars.OpenTimes[obj.Chart.FirstVisibleBarIndex]));
                }
            }
        }

        private void ScrollChart(SyncObjectsInstance targetInstance, DateTime targetTime)
        {
            //p("ScrollChart " + targetTime);

            targetInstance.Print("ScrollChart | ", targetInstance.TimeFrame);

            targetInstance.Chart.ScrollXTo(targetTime);

            //int middleBar = targetInstance.Chart.FirstVisibleBarIndex + ((targetInstance.Chart.LastVisibleBarIndex - targetInstance.Chart.FirstVisibleBarIndex) / 2);
            //DateTime midDate = targetInstance.Bars.OpenTimes[middleBar];
            //bool crossDate = false;
            //int crossDir = midDate < targetTime ? 1 : -1;

            //while (midDate != targetTime && !crossDate)
            //{
            //    p("midDate= " + midDate + " targetTime= " + targetTime + " middleBar= " + middleBar + " midDate= " + midDate);
            //    p("FirstVisibleBarIndex= " + targetInstance.Chart.FirstVisibleBarIndex + " LastVisibleBarIndex= " + targetInstance.Chart.LastVisibleBarIndex);
            //    targetInstance.Chart.ScrollXBy(crossDir);
            //    middleBar = targetInstance.Chart.FirstVisibleBarIndex + ((targetInstance.Chart.LastVisibleBarIndex - targetInstance.Chart.FirstVisibleBarIndex) / 2);
            //    midDate = targetInstance.Bars.OpenTimes[middleBar];
            //    crossDate = crossDir == 1 ? midDate > targetTime : midDate < targetTime;
            //}
        }

        private void p(string text)
        {
            File.AppendAllText("d:\\temp\\a.log", text + Environment.NewLine);
        }

        //p("a targetTime= " + targetTime +
        // " targetInstance.Chart.TimeFrame.ToString()= " + targetInstance.Chart.TimeFrame.ToString() +
        // " FirstVisibleBarIndex= " + targetInstance.Chart.FirstVisibleBarIndex +
        // );

        //targetInstance.Chart.ScrollXBy(100);
        //targetInstance.Chart.ScrollXTo(targetTime);
        //Thread.Sleep(300);
        //targetInstance.Chart.ScrollXBy((targetInstance.Chart.FirstVisibleBarIndex - targetInstance.Chart.LastVisibleBarIndex) / 2);

        //p("diff= " + ((targetInstance.Chart.FirstVisibleBarIndex - targetInstance.Chart.LastVisibleBarIndex) / 2));

        //p("f" + " TimeFrame= " + targetInstance.Chart.TimeFrame + " args.targetTime= " + targetTime + " LastVisibleBarIndex= " +
        //  targetInstance.Chart.LastVisibleBarIndex + " lastTime= " + targetInstance.Bars.OpenTimes[targetInstance.Chart.LastVisibleBarIndex] +
        //  " FirstVisibleBarIndex= " + targetInstance.Chart.FirstVisibleBarIndex + " firstTime= " +
        //  targetInstance.Bars.OpenTimes[targetInstance.Chart.FirstVisibleBarIndex] +
        //  " middleIdx= " + middleBar +
        //  " middleDate= " + targetInstance.Bars.OpenTimes[middleBar] +
        //  "-2Bars= " + ((targetInstance.Chart.LastVisibleBarIndex - targetInstance.Chart.FirstVisibleBarIndex) / -2).ToString()
        //  );

        //p("crossDir= " + crossDir);
        //p("a FirstVisibleBarIndex= " + targetInstance.Chart.FirstVisibleBarIndex + " LastVisibleBarIndex= " + targetInstance.Chart.LastVisibleBarIndex);

        //for (int i = 0; i < 100; i++)
        //{
        //    //p("FirstVisibleBarIndex= " + targetInstance.Chart.FirstVisibleBarIndex + " LastVisibleBarIndex= " + targetInstance.Chart.LastVisibleBarIndex);
        //    targetInstance.Chart.ScrollXBy(-1);
        //    targetInstance.Chart.
        //    //Thread.Sleep(300);
        //}

        //p("b FirstVisibleBarIndex= " + targetInstance.Chart.FirstVisibleBarIndex + " LastVisibleBarIndex= " + targetInstance.Chart.LastVisibleBarIndex);

        private void Restore(SyncObjectsInstance sender)
        {
            HashSet<SyncObjectsInstance> symbolInstances;

            if (!_instances.TryGetValue(sender.SymbolName, out symbolInstances))
            {
                symbolInstances = new HashSet<SyncObjectsInstance>();
                _instances.Add(sender.SymbolName, symbolInstances);
            }

            sender.Chart.ScrollChanged += Chart_ScrollChanged; ;

            symbolInstances.Add(sender);
            _instanceHeartbeats[sender] = DateTime.Now;
        }
    }
}

You can improve it by using Bars delta of scroll changed event args instead of first visible bar on the chart.

Also there is the issue of simultaneous scrolling which can be solved if you work little bit on it.

 


@amusleh