Topics
Forum Topics not found
Replies
amusleh
06 Jan 2022, 08:51
RE: Diffenence of multiple positions
budda_dan2020 said:
Hi Amusleh,
if an order partially filled by multiple positions, and the positions have same order ID and position IDs, what are difference of these positions? How can I differentiate them? Are there any other IDs which differ from each other?
Hi,
Each order is linked to a single position, it doesn't matter by how many deals it gets filled.
You can use the filled volume in ExecutionEvent or deals to differentiate between them, an order can be filled by one or more deals but it will always have one position ID.
@amusleh
amusleh
05 Jan 2022, 09:13
RE: RE: Any update?
budda_dan2020 said:
amusleh said:
Hi,
We are aware of this issue and we are investigating to find the cause of it.
Thanks for reporting.
Hi Amusleh,
is there any update about issue?
I can not run the app since two days.
Can a new app with new auth datas work?
Kind regards
Hi,
The issue is not fixed yet, we are working on it and it will be fixed ASAP.
Thanks for you patience.
@amusleh
amusleh
05 Jan 2022, 08:14
RE: Backtesting features
scotpip said:
Hi,
Why do you think cTrader back tester is a toy back tester?
Well - if I'm missing something please tell me, but on a first view:
- The reporting is minimal. Many important metrics missing. No analysis by month, day, hour, session-hour, for example - which is vital stuff.
- There is an optimiser, but it's very hard to review the results for robustness and there's no export, so I'd have to hack something.
- No Monte Carlo robustness testing.
- No walk-forward testing - which is extremely important for avoiding over-fitting
- Doesn't work with Renko and Range bars
- Pretty cludgy to test multiple pairs on the same strategy
- Looks virtually impossible to do real portfolio testing and optimisation
Etc.
Not anywhere approaching a professional backtesting tool, IMHO. Or am I wrong?
Hi,
The Renko/Range back testing will be added in next upcoming versions.
You can calculate any metric programmatically by using the cBot history and generate a report based on your needs, then you can write it on a CSV or Excel file on your disk.
Regarding your other mentioned points, please open a thread under suggestions section of forum, if your suggested features got enough vote from community then we will consider adding them.
@amusleh
amusleh
05 Jan 2022, 08:09
Hi,
If an order partially filled by multiple positions then the order/position ID will be same on subsequent ExecutionEvent calls.
Yes, you will get two ProtoOAExecutionEvent calls for each fill.
Yes, again the order/position ID will be same but it will have two different deal IDs.
@amusleh
amusleh
04 Jan 2022, 08:52
RE: RE:
scotpip said:
amusleh said:
Hi,
If you use bars data then the back tester will only have access to each bar OHLC prices, and it will iterate over them one by one.
If you have a pending order at 1.02 and the next bar close/high price was above it then the order will be considered triggered at 1.02 not at the bar close/high price.
To get more accurate results you can use tick data for back testing instead of bars data.
Thanks - that's what I was hoping. It's surprising how many backtesters can't do this and only recognise pending orders at the price on bar close.
My preferred workflow is to do initial research with bars for speed, and verification at tick resolution for accuracy. But if you're ever tested a 20 pair algo with ticks you'll realise how glacially slow that can be!
So now I can put cTrader on the list, I have to decide whether to go with the nice CTrader API and live with the toy backtester, or go with the better backtester on MT5 and live with the horrible API and strange proprietary language...
Can I ask why you decided to go with CTrader?
Hi,
Why do you think cTrader back tester is a toy back tester?
@amusleh
amusleh
03 Jan 2022, 11:48
( Updated at: 19 Mar 2025, 08:57 )
RE: RE: RE:
Ketu said:
amusleh said:
Ketu said:
I've got the console sample app working on VS 2022 but it just hangs after authenticating
Hi,
Does it show any error message?
Sorry, Hangs wasn't the right word, the program still runs but the account authorisation window stays up and nothing happens
Hi,
I just tested with a PepperstoneUK demo account, it works fine on my system.
I tired on both TCP and web socket, and both of them worked fine.
Can you try on console sample web socket?
If it still doesn't work please email us these info:
- Your Open API application credentials
- Access token you used
- Proxy/endpoint and port you tried to connect
- And if possible screenshot or a video of your experience
email: support@ctrader.com
@amusleh
amusleh
03 Jan 2022, 09:28
( Updated at: 03 Jan 2022, 09:32 )
Hi,
Try to use our console sample at: spotware/OpenAPI.Net: Spotware Open API .NET Rx library (github.com)
See if it works or you will get the same error message.
I just tested and everything was working fine.
You can also test this web app sample: Blazor.WebSocket.Sample (spotware.github.io)
And can you tell which broker you are using and which endpoint? live or demo?
@amusleh
amusleh
03 Jan 2022, 09:24
Hi,
What kind of resources do you need?
cTrader automate API works on .NET environment, If you have a moderate level of C# .NET experience then the API references with its examples codes will be enough.
There are a ton of resources available for C# and .NET on internet.
@amusleh
amusleh
03 Jan 2022, 09:21
Hi,
Try this:
using cAlgo.API;
using System;
using System.Globalization;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class TradingTimePeriods : Indicator
{
private readonly string _name = "Trading Time Periods";
private TimeSpan _startTime, _endTime;
private Color _backgroundColor;
private bool _isLinesPlotted;
[Parameter("Start", DefaultValue = "08:00:00", Group = "Time")]
public string StartTime { get; set; }
[Parameter("End", DefaultValue = "16:30:00", Group = "Time")]
public string EndTime { get; set; }
[Parameter("Background", DefaultValue = "Blue", Group = "Color")]
public string BackgroundColor { get; set; }
[Parameter("Transparency", DefaultValue = 50, MaxValue = 255, MinValue = 0, Group = "Color")]
public int Transparency { get; set; }
[Parameter("Thickness", DefaultValue = 1, Group = "Lines")]
public int LinesThickness { get; set; }
[Parameter("Style", DefaultValue = LineStyle.DotsVeryRare, Group = "Lines")]
public LineStyle LinesStyle { get; set; }
[Parameter("Enable", DefaultValue = true, Group = "Max Time Frame")]
public bool EnableMaxTimeFrame { get; set; }
[Parameter("Time Frame", DefaultValue = "Hour4", Group = "Max Time Frame")]
public TimeFrame MaxTimeFrame { get; set; }
[Parameter("Interactive", DefaultValue = false, Group = "Appearance")]
public bool Interactive { get; set; }
[Parameter("Filled", DefaultValue = true, Group = "Appearance")]
public bool Filled { get; set; }
[Parameter("Past Days #", DefaultValue = 5, MinValue = 0, Group = "Others")]
public int PastDaysNumber { get; set; }
[Parameter("Future Days #", DefaultValue = 5, MinValue = 0, Group = "Others")]
public int FutureDaysNumber { get; set; }
[Parameter("Skip Weekends", DefaultValue = true, Group = "Others")]
public bool SkipWeekends { get; set; }
public ChartArea Area
{
get
{
return IndicatorArea ?? (ChartArea)Chart;
}
}
protected override void Initialize()
{
_backgroundColor = GetColor(BackgroundColor, Transparency);
ParseTimes();
Application.UserTimeOffsetChanged += Application_UserTimeOffsetChanged;
}
public override void Calculate(int index)
{
if (!IsLastBar || _isLinesPlotted || (EnableMaxTimeFrame && TimeFrame > MaxTimeFrame))
{
return;
}
_isLinesPlotted = true;
DateTime firstBarTime = AddDays(Bars.OpenTimes[index], -PastDaysNumber).Date;
DateTime lastDayTime = AddDays(Bars.OpenTimes[index], FutureDaysNumber).Date;
double maxPriceLevel = Bars.HighPrices.Maximum(Bars.HighPrices.Count) * 3;
for (DateTime currentTime = firstBarTime; currentTime <= lastDayTime; currentTime = AddDays(currentTime, 1))
{
string objName = string.Format("{0} {1}", _name, currentTime);
DateTime startTime = currentTime.Add(_startTime);
DateTime endTime = currentTime.Add(_endTime);
ChartRectangle rectangle = Chart.DrawRectangle(objName, startTime, 0, endTime, maxPriceLevel, _backgroundColor, LinesThickness, LinesStyle);
rectangle.IsFilled = Filled;
rectangle.IsInteractive = Interactive;
}
}
private void Application_UserTimeOffsetChanged(UserTimeOffsetChangedEventArgs obj)
{
ParseTimes();
_isLinesPlotted = false;
}
private DateTime AddDays(DateTime dateTime, int numberOfDays)
{
DateTime result = dateTime;
for (int i = 1; i <= Math.Abs(numberOfDays); i++)
{
result = result.AddDays(numberOfDays > 0 ? 1 : -1);
if (SkipWeekends && (result.DayOfWeek == DayOfWeek.Saturday || result.DayOfWeek == DayOfWeek.Sunday))
{
result = result.DayOfWeek == DayOfWeek.Saturday ? result.AddDays(numberOfDays > 0 ? 2 : -2) : result.AddDays(numberOfDays > 0 ? 1 : -1);
}
}
return result;
}
private void ParseTimes()
{
if (TimeSpan.TryParse(StartTime, CultureInfo.InvariantCulture, out _startTime))
{
_startTime = _startTime.Add(-Application.UserTimeOffset);
}
if (TimeSpan.TryParse(EndTime, CultureInfo.InvariantCulture, out _endTime))
{
_endTime = _endTime.Add(-Application.UserTimeOffset);
}
}
private Color GetColor(string colorString, int alpha = 255)
{
var color = colorString[0] == '#' ? Color.FromHex(colorString) : Color.FromName(colorString);
return Color.FromArgb(alpha, color);
}
}
}
@amusleh
amusleh
03 Jan 2022, 09:15
RE: Was anything done about this
sue.bugg said:
Hi, was anythng ever done about this? I want my 1HR cBot to start a 5min cBot with specific instance name.
Eg AUDUSD 1 HR chart has cBot running. If all logic in this is met, I don't want it to trade, but I want it start the AUDUSD cBot on the 5 min chart. Say it's instance name is "S1AU"
Can this now be done?
Thanks
Sue
Hi,
Why you need such a feature? I don't get this at all.
Can't you put the logic of your 5 min cBot inside your 1h cBot? If conditions met in your 1h cBot then it will execute the logic of 5 min cBot.
Why you want to make it more complicated?
You can access different time frames data inside a single cBot/indicator instance, you don't have to run multiple instances to get multiple time frames data.
You can also access other symbols data, not just the current symbol other time frames data.
@amusleh
amusleh
03 Jan 2022, 09:11
Hi,
If you use bars data then the back tester will only have access to each bar OHLC prices, and it will iterate over them one by one.
If you have a pending order at 1.02 and the next bar close/high price was above it then the order will be considered triggered at 1.02 not at the bar close/high price.
To get more accurate results you can use tick data for back testing instead of bars data.
@amusleh
amusleh
03 Jan 2022, 09:06
Hi,
It doesn't show the dots because your limiting it to only draw the dot if it's a LastBar, try this:
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class X_SMA : Indicator
{
[Output("SMA Move", LineColor = "75FFFFFF", Thickness = 2, PlotType = PlotType.Line)]
public IndicatorDataSeries SMA_Move { get; set; } //White SMA line
[Output("Cross SMA Above", LineColor = "BB77FF77", IsHistogram = false, LineStyle = LineStyle.Dots, PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries CrossAbove { get; set; } //Green Dot Signal
[Output("Cross SMA Below", LineColor = "BBFF7777", IsHistogram = false, LineStyle = LineStyle.Dots, PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries CrossBelow { get; set; } //Red Dot Signal
private SimpleMovingAverage SMA;
protected override void Initialize()
{
SMA = Indicators.SimpleMovingAverage(Bars.ClosePrices, 50);
}
public override void Calculate(int index)
{
SMA_Move[index] = SMA.Result.LastValue;
if (TimeFrame.Name != "Hour") return;
if (Bars.ClosePrices.LastValue > SMA.Result.LastValue && Bars.ClosePrices.Last(1) < SMA.Result.Last(1))
{
CrossAbove[index] = Bars.LowPrices[index] - 10 * Symbol.PipSize;
if (IsLastBar)
{
Notifications.SendEmail("*****@icloud.com", "*****@icloud.com", "X 50 SMA - " + Symbol.Name, "X ABOVE");
}
}
else if (Bars.ClosePrices.LastValue < SMA.Result.LastValue && Bars.ClosePrices.Last(1) > SMA.Result.Last(1))
{
CrossBelow[index] = Bars.HighPrices[index] + 10 * Symbol.PipSize;
if (IsLastBar)
{
Notifications.SendEmail("*****@icloud.com", "*****@icloud.com", "X 50 SMA - " + Symbol.Name, "X BELOW");
}
}
}
}
}
There is still one issue with above code, you are drawing a dot for bars that are not closed/completed yet.
And that will cause repaint issue as the close price can change, instead you should use Last closed bar data:
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class X_SMA : Indicator
{
[Output("SMA Move", LineColor = "75FFFFFF", Thickness = 2, PlotType = PlotType.Line)]
public IndicatorDataSeries SMA_Move { get; set; } //White SMA line
[Output("Cross SMA Above", LineColor = "BB77FF77", IsHistogram = false, LineStyle = LineStyle.Dots, PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries CrossAbove { get; set; } //Green Dot Signal
[Output("Cross SMA Below", LineColor = "BBFF7777", IsHistogram = false, LineStyle = LineStyle.Dots, PlotType = PlotType.Points, Thickness = 5)]
public IndicatorDataSeries CrossBelow { get; set; } //Red Dot Signal
private SimpleMovingAverage SMA;
protected override void Initialize()
{
SMA = Indicators.SimpleMovingAverage(Bars.ClosePrices, 50);
}
public override void Calculate(int index)
{
SMA_Move[index] = SMA.Result[index];
if (TimeFrame.Name != "Hour") return;
if (Bars.ClosePrices[index - 1] > SMA.Result[index - 1] && Bars.ClosePrices[index - 2] < SMA.Result[index - 2])
{
CrossAbove[index - 1] = Bars.LowPrices[index - 1] - 10 * Symbol.PipSize;
if (IsLastBar)
{
Notifications.SendEmail("*****@icloud.com", "*****@icloud.com", "X 50 SMA - " + Symbol.Name, "X ABOVE");
}
}
else if (Bars.ClosePrices[index - 1] < SMA.Result[index - 1] && Bars.ClosePrices[index - 2] > SMA.Result[index - 2])
{
CrossBelow[index - 1] = Bars.HighPrices[index - 1] + 10 * Symbol.PipSize;
if (IsLastBar)
{
Notifications.SendEmail("*****@icloud.com", "*****@icloud.com", "X 50 SMA - " + Symbol.Name, "X BELOW");
}
}
}
}
}
@amusleh
amusleh
03 Jan 2022, 08:57
Hi,
If you want to get the count of open Positions then you can use Positions.Count:
private void OnPositionsOpened(PositionOpenedEventArgs args)
{
Print("Positions Count: ", Positions.Count);
}
And regarding your code, there is no issue, I tested it and it does work fine, but you don't have to count the Position by yourself, just use Positions.Count.
@amusleh
amusleh
31 Dec 2021, 12:00
RE: Order execution error
SummerSalt said:
Thank you very much amusleh, the code works great. I have another question if you don't mind. How do I prevent order execution errors when running the same bot on multiple FX pairs/assets? My strategy requires me to execute a lot of orders at the open of each bar and exit on the close of each bar. The coding works well when running on one FX pair/asset but behaves erratic on multiple charts. For example: executing orders and closing it immediately, delayed execution or not executing orders at all. I have tried to counter this by closing position well before the next bar opens (which was why I initially sought your help), so that the closing orders does not conflict with opening orders but the problem still persist. I have also tried creating multiple cbots with the same code but for the different pairs that I want to trade to avoid conflict but this also did not work. Is there any kind of solution for this problem or does it mean I can only run one bot at a time? Thanks for your time.
Hi,
There is no connection between two running instances of a cBot, so there should be no conflict at all.
Most probably something is wrong with your code, post it then I will be able to help you.
And also post the error messages.
@amusleh
amusleh
07 Jan 2022, 07:21
Hi,
Not sure exactly what you are looking for, but when you change a chart time frame the indicator is re-initialized, so cTrader will call the indicator Initialize method and everything is recalculated.
If you want to get one candle before just minus one the index, if you have date time then first change it to index by using "Bars.OpenTimes.GetIndexByTime" then minus one the result.
@amusleh