Topics
Replies
ctid+customer-395444
22 Apr 2022, 04:20
( Updated at: 21 Dec 2023, 09:22 )
RE:
amusleh said:
Hi,
There is no way to know when a tick results on opening of a new bar, you can use a timer to count the remaining time of a bar but that works only for time based bars not for Tick, Renko, and range bars, you have to know the time period of a time frame bar as there is no way for now to know how much is a time based time frame bar time period programmatically.
I recommend you to change the way you designed your bot, instead of using the timer, as it would be not 100% accurate.
cBots OnTick method is called before OnBar, and by a single thread, so they will never be called concurrently.
These types of issues are more related to the way you design your Bot than API limitations.
I don't think you fully understand what I'm trying to describe or I'm not being clear enough.
I do not use a timer in the algorithm. The timings you see in the log statements are from the "Log" tab in cTrader.
I use the "OnTick" and "OnBar" methods with a boolean flag. The code is structured as per my first post.
The bot checked to increase the position size. Since it hadn't already done so that bar, it increased by 0.1 lots, and then set the flag "_enteredPositionThisBar" to true so the bot would not enter another position in the current bar.
That was on the tick called just before the OnBar, but the clock had already ticked over to the next minute, so it should have occurred during the 21.00.00 bar and not the bar opened at 20.59.00.
Then the next bar is opened, and since the flag "_enteredPositionThisBar" was set to false in the "OnBar" method, the bot immediately increased the position size again during the 21.00.00 bar as you can see from the screen capture. Note I do NOT use any asynchronous calls when placing orders.
20/04/2022 20:51:39.729 | cBot "BugBot" was started successfully for NAS100, m1.
20/04/2022 20:51:54.348 | OnTick
:
20/04/2022 21:00:00.775 | OnTick --> it increased the size here, adding 0.1 lots. "_enteredPositionThisBar" set to true.
20/04/2022 21:00:00.909 | OnBar --> "_enteredPositionThisBar" set to false.
20/04/2022 21:01:00.313 | OnTick --> it increased the size again here by 0.1 lots and reset the flag the true.
20/04/2022 21:00:00.418 | OnTick
So because the "OnBar" even occurs _after_ the first tick event in that new bar, it opens a position twice during the same bar when technically it shouldn't since the new bar hadn't been opened yet. With the log above and screen captures below, do you see what I'm saying?
Since boolean flags won't work since tick events for a new bar's timeframe occur _before_ the "OnBar" event actually occurs, and we can't use the Bars.OpenTimes.Count to check if the number of bars have increased (because on the first tick in the new M1 bar, the new bar hasn't been formed yet), would you be able to provide sample code to get around this issue?
Here's what happened. Note the deal map and compare against the logs. cTrader shows BOTH orders being displayed in the same bar even though the first one happened during the tick event _before_ the "OnBar" event:
Thank you.
@ctid+customer-395444
ctid+customer-395444
20 Apr 2022, 10:27
RE:
amusleh said:
Hi,
cTrader can load different number of data on your chart, there is no fixed number of bars.
You shouldn't rely on bar index numbers to be same across different machines, use bar open times instead.
Thank you for your response and confirmation.
@ctid+customer-395444
ctid+customer-395444
20 Apr 2022, 04:03
( Updated at: 21 Dec 2023, 09:22 )
Any updates @Spotware?
Hello @Spotware / @Panagiotis / @Amusleh,
Following up as haven't seen a response in regards to this.
This is AUDCAD pair on Daily timeframe. First capture below is from my local laptop in UTC+8 time; second capture is from my VPS located in New York, but set to UTC + 8 time.
Since this is the DAILY timeframe and both charts are freshly loaded (eg, cTrader just started and no scrolling has been done), I would expect bar numbers and current indexes to be the same on both charts; however as you can see, they are way off. The indicator code used is in my original post.
This tells me that cTrader initially loads different amounts of chart data depending on what machine it's running on? Are you able to confirm this?
Otherwise, why would one daily chart for AUDCAD be up to bar number 1498 and the other be up to 1654 when both charts are freshly loaded on the same day in UTC time without any historical scrolling having been done?
Thank you.
Capture from local laptop:
Capture from VPS on same day a few minutes apart:
@ctid+customer-395444
ctid+customer-395444
05 Apr 2022, 09:09
> Hi, I would like to run this bot on different instances, but I couldnt do it. I tried label and symbol too.
What do you mean?
To run multiple instances of a bot, just add more "instances" under the bot and choose the symbol you want to run it against.
Example:
In teh above, one instance will run on EURUSD H1 timeframe, the other bot will run under the US30 symbol.
@ctid+customer-395444
ctid+customer-395444
05 Apr 2022, 09:04
Copy what?
Trades?
Code?
Set ups?
Charting templates?
Indicators?
EA's?
You'd probably get more answers if your request wasn't so vague.
@ctid+customer-395444
ctid+customer-395444
29 Mar 2022, 11:28
RE:
amusleh said:
Hi,
It does work, but it depends on the email service you are using, some services doesn't allow old SMTP authentication anymore like Gmail as it causes security issues.
I tested it on my own email server and it works fine.
cTrader automate API just provide a wrapper over .NET SMTP API to make it easier.
So it definitely works with gmail? I can set up a test gmail account, keep the same indicator code (except changing the email addresses as appropriate), and all should work "as is"?
Also, what do you mean by "old smtp authentication"? All the links I found, and posted in my original post, explain how to set up SMTP authentication with the parameters required by cTrader.
Thank you.
@ctid+customer-395444
ctid+customer-395444
20 Mar 2022, 05:02
Obviously it could depending on what your cBot does.
For instance - latency. Does your cBot continuously make big network calls sending and/or receiving huge amounts of data across the network? If so, then duh... obviously it could affect latency.
Your cBot could be 1Gig in size, but in terms of performance, size doesn't matter ;-) Again, it's what's inside the code that counts. If your cBot is heavily CPU intensive, then duh again... obviously it could affect performance depending on your VPS's configuration (Eg, do you have 2 cores? 4 cores? 8 cores?)
So without posting details of your cBot, there's no "one size fits all answer" to your question.
@ctid+customer-395444
ctid+customer-395444
18 Mar 2022, 09:00
RE:
amusleh said:
Hi,
The bar index should work fine, if it's not then it's a bug.
Can you tell me which broker you are using? which Renko chart? I will try to reproduce it.
Great. You have the code and screen capture above.
Broker is Pepperstone.
Renko chart is Re5.
Chart is NAS100. Date/time on the screen capture. But just in case, time in UTC + 8 is Mar 17th from 04:43 - 04:54
Thank you.
@ctid+customer-395444
ctid+customer-395444
12 Mar 2022, 12:29
No, it's not as of the last time I asked @Panagiotis.
Perhaps either he or someone from @Spotware can indicate if it's even in the timeline as a feature to be added to the API?
@ctid+customer-395444
ctid+customer-395444
10 Mar 2022, 15:01
THis happened with the latest driver update today from Intel. I've had to roll it back.
I've submitted a report to @Spotware through the ctrl-shift-alt-T so Spotware can confirm if it is another bug in the Intel driver or not.
It affects both my Pepperstone and IC Markets cTrader platforms.
@ctid+customer-395444
ctid+customer-395444
22 Feb 2022, 13:28
( Updated at: 22 Feb 2022, 13:31 )
RE: RE: RE: RE:
BJORNBERNAU said:
Thank you! Well, I tried it. This does not change it. The code is now the following
protected override void OnBar()
{
double a = S2.sell_ONE.Last(0);
a = S2.liq_ONE.Last(0);
double b = S2.liq_ONE.Last(0);
b = S2.liq_ONE.Last(0);
if (!double.IsNaN(S2.sell_ONE.Last(0)) == true)
{
Print("SELL ", Bars.Count, " ", S2.sell_ONE);
}
if (!double.IsNaN(S2.liq_ONE.Last(0)) == true)
{
Print("LIQ ", Bars.Count, " ", S2.liq_ONE);
}
}
Resulting in this data stream - a block-wise procurement of SELL and LIQ.
double b = S2.liq_ONE.Last(0);
b = S2.liq_ONE.Last(0);
WHY ARE YOU DOING THIS?! Not only are you assigning the same value to "b" twice, but you're also declaring a new variable, of which there's no need to declare a new "double b".
As I said, who cares if "a" gets overwritten with a new value?? -- you don't care about the value, and you never use it again. Don't take up extra computing resources by declaring another "double" that's not going to be used anywhere and that you're just assigning the same value to it twice in a row.
As for your overall print statements, it is working now. Your original issue was it kept printing "LastValue: NaN". It no longer does that. That issue is resolved.
If there's other logic issues in your indicator code, you'll have to figure those out or ask for help from others. :-)
@ctid+customer-395444
ctid+customer-395444
22 Feb 2022, 12:42
RE: RE:
BJORNBERNAU said:
So the "forcing" is actually happening in the Robot, not the Indicator?
Yes. You don't need to do it in the indicator. The indicator always gets its own values. However, to save on resources, the robots don't load indicator data until they're actually needed. That's why you need to force it to load to tell the robot it's needed.
Then you have to convert the double.IsNaN(S2.liq_ONE[indicatorIndex]
into
double.IsNaN(a) == false
is it or how does the Robot find "a"?
You do not care about "a" any more as I said previously.
"a" is just used as some place to force a value to be returned from the indicator.
Now that the indicator data has been loaded by the bot, you can go about getting any values you want from the indicator. Nothing else in the code needs to be changed unless you need data from other indicators.
@ctid+customer-395444
ctid+customer-395444
22 Feb 2022, 12:17
protected override void OnTick()
{
//This is all you would have had to do:
double a = S2.sell_ONE.Last(0);
a = S2.liq_ONE.Last(0);
//That's it! Now the Calculate method has been
//forced to calculate for each sell_One and liq_one.
//You can now get any values from your indicator
//and they will be readily available.
var index = Bars.Count - 1;
var indicatorIndex = index - 2;
Chart.RemoveObject(indicatorIndex.ToString());
if (double.IsNaN(S2.sell_ONE[indicatorIndex]) == false)
{
Chart.DrawVerticalLine(indicatorIndex.ToString(), Bars.OpenTimes[indicatorIndex], Color.FromHex("#B511F7"));
}
else if (double.IsNaN(S2.liq_ONE[indicatorIndex]) == false)
{
Chart.DrawVerticalLine(indicatorIndex.ToString(), Bars.OpenTimes[indicatorIndex], Color.FromHex("#FF7CFC00"));
}
}
See code example above. It's not really necessary with amusleh's rewrite, but it illustrates the point. It's literally that simple. You'd have to do that for any other custom indicator you need values for.
Advice is to put all those calls into one method, and then just call the method at OnTick, OnBar, or whenever you'll need values from your indicators.
@ctid+customer-395444
ctid+customer-395444
22 Feb 2022, 10:16
Nothing in the referenced link talks about or explains the concept of "lazy loading" of the indicators. There are numerous questions always popping up on the forums asking about this and people not being able to get values.
@ctid+customer-395444
ctid+customer-395444
22 Feb 2022, 10:00
RE: RE:
BJORNBERNAU said:
Again, thank you.
I've just posted a broader inquiry on this issue.
https://ctrader.com/forum/indicator-support/37673
A new problem arises when I define a VAR as you suggest. How then is it converted into a IndicatorDataSeries which is defined as a parameter in the program? In doing so the Indicator doesn't work anymore.
And do we really have to go that far as to define the LAZY class to use this system?
Thank you!
You're doing it wrong, and I see you don't appear to understand some basic concepts.
I have no idea why you changed liq_one and sell_one to get a double value? Those are indicator data series, so leave them be!
You just have to FORCE the indicator to return a value. The value returned will be a double. So just create a new double variable and assign it an indicator value _before_ you actually need to compare values in your indicator data series.
As per the second article link I provided:
var a = S2.sell_ONE.Last(0);
or you could do:
double a = S2.sell_ONE.Last(0);
Do the same for liq_one right after the sell_one:
a = S2.liq_ONE.Last(0); //who cares if you overwrite the value in "a" because it's just a dummy to force the indicator to run the calculate method.
Now that you've forced the indicator to run the calculate method, just leave the rest of the code as you had it and all the indicator's values will be "full loaded". So you DO NOT have to do:
.a = S2.liq_ONE.Last(1);
.a = S2.liq_ONE.Last(2)
etc etc;
@ctid+customer-395444
ctid+customer-395444
22 Feb 2022, 02:38
HI @BJORNBERNAU
Running through Visual studio on a chart:
most of the data series in the indicator itself is NaN as you can see above.
In your bot you never get the latest indicator values before checking the latest indicator values.
It could the "lazy loading" of indicators from cbots as @Panagiotis mentioned in your thread here from Aug 3, 2020:
Also see the last post in this thread:
So you need to force the indicator to call the calculate method before you access any of the indicator's data series.
@ctid+customer-395444
ctid+customer-395444
21 Feb 2022, 15:16
( Updated at: 21 Feb 2022, 15:26 )
Glancing at your code, a possible problem is you never actually set the last value of either data series.
You only ever populate [index - 2] of either sell_One or liq_one, never [index].
Therefore:
s2.liq_one.last(0) is NAN
s2.sell_one.last(0) is NAN
s2.liq_one.last(1) is NAN
s2.sell_one.last(1) is NAN
so the .Last(0) values are both the same, hence the reason they both print out and don't alternate.
Similarly, the .Last(1) values are both the same too.
@ctid+customer-395444
ctid+customer-395444
18 Feb 2022, 07:08
There's at least two easy options:
1) save the values to a data file, and then read the values back in when you restart your bot. You'll probably want to save to an XML or Json file so you have easy parsers available.
2) not the best practice, but you can create static variable collections and reference those. As long as you don't close cTrader, they should be around. However, if you close down cTrader, you'll lose the values in those variables.
@ctid+customer-395444
ctid+customer-395444
14 Feb 2022, 14:41
RE: RE: RE: RE: RE: RE:
m4trader4 said:
cBot crashed: Error #90580554
cBot crashed: Error #65147970
If you check this thread:
according to @amusleh, this happens with high volatility. So again this probably comes back to your code trying to move/check a stop loss when the position could have been closed elsewhere.
This could happen either with ".HasValue" or ".Stoploss.GetValueOrDefault()".
It also says you need a flag in your code to see if the closing event has been fired. If it has, you can't go through, check positions, and modify stop losses because one of the positions in your foreach loop could have been closed between the time you check the length of the positions and go through the foreach loop.
@ctid+customer-395444
ctid+customer-395444
25 Apr 2022, 09:11
Yes.
In your code set a boolean flag to indicate that you've done your modification on the position. For example, "alreadyModifiedPosition = true;"
Every time you go through your code, only enter into the part that does the position modification if the flag is set to false.
Eg:
if (!alreadyModifiedPosition)
{
/// do your position modification
alreadyModifiedPosition = true;
}
Then you just have to figure out the logic when you reset alreadyModifiedPosition back to false. Every time you start your bot? When the position is closed? Something else?
Good luck! :-)
@ctid+customer-395444