Topics
Forum Topics not found
Replies
amusleh
26 Apr 2022, 10:05
Hi,
I did understood your point, what I was trying to explain was there is no way to accurately know when a tick will result in formation of a new bar.
Regarding your issue, you can do something like this:
using cAlgo.API;
using System.Collections.Generic;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Sample : Robot
{
private readonly Dictionary<int, PositionBarOperation> _positionBarOperations = new Dictionary<int, PositionBarOperation>();
protected override void OnTick()
{
foreach (var position in Positions)
{
PositionBarOperation operation;
// If position ID is inside _positionBarOperations
// Then it means we already executed something for it
if (_positionBarOperations.TryGetValue(position.Id, out operation))
{
// It means the bar is not changed yet
if (operation.BarIndex == Bars.Count)
{
continue;
}
// Bar changed
else
{
operation.BarIndex = Bars.Count;
// Modify, close, etc...
}
}
// Position ID is not inside _positionBarOperations
else
{
operation = new PositionBarOperation
{
Position = position,
BarIndex = Bars.Count
};
_positionBarOperations.Add(position.Id, operation);
// Modify, close, etc...
}
}
}
private class PositionBarOperation
{
public Position Position { get; set; }
public int BarIndex { get; set; }
}
}
}
@amusleh
amusleh
26 Apr 2022, 09:46
( Updated at: 26 Apr 2022, 09:47 )
RE: RE:
Fx4u said:
amusleh said:
Hi,
We are working on a cross platform console app that will allow you to run cBots on both Linux and Mac.
Regarding other Automate features like custom indicators you can only use them on a cTrader desktop which will not be available for Mac or Linux because it uses WPF.
I want to ask one more question.
I have created Cbot and run it on VPS. After a while (2 - 3 weeks), Ctrader takes up a lot of RAM (over 1GB). Is there a way to free up RAM without relaunching Ctrader?
Hi,
cTrader cBots/Indicators are based on .NET which is a managed programming environment, and .NET has a garbage collector that manages the memory and frees it automatically when your objects aren't used/referenced anymore.
So there is no need for manually freeing up the memory, it's the job of .NET garbage collector.
Regarding your issue, most probably your cBot has memory leaking issue, and you should fix it instead of cleaning the cTrader process memory.
The resource requirement of a cBot depends on your cBot, there is no fixed standard.
@amusleh
amusleh
26 Apr 2022, 09:41
( Updated at: 27 Apr 2022, 10:00 )
Hi,
When using Async calls the main idea is to avoid blocking or suspending the thread, so you call the order place/cancel method and instead of waiting for response you can continue using the thread until the response come back for your requests, so you shouldn't block the thread at all.
Regarding your issue, there are several solutions, when it comes to async programming you can use callbacks like this:
using System.Linq;
using cAlgo.API;
using System.Threading;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Sample : Robot
{
private int _numberOfPendingPlaceOrderOperations;
private int _numberOfPendingCancelOrderOperations;
protected override void OnStart()
{
SetTradesAsync();
}
private void SetTradesAsync()
{
var amountOfOrders = 20;
for (int i = 0; i < amountOfOrders; i++)
{
Interlocked.Increment(ref _numberOfPendingPlaceOrderOperations);
PlaceLimitOrderAsync(TradeType.Buy, SymbolName, Symbol.VolumeInUnitsMin, Symbol.Ask - (Symbol.PipSize * 50), "Trade_" + i, OnOrderPlaced);
}
}
private void OnOrderPlaced(TradeResult result)
{
if (Interlocked.Decrement(ref _numberOfPendingPlaceOrderOperations) == 0)
{
Print("All orders have been created");
CancelAllPendingOrdersAsync();
}
}
private void CancelAllPendingOrdersAsync()
{
var myPendingOrders = PendingOrders.Where(o => o.Label.Contains("Trade_")).ToArray();
foreach (var order in myPendingOrders)
{
Interlocked.Increment(ref _numberOfPendingCancelOrderOperations);
CancelPendingOrderAsync(order, OnOrderCancel);
}
}
private void OnOrderCancel(TradeResult result)
{
if (Interlocked.Decrement(ref _numberOfPendingCancelOrderOperations) == 0)
{
Print("All orders have been canceled");
Stop();
}
}
}
}
The better approach would be to use .NET async/await from Task parallel library but unfortunately cTrader automate API is not supporting them for now so you have to use the old callbacks pattern.
@amusleh
amusleh
26 Apr 2022, 09:19
RE: ADX INDICATOR ALERT ON TELEGRAM
steel.export said:
Dear Mr. Ahmad,
Thanks for your reply.
At the time of Built it is giving the below error
The type or namespace name 'AlertModel' could not be found (are you missing a using directive or an assembly reference?)
Hi,
Add the using cAlgo.API.Alert.Models:
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Alert;
using cAlgo.API.Alert.Models;
namespace cAlgo
{
[Indicator(IsOverlay = false, TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
public class ADXwithAlert : Indicator
{
private AverageDirectionalMovementIndexRating _adx;
private int _lastAlertBarIndex;
[Parameter(DefaultValue = 14, Group = "ADX")]
public int Periods { get; set; }
[Parameter("Level", DefaultValue = 25, Group = "Alert")]
public double AlertLevel { get; set; }
[Parameter("Setup", DefaultValue = 25, Group = "Alert")]
public bool AlertSetup { get; set; }
[Output("ADX", LineColor = "Blue")]
public IndicatorDataSeries Adx { get; set; }
[Output("ADXR", LineColor = "Yellow")]
public IndicatorDataSeries AdxR { get; set; }
[Output("DI+", LineColor = "Green")]
public IndicatorDataSeries DiPlus { get; set; }
[Output("DI-", LineColor = "Red")]
public IndicatorDataSeries DiMinus { get; set; }
protected override void Initialize()
{
_adx = Indicators.AverageDirectionalMovementIndexRating(Periods);
if (AlertSetup)
{
Notifications.ShowPopup();
}
}
public override void Calculate(int index)
{
Adx[index] = _adx.ADX[index];
AdxR[index] = _adx.ADXR[index];
DiPlus[index] = _adx.DIPlus[index];
DiMinus[index] = _adx.DIMinus[index];
if (IsLastBar && _lastAlertBarIndex != index && Adx[index] > AlertLevel)
{
_lastAlertBarIndex = index;
var type = string.Format("ADX above {0}", AlertLevel);
AlertModel alert = new AlertModel
{
TimeFrame = TimeFrame,
Symbol = Symbol,
Price = Adx[index],
TriggeredBy = "ADX with Alert",
Type = type,
Time = Server.Time
};
Notifications.TriggerAlert(alert);
}
}
}
}
@amusleh
amusleh
26 Apr 2022, 09:12
RE: RE:
TheNiatpac said:
Oh, thanks. I just realised.
Generally, if the object in chart is IsInteractive = True, is there any way to get rid of the highlight when the mouse hovers over? Maybe not, I guess...
amusleh said:
Hi,
To show the time on x axis you have to set the line IsInteractive property to true.
Hi,
You can try setting the IsLocked property of object to True, this will lock the object and user will not be able to update the object unless it's unlocked.
@amusleh
amusleh
26 Apr 2022, 09:11
Hi,
Your cBot code is executed by a single thread on an event loop, so while it's blocked inside OnStart method it can't execute OnTick or any other method/event.
If you want to keep checking for something then use the Timer, blocking the cBot thread will prevent execution of all other cBot methods, events, properties update, etc...
@amusleh
amusleh
26 Apr 2022, 09:05
Hi,
The indicator you are using on your cBot is not a cTrader built-in indicator, so I don't know how it works.
I added two more instance of it on your indicator and changed the entry/exit logic to also consider the crossing between them.
Here is the code:
using System;
using cAlgo.API;
using cAlgo.API.Requests;
using cAlgo.Indicators;
namespace cAlgo.Robots
{
[Robot()]
public class GannHiLoRobot : Robot
{
private GannHighLow _firstGann, _secondGann, _thirdGann;
private Position _position;
[Parameter("1st Gann Period", DefaultValue = 50)]
public int FirstGannPeriod { get; set; }
[Parameter("2nd Gann Period", DefaultValue = 10)]
public int SecondGannPeriod { get; set; }
[Parameter("3rd Gann Period", DefaultValue = 100)]
public int ThirdGannPeriod { get; set; }
[Parameter("Volume", DefaultValue = 10000)]
public int Volume { get; set; }
[Parameter(DefaultValue = 0)]
public int StopLoss { get; set; }
[Parameter(DefaultValue = 0)]
public int TakeProfit { get; set; }
[Parameter(DefaultValue = 0)]
public double SLTrigger { get; set; }
[Parameter(DefaultValue = 0)]
public double TrailingStop { get; set; }
private bool _isTrigerred;
protected bool UseTrailingStop
{
get { return SLTrigger > 0.0 && TrailingStop > 0.0; }
}
protected override void OnStart()
{
_firstGann = Indicators.GetIndicator<GannHighLow>(FirstGannPeriod);
_secondGann = Indicators.GetIndicator<GannHighLow>(SecondGannPeriod);
_thirdGann = Indicators.GetIndicator<GannHighLow>(ThirdGannPeriod);
}
protected override void OnTick()
{
if (Trade.IsExecuting || _position == null)
return;
if (UseTrailingStop)
Trail();
}
/// <summary>
/// If close price rises above the GannHighLow indicator a buy is triggered and
/// if the prices falls below the GannHighLow indicator a sell is triggered.
/// </summary>
protected override void OnBar()
{
bool isLongPositionOpen = _position != null && _position.TradeType == TradeType.Buy;
bool isShortPositionOpen = _position != null && _position.TradeType == TradeType.Sell;
if (_firstGann.Result.HasCrossedAbove(MarketSeries.Close, 1) && _secondGann.Result.HasCrossedBelow(_thirdGann.Result, 1) && !isShortPositionOpen)
{
ClosePosition();
Sell();
}
else if (_firstGann.Result.HasCrossedBelow(MarketSeries.Close, 1) && _secondGann.Result.HasCrossedAbove(_thirdGann.Result, 1) && !isLongPositionOpen)
{
ClosePosition();
Buy();
}
}
/// <summary>
/// Close the existing position
/// </summary>
private void ClosePosition()
{
if (_position == null)
return;
Trade.Close(_position);
_position = null;
}
/// <summary>
/// Send a Buy trade request
/// </summary>
private void Buy()
{
Request request = new MarketOrderRequest(TradeType.Buy, Volume)
{
Label = "Gann HiLo",
//SlippagePips = 1,
StopLossPips = StopLoss > 0 ? (int?)StopLoss : null,
TakeProfitPips = TakeProfit > 0 ? (int?)TakeProfit : null
};
Trade.Send(request);
}
/// <summary>
/// Send a Sell trade request
/// </summary>
private void Sell()
{
Request request = new MarketOrderRequest(TradeType.Sell, Volume)
{
Label = "Gann HiLo",
//SlippagePips = 1,
StopLossPips = StopLoss > 0 ? (int?)StopLoss : null,
TakeProfitPips = TakeProfit > 0 ? (int?)TakeProfit : null
};
Trade.Send(request);
}
protected override void OnPositionOpened(Position openedPosition)
{
_position = openedPosition;
}
protected override void OnStop()
{
// Put your deinitialization logic here
}
/// <summary>
/// Trailing Stop
/// </summary>
private void Trail()
{
if (_position.TradeType == TradeType.Buy)
{
double distance = Symbol.Bid - _position.EntryPrice;
if (distance >= SLTrigger * Symbol.PipSize)
{
if (!_isTrigerred)
{
_isTrigerred = true;
Print("Trailing Stop Loss triggered...");
}
double newStopLossPrice = Math.Round(Symbol.Bid - TrailingStop * Symbol.PipSize, Symbol.Digits);
if (_position.StopLoss == null || newStopLossPrice > _position.StopLoss)
{
Trade.ModifyPosition(_position, newStopLossPrice, _position.TakeProfit);
}
}
}
else
{
double distance = _position.EntryPrice - Symbol.Ask;
if (distance >= SLTrigger * Symbol.PipSize)
{
if (!_isTrigerred)
{
_isTrigerred = true;
Print("Trailing Stop Loss triggered...");
}
double newStopLossPrice = Math.Round(Symbol.Ask + TrailingStop * Symbol.PipSize, Symbol.Digits);
if (_position.StopLoss == null || newStopLossPrice < _position.StopLoss)
{
Trade.ModifyPosition(_position, newStopLossPrice, _position.TakeProfit);
}
}
}
}
}
}
I'm not sure that's what you are looking to add, but you can now change the entry/exit logic based on your needs.
@amusleh
amusleh
21 Apr 2022, 11:05
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.
@amusleh
amusleh
21 Apr 2022, 10:58
Hi,
There are several overloads available for ModifyPendingOrder method, if you don't want to change something then you can pass it's current value:
foreach (var order in PendingOrders)
{
if (order.Label == "SellD1TargetToBuyBackUpAirDist")
{
ModifyPendingOrder(order, order.VolumeInUnits, newStopLossInPips, order.TakeProfitPips);
// You can also use the PendingOrder.ModifyXXX methods
// order.ModifyStopLossPips(newStopLossInPips);
}
}
@amusleh
amusleh
21 Apr 2022, 10:45
Hi,
Try this:
using cAlgo.API;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AutoRescale = false, AccessRights = AccessRights.None)]
public class SampleSMA : Indicator
{
protected override void Initialize()
{
// Bullish bars
Chart.ColorSettings.BullFillColor = Color.Blue;
Chart.ColorSettings.BullOutlineColor = Color.Blue;
// Bearish bars
Chart.ColorSettings.BearFillColor = Color.Gold;
Chart.ColorSettings.BearOutlineColor = Color.Gold;
}
public override void Calculate(int index)
{
}
}
}
@amusleh
amusleh
21 Apr 2022, 10:42
Hi,
Try this:
using System;
using cAlgo.API;
using cAlgo.API.Internals;
using cAlgo.API.Indicators;
using cAlgo.Indicators;
namespace cAlgo
{
[Indicator(IsOverlay = true, TimeZone = TimeZones.UTC, AutoRescale = false, AccessRights = AccessRights.None)]
public class SampleSMA : Indicator
{
[Parameter("Source")]
public DataSeries Source { get; set; }
[Parameter(DefaultValue = 14)]
public int Periods { get; set; }
[Output("Main")]
public IndicatorDataSeries Result { get; set; }
private ToggleButton togglebutton;
private bool hideshow = true;
protected override void Initialize()
{
buttonDraw();
}
private void buttonDraw()
{
var stackPanel = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Bottom,
Margin = 20
};
togglebutton = new ToggleButton
{
Text = "Hide",
Width = 80
};
togglebutton.Click += togglebuttonclick;
stackPanel.AddChild(togglebutton);
Chart.AddControl(stackPanel);
}
private void togglebuttonclick(ToggleButtonEventArgs arg)
{
if (togglebutton.IsChecked)
{
hideshow = false;
togglebutton.Text = "Show";
for (int i = 0; i < Result.Count; i++)
{
Result[i] = double.NaN;
}
}
else
{
hideshow = true;
togglebutton.Text = "Hide";
for (int i = 0; i < Result.Count; i++)
{
Calculate(i);
}
}
}
public override void Calculate(int index)
{
if (hideshow)
{
double sum = 0.0;
for (int i = index - Periods + 1; i <= index; i++)
{
sum += Source[i];
}
Result[index] = sum / Periods;
}
}
}
}
@amusleh
amusleh
26 Apr 2022, 10:17
RE: RE:
instylereality2 said:
Hi,
I have no idea what you are trying to do, and why you need a loop inside OnStart method that will be stopped by OnTick event execution.
Use OnStart to populate or configure your bot and OnTick / OnBar / OnTimer / Events for trading.
Avoid blocking the cBot/Indicator thread for very long time as it will stop execution of all other methods and events.
What do you mean by: "the idea is the single thread in OnStart() to handle all the limit orders"
Your cBot thread is responsible for executing your cBot methods, event handlers, and updated it's properties.
@amusleh