Amount to trade depending on stop loss

Created at 19 Feb 2024, 19:13
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!
OL

oliver.r.m.92

Joined 11.04.2023

Amount to trade depending on stop loss
19 Feb 2024, 19:13


I am trying to configure a cBot so that regardless of the stop loss pips, it represents a loss of 1% of the account balance, I need it to make this calculation and operate with the appropriate amount in each case adapting to the stop loss (Something like the position tool in tradingview that calculates the amount to operate depending on your stop loss). When configuring it the way it appears in the code, the cBot does not execute any order and I do not know where the error is, I would appreciate your help. Thank you!

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;namespace cAlgo.Robots{    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]    public class MACDcBot : Robot    {        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 0.12, MinValue = 0.01, Step = 0.01)]        public double Quantity { get; set; }        // MA        [Parameter("Source", Group = "Moving Average")]        public DataSeries SourceSeries { get; set; }        [Parameter("MA Type", Group = "Moving Average")]        public MovingAverageType MAType { get; set; }        [Parameter("MA Periods", Group = "Moving Average", DefaultValue = 200)]        public int MAPeriods { get; set; }        // MACD        [Parameter("Period", DefaultValue = 9)]        public int Period { get; set; }        [Parameter("Long Cycle", DefaultValue = 26)]        public int LongCycle { get; set; }        [Parameter("Short Cycle", DefaultValue = 12)]        public int ShortCycle { get; set; }        [Parameter("Signal-line crossover true:if Signal-line crossover false: Zero crossover", DefaultValue = true)]        public bool IsSignalLineCrossover { get; set; }        private bool isPositionOpen;        private MacdCrossOver _MACD;        private MovingAverage EMA;        private const string label = "Trade ON";        private double VolumeInUnits        {            get { return Symbol.NormalizeVolumeInUnits(Symbol.QuantityToVolumeInUnits(Quantity)); }        }        protected override void OnStart()        {            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, Period);            EMA = Indicators.MovingAverage(SourceSeries, MAPeriods, MAType);            isPositionOpen = false;            Positions.Closed += OnPositionsClosed;        }        protected override void OnTick()        {            // Verifica si hay posiciones abiertas            if (Positions.Count == 0)                return;            // Obtén la última posición abierta            var position = Positions.LastOrDefault();            // Calcula el stop loss basado en la posición abierta            double stopLossPrice;            if (position.TradeType == TradeType.Buy)                stopLossPrice = EMA.Result.LastValue - Symbol.PipSize * 2; // 2 pips por debajo de la SMA de 200            else if (position.TradeType == TradeType.Sell)                stopLossPrice = EMA.Result.LastValue + Symbol.PipSize * 2; // 2 pips por encima de la SMA de 200                       else                return; // No es necesario establecer el stop loss para otras operaciones            // Calcula el take profit como 1.5 veces el stop loss            double takeProfitPrice = position.EntryPrice + (position.EntryPrice - stopLossPrice) * 1.5;            // Establece el stop loss y take profit            ModifyPosition(position, stopLossPrice, takeProfitPrice);        }        protected override void OnBar()        {            if (IsSignalLineCrossover)            {                if (!isPositionOpen && _MACD.Histogram.Last(1) > (0) && _MACD.MACD.Last(2) < _MACD.Signal.Last(2) && _MACD.MACD.Last(1) > _MACD.Signal.Last(1) && Bars.HighPrices.Last(1) > EMA.Result.Last(2))                {                    double stopLossPrice = EMA.Result.LastValue - Symbol.PipSize * 2; // 2 pips por debajo de la SMA de 200                    double takeProfitPrice = Bars.HighPrices.Last(1) + Symbol.PipSize * 2; // 2 pips por encima del máximo previo                    double lotSize = CalculateLotSize(stopLossPrice);                    ExecuteMarketOrder(TradeType.Buy, SymbolName, lotSize, label);                    isPositionOpen = true;                }                if (!isPositionOpen && _MACD.Histogram.Last(1) < (0) && _MACD.MACD.Last(2) > _MACD.Signal.Last(2) && _MACD.MACD.Last(1) < _MACD.Signal.Last(1) && Bars.LowPrices.Last(1) < EMA.Result.Last(2))                {                    double stopLossPrice = EMA.Result.LastValue + Symbol.PipSize * 2; // 2 pips por encima de la SMA de 200                    double takeProfitPrice = Bars.LowPrices.Last(1) - Symbol.PipSize * 2; // 2 pips por debajo del mínimo previo                    double lotSize = CalculateLotSize(stopLossPrice);                    ExecuteMarketOrder(TradeType.Sell, SymbolName, lotSize, label);                    isPositionOpen = true;                }            }        }        private double CalculateLotSize(double stopLossPrice)        {            double accountBalance = Account.Balance;            double riskAmount = accountBalance * 0.01; // 1% del balance            double distanceToStopLoss = Math.Abs(stopLossPrice - Bars.ClosePrices.LastValue); // Distancia al stop loss            double valuePerPip = Symbol.PipValue * Symbol.QuantityToVolumeInUnits(1); // Valor por pip            double lotSize = riskAmount / (distanceToStopLoss * valuePerPip); // Lotaje calculado            return lotSize;        }        private void OnPositionsClosed(PositionClosedEventArgs args)        {            // Actualizar el estado de la posición abierta            isPositionOpen = false;        }    }}

