Topics
Replies

firemyst
19 Jun 2019, 11:07

RE:

Panagiotis Charalampous said:

Hi FireMyst,

The problem is with the while loops. They block the execution of the thread. If you want to execute operations after a successful position opening I suggess to use the asynchronous callback event raised by this function.

Best Regards,

Panagiotis

Also, even more confusing, in the first example, the while loop runs until both async callbacks return a non "InExecuting" value. See this line:

 while (toLong.IsExecuting && toShort.IsExecuting)

While running in debug mode, both async callbacks complete, because the positions are opened with the stoplosses on the chart, so at least one of those values should be set to false, and thus while loop should exit.

Am I wrong?


@firemyst

firemyst
19 Jun 2019, 10:53

RE:

Panagiotis Charalampous said:

Hi FireMyst,

The problem is with the while loops. They block the execution of the thread. If you want to execute operations after a successful position opening I suggess to use the asynchronous callback event raised by this function.

Best Regards,

Panagiotis

HI @Panagiotis

I tried that in example #2. As per what I stated there, the callback Action even never gets called, so I'm confused.

Those callback events should set the boolean variables, which allow the While loop to exit. Otherwise, what's the suggested approach to ensure no other code gets executed until both callback events have completed?

Thank you.


@firemyst

firemyst
19 Jun 2019, 03:06

Version 2

Here's version 2 of the code. Run with same environment as stated above and same results. Orders placed, I can adjust stop losses on chart. However, the Print statement in OnTick:

Print("-------------------- Starting OnTick --------------------");

is never printed nor hit in the debugger.

A breakpoint I have on line 53:

if (r.IsSuccessful)

