Topics

Forum Topics not found

Replies

amusleh
23 Mar 2021, 15:02

Hi,

Try StringBuilder:

using cAlgo.API;
using System.Text;

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class Blank : Indicator
    {
        protected override void Initialize()
        {
            var stringBuilder = new StringBuilder();

            stringBuilder.AppendLine("First Text");
            stringBuilder.AppendLine("Second Text");

            var text = stringBuilder.ToString();

            Chart.DrawText("text", text, Chart.LastVisibleBarIndex, Bars.HighPrices[Chart.LastVisibleBarIndex], Color.Red);
        }

        public override void Calculate(int index)
        {
        }
    }
}

With string builder you can easily add as many line as you want to, and then you just have to call the DrawText once.

You can't use font size, that's for chart controls not chart objects.


@amusleh

amusleh
23 Mar 2021, 14:16

Please hire a consultant or post a job request, I can only give you guide lines or some sample code.


@amusleh

amusleh
23 Mar 2021, 13:19

Its an indicator that notifies you when the cBot entry logic happens not a cBot.


@amusleh

amusleh
22 Mar 2021, 21:16

I recommend you to first learn and have some basic understanding of C# code and then try to code indicators/cBots.

The cBot link you posted is a simple cBot that uses RSI indicator for taking trading signals, you don't have to create an indicator from a cBot, usually its the opposite way.

If you don't have enough time to learn C# then you can ask one of our consultants to do it for you or post a job on jobs page.

Here is RSI indicator that sends email notification and play sound based on the cBot code:

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class RSIAlertSample : Indicator
    {
        private RelativeStrengthIndex _rsi;

        private int _lastNotifiedBarIndex;
        
        [Parameter("Source")]
        public DataSeries Source { get; set; }
 
        [Parameter("Periods", DefaultValue = 10)]
        public int Period { get; set; }

        [Parameter("Sound File Path", DefaultValue = "C:\\Windows\\Media\\notify.wav")]
        public string SoundFilePath { get; set; }

        [Parameter("Sender Email")]
        public string SenderEmail { get; set; }

        [Parameter("Receiver Email")]
        public string ReceiverEmail { get; set; }


        protected override void Initialize()
        {
            _rsi = Indicators.RelativeStrengthIndex(Source, Period);
        }

        public override void Calculate(int index)
        {
            if (!IsLastBar || _lastNotifiedBarIndex == index) return;

            _lastNotifiedBarIndex = index;
            
            if (_rsi.Result.Last(1) > 50 && _rsi.Result.Last(1) < 70 && _rsi.Result.IsRising())
            {
                Notify("RSI Buy Signal");
            }
            else if (_rsi.Result.Last(1) < 50 && _rsi.Result.Last(1) > 30 && _rsi.Result.IsFalling())
            {
                Notify("RSI Sell Signal");
            }
        }
        
        private void Notify(string message)
        {
            if (!string.IsNullOrWhiteSpace(SoundFilePath))
            {
                Notifications.PlaySound(SoundFilePath);
            }

            if (!string.IsNullOrWhiteSpace(SenderEmail) && !string.IsNullOrWhiteSpace(ReceiverEmail))
            {
                Notifications.SendEmail(SenderEmail, ReceiverEmail, "Notification", message);
            }
        }
    }
}

 


@amusleh

amusleh
22 Mar 2021, 20:12

RE:

kebbo said:

Hi,

thank you very much for your detailed answer!

I will test this once, however, I do not have so much experience with web requests, possibly there are difficulties with the implementation.

Is it possible to access the time information that is displayed at the bottom right of the platform at "current time:"?

Thank you!

I updated the code, please try again.


@amusleh

amusleh
22 Mar 2021, 17:20 ( Updated at: 22 Mar 2021, 20:11 )