@oliver.r.m.92
Replies

PanagiotisCharalampous
20 Feb 2024, 06:57

Hi there, 

Please provide the code in proper format so that we can easily copy, paste and build it.

Best regards,

Panagiotis


@PanagiotisCharalampous

oliver.r.m.92
20 Feb 2024, 07:52

RE: Amount to trade depending on stop loss

PanagiotisCharalampous said: 

Hi there, 

Please provide the code in proper format so that we can easily copy, paste and build it.

Best regards,

Panagiotis

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;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class MACDcBot : Robot
    {
        [Parameter("Quantity (Lots)", Group = "Volume", DefaultValue = 0.12, MinValue = 0.01, Step = 0.01)]
        public double Quantity { get; set; }

        // MA
        [Parameter("Source", Group = "Moving Average")]
        public DataSeries SourceSeries { get; set; }

        [Parameter("MA Type", Group = "Moving Average")]
        public MovingAverageType MAType { get; set; }

        [Parameter("MA Periods", Group = "Moving Average", DefaultValue = 200)]
        public int MAPeriods { get; set; }

        // MACD
        [Parameter("Period", DefaultValue = 9)]
        public int Period { get; set; }

        [Parameter("Long Cycle", DefaultValue = 26)]
        public int LongCycle { get; set; }

        [Parameter("Short Cycle", DefaultValue = 12)]
        public int ShortCycle { get; set; }

        [Parameter("Signal-line crossover true:if Signal-line crossover false: Zero crossover", DefaultValue = true)]
        public bool IsSignalLineCrossover { get; set; }

        private bool isPositionOpen;
        private MacdCrossOver _MACD;
        private MovingAverage EMA;
        private const string label = "Trade ON";

        private double VolumeInUnits
        {
            get { return Symbol.NormalizeVolumeInUnits(Symbol.QuantityToVolumeInUnits(Quantity)); }
        }

        protected override void OnStart()
        {
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, Period);
            EMA = Indicators.MovingAverage(SourceSeries, MAPeriods, MAType);
            isPositionOpen = false;
            Positions.Closed += OnPositionsClosed;
        }

        protected override void OnTick()
        {
            // Verifica si hay posiciones abiertas
            if (Positions.Count == 0)
                return;

            // Obtén la última posición abierta
            var position = Positions.LastOrDefault();

            // Calcula el stop loss basado en la posición abierta
            double stopLossPrice;

            if (position.TradeType == TradeType.Buy)
                stopLossPrice = EMA.Result.LastValue - Symbol.PipSize * 2; // 2 pips por debajo de la SMA de 200
            else if (position.TradeType == TradeType.Sell)
                stopLossPrice = EMA.Result.LastValue + Symbol.PipSize * 2; // 2 pips por encima de la SMA de 200           
            else
                return; // No es necesario establecer el stop loss para otras operaciones

            // Calcula el take profit como 1.5 veces el stop loss
            double takeProfitPrice = position.EntryPrice + (position.EntryPrice - stopLossPrice) * 1.5;

            // Establece el stop loss y take profit
            ModifyPosition(position, stopLossPrice, takeProfitPrice);
        }

        protected override void OnBar()
        {
            if (IsSignalLineCrossover)
            {

                if (!isPositionOpen && _MACD.Histogram.Last(1) > (0) && _MACD.MACD.Last(2) < _MACD.Signal.Last(2) && _MACD.MACD.Last(1) > _MACD.Signal.Last(1) && Bars.HighPrices.Last(1) > EMA.Result.Last(2))
                {
                    double stopLossPrice = EMA.Result.LastValue - Symbol.PipSize * 2; // 2 pips por debajo de la SMA de 200
                    double takeProfitPrice = Bars.HighPrices.Last(1) + Symbol.PipSize * 2; // 2 pips por encima del máximo previo
                    double lotSize = CalculateLotSize(stopLossPrice);
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, lotSize, label);
                    isPositionOpen = true;
                }

                if (!isPositionOpen && _MACD.Histogram.Last(1) < (0) && _MACD.MACD.Last(2) > _MACD.Signal.Last(2) && _MACD.MACD.Last(1) < _MACD.Signal.Last(1) && Bars.LowPrices.Last(1) < EMA.Result.Last(2))
                {
                    double stopLossPrice = EMA.Result.LastValue + Symbol.PipSize * 2; // 2 pips por encima de la SMA de 200
                    double takeProfitPrice = Bars.LowPrices.Last(1) - Symbol.PipSize * 2; // 2 pips por debajo del mínimo previo
                    double lotSize = CalculateLotSize(stopLossPrice);
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, lotSize, label);
                    isPositionOpen = true;
                }
            }
        }

        private double CalculateLotSize(double stopLossPrice)
        {
            double accountBalance = Account.Balance;
            double riskAmount = accountBalance * 0.01; // 1% del balance
            double distanceToStopLoss = Math.Abs(stopLossPrice - Bars.ClosePrices.LastValue); // Distancia al stop loss
            double valuePerPip = Symbol.PipValue * Symbol.QuantityToVolumeInUnits(1); // Valor por pip
            double lotSize = riskAmount / (distanceToStopLoss * valuePerPip); // Lotaje calculado
            return lotSize;
        }

        private void OnPositionsClosed(PositionClosedEventArgs args)
        {
            // Actualizar el estado de la posición abierta
            isPositionOpen = false;
        }
    }
}