is never hit. As a result, the While loop never exits because the bool variables are never set to true, which they should be since the position is opened and should provide a TradeResult.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using Microsoft.Win32;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class Entrapment : Robot
    {
        //Specific to this robot
        [Parameter("Default Stop Loss (pips)", DefaultValue = 5, MinValue = 1)]
        public int DefaultStopLossPips { get; set; }
        [Parameter("Max Position Size (pips forex; units indicies)", DefaultValue = 1, MinValue = 0, Step = 1)]
        public double MaxPositionSize { get; set; }
        [Parameter("Use Trailing Stop Loss", DefaultValue = true)]
        public bool UseTrailingStopLoss { get; set; }
        [Parameter("Debug Log Mode", DefaultValue = true)]
        public bool DebugLogMode { get; set; }

        private string _positionLabelLong = string.Empty;
        private string _positionLabelShort = string.Empty;
        private double _stopLossLongPosition = 0;
        private double _stopLossShortPosition = 0;

        private double _entryPriceLong = 0;
        private double _entryPriceShort = 0;
        private double _stopLossLongPrice = 0;
        private double _stopLossShortPrice = 0;
        private bool _isLongEntrySuccessful = false;
        private bool _isShortEntrySuccessful = false;

        //for measuring the speed of methods
        private Stopwatch _sw = new Stopwatch();

        protected override void OnStart()
        {
            //Start the robot.
            _positionLabelLong = SimplyfyTimeFrame(MarketSeries.TimeFrame) + " Long " + Symbol.Code + " DLEntrapment Bot";
            _positionLabelShort = SimplyfyTimeFrame(MarketSeries.TimeFrame) + " Short " + Symbol.Code + " DLEntrapment Bot";
            Print("OnStart: Starting DLEntrapment Bot.");
                        
            //Place the long order
            ExecuteMarketOrderAsync(TradeType.Buy, Symbol, MaxPositionSize, _positionLabelLong, DefaultStopLossPips, null,
            (TradeResult r) =>
            {
                if (r.IsSuccessful)
                {
                    Position p = Positions.Find(_positionLabelLong, Symbol);
                    _entryPriceLong = p.EntryPrice;
                    _stopLossLongPrice = p.StopLoss.GetValueOrDefault();
                    //Set last so the while blocking loop won't complete until all variables are assigned
                    _isLongEntrySuccessful = true;
                    Print("Long order successful. Entry Price {0}, SL {1}", _entryPriceLong, _stopLossLongPrice);
                }
                else
                {
                    Print("Error! Long entry NOT successful!");
                };
            });

            //Place the short order
            ExecuteMarketOrderAsync(TradeType.Sell, Symbol, MaxPositionSize, _positionLabelShort, DefaultStopLossPips, null,
            (TradeResult r) =>
            {
                if (r.IsSuccessful)
                {
                    Position p = Positions.Find(_positionLabelShort, Symbol);
                    _entryPriceShort = p.EntryPrice;
                    _stopLossShortPrice = p.StopLoss.GetValueOrDefault();
                    //Set last so the while blocking loop won't complete until all variables are assigned
                    _isShortEntrySuccessful = true;
                    Print("Short order successful. Entry Price {0}, SL {1}", _entryPriceShort, _stopLossShortPrice);
                }
                else
                {
                    Print("Error! Short entry NOT successful!");
                };
            });

            //Wait for everything so we don't get to the OnTick method
            int whileLoopIndex = 0;
            while (!_isLongEntrySuccessful && !_isShortEntrySuccessful)
            {
                if (DebugLogMode && whileLoopIndex % 10 == 0)
                    Print("While loop still waiting. _isLongEntrySuccessful {0}, _isShortEntrySuccessful {1}", _isLongEntrySuccessful, _isShortEntrySuccessful);
                Thread.Sleep(100);
                whileLoopIndex += 1;
            }

        }

        protected override void OnTick()
        {
            _sw.Reset();
            Print("-------------------- Starting OnTick --------------------");
            _sw.Start();

            if (_isLongEntrySuccessful && _isShortEntrySuccessful)
            {
                double symbolBid = Symbol.Bid;
                double symbolAsk = Symbol.Ask;

                //Checking long position. If bid gets higher than the short position SL, then it's moving up
                //Move long SL to entry price plus a pip and turn into TSL in case it takes off
                if (symbolBid >= _stopLossShortPosition)
                {
                    Position p = Positions.Find(_positionLabelLong, Symbol);
                    double newSL = _entryPriceLong + Symbol.PipSize;
                    if (newSL >= symbolBid)
                        newSL = _entryPriceLong;
                    TradeResult r = ModifyPosition(p, newSL, null, UseTrailingStopLoss);
                    if (r.IsSuccessful)
                    {
                        Print("OT01: Successfully modified position \"{0} {1}\". Entry Price {2}, Old SL {3}, New SL {4}, Has TSL {5}.", p.Id, p.Label, _entryPriceLong, _stopLossLongPrice, newSL, UseTrailingStopLoss);
                        Stop();
                        return;
                    }
                    else
                    {
                        Print("OT01: WARNING! Could not modify position \"{0} {1}\"! SymbolBid {2}, SymbolAsk {3}, Entry Price {4}, Old SL {5}, New SL {6}, Has TSL {7}.", p.Id, p.Label, symbolBid, symbolAsk, _entryPriceLong, _stopLossLongPrice, newSL, UseTrailingStopLoss);
                    }
                }
                //Checking short position. If bid gets lower than the long position SL, then it's moving down
                //Move short SL to entry price plus a pip and turn into TSL in case it takes off
                else if (symbolAsk <= _stopLossLongPosition)
                {
                    Position p = Positions.Find(_positionLabelShort, Symbol);
                    double newSL = _entryPriceShort - Symbol.PipSize;
                    if (newSL <= symbolAsk)
                        newSL = _entryPriceShort;
                    TradeResult r = ModifyPosition(p, newSL, null, UseTrailingStopLoss);
                    if (r.IsSuccessful)
                    {
                        Print("OT02: Successfully modified position \"{0} {1}\". Entry Price {2}, Old SL {3}, New SL {4}, Has TSL {5}.", p.Id, p.Label, _entryPriceShort, _stopLossShortPrice, newSL, UseTrailingStopLoss);
                        Stop();
                        return;
                    }
                    else
                    {
                        Print("OT02: WARNING! Could not modify position \"{0} {1}\"! SymbolBid {2}, SymbolAsk {3}, Entry Price {4}, Old SL {5}, New SL {6}, Has TSL {7}.", p.Id, p.Label, symbolBid, symbolAsk, _entryPriceLong, _stopLossLongPrice, newSL, UseTrailingStopLoss);
                    }
                }
                else
                {
                    if (DebugLogMode)
                        Print("OT03: No conditions met to move SL for either long or short position.");
                }
            }
            else
            {
                if (DebugLogMode)
                    Print("OT04: Waiting for Long and Short orders to be successful. _isLongEntrySuccessful {0}, _isShortEntrySuccessful {1}", _isLongEntrySuccessful, _isShortEntrySuccessful);
            }
            _sw.Stop();
            Print("-------------------- End OnTick {0} --------------------", _sw.Elapsed.ToString("ss\\.fffffff"));
        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }

        /// <summary>
        /// Created to replace the long winded time frames with simplified versions more common on charts.
        /// Ex: "Hour" -> "H1"; "Minute5" ->"M5", etc
        /// </summary>
        /// <param name="tf">The TimeFrame period</param>
        /// <returns>Simplified string. "Hour" -> "H1"; "Minute5" -> "M5", etc</returns>
        private string SimplyfyTimeFrame(TimeFrame tf)
        {
            if (tf == TimeFrame.Hour)
                return "H1";
            if (tf == TimeFrame.Minute)
                return "M1";
            if (tf == TimeFrame.Daily)
                return "D1";
            if (tf == TimeFrame.Weekly)
                return "W1";
            if (tf.ToString().Contains("Hour"))
                return tf.ToString().Replace("Hour", "H");
            if (tf.ToString().Contains("Minute"))
                return tf.ToString().Replace("Minute", "M");
            if (tf.ToString().Contains("Day"))
                return tf.ToString().Replace("Day", "D");

            return tf.ToString();
        }

    }
}

 


