Category Trading  Published on 16/06/2024

Break-Even and Trailing Stop Plugin

Description

This plugin can move the stop loss of all the open positions to the user-defined break-even value in pips with respect to the entry price of each position, and it can also activate a trailing stop loss for all the open positions, which will trail at a user-defined distance in pips from the current price. It is possible to activate both the Break-Even and Trailing Stop features, or either one or the other.

Example when both the Activate Break-Even checkbox and the Activate Trailing Stop checkbox are checked: If the Break-Even Trigger in Pips parameter is set to 10 and the Break-Even Value in Pips parameter is set to 0, the the stop loss will be set at the position's entry price once it reaches 10 pips in profit. If the Trailing Stop Trigger in Pips parameter is set to 30 and the Trailing Distance in Pips parameter is set to 50, the stop loss will then start trailing once a profit of 30 pips is reached, at a distance of 50 pips from the current price.

Example when only the Activate Break-Even checkbox is checked: If the Break-Even Trigger in Pips parameter is set to 15 and the Break-Even Value in Pips parameter is set to 2, the stop loss will be set at the position's entry price + 2 pips in profit once it reaches 15 pips in profit.

Example when only the Activate Trailing Stop checkbox is checked: If the Trailing Stop Trigger in Pips parameter is set to 15 and the Trailing Distance in Pips parameter is set to 20, the stop loss will start trailing once a profit of 15 pips is reached, at a distance of 20 pips from the current price.


The author decided to hide the source code.
acronew's avatar
acronew

Joined on 15.06.2024

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: BreakEvenTrailingStop.algo
  • Rating: 5
  • Installs: 1034
  • Modified: 16/06/2024 13:24
Comments
Log in to add a comment.
LB
lbellego · 2 weeks ago

This is the same code but with a memory (LocalStorage)

using System;
using cAlgo.API;
using cAlgo.API.Collections;
using cAlgo.API.Indicators;
using cAlgo.API.Internals;

namespace cAlgo.Plugins
{
    [Plugin(AccessRights = AccessRights.None)]
    public class BreakEvenTrailingStop : Plugin
    {
        ViewModel viewModel = new ViewModel();
        CheckBox breakEvenOn;
        CheckBox trailingStopOn;
        TextBox breakEvenTextBox;
        TextBox breakEvenTextBox2;
        TextBox trailingStopTextBox;
        TextBox trailingStopTextBox2;

        public bool _breakEvenOn
        {
            get => Convert.ToBoolean(LocalStorage.GetString("breakEvenOn", LocalStorageScope.Type) ?? bool.FalseString);
            set
            {
                LocalStorage.SetString("breakEvenOn", value ? bool.TrueString : bool.FalseString, LocalStorageScope.Type);
                LocalStorage.Flush(LocalStorageScope.Type);
            }
        }

        protected override void OnStart()
        {
            AddControls();
            
            breakEvenOn.IsChecked = Convert.ToBoolean(LocalStorage.GetString("breakEvenOn", LocalStorageScope.Type) ?? bool.FalseString);
            viewModel.BreakEvenTrigger = double.Parse(LocalStorage.GetString("BreakEvenTrigger", LocalStorageScope.Type) ?? "10");
            viewModel.BreakEvenValue = double.Parse(LocalStorage.GetString("BreakEvenValue", LocalStorageScope.Type) ?? "0");
            //BreakEvenActivation();
            trailingStopOn.IsChecked = Convert.ToBoolean(LocalStorage.GetString("trailingStopOn", LocalStorageScope.Type) ?? bool.FalseString);
            viewModel.TrailingStopTrigger = double.Parse(LocalStorage.GetString("TrailingStopTrigger", LocalStorageScope.Type) ?? "30");
            viewModel.TrailingStopValue = double.Parse(LocalStorage.GetString("TrailingStopValue", LocalStorageScope.Type) ?? "50");

            viewModel_Changed();
            viewModel.Changed += viewModel_Changed;

            //TrailingStopActivation();
            Timer.Start(TimeSpan.FromSeconds(1));
        }