@oliver.r.m.92

PanagiotisCharalampous
20 Feb 2024, 10:23

Hi there,

Your calculation method does not make much sense to me. Here is the correct one


                        var maxAmountRisked = Account.Equity * (Risk / 100);
                        return Symbol.NormalizeVolumeInUnits(maxAmountRisked / (StopLoss * Symbol.PipValue), RoundingMode.Down);

@PanagiotisCharalampous

oliver.r.m.92
20 Feb 2024, 19:19

RE: Amount to trade depending on stop loss

PanagiotisCharalampous said: 

Hi there,

Your calculation method does not make much sense to me. Here is the correct one

                        var maxAmountRisked = Account.Equity * (Risk / 100);                        return Symbol.NormalizeVolumeInUnits(maxAmountRisked / (StopLoss * Symbol.PipValue), RoundingMode.Down);

I have rewritten the code and used your part of code but now I have a new problem, in the log it shows this message: Crashed in OnBar with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: volume. Actual value was: NaN.

The new code:

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;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class MACDcBot : Robot
    {
        private bool isPositionOpen;
        private MacdCrossOver _MACD;
        private MovingAverage EMA;
        private const string label = "Trade ON";
        private double stopLossPrice;

        //MA                
        [Parameter("Source", Group = "Moving Average")]
        public DataSeries SourceSeries { get; set; }

        [Parameter("MA Type", Group = "Moving Average")]
        public MovingAverageType MAType { get; set; }

        [Parameter("MA Periods", Group = "Moving Average", DefaultValue = 200)]
        public int MAPeriods { get; set; }

        //MACD
        [Parameter("Period", DefaultValue = 9)]
        public int Period { get; set; }

        [Parameter("Long Cycle", DefaultValue = 26)]
        public int LongCycle { get; set; }

        [Parameter("Short Cycle", DefaultValue = 12)]
        public int ShortCycle { get; set; }

        [Parameter("Signal-line crossover true:if Signal-line crossover false: Zero crossover", DefaultValue = true)]
        public bool IsSignalLineCrossover { get; set; }

        protected override void OnStart()
        {
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, Period);
            EMA = Indicators.MovingAverage(SourceSeries, MAPeriods, MAType);
            isPositionOpen = false;
            Positions.Closed += OnPositionsClosed;
        }

        protected override void OnBar()
        {           
            if (Positions.Count > 0)
            {
                var position = Positions.LastOrDefault();

                if (position != null)
                {
                    if (position.TradeType == TradeType.Buy)
                        stopLossPrice = EMA.Result.LastValue - Symbol.PipSize * 2; // 2 pips por debajo de la SMA de 200
                    else if (position.TradeType == TradeType.Sell)
                        stopLossPrice = EMA.Result.LastValue + Symbol.PipSize * 2; // 2 pips por encima de la SMA de 200

                    double takeProfitPrice = position.EntryPrice + (position.EntryPrice - stopLossPrice) * 1.5;

                    ModifyPosition(position, stopLossPrice, takeProfitPrice);
                }
            }

            // Calcula el tamaño de posición basado en el riesgo
            var maxAmountRisked = Account.Equity * (1 / 100);
            var positionSize = Symbol.NormalizeVolumeInUnits(maxAmountRisked / (stopLossPrice * Symbol.PipValue), RoundingMode.Down);
            
            if (IsSignalLineCrossover)
            {
                if (!isPositionOpen && _MACD.Histogram.LastValue > 0 && _MACD.Signal.LastValue < 0 && _MACD.MACD.Last(3) < _MACD.Signal.Last(3) && _MACD.MACD.Last(2) > _MACD.Signal.Last(2) && Bars.HighPrices.Last(1) > EMA.Result.Last(3))
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, positionSize, label);
                    isPositionOpen = true;
                }

                if (!isPositionOpen && _MACD.Histogram.LastValue < 0 && _MACD.Signal.LastValue > 0 && _MACD.MACD.Last(3) > _MACD.Signal.Last(3) && _MACD.MACD.Last(2) < _MACD.Signal.Last(2) && Bars.LowPrices.Last(1) < EMA.Result.Last(3))
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, positionSize, label);
                    isPositionOpen = true;
                }
            }
        }

        private void OnPositionsClosed(PositionClosedEventArgs args)
        {
            isPositionOpen = false;
        }
    }
}

 