@firemyst

firemyst
19 Jun 2019, 02:55

Version 1

Here is Version 1 of the code. Run against 5 minute chart of EURUSD on Pepperstone using cTrader V3.3.

When running this code in debug mode, I have a break point on line 65:

TradeResult r = toLong.TradeResult;

which is never hit.

Also, line 99 in OnTick:

Print("-------------------- Starting OnTick --------------------");

never runs.

However, the orders are placed and I can adjust the stop losses on the cTrader chart.

 

The bot permissions has "full access" because I removed some code that reads/writes info to the local drive.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using cAlgo.API;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;
using cAlgo.Indicators;
using Microsoft.Win32;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class Entrapment : Robot
    {
        //Specific to this robot
        [Parameter("Default Stop Loss (pips)", DefaultValue = 5, MinValue = 1)]
        public int DefaultStopLossPips { get; set; }
        [Parameter("Max Position Size (pips forex; units indicies)", DefaultValue = 1, MinValue = 0, Step = 1)]
        public double MaxPositionSize { get; set; }
        [Parameter("Use Trailing Stop Loss", DefaultValue = true)]
        public bool UseTrailingStopLoss { get; set; }
        [Parameter("Debug Log Mode", DefaultValue = false)]
        public bool DebugLogMode { get; set; }

        private string _positionLabelLong = string.Empty;
        private string _positionLabelShort = string.Empty;
        private double _stopLossLongPosition = 0;
        private double _stopLossShortPosition = 0;

        private double _entryPriceLong = 0;
        private double _entryPriceShort = 0;
        private double _stopLossLongPrice = 0;
        private double _stopLossShortPrice = 0;
        private bool _isLongEntrySuccessful = false;
        private bool _isShortEntrySuccessful = false;

        //for measuring the speed of methods
        private Stopwatch _sw = new Stopwatch();

        protected override void OnStart()
        {
            //Start the robot.
            _positionLabelLong = SimplyfyTimeFrame(MarketSeries.TimeFrame) + " Long " + Symbol.Code + " DLEntrapment Bot";
            _positionLabelShort = SimplyfyTimeFrame(MarketSeries.TimeFrame) + " Short " + Symbol.Code + " DLEntrapment Bot";
            Print("OnStart: Starting DLEntrapment Bot.");

            //Place the long order
            TradeOperation toLong = ExecuteMarketOrderAsync(TradeType.Buy, Symbol, MaxPositionSize, _positionLabelLong, DefaultStopLossPips, null);
            
            //Place the short order
            TradeOperation toShort = ExecuteMarketOrderAsync(TradeType.Sell, Symbol, MaxPositionSize, _positionLabelShort, DefaultStopLossPips, null);
            
            //Wait for everything so we don't get to the OnTick method
            int whileLoopIndex = 0;
            while (toLong.IsExecuting && toShort.IsExecuting)
            {
                if (DebugLogMode && whileLoopIndex % 10 == 0)
                    Print("While loop still waiting. ");
                Thread.Sleep(100);
                whileLoopIndex += 1;
            }

            TradeResult r = toLong.TradeResult;
            Position p;
            if (r.IsSuccessful)
            {
                p = Positions.Find(_positionLabelLong, Symbol);
                _entryPriceLong = p.EntryPrice;
                _stopLossLongPrice = p.StopLoss.GetValueOrDefault();
                _isLongEntrySuccessful = true;
                Print("Long order successful. Entry Price {0}, SL {1}", _entryPriceLong, _stopLossLongPrice);
            }
            else
            {
                Print("Error! Long entry NOT successful!");
            };

            r = toShort.TradeResult;
            if (r.IsSuccessful)
            {
                p = Positions.Find(_positionLabelShort, Symbol);
                _entryPriceShort = p.EntryPrice;
                _stopLossShortPrice = p.StopLoss.GetValueOrDefault();
                _isShortEntrySuccessful = true;
                Print("Short order successful. Entry Price {0}, SL {1}", _entryPriceShort, _stopLossShortPrice);
            }
            else
            {
                Print("Error! Short entry NOT successful!");
            };

        }

        protected override void OnTick()
        {
            _sw.Reset();
            Print("-------------------- Starting OnTick --------------------");
            _sw.Start();

            if (_isLongEntrySuccessful && _isShortEntrySuccessful)
            {
                double symbolBid = Symbol.Bid;
                double symbolAsk = Symbol.Ask;

                //Checking long position. If bid gets higher than the short position SL, then it's moving up
                //Move long SL to entry price plus a pip and turn into TSL in case it takes off
                if (symbolBid >= _stopLossShortPosition)
                {
                    Position p = Positions.Find(_positionLabelLong, Symbol);
                    double newSL = _entryPriceLong + Symbol.PipSize;
                    if (newSL >= symbolBid)
                        newSL = _entryPriceLong;
                    TradeResult r = ModifyPosition(p, newSL, null, UseTrailingStopLoss);
                    if (r.IsSuccessful)
                    {
                        Print("OT01: Successfully modified position \"{0} {1}\". Entry Price {2}, Old SL {3}, New SL {4}, Has TSL {5}.", p.Id, p.Label, _entryPriceLong, _stopLossLongPrice, newSL, UseTrailingStopLoss);
                        Stop();
                        return;
                    }
                    else
                    {
                        Print("OT01: WARNING! Could not modify position \"{0} {1}\"! SymbolBid {2}, SymbolAsk {3}, Entry Price {4}, Old SL {5}, New SL {6}, Has TSL {7}.", p.Id, p.Label, symbolBid, symbolAsk, _entryPriceLong, _stopLossLongPrice, newSL, UseTrailingStopLoss);
                    }
                }
                //Checking short position. If bid gets lower than the long position SL, then it's moving down
                //Move short SL to entry price plus a pip and turn into TSL in case it takes off
                else if (symbolAsk <= _stopLossLongPosition)
                {
                    Position p = Positions.Find(_positionLabelShort, Symbol);
                    double newSL = _entryPriceShort - Symbol.PipSize;
                    if (newSL <= symbolAsk)
                        newSL = _entryPriceShort;
                    TradeResult r = ModifyPosition(p, newSL, null, UseTrailingStopLoss);
                    if (r.IsSuccessful)
                    {
                        Print("OT02: Successfully modified position \"{0} {1}\". Entry Price {2}, Old SL {3}, New SL {4}, Has TSL {5}.", p.Id, p.Label, _entryPriceShort, _stopLossShortPrice, newSL, UseTrailingStopLoss);
                        Stop();
                        return;
                    }
                    else
                    {
                        Print("OT02: WARNING! Could not modify position \"{0} {1}\"! SymbolBid {2}, SymbolAsk {3}, Entry Price {4}, Old SL {5}, New SL {6}, Has TSL {7}.", p.Id, p.Label, symbolBid, symbolAsk, _entryPriceLong, _stopLossLongPrice, newSL, UseTrailingStopLoss);
                    }
                }
                else
                {
                    if (DebugLogMode)
                        Print("OT03: No conditions met to move SL for either long or short position.");
                }
            }
            else
            {
                if (DebugLogMode)
                    Print("OT04: Waiting for Long and Short orders to be successful. _isLongEntrySuccessful {0}, _isShortEntrySuccessful {1}", _isLongEntrySuccessful, _isShortEntrySuccessful);
            }
            _sw.Stop();
            Print("-------------------- End OnTick {0} --------------------", _sw.Elapsed.ToString("ss\\.fffffff"));
        }

        protected override void OnStop()
        {
            // Put your deinitialization logic here
        }

        /// <summary>
        /// Created to replace the long winded time frames with simplified versions more common on charts.
        /// Ex: "Hour" -> "H1"; "Minute5" ->"M5", etc
        /// </summary>
        /// <param name="tf">The TimeFrame period</param>
        /// <returns>Simplified string. "Hour" -> "H1"; "Minute5" -> "M5", etc</returns>
        private string SimplyfyTimeFrame(TimeFrame tf)
        {
            if (tf == TimeFrame.Hour)
                return "H1";
            if (tf == TimeFrame.Minute)
                return "M1";
            if (tf == TimeFrame.Daily)
                return "D1";
            if (tf == TimeFrame.Weekly)
                return "W1";
            if (tf.ToString().Contains("Hour"))
                return tf.ToString().Replace("Hour", "H");
            if (tf.ToString().Contains("Minute"))
                return tf.ToString().Replace("Minute", "M");
            if (tf.ToString().Contains("Day"))
                return tf.ToString().Replace("Day", "D");

            return tf.ToString();
        }

       
    }
}

