Topics
Replies

firemyst
22 Apr 2021, 10:16 ( Updated at: 24 May 2022, 03:29 )

RE:

amusleh said:

Hi,

You can call Bars.OpenTimes.GetindexByTime two times with two indices, for example you call it first with index 5 and then 6, and it returns 10 for the first call and 20 for the second call, then you know from 10 to 20 belongs to first index.

Thank you!

That makes perfect sense!!

I appreciate your help!!


@firemyst

firemyst
22 Apr 2021, 09:42 ( Updated at: 22 Apr 2021, 09:44 )

RE:

amusleh said:

Hi,

Indices are not fixed, they can change, instead use bar open times.

If you want to get index of a bar on a different time frame you can use GetIndexByTime method of Bars.OpenTimes, check this sample indicator code:

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

namespace cAlgo
{
    [Cloud("Top", "Bottom", Opacity = 0.2)]
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class BollingerBandsMTFCloudSample : Indicator
    {
        private BollingerBands _bollingerBands;

        private Bars _baseBars;

        [Parameter("Base TimeFrame", DefaultValue = "Daily")]
        public TimeFrame BaseTimeFrame { get; set; }

        [Parameter("Source", DefaultValue = DataSeriesType.Close)]
        public DataSeriesType DataSeriesType { get; set; }

        [Parameter("Periods", DefaultValue = 14, MinValue = 0)]
        public int Periods { get; set; }

        [Parameter("Standard Deviation", DefaultValue = 2, MinValue = 0)]
        public double StandardDeviation { get; set; }

        [Parameter("MA Type", DefaultValue = MovingAverageType.Simple)]
        public MovingAverageType MaType { get; set; }

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

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

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

        protected override void Initialize()
        {
            _baseBars = MarketData.GetBars(BaseTimeFrame);

            var baseSeries = GetBaseSeries();

            _bollingerBands = Indicators.BollingerBands(baseSeries, Periods, StandardDeviation, MaType);
        }

        public override void Calculate(int index)
        {
            var baseIndex = _baseBars.OpenTimes.GetIndexByTime(Bars.OpenTimes[index]);

            Main[index] = _bollingerBands.Main[baseIndex];
            Top[index] = _bollingerBands.Top[baseIndex];
            Bottom[index] = _bollingerBands.Bottom[baseIndex];
        }

        private DataSeries GetBaseSeries()
        {
            switch (DataSeriesType)
            {
                case DataSeriesType.Open:
                    return _baseBars.OpenPrices;

                case DataSeriesType.High:
                    return _baseBars.HighPrices;

                case DataSeriesType.Low:
                    return _baseBars.LowPrices;

                case DataSeriesType.Close:
                    return _baseBars.ClosePrices;
                default:

                    throw new ArgumentOutOfRangeException("DataSeriesType");
            }
        }
    }

    public enum DataSeriesType
    {
        Open,
        High,
        Low,
        Close
    }
}

 

 

Thank you, but this doesn't help and doesn't work for what I need.

 

What your code is doing is getting the indexes of the bars in the faster time frame and mapping that to a single index to the bars in the slower time frame.

With your method, it's going from "many" to "one".

I need the opposite. I have 1 index in a higher time frame and need to map it to all the related indexes in the faster time frame.

I have "one" and need to map it to "many".

If you try doing the opposite:

Bars.OpenTimes.GetIndexByTime(_baseBars.OpenTimes[index]);

simply won't work because it will not return all the indexes related. That is, if _baseBars is M15, and the chart is M1, Bars.OpenTimes.GetindexByTime will only return 1 index, not the corresponding 15 indexes I need.

 


@firemyst

firemyst
22 Apr 2021, 09:21

RE:

sascha.dawe said:

Hi,

 

I am having trouble playing sound notifications in an indicator in the latest version of Ctrader 4.0.

Yes, I have sound enabled and notifications on Ctrader and Filesystem access is enabled. I have

also converted this code to a cBot and the sound works fine. What could be causing this issue?

See demonstration code below.

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

