Category Other  Published on 22/03/2020

Simple Trading Panel

Description

Based on cTrader sample example. This bot has the hability to open positions based on RR or fixed Lots.

Usefull to make manual backtests!


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

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
    public class SimpleTradingPanel : Robot
    {
        [Parameter("Vertical Position", Group = "Panel alignment", DefaultValue = VerticalAlignment.Bottom)]
        public VerticalAlignment PanelVerticalAlignment { get; set; }

        [Parameter("Horizontal Position", Group = "Panel alignment", DefaultValue = HorizontalAlignment.Right)]
        public HorizontalAlignment PanelHorizontalAlignment { get; set; }

        [Parameter("Default Lots", Group = "Default trade parameters", DefaultValue = 0.01)]
        public double DefaultLots { get; set; }

        [Parameter("Default % RR ratio", Group = "Default trade parameters", DefaultValue = 0.05)]
        public double DefaultRR { get; set; }

        [Parameter("Has Fixed Lots", Group = "Default trade parameters", DefaultValue = false)]
        public bool DefaultHasFixedLots { get; set; }

        [Parameter("Default Take Profit (pips)", Group = "Default trade parameters", DefaultValue = 3.5)]
        public double DefaultTakeProfitPips { get; set; }

        [Parameter("Default Stop Loss (pips)", Group = "Default trade parameters", DefaultValue = 7)]
        public double DefaultStopLossPips { get; set; }

        protected override void OnStart()
        {
            var tradingPanel = new TradingPanel(this, Account, Symbol, DefaultLots, DefaultRR, DefaultHasFixedLots, DefaultStopLossPips, DefaultTakeProfitPips);

            var border = new Border 
            {
                VerticalAlignment = PanelVerticalAlignment,
                HorizontalAlignment = PanelHorizontalAlignment,
                Style = Styles.CreatePanelBackgroundStyle(),
                Margin = "20 40 20 20",
                Width = 225,
                Child = tradingPanel
            };

            Chart.AddControl(border);

            Positions.Opened += OnPositionsOpened;
        }

        void OnPositionsOpened(PositionOpenedEventArgs obj)
        {
            var position = obj.Position;

            Print(string.Format("Open position with: LotsParameter: {0}, StopLossPipsParameter: {1}, TakeProfitPipsParameter: {2}", position.Quantity, position.StopLoss, position.TakeProfit));
        }
    }

    public class TradingPanel : CustomControl
    {
        private const string LotsInputKey = "LotsKey";
        private const string RRLotsKey = "RRLotsKey";
        private const string HasFixedLotsKey = "HasFixedLotsKey";
        private const string TakeProfitInputKey = "TPKey";
        private const string StopLossInputKey = "SLKey";
        private const string BotLabel = "SimpleTradingPanel";
        private readonly IDictionary<string, TextBox> _inputMap = new Dictionary<string, TextBox>();
        private readonly IDictionary<string, CheckBox> _checkboxMap = new Dictionary<string, CheckBox>();
        private readonly IAccount _account;
        private readonly Robot _robot;
        private readonly Symbol _symbol;

        public TradingPanel(Robot robot, IAccount account, Symbol symbol, double defaultLots, double defaultRRLots, bool defaultHasFixedLots, double defaultStopLossPips, double defaultTakeProfitPips)
        {
            _account = account;
            _robot = robot;
            _symbol = symbol;
            AddChild(CreateTradingPanel(defaultLots, defaultRRLots, defaultHasFixedLots, defaultStopLossPips, defaultTakeProfitPips));
        }

        private ControlBase CreateTradingPanel(double defaultLots, double defaultRRLots, bool defaultHasFixedLots, double defaultStopLossPips, double defaultTakeProfitPips)
        {
            var mainPanel = new StackPanel();

            var header = CreateHeader();
            mainPanel.AddChild(header);

            var contentPanel = CreateContentPanel(defaultLots, defaultRRLots, defaultHasFixedLots, defaultStopLossPips, defaultTakeProfitPips);
            mainPanel.AddChild(contentPanel);

            return mainPanel;
        }

        private ControlBase CreateHeader()
        {
            var headerBorder = new Border 
            {
                BorderThickness = "0 0 0 1",
                Style = Styles.CreateCommonBorderStyle()
            };

            var header = new TextBlock 
            {
                Text = "Quick Trading Panel",
                Margin = "10 7",
                Style = Styles.CreateHeaderStyle()
            };

            headerBorder.Child = header;
            return headerBorder;
        }

        private StackPanel CreateContentPanel(double defaultLots, double defaultRRLots, bool defaultHasFixedLots, double defaultStopLossPips, double defaultTakeProfitPips)
        {
            var contentPanel = new StackPanel 
            {
                Margin = 10
            };
            var grid = new Grid(6, 3);
            grid.Columns[1].SetWidthInPixels(5);

            var sellButton = CreateTradeButton("SELL", Styles.CreateSellButtonStyle(), TradeType.Sell);
            grid.AddChild(sellButton, 0, 0);

            var buyButton = CreateTradeButton("BUY", Styles.CreateBuyButtonStyle(), TradeType.Buy);
            grid.AddChild(buyButton, 0, 2);

            var lotsInput = CreateInputWithLabel("Quantity (Lots)", defaultLots.ToString("F2"), LotsInputKey);
            grid.AddChild(lotsInput, 1, 0);

            var rrLotsInput = CreateInputWithLabel("RR (%)", defaultRRLots.ToString("F2"), RRLotsKey);
            grid.AddChild(rrLotsInput, 1, 2);

            var hasFixedLotsInput = CreateCheckboxWithLabel("Has Fixed Lots", defaultHasFixedLots, HasFixedLotsKey);
            grid.AddChild(hasFixedLotsInput, 2, 0, 1, 3);

            var stopLossInput = CreateInputWithLabel("Stop Loss (Pips)", defaultStopLossPips.ToString("F1"), StopLossInputKey);
            grid.AddChild(stopLossInput, 3, 0);

            var takeProfitInput = CreateInputWithLabel("Take Profit (Pips)", defaultTakeProfitPips.ToString("F1"), TakeProfitInputKey);
            grid.AddChild(takeProfitInput, 3, 2);

            var closeSellButton = CreateCloseSellButton();
            grid.AddChild(closeSellButton, 4, 0);

            var closeBuyButton = CreateCloseBuyButton();
            grid.AddChild(closeBuyButton, 4, 2);

            var closeAllButton = CreateCloseAllButton();
            grid.AddChild(closeAllButton, 5, 0, 1, 3);

            contentPanel.AddChild(grid);

            return contentPanel;
        }

        private Button CreateTradeButton(string text, Style style, TradeType tradeType)
        {
            var tradeButton = new Button 
            {
                Text = text,
                Style = style,
                Height = 25
            };

            tradeButton.Click += args => ExecuteMarketOrderAsync(tradeType);

            return tradeButton;
        }

        private ControlBase CreateCloseSellButton()
        {
            var closeSellBorder = new Border 
            {
                Margin = "0 10 0 0",
                BorderThickness = "0 1 0 0",
                Style = Styles.CreateCommonBorderStyle()
            };

            var closeSellButton = new Button 
            {
                Style = Styles.CreateSellButtonStyle(),
                Text = "Close Sell",
                Margin = "0 10 0 0"
            };

            closeSellButton.Click += args => CloseAllSell();
            closeSellBorder.Child = closeSellButton;

            return closeSellBorder;
        }

        private ControlBase CreateCloseBuyButton()
        {
            var closeBuyBorder = new Border 
            {
                Margin = "0 10 0 0",
                BorderThickness = "0 1 0 0",
                Style = Styles.CreateCommonBorderStyle()
            };

            var closeBuyButton = new Button 
            {
                Style = Styles.CreateBuyButtonStyle(),
                Text = "Close Buy",
                Margin = "0 10 0 0"
            };

            closeBuyButton.Click += args => CloseAllBuy();
            closeBuyBorder.Child = closeBuyButton;

            return closeBuyBorder;
        }

        private ControlBase CreateCloseAllButton()
        {
            var closeAllBorder = new Border 
            {
                Margin = "0 10 0 0",
                BorderThickness = "0 1 0 0",
                Style = Styles.CreateCommonBorderStyle()
            };

            var closeButton = new Button 
            {
                Style = Styles.CreateCloseButtonStyle(),
                Text = "Close All",
                Margin = "0 10 0 0"
            };

            closeButton.Click += args => CloseAll();
            closeAllBorder.Child = closeButton;

            return closeAllBorder;
        }

        private Panel CreateInputWithLabel(string label, string defaultValue, string inputKey)
        {
            var stackPanel = new StackPanel 
            {
                Orientation = Orientation.Vertical,
                Margin = "0 10 0 0"
            };

            var textBlock = new TextBlock 
            {
                Text = label
            };

            var input = new TextBox 
            {
                Margin = "0 5 0 0",
                Text = defaultValue,
                Style = Styles.CreateInputStyle()
            };

            _inputMap.Add(inputKey, input);

            stackPanel.AddChild(textBlock);
            stackPanel.AddChild(input);

            return stackPanel;
        }

        private ControlBase CreateCheckboxWithLabel(string label, bool defaultValue, string inputKey)
        {
            var closeSellBorder = new Border 
            {
                Margin = "0 10 0 0",
                BorderThickness = "0 1 0 1",
                Style = Styles.CreateCommonBorderStyle()
            };

            var stackPanel = new StackPanel 
            {
                Orientation = Orientation.Horizontal,
                Margin = "0 10 0 10"
            };

            var input = new CheckBox 
            {
                Margin = "0 0 5 0",
                IsChecked = defaultValue
            };

            var textBlock = new TextBlock 
            {
                Text = label
            };

            _checkboxMap.Add(inputKey, input);

            stackPanel.AddChild(input);
            stackPanel.AddChild(textBlock);

            closeSellBorder.Child = stackPanel;

            return closeSellBorder;
        }

        private void ExecuteMarketOrderAsync(TradeType tradeType)
        {
            double volume;
            var lots = GetValueFromInput(LotsInputKey, 0);
            var hasFixedLots = GetValueFromCheckbox(HasFixedLotsKey, false);

            if (!hasFixedLots && lots <= 0)
            {
                _robot.Print(string.Format("{0} failed, invalid Lots", tradeType));
                return;
            }

            var stopLossPips = GetValueFromInput(StopLossInputKey, 0);
            var takeProfitPips = GetValueFromInput(TakeProfitInputKey, 0);
            var rrLots = GetValueFromInput(RRLotsKey, 0);

            if (hasFixedLots)
            {
                volume = _symbol.QuantityToVolumeInUnits(lots);
            }
            else
            {
                double commissionInPips = 0.8;

                // Our total balance is our account balance plus any reserve funds. We do not always keep all our money in the trading account. 
                double totalBalance = _account.Balance;

                // Calculate the total risk allowed per trade.
                double riskPerTrade = (totalBalance * rrLots) / 100;

                // Add the stop loss, commission pips and spread to get the total pips used for the volume calculation.
                double totalPips = takeProfitPips + commissionInPips + _symbol.Spread;

                // Calculate the exact volume to be traded. Then round the volume to the nearest 100,000 and convert to an int so that it can be returned to the caller.
                double exactVolume = Math.Round(riskPerTrade / (_symbol.PipValue * totalPips), 2);
                volume = (((int)exactVolume) / 100000) * 100000;
            }

            _robot.ExecuteMarketOrderAsync(tradeType, _symbol.Name, volume, BotLabel, stopLossPips, takeProfitPips);
        }

        private double GetValueFromInput(string inputKey, double defaultValue)
        {
            double value;

            return double.TryParse(_inputMap[inputKey].Text, out value) ? value : defaultValue;
        }

        private bool GetValueFromCheckbox(string inputKey, bool defaultValue)
        {
            return _checkboxMap[inputKey].IsChecked ?? defaultValue;
        }

        private void CloseAllBuy()
        {
            foreach (var position in _robot.Positions.FindAll(BotLabel, _symbol.Name, TradeType.Buy))
                _robot.ClosePositionAsync(position);
        }

        private void CloseAllSell()
        {
            foreach (var position in _robot.Positions.FindAll(BotLabel, _symbol.Name, TradeType.Sell))
                _robot.ClosePositionAsync(position);
        }

        private void CloseAll()
        {
            CloseAllBuy();
            CloseAllSell();
        }
    }

    public static class Styles
    {
        public static Style CreatePanelBackgroundStyle()
        {
            var style = new Style();
            style.Set(ControlProperty.CornerRadius, 3);
            style.Set(ControlProperty.BackgroundColor, GetColorWithOpacity(Color.FromHex("#292929"), 0.85m), ControlState.DarkTheme);
            style.Set(ControlProperty.BackgroundColor, GetColorWithOpacity(Color.FromHex("#FFFFFF"), 0.85m), ControlState.LightTheme);
            style.Set(ControlProperty.BorderColor, Color.FromHex("#3C3C3C"), ControlState.DarkTheme);
            style.Set(ControlProperty.BorderColor, Color.FromHex("#C3C3C3"), ControlState.LightTheme);
            style.Set(ControlProperty.BorderThickness, new Thickness(1));

            return style;
        }

        public static Style CreateCommonBorderStyle()
        {
            var style = new Style();
            style.Set(ControlProperty.BorderColor, GetColorWithOpacity(Color.FromHex("#FFFFFF"), 0.12m), ControlState.DarkTheme);
            style.Set(ControlProperty.BorderColor, GetColorWithOpacity(Color.FromHex("#000000"), 0.12m), ControlState.LightTheme);
            return style;
        }

        public static Style CreateHeaderStyle()
        {
            var style = new Style();
            style.Set(ControlProperty.ForegroundColor, GetColorWithOpacity("#FFFFFF", 0.70m), ControlState.DarkTheme);
            style.Set(ControlProperty.ForegroundColor, GetColorWithOpacity("#000000", 0.65m), ControlState.LightTheme);
            return style;
        }

        public static Style CreateInputStyle()
        {
            var style = new Style(DefaultStyles.TextBoxStyle);
            style.Set(ControlProperty.BackgroundColor, Color.FromHex("#1A1A1A"), ControlState.DarkTheme);
            style.Set(ControlProperty.BackgroundColor, Color.FromHex("#111111"), ControlState.DarkTheme | ControlState.Hover);
            style.Set(ControlProperty.BackgroundColor, Color.FromHex("#E7EBED"), ControlState.LightTheme);
            style.Set(ControlProperty.BackgroundColor, Color.FromHex("#D6DADC"), ControlState.LightTheme | ControlState.Hover);
            style.Set(ControlProperty.CornerRadius, 3);
            return style;
        }

        public static Style CreateBuyButtonStyle()
        {
            return CreateButtonStyle(Color.FromHex("#009345"), Color.FromHex("#10A651"));
        }

        public static Style CreateSellButtonStyle()
        {
            return CreateButtonStyle(Color.FromHex("#F05824"), Color.FromHex("#FF6C36"));
        }

        public static Style CreateCloseButtonStyle()
        {
            return CreateButtonStyle(Color.FromHex("#F05824"), Color.FromHex("#FF6C36"));
        }

        private static Style CreateButtonStyle(Color color, Color hoverColor)
        {
            var style = new Style(DefaultStyles.ButtonStyle);
            style.Set(ControlProperty.BackgroundColor, color, ControlState.DarkTheme);
            style.Set(ControlProperty.BackgroundColor, color, ControlState.LightTheme);
            style.Set(ControlProperty.BackgroundColor, hoverColor, ControlState.DarkTheme | ControlState.Hover);
            style.Set(ControlProperty.BackgroundColor, hoverColor, ControlState.LightTheme | ControlState.Hover);
            style.Set(ControlProperty.ForegroundColor, Color.FromHex("#FFFFFF"), ControlState.DarkTheme);
            style.Set(ControlProperty.ForegroundColor, Color.FromHex("#FFFFFF"), ControlState.LightTheme);
            return style;
        }

        private static Color GetColorWithOpacity(Color baseColor, decimal opacity)
        {
            var alpha = (int)Math.Round(byte.MaxValue * opacity, MidpointRounding.AwayFromZero);
            return Color.FromArgb(alpha, baseColor);
        }
    }
}