@firemyst

firemyst
19 Jun 2019, 02:48

RE:

Panagiotis Charalampous said:

Hi FireMyst,

Can you please share a complete cBot code that reproduces the issue?

Best Regards,

Panagiotis

Sorry. I should have known better and done so. :-)

I'll post two versions of the code below. Neither way works as expected.


@firemyst

firemyst
19 Jun 2019, 02:48

RE:

Panagiotis Charalampous said:

Hi FireMyst,

Can you please share a complete cBot code that reproduces the issue?

Best Regards,

Panagiotis

Sorry. I should have known better and done so. :-)

I'll post two versions of the code below. Neither way works as expected.


@firemyst

firemyst
17 Jun 2019, 08:19

RE:

lec0456 said:

Other than that I like the new interface, I saw a good idea to have a pushpin instead of a sproket for the Bot parameters so you can pin them open or not. 

Thank you - that was one of my suggestions. :-)

The other one related to bots was to add a "+" next to a paramter group so we can expand/collapse particular groups as needed.

 

Anyway, hopefully Spotware will keep us updated here with your issue.


@firemyst

firemyst
17 Jun 2019, 07:01

Writing and subscribing as I'd like to know as well because as it stands, there are several key differences between 3.3 & 3.5 that I find makes version 3.5 less user friendly, especially in the realm of configuring bots.