namespace cAlgo
{
    [Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
    public class TestSound : Indicator
    {
        [Parameter("Enable Sound Alerts", Group = "Sound", DefaultValue = false)]
        public bool soundAlerts { get; set; }
        [Parameter("Sound Filename", Group = "Sound", DefaultValue = "foghorn.mp3")]
        public string filename { get; set; }

        private string path;
        private bool notified = false;

        protected override void Initialize()
        {
            try
            {
                var desktopFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
                path = Path.Combine(desktopFolder, filename);
                Print(path);
            } catch (Exception error)
            {
                Print("Error: " + error);
            }
            PlaySound();
        }

        public override void Calculate(int index)
        {
            if (!notified)
            {
                notified = true;
                PlaySound();
            }
        }

        private void PlaySound()
        {
            if (soundAlerts)
            {
                try
                {
                    Notifications.PlaySound(path);
                } catch (Exception error)
                {
                    Print("Error: " + error);
                }
            }
        }
    }
}

 

 

Have you tried stepping through your code in Visual Studio debug mode to see what happens?

Also, because ticks could be coming in rather quickly, the system might be trying to play, but then gets stopped immediately when the next tick comes in because the "notified" variable is never set to true. I've had that happen before when trying to play sounds too quickly in succession.

Finally, what's the Print/logging output?


@firemyst

firemyst
22 Apr 2021, 09:12

Rough pseudo logic:

 

DateTime startTime = new DateTime(2021,4,22,6,0,0);

int startTimeIndex = Bars.OpenTimes.GetIndexByTime(startTime);

DateTime endTime = new DateTime(2021,4,22,10,0,0);

int endTimeIndex = Bars.OpenTimes.GetIndexByTime(endTime);

double highestPrice = 0;

for (int x= startTimeIndex; x <= endTimeIndex; x++)

{

    if (Bars.HighPrices[x] > highestPrice)

       highestPrice = Bars.HighPrices[x];

}


@firemyst

firemyst
13 Apr 2021, 15:28

RE:

amusleh said:

Hi,

Right now there is no way to change a chart time frame while a cBot instance is running on that chart.

We have plans to allow changing the time frame but we can't give you any ETA for that, I recommend you to open a thread for this feature request on forum suggestions section.

For clarification, yomm0401 didn't specifically ask if the "chart" time frame itself could be changed, only if the timeframe could be changed.

The answer to the latter question is yes in cBot code if that's what the intended question was.

The cbot code can always call:

MarketData.GetBars(TimeFrame.xxx, Symbol.Name);

and get data from a diffferent symbol and/or timeframe than what's on the chart.


@firemyst

firemyst
13 Apr 2021, 05:40

RE:

PanagiotisCharalampous said:

Hi caglar_G,

There is no option for the shift in the API at the moment, but it is not hard to implement it yourself.

Best Regards,

Panagiotis 

Join us on Telegram

Just wanted to follow up on this as I was looking for this parameter today as well, and it's not there (yet?) with the latest cTrader release.

Is it on a road map to implement natively? Seems like an easy job that shouldn't take a @Spotware programmer more than half a day since it's basically 1 important line of code similar to:

Result[index + Shift] = _movingAverage.Result[index];

THank you. :-)

 


@firemyst

firemyst
05 Apr 2021, 11:43

RE:

PanagiotisCharalampous said:

Hi firemyst,

It is not possible at the moment. To avoid this, you could use a trend line instead.

Best Regards,

Panagiotis 

Join us on Telegram

Thank you @Panagiotis. I'll do that. Hopefully that API option will be available in a future release.


@firemyst

firemyst
05 Apr 2021, 10:38

RE:

travkinsm1 said:

Hello! When testing the indicator, I noticed that one of the variables (_isLong) is displayed differently in the indicator and in the robot. If you do not pay attention to the different processing of ticks by the robot and the indicator, you can verify this by analyzing 2 log files. Tell me, please, what could be the matter? (For testing, you need to change the paths to the files)

using System;
using System.Linq;
using System.Windows;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;


