Topics
Forum Topics not found
Replies
amusleh
08 Apr 2022, 11:06
Hi,
You cBot code has lots of issues, I can't fix all of them and I always recommend users to first learn C# basics then start developing cBots/Indicators.
I fixed some of your cBot issues, 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 DemoBot : Robot
{
[Parameter(DefaultValue = 10000, MinValue = 1000)]
public int FirstLot { get; set; }
[Parameter(DefaultValue = 10000, MinValue = 1000)]
public int LotStep { get; set; }
[Parameter("Stop_Loss", DefaultValue = 50, MinValue = 0)]
public int Stop_Loss { get; set; }
[Parameter("Take_Profit", DefaultValue = 180, MinValue = 10)]
public int TakeProfit { get; set; }
[Parameter("Tral_Start", DefaultValue = 50, MinValue = 10)]
public int Tral_Start { get; set; }
[Parameter("Tral_Stop", DefaultValue = 50, MinValue = 10)]
public int Tral_Stop { get; set; }
[Parameter(DefaultValue = 5, MinValue = 2)]
public int MaxOrders { get; set; }
private bool RobotStopped;
private string botLabel;
protected override void OnStart()
{
botLabel = string.Format("{0}_{1}_DemoBot", SymbolName, TimeFrame);
Positions.Opened += OnPositionOpened;
}
protected override void OnTick()
{
double Bid = Symbol.Bid;
double Ask = Symbol.Ask;
double Point = Symbol.TickSize;
if (Positions.Count > 0 && RobotStopped)
return;
else
RobotStopped = false;
if (Positions.Count == 0)
SendFirstOrder(FirstLot);
else
ControlSeries();
foreach (var position in Positions.FindAll(botLabel, SymbolName))
{
if (position.SymbolName == Symbol.Name)
{
if (position.TradeType == TradeType.Buy)
{
if (Bid - GetAveragePrice(TradeType.Buy) >= Tral_Start * Point)
if (Bid - Tral_Stop * Point >= position.StopLoss)
ModifyPosition(position, Bid - Tral_Stop * Point,
position.TakeProfit);
}
if (position.TradeType == TradeType.Sell)
{
if (GetAveragePrice(TradeType.Sell) - Ask >= Tral_Start * Point)
if (Ask + Tral_Stop * Point <= position.StopLoss || position.StopLoss
== 0)
ModifyPosition(position, Ask + Tral_Stop * Point,
position.TakeProfit);
}
}
}
}
protected override void OnError(Error CodeOfError)
{
if (CodeOfError.Code == ErrorCode.NoMoney)
{
RobotStopped = true;
Print("ERROR!!! No money for order open, robot is stopped!");
}
else if (CodeOfError.Code == ErrorCode.BadVolume)
{
RobotStopped = true;
Print("ERROR!!! Bad volume for order open, robot is stopped!");
}
}
private void SendFirstOrder(int OrderVolume)
{
int Signal = GetStdIlanSignal();
if (!(Signal < 0))
switch (Signal)
{
case 0:
ExecuteMarketOrder(TradeType.Buy, SymbolName, OrderVolume, botLabel);
break;
case 1:
ExecuteMarketOrder(TradeType.Sell, SymbolName, OrderVolume, botLabel);
break;
}
}
private void OnPositionOpened(PositionOpenedEventArgs args)
{
if (args.Position.Label.Equals(botLabel, StringComparison.Ordinal) == false) return;
double? StopLossPrice = null;
double? TakeProfitPrice = null;
if (Positions.Count == 1)
{
var position = args.Position;
if (position.TradeType == TradeType.Buy)
TakeProfitPrice = position.EntryPrice + TakeProfit * Symbol.TickSize;
if (position.TradeType == TradeType.Sell)
TakeProfitPrice = position.EntryPrice - TakeProfit * Symbol.TickSize;
}
else
switch (GetPositionsSide())
{
case 0:
TakeProfitPrice = GetAveragePrice(TradeType.Buy) + TakeProfit *
Symbol.TickSize;
break;
case 1:
TakeProfitPrice = GetAveragePrice(TradeType.Sell) - TakeProfit *
Symbol.TickSize;
break;
}
foreach (var position in Positions)
{
if (position.Label.Equals(botLabel, StringComparison.Ordinal) == false) continue;
if (StopLossPrice != null || TakeProfitPrice != null)
{
ModifyPosition(position, position.StopLoss, TakeProfitPrice);
}
}
}
private double GetAveragePrice(TradeType TypeOfTrade)
{
double Result = Symbol.Bid;
double AveragePrice = 0;
long Count = 0;
foreach (var position in Positions)
{
if (position.Label.Equals(botLabel, StringComparison.Ordinal) == false) continue;
if (position.TradeType == TypeOfTrade)
{
AveragePrice += position.EntryPrice * position.Volume;
Count += position.Volume;
}
}
if (AveragePrice > 0 && Count > 0)
Result = AveragePrice / Count;
return Result;
}
private int GetPositionsSide()
{
int Result = -1;
int i, BuySide = 0, SellSide = 0;
for (i = 0; i < Positions.Count; i++)
{
if (Positions[i].TradeType == TradeType.Buy)
BuySide++;
if (Positions[i].TradeType == TradeType.Sell)
SellSide++;
}
if (BuySide == Positions.Count)
Result = 0;
if (SellSide == Positions.Count)
Result = 1;
return Result;
}
private void ControlSeries()
{
const int BarCount = 25;
int gradient = MaxOrders - 1;
foreach (Position position in Positions.FindAll(botLabel, SymbolName))
{
if (-position.Pips > Stop_Loss)
ClosePosition(position);
}
int _pipstep = GetDynamicPipstep(BarCount, gradient);
if (Positions.Count < MaxOrders)
{
long NewVolume = Symbol.NormalizeVolume(FirstLot + FirstLot * Positions.Count,
RoundingMode.ToNearest);
int positionSide = GetPositionsSide();
switch (positionSide)
{
case 0:
if (Symbol.Ask < FindLastPrice(TradeType.Buy) - _pipstep *
Symbol.TickSize)
{
if (NewVolume >= LotStep)
ExecuteMarketOrder(TradeType.Buy, SymbolName, NewVolume,
botLabel);
}
break;
case 1:
if (Symbol.Bid > FindLastPrice(TradeType.Sell) + _pipstep *
Symbol.TickSize)
{
if (NewVolume >= LotStep)
ExecuteMarketOrder(TradeType.Sell, SymbolName, NewVolume,
botLabel);
}
break;
}
}
}
private int GetDynamicPipstep(int CountOfBars, int gradient)
{
int Result;
double HighestPrice = 0, LowestPrice = 0;
int StartBar = Bars.ClosePrices.Count - 2 - CountOfBars;
int EndBar = Bars.ClosePrices.Count - 2;
for (int i = StartBar; i < EndBar; i++)
{
if (HighestPrice == 0 && LowestPrice == 0)
{
HighestPrice = Bars.HighPrices[i];
LowestPrice = Bars.LowPrices[i];
continue;
}
if (Bars.HighPrices[i] > HighestPrice)
HighestPrice = Bars.HighPrices[i];
if (Bars.LowPrices[i] < LowestPrice)
LowestPrice = Bars.LowPrices[i];
}
Result = (int)((HighestPrice - LowestPrice) / Symbol.TickSize / gradient);
return Result;
}
private double FindLastPrice(TradeType TypeOfTrade)
{
double LastPrice = 0;
foreach (var position in Positions)
{
if (position.Label.Equals(botLabel, StringComparison.Ordinal) == false) continue;
if (TypeOfTrade == TradeType.Buy && position.TradeType == TypeOfTrade)
{
if (LastPrice == 0)
{
LastPrice = position.EntryPrice;
continue;
}
if (position.EntryPrice < LastPrice)
LastPrice = position.EntryPrice;
}
if (TypeOfTrade == TradeType.Sell && position.TradeType == TypeOfTrade)
{
if (LastPrice == 0)
{
LastPrice = position.EntryPrice;
continue;
}
if (position.EntryPrice > LastPrice)
LastPrice = position.EntryPrice;
}
}
return LastPrice;
}
private int GetStdIlanSignal()
{
int Result = -1;
int LastBarIndex = Bars.ClosePrices.Count - 2;
int PrevBarIndex = LastBarIndex - 1;
if (Bars.ClosePrices[LastBarIndex] > Bars.OpenPrices[LastBarIndex])
if (Bars.ClosePrices[PrevBarIndex] > Bars.OpenPrices[PrevBarIndex])
Result = 0;
if (Bars.ClosePrices[LastBarIndex] < Bars.OpenPrices[LastBarIndex])
if (Bars.ClosePrices[PrevBarIndex] < Bars.OpenPrices[PrevBarIndex])
Result = 1;
return Result;
}
}
}
@amusleh
amusleh
08 Apr 2022, 10:54
( Updated at: 08 Apr 2022, 11:12 )
RE: RE:
shaahin69 said:
amusleh said:
Hi,
Visual studio 2022 and .NET 6 support is added to cTrader 4.2 which is in beta testing now, you can try it by downloading Spotware cTrader Beta.
Thank you for your reply.
However, I already got cTrader 4.2 along with a new cBot project using .NET 6.
Although I can build the cBot ok without any issue, but debugging doesn't work.
Do I require https://marketplace.visualstudio.com/items?itemName=Spotwareextensions.cBotsandCustomIndicators for Visual Studio 2022 for debugging?
Hi,
If your project is a legacy project created by older versions of cTrader and targeting .NET framework then you can't use Visual Studio 2022 to open it, you can only open new cTrader 4.2 projects with Visual Studio 2022.
Regarding debugging, the way we were debugging old .NET framework cBots/Indicators has changed, and there is no Visual studio extension for new cTrader 4.2 projects, you can build a cBot/Indicator algo file with any .NET IDE just by referencing the cTrader automate Nuget package.
We are creating a new documentation for cTrader automate that covers all these topics, it will be released alongside cTrader 4.2.
@amusleh
amusleh
08 Apr 2022, 10:40
Hi,
You can separate each instance orders by using the Symbol name, example:
using System;
using System.Linq;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using System.Collections.Generic;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public class DemoBot : Robot
{
[Parameter("Name", Group = "Global Settings", DefaultValue = "DemoBot")]
public string _name { get; set; }
[Parameter("High/Low Timeframe", Group = "Global Settings", DefaultValue = "Daily")]
public TimeFrame _hltf { get; set; }
[Parameter("Take Profit", Group = "Risk", DefaultValue = 15.0, MinValue = 0.1, MaxValue = 1000000.0)]
public double _takeprofit { get; set; }
[Parameter("Risk %", Group = "Risk", DefaultValue = 1.0, MinValue = 0.1, MaxValue = 100.0)]
public double _riskpercent { get; set; }
[Parameter("Stop Loss", Group = "Risk", DefaultValue = 10.0, MinValue = 0.1, MaxValue = 1000000.0)]
public double _stoploss { get; set; }
[Parameter("Safety (Pips)", Group = "Risk", DefaultValue = 3.0, MinValue = 0.1, MaxValue = 10000.0)]
public double _safety { get; set; }
private Bars _hlbars;
private double _previoushigh, _previouslow, _volume;
private bool _todaybuyhappened, _todaysellhappened;
public IEnumerable<PendingOrder> BotPendingOrders
{
get
{
return PendingOrders.Where(order => order.SymbolName.Equals(SymbolName, StringComparison.Ordinal) && order.Label.Equals(_name, StringComparison.Ordinal));
}
}
protected override void OnStart()
{
_hlbars = MarketData.GetBars(_hltf);
_hlbars.BarOpened += _hlbars_BarOpened;
_safety *= Symbol.PipSize;
Positions.Closed += PositionsOnClosed;
_todaybuyhappened = true;
_todaysellhappened = true;
}
private void _hlbars_BarOpened(BarOpenedEventArgs obj)
{
_todaybuyhappened = false;
_todaysellhappened = false;
foreach (var _PendingOrders in BotPendingOrders)
{
CancelPendingOrder(_PendingOrders);
}
if (_hlbars.OpenPrices.Last(0) > _hlbars.HighPrices.Last(1))
{
_todaybuyhappened = true;
}
if (_hlbars.OpenPrices.Last(0) < _hlbars.LowPrices.Last(1))
{
_todaysellhappened = true;
}
_volume = GetVolume();
_previoushigh = Math.Abs(_hlbars.HighPrices.Last(1) + _safety);
_previouslow = Math.Abs(_hlbars.LowPrices.Last(1) - _safety);
if (!_todaybuyhappened)
{
PlaceStopOrder(TradeType.Buy, Symbol.Name, _volume, _previoushigh, _name, _stoploss, _takeprofit);
_todaybuyhappened = true;
}
if (!_todaysellhappened)
{
PlaceStopOrder(TradeType.Sell, Symbol.Name, _volume, _previouslow, _name, _stoploss, _takeprofit);
_todaysellhappened = true;
}
}
private void PositionsOnClosed(PositionClosedEventArgs args)
{
_volume = GetVolume();
foreach (var _PendingOrders in BotPendingOrders)
{
if (_PendingOrders.VolumeInUnits != _volume)
{
_PendingOrders.ModifyVolume(_volume);
}
}
}
private double GetVolume()
{
double costPerPip = (double)((int)(Symbol.PipValue * 10000000)) / 100;
double baseNumber = Account.Balance;
double sizeInLots = Math.Round((baseNumber * _riskpercent / 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;
}
}
}
The BotPendingOrders enumerable will give you only those orders that are from same symbol and with same Label.
@amusleh
amusleh
08 Apr 2022, 10:31
Hi,
Do you mean adding categories/folder support on cTrader automate cBots/Indicators list? Or showing the .NET solution projects hierarchy like Visual Studio? If you mean the later then you can use Visual Studio or any other .NET IDE (after 4.2 release) for it, in case of the former please create a new thread under suggestions section.
@amusleh
amusleh
08 Apr 2022, 10:26
Hi,
There is no need to create a new data series for that, you can work on a split of a data series, ex:
/// <summary>
/// Returns the minimum value between start and end (inclusive) index in a DataSeries
/// </summary>
/// <param name="dataSeries"></param>
/// <param name="startIndex">Start index Inclusive (Ex: 1)</param>
/// <param name="endIndex">End index Inclusive (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;
}
/// <summary>
/// Returns the maximum value between start and end (inclusive) index in a DataSeries
/// </summary>
/// <param name="dataSeries"></param>
/// <param name="startIndex">Start index Inclusive (Ex: 1)</param>
/// <param name="endIndex">End index Inclusive (Ex: 10)</param>
/// <returns>double</returns>
private double Maximum(DataSeries dataSeries, int startIndex, int endIndex)
{
var max = double.NegativeInfinity;
for (var i = startIndex; i <= endIndex; i++)
{
max = Math.Max(dataSeries[i], max);
}
return max;
}
@amusleh
amusleh
07 Apr 2022, 09:16
( Updated at: 21 Dec 2023, 09:22 )
Hi,
The Chart drawings are working fine in Visual back test mode, ex:
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class TestDrawing : Robot
{
[Parameter(DefaultValue = 0.0)]
public double Price { get; set; }
protected override void OnStart()
{
var line = Chart.DrawHorizontalLine("1", Price, Color.Red);
line.IsInteractive = true;
}
}
}
Result:
If you set the IsInteractive property of a chart object to True then it will remain on chart even if back test stop, otherwise the objects will be removed from the chart on back test stop.
In non visual mode the chart drawings will not show up.
@amusleh
amusleh
07 Apr 2022, 09:06
Hi,
All cTrader services use cTrader Proxy cloud, please read: cTrader Proxy Cloud | Solving Last Mile Latency | Spotware Systems Ltd
@amusleh
amusleh
07 Apr 2022, 09:02
Hi,
If your cBot/Indicator is built with previous version of cTrader then you need Visual Studio 2019 for opening it.
For cBots/Indicators that are built with cTrader 4.2 you need Visual Studio 2022.
Installing Visual Studio 2019 might solve this issue, you can install both Visual Studio 2019 and 2022.
@amusleh
amusleh
07 Apr 2022, 08:00
Hi,
Check the API references for a sample: Event Closed | API reference | cTrader Community
@amusleh
amusleh
05 Apr 2022, 14:17
( Updated at: 21 Dec 2023, 09:22 )
RE: RE:
hejnekem said:
firemyst said:
> 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.
It doesnt work, because when I start a second instance it doesnt start, other times the first instance closes the second one.
Hi,
Are you sure it's a cTrader cBot code? It uses some APIs that is not part of cTrader automate API.
@amusleh
amusleh
05 Apr 2022, 09:54
Hi,
When you are using Nuget packages on your Indicators/cBots that target .NET framework you have to use the .NET SDK compiler, you can't use embedded compiler for those types of Indicators/cBots.
You can change the .NET compiler from your cTrader settings -> Automate tab.
@amusleh
amusleh
05 Apr 2022, 09:52
Hi,
The account balance is updated when you close a position, or you deposit/withdraw funds, for later two there is no way to know that but for position close you can use the Positions Closed event.
So what you need to do is first subscribe to Positions.Closed event and then move your calculation logic to Positions closed event handler.
@amusleh
amusleh
08 Apr 2022, 11:14
Hi,
It's not a GUID problem, cTrader 4.2 uses new SDK style projects and cTrader 4.1 uses old legacy style projects, these two aren't compatible with each other.
Here is an example of cTrader 4.2 .NET framework cBot project file:
And cTrader 4.1 cBot project file:
cTrader 4.2 can use and build cTrader 4.1 cBots/Indicators, but cTrader 4.1 can't.
@amusleh