        private void CheckBox_breakEvenOn_checked(CheckBoxEventArgs obj)
        {
            LocalStorage.SetString("breakEvenOn", obj.CheckBox.IsChecked.Value ? bool.TrueString : bool.FalseString, LocalStorageScope.Type);
            LocalStorage.Flush(LocalStorageScope.Type);
        }
        private void CheckBox_trailingStopOn_checked(CheckBoxEventArgs obj)
        {
            LocalStorage.SetString("trailingStopOn", obj.CheckBox.IsChecked.Value ? bool.TrueString : bool.FalseString, LocalStorageScope.Type);
            LocalStorage.Flush(LocalStorageScope.Type);
        }


        private void AddControls()
        {
            var block = Asp.SymbolTab.AddBlock("Break-Even and Trailing Stop Plugin");
            block.IsExpanded = true;
            block.IsDetachable = false;
            block.Index = 1;
            block.Height = 400;

            var rootStackPanel = new StackPanel { Margin = new Thickness(10) };

            rootStackPanel.AddChild(new TextBlock { Text = "Activate Break-Even:", Margin = new Thickness(0, 10, 0, 0) });

            var grid = new Grid { Margin = new Thickness(10) };
            grid.AddColumn().SetWidthInStars(1);
            grid.AddColumn().SetWidthToAuto();
            grid.AddColumn().SetWidthToAuto();
            grid.AddColumn().SetWidthToAuto();

            breakEvenOn = new CheckBox();
            breakEvenOn.Checked += CheckBox_breakEvenOn_checked;
            breakEvenOn.Unchecked += CheckBox_breakEvenOn_checked;

            grid.AddChild(breakEvenOn, 0, 0);
            rootStackPanel.AddChild(grid);

            rootStackPanel.AddChild(new TextBlock { Text = "Break-Even Trigger in Pips:", Margin = new Thickness(0, 10, 0, 0) });

            var grid1 = new Grid { Margin = new Thickness(10) };
            grid1.AddColumn().SetWidthInStars(1);
            grid1.AddColumn().SetWidthToAuto();
            grid1.AddColumn().SetWidthToAuto();
            grid1.AddColumn().SetWidthToAuto();

            breakEvenTextBox = new TextBox { IsReadOnly = false, TextAlignment = TextAlignment.Right };
            var breakEvenTextBoxStyle = new Style();
            breakEvenTextBoxStyle.Set(ControlProperty.BackgroundColor, Color.FromArgb(26, 26, 26), ControlState.DarkTheme);
            breakEvenTextBoxStyle.Set(ControlProperty.ForegroundColor, Color.FromArgb(255, 255, 255), ControlState.DarkTheme);
            breakEvenTextBoxStyle.Set(ControlProperty.BackgroundColor, Color.FromArgb(231, 235, 237), ControlState.LightTheme);
            breakEvenTextBoxStyle.Set(ControlProperty.ForegroundColor, Color.FromArgb(55, 56, 57), ControlState.LightTheme);
            breakEvenTextBox.Style = breakEvenTextBoxStyle;
            grid1.AddChild(breakEvenTextBox, 0, 0);
            var decreaseTriggerButton = new Button { Text = "-", Margin = new Thickness(5, 0, 5, 0) };
            decreaseTriggerButton.Click += decreaseTriggerButton_Click;
            grid1.AddChild(decreaseTriggerButton, 0, 1);
            var increaseTriggerButton = new Button { Text = "+" };
            increaseTriggerButton.Click += increaseTriggerButton_Click;
            grid1.AddChild(increaseTriggerButton, 0, 2);
            rootStackPanel.AddChild(grid1);

            rootStackPanel.AddChild(new TextBlock { Text = "Break-Even Value in Pips:", Margin = new Thickness(0, 10, 0, 0) });

            var grid2 = new Grid { Margin = new Thickness(10) };
            grid2.AddColumn().SetWidthInStars(1);
            grid2.AddColumn().SetWidthToAuto();
            grid2.AddColumn().SetWidthToAuto();
            grid2.AddColumn().SetWidthToAuto();

            breakEvenTextBox2 = new TextBox { IsReadOnly = false, TextAlignment = TextAlignment.Right };
            breakEvenTextBox2.Style = breakEvenTextBoxStyle;
            grid2.AddChild(breakEvenTextBox2, 0, 0);
            var decreaseValueButton = new Button { Text = "-", Margin = new Thickness(5, 0, 5, 0) };
            decreaseValueButton.Click += decreaseValueButton_Click;
            grid2.AddChild(decreaseValueButton, 0, 1);
            var increaseValueButton = new Button { Text = "+" };
            increaseValueButton.Click += increaseValueButton_Click;
            grid2.AddChild(increaseValueButton, 0, 2);
            rootStackPanel.AddChild(grid2);

            rootStackPanel.AddChild(new TextBlock { Text = "Activate Trailing Stop:", Margin = new Thickness(0, 10, 0, 0) });

            var grid3 = new Grid { Margin = new Thickness(10) };
            grid3.AddColumn().SetWidthInStars(1);
            grid3.AddColumn().SetWidthToAuto();
            grid3.AddColumn().SetWidthToAuto();
            grid3.AddColumn().SetWidthToAuto();

            trailingStopOn = new CheckBox();
            trailingStopOn.Checked += CheckBox_trailingStopOn_checked;
            trailingStopOn.Unchecked += CheckBox_trailingStopOn_checked;
            grid3.AddChild(trailingStopOn, 0, 0);
            rootStackPanel.AddChild(grid3);

            rootStackPanel.AddChild(new TextBlock { Text = "Trailing Stop Trigger in Pips:", Margin = new Thickness(0, 10, 0, 0) });

            var grid4 = new Grid { Margin = new Thickness(10) };
            grid4.AddColumn().SetWidthInStars(1);
            grid4.AddColumn().SetWidthToAuto();
            grid4.AddColumn().SetWidthToAuto();
            grid4.AddColumn().SetWidthToAuto();

            trailingStopTextBox = new TextBox { IsReadOnly = false, TextAlignment = TextAlignment.Right };
            var trailingStopTextBoxStyle = new Style();
            trailingStopTextBoxStyle.Set(ControlProperty.BackgroundColor, Color.FromArgb(26, 26, 26), ControlState.DarkTheme);
            trailingStopTextBoxStyle.Set(ControlProperty.ForegroundColor, Color.FromArgb(255, 255, 255), ControlState.DarkTheme);
            trailingStopTextBoxStyle.Set(ControlProperty.BackgroundColor, Color.FromArgb(231, 235, 237), ControlState.LightTheme);
            trailingStopTextBoxStyle.Set(ControlProperty.ForegroundColor, Color.FromArgb(55, 56, 57), ControlState.LightTheme);
            trailingStopTextBox.Style = trailingStopTextBoxStyle;
            grid4.AddChild(trailingStopTextBox, 0, 0);
            var decreaseTriggerButton2 = new Button { Text = "-", Margin = new Thickness(5, 0, 5, 0) };
            decreaseTriggerButton2.Click += decreaseTriggerButton_Click2;
            grid4.AddChild(decreaseTriggerButton2, 0, 1);
            var increaseTriggerButton2 = new Button { Text = "+" };
            increaseTriggerButton2.Click += increaseTriggerButton_Click2;
            grid4.AddChild(increaseTriggerButton2, 0, 2);
            rootStackPanel.AddChild(grid4);

            rootStackPanel.AddChild(new TextBlock { Text = "Trailing Distance in Pips:", Margin = new Thickness(0, 10, 0, 0) });

            var grid5 = new Grid { Margin = new Thickness(10) };
            grid5.AddColumn().SetWidthInStars(1);
            grid5.AddColumn().SetWidthToAuto();
            grid5.AddColumn().SetWidthToAuto();
            grid5.AddColumn().SetWidthToAuto();

            trailingStopTextBox2 = new TextBox { IsReadOnly = false, TextAlignment = TextAlignment.Right };
            trailingStopTextBox2.Style = trailingStopTextBoxStyle;
            grid5.AddChild(trailingStopTextBox2, 0, 0);
            var decreaseValueButton2 = new Button { Text = "-", Margin = new Thickness(5, 0, 5, 0) };
            decreaseValueButton2.Click += decreaseValueButton_Click2;
            grid5.AddChild(decreaseValueButton2, 0, 1);
            var increaseValueButton2 = new Button { Text = "+" };
            increaseValueButton2.Click += increaseValueButton_Click2;
            grid5.AddChild(increaseValueButton2, 0, 2);
            rootStackPanel.AddChild(grid5);

            block.Child = rootStackPanel;
        }