You can use this class to get the current time:

        public static DateTimeOffset GetTime()
        {
            var currentTime = DateTimeOffset.Now;

            string[] hosts =
            {
                "http://www.google.com",
                "http://www.microsoft.com",
                "http://timezonedb.com/",
                "http://www.twitter.com",
                "http://spotware.com",
            };

            Parallel.ForEach(hosts, (host, loopState) =>
            {
                try
                {
                    if (loopState.ShouldExitCurrentIteration)
                    {
                        return;
                    }

                    var response = WebRequest.Create(host).GetResponse();

                    if (loopState.ShouldExitCurrentIteration)
                    {
                        return;
                    }

                    currentTime = DateTime.ParseExact(
                        response.Headers["date"],
                        "ddd, dd MMM yyyy HH:mm:ss 'GMT'",
                        CultureInfo.InvariantCulture.DateTimeFormat
                        , DateTimeStyles.AssumeUniversal).ToLocalTime();

                    if (loopState.ShouldExitCurrentIteration)
                    {
                        return;
                    }

                    loopState.Stop();
                }
                catch (Exception ex) when (ex is WebException || ex is FormatException)
                {
                }
            });

            return currentTime;
        }

If the system isn't connected to internet or there was any other issue in connection with listed hosts the method will return DateTimeOffset.Now which is the current time of user system, modify it based on your needs.


@amusleh

amusleh
22 Mar 2021, 17:12

Server.Time works very well on back test, if you use the server time on back test it will give you the time based on back test time not current time.

If you want to get the current time during back test you can use the DateTimeOffset.Now and then change its offset to server time offset, its still dependent to user system time.

If you need the current time but you don't want to use the user system time then you can use a web request, the current time will be on request header.

 


@amusleh

amusleh
22 Mar 2021, 14:26 ( Updated at: 22 Mar 2021, 15:23 )

I was able to build your posted indicator code on my system without any issue on Spotware cTrader beta.

Please create a new indicator, copy the code of your old indicator, and try to re-build.

I recommend you to use Visual Studio, it will mark the error for you.


@amusleh

amusleh
21 Mar 2021, 18:14

RE: RE:

lec0456 said:

Thanks, but what i am really looking for it that the tooltip appears when the mouse rolls over the Output line. So, if Main is the moving average, when you rollover main it displays the text at the point you rolled over the line.

The sample I posted does the same thing, please test.


@amusleh

amusleh
21 Mar 2021, 13:16

Hi,

This sample indicator might help you:

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