@oliver.r.m.92

firemyst
21 Feb 2024, 01:38

RE: RE: Amount to trade depending on stop loss

oliver.r.m.92 said: 

 

I have rewritten the code and used your part of code but now I have a new problem, in the log it shows this message: Crashed in OnBar with ArgumentOutOfRangeException: Specified argument was out of the range of valid values. Parameter name: volume. Actual value was: NaN.

The new code:

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;

namespace cAlgo.Robots
{
    [Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.FullAccess)]
    public class MACDcBot : Robot
    {
        private bool isPositionOpen;
        private MacdCrossOver _MACD;
        private MovingAverage EMA;
        private const string label = "Trade ON";
        private double stopLossPrice;

        //MA                
        [Parameter("Source", Group = "Moving Average")]
        public DataSeries SourceSeries { get; set; }

        [Parameter("MA Type", Group = "Moving Average")]
        public MovingAverageType MAType { get; set; }

        [Parameter("MA Periods", Group = "Moving Average", DefaultValue = 200)]
        public int MAPeriods { get; set; }

        //MACD
        [Parameter("Period", DefaultValue = 9)]
        public int Period { get; set; }

        [Parameter("Long Cycle", DefaultValue = 26)]
        public int LongCycle { get; set; }

        [Parameter("Short Cycle", DefaultValue = 12)]
        public int ShortCycle { get; set; }

        [Parameter("Signal-line crossover true:if Signal-line crossover false: Zero crossover", DefaultValue = true)]
        public bool IsSignalLineCrossover { get; set; }

        protected override void OnStart()
        {
            _MACD = Indicators.MacdCrossOver(LongCycle, ShortCycle, Period);
            EMA = Indicators.MovingAverage(SourceSeries, MAPeriods, MAType);
            isPositionOpen = false;
            Positions.Closed += OnPositionsClosed;
        }

        protected override void OnBar()
        {           
            if (Positions.Count > 0)
            {
                var position = Positions.LastOrDefault();

                if (position != null)
                {
                    if (position.TradeType == TradeType.Buy)
                        stopLossPrice = EMA.Result.LastValue - Symbol.PipSize * 2; // 2 pips por debajo de la SMA de 200
                    else if (position.TradeType == TradeType.Sell)
                        stopLossPrice = EMA.Result.LastValue + Symbol.PipSize * 2; // 2 pips por encima de la SMA de 200

                    double takeProfitPrice = position.EntryPrice + (position.EntryPrice - stopLossPrice) * 1.5;

                    ModifyPosition(position, stopLossPrice, takeProfitPrice);
                }
            }

            // Calcula el tamaño de posición basado en el riesgo
            var maxAmountRisked = Account.Equity * (1 / 100);
            var positionSize = Symbol.NormalizeVolumeInUnits(maxAmountRisked / (stopLossPrice * Symbol.PipValue), RoundingMode.Down);
            
            if (IsSignalLineCrossover)
            {
                if (!isPositionOpen && _MACD.Histogram.LastValue > 0 && _MACD.Signal.LastValue < 0 && _MACD.MACD.Last(3) < _MACD.Signal.Last(3) && _MACD.MACD.Last(2) > _MACD.Signal.Last(2) && Bars.HighPrices.Last(1) > EMA.Result.Last(3))
                {
                    ExecuteMarketOrder(TradeType.Buy, SymbolName, positionSize, label);
                    isPositionOpen = true;
                }

                if (!isPositionOpen && _MACD.Histogram.LastValue < 0 && _MACD.Signal.LastValue > 0 && _MACD.MACD.Last(3) > _MACD.Signal.Last(3) && _MACD.MACD.Last(2) < _MACD.Signal.Last(2) && Bars.LowPrices.Last(1) < EMA.Result.Last(3))
                {
                    ExecuteMarketOrder(TradeType.Sell, SymbolName, positionSize, label);
                    isPositionOpen = true;
                }
            }
        }

        private void OnPositionsClosed(PositionClosedEventArgs args)
        {
            isPositionOpen = false;
        }
    }
}

 

Of course it throws an error. Look at your code in OnBar.

Look at the code you ALWAYS execute regardless if you're in a position or not. 

Where do you set stopLossPrice (I bolded it in the code above) if you don't have any positions? You're trying to do a calculation using it when you're not setting it.

 

I also suggest you starting learning how to use Print statements in your code to print values to the log. THen if you put print statements everywhere, you also know where your code is crashing.

 


@firemyst