Even though I've provided feedback, I haven't seen any of my suggestions implemented yet with each beta update, nor sure if they will be.


@firemyst

firemyst
17 Jun 2019, 04:45

If you're with Pepperstone, they are partners with NYCServers and FXVM.

Both of those companies offer 20% -  25% discounts if you have a Pepperstone account.

Details here:

https://pepperstone.com/au/trading-platforms/tools/vps-hosting

 

I have been using NYCServers for two months now (I get their services for free), and have had zero restarts except on one weekend when I restarted my server after applying Microsoft updates.

 


@firemyst

firemyst
14 Jun 2019, 10:13 ( Updated at: 21 Dec 2023, 09:21 )

RE:

Panagiotis Charalampous said:

Hi FireMyst,

Here is your problem

Best Regards,

Panagiotis

 

Thank you! Debugging in VS i didn't see those -- only what's written in the log. I appreciate your time.:-)

 


@firemyst

firemyst
14 Jun 2019, 09:41

RE: RE:

Panagiotis Charalampous said:

Hi FireMyst,

To investigate the reasons for the specific position you need to go through Pepperstone. They are in charge of their execution. Regarding the code sample, I will need the complete cBot and steps to reproduce. Since I do not see any obvious reason for a technical error, I might need to forward to the QA team for further investigation. Btw a more proper way to round prices is the following