        protected override void OnTimer()
        {
            if (breakEvenOn.IsChecked == true) BreakEvenActivation();
            if (trailingStopOn.IsChecked == true) TrailingStopActivation();
        }

        private void viewModel_Changed()
        {
            breakEvenTextBox.Text = viewModel.BreakEvenTrigger.ToString();
            breakEvenTextBox2.Text = viewModel.BreakEvenValue.ToString();
            trailingStopTextBox.Text = viewModel.TrailingStopTrigger.ToString();
            trailingStopTextBox2.Text = viewModel.TrailingStopValue.ToString();

            LocalStorage.SetString("BreakEvenTrigger", viewModel.BreakEvenTrigger.ToString(), LocalStorageScope.Type);
            LocalStorage.SetString("BreakEvenValue", viewModel.BreakEvenValue.ToString(), LocalStorageScope.Type);
            LocalStorage.SetString("TrailingStopTrigger", viewModel.TrailingStopTrigger.ToString(), LocalStorageScope.Type);
            LocalStorage.SetString("TrailingStopValue", viewModel.TrailingStopValue.ToString(), LocalStorageScope.Type);

            LocalStorage.Flush(LocalStorageScope.Type);
        }

        private void increaseTriggerButton_Click(ButtonClickEventArgs obj)
        {
            viewModel.BreakEvenTrigger += 1;
        }