namespace cAlgo
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class MycBot : Robot
    {
        [Parameter("MA Method", DefaultValue = MovingAverageType.Simple)]
        public MovingAverageType MaType { get; set; }


        [Parameter("True:High_Low False:Close", DefaultValue = false)]
        public bool UseHighAndLow { get; set; }

        [Parameter("Timeframe1", DefaultValue = "Ticks10")]
        public TimeFrame TF { get; set; }

        [Parameter("Загрузить Баров", DefaultValue = 30000)]
        public int BarCount_set { get; set; }

        [Parameter("Загрузка ист. в индикаторе", DefaultValue = false)]
        public bool LoadIndHist { get; set; }

        [Parameter("Запись в файл", DefaultValue = true)]
        public bool FileWrite { get; set; }
        [Parameter("Запись торговли в файл", DefaultValue = true)]
        public bool FileWrite2 { get; set; }

        private Bars Bars2;
        public StreamWriter file;
        public int LoadHistory_for_ind = 3000;


        protected override void OnStart()
        {
            while (Bars.Count < BarCount_set)
            {
                var loadedCount = Bars.LoadMoreHistory();
                Print("Loaded {0} bars", loadedCount);
                Print("Total bars {0}", Bars.Count);
                if (loadedCount == 0)
                    break;
            }
            Print("Finished, total bars {0}", Bars.Count);
            Bars2 = MarketData.GetBars(TF, Symbol.Name);
            while (Bars2.Count < (BarCount_set / 10))
            {
                var loadedCount = Bars2.LoadMoreHistory();
                if (loadedCount == 0)
                    break;
            }
            Print("Finished, total bars {0}", Bars2.Count);
            file = new StreamWriter("C:\\Users\\travk_000\\Documents\\bot1.3_is_long2.txt", false);
            file.WriteLine("Begin");
            file.Flush();
            file.Close();
        }
        protected override void OnTick()
        {
            bool dirInd = Indicators.GetIndicator<ATR_Stops_time_MarketData_History>(TF, MaType, 10, 4, UseHighAndLow, LoadHistory_for_ind, LoadIndHist)._isLong;
            file = new StreamWriter("C:\\Users\\travk_000\\Documents\\bot1.3_is_long2.txt", true);
            file.WriteLine("Bot " + Bars.OpenPrices.LastValue + " " + Bars.OpenTimes.LastValue + " " + dirInd);
            file.Flush();
            file.Close();
        }
    }
}
using System;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
namespace cAlgo.Indicators
{
    [Indicator("ATR Trailing Stop", AutoRescale = false, IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.FileSystem)]
    public class ATR_Stops_time_MarketData_History : Indicator
    {

        [Parameter("Timeframe", DefaultValue = 0.0)]
        public TimeFrame TF { get; set; }

        [Parameter("MA Method", DefaultValue = MovingAverageType.Simple)]
        public MovingAverageType MaType { get; set; }

        [Parameter("Period", DefaultValue = 10, MinValue = 2, MaxValue = 50000)]
        public int Period { get; set; }

        [Parameter("Weight", DefaultValue = 4.0, MinValue = 0.1, MaxValue = 400.0)]
        public double Weight { get; set; }

        [Parameter("True:High_Low False:Close", DefaultValue = false)]
        public bool UseHighAndLow { get; set; }

        [Parameter(DefaultValue = 16000)]
        public int LoadHistory_for_ind { get; set; }

        [Parameter("Загрузка ист. в индикаторе", DefaultValue = false)]
        public bool LoadIndHist { get; set; }


        [Output("Main")]
        public IndicatorDataSeries Result { get; set; }

        private ATRMTF_time_History _atr;
        public bool _isLong;

        private MarketSeries _marketSeries;

        private Bars Bars2;
        public StreamWriter file3;

        protected override void Initialize()
        {
            Print("Start");

            Bars2 = MarketData.GetBars(TF, Symbol.Name);
            Print("{0} bar on the chart. Loading 2.000.000 bars", Bars2.Count);
            if (LoadIndHist == true)
            {
                while (Bars2.Count < LoadHistory_for_ind)
                {
                    var loadedCount = Bars2.LoadMoreHistory();
                    if (loadedCount == 0)
                        break;
                }
                Print("Finished, total bars {0}", Bars2.Count);
            }
            Print("1");
            this._marketSeries = MarketData.GetSeries(MarketData.GetSymbol(Symbol.Name), TF);

        }


        public override void Calculate(int index)
        {
            var currentAtr = Weight * _atr.Result[index];

            if (double.IsNaN(currentAtr))
                return;

if (double.IsNaN(Result[index - 1]) && !double.IsNaN(_atr.Result[index - 1]))
            {
                var previousATR = Weight * _atr.Result[index - 1];

                _isLong = MarketSeries.Close.IsRising();

                var previous = UseHighAndLow ? (_isLong ? this._marketSeries.High[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index - 1])] : this._marketSeries.Low[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index - 1])]) : this._marketSeries.Close[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index - 1])];

                Result[index] = _isLong ? previous - previousATR : previous + previousATR;
            }
            else
            {
                var current = this._marketSeries.Close[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index])];

                if (_isLong)
                {
                    if (current >= Result[index - 1])
                    {
                        if (UseHighAndLow)
                            current = this._marketSeries.High[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index])];
                        Result[index] = Math.Max(Result[index - 1], current - currentAtr);
                    }
                    else
                    {
                        _isLong = false;
                        if (UseHighAndLow)
                            current = this._marketSeries.Low[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index])];
                        Result[index] = current + currentAtr;
                    }
                }
                else
                {
                    if (current <= Result[index - 1])
                    {
                        if (UseHighAndLow)
                            current = this._marketSeries.Low[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index])];
                        Result[index] = Math.Min(Result[index - 1], current + currentAtr);
                    }
                    else
                    {
                        _isLong = true;
                        if (UseHighAndLow)
                            current = this._marketSeries.High[_marketSeries.OpenTime.GetIndexByTime(MarketSeries.OpenTime[index])];
                        Result[index] = current - currentAtr;
                    }
                }
            }
            file3 = new StreamWriter("C:\\Users\\travk_000\\Documents\\bot1.3_is_long.txt", true);
            file3.WriteLine("Indicator " + Bars.OpenPrices.LastValue + " " + Bars.OpenTimes.LastValue + " " + Period + " " + Weight + " " + _isLong);
            file3.Flush();
            file3.Close();

        }
    }
}

 