Math.Round(price, Symbol.Digits)

Best Regards,

Panagiotis

Here's sample code which reproduces the issue every time for me on Pepperstone. Use on EURAUD 15-minute chart:

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.None)]
    public class TestBot : Robot
    {
        private string _positionLabel = String.Empty;
        private MarketSeries _marketSeries;
        private int _counter = 0;
        double _prevStopLossLocation = 0;

        protected override void OnStart()
        {
            _marketSeries = MarketData.GetSeries(Symbol, MarketSeries.TimeFrame);
            DataSeries series = _marketSeries.Close;
            _positionLabel = (MarketSeries.TimeFrame) + " " + Symbol.Code + " Test Bot";
            ExecuteMarketOrder(TradeType.Buy,Symbol,1000,_positionLabel,30,null);
            _prevStopLossLocation = 0;
        }

        protected override void OnTick()
        {
            Print("Tick! {0}", _counter);
            Position p = Positions.Find(_positionLabel, Symbol);

            double pipAdjustment = Symbol.PipSize * 10;
            double newSL = RoundPips(Symbol, p.EntryPrice - pipAdjustment);

            if (_prevStopLossLocation != 0)
                newSL += _prevStopLossLocation;

            Print("MP10: Position \"{0} {1}\" is in profit. Setting new SL to {2} from {3}.", p.Id, p.Label, newSL, _prevStopLossLocation);

            TradeResult r = p.ModifyStopLossPrice(newSL);
            if (r.IsSuccessful)
            {
                _prevStopLossLocation = newSL + 0.00004;
                Print("MP10: Successfully set stoploss to \"{0}\" for position \"{1} {2}\".", newSL, p.Id, p.Label);
            }
            else
            {
                Print("MP10: Problem! Could not set stoploss to \"{0}\" for position \"{1} {2}\".", newSL, p.Id, p.Label);
                Stop();
                return;
            }
        }

        private double RoundPips(Symbol s, double pips)
        {
                return ((int)(pips * Math.Pow(10, 4))) / Math.Pow(10, 4);
        }

    }
}

It typically bombs out the second or 3rd time through for me after I add the "0.00004" to the newSL value.


@firemyst

firemyst
14 Jun 2019, 09:41

RE: RE:

Panagiotis Charalampous said:

Hi FireMyst,

To investigate the reasons for the specific position you need to go through Pepperstone. They are in charge of their execution. Regarding the code sample, I will need the complete cBot and steps to reproduce. Since I do not see any obvious reason for a technical error, I might need to forward to the QA team for further investigation. Btw a more proper way to round prices is the following

Math.Round(price, Symbol.Digits)

Best Regards,

Panagiotis

Here's sample code which reproduces the issue every time for me on Pepperstone. Use on EURAUD 15-minute chart:

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.None)]
    public class TestBot : Robot
    {
        private string _positionLabel = String.Empty;
        private MarketSeries _marketSeries;
        private int _counter = 0;
        double _prevStopLossLocation = 0;

        protected override void OnStart()
        {
            _marketSeries = MarketData.GetSeries(Symbol, MarketSeries.TimeFrame);
            DataSeries series = _marketSeries.Close;
            _positionLabel = (MarketSeries.TimeFrame) + " " + Symbol.Code + " Test Bot";
            ExecuteMarketOrder(TradeType.Buy,Symbol,1000,_positionLabel,30,null);
            _prevStopLossLocation = 0;
        }

        protected override void OnTick()
        {
            Print("Tick! {0}", _counter);
            Position p = Positions.Find(_positionLabel, Symbol);

            double pipAdjustment = Symbol.PipSize * 10;
            double newSL = RoundPips(Symbol, p.EntryPrice - pipAdjustment);

            if (_prevStopLossLocation != 0)
                newSL += _prevStopLossLocation;

            Print("MP10: Position \"{0} {1}\" is in profit. Setting new SL to {2} from {3}.", p.Id, p.Label, newSL, _prevStopLossLocation);

            TradeResult r = p.ModifyStopLossPrice(newSL);
            if (r.IsSuccessful)
            {
                _prevStopLossLocation = newSL + 0.00004;
                Print("MP10: Successfully set stoploss to \"{0}\" for position \"{1} {2}\".", newSL, p.Id, p.Label);
            }
            else
            {
                Print("MP10: Problem! Could not set stoploss to \"{0}\" for position \"{1} {2}\".", newSL, p.Id, p.Label);
                Stop();
                return;
            }
        }

        private double RoundPips(Symbol s, double pips)
        {
                return ((int)(pips * Math.Pow(10, 4))) / Math.Pow(10, 4);
        }

    }
}

