Close onBar order execution too late
Close onBar order execution too late
13 Aug 2023, 00:40
Evening all,
I am new to CTrader, although I've been a DevOps engineer for many years, but this bug in my code is starting to bake my noodle just a bit :)
I have a threshold where if I see a crossover I place an order, but, and here's my problem, the code that I have always enters my trade 1 candle from where I think the code should be entering it (diagram below). I am testing the code (see below) in backtesting on the ‘tick data from server’ setting. My code is running on the 5-min timeframe.
Point 1 - candle below threshold
Point 2- candle crossing threshold and where I would like my code to execute the order at the close of the candle
Point 3. candle where the code currently executes the order (1 candle too late) at the close + spread
Point 4. order exited
My code is below.
If anyone can shed some light on this I would be most grateful. I am using the onBar method, and last(1) and last(2), but whatever I try the order does not execute at the close of the candle at position 2.
=========================================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
// https://ctrader.com/forum/cbot-support/13565
namespace cAlgo
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class Trailing_algo : Robot
{
[Parameter(DefaultValue = "Hello world!")]
public string Message { get; set; }
[Parameter("Take Profit", DefaultValue = 5)]
public int TakeProfit { get; set; }
[Parameter("Stop Loss", DefaultValue = 9)]
public int StopLoss { get; set; }
[Parameter("Quantity (Lots)", DefaultValue = 1, MinValue = 0.01, Step = 0.01)]
public double Quantity { get; set; }
[Parameter("LotSize", DefaultValue = 0.1)]
public double LotSize { get; set; }
[Parameter("Include Trailing Stop", DefaultValue = false)]
public bool IncludeTrailingStop { get; set; }
[Parameter("Trailing Stop Trigger (pips)", DefaultValue = 20)]
public int TrailingStopTrigger { get; set; }
[Parameter("Trailing Points (pips)", DefaultValue = 10)]
public int TrailingStopStep { get; set; }
[Parameter("Session High (price)", DefaultValue = 10)]
public double SessionHigh { get; set; }
[Parameter("Session Low (price)", DefaultValue = 10)]
public double SessionLow { get; set; }
private string managePositionTradeType;
/* This method performs an action of your choosing
when a cbot is lanunched.*/
protected override void OnStart()
{
// To learn more about cTrader Automate visit our Help Center:
// https://help.ctrader.com/ctrader-automate
// Put your initialisation code here
Print(Message);
//var line = Chart.DrawHorizontalLine("1", 7500, Color.Red);
//line.IsInteractive = true;
}
/* This method performs an actio of your choosing
on every tick. */
protected override void OnTick()
{
// Handle price updates here
// Put your core logic here
if (IncludeTrailingStop)
{
SetTrailingStop();
}
//ManagePositions();
}
/* This method performs an action of your choosing
when a cBot stops working.*/
protected override void OnStop()
{
// Handle cBot stop here
// Put your deinitialisation logic here
}
protected override void OnBar()
{
ManagePositions();
}
private void ManagePositions()
{
// This works!
double closePriceLastBar_1 = Bars.ClosePrices.Last(1); //1,1,2,2
double openPriceLastBar_1 = Bars.OpenPrices.Last(1);
double closePriceLastBar_2 = Bars.ClosePrices.Last(2);
double openPriceLastBar_2 = Bars.OpenPrices.Last(2);
bool greenCandle_1 = openPriceLastBar_1 < closePriceLastBar_1;
bool redCandle_1 = openPriceLastBar_1 > closePriceLastBar_1;
bool greenCandle_2 = openPriceLastBar_2 < closePriceLastBar_2;
bool redCandle_2 = openPriceLastBar_2 > closePriceLastBar_2;
// To go long the previous bar close must be > high and the current bar close
// must be > high (crossover)
if (greenCandle_2 && (closePriceLastBar_2 < SessionHigh) && greenCandle_1 && (closePriceLastBar_1 > SessionHigh))
{
if (!IsPositionOpen())
{
//Enter long position only if there are no positions already open
//ManagePositions2("long");
OpenPosition(TradeType.Buy);
}
}
// To go short the previous bar close must be > low and the current bar close
// must be < low (crossunder)
if (redCandle_2 && (closePriceLastBar_2 > SessionHigh) && redCandle_1 && (closePriceLastBar_1 < SessionHigh))
{
if (!IsPositionOpen())
{
//Enter short position only if there are no positions already open
//ManagePositions2("short");
OpenPosition(TradeType.Sell);
}
}
}
// Check if any positions are already open (only one position at a time is allowed)
private bool IsPositionOpen()
{
var p = Positions.FindAll(InstanceId, SymbolName);
if (p.Count() >= 1)
{
Print("The number of open positions is {0}", p);
return true;
}
return false;
}
// Position management (both long and short)
//private void ManagePositions2(string positionType)
//{
// // if there are no open positions then open long
// if (positionType == "short"){
// Print("!IsPositionOpen for short was called");
// OpenPosition(TradeType.Sell);
// }
// // if there are not open positions then open short
// if (positionType == "long"){
// Print("!IsPositionOpen for long was called");
// OpenPosition(TradeType.Buy);
// }
//}
// Opens a new position
private void OpenPosition(TradeType type)
{
// Calculate volume from lot size
double vol = Symbol.QuantityToVolumeInUnits(LotSize);
// Open a new position
var result = ExecuteMarketOrder(type, SymbolName, vol, InstanceId, StopLoss, TakeProfit); // might need to tweak stoploss and take profit units
if (result.IsSuccessful)
{
var position = result.Position;
Print("Position entry price is {0}", position.EntryPrice);
Print("Position SL price is {0}", position.StopLoss);
Print("Instance ID is {0}", InstanceId);
Print("Type is {0}", type);
Print("Symbol name {0}", SymbolName);
}
}
// Sets the trailing stop
private void SetTrailingStop()
{
// Deal with sell positions
var sellPositions = Positions.FindAll(InstanceId, SymbolName, TradeType.Sell);
foreach (Position position in sellPositions)
{
double distance = position.EntryPrice - Symbol.Ask;
if (distance < TrailingStopTrigger * Symbol.PipSize)
continue;
double newStopLossPrice = Symbol.Ask + TrailingStopStep * Symbol.PipSize;
if(position.StopLoss == null || newStopLossPrice < position.StopLoss)
{
ModifyPosition(position, newStopLossPrice, position.TakeProfit);
}
}
// Deal with buy positions
var buyPositions = Positions.FindAll(InstanceId, SymbolName, TradeType.Buy);
foreach (Position position in buyPositions)
{
double distance = Symbol.Bid - position.EntryPrice;
if (distance < TrailingStopTrigger * Symbol.PipSize)
continue;
double newStopLossPrice = Symbol.Bid + TrailingStopStep * Symbol.PipSize;
if(position.StopLoss == null || newStopLossPrice > position.StopLoss)
{
ModifyPosition(position, newStopLossPrice, position.TakeProfit);
}
}
}
}
}
===============================================
Replies
michaelgriffiths00
13 Aug 2023, 11:05
RE: Close onBar order execution too late
firemyst said:
Your requirements are tricky because unless you're keeping track of time yourself and have a timer-method to check the time when a bar should be closing, there's no way to tell if a bar has closed or not until the next tick comes in.
For instance, a tick could come in on the current M1 candle at 12:59:55pm; the next tick might not come in until 1:00:05pm - difference of ten seconds. And regardless of time, that next tick could jump up down by several pips from the last value depending on what's happening in the market.
If you had a separate event method being called on say, every second, then your bot should execute the method at 1:00:00pm, in which can you can make the judgment call in your code to open a position.
Another alternative is once your threshold is crossed, you can wait to open a position once price is a pip, 2 pips, or x-pips above the threshold before waiting for the candle to close and next candle to open.
Many thanks for the reply firemyst. I have tried the following
protected override void OnTick()
{
// Handle price updates here
// Put your core logic here
if (IncludeTrailingStop)
{
SetTrailingStop();
}
DateTime currentTickTime = Server.Time;
DateTime lastCandleCloseTime = Bars.OpenTimes.Last(1).AddMinutes(5);
TimeSpan timeDifference = currentTickTime - lastCandleCloseTime;
double millisecondsDifference = timeDifference.TotalMilliseconds;
double tolerance = 50; // Define a tolerance in milliseconds
if (Math.Abs(millisecondsDifference - 300000) <= tolerance)
{
// This tick is likely the first tick after the close of the previous candle
// You can use this information for your logic
ManagePositions();
}
Print("The number of ms's is {0}", millisecondsDifference);
}
However, as the number of milliseconds for each candle is not always exactly 300,000, it's very difficult to gauge what the close price of the candle will be
@michaelgriffiths00
firemyst
13 Aug 2023, 11:32
RE: RE: Close onBar order execution too late
michaelgriffiths00 said:
Many thanks for the reply firemyst. I have tried the following
protected override void OnTick()
{
// Handle price updates here
// Put your core logic here
if (IncludeTrailingStop)
{
SetTrailingStop();
}
DateTime currentTickTime = Server.Time;
DateTime lastCandleCloseTime = Bars.OpenTimes.Last(1).AddMinutes(5);
TimeSpan timeDifference = currentTickTime - lastCandleCloseTime;
double millisecondsDifference = timeDifference.TotalMilliseconds;
double tolerance = 50; // Define a tolerance in milliseconds
if (Math.Abs(millisecondsDifference - 300000) <= tolerance)
{
// This tick is likely the first tick after the close of the previous candle
// You can use this information for your logic
ManagePositions();
}
Print("The number of ms's is {0}", millisecondsDifference);
}However, as the number of milliseconds for each candle is not always exactly 300,000, it's very difficult to gauge what the close price of the candle will be
Not what I meant. :-)
The way I'm suggesting, you don't use your onTick or OnBar methods.
You create your own SEPARATE event handler method that's called every 5 minutes once you start the timer ticking.
It's in that new event method you will perform your logic, as you know it will reliably happen close to every 5 minutes.
Example:
using System;
using System.Timers;
//First, set up your timer
Timer t = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds); // Set the time (5 mins in this case)
t.AutoReset = true;
t.Elapsed += new System.Timers.ElapsedEventHandler(your_method);
//You'll have to figure out the logic to start it exactly when you want
t.Start();
// This method is called every 5 mins
private static void your_method(object sender, ElapsedEventArgs e)
{
ManagePositions();
}
That's using the native C# timers, but hopefully you get my point.
cAlgo has it's own timer example, but make sure to read the notes they put in at the top of the code :
https://help.ctrader.com/ctrader-automate/references/Timer/Timer/#namespace
@firemyst
PanagiotisChar
14 Aug 2023, 05:18
Hi there,
What you see is correct. OnBar is executed on the opening of the next bar, so on bar 3 the conditions are valid and the trade is entered.
Need help? Join us on Telegram
@PanagiotisChar
michaelgriffiths00
14 Aug 2023, 05:30
( Updated at: 14 Aug 2023, 05:31 )
RE: Close onBar order execution too late
PanagiotisChar said:
Hi there,
What you see is correct. OnBar is executed on the opening of the next bar, so on bar 3 the conditions are valid and the trade is entered.
Need help? Join us on Telegram
Hi,Yeah, the current code is acting as it should according to the logic of the API, but if I wanted to enter the order at the close of the candle, that registers simultaneuously at the open of the current, my code as it is fails. Just wondering if there is a better way of getting the order to execute on the candle (candle 2) prior to the current execution candle rather than to rely on timing
@michaelgriffiths00
michaelgriffiths00
14 Aug 2023, 06:50
RE: RE: RE: Close onBar order execution too late
firemyst said:
michaelgriffiths00 said:
Many thanks for the reply firemyst. I have tried the following
protected override void OnTick()
{
// Handle price updates here
// Put your core logic here
if (IncludeTrailingStop)
{
SetTrailingStop();
}
DateTime currentTickTime = Server.Time;
DateTime lastCandleCloseTime = Bars.OpenTimes.Last(1).AddMinutes(5);
TimeSpan timeDifference = currentTickTime - lastCandleCloseTime;
double millisecondsDifference = timeDifference.TotalMilliseconds;
double tolerance = 50; // Define a tolerance in milliseconds
if (Math.Abs(millisecondsDifference - 300000) <= tolerance)
{
// This tick is likely the first tick after the close of the previous candle
// You can use this information for your logic
ManagePositions();
}
Print("The number of ms's is {0}", millisecondsDifference);
}However, as the number of milliseconds for each candle is not always exactly 300,000, it's very difficult to gauge what the close price of the candle will be
Not what I meant. :-)
The way I'm suggesting, you don't use your onTick or OnBar methods.
You create your own SEPARATE event handler method that's called every 5 minutes once you start the timer ticking.
It's in that new event method you will perform your logic, as you know it will reliably happen close to every 5 minutes.
Example:
using System; using System.Timers; //First, set up your timer Timer t = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds); // Set the time (5 mins in this case) t.AutoReset = true; t.Elapsed += new System.Timers.ElapsedEventHandler(your_method); //You'll have to figure out the logic to start it exactly when you want t.Start();
// This method is called every 5 mins private static void your_method(object sender, ElapsedEventArgs e) { ManagePositions(); }
That's using the native C# timers, but hopefully you get my point.
cAlgo has it's own timer example, but make sure to read the notes they put in at the top of the code :
https://help.ctrader.com/ctrader-automate/references/Timer/Timer/#namespace
Thanks firemyst, I'll give this a whirl today
@michaelgriffiths00
thanhbachle2766
16 Aug 2023, 04:44
RE: RE: RE: RE: Close onBar order execution too late sda
michaelgriffiths00 said:
firemyst said:
michaelgriffiths00 said:
Many thanks for the reply firemyst. I have tried the following
protected override void OnTick()
{
// Handle price updates here
// Put your core logic here
if (IncludeTrailingStop)
{
SetTrailingStop();
}
DateTime currentTickTime = Server.Time;
DateTime lastCandleCloseTime = Bars.OpenTimes.Last(1).AddMinutes(5);
TimeSpan timeDifference = currentTickTime - lastCandleCloseTime;
double millisecondsDifference = timeDifference.TotalMilliseconds;
double tolerance = 50; // Define a tolerance in milliseconds
if (Math.Abs(millisecondsDifference - 300000) <= tolerance)
{
// This tick is likely the first tick after the close of the previous candle
// You can use this information for your logic
ManagePositions();
}
Print("The number of ms's is {0}", millisecondsDifference);
}However, as the number of milliseconds for each candle is not always exactly 300,000, it's very difficult to gauge what the close price of the candle will be
Not what I meant. :-)
The way I'm suggesting, you don't use your onTick or OnBar methods.
You create your own SEPARATE event handler method that's called every 5 minutes once you start the timer ticking.
It's in that new event method you will perform your logic, as you know it will reliably happen close to every 5 minutes.
Example:
using System; using System.Timers; //First, set up your timer Timer t = new Timer(TimeSpan.FromMinutes(5).TotalMilliseconds); // Set the time (5 mins in this case) t.AutoReset = true; t.Elapsed += new System.Timers.ElapsedEventHandler(your_method); //You'll have to figure out the logic to start it exactly when you want t.Start();
// This method is called every 5 mins private static void your_method(object sender, ElapsedEventArgs e) { ManagePositions(); }
That's using the native C# timers, but hopefully you get my point.
cAlgo has it's own timer example, but make sure to read the notes they put in at the top of the code : MyBKExperience
https://help.ctrader.com/ctrader-automate/references/Timer/Timer/#namespace
Thanks firemyst, I'll give this a whirl today
This information is really helpful for who really needs this. I hope you will many more write post like this..
@thanhbachle2766
... Deleted by UFO ...
firemyst
13 Aug 2023, 05:10
Your requirements are tricky because unless you're keeping track of time yourself and have a timer-method to check the time when a bar should be closing, there's no way to tell if a bar has closed or not until the next tick comes in.
For instance, a tick could come in on the current M1 candle at 12:59:55pm; the next tick might not come in until 1:00:05pm - difference of ten seconds. And regardless of time, that next tick could jump up down by several pips from the last value depending on what's happening in the market.
If you had a separate event method being called on say, every second, then your bot should execute the method at 1:00:00pm, in which can you can make the judgment call in your code to open a position.
Another alternative is once your threshold is crossed, you can wait to open a position once price is a pip, 2 pips, or x-pips above the threshold before waiting for the candle to close and next candle to open.
@firemyst