        private void decreaseTriggerButton_Click(ButtonClickEventArgs obj)
        {
            if (viewModel.BreakEvenTrigger > 0) viewModel.BreakEvenTrigger -= 1;
            else viewModel.BreakEvenTrigger = 0;
        }

        private void increaseValueButton_Click(ButtonClickEventArgs obj)
        {
            viewModel.BreakEvenValue += 1;
        }

        private void decreaseValueButton_Click(ButtonClickEventArgs obj)
        {
            if (viewModel.BreakEvenValue > 0) viewModel.BreakEvenValue -= 1;
            else viewModel.BreakEvenValue = 0;
        }

        private void increaseTriggerButton_Click2(ButtonClickEventArgs obj)
        {
            viewModel.TrailingStopTrigger += 1;
        }

        private void decreaseTriggerButton_Click2(ButtonClickEventArgs obj)
        {
            if (viewModel.TrailingStopTrigger > 0) viewModel.TrailingStopTrigger -= 1;
            else viewModel.TrailingStopTrigger = 0;
        }

        private void increaseValueButton_Click2(ButtonClickEventArgs obj)
        {
            viewModel.TrailingStopValue += 1;
        }

        private void decreaseValueButton_Click2(ButtonClickEventArgs obj)
        {
            if (viewModel.TrailingStopValue > 0) viewModel.TrailingStopValue -= 1;
            else viewModel.TrailingStopValue = 0;
        }