When calling an indicator from a bot, you ALWAYS have to get the latest value from the indicator, which you aren't doing. You're initializing the indicator on every tick in the bot, but you're not getting its latest value.

You need to do something like the following in your bot:

//because cTrader does "lazy loading" of indicator values, so force it
double a = YourIndicator.Result.Last(0); 

//now that the indicator's values have been refresh above, get the latest value.
_isLong = YourIndicator.Result.Last(0);

 


@firemyst

firemyst
04 Apr 2021, 13:38

RE:

PanagiotisCharalampous said:

Hi firemyst,

It seems to be a bug. We will fix it in an upcoming update.

Best Regards,

Panagiotis 

Join us on Telegram

Awesome.

Thank you @Panagiotis! If you could update here when fixed that would be great.

ALSO! Would it be possible to include within the API a "data source" parameter within the constructor? It seems incredibly limiting that we cannot pass in another datasource (eg, different time frame for example) like we can with PSARs and other indicators.

Thank you. And Happy Easter if you celebrate it. :-)


@firemyst

firemyst
01 Apr 2021, 05:46

HI @Panagiotis / @Spotware:

Any updates or comments on this?

Thank you.


@firemyst

firemyst
28 Mar 2021, 17:05

RE:

PanagiotisCharalampous said:

Hi firemyst,

I would put forth the suggestion that since Spotware has everyone's email address that has joined the forum, to put out a survey to all registered forum users through something like Survey Monkey.

We cannot get into this burden every time someone dislikes something. We would be sending five polls a day :) Also I do not think that we need to get everybody's vote to make such decisions. This is not how polls and statistics work. I believe that the forums have enough participants and voters to run a representative poll with a low margin of error. 

Best Regards,

Panagiotis 

Join us on Telegram

We're not asking you to send 5 polls a day. Spotware put up 1 poll on Telegram -- how difficult is it to put that same poll in a location where you have a greater audience and feedback from every cTrader user?

What Spotware has done is like only asking the people in California who the President should be rather than the whole of the USA.

 


@firemyst

firemyst
27 Mar 2021, 03:56

RE:

PanagiotisCharalampous said:

Hi firemyst,

I don't like the new layout because the "New Order" button at the top has been removed! That was so convenient!

Why don't you just press F9?

Best Regards,

Panagiotis 

Join us on Telegram 


 

Because I (like others as expressed in this thread), don't know or have keyboard shortcuts memorized.

Why can't Spotware just put it back? There's still plenty of room for it, even in the new layout.


@firemyst

firemyst
26 Mar 2021, 04:32

Where is there a button to vote against the new layout?

I don't like the new layout because the "New Order" button at the top has been removed! That was so convenient!

Why was that removed?!

So this should have no more than 157 votes :-P

Put the "New Order" button back and I'll consider changing my vote back so it's 158.


@firemyst

firemyst
26 Mar 2021, 04:28

RE: RE:

abcdavid13 said:

i really don't mind the changes. 

i just have ONE QUESTION:

Where did the "NEW ORDER" button go? that was very useful and it disappeared. i just see "quick trade" but not the New Order haha

@Panagiotis:

I have the same question and haven't seen an answer to this -- where did the "New Order" button go?

Thank you.


@firemyst

firemyst
26 Mar 2021, 04:26 ( Updated at: 23 Jan 2024, 13:14 )

RE:

PanagiotisCharalampous said:

Hi firemyst,

This vote is biased in that it's only asking for feedback on one specific element of the layout.