namespace cAlgo
{
    /// <summary>
    /// This sample shows how to create a tooltips for an indicator data series or output
    /// </summary>
    [Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class OutputSnapshotSample : Indicator
    {
        private MarketSnapshotControl _marketSnapshotControl;

        private MovingAverage _ma;

        [Parameter("Tolerance (Pips)", DefaultValue = 8)]
        public double ToleranceInPips { get; set; }

        [Output("Main", LineColor = "Yellow", PlotType = PlotType.Line, Thickness = 1)]
        public IndicatorDataSeries Main { get; set; }

        protected override void Initialize()
        {
            ToleranceInPips *= Symbol.PipSize;

            _ma = Indicators.MovingAverage(Bars.ClosePrices, 9, MovingAverageType.Exponential);

            _marketSnapshotControl = new MarketSnapshotControl
            {
                BackgroundColor = Color.Gold,
                BorderColor = Color.Gray,
                VerticalAlignment = VerticalAlignment.Top,
                HorizontalAlignment = HorizontalAlignment.Left,
                Opacity = 0.6,
                Margin = 2,
                IsVisible = false,
                Width = 220,
                Height = 50
            };

            IndicatorArea.AddControl(_marketSnapshotControl);

            IndicatorArea.MouseMove += IndicatorArea_MouseMove;
            IndicatorArea.MouseLeave += IndicatorArea_MouseLeave;
        }

        private void IndicatorArea_MouseMove(ChartMouseEventArgs obj)
        {
            var index = (int)obj.BarIndex;
            var yValue = Math.Round(obj.YValue, Symbol.Digits);
            var mainValue = Math.Round(Main[index], Symbol.Digits);

            if (double.IsNaN(mainValue) || Math.Abs(mainValue - yValue) > ToleranceInPips)
            {
                _marketSnapshotControl.IsVisible = false;

                return;
            }

            var extraDelta = 10;
            var width = _marketSnapshotControl.Width;
            var height = _marketSnapshotControl.Height;
            var left = Chart.Width - obj.MouseX > width + extraDelta ? obj.MouseX + extraDelta : obj.MouseX - width - extraDelta;
            var right = Chart.Height - obj.MouseY > height + extraDelta ? obj.MouseY + extraDelta : obj.MouseY - height - extraDelta;

            _marketSnapshotControl.Margin = new Thickness(left, right, 0, 0);

            _marketSnapshotControl.Time = Bars.OpenTimes[index].ToString("g");
            _marketSnapshotControl.Value = mainValue.ToString();

            _marketSnapshotControl.IsVisible = true;
        }

        private void IndicatorArea_MouseLeave(ChartMouseEventArgs obj)
        {
            _marketSnapshotControl.IsVisible = false;
        }

        public override void Calculate(int index)
        {
            Main[index] = _ma.Result[index];
        }
    }

    public class MarketSnapshotControl : CustomControl
    {
        private readonly Border _border;

        private TextBlock _valueTextBlock;

        private TextBlock _timeTextBlock;

        public MarketSnapshotControl()
        {
            _border = new Border
            {
                BackgroundColor = "#3F3F3F",
                BorderColor = "#969696",
                BorderThickness = 1,
                CornerRadius = 5
            };

            var style = new Style();

            style.Set(ControlProperty.Margin, new Thickness(3, 3, 0, 0));
            style.Set(ControlProperty.FontWeight, FontWeight.Bold);
            style.Set(ControlProperty.ForegroundColor, Color.Black);
            style.Set(ControlProperty.HorizontalContentAlignment, HorizontalAlignment.Left);
            style.Set(ControlProperty.VerticalContentAlignment, VerticalAlignment.Center);

            _valueTextBlock = new TextBlock
            {
                Style = style
            };
            _timeTextBlock = new TextBlock
            {
                Style = style
            };

            var grid = new Grid(2, 2)
            {
                ShowGridLines = true
            };

            grid.AddChild(new TextBlock
            {
                Text = "Value",
                Style = style
            }, 0, 0);
            grid.AddChild(new TextBlock
            {
                Text = "Time",
                Style = style
            }, 1, 0);

            grid.AddChild(_valueTextBlock, 0, 1);
            grid.AddChild(_timeTextBlock, 1, 1);

            _border.Child = grid;

            AddChild(_border);
        }

        public Color BackgroundColor
        {
            get { return _border.BackgroundColor; }
            set { _border.BackgroundColor = value; }
        }

        public Color BorderColor
        {
            get { return _border.BorderColor; }
            set { _border.BorderColor = value; }
        }

        public string Value
        {
            get { return _valueTextBlock.Text; }
            set { _valueTextBlock.Text = value; }
        }

        public string Time
        {
            get { return _timeTextBlock.Text; }
            set { _timeTextBlock.Text = value; }
        }
    }
}

The only issue with sample is tolerance parameter, you have to find a way to calculate a good tolerance that works well for all symbols and outputs, you can do it by using an output x previous values average difference or change, or you can use percentage change in output.

If the tooltips is not showing for some very volatile symbols like BTCUSD then try to increase the tolerance.

There might be other better ways to achieve this but right now this is the only option in my mind.


@amusleh

amusleh
20 Mar 2021, 19:26

RE: RE:

westend.trading said:

PanagiotisCharalampous said:

Hi westend.trading,

Our economic calendar is provided by a third party source and we do not have the right to redistribute the information. You can contact these providers yourself and ask about direct access to the calendar.

Best Regards,

Panagiotis 

Join us on Telegram

Hi Panagiotis,

it sounds paradox since this information is somehow distributed by the cTrader app anyway. But if that is the way it is...
It is giving MT5 a head start, though.

Best regards,
Ben;

You can get the news data from 3rd party services like TradingEconomics API.


@amusleh

amusleh
19 Mar 2021, 17:52

Hi,

The current version of cTrader automate uses C# 4.0 for compiling your code, and it doesn't support string interpolation, all new features of C# and .NET will be supported on cTrader automate after migration to .NET core, right now that's the main priority.

 


@amusleh

amusleh
19 Mar 2021, 16:07

RE: RE: RE:

duketv said:

amusleh said:

Hi,

The code above checks last MA value which is not closed yet and can change, so it can go above or below last closed bar price.

You should use Last(1) not LastValue:

if (Bars.Last(1).Open < Sma.Result.Last(1) && Bars.Last(1).Close > Sma.Result.Last(1))
{

​}

Last(1) gives you the latest closed value, and LastValue gives you the last value of series which is not closed yet and can change which will give you inconsistent results both in backtest and live.

You can use Last(2) to get the last second closed value of a series.

Thank you, That is indeed some valuable information for further coding.. A small but not unimportant detail!

Still curious how the hascrossedabove method works though? My interpretation of 'hascrossedabove' is that after crossing, the method will return true for an x amount of bars. X being the 'int Period' from the method. However, I do see an irregular amount 'true' when I code for this.

You can read the documentation of HasCroossedAbove method here: 

 


@amusleh

amusleh
19 Mar 2021, 14:57

Yes, its possible.


@amusleh

amusleh
19 Mar 2021, 12:10

Hi,

There is no way to disable the log right now, but you can use the .NET sound player instead of Notifications.PlaySound method and it will not product any log.