        private void BreakEvenActivation()
        {
            foreach (var pos in Positions)
            {
                if (pos.TradeType == TradeType.Buy && pos.Symbol.Bid >= pos.EntryPrice + viewModel.BreakEvenTrigger * pos.Symbol.PipSize)
                {
                    double newSL = pos.EntryPrice + viewModel.BreakEvenValue * pos.Symbol.PipSize;
                    if (newSL > pos.StopLoss || pos.StopLoss == null) ModifyPosition(pos, newSL, pos.TakeProfit);
                }
                else if (pos.TradeType == TradeType.Sell && pos.Symbol.Ask <= pos.EntryPrice - viewModel.BreakEvenTrigger * pos.Symbol.PipSize)
                {
                    double newSL = pos.EntryPrice - viewModel.BreakEvenValue * pos.Symbol.PipSize;
                    if (newSL < pos.StopLoss || pos.StopLoss == null) ModifyPosition(pos, newSL, pos.TakeProfit);
                }
            }
        }

        private void TrailingStopActivation()
        {
            foreach (var pos in Positions)
            {
                if (pos.TradeType == TradeType.Buy && pos.Symbol.Bid >= pos.EntryPrice + (viewModel.TrailingStopTrigger * pos.Symbol.PipSize))
                {
                    double newSL = pos.Symbol.Bid - (viewModel.TrailingStopValue * pos.Symbol.PipSize);
                    if (pos.StopLoss == null || newSL > pos.StopLoss) ModifyPosition(pos, newSL, pos.TakeProfit);
                }
                else if (pos.TradeType == TradeType.Sell && pos.Symbol.Ask <= pos.EntryPrice - (viewModel.TrailingStopTrigger * pos.Symbol.PipSize))
                {
                    double newSL = pos.Symbol.Ask + (viewModel.TrailingStopValue * pos.Symbol.PipSize);
                    if (pos.StopLoss == null || newSL < pos.StopLoss) ModifyPosition(pos, newSL, pos.TakeProfit);
                }
            }
        }

        protected override void OnStop()
        {
            //
        }
    }
}
class ViewModel
{
    private double _breakEvenTrigger;
    public double BreakEvenTrigger
    {
        get { return _breakEvenTrigger; }
        set
        {
            if (value == _breakEvenTrigger)
                return;
            _breakEvenTrigger = value;

            Changed?.Invoke();
        }
    }

    private double _breakEvenValue;
    public double BreakEvenValue
    {
        get { return _breakEvenValue; }
        set
        {
            if (value == _breakEvenValue)
                return;
            _breakEvenValue = value;

            Changed?.Invoke();
        }
    }
    private double _trailingStopTrigger;
    public double TrailingStopTrigger
    {
        get { return _trailingStopTrigger; }
        set
        {
            if (value == _trailingStopTrigger)
                return;
            _trailingStopTrigger = value;

            Changed?.Invoke();
        }
    }

    private double _trailingStopValue;
    public double TrailingStopValue
    {
        get { return _trailingStopValue; }
        set
        {
            if (value == _trailingStopValue)
                return;
            _trailingStopValue = value;

            Changed?.Invoke();
        }
    }

    public event Action Changed;
}
TH
thedarkforex21 · 5 months ago

First of all, thank you for your work. I want to dicuss a trouble that i have with the plugin, everytime i close and open Ctrader, the values for active break even and active trailing stop are rebooted to default (break even 10 pips, Trailing stop trigger 30 pips, distance 50 pips), is there a way to maintain my last values for break even and trailing stop?

HE
Hez_O · 5 months ago

I dont know how to start the plugin. It has installed however the Start button does not appear.

CT
ctid7518380 · 5 months ago

Hi,

It looks good. Is it also possible to set it up for just specific position(s) or have different parameters per position?

Thanks. Job

TommyDee's avatar
TommyDee · 6 months ago

Hi Acronew,
I wanted to take a moment to thank you for sharing your trading indicator with everyone. Your generosity and willingness to help others is truly appreciated. I've started using the indicator and it's already making a difference in my trading. Thank you for your hard work and for giving back to the community. 

Best regards, Anastasios