It typically bombs out the second or 3rd time through for me after I add the "0.00004" to the newSL value.


@firemyst

firemyst
14 Jun 2019, 06:21

RE:

Panagiotis Charalampous said:

Hi FireMyst,

If you can share the cBot code and cBot parameters we can investigate further.

Best Regards,

Panagiotis

Here is the bot code for that particular section:

{
                            pipAdjustment = Symbol.PipSize * 1;

                            if (p.TradeType == TradeType.Buy)
                                newSL = RoundPips(Symbol, p.EntryPrice + pipAdjustment);
                            else
                                newSL = RoundPips(Symbol, p.EntryPrice - pipAdjustment);

                            if (DebugLogMode)
                                Print("MP11: Position \"{0} {1}\" is in profit by at least {2} pips. Setting new SL to {3} from {4}.", p.Id, p.Label, _minPipProfitPastEntryPriceThreshold, newSL, _prevStopLossLocation);

                            TradeResult r = p.ModifyStopLossPrice(newSL);
                            if (r.IsSuccessful)
                            {
                                _prevStopLossLocation = newSL;
                                Print("MP11: Successfully set stoploss to \"{0}\" for position \"{1} {2}\".", newSL, p.Id, p.Label);
                            }
                            else
                            {
                                Print("MP11: Problem! Could not set stoploss to \"{0}\" for position \"{1} {2}\".", newSL, p.Id, p.Label);
                                Stop();
                                return;
                            }
                        }

Here is the function "RoundPips":

/// <summary>
        /// JPY currency in pips only goes to 2 decimal places. If the symbol contains JPY, round pips value to 2 places; otherwise 4.
        /// </summary>
        /// <param name="s">The Symbol</param>
        /// <param name="pips">The Pip value</param>
        /// <returns>The rounded pip value to either 2 or 4 places if symbol s contains JPY.</returns>
        private double RoundPips(Symbol s, double pips)
        {
            if (s.Code.ToUpper().Contains("JPY"))
            {
                return ((int)(pips * 100)) / 100.0;
            }
            else
            {
                return ((int)(pips * 10000)) / 10000.0;
            }
        }

The variable "p" is just the current position:

Position p = Positions.Find(_positionLabel, Symbol);

This is coded with cTrader v 3.3.


@firemyst

firemyst
14 Jun 2019, 04:15

RE:

Panagiotis Charalampous said:

Hi FireMyst,

No there is no such option at the moment.

Best Regards,

Panagiotis

Thank you for the clarification @Panagiotis.

It would be great if Spotware would seriously consider this.

After all, if cTrader puts traders first, certainly they must see it would be great for us to be able to put our own messages there in circumstances like this to help us distinguish what messages are coming from what positions. :-)

Thanks again! :-)


@firemyst

firemyst
14 Jun 2019, 04:12

HI @Panagiotis:

Before sharing code, since the PID was given in the log output from cTrader, is your team able to look it up in any "transaction logs" to see what, if anything, was logged on the server?

Or is that something I can pass along to Pepperstone, and then they would contact Spotware to find out more?

Thank you,

 


@firemyst

firemyst
05 Jun 2019, 12:14

RE:

Panagiotis Charalampous said:

Hi FireMyst,

We have investigated this issue. The reason this happens is that during Visual Backtesting chart and the indicators added to it receive a subset of all the ticks in backtesting. The number of lost ticks depends on backtesting speed and this a necessary compromise to achieve acceptable speed. However this does not come without side effects and this is one of them. For this specific indicator all ticks matter since the value for each bar is calculated for each index using the median price of the trend bar. If you miss one tick in a bar somewhere in the middle, indicator would have a different value than in case you calculate it on every tick. You should not experience this issue in silent backtesting or on during live execution.

