Trailing Stop

Created at 03 Feb 2021, 00:17
How’s your experience with the cTrader Platform?
Your feedback is crucial to cTrader's development. Please take a few seconds to share your opinion and help us improve your trading experience. Thanks!
lattymor's avatar

lattymor

Joined 24.02.2020

Trailing Stop
03 Feb 2021, 00:17


Hi, Can someone help me with the working TRAILING STOP (with step) for the following cBot please.

 


        [Parameter("Place Test Buy Order", Group = "Backtest", DefaultValue = false)]
        public bool PlaceTestBuy { get; set; }

        [Parameter("Place Test Sell Order", Group = "Backtest", DefaultValue = false)]
        public bool PlaceTestSell { get; set; }

        [Parameter("Comment ID", DefaultValue = "Robot")]
        public string CommentID { get; set; }

        [Parameter("Stop-Loss in Pips", DefaultValue = 0)]
        public double SLPips { get; set; }

        [Parameter("Stop-Loss in Cash", DefaultValue = 0)]
        public double SLCash { get; set; }

        [Parameter("Take-Profit in Pips", DefaultValue = 0)]
        public double TPPips { get; set; }

        [Parameter("Take-Profit in Cash", DefaultValue = 0)]
        public double TPCash { get; set; }

        [Parameter("Move SL to Breakeven after Pips", DefaultValue = 0)]
        public double BreakevenAfterPips { get; set; }

        [Parameter("Trailing-Stop in Pips", DefaultValue = 0)]
        public double TSLPips { get; set; }

        [Parameter("Trailing-Stop in Cash", DefaultValue = 0)]
        public double TSLCash { get; set; }

        [Parameter("Update Stop-Loss on New Bar Only", DefaultValue = true)]
        public bool UpdateSLOnBar { get; set; }

        [Parameter("Manage Manual Orders", DefaultValue = false)]
        public bool HandleManualOrders { get; set; }

        [Parameter("Orders' Distance in Pips", DefaultValue = 0)]
        public double OrdersDistancePips { get; set; }

        [Parameter("Orders' Distance in Cash", DefaultValue = 0)]
        public double OrdersDistanceCash { get; set; }

        [Parameter("Trading hours", DefaultValue = "hh:mm-hh:mm")]
        public string TradingHoursString { get; set; }

        public DateTime[] TradingTimes;

        protected override void OnStart()
        {
            Positions.Opened += MarketOrderOpened;
            PendingOrders.Filled += PendingOrderFilled;

            char[] separator = 
            {
                '-'
            };

            // Check if a trading range was given as parameter.
            if (TradingHoursString != "" && TradingHoursString != "hh:mm-hh:mm")
            {
                // Save the trading range as DateTime array with 2 elements.
                string[] bounds = TradingHoursString.Split(separator);
                TradingTimes = new DateTime[] 
                {
                    DateTime.Parse(bounds[0]),
                    DateTime.Parse(bounds[1])
                };
            }

            // For debugging.
            //ExecuteMarketOrder(TradeType.Buy, SymbolName, 100000, null, 0, 0, CommentID);
            //ExecuteMarketOrder(TradeType.Sell, SymbolName, 100000, null, 0, 0, CommentID);
            if (PlaceTestBuy)
                ExecuteMarketOrder(TradeType.Buy, Symbol.Name, 1000, "", 10, null, CommentID);

            if (PlaceTestSell)
                ExecuteMarketOrder(TradeType.Sell, Symbol.Name, 1000, "", 10, null, CommentID);
        }

        protected override void OnTick()
        {
            if (!UpdateSLOnBar)
            {
                foreach (Position pos in Positions)
                {
                    if (HandleManualOrders || pos.Label == "Robot Placed")
                    {
                        if (pos.Comment == CommentID || CommentID == "")
                        {
                            if (pos.SymbolName == SymbolName)
                            {
                                // This order is controlled by this robot.
                                UpdateStopLoss(pos);
                            }
                        }
                    }
                }
            }
        }

        protected override void OnBar()
        {
            if (UpdateSLOnBar)
            {
                foreach (Position pos in Positions)
                {
                    if (HandleManualOrders || pos.Label == "Robot Placed")
                    {
                        if (pos.Comment == CommentID || CommentID == "")
                        {
                            if (pos.SymbolName == SymbolName)
                            {
                                // This order is controlled by this robot.
                                UpdateStopLoss(pos);
                            }
                        }
                    }
                }
            }
        }

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

        protected void MarketOrderOpened(PositionOpenedEventArgs args)
        {
            if (args.Position.Label == "Robot Placed")
            {
                if (args.Position.Comment == CommentID || CommentID == "")
                {
                    if (args.Position.SymbolName == Bars.SymbolName)
                    {
                        if (isTradingTime(Time))
                        {
                            PlaceStopOrderInDistance(args.Position);
                        }
                    }
                }

                if (HandleManualOrders)
                {

                    double stopLossPips;
                    double takeProfitPips;
                    double VolumeInUnits = args.Position.VolumeInUnits;

                    if (SLPips != 0)
                    {
                        stopLossPips = SLPips;
                    }

                    else if (SLCash != 0)
                    {
                        stopLossPips = Math.Round(SLCash / (Symbol.PipValue * VolumeInUnits), 1);
                    }

                    else
                    {
                        stopLossPips = 0;
                    }

                    if (TPPips != 0)
                    {
                        takeProfitPips = TPPips;
                    }

                    else if (TPCash != 0)
                    {
                        takeProfitPips = Math.Round(TPCash / (Symbol.PipValue * VolumeInUnits), 1);
                    }

                    else
                    {
                        takeProfitPips = 0;
                    }

                    double newstoploss = args.Position.TradeType == TradeType.Buy ? args.Position.EntryPrice - stopLossPips * Symbol.PipSize : args.Position.EntryPrice + stopLossPips * Symbol.PipSize;
                    double newtakeprofit = args.Position.TradeType == TradeType.Buy ? args.Position.EntryPrice + takeProfitPips * Symbol.PipSize : args.Position.EntryPrice - takeProfitPips * Symbol.PipSize;

                    ModifyPosition(args.Position, newstoploss, newtakeprofit);
                }
            }
        }

        protected void PendingOrderFilled(PendingOrderFilledEventArgs args)
        {
            if (args.PendingOrder.Comment == CommentID)
            {
                if (args.PendingOrder.SymbolName == Bars.SymbolName || CommentID == "")
                {
                    if (isTradingTime(Time))
                    {
                        PlaceStopOrderInDistance(args.Position);
                    }
                }
            }
        }

        protected void PlaceStopOrderInDistance(Position pos)
        {
            double targetPrice;
            double stopLossPips;
            double takeProfitPips;
            double VolumeInUnits = pos.VolumeInUnits;

            if (OrdersDistancePips != 0)
            {
                if (pos.TradeType == TradeType.Buy)
                {
                    targetPrice = pos.EntryPrice + OrdersDistancePips * Symbol.PipSize;
                }

                else
                {
                    targetPrice = pos.EntryPrice - OrdersDistancePips * Symbol.PipSize;
                }
            }

            else if (OrdersDistanceCash != 0)
            {
                if (pos.TradeType == TradeType.Buy)
                {
                    targetPrice = pos.EntryPrice + OrdersDistanceCash;
                }

                else
                {
                    targetPrice = pos.EntryPrice - OrdersDistanceCash;
                }
            }

            else
            {
                return;
            }

            if (SLPips != 0)
            {
                stopLossPips = SLPips;
            }

            else if (SLCash != 0)
            {
                stopLossPips = Math.Round(SLCash / (Symbol.PipValue * VolumeInUnits), 1);
            }

            else
            {
                stopLossPips = 0;
            }

            if (TPPips != 0)
            {
                takeProfitPips = TPPips;
            }

            else if (TPCash != 0)
            {
                takeProfitPips = Math.Round(TPCash / (Symbol.PipValue * VolumeInUnits), 1);
            }

            else
            {
                takeProfitPips = 0;
            }

            TradeResult result = PlaceStopOrder(pos.TradeType, pos.SymbolName, VolumeInUnits, targetPrice, "Robot Placed", stopLossPips, takeProfitPips, null, CommentID);


            if (result.IsSuccessful)
            {
                Print("Pending order placed.");
            }

            else
            {
                Print(string.Format("Pending order could not be placed. Error: {1}", pos.Id, result.Error));
            }
        }

        public void UpdateStopLoss(Position pos)
        {
            if (pos.TradeType == TradeType.Buy)
            {

                // Check if trailing stop can be updated.
                if (TSLPips != 0 && (Bars.ClosePrices.LastValue - pos.StopLoss) / Symbol.PipSize >= TSLPips)
                {
                    TradeResult result = ModifyPosition(pos, Bars.ClosePrices.LastValue - TSLPips * Symbol.PipSize, pos.TakeProfit);

                    if (result.IsSuccessful)
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss updated", pos.Id));
                    }

                    else
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss could not be updated. Error: {1}", pos.Id, result.Error));
                    }

                    return;
                }

                if (TSLCash != 0 && (Bars.ClosePrices.LastValue - pos.StopLoss) * pos.VolumeInUnits >= TSLCash)
                {
                    double newstoploss = Bars.ClosePrices.LastValue - TSLCash / pos.VolumeInUnits;
                    TradeResult result = ModifyPosition(pos, newstoploss, pos.TakeProfit);

                    if (result.IsSuccessful)
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss updated", pos.Id));
                    }

                    else
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss could not be updated. Error: {1}", pos.Id, result.Error));
                    }

                    return;
                }

                // Check if stoploss can be moved to breakeven.
                if (BreakevenAfterPips != 0 && pos.GrossProfit / Symbol.PipSize >= BreakevenAfterPips && pos.StopLoss < pos.EntryPrice)
                {
                    TradeResult result = ModifyPosition(pos, pos.EntryPrice, pos.TakeProfit);

                    if (result.IsSuccessful)
                    {
                        Print(string.Format("Position {0}: Stop-loss moved to break even", pos.Id));
                    }

                    else
                    {
                        Print(string.Format("Position {0}: Stop-loss could not be moved to break even. Error: {1}", pos.Id, result.Error));
                    }

                    return;
                }
            }

            else if (pos.TradeType == TradeType.Sell)
            {

                // Check if trailing stop can be updated.
                if (TSLPips != 0 && (pos.StopLoss - Bars.ClosePrices.LastValue) / Symbol.PipSize >= TSLPips)
                {
                    TradeResult result = ModifyPosition(pos, Bars.ClosePrices.LastValue + TSLPips * Symbol.PipSize, pos.TakeProfit);

                    if (result.IsSuccessful)
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss updated", pos.Id));
                    }

                    else
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss could not be updated. Error: {1}", pos.Id, result.Error));
                    }

                    return;
                }

                if (TSLCash != 0 && (pos.StopLoss - Bars.ClosePrices.LastValue) * pos.VolumeInUnits >= TSLCash)
                {
                    double newstoploss = Bars.ClosePrices.LastValue + TSLCash / pos.VolumeInUnits;
                    TradeResult result = ModifyPosition(pos, newstoploss, pos.TakeProfit);

                    if (result.IsSuccessful)
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss updated", pos.Id));
                    }

                    else
                    {
                        Print(string.Format("Position {0}: Trailing stop-loss could not be updated. Error: {1}", pos.Id, result.Error));
                    }

                    return;
                }

                // Check if stoploss can be moved to breakeven.
                if (BreakevenAfterPips != 0 && pos.GrossProfit / Symbol.PipSize > BreakevenAfterPips && pos.StopLoss > pos.EntryPrice)
                {
                    TradeResult result = ModifyPosition(pos, pos.EntryPrice, pos.TakeProfit);

                    if (result.IsSuccessful)
                    {
                        Print(string.Format("Position {0}: Stop-loss moved to break even", pos.Id));
                    }

                    else
                    {
                        Print(string.Format("Position {0}: Stop-loss could not be moved to break even. Error: {1}", pos.Id, result.Error));
                    }

                    return;
                }
            }

        }

        public bool isTradingTime(DateTime presentTime)
        {
            if (TradingTimes == null)
            {
                return true;
            }
            else
            {
                if (TradingTimes[0].Hour < presentTime.Hour || (TradingTimes[0].Hour == presentTime.Hour && TradingTimes[0].Minute <= presentTime.Minute))
                {
                    // The present time is greater than the starting time of the i-th trading time range.

                    if (TradingTimes[1].Hour > presentTime.Hour || (TradingTimes[1].Hour == presentTime.Hour && TradingTimes[1].Minute > presentTime.Minute))
                    {
                        // The present time is lower than the end time of the i-th trading time range.
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
 


@lattymor
Replies

PanagiotisCharalampous
03 Feb 2021, 08:32

Hi lattymor,

If you need somebody to help you with your development, you can also consider posting a Job.

Best Regards,

Panagiotis 

Join us on Telegram 

 


@PanagiotisCharalampous