reyx's avatar
reyx

Joined on 16.02.2019

  • Distribution: Free
  • Language: C#
  • Trading platform: cTrader Automate
  • File name: Simple Trading Panel.algo
  • Rating: 2.5
  • Installs: 2491
  • Modified: 13/10/2021 09:54
Comments
Log in to add a comment.
NE
newbee · 1 year ago

Hello reyx.

Thanks for this.  Is there any way you can show me a sample of the code to insert a ComboBox into this trading panel say where the close all button is. (ie Instead of the close All button-just replace that with the ComboBox that would have a drop down list of texts to choose from). I've been trying to do that for a very long time with no success.

Any help would be greatly appreciated. 

thanks

BE
belasto · 3 years ago

This robot seems not to work. Tried it in backtesting and no trades were opened or closed. Too bad..

YA
yaghouti · 3 years ago

Hi

how could we change and refresh the information on textbox and checkboxes?

MA
matthewgrayiveta · 4 years ago

is dont work form i am trying test my strategies on back test and is don't buy or sell when I press is don't work for me 

DA
davidiesis · 4 years ago

I think it has some issue because it cannot calculate the RR% correctly. Can you fix it?

DA
davidiesis · 4 years ago

Why the Risk Ratio doesn't calculate less than 1 lot?

DA
davidiesis · 4 years ago

Thank you for this little great tool!!!

GA
GAEMFX · 4 years ago

 

you can configure one that has an option to indicate a trailing stop???