        private static void PlaySound(string path)
        {
            if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException($"'{nameof(path)}' cannot be null or whitespace", nameof(path));

            SoundPlayer soundPlayer = null;

            try
            {
                soundPlayer = new SoundPlayer(path);

                soundPlayer.PlaySync();
            }
            finally
            {
                if (soundPlayer != null) soundPlayer.Dispose();
            }
        }

You have to add "using System.Media" for the above code to work.


@amusleh

amusleh
19 Mar 2021, 11:36

Hi,

This sample bot might help you:

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

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

        [Parameter("Breakount Amount (Pips)", DefaultValue = 10, MinValue = 0)]
        public double BreakountAmountInPips { get; set; }

        [Parameter("Volume (Lots)", DefaultValue = 0.01)]
        public double VolumeInLots { get; set; }

        [Parameter("Label", DefaultValue = "BarBreakoutTradingSample")]
        public string Label { get; set; }

        public Position[] BotPositions
        {
            get
            {
                return Positions.Where(iPosition => Label.Equals(iPosition.Label, StringComparison.OrdinalIgnoreCase)
                && iPosition.SymbolName.Equals(SymbolName, StringComparison.OrdinalIgnoreCase))
                    .ToArray();
            }
        }

        protected override void OnStart()
        {
            if (string.IsNullOrWhiteSpace(Label))
            {
                Print("Please provide a label");

                Stop();
            }

            BreakountAmountInPips *= Symbol.PipSize;

            _volumeInUnits = Symbol.QuantityToVolumeInUnits(VolumeInLots);
        }

        protected override void OnTick()
        {
            if (BotPositions.Length > 0) return;

            if (Symbol.Ask >= Bars.Last(1).High + BreakountAmountInPips)
            {
                ExecuteMarketOrder(TradeType.Buy, SymbolName, _volumeInUnits, Label);
            }
            else if (Symbol.Bid <= Bars.Last(1).Low - BreakountAmountInPips)
            {
                ExecuteMarketOrder(TradeType.Sell, SymbolName, _volumeInUnits, Label);
            }
        }