Best Regards,

Panagiotis

HI @Panagiotis:

THank you for your investigation. I'm afraid I'm a bit confused and here's why.

I ran some tests today. It works perfectly with SpotWare's cTrader 3.5, and IC Markets version 3.3 of cTrader.

It doesn't work as expected with Pepperstone's version 3.3 of cTrader.

All run in visual backtesting mode.

So if only a subset of ticks are received and utilized, and that's the way cTrader operates, why does it work the same with all the other providers of cTrader except Pepperstone's? I would expect it to work the same with every single broker since the underlying logic of cTrader should be the same, and thus leads me to believe there's an issue with Pepperstone's version of cTrader?

??


@firemyst

firemyst
21 May 2019, 08:04

RE:
Panagiotis Charalampous said:

Hi guys, 

We plan to improve this in a future update and allow simultaneous sign in to different cTIDs. 

Best Regards,

Panagiotis


Will this be available in the 3.5 release? Or do we have to wait for at least 3.6?
@firemyst

firemyst
12 May 2019, 13:32 ( Updated at: 21 Dec 2023, 09:21 )

RE:

Spotware said:

 

Download cTrader Desktop 3.5 Beta

New Features in cTrader Automate 3.5

cTrader Automate, which is now a native feature of cTrader, also got a facelift, as well as added new features.

New Look for cBots and Indicators Lists

Same way as Watchlists in Trade application, the cBots and indicators lists have moved in the side menu under Automate application, where now you can also find a Backtesting and Optimization progress bar and input parameters grouping for cBots and indicators.

cTrader Desktop 3.5 will be released to brokers in the nearest future. Meanwhile, you can try the new features on Beta version.

Download cTrader Desktop 3.5 Beta

 

This is an annoying new feature that requires 1 simple change to make it more practical for developers/bot users.

In 3.5, every time a user wants to run a bot with different values for parameters, the amount of clicks they have to go through to do so does not increase "usability".

For example, I load up a bot, set one parameter and after running, want to change the value of another parameter. What do I have to do?

I have to:

  1. click the cog-wheel,
  2. scroll down the parameter list to the parameterI want to change,
  3. and then change the parameter value.

Users should NOT have to go through this excessive clicking exercise every time.

SpotWare needs to seriously consider implementing the "pin" functionality Microsoft (and other companies use). See the attached screen capture from Visual Studio with the circled 'pin':

If I want that window to stay open, I click the pin to pin it so it stays open. Otherwise, it automatically hides (as you have it) when I'm not using that frame.

It is significantly more user friendly, and will not only allow users to keep the parameters minimized when they want, but also allow those who need to keep them permanently visible for testing/running bots to do so as well.

Thank you.


@firemyst

firemyst
12 May 2019, 13:24 ( Updated at: 21 Dec 2023, 09:21 )

RE:

Spotware said:

 

Download cTrader Desktop 3.5 Beta

cTrader Desktop 3.5 will be released to brokers in the nearest future. Meanwhile, you can try the new features on Beta version.

Download cTrader Desktop 3.5 Beta

 

You should provide users with the ability to 'expand/collapse' parameter groups.

The functionality I suggest is similar to putting a plus "+" sign next to each group label that will allow users to expand/collapse the group.

For example, in the attached screen capture, what if I don't care about the parameters in group G1 and want them hidden? This might be because they're a set of parameters that I set once and never have to worry about over and over (like an email address, or yes/no flag for raising alerts, etc).

Thank you! :-)

 


@firemyst

firemyst
12 May 2019, 13:21 ( Updated at: 21 Dec 2023, 09:21 )

RE:

Spotware said:

cTrader Desktop 3.5 will be released to brokers in the nearest future. Meanwhile, you can try the new features on Beta version.

Download cTrader Desktop 3.5 Beta

 

 

A lot of developers/traders have multiple versions of indicators. In the attached example, how do I know from the menu which version is which?

Either the menu needs to be expanded, or might I suggest at the very least have your team implement a "tool tip" to display the full-name of the indicator when a user hovers over it.

Thank you.

 


@firemyst