This is what the main complaint was about, hence the relevant question. 

 I agree with everyone else here and have complained about it previously -- I should NOT have to join telegram for this. There's websites for taking polls like Survey Monkey and others. I shouldn't have to download an application, that requires my phone number, just to provide feedback on a product. So Spotware is alienating a lot of users this way.

We are non alienating anyone. As explained above, this change was proposed by traders inside this forum and was voted by 158 users. Voting in Telegram is indicative, since a substantial number of users has joined and we can have a sample with statistical significance. The vote just confirms that the vast majority of cTrader uses either approve this change or have no problem with it. Out of 2700+ members of the group, only 19 explicitly prefer the old layout. If you are so dissatisfied with this change, create a suggestion of what you would like to change, bring a considerable number of votes and we will seriously consider it. [This is how other traders achieved this change]. If you want the software to be developed by traders for traders, then you will also have to accept the burden of democracy :). 

Best Regards,

Panagiotis 

Join us on Telegram

Ha ha ha to your burden of democracy comment :-)

How many registered users are there? I haven't actually seen that post, but if 158/180 users voted for it, okay; but if you have 158 out of 1000+ registered cTrader users (they all have to have accounts to use cTrader), that's just scraping 15% approval rating from users, which isn't a majority by any means.

I would put forth the suggestion that since Spotware has everyone's email address that has joined the forum, to put out a survey to all registered forum users through something like Survey Monkey. This has two advantages:

1) nobody has to download, sign up to, or register themselves with any other phone/mobile application or website

2) you can easily count all the votes from all your registered cTrader users, not just those small number of people who are active within the forums (which your currently survey is limited by since numerous people who use cTrader don't use the forums)

So I am for the burden of democracy, but only the kind of democracy that has been announced and open to everyone - not just the select few in one forum or on one application.

:-P :-)


@firemyst

firemyst
22 Mar 2021, 15:39 ( Updated at: 22 Mar 2021, 15:43 )

This vote is biased in that it's only asking for feedback on one specific element of the layout.

The problem and issue I have is where did the "new order" button go to? I can't find it anywhere.

I either get the one click shortcuts at the top of the chart (which is horrendous), or I have to right click to get "new order", or (worst case scenario) I have to have active symbol panel open (which takes up a lot of space!).

So my vote is bring back the old layout if we can get those "new order" buttons that were at the top back! Unless they're somewhere else?

 

Thank you.

 

PS: I agree with everyone else here and have complained about it previously -- I should NOT have to join telegram for this. There's websites for taking polls like Survey Monkey and others. I shouldn't have to download an application, that requires my phone number, just to provide feedback on a product. So Spotware is alienating a lot of users this way.

Can we have a poll on whether Telegram, Survey Monkey, or some other software should be used in the future for polling? :-)


@firemyst

firemyst
22 Feb 2021, 08:55

RE:

PanagiotisCharalampous said:

Hi firemyst,

You can create a custom enum and then a method that would return the respective data source.

Best Regards,

Panagiotis 

Join us on Telegram

Thank you @Panagiotis.

I was hoping I wouldn't have to do that though, because in the future I might want other sources to be other indicators that are on the chart. Currently, the source let's us select those and it changes dynamically depending on the indicators on the screen. I was hoping there would be a similar, easy way with the MTF approach.


@firemyst

firemyst
16 Feb 2021, 07:37

It depends on how the bot is programmed.

 

If a bot is stopped, it loses all its current information, including any positions it opened.

 

So the bot has to be programmed to pick all that back up when it's restarted so it can identify any positions it had opened, and retake control of them.


@firemyst

firemyst
09 Feb 2021, 04:10

In your OnTick or SymbolTick methods, it doesn't look like you're getting any values for the indicators.

In OnTick, with every tick, you're getting the actual indicator itself over and over and over again (waste of CPU and memory resources), but not the values.


//You need to do something like this using MACD as an example:

//_macd1 is a class variable defined as MacdCrossOver _macd1;
//all the variables ending "CurrentValue" are just plain doubles

double a = _macd1.Histogram.Last(0);
a = _macd1.MACD.Last(0);
a = _macd1.Signal.Last(0);
_macd1HistogramCurrentValue = _macd1.Histogram.Last(0);
_macd1LineCurrentValue = _macd1.MACD.Last(0);
_macd1SignalLineCurrentValue = _macd1.Signal.Last(0);

The reason' it's done as it is above is because cTrader does "lazy" loading of indicator values. That is, it doesn't load the values unless explicitly called. Hence why the safe way to get the values is to follow the pattern as shown above.


@firemyst