        protected override void OnBar()
        {
            foreach (var position in BotPositions)
            {
                if ((position.TradeType == TradeType.Buy && Bars.Last(1).Close < Bars.Last(1).Open)
                    || (position.TradeType == TradeType.Sell && Bars.Last(1).Close > Bars.Last(1).Open))
                {
                    ClosePosition(position);
                }
            }
        }
    }
}

Please read the API references if its hard to understand the sample bot code.


@amusleh

amusleh
19 Mar 2021, 11:03

Hi,

You can use order label parameter to set unique ID for orders, add a string type label parameter to your bot and then use that parameter value as your bot executed orders label, the other bot can use the same label to filter the orders and find the previous bot opened orders.


@amusleh

amusleh
19 Mar 2021, 09:24

First make your text box globally accessible by making it a private field inside your cBot/indicator class, then you can change any of its properties anywhere based on any condition, this sample indicator might be helpful for you:

using cAlgo.API;

namespace cAlgo
{
    /// <summary>
    /// This is a sample of how to catch or handle the keyboard events on chart
    /// </summary>
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class ChartKeyboardSample : Indicator
    {
        private TextBlock _keyDownTextBlock, _keyCombinationTextBlock;

        protected override void Initialize()
        {
            var stackPanel = new StackPanel 
            {
                Orientation = Orientation.Vertical,
                HorizontalAlignment = HorizontalAlignment.Center,
                VerticalAlignment = VerticalAlignment.Center,
                BackgroundColor = Color.Gold,
                Opacity = 0.7,
                Width = 200
            };

            stackPanel.AddChild(new TextBlock 
            {
                Text = "Keyboard Handler",
                FontWeight = FontWeight.ExtraBold,
                Margin = 5,
                HorizontalAlignment = HorizontalAlignment.Center,
                ForegroundColor = Color.Black
            });

            var grid = new Grid(2, 2);

            grid.AddChild(new TextBlock 
            {
                Text = "Key Down",
                Margin = 5,
                ForegroundColor = Color.Black
            }, 0, 0);

            _keyDownTextBlock = new TextBlock 
            {
                Margin = 5,
                ForegroundColor = Color.Black
            };

            grid.AddChild(_keyDownTextBlock, 0, 1);

            grid.AddChild(new TextBlock 
            {
                Text = "Key Combination",
                Margin = 5,
                ForegroundColor = Color.Black
            }, 1, 0);

            _keyCombinationTextBlock = new TextBlock 
            {
                Margin = 5,
                ForegroundColor = Color.Black
            };

            grid.AddChild(_keyCombinationTextBlock, 1, 1);

            stackPanel.AddChild(grid);

            Chart.AddControl(stackPanel);

            Chart.KeyDown += Chart_KeyDown;
        }

        private void Chart_KeyDown(ChartKeyboardEventArgs obj)
        {
            _keyDownTextBlock.Text = obj.Key.ToString();

            _keyCombinationTextBlock.Text = string.Empty;

            if (obj.AltKey)
                _keyCombinationTextBlock.Text += "Alt, ";
            if (obj.ShiftKey)
                _keyCombinationTextBlock.Text += "Shift, ";
            if (obj.CtrlKey)
                _keyCombinationTextBlock.Text += "Ctrl, ";
        }

        public override void Calculate(int index)
        {
        }
    }
}

 


@amusleh

amusleh
19 Mar 2021, 09:20

RE: RE: RE:

Hi,

You can set pre-defined stop loss and take profit from Quick trade settings:

 

 


@amusleh

amusleh
18 Mar 2021, 11:01

Hi,

You can use tick time frames, one second time frame is same like 1 tick time frame, there is no reason to add it.

If you need custom time frames you can use an indicator like Custom Period Chart.


@amusleh